Blob
1 #include <u.h>2 #include <libc.h>3 #include <httpd.h>5 static char hstates[] = "nrewE";6 static char hxfers[] = " x";7 static int _hflush(Hio*, int, int);9 int10 hinit(Hio *h, int fd, int mode)11 {12 if(fd == -1 || mode != Hread && mode != Hwrite)13 return -1;14 h->hh = nil;15 h->fd = fd;16 h->seek = 0;17 h->state = mode;18 h->start = h->buf + 16; /* leave space for chunk length */19 h->stop = h->pos = h->start;20 if(mode == Hread){21 h->bodylen = ~0UL;22 *h->pos = '\0';23 }else24 h->stop = h->start + Hsize;25 return 0;26 }28 int29 hiserror(Hio *h)30 {31 return h->state == Herr;32 }34 int35 hgetc(Hio *h)36 {37 uchar *p;39 p = h->pos;40 if(p < h->stop){41 h->pos = p + 1;42 return *p;43 }44 p -= UTFmax;45 if(p < h->start)46 p = h->start;47 if(!hreadbuf(h, p) || h->pos == h->stop)48 return -1;49 return *h->pos++;50 }52 int53 hungetc(Hio *h)54 {55 if(h->state == Hend)56 h->state = Hread;57 else if(h->state == Hread)58 h->pos--;59 if(h->pos < h->start || h->state != Hread){60 h->state = Herr;61 h->pos = h->stop;62 return -1;63 }64 return 0;65 }67 /*68 * fill the buffer, saving contents from vsave onwards.69 * nothing is saved if vsave is nil.70 * returns the beginning of the buffer.71 *72 * understands message body sizes and chunked transfer encoding73 */74 void *75 hreadbuf(Hio *h, void *vsave)76 {77 Hio *hh;78 uchar *save;79 int c, in, cpy, dpos;81 save = vsave;82 if(save && (save < h->start || save > h->stop)83 || h->state != Hread && h->state != Hend){84 h->state = Herr;85 h->pos = h->stop;86 return nil;87 }89 dpos = 0;90 if(save && h->pos > save)91 dpos = h->pos - save;92 cpy = 0;93 if(save){94 cpy = h->stop - save;95 memmove(h->start, save, cpy);96 }97 h->seek += h->stop - h->start - cpy;98 h->pos = h->start + dpos;100 in = Hsize - cpy;101 if(h->state == Hend)102 in = 0;103 else if(in > h->bodylen)104 in = h->bodylen;106 /*107 * for chunked encoding, fill buffer,108 * then read in new chunk length and wipe out that line109 */110 hh = h->hh;111 if(hh != nil){112 if(!in && h->xferenc && h->state != Hend){113 if(h->xferenc == 2){114 c = hgetc(hh);115 if(c == '\r')116 c = hgetc(hh);117 if(c != '\n'){118 h->pos = h->stop;119 h->state = Herr;120 return nil;121 }122 }123 h->xferenc = 2;124 in = 0;125 while((c = hgetc(hh)) != '\n'){126 if(c >= '0' && c <= '9')127 c -= '0';128 else if(c >= 'a' && c <= 'f')129 c -= 'a' - 10;130 else if(c >= 'A' && c <= 'F')131 c -= 'A' - 10;132 else133 break;134 in = in * 16 + c;135 }136 while(c != '\n'){137 if(c < 0){138 h->pos = h->stop;139 h->state = Herr;140 return nil;141 }142 c = hgetc(hh);143 }144 h->bodylen = in;146 in = Hsize - cpy;147 if(in > h->bodylen)148 in = h->bodylen;149 }150 if(in){151 while(hh->pos + in > hh->stop){152 if(hreadbuf(hh, hh->pos) == nil){153 h->pos = h->stop;154 h->state = Herr;155 return nil;156 }157 }158 memmove(h->start + cpy, hh->pos, in);159 hh->pos += in;160 }161 }else if(in){162 if((in = read(h->fd, h->start + cpy, in)) < 0){163 h->state = Herr;164 h->pos = h->stop;165 return nil;166 }167 }168 if(in == 0)169 h->state = Hend;171 h->bodylen -= in;173 h->stop = h->start + cpy + in;174 *h->stop = '\0';175 if(h->pos == h->stop)176 return nil;177 return h->start;178 }180 int181 hbuflen(Hio *h, void *p)182 {183 return h->stop - (uchar*)p;184 }186 /*187 * prepare to receive a message body188 * len is the content length (~0 => unspecified)189 * te is the transfer encoding190 * returns < 0 if setup failed191 */192 Hio*193 hbodypush(Hio *hh, ulong len, HFields *te)194 {195 Hio *h;196 int xe;198 if(hh->state != Hread)199 return nil;200 xe = 0;201 if(te != nil){202 if(te->params != nil || te->next != nil)203 return nil;204 if(cistrcmp(te->s, "chunked") == 0){205 xe = 1;206 len = 0;207 }else if(cistrcmp(te->s, "identity") == 0){208 ;209 }else210 return nil;211 }213 h = malloc(sizeof *h);214 if(h == nil)215 return nil;217 h->hh = hh;218 h->fd = -1;219 h->seek = 0;220 h->state = Hread;221 h->xferenc = xe;222 h->start = h->buf + 16; /* leave space for chunk length */223 h->stop = h->pos = h->start;224 *h->pos = '\0';225 h->bodylen = len;226 return h;227 }229 /*230 * dump the state of the io buffer into a string231 */232 char *233 hunload(Hio *h)234 {235 uchar *p, *t, *stop, *buf;236 int ne, n, c;238 stop = h->stop;239 ne = 0;240 for(p = h->pos; p < stop; p++){241 c = *p;242 if(c == 0x80)243 ne++;244 }245 p = h->pos;247 n = (stop - p) + ne + 3;248 buf = mallocz(n, 1);249 if(buf == nil)250 return nil;251 buf[0] = hstates[h->state];252 buf[1] = hxfers[h->xferenc];254 t = &buf[2];255 for(; p < stop; p++){256 c = *p;257 if(c == 0 || c == 0x80){258 *t++ = 0x80;259 if(c == 0x80)260 *t++ = 0x80;261 }else262 *t++ = c;263 }264 *t++ = '\0';265 if(t != buf + n)266 return nil;267 return (char*)buf;268 }270 /*271 * read the io buffer state from a string272 */273 int274 hload(Hio *h, char *buf)275 {276 uchar *p, *t, *stop;277 char *s;278 int c;280 s = strchr(hstates, buf[0]);281 if(s == nil)282 return -1;283 h->state = s - hstates;285 s = strchr(hxfers, buf[1]);286 if(s == nil)287 return -1;288 h->xferenc = s - hxfers;290 t = h->start;291 stop = t + Hsize;292 for(p = (uchar*)&buf[2]; c = *p; p++){293 if(c == 0x80){294 if(p[1] != 0x80)295 c = 0;296 else297 p++;298 }299 *t++ = c;300 if(t >= stop)301 return -1;302 }303 *t = '\0';304 h->pos = h->start;305 h->stop = t;306 h->seek = 0;307 return 0;308 }310 void311 hclose(Hio *h)312 {313 if(h->fd >= 0){314 if(h->state == Hwrite)315 hxferenc(h, 0);316 close(h->fd);317 }318 h->stop = h->pos = nil;319 h->fd = -1;320 }322 /*323 * flush the buffer and possibly change encoding modes324 */325 int326 hxferenc(Hio *h, int on)327 {328 if(h->xferenc && !on && h->pos != h->start)329 hflush(h);330 if(_hflush(h, 1, 0) < 0)331 return -1;332 h->xferenc = !!on;333 return 0;334 }336 int337 hputc(Hio *h, int c)338 {339 uchar *p;341 p = h->pos;342 if(p < h->stop){343 h->pos = p + 1;344 return *p = c;345 }346 if(hflush(h) < 0)347 return -1;348 return *h->pos++ = c;349 }351 static int352 fmthflush(Fmt *f)353 {354 Hio *h;356 h = f->farg;357 h->pos = f->to;358 if(hflush(h) < 0)359 return 0;360 f->stop = h->stop;361 f->to = h->pos;362 f->start = h->pos;363 return 1;364 }366 int367 hvprint(Hio *h, char *fmt, va_list args)368 {369 int n;370 Fmt f;372 f.runes = 0;373 f.stop = h->stop;374 f.to = h->pos;375 f.start = h->pos;376 f.flush = fmthflush;377 f.farg = h;378 f.nfmt = 0;379 fmtlocaleinit(&f, nil, nil, nil);380 n = fmtvprint(&f, fmt, args);381 h->pos = f.to;382 return n;383 }385 int386 hprint(Hio *h, char *fmt, ...)387 {388 int n;389 va_list arg;391 va_start(arg, fmt);392 n = hvprint(h, fmt, arg);393 va_end(arg);394 return n;395 }397 static int398 _hflush(Hio *h, int force, int dolength)399 {400 uchar *s;401 int w;403 if(h->state != Hwrite){404 h->state = Herr;405 h->stop = h->pos;406 return -1;407 }408 s = h->start;409 w = h->pos - s;410 if(w == 0 && !force)411 return 0;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(dolength)424 fprint(h->fd, "Content-Length: %d\r\n\r\n", w);425 if(write(h->fd, s, w) != w){426 h->state = Herr;427 h->stop = h->pos;428 return -1;429 }430 h->seek += w;431 h->pos = h->start;432 return 0;433 }435 int436 hflush(Hio *h)437 {438 return _hflush(h, 0, 0);439 }441 int442 hlflush(Hio* h)443 {444 return _hflush(h, 0, 1);445 }447 int448 hwrite(Hio *h, void *vbuf, int len)449 {450 uchar *buf;451 int n, m;453 buf = vbuf;454 n = len;455 if(n < 0 || h->state != Hwrite){456 h->state = Herr;457 h->stop = h->pos;458 return -1;459 }460 if(h->pos + n >= h->stop){461 if(h->start != h->pos)462 if(hflush(h) < 0)463 return -1;464 while(h->pos + n >= h->stop){465 m = h->stop - h->pos;466 if(h->xferenc){467 memmove(h->pos, buf, m);468 h->pos += m;469 if(hflush(h) < 0)470 return -1;471 }else{472 if(write(h->fd, buf, m) != m){473 h->state = Herr;474 h->stop = h->pos;475 return -1;476 }477 h->seek += m;478 }479 n -= m;480 buf += m;481 }482 }483 memmove(h->pos, buf, n);484 h->pos += n;485 return len;486 }