497 lines · 7.8 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <httpd.h> static	char	hstates[] = "nrewE";static	char	hxfers[] = " x";static int _hflush(Hio*, int, int); inthinit(Hio *h, int fd, int mode){	if(fd == -1 || mode != Hread && mode != Hwrite)		return -1;	h->hh = nil;	h->fd = fd;	h->seek = 0;	h->state = mode;	h->start = h->buf + 16;		/* leave space for chunk length */	h->stop = h->pos = h->start;	if(mode == Hread){		h->bodylen = ~0UL;		*h->pos = '\0';	}else		h->stop = h->start + Hsize;	return 0;} inthiserror(Hio *h){	return h->state == Herr;} inthgetc(Hio *h){	uchar *p; 	p = h->pos;	if(p < h->stop){		h->pos = p + 1;		return *p;	}	p -= UTFmax;	if(p < h->start)		p = h->start;	if(!hreadbuf(h, p) || h->pos == h->stop)		return -1;	return *h->pos++;} inthungetc(Hio *h){	if(h->state == Hend)		h->state = Hread;	else if(h->state == Hread)		h->pos--;	if(h->pos < h->start || h->state != Hread){		h->state = Herr;		h->pos = h->stop;		return -1;	}	return 0;} /* * fill the buffer, saving contents from vsave onwards. * nothing is saved if vsave is nil. * returns the beginning of the buffer. * * understands message body sizes and chunked transfer encoding */void *hreadbuf(Hio *h, void *vsave){	Hio *hh;	uchar *save;	int c, in, cpy, dpos; 	save = vsave;	if(save && (save < h->start || save > h->stop)	|| h->state != Hread && h->state != Hend){		h->state = Herr;		h->pos = h->stop;		return nil;	} 	dpos = 0;	if(save && h->pos > save)		dpos = h->pos - save;	cpy = 0;	if(save){		cpy = h->stop - save;		memmove(h->start, save, cpy);	}	h->seek += h->stop - h->start - cpy;	h->pos = h->start + dpos; 	in = Hsize - cpy;	if(h->state == Hend)		in = 0;	else if(in > h->bodylen)		in = h->bodylen; 	/*	 * for chunked encoding, fill buffer,	 * then read in new chunk length and wipe out that line	 */	hh = h->hh;	if(hh != nil){		if(!in && h->xferenc && h->state != Hend){			if(h->xferenc == 2){				c = hgetc(hh);				if(c == '\r')					c = hgetc(hh);				if(c != '\n'){					h->pos = h->stop;					h->state = Herr;					return nil;				}			}			h->xferenc = 2;			in = 0;			while((c = hgetc(hh)) != '\n'){				if(c >= '0' && c <= '9')					c -= '0';				else if(c >= 'a' && c <= 'f')					c -= 'a' - 10;				else if(c >= 'A' && c <= 'F')					c -= 'A' - 10;				else					break;				in = in * 16 + c;			}			while(c != '\n'){				if(c < 0){					h->pos = h->stop;					h->state = Herr;					return nil;				}				c = hgetc(hh);			}			h->bodylen = in; 			in = Hsize - cpy;			if(in > h->bodylen)				in = h->bodylen;		}		if(in){			while(hh->pos + in > hh->stop){				if(hreadbuf(hh, hh->pos) == nil){					h->pos = h->stop;					h->state = Herr;					return nil;				}			}			memmove(h->start + cpy, hh->pos, in);			hh->pos += in;		}	}else if(in){		if((in = read(h->fd, h->start + cpy, in)) < 0){			h->state = Herr;			h->pos = h->stop;			return nil;		}	}	if(in == 0)		h->state = Hend; 	h->bodylen -= in; 	h->stop = h->start + cpy + in;	*h->stop = '\0';	if(h->pos == h->stop)		return nil;	return h->start;} inthbuflen(Hio *h, void *p){	return h->stop - (uchar*)p;} /* * prepare to receive a message body * len is the content length (~0 => unspecified) * te is the transfer encoding * returns < 0 if setup failed */Hio*hbodypush(Hio *hh, ulong len, HFields *te){	Hio *h;	int xe; 	if(hh->state != Hread)		return nil;	xe = 0;	if(te != nil){		if(te->params != nil || te->next != nil)			return nil;		if(cistrcmp(te->s, "chunked") == 0){			xe = 1;			len = 0;		}else if(cistrcmp(te->s, "identity") == 0){			;		}else			return nil;	} 	h = malloc(sizeof *h);	if(h == nil)		return nil; 	h->hh = hh;	h->fd = -1;	h->seek = 0;	h->state = Hread;	h->xferenc = xe;	h->start = h->buf + 16;		/* leave space for chunk length */	h->stop = h->pos = h->start;	*h->pos = '\0';	h->bodylen = len;	return h;} /* * dump the state of the io buffer into a string */char *hunload(Hio *h){	uchar *p, *t, *stop, *buf;	int ne, n, c; 	stop = h->stop;	ne = 0;	for(p = h->pos; p < stop; p++){		c = *p;		if(c == 0x80)			ne++;	}	p = h->pos; 	n = (stop - p) + ne + 3;	buf = mallocz(n, 1);	if(buf == nil)		return nil;	buf[0] = hstates[h->state];	buf[1] = hxfers[h->xferenc]; 	t = &buf[2];	for(; p < stop; p++){		c = *p;		if(c == 0 || c == 0x80){			*t++ = 0x80;			if(c == 0x80)				*t++ = 0x80;		}else			*t++ = c;	}	*t++ = '\0';	if(t != buf + n)		return nil;	return (char*)buf;} /* * read the io buffer state from a string */inthload(Hio *h, char *buf){	uchar *p, *t, *stop;	char *s;	int c; 	s = strchr(hstates, buf[0]);	if(s == nil)		return -1;	h->state = s - hstates; 	s = strchr(hxfers, buf[1]);	if(s == nil)		return -1;	h->xferenc = s - hxfers; 	t = h->start;	stop = t + Hsize;	for(p = (uchar*)&buf[2]; c = *p; p++){		if(c == 0x80){			if(p[1] != 0x80)				c = 0;			else				p++;		}		*t++ = c;		if(t >= stop)			return -1;	}	*t = '\0';	h->pos = h->start;	h->stop = t;	h->seek = 0;	return 0;} voidhclose(Hio *h){	if(h->fd >= 0){		if(h->state == Hwrite)			hxferenc(h, 0);		close(h->fd);	}	h->stop = h->pos = nil;	h->fd = -1;} /* * flush the buffer and possibly change encoding modes */inthxferenc(Hio *h, int on){	if(h->xferenc && !on && h->pos != h->start)		hflush(h);	if(_hflush(h, 1, 0) < 0)		return -1;	h->xferenc = !!on;	return 0;} inthputc(Hio *h, int c){	uchar *p; 	p = h->pos;	if(p < h->stop){		h->pos = p + 1;		return *p = c;	}	if(hflush(h) < 0)		return -1;	return *h->pos++ = c;} static intfmthflush(Fmt *f){	Hio *h; 	h = f->farg;	h->pos = f->to;	if(hflush(h) < 0)		return 0;	f->stop = h->stop;	f->to = h->pos;	f->start = h->pos;	return 1;} inthvprint(Hio *h, char *fmt, va_list args){	int n;	Fmt f; 	f.runes = 0;	f.stop = h->stop;	f.to = h->pos;	f.start = h->pos;	f.flush = fmthflush;	f.farg = h;	f.nfmt = 0;//	fmtlocaleinit(&f, nil, nil, nil);	n = fmtvprint(&f, fmt, args);	h->pos = f.to;	return n;} inthprint(Hio *h, char *fmt, ...){	int n;	va_list arg; 	va_start(arg, fmt);	n = hvprint(h, fmt, arg);	va_end(arg);	return n;} static int_hflush(Hio *h, int force, int dolength){	uchar *s;	int w; 	if(h == nil)		return -1;	if(h->state != Hwrite){		h->state = Herr;		h->stop = h->pos;		return -1;	}	s = h->start;	w = h->pos - s;	if(w == 0 && !force)		return 0;	if(h->xferenc){		*--s = '\n';		*--s = '\r';		do{			*--s = "0123456789abcdef"[w & 0xf];			w >>= 4;		}while(w);		h->pos[0] = '\r';		h->pos[1] = '\n';		w = &h->pos[2] - s;	}	if(dolength)		fprint(h->fd, "Content-Length: %d\r\n\r\n", w);	if(write(h->fd, s, w) != w){		h->state = Herr;		h->stop = h->pos;		return -1;	}	h->seek += w;	h->pos = h->start;	return 0;} inthflush(Hio *h){	return _hflush(h, 0, 0);} inthlflush(Hio* h){	return _hflush(h, 0, 1);} inthwrite(Hio *h, void *vbuf, int len){	uchar *buf;	int n, m; 	buf = vbuf;	n = len;	if(n < 0 || h->state != Hwrite){		h->state = Herr;		h->stop = h->pos;		return -1;	}	if(h->pos + n >= h->stop){		if(h->start != h->pos)			if(hflush(h) < 0)				return -1;		while(h->pos + n >= h->stop){			m = h->stop - h->pos;			if(h->xferenc){				memmove(h->pos, buf, m);				h->pos += m;				if(hflush(h) < 0)					return -1;			}else{				if(write(h->fd, buf, m) != m){					h->state = Herr;					h->stop = h->pos;					return -1;				}				h->seek += m;			}			n -= m;			buf += m;		}	}	memmove(h->pos, buf, n);	h->pos += n;	return len;}