X-Git-Url: http://www.dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fhtparser.c;h=92445d7bf85f7554a7a6ef0066d28314963f63f7;hb=b71ad67f41a9e99bd243d57b5038de28f1696f27;hp=4086f7926eacc01fc17c15c5d4c3cf757ef751f0;hpb=ae13a8b18216c45b0e75060a4df65659e572822a;p=ashd.git diff --git a/src/htparser.c b/src/htparser.c index 4086f79..92445d7 100644 --- a/src/htparser.c +++ b/src/htparser.c @@ -41,6 +41,7 @@ static int plex; static char *pidfile = NULL; static int daemonize, usesyslog; +struct mtbuf listeners; static void trimx(struct hthead *req) { @@ -148,6 +149,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]; @@ -226,40 +280,69 @@ static int http10keep(struct hthead *req, struct hthead *resp) } } +static char *connid(void) +{ + static struct charbuf cur; + int i; + char *ret; + + for(i = 0; i < cur.d; i++) { + if((++cur.b[i]) > 'Z') + cur.b[i] = 'A'; + else + goto done; + } + bufadd(cur, 'A'); +done: + ret = memcpy(smalloc(cur.d + 1), cur.b, cur.d); + ret[cur.d] = 0; + return(ret); +} + void serve(FILE *in, struct conn *conn) { int pfds[2]; FILE *out; struct hthead *req, *resp; - char *hd; + char *hd, *id; off_t dlen; int keep; + id = connid(); out = NULL; req = resp = NULL; - while(1) { + while(plex >= 0) { if((req = parsereq(in)) == NULL) break; if(!canonreq(req)) break; + headappheader(req, "X-Ash-Connection-ID", id); if((conn->initreq != NULL) && conn->initreq(conn, req)) break; - if(block(plex, EV_WRITE, 60) <= 0) + if((plex < 0) || block(plex, EV_WRITE, 60) <= 0) break; if(socketpair(PF_UNIX, SOCK_STREAM, 0, pfds)) break; if(sendreq(plex, req, pfds[0])) break; close(pfds[0]); - out = mtstdopen(pfds[1], 1, 600, "r+"); + out = mtstdopen(pfds[1], 1, 600, "r+", NULL); - 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)) @@ -274,7 +357,7 @@ 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")) { + if(!strcasecmp(req->ver, "HTTP/1.0")) { if(!strcasecmp(req->method, "head")) { keep = http10keep(req, resp); writeresp(in, resp); @@ -295,7 +378,7 @@ void serve(FILE *in, struct conn *conn) } 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"); @@ -337,36 +420,51 @@ void serve(FILE *in, struct conn *conn) if(resp != NULL) freehthead(resp); fclose(in); + free(id); } static void plexwatch(struct muth *muth, va_list args) { vavar(int, fd); char *buf; - int ret; + int i, s, ret; + s = 0; while(1) { - block(fd, EV_READ, 0); + if(block(fd, EV_READ, 0) == 0) + break; buf = smalloc(65536); ret = recv(fd, buf, 65536, 0); if(ret < 0) { flog(LOG_WARNING, "received error on rootplex read channel: %s", strerror(errno)); exit(1); } else if(ret == 0) { - exit(0); + s = 1; + free(buf); + break; } /* Maybe I'd like to implement some protocol in this direction * some day... */ free(buf); } + shutdown(plex, SHUT_RDWR); + for(i = 0; i < listeners.d; i++) { + if(listeners.b[i] == muth) + bufdel(listeners, i); + } + if(s) { + flog(LOG_INFO, "root handler exited, so shutting down listening..."); + while(listeners.d > 0) + resume(listeners.b[0], 0); + } } static void initroot(void *uu) { int fd; + setsid(); if(daemonize) { - setsid(); chdir("/"); if((fd = open("/dev/null", O_RDWR)) >= 0) { dup2(fd, 0); @@ -433,9 +531,14 @@ static void addport(char *spec) buffree(vals); } +static void sighandler(int sig) +{ + exitioloop(1); +} + int main(int argc, char **argv) { - int c; + int c, d; int i, s1; char *root; FILE *pidout; @@ -487,7 +590,7 @@ int main(int argc, char **argv) flog(LOG_ERR, "could not spawn root multiplexer: %s", strerror(errno)); return(1); } - mustart(plexwatch, plex); + bufadd(listeners, mustart(plexwatch, plex)); pidout = NULL; if(pidfile != NULL) { if((pidout = fopen(pidfile, "w")) == NULL) { @@ -514,6 +617,9 @@ int main(int argc, char **argv) } } signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + signal(SIGINT, sighandler); + signal(SIGTERM, sighandler); if(daemonize) { daemon(0, 0); } @@ -521,6 +627,22 @@ int main(int argc, char **argv) fprintf(pidout, "%i\n", getpid()); fclose(pidout); } - ioloop(); + d = 0; + while(!d) { + switch(ioloop()) { + case 0: + d = 1; + break; + case 1: + if(listeners.d > 0) { + while(listeners.d > 0) + resume(listeners.b[0], 0); + flog(LOG_INFO, "no longer listening"); + } else { + d = 1; + } + break; + } + } return(0); }