hio.c (7538B)
1 #include <u.h> 2 #include <libc.h> 3 #include <httpd.h> 4 5 static char hstates[] = "nrewE"; 6 static char hxfers[] = " x"; 7 static int _hflush(Hio*, int, int); 8 9 int 10 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 }else 24 h->stop = h->start + Hsize; 25 return 0; 26 } 27 28 int 29 hiserror(Hio *h) 30 { 31 return h->state == Herr; 32 } 33 34 int 35 hgetc(Hio *h) 36 { 37 uchar *p; 38 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 } 51 52 int 53 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 } 66 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 encoding 73 */ 74 void * 75 hreadbuf(Hio *h, void *vsave) 76 { 77 Hio *hh; 78 uchar *save; 79 int c, in, cpy, dpos; 80 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 } 88 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; 99 100 in = Hsize - cpy; 101 if(h->state == Hend) 102 in = 0; 103 else if(in > h->bodylen) 104 in = h->bodylen; 105 106 /* 107 * for chunked encoding, fill buffer, 108 * then read in new chunk length and wipe out that line 109 */ 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 else 133 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; 145 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; 170 171 h->bodylen -= in; 172 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 } 179 180 int 181 hbuflen(Hio *h, void *p) 182 { 183 return h->stop - (uchar*)p; 184 } 185 186 /* 187 * prepare to receive a message body 188 * len is the content length (~0 => unspecified) 189 * te is the transfer encoding 190 * returns < 0 if setup failed 191 */ 192 Hio* 193 hbodypush(Hio *hh, ulong len, HFields *te) 194 { 195 Hio *h; 196 int xe; 197 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 }else 210 return nil; 211 } 212 213 h = malloc(sizeof *h); 214 if(h == nil) 215 return nil; 216 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 } 228 229 /* 230 * dump the state of the io buffer into a string 231 */ 232 char * 233 hunload(Hio *h) 234 { 235 uchar *p, *t, *stop, *buf; 236 int ne, n, c; 237 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; 246 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]; 253 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 }else 262 *t++ = c; 263 } 264 *t++ = '\0'; 265 if(t != buf + n) 266 return nil; 267 return (char*)buf; 268 } 269 270 /* 271 * read the io buffer state from a string 272 */ 273 int 274 hload(Hio *h, char *buf) 275 { 276 uchar *p, *t, *stop; 277 char *s; 278 int c; 279 280 s = strchr(hstates, buf[0]); 281 if(s == nil) 282 return -1; 283 h->state = s - hstates; 284 285 s = strchr(hxfers, buf[1]); 286 if(s == nil) 287 return -1; 288 h->xferenc = s - hxfers; 289 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 else 297 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 } 309 310 void 311 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 } 321 322 /* 323 * flush the buffer and possibly change encoding modes 324 */ 325 int 326 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 } 335 336 int 337 hputc(Hio *h, int c) 338 { 339 uchar *p; 340 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 } 350 351 static int 352 fmthflush(Fmt *f) 353 { 354 Hio *h; 355 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 } 365 366 int 367 hvprint(Hio *h, char *fmt, va_list args) 368 { 369 int n; 370 Fmt f; 371 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 } 384 385 int 386 hprint(Hio *h, char *fmt, ...) 387 { 388 int n; 389 va_list arg; 390 391 va_start(arg, fmt); 392 n = hvprint(h, fmt, arg); 393 va_end(arg); 394 return n; 395 } 396 397 static int 398 _hflush(Hio *h, int force, int dolength) 399 { 400 uchar *s; 401 int w; 402 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 } 434 435 int 436 hflush(Hio *h) 437 { 438 return _hflush(h, 0, 0); 439 } 440 441 int 442 hlflush(Hio* h) 443 { 444 return _hflush(h, 0, 1); 445 } 446 447 int 448 hwrite(Hio *h, void *vbuf, int len) 449 { 450 uchar *buf; 451 int n, m; 452 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 }