/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <httpd.h> static char hstates[] = "nrewE";static char hxfers[] = " x";static int _hflush(Hio*, int, int); inthinit(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;} inthiserror(Hio *h){ return h->state == Herr;} inthgetc(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++;} inthungetc(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;} inthbuflen(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 */inthload(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;} voidhclose(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 */inthxferenc(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;} inthputc(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 intfmthflush(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;} inthvprint(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;} inthprint(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;} inthflush(Hio *h){ return _hflush(h, 0, 0);} inthlflush(Hio* h){ return _hflush(h, 0, 1);} inthwrite(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;}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
#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;
}