+int sendreq2(int sock, struct hthead *req, int fd, int flags)
+{
+ int ret, i;
+ struct charbuf buf;
+
+ bufinit(buf);
+ bufcatstr2(buf, req->method);
+ bufcatstr2(buf, req->url);
+ bufcatstr2(buf, req->ver);
+ bufcatstr2(buf, req->rest);
+ for(i = 0; i < req->noheaders; i++) {
+ bufcatstr2(buf, req->headers[i][0]);
+ bufcatstr2(buf, req->headers[i][1]);
+ }
+ bufcatstr2(buf, "");
+ ret = sendfd2(sock, fd, buf.b, buf.d, flags);
+ buffree(buf);
+ if(ret < 0)
+ return(-1);
+ else
+ return(0);
+}
+
+int sendreq(int sock, struct hthead *req, int fd)
+{
+ return(sendreq2(sock, req, fd, MSG_NOSIGNAL));
+}
+
+int recvreq(int sock, struct hthead **reqp)
+{
+ int fd;
+ struct charbuf buf;
+ char *p;
+ size_t l;
+ char *name, *val;
+ struct hthead *req;
+
+ if((fd = recvfd(sock, &buf.b, &buf.d)) < 0) {
+ return(-1);
+ }
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ buf.s = buf.d;
+ p = buf.b;
+ l = buf.d;
+
+ *reqp = omalloc(req);
+ if((req->method = sstrdup(decstr(&p, &l))) == NULL)
+ goto fail;
+ if((req->url = sstrdup(decstr(&p, &l))) == NULL)
+ goto fail;
+ if((req->ver = sstrdup(decstr(&p, &l))) == NULL)
+ goto fail;
+ if((req->rest = sstrdup(decstr(&p, &l))) == NULL)
+ goto fail;
+
+ while(1) {
+ if(!*(name = decstr(&p, &l)))
+ break;
+ val = decstr(&p, &l);
+ headappheader(req, name, val);
+ }
+
+ buffree(buf);
+ return(fd);
+
+fail:
+ close(fd);
+ freehthead(req);
+ errno = EPROTO;
+ return(-1);
+}
+
+char *unquoteurl(char *in)
+{
+ struct charbuf buf;
+ char *p;
+ int c;
+
+ bufinit(buf);
+ p = in;
+ while(*p) {
+ if(*p == '%') {
+ if(!p[1] || !p[2])
+ goto fail;
+ c = 0;
+ if((p[1] >= '0') && (p[1] <= '9')) c |= (p[1] - '0') << 4;
+ else if((p[1] >= 'a') && (p[1] <= 'f')) c |= (p[1] - 'a' + 10) << 4;
+ else if((p[1] >= 'A') && (p[1] <= 'F')) c |= (p[1] - 'A' + 10) << 4;
+ else goto fail;
+ if((p[2] >= '0') && (p[2] <= '9')) c |= (p[2] - '0');
+ else if((p[2] >= 'a') && (p[2] <= 'f')) c |= (p[2] - 'a' + 10);
+ else if((p[2] >= 'A') && (p[2] <= 'F')) c |= (p[2] - 'A' + 10);
+ else goto fail;
+ bufadd(buf, c);
+ p += 3;
+ } else {
+ bufadd(buf, *(p++));
+ }
+ }
+ bufadd(buf, 0);
+ return(buf.b);
+fail:
+ buffree(buf);
+ return(NULL);