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 && (in = read(h->fd, h->start + cpy, in)) < 0){161 h->state = Herr;162 h->pos = h->stop;163 return nil;164 }165 if(in == 0)166 h->state = Hend;168 h->bodylen -= in;170 h->stop = h->start + cpy + in;171 *h->stop = '\0';172 if(h->pos == h->stop)173 return nil;174 return h->start;175 }177 int178 hbuflen(Hio *h, void *p)179 {180 return h->stop - (uchar*)p;181 }183 /*184 * prepare to receive a message body185 * len is the content length (~0 => unspecified)186 * te is the transfer encoding187 * returns < 0 if setup failed188 */189 Hio*190 hbodypush(Hio *hh, ulong len, HFields *te)191 {192 Hio *h;193 int xe;195 if(hh->state != Hread)196 return nil;197 xe = 0;198 if(te != nil){199 if(te->params != nil || te->next != nil)200 return nil;201 if(cistrcmp(te->s, "chunked") == 0){202 xe = 1;203 len = 0;204 }else if(cistrcmp(te->s, "identity") == 0){205 ;206 }else207 return nil;208 }210 h = malloc(sizeof *h);211 if(h == nil)212 return nil;214 h->hh = hh;215 h->fd = -1;216 h->seek = 0;217 h->state = Hread;218 h->xferenc = xe;219 h->start = h->buf + 16; /* leave space for chunk length */220 h->stop = h->pos = h->start;221 *h->pos = '\0';222 h->bodylen = len;223 return h;224 }226 /*227 * dump the state of the io buffer into a string228 */229 char *230 hunload(Hio *h)231 {232 uchar *p, *t, *stop, *buf;233 int ne, n, c;235 stop = h->stop;236 ne = 0;237 for(p = h->pos; p < stop; p++){238 c = *p;239 if(c == 0x80)240 ne++;241 }242 p = h->pos;244 n = (stop - p) + ne + 3;245 buf = mallocz(n, 1);246 if(buf == nil)247 return nil;248 buf[0] = hstates[h->state];249 buf[1] = hxfers[h->xferenc];251 t = &buf[2];252 for(; p < stop; p++){253 c = *p;254 if(c == 0 || c == 0x80){255 *t++ = 0x80;256 if(c == 0x80)257 *t++ = 0x80;258 }else259 *t++ = c;260 }261 *t++ = '\0';262 if(t != buf + n)263 return nil;264 return (char*)buf;265 }267 /*268 * read the io buffer state from a string269 */270 int271 hload(Hio *h, char *buf)272 {273 uchar *p, *t, *stop;274 char *s;275 int c;277 s = strchr(hstates, buf[0]);278 if(s == nil)279 return 0;280 h->state = s - hstates;282 s = strchr(hxfers, buf[1]);283 if(s == nil)284 return 0;285 h->xferenc = s - hxfers;287 t = h->start;288 stop = t + Hsize;289 for(p = (uchar*)&buf[2]; c = *p; p++){290 if(c == 0x80){291 if(p[1] != 0x80)292 c = 0;293 else294 p++;295 }296 *t++ = c;297 if(t >= stop)298 return 0;299 }300 *t = '\0';301 h->pos = h->start;302 h->stop = t;303 h->seek = 0;304 return 1;305 }307 void308 hclose(Hio *h)309 {310 if(h->fd >= 0){311 if(h->state == Hwrite)312 hxferenc(h, 0);313 close(h->fd);314 }315 h->stop = h->pos = nil;316 h->fd = -1;317 }319 /*320 * flush the buffer and possibly change encoding modes321 */322 int323 hxferenc(Hio *h, int on)324 {325 if(h->xferenc && !on && h->pos != h->start)326 hflush(h);327 if(hflush(h) < 0)328 return -1;329 h->xferenc = !!on;330 return 0;331 }333 int334 hputc(Hio *h, int c)335 {336 uchar *p;338 p = h->pos;339 if(p < h->stop){340 h->pos = p + 1;341 return *p = c;342 }343 if(hflush(h) < 0)344 return -1;345 return *h->pos++ = c;346 }348 static int349 fmthflush(Fmt *f)350 {351 Hio *h;353 h = f->farg;354 h->pos = f->to;355 if(hflush(h) < 0)356 return 0;357 f->stop = h->stop;358 f->to = h->pos;359 f->start = h->pos;360 return 1;361 }363 int364 hvprint(Hio *h, char *fmt, va_list args)365 {366 int n;367 Fmt f;369 f.runes = 0;370 f.stop = h->stop;371 f.to = h->pos;372 f.start = h->pos;373 f.flush = fmthflush;374 f.farg = h;375 f.nfmt = 0;376 f.args = args;377 n = dofmt(&f, fmt);378 h->pos = f.to;379 return n;380 }382 int383 hprint(Hio *h, char *fmt, ...)384 {385 int n;386 va_list arg;388 va_start(arg, fmt);389 n = hvprint(h, fmt, arg);390 va_end(arg);391 return n;392 }394 int395 hflush(Hio *h)396 {397 uchar *s;398 int w;400 if(h->state != Hwrite){401 h->state = Herr;402 h->stop = h->pos;403 return -1;404 }405 s = h->start;406 w = h->pos - s;407 if(h->xferenc){408 *--s = '\n';409 *--s = '\r';410 do{411 *--s = "0123456789abcdef"[w & 0xf];412 w >>= 4;413 }while(w);414 h->pos[0] = '\r';415 h->pos[1] = '\n';416 w = &h->pos[2] - s;417 }418 if(write(h->fd, s, w) != w){419 h->state = Herr;420 h->stop = h->pos;421 return -1;422 }423 h->seek += w;424 h->pos = h->start;425 return 0;426 }428 int429 hwrite(Hio *h, void *vbuf, int len)430 {431 uchar *pos, *buf;432 int n, m;434 buf = vbuf;435 n = len;436 if(n < 0 || h->state != Hwrite){437 h->state = Herr;438 h->stop = h->pos;439 return -1;440 }441 pos = h->pos;442 if(pos + n >= h->stop){443 m = pos - h->start;444 if(m){445 m = Hsize - m;446 if(m){447 memmove(pos, buf, m);448 buf += m;449 n -= m;450 }451 if(write(h->fd, h->start, Hsize) != Hsize){452 h->state = Herr;453 h->stop = h->pos;454 return -1;455 }456 h->seek += Hsize;457 }458 m = n % Hsize;459 n -= m;460 if(n != 0 && write(h->fd, buf, n) != n){461 h->state = Herr;462 h->stop = h->pos;463 return -1;464 }465 h->seek += n;466 buf += n;467 pos = h->pos = h->start;468 n = m;469 }470 memmove(pos, buf, n);471 h->pos = pos + n;472 return len;473 }