15 typedef struct Hconn Hconn;
16 typedef struct Hpool Hpool;
17 typedef struct Hauth Hauth;
53 static Hpool hpool = {
56 .idle = 5, /* seconds */
62 static void hclose(Hconn *h);
65 tlstrace(char *fmt, ...)
70 r = vfprint(2, fmt, a);
76 tlswrap(int fd, char *servername)
80 memset(&conn, 0, sizeof(conn));
82 conn.trace = tlstrace;
84 conn.serverName = smprint("%N", servername);
85 if((fd = tlsClient(fd, &conn)) < 0){
86 if(debug) fprint(2, "tlsClient: %r\n");
90 free(conn.serverName);
95 hdial(Url *u, int cached)
101 snprint(addr, sizeof(addr), "tcp!%s!%s", u->host, u->port ? u->port : u->scheme);
105 for(p = nil, h = hpool.head; h; p = h, h = h->next){
106 if(strcmp(h->addr, addr) == 0){
110 hpool.head = h->next;
121 fprint(2, "hdial [%d] %s\n", hpool.active, addr);
124 snprint(addr, sizeof(addr), "tcp!%s!%s",
125 proxy->host, proxy->port ? proxy->port : proxy->scheme);
127 if((fd = dial(addr, 0, 0, &ctl)) < 0)
131 if(strcmp(proxy->scheme, "https") == 0)
132 fd = tlswrap(fd, proxy->host);
134 if(strcmp(u->scheme, "https") == 0)
135 fd = tlswrap(fd, u->host);
142 h = emalloc(sizeof(*h));
153 h->tunnel = strcmp(u->scheme, "https") == 0;
154 snprint(addr, sizeof(addr), "tcp!%s!%s", u->host, u->port ? u->port : u->scheme);
156 nstrcpy(h->addr, addr, sizeof(h->addr));
184 if(!h->tunnel && h->keep && h->fd >= 0){
185 for(n = 0, i = 0, t = nil, x = hpool.head; x; x = x->next){
186 if(strcmp(x->addr, h->addr) == 0)
189 if(++i < hpool.limit)
193 /* return connection to pool */
195 h->next = hpool.head;
211 * if h is first one in pool, spawn proc to close
215 if(rfork(RFMEM|RFPROC|RFNOWAIT) == 0){
228 if((now - h->time) > hpool.idle){
239 i = hpool.head != nil;
254 fprint(2, "hclose [%d] %s\n", hpool.active, h->addr);
267 fprint(2, "hangup pc=%p: %r\n", getcallerpc(&h));
274 hread(Hconn *h, void *data, int len)
279 memmove(data, h->buf, len);
282 memmove(h->buf, h->buf + len, h->len);
285 if((len = read(h->fd, data, len)) == 0)
293 hwrite(Hconn *h, void *data, int len)
295 if(write(h->fd, data, len) != len){
303 hline(Hconn *h, char *data, int len, int cont)
311 while(x = memchr(h->buf, '\n', h->len)){
313 if(n > 0 && x[-1] == '\r')
317 for(y = x+1; y < e; y++)
318 if(*y != ' ' && *y != '\t')
320 if(y >= e || *y == 0)
323 if(x > h->buf && x[-1] == '\r')
325 memmove(x, y, e - y);
332 memmove(data, h->buf, len);
334 h->len -= (++x - h->buf);
336 memmove(h->buf, x, h->len);
340 n = sizeof(h->buf) - h->len;
344 n = 1; /* do not read beyond header */
345 if((n = read(h->fd, h->buf + h->len, n)) <= 0){
354 hauthgetkey(char *params)
357 fprint(2, "hauthgetkey %s\n", params);
358 werrstr("needkey %s", params);
363 authenticate(Url *u, Url *ru, char *method, char *s)
365 char oerr[ERRMAX], *user, *pass, *realm, *nonce, *opaque, *x;
370 snprint(oerr, sizeof(oerr), "authentification failed");
371 errstr(oerr, sizeof(oerr));
375 realm = nonce = opaque = nil;
376 if(!cistrncmp(s, "Basic ", 6)){
380 if(x = cistrstr(s, "realm="))
381 realm = unquote(x+6, &s);
385 if(user == nil || pass == nil){
387 fmtprint(&fmt, " realm=%q", realm);
389 fmtprint(&fmt, " user=%q", user);
390 if((s = fmtstrflush(&fmt)) == nil)
392 up = auth_getuserpasswd(hauthgetkey,
393 "proto=pass service=http server=%q%s", u->host, s);
401 fmtprint(&fmt, "%s:%s", user ? user : "", pass ? pass : "");
403 memset(up->user, 0, strlen(up->user));
404 memset(up->passwd, 0, strlen(up->passwd));
407 if((s = fmtstrflush(&fmt)) == nil)
411 fmtprint(&fmt, "Basic %.*[", n, s);
414 u = saneurl(url(".", u)); /* all uris below the requested one */
416 if(!cistrncmp(s, "Digest ", 7)){
417 char chal[1024], ouser[128], resp[2*MD5LEN+1];
421 if(x = cistrstr(s, "realm="))
422 realm = unquote(x+6, &s);
423 if(x = cistrstr(s, "nonce="))
424 nonce = unquote(x+6, &s);
425 if(x = cistrstr(s, "opaque="))
426 opaque = unquote(x+7, &s);
427 if(realm == nil || nonce == nil)
430 fmtprint(&fmt, " realm=%q", realm);
432 fmtprint(&fmt, " user=%q", user);
433 if((s = fmtstrflush(&fmt)) == nil)
435 nchal = snprint(chal, sizeof(chal), "%s %s %U", nonce, method, ru);
436 n = auth_respond(chal, nchal, ouser, sizeof ouser, resp, sizeof resp, hauthgetkey,
437 "proto=httpdigest role=client server=%q%s", u->host, s);
438 memset(chal, 0, sizeof(chal));
443 fmtprint(&fmt, "Digest ");
444 fmtprint(&fmt, "username=\"%s\", ", ouser);
445 fmtprint(&fmt, "realm=\"%s\", ", realm);
446 fmtprint(&fmt, "host=\"%N\", ", u->host);
447 fmtprint(&fmt, "uri=\"%U\", ", ru);
448 fmtprint(&fmt, "nonce=\"%s\", ", nonce);
449 fmtprint(&fmt, "response=\"%s\"", resp);
451 fmtprint(&fmt, ", opaque=\"%s\"", opaque);
452 u = saneurl(url("/", u)); /* BUG: should be the ones in domain= only */
455 if((s = fmtstrflush(&fmt)) == nil){
464 a = emalloc(sizeof(*a));
472 errstr(oerr, sizeof(oerr));
477 hauthenticate(Url *u, Url *ru, char *method, char *key, Key *hdr)
479 for(hdr = getkey(hdr, key); hdr != nil; hdr = getkey(hdr->next, key))
480 if(authenticate(u, ru, method, hdr->val) == 0)
486 flushauth(Url *u, char *t)
492 for(p = nil, a = hauth; a; p = a, a = a->next)
493 if(matchurl(u, a->url) && (t == nil || !strcmp(t, a->auth))){
499 fprint(2, "flushauth for %U\n", a->url);
501 memset(a->auth, 0, strlen(a->auth));
510 catch(void *, char *msg)
512 if(strstr("alarm", msg) != nil)
518 #define NOLENGTH 0x7fffffffffffffffLL
521 http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
523 int i, l, n, try, pid, fd, cfd, needlength, chunked, retry, nobody, badauth;
524 char *s, *x, buf[8192+2], status[256], method[16], *host;
525 vlong length, offset;
532 if(qpost) incref(qpost);
533 nstrcpy(method, m, sizeof(method));
534 switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
538 buclose(qbody, "can't fork");
540 buclose(qpost, "can't fork");
554 /* file for spooling the postbody if we need to restart the request */
555 snprint(buf, sizeof(buf), "/tmp/http.%d.%d.post", getppid(), getpid());
556 fd = create(buf, OEXCL|ORDWR|ORCLOSE, 0600);
566 for(try = 0; try < 12; try++){
567 strcpy(status, "0 No status");
568 if(u == nil || (strcmp(u->scheme, "http") && strcmp(u->scheme, "https"))){
569 werrstr("bad url scheme");
574 fprint(2, "http(%d): %s %U\n", try, method, u);
576 /* preemptive authentication from hauth cache */
578 if(proxy && !lookkey(shdr, "Proxy-Authorization"))
579 for(a = hauth; a; a = a->next)
580 if(matchurl(a->url, proxy)){
581 shdr = addkey(shdr, "Proxy-Authorization", a->auth);
584 if(!lookkey(shdr, "Authorization"))
585 for(a = hauth; a; a = a->next)
586 if(matchurl(a->url, u)){
587 shdr = addkey(shdr, "Authorization", a->auth);
595 /* have to read it to temp file to figure out the length */
596 if(fd >= 0 && needlength && lookkey(shdr, "Content-Length") == nil){
598 while((n = buread(qpost, buf, sizeof(buf))) > 0)
600 shdr = delkey(shdr, "Transfer-Encoding");
604 /* wait until buffer is full, most posts are small */
605 while(!qpost->closed && qpost->size < qpost->limit && qpost->nwq == 0)
608 if(lookkey(shdr, "Content-Length"))
610 else if(x = lookkey(shdr, "Transfer-Encoding"))
611 chunked = cistrstr(x, "chunked") != nil;
612 else if(chunked = !qpost->closed)
613 shdr = addkey(shdr, "Transfer-Encoding", "chunked");
614 else if(qpost->closed){
616 length = seek(fd, 0, 2);
620 length += qpost->size;
621 snprint(buf, sizeof(buf), "%lld", length);
622 shdr = addkey(shdr, "Content-Length", buf);
627 /* http requires ascii encoding of host */
629 host = smprint("%N", u->host);
631 if(proxy && strcmp(u->scheme, "https") != 0){
636 memset(&ru, 0, sizeof(ru));
640 n = snprint(buf, sizeof(buf), "%s %U HTTP/1.1\r\nHost: %s%s%s\r\n",
641 method, &ru, host, u->port ? ":" : "", u->port ? u->port : "");
642 if(n >= sizeof(buf)-64){
643 werrstr("request too large");
648 if((h = hdial(u, qpost==nil)) == nil)
652 n = snprint(buf, sizeof(buf), "CONNECT %s:%s HTTP/1.1\r\nHost: %s:%s\r\n",
653 host, u->port ? u->port : "443",
654 host, u->port ? u->port : "443");
656 else if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){
657 /* only scheme, host and path are relevant for cookies */
658 memset(&tu, 0, sizeof(tu));
659 tu.scheme = u->scheme;
662 fprint(cfd, "%U", &tu);
664 if(n >= sizeof(buf)-2){
666 fprint(2, "-> %.*s", n, buf);
667 if(hwrite(h, buf, n) != n)
671 if((l = read(cfd, buf+n, sizeof(buf)-2 - n)) == 0)
682 for(k = shdr; k; k = k->next){
683 /* only send proxy headers when establishing tunnel */
684 if(h->tunnel && cistrncmp(k->key, "Proxy-", 6) != 0)
686 n += snprint(buf+n, sizeof(buf)-2 - n, "%s: %s\r\n", k->key, k->val);
688 n += snprint(buf+n, sizeof(buf)-n, "\r\n");
690 fprint(2, "-> %.*s", n, buf);
691 if(hwrite(h, buf, n) != n){
697 if(qpost && !h->tunnel){
699 if((pid = rfork(RFMEM|RFPROC)) <= 0){
706 if((ifd < 0) || ((n = read(ifd, buf, sizeof(buf)-2)) <= 0)){
708 if((n = buread(qpost, buf, sizeof(buf)-2)) <= 0)
711 if(write(fd, buf, n) != n)
717 if(hwrite(h, tmp, snprint(tmp, sizeof(tmp), "%x\r\n", n)) < 0)
722 if(hwrite(h, buf, n) != n)
727 hwrite(h, "0\r\n\r\n", 5);
733 /* no timeout when posting */
744 for(l = 0; hline(h, s = buf, sizeof(buf)-1, 1) > 0; l++){
746 fprint(2, "<- %s\n", s);
748 if(x = strchr(s, ' '))
751 if(cistrncmp(s, "HTTP", 4)){
753 if(cistrcmp(s, "ICY"))
757 nstrcpy(status, x, sizeof(status));
760 if((k = parsehdr(s)) == nil)
762 if(!cistrcmp(k->key, "Connection")){
763 if(cistrstr(k->val, "close"))
766 else if(!cistrcmp(k->key, "Content-Length"))
767 length = atoll(k->val);
768 else if(!cistrcmp(k->key, "Transfer-Encoding")){
769 if(cistrstr(k->val, "chunked"))
772 else if(!cistrcmp(k->key, "Set-Cookie") ||
773 !cistrcmp(k->key, "Set-Cookie2")){
775 fprint(cfd, "Set-Cookie: %s\n", k->val);
788 nobody = !cistrcmp(method, "HEAD");
789 if((i = atoi(status)) < 0)
798 case 100: /* Continue */
799 case 101: /* Switching Protocols */
805 case 304: /* Not Modified */
807 case 305: /* Use Proxy */
808 case 400: /* Bad Request */
809 case 402: /* Payment Required */
810 case 403: /* Forbidden */
811 case 404: /* Not Found */
812 case 405: /* Method Not Allowed */
813 case 406: /* Not Acceptable */
814 case 408: /* Request Timeout */
815 case 409: /* Conflict */
818 case 411: /* Length Required */
825 case 412: /* Precondition Failed */
826 case 413: /* Request Entity Too Large */
827 case 414: /* Request URI Too Large */
828 case 415: /* Unsupported Media Type */
829 case 416: /* Requested Range Not Satisfiable */
830 case 417: /* Expectation Failed */
831 case 500: /* Internal server error */
832 case 501: /* Not implemented */
833 case 502: /* Bad gateway */
834 case 503: /* Service unavailable */
835 case 504: /* Gateway Timeout */
836 case 505: /* HTTP Version not Supported */
839 buclose(qbody, status);
840 buclose(qpost, status);
842 case 300: /* Multiple choices */
843 case 302: /* Found */
844 case 303: /* See Other */
854 shdr = delkey(shdr, "Content-Length");
855 shdr = delkey(shdr, "Content-Type");
856 shdr = delkey(shdr, "Transfer-Encoding");
857 if(cistrcmp(method, "HEAD"))
858 nstrcpy(method, "GET", sizeof(method));
859 case 301: /* Moved Permanently */
860 case 307: /* Temporary Redirect */
861 case 308: /* Resume Incomplete */
862 if((x = lookkey(rhdr, "Location")) == nil)
864 if((nu = saneurl(url(x, u))) == nil)
869 case 401: /* Unauthorized */
870 if(x = lookkey(shdr, "Authorization")){
875 if(hauthenticate(u, &ru, method, "WWW-Authenticate", rhdr) < 0){
878 rerrstr(buf, sizeof(buf));
885 case 407: /* Proxy Auth */
888 if(x = lookkey(shdr, "Proxy-Authorization")){
893 if(hauthenticate(proxy, proxy, method, "Proxy-Authenticate", rhdr) < 0)
896 case 0: /* No status */
905 case 204: /* No Content */
906 case 205: /* Reset Content */
909 case 201: /* Created */
910 case 202: /* Accepted */
911 case 203: /* Non-Authoritative Information */
912 case 206: /* Partial Content */
915 qbody->url = u; u = nil;
916 qbody->hdr = rhdr; rhdr = nil;
928 * remove authorization headers so on the next round, we use
929 * the hauth cache (wich checks the scope url). this makes
930 * sure we wont send credentials to the wrong url after
933 shdr = delkey(shdr, "Proxy-Authorization");
934 shdr = delkey(shdr, "Authorization");
937 * when 2xx response is given for the CONNECT request
938 * then the proxy server has established the connection.
940 if(h->tunnel && !retry && (i/100) == 2){
941 if((h->fd = tlswrap(h->fd, host)) < 0)
944 /* proceed to the original request */
949 if(!chunked && length == NOLENGTH)
953 * read the response body (if any). retry means we'r just
954 * skipping the error page so we wont touch qbody.
957 if((qbody->closed || retry) && !h->keep)
960 if(hline(h, buf, sizeof(buf)-1, 0) <= 0)
962 length = strtoll(buf, nil, 16);
965 while(offset < length){
967 if(l > (length - offset))
968 l = (length - offset);
969 if((n = hread(h, buf, l)) <= 0)
973 if(buwrite(qbody, buf, n) != n)
976 if(offset != length){
978 if(length != NOLENGTH)
982 while(hline(h, buf, sizeof(buf)-1, 1) > 0){
984 fprint(2, "<= %s\n", buf);
986 if(k = parsehdr(buf)){
987 k->next = qbody->hdr;
1012 snprint(buf, sizeof(buf), "%s %r", status);
1013 buclose(qbody, buf);
1019 buclose(qpost, buf);