plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

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 }