Blob
1 #include <u.h>2 #include <libc.h>3 #include <httpd.h>5 static char hstates[] = "nrewE";6 static char hxfers[] = " x";8 int9 hinit(Hio *h, int fd, int mode)10 {11 if(fd == -1 || mode != Hread && mode != Hwrite)12 return -1;13 h->hh = nil;14 h->fd = fd;15 h->seek = 0;16 h->state = mode;17 h->start = h->buf + 16; /* leave space for chunk length */18 h->stop = h->pos = h->start;19 if(mode == Hread){20 h->bodylen = ~0UL;21 *h->pos = '\0';22 }else23 h->stop = h->start + Hsize;24 return 0;25 }27 int28 hiserror(Hio *h)29 {30 return h->state == Herr;31 }33 int34 hgetc(Hio *h)35 {36 uchar *p;38 p = h->pos;39 if(p < h->stop){40 h->pos = p + 1;41 return *p;42 }43 p -= UTFmax;44 if(p < h->start)45 p = h->start;46 if(!hreadbuf(h, p) || h->pos == h->stop)47 return -1;48 return *h->pos++;49 }51 int52 hungetc(Hio *h)53 {54 if(h->state == Hend)55 h->state = Hread;56 else if(h->state == Hread)57 h->pos--;58 if(h->pos < h->start || h->state != Hread){59 h->state = Herr;60 h->pos = h->stop;61 return -1;62 }63 return 0;64 }66 /*67 * fill the buffer, saving contents from vsave onwards.68 * nothing is saved if vsave is nil.69 * returns the beginning of the buffer.70 *71 * understands message body sizes and chunked transfer encoding72 */73 void *74 hreadbuf(Hio *h, void *vsave)75 {76 Hio *hh;77 uchar *save;78 int c, in, cpy, dpos;80 save = vsave;81 if(save && (save < h->start || save > h->stop)82 || h->state != Hread && h->state != Hend){83 h->state = Herr;84 h->pos = h->stop;85 return nil;86 }88 dpos = 0;89 if(save && h->pos > save)90 dpos = h->pos - save;91 cpy = 0;92 if(save){93 cpy = h->stop - save;94 memmove(h->start, save, cpy);95 }96 h->seek += h->stop - h->start - cpy;97 h->pos = h->start + dpos;99 in = Hsize - cpy;100 if(h->state == Hend)101 in = 0;102 else if(in > h->bodylen)103 in = h->bodylen;105 /*106 * for chunked encoding, fill buffer,107 * then read in new chunk length and wipe out that line108 */109 hh = h->hh;110 if(hh != nil){111 if(!in && h->xferenc && h->state != Hend){112 if(h->xferenc == 2){113 c = hgetc(hh);114 if(c == '\r')115 c = hgetc(hh);116 if(c != '\n'){117 h->pos = h->stop;118 h->state = Herr;119 return nil;120 }121 }122 h->xferenc = 2;123 in = 0;124 while((c = hgetc(hh)) != '\n'){125 if(c >= '0' && c <= '9')126 c -= '0';127 else if(c >= 'a' && c <= 'f')128 c -= 'a' - 10;129 else if(c >= 'A' && c <= 'F')130 c -= 'A' - 10;131 else132 break;133 in = in * 16 + c;134 }135 while(c != '\n'){136 if(c < 0){137 h->pos = h->stop;138 h->state = Herr;139 return nil;140 }141 c = hgetc(hh);142 }143 h->bodylen = in;145 in = Hsize - cpy;146 if(in > h->bodylen)147 in = h->bodylen;148 }149 if(in){150 while(hh->pos + in > hh->stop){151 if(hreadbuf(hh, hh->pos) == nil){152 h->pos = h->stop;153 h->state = Herr;154 return nil;155 }156 }157 memmove(h->start + cpy, hh->pos, in);158 hh->pos += in;159 }160 }else if(in){161 fprint(2, "read %d from %d\n", in, h->fd);162 if((in = read(h->fd, h->start + cpy, in)) < 0){163 fprint(2, "got error: %r\n");164 h->state = Herr;165 h->pos = h->stop;166 return nil;167 }168 fprint(2, "got %d\n", in);169 }170 if(in == 0)171 h->state = Hend;173 h->bodylen -= in;175 h->stop = h->start + cpy + in;176 *h->stop = '\0';177 if(h->pos == h->stop)178 return nil;179 return h->start;180 }182 int183 hbuflen(Hio *h, void *p)184 {185 return h->stop - (uchar*)p;186 }188 /*189 * prepare to receive a message body190 * len is the content length (~0 => unspecified)191 * te is the transfer encoding192 * returns < 0 if setup failed193 */194 Hio*195 hbodypush(Hio *hh, ulong len, HFields *te)196 {197 Hio *h;198 int xe;200 if(hh->state != Hread)201 return nil;202 xe = 0;203 if(te != nil){204 if(te->params != nil || te->next != nil)205 return nil;206 if(cistrcmp(te->s, "chunked") == 0){207 xe = 1;208 len = 0;209 }else if(cistrcmp(te->s, "identity") == 0){210 ;211 }else212 return nil;213 }215 h = malloc(sizeof *h);216 if(h == nil)217 return nil;219 h->hh = hh;220 h->fd = -1;221 h->seek = 0;222 h->state = Hread;223 h->xferenc = xe;224 h->start = h->buf + 16; /* leave space for chunk length */225 h->stop = h->pos = h->start;226 *h->pos = '\0';227 h->bodylen = len;228 return h;229 }231 /*232 * dump the state of the io buffer into a string233 */234 char *235 hunload(Hio *h)236 {237 uchar *p, *t, *stop, *buf;238 int ne, n, c;240 stop = h->stop;241 ne = 0;242 for(p = h->pos; p < stop; p++){243 c = *p;244 if(c == 0x80)245 ne++;246 }247 p = h->pos;249 n = (stop - p) + ne + 3;250 buf = mallocz(n, 1);251 if(buf == nil)252 return nil;253 buf[0] = hstates[h->state];254 buf[1] = hxfers[h->xferenc];256 t = &buf[2];257 for(; p < stop; p++){258 c = *p;259 if(c == 0 || c == 0x80){260 *t++ = 0x80;261 if(c == 0x80)262 *t++ = 0x80;263 }else264 *t++ = c;265 }266 *t++ = '\0';267 if(t != buf + n)268 return nil;269 return (char*)buf;270 }272 /*273 * read the io buffer state from a string274 */275 int276 hload(Hio *h, char *buf)277 {278 uchar *p, *t, *stop;279 char *s;280 int c;282 s = strchr(hstates, buf[0]);283 if(s == nil)284 return 0;285 h->state = s - hstates;287 s = strchr(hxfers, buf[1]);288 if(s == nil)289 return 0;290 h->xferenc = s - hxfers;292 t = h->start;293 stop = t + Hsize;294 for(p = (uchar*)&buf[2]; c = *p; p++){295 if(c == 0x80){296 if(p[1] != 0x80)297 c = 0;298 else299 p++;300 }301 *t++ = c;302 if(t >= stop)303 return 0;304 }305 *t = '\0';306 h->pos = h->start;307 h->stop = t;308 h->seek = 0;309 return 1;310 }312 void313 hclose(Hio *h)314 {315 if(h->fd >= 0){316 if(h->state == Hwrite)317 hxferenc(h, 0);318 close(h->fd);319 }320 h->stop = h->pos = nil;321 h->fd = -1;322 }324 /*325 * flush the buffer and possibly change encoding modes326 */327 int328 hxferenc(Hio *h, int on)329 {330 if(h->xferenc && !on && h->pos != h->start)331 hflush(h);332 if(hflush(h) < 0)333 return -1;334 h->xferenc = !!on;335 return 0;336 }338 int339 hputc(Hio *h, int c)340 {341 uchar *p;343 p = h->pos;344 if(p < h->stop){345 h->pos = p + 1;346 return *p = c;347 }348 if(hflush(h) < 0)349 return -1;350 return *h->pos++ = c;351 }353 static int354 fmthflush(Fmt *f)355 {356 Hio *h;358 h = f->farg;359 h->pos = f->to;360 if(hflush(h) < 0)361 return 0;362 f->stop = h->stop;363 f->to = h->pos;364 f->start = h->pos;365 return 1;366 }368 int369 hvprint(Hio *h, char *fmt, va_list args)370 {371 int n;372 Fmt f;374 f.runes = 0;375 f.stop = h->stop;376 f.to = h->pos;377 f.start = h->pos;378 f.flush = fmthflush;379 f.farg = h;380 f.nfmt = 0;381 f.args = args;382 n = dofmt(&f, fmt);383 h->pos = f.to;384 return n;385 }387 int388 hprint(Hio *h, char *fmt, ...)389 {390 int n;391 va_list arg;393 va_start(arg, fmt);394 n = hvprint(h, fmt, arg);395 va_end(arg);396 return n;397 }399 int400 hflush(Hio *h)401 {402 uchar *s;403 int w;405 if(h->state != Hwrite){406 h->state = Herr;407 h->stop = h->pos;408 return -1;409 }410 s = h->start;411 w = h->pos - s;412 if(h->xferenc){413 *--s = '\n';414 *--s = '\r';415 do{416 *--s = "0123456789abcdef"[w & 0xf];417 w >>= 4;418 }while(w);419 h->pos[0] = '\r';420 h->pos[1] = '\n';421 w = &h->pos[2] - s;422 }423 if(write(h->fd, s, w) != w){424 h->state = Herr;425 h->stop = h->pos;426 return -1;427 }428 h->seek += w;429 h->pos = h->start;430 return 0;431 }433 int434 hwrite(Hio *h, void *vbuf, int len)435 {436 uchar *pos, *buf;437 int n, m;439 buf = vbuf;440 n = len;441 if(n < 0 || h->state != Hwrite){442 h->state = Herr;443 h->stop = h->pos;444 return -1;445 }446 pos = h->pos;447 if(pos + n >= h->stop){448 m = pos - h->start;449 if(m){450 m = Hsize - m;451 if(m){452 memmove(pos, buf, m);453 buf += m;454 n -= m;455 }456 if(write(h->fd, h->start, Hsize) != Hsize){457 h->state = Herr;458 h->stop = h->pos;459 return -1;460 }461 h->seek += Hsize;462 }463 m = n % Hsize;464 n -= m;465 if(n != 0 && write(h->fd, buf, n) != n){466 h->state = Herr;467 h->stop = h->pos;468 return -1;469 }470 h->seek += n;471 buf += n;472 pos = h->pos = h->start;473 n = m;474 }475 memmove(pos, buf, n);476 h->pos = pos + n;477 return len;478 }