15 typedef struct Hconn Hconn;
16 typedef struct Hpool Hpool;
17 typedef struct Hauth Hauth;
51 static Hpool hpool = {
54 .idle = 5, /* seconds */
60 static void hclose(Hconn *h);
69 snprint(addr, sizeof(addr), "tcp!%s!%s", u->host, u->port ? u->port : u->scheme);
72 for(p = nil, h = hpool.head; h; p = h, h = h->next){
73 if(strcmp(h->addr, addr) == 0){
86 fprint(2, "hdial [%d] %s\n", hpool.active, addr);
88 if((fd = dial(addr, 0, 0, 0)) < 0)
90 if(strcmp(u->scheme, "https") == 0){
93 tc = emalloc(sizeof(*tc));
94 fd = tlsClient(ofd = fd, tc);
96 /* BUG: should validate but how? */
104 h = emalloc(sizeof(*h));
111 nstrcpy(h->addr, addr, sizeof(h->addr));
139 if(h->keep && h->fd >= 0){
140 for(n = 0, i = 0, t = nil, x = hpool.head; x; x = x->next){
141 if(strcmp(x->addr, h->addr) == 0)
144 if(++i < hpool.limit)
148 /* return connection to pool */
150 h->next = hpool.head;
166 * if h is first one in pool, spawn proc to close
170 if(rfork(RFMEM|RFPROC|RFNOWAIT) == 0){
183 if((now - h->time) > hpool.idle){
194 i = hpool.head != nil;
209 fprint(2, "hclose [%d] %s\n", hpool.active, h->addr);
217 hread(Hconn *h, void *data, int len)
222 memmove(data, h->buf, len);
225 memmove(h->buf, h->buf + len, h->len);
228 if((len = read(h->fd, data, len)) <= 0)
234 hwrite(Hconn *h, void *data, int len)
236 if(write(h->fd, data, len) != len){
244 hline(Hconn *h, char *data, int len, int cont)
252 while(x = memchr(h->buf, '\n', h->len)){
254 if(n > 0 && x[-1] == '\r')
258 for(y = x+1; y < e; y++)
259 if(!strchr("\t ", *y))
261 if(y >= e || strchr("\t ", *y))
264 if(x > h->buf && x[-1] == '\r')
266 memmove(x, y, e - y);
273 memmove(data, h->buf, len);
275 h->len -= (++x - h->buf);
277 memmove(h->buf, x, h->len);
281 if(h->len >= sizeof(h->buf))
283 if((n = read(h->fd, h->buf + h->len, sizeof(h->buf) - h->len)) <= 0){
292 authenticate(Url *u, Url *ru, char *method, char *s)
294 char *user, *pass, *realm, *nonce, *opaque, *x;
301 realm = nonce = opaque = nil;
303 if(!cistrncmp(s, "Basic ", 6)){
304 char cred[128], plain[128];
308 if(x = cistrstr(s, "realm="))
309 realm = unquote(x+6, &s);
313 if(user == nil || pass == nil){
314 fmtprint(&fmt, " realm=%q", realm);
316 fmtprint(&fmt, " user=%q", user);
317 if((s = fmtstrflush(&fmt)) == nil)
319 up = auth_getuserpasswd(nil, "proto=pass service=http server=%q%s", u->host, s);
326 n = snprint(plain, sizeof(plain), "%s:%s", user ? user : "", pass ? pass : "");
328 memset(up->user, 0, strlen(up->user));
329 memset(up->passwd, 0, strlen(up->passwd));
332 n = enc64(cred, sizeof(cred), (uchar*)plain, n);
333 memset(plain, 0, sizeof(plain));
337 fmtprint(&fmt, "Basic %s", cred);
338 memset(cred, 0, sizeof(cred));
339 u = saneurl(url(".", u)); /* all uris below the requested one */
341 if(!cistrncmp(s, "Digest ", 7)){
342 char chal[1024], ouser[128], resp[2*MD5LEN+1];
346 if(x = cistrstr(s, "realm="))
347 realm = unquote(x+6, &s);
348 if(x = cistrstr(s, "nonce="))
349 nonce = unquote(x+6, &s);
350 if(x = cistrstr(s, "opaque="))
351 opaque = unquote(x+7, &s);
352 if(realm == nil || nonce == nil)
354 fmtprint(&fmt, " realm=%q", realm);
356 fmtprint(&fmt, " user=%q", user);
357 if((s = fmtstrflush(&fmt)) == nil)
359 nchal = snprint(chal, sizeof(chal), "%s %s %U", nonce, method, ru);
360 n = auth_respond(chal, nchal, ouser, sizeof ouser, resp, sizeof resp, nil,
361 "proto=httpdigest role=client server=%q%s", u->host, s);
362 memset(chal, 0, sizeof(chal));
367 fmtprint(&fmt, "Digest ");
368 fmtprint(&fmt, "username=\"%s\", ", ouser);
369 fmtprint(&fmt, "realm=\"%s\", ", realm);
370 fmtprint(&fmt, "host=\"%s\", ", u->host);
371 fmtprint(&fmt, "uri=\"%U\", ", ru);
372 fmtprint(&fmt, "nonce=\"%s\", ", nonce);
373 fmtprint(&fmt, "response=\"%s\"", resp);
375 fmtprint(&fmt, ", opaque=\"%s\"", opaque);
376 u = saneurl(url("/", u)); /* BUG: should be the ones in domain= only */
381 if((s = fmtstrflush(&fmt)) == nil){
385 a = emalloc(sizeof(*a));
397 flushauth(Url *u, char *t)
403 for(p = nil, a = hauth; a; p = a, a = a->next)
404 if(matchurl(u, a->url) && (t == nil || !strcmp(t, a->auth))){
410 fprint(2, "flushauth for %U\n", a->url);
412 memset(a->auth, 0, strlen(a->auth));
421 catch(void *, char *msg)
423 if(strstr("alarm", msg) || strstr("die", msg))
429 #define NOLENGTH 0x7fffffffffffffffLL
432 http(char *m, Url *u, Key *shdr, Buq *qbody, Buq *qpost)
434 int i, l, n, try, pid, fd, cfd, chunked, retry, nobody;
435 char *s, *x, buf[8192+2], status[256], method[16];
436 vlong length, offset;
443 if(qpost) incref(qpost);
444 nstrcpy(method, m, sizeof(method));
445 switch(rfork(RFPROC|RFMEM|RFNOWAIT)){
449 buclose(qbody, "can't fork");
451 buclose(qpost, "can't fork");
465 /* file for spooling the postbody if we need to restart the request */
466 snprint(buf, sizeof(buf), "/tmp/http.%d.%d.post", getppid(), getpid());
467 fd = create(buf, OEXCL|ORDWR|ORCLOSE, 0600);
473 werrstr("too many errors");
474 for(try = 0; try < 6; try++){
475 if(u == nil || (strcmp(u->scheme, "http") && strcmp(u->scheme, "https"))){
481 fprint(2, "http(%d): %s %U\n", try, method, u);
483 /* preemptive authentication from hauth cache */
485 if(proxy && !lookkey(shdr, "Proxy-Authorization"))
486 for(a = hauth; a; a = a->next)
487 if(matchurl(a->url, proxy)){
488 shdr = addkey(shdr, "Proxy-Authorization", a->auth);
491 if(!lookkey(shdr, "Authorization"))
492 for(a = hauth; a; a = a->next)
493 if(matchurl(a->url, u)){
494 shdr = addkey(shdr, "Authorization", a->auth);
503 memset(&ru, 0, sizeof(tu));
507 n = snprint(buf, sizeof(buf), "%s %U HTTP/1.1\r\nHost: %s%s%s\r\n",
508 method, &ru, u->host, u->port ? ":" : "", u->port ? u->port : "");
510 for(k = shdr; k; k = k->next)
511 n += snprint(buf+n, sizeof(buf)-2 - n, "%s: %s\r\n", k->key, k->val);
513 if(n >= sizeof(buf)-64){
514 werrstr("request too large");
518 nobody = !cistrcmp(method, "HEAD");
523 /* wait until buffer is full, most posts are small */
524 while(!qpost->closed && qpost->size < qpost->limit)
527 if(lookkey(shdr, "Content-Length"))
529 else if(x = lookkey(shdr, "Transfer-Encoding"))
530 chunked = cistrstr(x, "chunked") != nil;
531 else if(chunked = !qpost->closed)
532 n += snprint(buf+n, sizeof(buf)-n, "Transfer-Encoding: chunked\r\n");
533 else if(qpost->closed){
535 length = seek(fd, 0, 2);
539 length += qpost->size;
540 n += snprint(buf+n, sizeof(buf)-n, "Content-Length: %lld\r\n", length);
547 if((h = hdial(proxy ? proxy : u)) == nil)
551 if((cfd = open("/mnt/webcookies/http", ORDWR)) >= 0){
552 /* only scheme, host and path are relevant for cookies */
553 memset(&tu, 0, sizeof(tu));
554 tu.scheme = u->scheme;
557 fprint(cfd, "%U", &tu);
559 if(n >= sizeof(buf)-2){
561 fprint(2, "-> %.*s", n, buf);
562 if(hwrite(h, buf, n) != n)
566 if((l = read(cfd, buf+n, sizeof(buf)-2 - n)) == 0)
577 n += snprint(buf+n, sizeof(buf)-n, "\r\n");
579 fprint(2, "-> %.*s", n, buf);
580 if(hwrite(h, buf, n) != n){
588 if((pid = rfork(RFMEM|RFPROC)) <= 0){
595 if((ifd < 0) || ((n = read(ifd, buf, sizeof(buf)-2)) <= 0)){
597 if((n = buread(qpost, buf, sizeof(buf)-2)) <= 0)
600 if(write(fd, buf, n) != n)
605 hwrite(h, tmp, snprint(tmp, sizeof(tmp), "%x\r\n", n));
609 if(hwrite(h, buf, n) != n)
613 hwrite(h, "0\r\n\r\n", 5);
619 /* no timeout when posting */
631 for(l = 0; hline(h, s = buf, sizeof(buf)-1, 1) > 0; l++){
633 fprint(2, "<- %s\n", s);
635 if(x = strchr(s, ' '))
638 if(cistrncmp(s, "HTTP", 4)){
640 if(cistrcmp(s, "ICY"))
643 nstrcpy(status, x, sizeof(status));
646 if((k = parsehdr(s)) == nil)
648 if(!cistrcmp(k->key, "Connection")){
649 if(cistrstr(k->val, "close"))
652 else if(!cistrcmp(k->key, "Content-Length"))
653 length = atoll(k->val);
654 else if(!cistrcmp(k->key, "Transfer-Encoding")){
655 if(cistrstr(k->val, "chunked"))
658 else if(!cistrcmp(k->key, "Set-Cookie") ||
659 !cistrcmp(k->key, "Set-Cookie2")){
661 fprint(cfd, "Set-Cookie: %s\n", k->val);
674 if((i = atoi(status)) < 0)
683 case 100: /* Continue */
684 case 101: /* Switching Protocols */
690 case 304: /* Not Modified */
692 case 305: /* Use Proxy */
693 case 400: /* Bad Request */
694 case 402: /* Payment Required */
695 case 403: /* Forbidden */
696 case 404: /* Not Found */
697 case 405: /* Method Not Allowed */
698 case 406: /* Not Acceptable */
699 case 408: /* Request Timeout */
700 case 409: /* Conflict */
702 case 411: /* Length Required */
703 case 412: /* Precondition Failed */
704 case 413: /* Request Entity Too Large */
705 case 414: /* Request URI Too Large */
706 case 415: /* Unsupported Media Type */
707 case 416: /* Requested Range Not Satisfiable */
708 case 417: /* Expectation Failed */
709 case 500: /* Internal server error */
710 case 501: /* Not implemented */
711 case 502: /* Bad gateway */
712 case 503: /* Service unavailable */
713 case 504: /* Gateway Timeout */
714 case 505: /* HTTP Version not Supported */
717 buclose(qbody, status);
718 buclose(qpost, status);
720 case 300: /* Multiple choices */
721 case 302: /* Found */
722 case 303: /* See Other */
732 if(cistrcmp(method, "HEAD"))
733 nstrcpy(method, "GET", sizeof(method));
734 case 301: /* Moved Permanently */
735 case 307: /* Temporary Redirect */
736 case 308: /* Resume Incomplete */
737 if((x = lookkey(rhdr, "Location")) == nil)
739 if((nu = saneurl(url(x, u))) == nil)
744 case 401: /* Unauthorized */
745 if(x = lookkey(shdr, "Authorization"))
747 if((x = lookkey(rhdr, "WWW-Authenticate")) == nil)
749 if(authenticate(u, &ru, method, x) < 0)
753 case 407: /* Proxy Auth */
756 if(x = lookkey(shdr, "Proxy-Authorization"))
758 if((x = lookkey(rhdr, "Proxy-Authenticate")) == nil)
760 if(authenticate(proxy, proxy, method, x) < 0)
763 case 0: /* No status */
772 case 204: /* No Content */
773 case 205: /* Reset Content */
776 case 201: /* Created */
777 case 202: /* Accepted */
778 case 203: /* Non-Authoritative Information */
779 case 206: /* Partial Content */
780 qbody->url = u; u = nil;
781 qbody->hdr = rhdr; rhdr = nil;
793 * remove authorization headers so on the next round, we use
794 * the hauth cache (wich checks the scope url). this makes
795 * sure we wont send credentials to the wrong url after
798 shdr = delkey(shdr, "Proxy-Authorization");
799 shdr = delkey(shdr, "Authorization");
801 if(!chunked && length == NOLENGTH)
805 * read the response body (if any). retry means we'r just
806 * skipping the error page so we wont touch qbody.
809 if((qbody->closed || retry) && !h->keep)
812 if(hline(h, buf, sizeof(buf)-1, 0) <= 0)
814 length = strtoll(buf, nil, 16);
817 while(offset < length){
819 if(l > (length - offset))
820 l = (length - offset);
821 if((n = hread(h, buf, l)) <= 0)
825 if(buwrite(qbody, buf, n) != n)
828 if(offset != length){
830 if(length != NOLENGTH)
834 while(hline(h, buf, sizeof(buf)-1, 1) > 0){
836 fprint(2, "<= %s\n", buf);
838 if(k = parsehdr(buf)){
839 k->next = qbody->hdr;
865 rerrstr(buf, sizeof(buf));