#include #include #include static char hstates[] = "nrewE"; static char hxfers[] = " x"; static int _hflush(Hio*, int, int); int hinit(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; } int hiserror(Hio *h) { return h->state == Herr; } int hgetc(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++; } int hungetc(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; } int hbuflen(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 */ int hload(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; } void hclose(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 */ int hxferenc(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; } int hputc(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 int fmthflush(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; } int hvprint(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; } int hprint(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; } int hflush(Hio *h) { return _hflush(h, 0, 0); } int hlflush(Hio* h) { return _hflush(h, 0, 1); } int hwrite(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; }