123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- #include <u.h>
- #include <libc.h>
- #include <httpd.h>
- 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->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;
- }
|