X-Git-Url: http://www.dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fhtparser.c;h=5dfed8bc135f4daadbae4a5e9e257a76c1cd283a;hb=2dbb9937cfe0d4f4a059181621c52d0fbd0c7eb1;hp=a195fab3d81bd885771c06085ba0510b95a23a51;hpb=056f947352274dd1f6ec51b03c423c12e3828623;p=ashd.git diff --git a/src/htparser.c b/src/htparser.c index a195fab..5dfed8b 100644 --- a/src/htparser.c +++ b/src/htparser.c @@ -148,6 +148,59 @@ static off_t passdata(FILE *in, FILE *out, off_t max) return(total); } +static int recvchunks(FILE *in, FILE *out) +{ + char buf[8192]; + size_t read, chlen; + int c, r; + + while(1) { + chlen = 0; + r = 0; + while(1) { + c = getc(in); + if(c == 10) { + if(!r) + return(-1); + break; + } else if(c == 13) { + } else if((c >= '0') && (c <= '9')) { + chlen = (chlen << 4) + (c - '0'); + r = 1; + } else if((c >= 'A') && (c <= 'F')) { + chlen = (chlen << 4) + (c + 10 - 'A'); + r = 1; + } else if((c >= 'a') && (c <= 'f')) { + chlen = (chlen << 4) + (c + 10 - 'a'); + r = 1; + } else { + /* XXX: Technically, there may be chunk extensions to + * be read, but since that will likely never actually + * happen in practice, I can just as well add support + * for that if it actually does become relevant. */ + return(-1); + } + } + if(chlen == 0) + break; + while(chlen > 0) { + read = fread(buf, 1, min(sizeof(buf), chlen), in); + if(feof(in) || ferror(in)) + return(-1); + if(fwrite(buf, 1, read, out) != read) + return(-1); + chlen -= read; + } + if((getc(in) != 13) || (getc(in) != 10)) + return(-1); + } + /* XXX: Technically, there may be trailers to be read, but that's + * just about as likely as chunk extensions. */ + if((getc(in) != 13) || (getc(in) != 10)) + return(-1); + return(0); +} + static int passchunks(FILE *in, FILE *out) { char buf[8192]; @@ -212,6 +265,20 @@ static int canonreq(struct hthead *req) return(0); } +static int http10keep(struct hthead *req, struct hthead *resp) +{ + int fc; + + fc = hasheader(resp, "connection", "close"); + headrmheader(resp, "connection"); + if(!fc && hasheader(req, "connection", "keep-alive")) { + headappheader(resp, "Connection", "Keep-Alive"); + return(1); + } else { + return(0); + } +} + void serve(FILE *in, struct conn *conn) { int pfds[2]; @@ -219,6 +286,7 @@ void serve(FILE *in, struct conn *conn) struct hthead *req, *resp; char *hd; off_t dlen; + int keep; out = NULL; req = resp = NULL; @@ -240,11 +308,19 @@ void serve(FILE *in, struct conn *conn) close(pfds[0]); out = mtstdopen(pfds[1], 1, 600, "r+"); - if((hd = getheader(req, "content-length")) != NULL) { - dlen = atoo(hd); - if(dlen > 0) { - if(passdata(in, out, dlen) != dlen) + if(getheader(req, "content-type") != NULL) { + if((hd = getheader(req, "content-length")) != NULL) { + dlen = atoo(hd); + if(dlen > 0) { + if(passdata(in, out, dlen) != dlen) + break; + } + } else if(((hd = getheader(req, "transfer-encoding")) != NULL) && !strcasecmp(hd, "chunked")) { + if(recvchunks(in, out)) break; + } else { + /* Ignore rather than abort, to be kinder to broken clients. */ + headrmheader(req, "content-type"); } } if(fflush(out)) @@ -259,25 +335,28 @@ void serve(FILE *in, struct conn *conn) if(!getheader(resp, "server")) headappheader(resp, "Server", sprintf3("ashd/%s", VERSION)); - if(!strcmp(req->ver, "HTTP/1.0")) { - writeresp(in, resp); - fprintf(in, "\r\n"); + if(!strcasecmp(req->ver, "HTTP/1.0")) { if(!strcasecmp(req->method, "head")) { - if(!hasheader(req, "connection", "keep-alive")) - break; + keep = http10keep(req, resp); + writeresp(in, resp); + fprintf(in, "\r\n"); } else if((hd = getheader(resp, "content-length")) != NULL) { + keep = http10keep(req, resp); dlen = atoo(hd); + writeresp(in, resp); + fprintf(in, "\r\n"); if(passdata(out, in, dlen) != dlen) break; - if(!hasheader(req, "connection", "keep-alive")) - break; } else { + headrmheader(resp, "connection"); + writeresp(in, resp); + fprintf(in, "\r\n"); passdata(out, in, -1); break; } - if(hasheader(req, "connection", "close") || hasheader(resp, "connection", "close")) + if(!keep) break; - } else if(!strcmp(req->ver, "HTTP/1.1")) { + } else if(!strcasecmp(req->ver, "HTTP/1.1")) { if(!strcasecmp(req->method, "head")) { writeresp(in, resp); fprintf(in, "\r\n");