xref: /plan9/sys/src/libhttpd/hio.c (revision e02f7f02bd837384880240d3ead0b1c0930eacbe)
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
hinit(Hio * h,int fd,int mode)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
hiserror(Hio * h)29 hiserror(Hio *h)
30 {
31 	return h->state == Herr;
32 }
33 
34 int
hgetc(Hio * h)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
hungetc(Hio * h)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 *
hreadbuf(Hio * h,void * vsave)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
hbuflen(Hio * h,void * p)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*
hbodypush(Hio * hh,ulong len,HFields * te)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 *
hunload(Hio * h)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
hload(Hio * h,char * buf)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
hclose(Hio * h)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
hxferenc(Hio * h,int on)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
hputc(Hio * h,int c)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
fmthflush(Fmt * f)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
hvprint(Hio * h,char * fmt,va_list args)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
hprint(Hio * h,char * fmt,...)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
_hflush(Hio * h,int force,int dolength)398 _hflush(Hio *h, int force, int dolength)
399 {
400 	uchar *s;
401 	int w;
402 
403 	if(h == nil)
404 		return -1;
405 	if(h->state != Hwrite){
406 		h->state = Herr;
407 		h->stop = h->pos;
408 		return -1;
409 	}
410 	s = h->start;
411 	w = h->pos - s;
412 	if(w == 0 && !force)
413 		return 0;
414 	if(h->xferenc){
415 		*--s = '\n';
416 		*--s = '\r';
417 		do{
418 			*--s = "0123456789abcdef"[w & 0xf];
419 			w >>= 4;
420 		}while(w);
421 		h->pos[0] = '\r';
422 		h->pos[1] = '\n';
423 		w = &h->pos[2] - s;
424 	}
425 	if(dolength)
426 		fprint(h->fd, "Content-Length: %d\r\n\r\n", w);
427 	if(write(h->fd, s, w) != w){
428 		h->state = Herr;
429 		h->stop = h->pos;
430 		return -1;
431 	}
432 	h->seek += w;
433 	h->pos = h->start;
434 	return 0;
435 }
436 
437 int
hflush(Hio * h)438 hflush(Hio *h)
439 {
440 	return _hflush(h, 0, 0);
441 }
442 
443 int
hlflush(Hio * h)444 hlflush(Hio* h)
445 {
446 	return _hflush(h, 0, 1);
447 }
448 
449 int
hwrite(Hio * h,void * vbuf,int len)450 hwrite(Hio *h, void *vbuf, int len)
451 {
452 	uchar *buf;
453 	int n, m;
454 
455 	buf = vbuf;
456 	n = len;
457 	if(n < 0 || h->state != Hwrite){
458 		h->state = Herr;
459 		h->stop = h->pos;
460 		return -1;
461 	}
462 	if(h->pos + n >= h->stop){
463 		if(h->start != h->pos)
464 			if(hflush(h) < 0)
465 				return -1;
466 		while(h->pos + n >= h->stop){
467 			m = h->stop - h->pos;
468 			if(h->xferenc){
469 				memmove(h->pos, buf, m);
470 				h->pos += m;
471 				if(hflush(h) < 0)
472 					return -1;
473 			}else{
474 				if(write(h->fd, buf, m) != m){
475 					h->state = Herr;
476 					h->stop = h->pos;
477 					return -1;
478 				}
479 				h->seek += m;
480 			}
481 			n -= m;
482 			buf += m;
483 		}
484 	}
485 	memmove(h->pos, buf, n);
486 	h->pos += n;
487 	return len;
488 }
489