doc: Documented htpipe.
[ashd.git] / src / htparser.c
index 73d9404..e281abd 100644 (file)
@@ -40,7 +40,6 @@
 #include "htparser.h"
 
 static int plex;
-static char *pidfile = NULL;
 static int daemonize, usesyslog;
 struct mtbuf listeners;
 
@@ -152,7 +151,7 @@ static off_t passdata(struct bufio *in, struct bufio *out, off_t max)
 
 static int recvchunks(struct bufio *in, struct bufio *out)
 {
-    size_t read, chlen;
+    ssize_t read, chlen;
     int c, r;
     
     while(1) {
@@ -305,14 +304,58 @@ done:
     return(ret);
 }
 
-void serve(struct bufio *in, struct conn *conn)
+static void passduplex(struct bufio *a, int afd, struct bufio *b, int bfd)
+{
+    struct selected pfd[4], sel;
+    struct bufio *sio;
+    int n, ev;
+    
+    while(!bioeof(a) && !bioeof(b)) {
+       biocopybuf(b, a);
+       biocopybuf(a, b);
+       n = 0;
+       if(!a->eof) {
+           ev = 0;
+           if(biorspace(a))
+               ev |= EV_READ;
+           if(biowdata(a))
+               ev |= EV_WRITE;
+           if(ev)
+               pfd[n++] = (struct selected){.fd = afd, .ev = ev};
+       }
+       if(!b->eof) {
+           ev = 0;
+           if(!b->eof && biorspace(b))
+               ev |= EV_READ;
+           if(biowdata(b))
+               ev |= EV_WRITE;
+           if(ev)
+               pfd[n++] = (struct selected){.fd = bfd, .ev = ev};
+       }
+       if((sel = mblock(600, n, pfd)).ev == 0)
+           break;
+       if(sel.fd == afd)
+           sio = a;
+       else if(sel.fd == bfd)
+           sio = b;
+       else
+           break;
+       if((sel.ev & EV_READ) && (biofillsome(sio) < 0))
+           break;
+       if((sel.ev & EV_WRITE) && (bioflushsome(sio) < 0))
+           break;
+    }
+}
+
+void serve(struct bufio *in, int infd, struct conn *conn)
 {
     int pfds[2];
-    struct bufio *out;
+    struct bufio *out, *dout;
+    struct stdiofd *outi;
     struct hthead *req, *resp;
     char *hd, *id;
     off_t dlen;
-    int keep;
+    int keep, duplex;
     
     id = connid();
     out = NULL;
@@ -335,7 +378,7 @@ void serve(struct bufio *in, struct conn *conn)
        if(sendreq(plex, req, pfds[0]))
            break;
        close(pfds[0]);
-       out = mtbioopen(pfds[1], 1, 600, "r+", NULL);
+       out = mtbioopen(pfds[1], 1, 600, "r+", &outi);
 
        if(getheader(req, "content-type") != NULL) {
            if((hd = getheader(req, "content-length")) != NULL) {
@@ -363,8 +406,20 @@ void serve(struct bufio *in, struct conn *conn)
        
        if(!getheader(resp, "server"))
            headappheader(resp, "Server", sprintf3("ashd/%s", VERSION));
+       duplex = hasheader(resp, "x-ash-switch", "duplex");
+       trimx(resp);
 
-       if(!strcasecmp(req->ver, "HTTP/1.0")) {
+       if(duplex) {
+           if(outi->rights < 0)
+               break;
+           writerespb(in, resp);
+           bioprintf(in, "\r\n");
+           dout = mtbioopen(outi->rights, 1, 600, "r+", NULL);
+           passduplex(in, infd, dout, outi->rights);
+           outi->rights = -1;
+           bioclose(dout);
+           break;
+       } else if(!strcasecmp(req->ver, "HTTP/1.0")) {
            if(!strcasecmp(req->method, "head")) {
                keep = http10keep(req, resp);
                writerespb(in, resp);
@@ -547,12 +602,12 @@ int main(int argc, char **argv)
 {
     int c, d;
     int i, s1;
-    char *root;
+    char *root, *pidfile, *pidtmp;
     FILE *pidout;
     struct passwd *pwent;
     
     daemonize = usesyslog = 0;
-    root = NULL;
+    root = pidfile = NULL;
     pwent = NULL;
     while((c = getopt(argc, argv, "+hSfu:r:p:")) >= 0) {
        switch(c) {
@@ -566,16 +621,16 @@ int main(int argc, char **argv)
            usesyslog = 1;
            break;
        case 'u':
-           if((pwent = getpwnam(optarg)) == NULL) {
+           if(optarg[0] && ((pwent = getpwnam(optarg)) == NULL)) {
                flog(LOG_ERR, "could not find user %s", optarg);
                exit(1);
            }
            break;
        case 'r':
-           root = optarg;
+           root = optarg[0] ? optarg : NULL;
            break;
        case 'p':
-           pidfile = optarg;
+           pidfile = optarg[0] ? optarg : NULL;
            break;
        default:
            usage(stderr);
@@ -600,8 +655,14 @@ int main(int argc, char **argv)
     bufadd(listeners, mustart(plexwatch, plex));
     pidout = NULL;
     if(pidfile != NULL) {
-       if((pidout = fopen(pidfile, "w")) == NULL) {
-           flog(LOG_ERR, "could not open %s for writing: %s", pidfile, strerror(errno));
+       pidtmp = sprintf3("%s.new", pidfile);
+       if((pidout = fopen(pidtmp, "w")) == NULL) {
+           flog(LOG_ERR, "could not open %s for writing: %s", pidtmp, strerror(errno));
+           return(1);
+       }
+       if(rename(pidtmp, pidfile)) {
+           flog(LOG_ERR, "could not overwrite %s: %s", pidfile, strerror(errno));
+           unlink(pidtmp);
            return(1);
        }
     }
@@ -632,7 +693,7 @@ int main(int argc, char **argv)
     }
     if(pidout != NULL) {
        fprintf(pidout, "%i\n", getpid());
-       fclose(pidout);
+       fflush(pidout);
     }
     d = 0;
     while(!d) {
@@ -645,11 +706,17 @@ int main(int argc, char **argv)
                while(listeners.d > 0)
                    resume(listeners.b[0], 0);
                flog(LOG_INFO, "no longer listening");
+               if(pidout != NULL) {
+                   putc('\n', pidout);
+                   fflush(pidout);
+               }
            } else {
                d = 1;
            }
            break;
        }
     }
+    if(pidout != NULL)
+       ftruncate(fileno(pidout), 0);
     return(0);
 }