Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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 == 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;
}