2003-11-23 18:19:35 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <httpd.h>
|
|
|
|
|
|
|
|
static char hstates[] = "nrewE";
|
|
|
|
static char hxfers[] = " x";
|
2007-04-21 19:48:16 +00:00
|
|
|
static int _hflush(Hio*, int, int);
|
2003-11-23 18:19:35 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2004-03-21 04:33:13 +00:00
|
|
|
}else if(in){
|
|
|
|
if((in = read(h->fd, h->start + cpy, in)) < 0){
|
|
|
|
h->state = Herr;
|
|
|
|
h->pos = h->stop;
|
|
|
|
return nil;
|
|
|
|
}
|
2003-11-23 18:19:35 +00:00
|
|
|
}
|
|
|
|
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)
|
2004-05-05 04:22:16 +00:00
|
|
|
return -1;
|
2003-11-23 18:19:35 +00:00
|
|
|
h->state = s - hstates;
|
|
|
|
|
|
|
|
s = strchr(hxfers, buf[1]);
|
|
|
|
if(s == nil)
|
2004-12-27 00:13:04 +00:00
|
|
|
return -1;
|
2003-11-23 18:19:35 +00:00
|
|
|
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)
|
2004-05-05 04:22:16 +00:00
|
|
|
return -1;
|
2003-11-23 18:19:35 +00:00
|
|
|
}
|
|
|
|
*t = '\0';
|
|
|
|
h->pos = h->start;
|
|
|
|
h->stop = t;
|
|
|
|
h->seek = 0;
|
2004-05-05 04:22:16 +00:00
|
|
|
return 0;
|
2003-11-23 18:19:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2007-04-21 19:48:16 +00:00
|
|
|
if(_hflush(h, 1, 0) < 0)
|
2003-11-23 18:19:35 +00:00
|
|
|
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;
|
2007-04-21 19:48:16 +00:00
|
|
|
// fmtlocaleinit(&f, nil, nil, nil);
|
2005-02-08 21:03:36 +00:00
|
|
|
n = fmtvprint(&f, fmt, args);
|
2003-11-23 18:19:35 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2004-12-27 00:13:04 +00:00
|
|
|
static int
|
2007-04-21 19:48:16 +00:00
|
|
|
_hflush(Hio *h, int force, int dolength)
|
2003-11-23 18:19:35 +00:00
|
|
|
{
|
|
|
|
uchar *s;
|
|
|
|
int w;
|
|
|
|
|
|
|
|
if(h->state != Hwrite){
|
|
|
|
h->state = Herr;
|
|
|
|
h->stop = h->pos;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
s = h->start;
|
|
|
|
w = h->pos - s;
|
2007-04-21 19:48:16 +00:00
|
|
|
if(w == 0 && !force)
|
|
|
|
return 0;
|
2003-11-23 18:19:35 +00:00
|
|
|
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;
|
|
|
|
}
|
2004-12-27 00:13:04 +00:00
|
|
|
if(dolength)
|
|
|
|
fprint(h->fd, "Content-Length: %d\r\n\r\n", w);
|
2003-11-23 18:19:35 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2004-12-27 00:13:04 +00:00
|
|
|
int
|
|
|
|
hflush(Hio *h)
|
|
|
|
{
|
2007-04-21 19:48:16 +00:00
|
|
|
return _hflush(h, 0, 0);
|
2004-12-27 00:13:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
hlflush(Hio* h)
|
|
|
|
{
|
2007-04-21 19:48:16 +00:00
|
|
|
return _hflush(h, 0, 1);
|
2004-12-27 00:13:04 +00:00
|
|
|
}
|
|
|
|
|
2003-11-23 18:19:35 +00:00
|
|
|
int
|
|
|
|
hwrite(Hio *h, void *vbuf, int len)
|
|
|
|
{
|
2005-02-16 17:13:41 +00:00
|
|
|
uchar *buf;
|
2003-11-23 18:19:35 +00:00
|
|
|
int n, m;
|
|
|
|
|
|
|
|
buf = vbuf;
|
|
|
|
n = len;
|
|
|
|
if(n < 0 || h->state != Hwrite){
|
|
|
|
h->state = Herr;
|
|
|
|
h->stop = h->pos;
|
|
|
|
return -1;
|
|
|
|
}
|
2005-02-16 17:13:41 +00:00
|
|
|
if(h->pos + n >= h->stop){
|
|
|
|
if(h->start != h->pos)
|
|
|
|
if(hflush(h) < 0)
|
2003-11-23 18:19:35 +00:00
|
|
|
return -1;
|
2005-02-16 17:13:41 +00:00
|
|
|
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;
|
2003-11-23 18:19:35 +00:00
|
|
|
}
|
2005-02-16 17:13:41 +00:00
|
|
|
n -= m;
|
|
|
|
buf += m;
|
2003-11-23 18:19:35 +00:00
|
|
|
}
|
|
|
|
}
|
2005-02-16 17:13:41 +00:00
|
|
|
memmove(h->pos, buf, n);
|
|
|
|
h->pos += n;
|
2003-11-23 18:19:35 +00:00
|
|
|
return len;
|
|
|
|
}
|