Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <httpd.h>
5 static char hstates[] = "nrewE";
6 static char hxfers[] = " x";
7 static int _hflush(Hio*, int, int);
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 }
28 int
29 hiserror(Hio *h)
30 {
31 return h->state == Herr;
32 }
34 int
35 hgetc(Hio *h)
36 {
37 uchar *p;
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 }
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 }
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;
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 }
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;
100 in = Hsize - cpy;
101 if(h->state == Hend)
102 in = 0;
103 else if(in > h->bodylen)
104 in = h->bodylen;
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;
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;
136 while(c != '\n'){
137 if(c < 0){
138 h->pos = h->stop;
139 h->state = Herr;
140 return nil;
142 c = hgetc(hh);
144 h->bodylen = in;
146 in = Hsize - cpy;
147 if(in > h->bodylen)
148 in = h->bodylen;
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;
158 memmove(h->start + cpy, hh->pos, in);
159 hh->pos += in;
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;
168 if(in == 0)
169 h->state = Hend;
171 h->bodylen -= in;
173 h->stop = h->start + cpy + in;
174 *h->stop = '\0';
175 if(h->pos == h->stop)
176 return nil;
177 return h->start;
180 int
181 hbuflen(Hio *h, void *p)
183 return h->stop - (uchar*)p;
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)
195 Hio *h;
196 int xe;
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){
209 }else
210 return nil;
213 h = malloc(sizeof *h);
214 if(h == nil)
215 return nil;
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;
229 /*
230 * dump the state of the io buffer into a string
231 */
232 char *
233 hunload(Hio *h)
235 uchar *p, *t, *stop, *buf;
236 int ne, n, c;
238 stop = h->stop;
239 ne = 0;
240 for(p = h->pos; p < stop; p++){
241 c = *p;
242 if(c == 0x80)
243 ne++;
245 p = h->pos;
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];
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;
264 *t++ = '\0';
265 if(t != buf + n)
266 return nil;
267 return (char*)buf;
270 /*
271 * read the io buffer state from a string
272 */
273 int
274 hload(Hio *h, char *buf)
276 uchar *p, *t, *stop;
277 char *s;
278 int c;
280 s = strchr(hstates, buf[0]);
281 if(s == nil)
282 return -1;
283 h->state = s - hstates;
285 s = strchr(hxfers, buf[1]);
286 if(s == nil)
287 return -1;
288 h->xferenc = s - hxfers;
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++;
299 *t++ = c;
300 if(t >= stop)
301 return -1;
303 *t = '\0';
304 h->pos = h->start;
305 h->stop = t;
306 h->seek = 0;
307 return 0;
310 void
311 hclose(Hio *h)
313 if(h->fd >= 0){
314 if(h->state == Hwrite)
315 hxferenc(h, 0);
316 close(h->fd);
318 h->stop = h->pos = nil;
319 h->fd = -1;
322 /*
323 * flush the buffer and possibly change encoding modes
324 */
325 int
326 hxferenc(Hio *h, int on)
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;
336 int
337 hputc(Hio *h, int c)
339 uchar *p;
341 p = h->pos;
342 if(p < h->stop){
343 h->pos = p + 1;
344 return *p = c;
346 if(hflush(h) < 0)
347 return -1;
348 return *h->pos++ = c;
351 static int
352 fmthflush(Fmt *f)
354 Hio *h;
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;
366 int
367 hvprint(Hio *h, char *fmt, va_list args)
369 int n;
370 Fmt f;
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;
385 int
386 hprint(Hio *h, char *fmt, ...)
388 int n;
389 va_list arg;
391 va_start(arg, fmt);
392 n = hvprint(h, fmt, arg);
393 va_end(arg);
394 return n;
397 static int
398 _hflush(Hio *h, int force, int dolength)
400 uchar *s;
401 int w;
403 if(h->state != Hwrite){
404 h->state = Herr;
405 h->stop = h->pos;
406 return -1;
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;
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;
430 h->seek += w;
431 h->pos = h->start;
432 return 0;
435 int
436 hflush(Hio *h)
438 return _hflush(h, 0, 0);
441 int
442 hlflush(Hio* h)
444 return _hflush(h, 0, 1);
447 int
448 hwrite(Hio *h, void *vbuf, int len)
450 uchar *buf;
451 int n, m;
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;
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;
477 h->seek += m;
479 n -= m;
480 buf += m;
483 memmove(h->pos, buf, n);
484 h->pos += n;
485 return len;