+ return(sent);
+}
+
+static void serve(struct muth *muth, va_list args)
+{
+ vavar(int, fd);
+ vavar(struct sockaddr_storage, name);
+ int cfd;
+ int pfds[2];
+ char old;
+ char *hd, *p;
+ struct charbuf inbuf, outbuf;
+ struct hthead *req, *resp;
+ off_t dlen, sent;
+ ssize_t headoff;
+ char nmbuf[256];
+
+ bufinit(inbuf);
+ bufinit(outbuf);
+ cfd = -1;
+ req = resp = NULL;
+ while(1) {
+ /*
+ * First, find and decode the header:
+ */
+ if((headoff = readhead(fd, &inbuf)) < 0)
+ goto out;
+ if(headoff > 65536) {
+ /* We cannot handle arbitrarily large headers, as they
+ * need to fit within a single Unix datagram. This is
+ * probably a safe limit, and larger packets than this are
+ * most likely erroneous (or malicious) anyway. */
+ goto out;
+ }
+ old = inbuf.b[headoff];
+ inbuf.b[headoff] = 0;
+ if((req = parserawreq(inbuf.b)) == NULL)
+ goto out;
+ inbuf.b[headoff] = old;
+ bufeat(inbuf, headoff);
+ /* We strip off the leading slash and any param string from
+ * the rest string, so that multiplexers can parse
+ * coherently. */
+ if(req->rest[0] == '/')
+ replrest(req, req->rest + 1);
+ if((p = strchr(req->rest, '?')) != NULL)
+ *p = 0;
+
+ /*
+ * Add metainformation and then send the request to the root
+ * multiplexer:
+ */
+ if(name.ss_family == AF_INET) {
+ headappheader(req, "X-Ash-Address", inet_ntop(AF_INET, &((struct sockaddr_in *)&name)->sin_addr, nmbuf, sizeof(nmbuf)));
+ headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in *)&name)->sin_port)));
+ } else if(name.ss_family == AF_INET6) {
+ headappheader(req, "X-Ash-Address", inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&name)->sin6_addr, nmbuf, sizeof(nmbuf)));
+ headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in6 *)&name)->sin6_port)));
+ }
+ if(block(plex, EV_WRITE, 60) <= 0)
+ goto out;
+ if(socketpair(PF_UNIX, SOCK_STREAM, 0, pfds))
+ goto out;
+ if(sendreq(plex, req, pfds[0]))
+ goto out;
+ close(pfds[0]);
+ cfd = pfds[1];
+
+ /*
+ * If there is message data, pass it:
+ */
+ if((hd = getheader(req, "content-length")) != NULL) {
+ dlen = atoo(hd);
+ if(dlen > 0) {
+ if(passdata(fd, cfd, &inbuf, dlen) < 0)
+ goto out;
+ }
+ }
+ /* Make sure to send EOF */
+ shutdown(cfd, SHUT_WR);
+
+ /*
+ * Find and decode the response header:
+ */
+ outbuf.d = 0;
+ if((headoff = readhead(cfd, &outbuf)) < 0)
+ goto out;
+ hd = memcpy(smalloc(headoff + 1), outbuf.b, headoff);
+ hd[headoff] = 0;
+ if((resp = parserawresp(hd)) == NULL)
+ goto out;
+
+ /*
+ * Pass the actual output:
+ */
+ sizebuf(outbuf, 65536);
+ if((sent = passdata(cfd, fd, &outbuf, -1)) < 0)
+ goto out;
+ sent -= headoff;
+
+ /*
+ * Check for connection expiry
+ */
+ if(strcasecmp(req->method, "head")) {
+ if((hd = getheader(resp, "content-length")) != NULL) {
+ if(sent != atoo(hd)) {
+ /* Exit because of error */
+ goto out;
+ }
+ } else {
+ if(((hd = getheader(resp, "transfer-encoding")) == NULL) || !strcasecmp(hd, "identity"))
+ break;
+ }
+ if(((hd = getheader(req, "connection")) != NULL) && !strcasecmp(hd, "close"))
+ break;
+ if(((hd = getheader(resp, "connection")) != NULL) && !strcasecmp(hd, "close"))
+ break;
+ }
+
+ close(cfd);
+ cfd = -1;
+ freehthead(req);
+ req = NULL;
+ freehthead(resp);
+ resp = NULL;
+ }
+
+out:
+ if(cfd >= 0)
+ close(cfd);
+ if(req != NULL)
+ freehthead(req);
+ if(resp != NULL)
+ freehthead(resp);
+ buffree(inbuf);
+ buffree(outbuf);
+ close(fd);
+}
+
+static void listenloop(struct muth *muth, va_list args)
+{
+ vavar(int, ss);
+ int ns;
+ struct sockaddr_storage name;
+ socklen_t namelen;
+
+ while(1) {
+ namelen = sizeof(name);
+ block(ss, EV_READ, 0);
+ ns = accept(ss, (struct sockaddr *)&name, &namelen);
+ if(ns < 0) {
+ flog(LOG_ERR, "accept: %s", strerror(errno));
+ goto out;
+ }
+ mustart(serve, ns, name);
+ }
+
+out:
+ close(ss);