Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <httpd.h>
5 static char hstates[] = "nrewE";
6 static char hxfers[] = " x";
8 int
9 hinit(Hio *h, int fd, int mode)
10 {
11 if(fd == -1 || mode != Hread && mode != Hwrite)
12 return -1;
13 h->hh = nil;
14 h->fd = fd;
15 h->seek = 0;
16 h->state = mode;
17 h->start = h->buf + 16; /* leave space for chunk length */
18 h->stop = h->pos = h->start;
19 if(mode == Hread){
20 h->bodylen = ~0UL;
21 *h->pos = '\0';
22 }else
23 h->stop = h->start + Hsize;
24 return 0;
25 }
27 int
28 hiserror(Hio *h)
29 {
30 return h->state == Herr;
31 }
33 int
34 hgetc(Hio *h)
35 {
36 uchar *p;
38 p = h->pos;
39 if(p < h->stop){
40 h->pos = p + 1;
41 return *p;
42 }
43 p -= UTFmax;
44 if(p < h->start)
45 p = h->start;
46 if(!hreadbuf(h, p) || h->pos == h->stop)
47 return -1;
48 return *h->pos++;
49 }
51 int
52 hungetc(Hio *h)
53 {
54 if(h->state == Hend)
55 h->state = Hread;
56 else if(h->state == Hread)
57 h->pos--;
58 if(h->pos < h->start || h->state != Hread){
59 h->state = Herr;
60 h->pos = h->stop;
61 return -1;
62 }
63 return 0;
64 }
66 /*
67 * fill the buffer, saving contents from vsave onwards.
68 * nothing is saved if vsave is nil.
69 * returns the beginning of the buffer.
70 *
71 * understands message body sizes and chunked transfer encoding
72 */
73 void *
74 hreadbuf(Hio *h, void *vsave)
75 {
76 Hio *hh;
77 uchar *save;
78 int c, in, cpy, dpos;
80 save = vsave;
81 if(save && (save < h->start || save > h->stop)
82 || h->state != Hread && h->state != Hend){
83 h->state = Herr;
84 h->pos = h->stop;
85 return nil;
86 }
88 dpos = 0;
89 if(save && h->pos > save)
90 dpos = h->pos - save;
91 cpy = 0;
92 if(save){
93 cpy = h->stop - save;
94 memmove(h->start, save, cpy);
95 }
96 h->seek += h->stop - h->start - cpy;
97 h->pos = h->start + dpos;
99 in = Hsize - cpy;
100 if(h->state == Hend)
101 in = 0;
102 else if(in > h->bodylen)
103 in = h->bodylen;
105 /*
106 * for chunked encoding, fill buffer,
107 * then read in new chunk length and wipe out that line
108 */
109 hh = h->hh;
110 if(hh != nil){
111 if(!in && h->xferenc && h->state != Hend){
112 if(h->xferenc == 2){
113 c = hgetc(hh);
114 if(c == '\r')
115 c = hgetc(hh);
116 if(c != '\n'){
117 h->pos = h->stop;
118 h->state = Herr;
119 return nil;
122 h->xferenc = 2;
123 in = 0;
124 while((c = hgetc(hh)) != '\n'){
125 if(c >= '0' && c <= '9')
126 c -= '0';
127 else if(c >= 'a' && c <= 'f')
128 c -= 'a' - 10;
129 else if(c >= 'A' && c <= 'F')
130 c -= 'A' - 10;
131 else
132 break;
133 in = in * 16 + c;
135 while(c != '\n'){
136 if(c < 0){
137 h->pos = h->stop;
138 h->state = Herr;
139 return nil;
141 c = hgetc(hh);
143 h->bodylen = in;
145 in = Hsize - cpy;
146 if(in > h->bodylen)
147 in = h->bodylen;
149 if(in){
150 while(hh->pos + in > hh->stop){
151 if(hreadbuf(hh, hh->pos) == nil){
152 h->pos = h->stop;
153 h->state = Herr;
154 return nil;
157 memmove(h->start + cpy, hh->pos, in);
158 hh->pos += in;
160 }else if(in){
161 if((in = read(h->fd, h->start + cpy, in)) < 0){
162 h->state = Herr;
163 h->pos = h->stop;
164 return nil;
167 if(in == 0)
168 h->state = Hend;
170 h->bodylen -= in;
172 h->stop = h->start + cpy + in;
173 *h->stop = '\0';
174 if(h->pos == h->stop)
175 return nil;
176 return h->start;
179 int
180 hbuflen(Hio *h, void *p)
182 return h->stop - (uchar*)p;
185 /*
186 * prepare to receive a message body
187 * len is the content length (~0 => unspecified)
188 * te is the transfer encoding
189 * returns < 0 if setup failed
190 */
191 Hio*
192 hbodypush(Hio *hh, ulong len, HFields *te)
194 Hio *h;
195 int xe;
197 if(hh->state != Hread)
198 return nil;
199 xe = 0;
200 if(te != nil){
201 if(te->params != nil || te->next != nil)
202 return nil;
203 if(cistrcmp(te->s, "chunked") == 0){
204 xe = 1;
205 len = 0;
206 }else if(cistrcmp(te->s, "identity") == 0){
208 }else
209 return nil;
212 h = malloc(sizeof *h);
213 if(h == nil)
214 return nil;
216 h->hh = hh;
217 h->fd = -1;
218 h->seek = 0;
219 h->state = Hread;
220 h->xferenc = xe;
221 h->start = h->buf + 16; /* leave space for chunk length */
222 h->stop = h->pos = h->start;
223 *h->pos = '\0';
224 h->bodylen = len;
225 return h;
228 /*
229 * dump the state of the io buffer into a string
230 */
231 char *
232 hunload(Hio *h)
234 uchar *p, *t, *stop, *buf;
235 int ne, n, c;
237 stop = h->stop;
238 ne = 0;
239 for(p = h->pos; p < stop; p++){
240 c = *p;
241 if(c == 0x80)
242 ne++;
244 p = h->pos;
246 n = (stop - p) + ne + 3;
247 buf = mallocz(n, 1);
248 if(buf == nil)
249 return nil;
250 buf[0] = hstates[h->state];
251 buf[1] = hxfers[h->xferenc];
253 t = &buf[2];
254 for(; p < stop; p++){
255 c = *p;
256 if(c == 0 || c == 0x80){
257 *t++ = 0x80;
258 if(c == 0x80)
259 *t++ = 0x80;
260 }else
261 *t++ = c;
263 *t++ = '\0';
264 if(t != buf + n)
265 return nil;
266 return (char*)buf;
269 /*
270 * read the io buffer state from a string
271 */
272 int
273 hload(Hio *h, char *buf)
275 uchar *p, *t, *stop;
276 char *s;
277 int c;
279 s = strchr(hstates, buf[0]);
280 if(s == nil)
281 return -1;
282 h->state = s - hstates;
284 s = strchr(hxfers, buf[1]);
285 if(s == nil)
286 return -1;
287 h->xferenc = s - hxfers;
289 t = h->start;
290 stop = t + Hsize;
291 for(p = (uchar*)&buf[2]; c = *p; p++){
292 if(c == 0x80){
293 if(p[1] != 0x80)
294 c = 0;
295 else
296 p++;
298 *t++ = c;
299 if(t >= stop)
300 return -1;
302 *t = '\0';
303 h->pos = h->start;
304 h->stop = t;
305 h->seek = 0;
306 return 0;
309 void
310 hclose(Hio *h)
312 if(h->fd >= 0){
313 if(h->state == Hwrite)
314 hxferenc(h, 0);
315 close(h->fd);
317 h->stop = h->pos = nil;
318 h->fd = -1;
321 /*
322 * flush the buffer and possibly change encoding modes
323 */
324 int
325 hxferenc(Hio *h, int on)
327 if(h->xferenc && !on && h->pos != h->start)
328 hflush(h);
329 if(hflush(h) < 0)
330 return -1;
331 h->xferenc = !!on;
332 return 0;
335 int
336 hputc(Hio *h, int c)
338 uchar *p;
340 p = h->pos;
341 if(p < h->stop){
342 h->pos = p + 1;
343 return *p = c;
345 if(hflush(h) < 0)
346 return -1;
347 return *h->pos++ = c;
350 static int
351 fmthflush(Fmt *f)
353 Hio *h;
355 h = f->farg;
356 h->pos = f->to;
357 if(hflush(h) < 0)
358 return 0;
359 f->stop = h->stop;
360 f->to = h->pos;
361 f->start = h->pos;
362 return 1;
365 int
366 hvprint(Hio *h, char *fmt, va_list args)
368 int n;
369 Fmt f;
371 f.runes = 0;
372 f.stop = h->stop;
373 f.to = h->pos;
374 f.start = h->pos;
375 f.flush = fmthflush;
376 f.farg = h;
377 f.nfmt = 0;
378 fmtlocaleinit(&f, nil, nil, nil);
379 n = fmtvprint(&f, fmt, args);
380 h->pos = f.to;
381 return n;
384 int
385 hprint(Hio *h, char *fmt, ...)
387 int n;
388 va_list arg;
390 va_start(arg, fmt);
391 n = hvprint(h, fmt, arg);
392 va_end(arg);
393 return n;
396 static int
397 _hflush(Hio *h, int dolength)
399 uchar *s;
400 int w;
402 if(h->state != Hwrite){
403 h->state = Herr;
404 h->stop = h->pos;
405 return -1;
407 s = h->start;
408 w = h->pos - s;
409 if(h->xferenc){
410 *--s = '\n';
411 *--s = '\r';
412 do{
413 *--s = "0123456789abcdef"[w & 0xf];
414 w >>= 4;
415 }while(w);
416 h->pos[0] = '\r';
417 h->pos[1] = '\n';
418 w = &h->pos[2] - s;
420 if(dolength)
421 fprint(h->fd, "Content-Length: %d\r\n\r\n", w);
422 if(write(h->fd, s, w) != w){
423 h->state = Herr;
424 h->stop = h->pos;
425 return -1;
427 h->seek += w;
428 h->pos = h->start;
429 return 0;
432 int
433 hflush(Hio *h)
435 return _hflush(h, 0);
438 int
439 hlflush(Hio* h)
441 return _hflush(h, 1);
444 int
445 hwrite(Hio *h, void *vbuf, int len)
447 uchar *buf;
448 int n, m;
450 buf = vbuf;
451 n = len;
452 if(n < 0 || h->state != Hwrite){
453 h->state = Herr;
454 h->stop = h->pos;
455 return -1;
457 if(h->pos + n >= h->stop){
458 if(h->start != h->pos)
459 if(hflush(h) < 0)
460 return -1;
461 while(h->pos + n >= h->stop){
462 m = h->stop - h->pos;
463 if(h->xferenc){
464 memmove(h->pos, buf, m);
465 h->pos += m;
466 if(hflush(h) < 0)
467 return -1;
468 }else{
469 if(write(h->fd, buf, m) != m){
470 h->state = Herr;
471 h->stop = h->pos;
472 return -1;
474 h->seek += m;
476 n -= m;
477 buf += m;
480 memmove(h->pos, buf, n);
481 h->pos += n;
482 return len;