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 if((in = read(h->fd, h->start + cpy, in)) < 0){162 h->state = Herr;163 h->pos = h->stop;164 return nil;165 }166 }167 if(in == 0)168 h->state = Hend;170 h->bodylen -= in;172 h->stop = h->start + cpy + in;173 *h->stop = '\0';174 if(h->pos == h->stop)175 return nil;176 return h->start;177 }179 int180 hbuflen(Hio *h, void *p)181 {182 return h->stop - (uchar*)p;183 }185 /*186 * prepare to receive a message body187 * len is the content length (~0 => unspecified)188 * te is the transfer encoding189 * returns < 0 if setup failed190 */191 Hio*192 hbodypush(Hio *hh, ulong len, HFields *te)193 {194 Hio *h;195 int xe;197 if(hh->state != Hread)198 return nil;199 xe = 0;200 if(te != nil){201 if(te->params != nil || te->next != nil)202 return nil;203 if(cistrcmp(te->s, "chunked") == 0){204 xe = 1;205 len = 0;206 }else if(cistrcmp(te->s, "identity") == 0){207 ;208 }else209 return nil;210 }212 h = malloc(sizeof *h);213 if(h == nil)214 return nil;216 h->hh = hh;217 h->fd = -1;218 h->seek = 0;219 h->state = Hread;220 h->xferenc = xe;221 h->start = h->buf + 16; /* leave space for chunk length */222 h->stop = h->pos = h->start;223 *h->pos = '\0';224 h->bodylen = len;225 return h;226 }228 /*229 * dump the state of the io buffer into a string230 */231 char *232 hunload(Hio *h)233 {234 uchar *p, *t, *stop, *buf;235 int ne, n, c;237 stop = h->stop;238 ne = 0;239 for(p = h->pos; p < stop; p++){240 c = *p;241 if(c == 0x80)242 ne++;243 }244 p = h->pos;246 n = (stop - p) + ne + 3;247 buf = mallocz(n, 1);248 if(buf == nil)249 return nil;250 buf[0] = hstates[h->state];251 buf[1] = hxfers[h->xferenc];253 t = &buf[2];254 for(; p < stop; p++){255 c = *p;256 if(c == 0 || c == 0x80){257 *t++ = 0x80;258 if(c == 0x80)259 *t++ = 0x80;260 }else261 *t++ = c;262 }263 *t++ = '\0';264 if(t != buf + n)265 return nil;266 return (char*)buf;267 }269 /*270 * read the io buffer state from a string271 */272 int273 hload(Hio *h, char *buf)274 {275 uchar *p, *t, *stop;276 char *s;277 int c;279 s = strchr(hstates, buf[0]);280 if(s == nil)281 return -1;282 h->state = s - hstates;284 s = strchr(hxfers, buf[1]);285 if(s == nil)286 return -1;287 h->xferenc = s - hxfers;289 t = h->start;290 stop = t + Hsize;291 for(p = (uchar*)&buf[2]; c = *p; p++){292 if(c == 0x80){293 if(p[1] != 0x80)294 c = 0;295 else296 p++;297 }298 *t++ = c;299 if(t >= stop)300 return -1;301 }302 *t = '\0';303 h->pos = h->start;304 h->stop = t;305 h->seek = 0;306 return 0;307 }309 void310 hclose(Hio *h)311 {312 if(h->fd >= 0){313 if(h->state == Hwrite)314 hxferenc(h, 0);315 close(h->fd);316 }317 h->stop = h->pos = nil;318 h->fd = -1;319 }321 /*322 * flush the buffer and possibly change encoding modes323 */324 int325 hxferenc(Hio *h, int on)326 {327 if(h->xferenc && !on && h->pos != h->start)328 hflush(h);329 if(hflush(h) < 0)330 return -1;331 h->xferenc = !!on;332 return 0;333 }335 int336 hputc(Hio *h, int c)337 {338 uchar *p;340 p = h->pos;341 if(p < h->stop){342 h->pos = p + 1;343 return *p = c;344 }345 if(hflush(h) < 0)346 return -1;347 return *h->pos++ = c;348 }350 static int351 fmthflush(Fmt *f)352 {353 Hio *h;355 h = f->farg;356 h->pos = f->to;357 if(hflush(h) < 0)358 return 0;359 f->stop = h->stop;360 f->to = h->pos;361 f->start = h->pos;362 return 1;363 }365 int366 hvprint(Hio *h, char *fmt, va_list args)367 {368 int n;369 Fmt f;371 f.runes = 0;372 f.stop = h->stop;373 f.to = h->pos;374 f.start = h->pos;375 f.flush = fmthflush;376 f.farg = h;377 f.nfmt = 0;378 n = fmtvprint(&f, fmt, args);379 h->pos = f.to;380 return n;381 }383 int384 hprint(Hio *h, char *fmt, ...)385 {386 int n;387 va_list arg;389 va_start(arg, fmt);390 n = hvprint(h, fmt, arg);391 va_end(arg);392 return n;393 }395 static int396 _hflush(Hio *h, int dolength)397 {398 uchar *s;399 int w;401 if(h->state != Hwrite){402 h->state = Herr;403 h->stop = h->pos;404 return -1;405 }406 s = h->start;407 w = h->pos - s;408 if(h->xferenc){409 *--s = '\n';410 *--s = '\r';411 do{412 *--s = "0123456789abcdef"[w & 0xf];413 w >>= 4;414 }while(w);415 h->pos[0] = '\r';416 h->pos[1] = '\n';417 w = &h->pos[2] - s;418 }419 if(dolength)420 fprint(h->fd, "Content-Length: %d\r\n\r\n", w);421 if(write(h->fd, s, w) != w){422 h->state = Herr;423 h->stop = h->pos;424 return -1;425 }426 h->seek += w;427 h->pos = h->start;428 return 0;429 }431 int432 hflush(Hio *h)433 {434 return _hflush(h, 0);435 }437 int438 hlflush(Hio* h)439 {440 return _hflush(h, 1);441 }443 int444 hwrite(Hio *h, void *vbuf, int len)445 {446 uchar *buf;447 int n, m;449 buf = vbuf;450 n = len;451 if(n < 0 || h->state != Hwrite){452 h->state = Herr;453 h->stop = h->pos;454 return -1;455 }456 if(h->pos + n >= h->stop){457 if(h->start != h->pos)458 if(hflush(h) < 0)459 return -1;460 while(h->pos + n >= h->stop){461 m = h->stop - h->pos;462 if(h->xferenc){463 memmove(h->pos, buf, m);464 h->pos += m;465 if(hflush(h) < 0)466 return -1;467 }else{468 if(write(h->fd, buf, m) != m){469 h->state = Herr;470 h->stop = h->pos;471 return -1;472 }473 h->seek += m;474 }475 n -= m;476 buf += m;477 }478 }479 memmove(h->pos, buf, n);480 h->pos += n;481 return len;482 }