diff options
Diffstat (limited to 'src/libhttpd/hio.c')
-rw-r--r-- | src/libhttpd/hio.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/src/libhttpd/hio.c b/src/libhttpd/hio.c new file mode 100644 index 00000000..7cfb5557 --- /dev/null +++ b/src/libhttpd/hio.c @@ -0,0 +1,473 @@ +#include <u.h> +#include <libc.h> +#include <httpd.h> + +static char hstates[] = "nrewE"; +static char hxfers[] = " x"; + +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 && (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 0; + h->state = s - hstates; + + s = strchr(hxfers, buf[1]); + if(s == nil) + return 0; + 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 0; + } + *t = '\0'; + h->pos = h->start; + h->stop = t; + h->seek = 0; + return 1; +} + +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) < 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; + f.args = args; + n = dofmt(&f, fmt); + 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; +} + +int +hflush(Hio *h) +{ + uchar *s; + int w; + + if(h->state != Hwrite){ + h->state = Herr; + h->stop = h->pos; + return -1; + } + s = h->start; + w = h->pos - s; + 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(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 +hwrite(Hio *h, void *vbuf, int len) +{ + uchar *pos, *buf; + int n, m; + + buf = vbuf; + n = len; + if(n < 0 || h->state != Hwrite){ + h->state = Herr; + h->stop = h->pos; + return -1; + } + pos = h->pos; + if(pos + n >= h->stop){ + m = pos - h->start; + if(m){ + m = Hsize - m; + if(m){ + memmove(pos, buf, m); + buf += m; + n -= m; + } + if(write(h->fd, h->start, Hsize) != Hsize){ + h->state = Herr; + h->stop = h->pos; + return -1; + } + h->seek += Hsize; + } + m = n % Hsize; + n -= m; + if(n != 0 && write(h->fd, buf, n) != n){ + h->state = Herr; + h->stop = h->pos; + return -1; + } + h->seek += n; + buf += n; + pos = h->pos = h->start; + n = m; + } + memmove(pos, buf, n); + h->pos = pos + n; + return len; +} |