xref: /plan9/sys/src/cmd/webfs/http.c (revision 360053c830964336c451d94d3711af93cb0045f0)
19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <bio.h>
49a747e4fSDavid du Colombier #include <ip.h>
59a747e4fSDavid du Colombier #include <plumb.h>
69a747e4fSDavid du Colombier #include <thread.h>
79a747e4fSDavid du Colombier #include <fcall.h>
89a747e4fSDavid du Colombier #include <9p.h>
99dfc0cb2SDavid du Colombier #include <libsec.h>
109dfc0cb2SDavid du Colombier #include <auth.h>
119a747e4fSDavid du Colombier #include "dat.h"
129a747e4fSDavid du Colombier #include "fns.h"
139a747e4fSDavid du Colombier 
141066d6deSDavid du Colombier char PostContentType[] = "application/x-www-form-urlencoded";
159a747e4fSDavid du Colombier int httpdebug;
169a747e4fSDavid du Colombier 
179a747e4fSDavid du Colombier typedef struct HttpState HttpState;
189a747e4fSDavid du Colombier struct HttpState
199a747e4fSDavid du Colombier {
209a747e4fSDavid du Colombier 	int fd;
219a747e4fSDavid du Colombier 	Client *c;
229a747e4fSDavid du Colombier 	char *location;
239a747e4fSDavid du Colombier 	char *setcookie;
249a747e4fSDavid du Colombier 	char *netaddr;
259dfc0cb2SDavid du Colombier 	char *credentials;
269dfc0cb2SDavid du Colombier 	char autherror[ERRMAX];
279a747e4fSDavid du Colombier 	Ibuf	b;
289a747e4fSDavid du Colombier };
299a747e4fSDavid du Colombier 
309a747e4fSDavid du Colombier static void
319a747e4fSDavid du Colombier location(HttpState *hs, char *value)
329a747e4fSDavid du Colombier {
339a747e4fSDavid du Colombier 	if(hs->location == nil)
349a747e4fSDavid du Colombier 		hs->location = estrdup(value);
359a747e4fSDavid du Colombier }
369a747e4fSDavid du Colombier 
379a747e4fSDavid du Colombier static void
389a747e4fSDavid du Colombier contenttype(HttpState *hs, char *value)
399a747e4fSDavid du Colombier {
403b56890dSDavid du Colombier 	if(hs->c->contenttype != nil)
413b56890dSDavid du Colombier 		free(hs->c->contenttype);
429a747e4fSDavid du Colombier 	hs->c->contenttype = estrdup(value);
439a747e4fSDavid du Colombier }
449a747e4fSDavid du Colombier 
459a747e4fSDavid du Colombier static void
469a747e4fSDavid du Colombier setcookie(HttpState *hs, char *value)
479a747e4fSDavid du Colombier {
489a747e4fSDavid du Colombier 	char *s, *t;
499a747e4fSDavid du Colombier 	Fmt f;
509a747e4fSDavid du Colombier 
519a747e4fSDavid du Colombier 	s = hs->setcookie;
529a747e4fSDavid du Colombier 	fmtstrinit(&f);
539a747e4fSDavid du Colombier 	if(s)
549a747e4fSDavid du Colombier 		fmtprint(&f, "%s", s);
559a747e4fSDavid du Colombier 	fmtprint(&f, "set-cookie: ");
569a747e4fSDavid du Colombier 	fmtprint(&f, "%s", value);
579a747e4fSDavid du Colombier 	fmtprint(&f, "\n");
589a747e4fSDavid du Colombier 	t = fmtstrflush(&f);
599a747e4fSDavid du Colombier 	if(t){
609a747e4fSDavid du Colombier 		free(s);
619a747e4fSDavid du Colombier 		hs->setcookie = t;
629a747e4fSDavid du Colombier 	}
639a747e4fSDavid du Colombier }
649a747e4fSDavid du Colombier 
659dfc0cb2SDavid du Colombier static char*
669dfc0cb2SDavid du Colombier unquote(char *s, char **ps)
679dfc0cb2SDavid du Colombier {
689dfc0cb2SDavid du Colombier 	char *p;
699dfc0cb2SDavid du Colombier 
709dfc0cb2SDavid du Colombier 	if(*s != '"'){
719dfc0cb2SDavid du Colombier 		p = strpbrk(s, " \t\r\n");
729dfc0cb2SDavid du Colombier 		*p++ = 0;
739dfc0cb2SDavid du Colombier 		*ps = p;
749dfc0cb2SDavid du Colombier 		return s;
759dfc0cb2SDavid du Colombier 	}
769dfc0cb2SDavid du Colombier 	for(p=s+1; *p; p++){
779dfc0cb2SDavid du Colombier 		if(*p == '\"'){
789dfc0cb2SDavid du Colombier 			*p++ = 0;
799dfc0cb2SDavid du Colombier 			break;
809dfc0cb2SDavid du Colombier 		}
819dfc0cb2SDavid du Colombier 		if(*p == '\\' && *(p+1)){
829dfc0cb2SDavid du Colombier 			p++;
839dfc0cb2SDavid du Colombier 			continue;
849dfc0cb2SDavid du Colombier 		}
859dfc0cb2SDavid du Colombier 	}
869dfc0cb2SDavid du Colombier 	memmove(s, s+1, p-(s+1));
879dfc0cb2SDavid du Colombier 	s[p-(s+1)] = 0;
889dfc0cb2SDavid du Colombier 	*ps = p;
899dfc0cb2SDavid du Colombier 	return s;
909dfc0cb2SDavid du Colombier }
919dfc0cb2SDavid du Colombier 
929dfc0cb2SDavid du Colombier static char*
939dfc0cb2SDavid du Colombier servername(char *addr)
949dfc0cb2SDavid du Colombier {
959dfc0cb2SDavid du Colombier 	char *p;
969dfc0cb2SDavid du Colombier 
979dfc0cb2SDavid du Colombier 	if(strncmp(addr, "tcp!", 4) == 0
989dfc0cb2SDavid du Colombier 	|| strncmp(addr, "net!", 4) == 0)
999dfc0cb2SDavid du Colombier 		addr += 4;
1009dfc0cb2SDavid du Colombier 	addr = estrdup(addr);
1019dfc0cb2SDavid du Colombier 	p = addr+strlen(addr);
1029dfc0cb2SDavid du Colombier 	if(p>addr && *(p-1) == 's')
1039dfc0cb2SDavid du Colombier 		p--;
1049dfc0cb2SDavid du Colombier 	if(p>addr+5 && strcmp(p-5, "!http") == 0)
1059dfc0cb2SDavid du Colombier 		p[-5] = 0;
1069dfc0cb2SDavid du Colombier 	return addr;
1079dfc0cb2SDavid du Colombier }
1089dfc0cb2SDavid du Colombier 
1099dfc0cb2SDavid du Colombier void
1109dfc0cb2SDavid du Colombier wwwauthenticate(HttpState *hs, char *line)
1119dfc0cb2SDavid du Colombier {
1129dfc0cb2SDavid du Colombier 	char cred[64], *user, *pass, *realm, *s, *spec, *name;
1139dfc0cb2SDavid du Colombier 	Fmt fmt;
1149dfc0cb2SDavid du Colombier 	UserPasswd *up;
1159dfc0cb2SDavid du Colombier 
1169dfc0cb2SDavid du Colombier 	spec = nil;
1179dfc0cb2SDavid du Colombier 	up = nil;
1189dfc0cb2SDavid du Colombier 	cred[0] = 0;
1199dfc0cb2SDavid du Colombier 	hs->autherror[0] = 0;
1209dfc0cb2SDavid du Colombier 	if(cistrncmp(line, "basic ", 6) != 0){
1219dfc0cb2SDavid du Colombier 		werrstr("unknown auth: %s", line);
1229dfc0cb2SDavid du Colombier 		goto error;
1239dfc0cb2SDavid du Colombier 	}
1249dfc0cb2SDavid du Colombier 	line += 6;
1259dfc0cb2SDavid du Colombier 	if(cistrncmp(line, "realm=", 6) != 0){
1269dfc0cb2SDavid du Colombier 		werrstr("missing realm: %s", line);
1279dfc0cb2SDavid du Colombier 		goto error;
1289dfc0cb2SDavid du Colombier 	}
1299dfc0cb2SDavid du Colombier 	line += 6;
1309dfc0cb2SDavid du Colombier 	user = hs->c->url->user;
1319dfc0cb2SDavid du Colombier 	pass = hs->c->url->passwd;
1329dfc0cb2SDavid du Colombier 	if(user==nil || pass==nil){
1339dfc0cb2SDavid du Colombier 		realm = unquote(line, &line);
1349dfc0cb2SDavid du Colombier 		fmtstrinit(&fmt);
1359dfc0cb2SDavid du Colombier 		name = servername(hs->netaddr);
1369dfc0cb2SDavid du Colombier 		fmtprint(&fmt, "proto=pass service=http server=%q realm=%q", name, realm);
1379dfc0cb2SDavid du Colombier 		free(name);
1389dfc0cb2SDavid du Colombier 		if(hs->c->url->user)
1399dfc0cb2SDavid du Colombier 			fmtprint(&fmt, " user=%q", hs->c->url->user);
1409dfc0cb2SDavid du Colombier 		spec = fmtstrflush(&fmt);
1419dfc0cb2SDavid du Colombier 		if(spec == nil)
1429dfc0cb2SDavid du Colombier 			goto error;
1439dfc0cb2SDavid du Colombier 		if((up = auth_getuserpasswd(nil, "%s", spec)) == nil)
1449dfc0cb2SDavid du Colombier 			goto error;
1459dfc0cb2SDavid du Colombier 		user = up->user;
1469dfc0cb2SDavid du Colombier 		pass = up->passwd;
1479dfc0cb2SDavid du Colombier 	}
1489dfc0cb2SDavid du Colombier 	if((s = smprint("%s:%s", user, pass)) == nil)
1499dfc0cb2SDavid du Colombier 		goto error;
1509dfc0cb2SDavid du Colombier 	free(up);
1519dfc0cb2SDavid du Colombier 	enc64(cred, sizeof(cred), (uchar*)s, strlen(s));
1529dfc0cb2SDavid du Colombier 	memset(s, 0, strlen(s));
1539dfc0cb2SDavid du Colombier 	free(s);
1549dfc0cb2SDavid du Colombier 	hs->credentials = smprint("Basic %s", cred);
1559dfc0cb2SDavid du Colombier 	if(hs->credentials == nil)
1569dfc0cb2SDavid du Colombier 		goto error;
1579dfc0cb2SDavid du Colombier 	return;
1589dfc0cb2SDavid du Colombier 
1599dfc0cb2SDavid du Colombier error:
1609dfc0cb2SDavid du Colombier 	free(up);
1619dfc0cb2SDavid du Colombier 	free(spec);
1629dfc0cb2SDavid du Colombier 	snprint(hs->autherror, sizeof hs->autherror, "%r");
163ac84fd08SDavid du Colombier 	fprint(2, "%s: Authentication failed: %r\n", argv0);
1649dfc0cb2SDavid du Colombier }
1659dfc0cb2SDavid du Colombier 
1669a747e4fSDavid du Colombier struct {
1679a747e4fSDavid du Colombier 	char *name;									/* Case-insensitive */
1689a747e4fSDavid du Colombier 	void (*fn)(HttpState *hs, char *value);
1699a747e4fSDavid du Colombier } hdrtab[] = {
1709a747e4fSDavid du Colombier 	{ "location:", location },
1719a747e4fSDavid du Colombier 	{ "content-type:", contenttype },
1729a747e4fSDavid du Colombier 	{ "set-cookie:", setcookie },
1739dfc0cb2SDavid du Colombier 	{ "www-authenticate:", wwwauthenticate },
1749a747e4fSDavid du Colombier };
1759a747e4fSDavid du Colombier 
1769a747e4fSDavid du Colombier static int
1779a747e4fSDavid du Colombier httprcode(HttpState *hs)
1789a747e4fSDavid du Colombier {
1799a747e4fSDavid du Colombier 	int n;
1809a747e4fSDavid du Colombier 	char *p;
1819a747e4fSDavid du Colombier 	char buf[256];
1829a747e4fSDavid du Colombier 
1839a747e4fSDavid du Colombier 	n = readline(&hs->b, buf, sizeof(buf)-1);
1849a747e4fSDavid du Colombier 	if(n <= 0)
1859a747e4fSDavid du Colombier 		return n;
1869a747e4fSDavid du Colombier 	if(httpdebug)
1879a747e4fSDavid du Colombier 		fprint(2, "-> %s\n", buf);
1889a747e4fSDavid du Colombier 	p = strchr(buf, ' ');
1899a747e4fSDavid du Colombier 	if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){
1909a747e4fSDavid du Colombier 		werrstr("bad response from server");
1919a747e4fSDavid du Colombier 		return -1;
1929a747e4fSDavid du Colombier 	}
1939a747e4fSDavid du Colombier 	buf[n] = 0;
1949a747e4fSDavid du Colombier 	return atoi(p+1);
1959a747e4fSDavid du Colombier }
1969a747e4fSDavid du Colombier 
1979a747e4fSDavid du Colombier /*
1989a747e4fSDavid du Colombier  *  read a single mime header, collect continuations.
1999a747e4fSDavid du Colombier  *
2009a747e4fSDavid du Colombier  *  this routine assumes that there is a blank line twixt
2019a747e4fSDavid du Colombier  *  the header and the message body, otherwise bytes will
2029a747e4fSDavid du Colombier  *  be lost.
2039a747e4fSDavid du Colombier  */
2049a747e4fSDavid du Colombier static int
2059a747e4fSDavid du Colombier getheader(HttpState *hs, char *buf, int n)
2069a747e4fSDavid du Colombier {
2079a747e4fSDavid du Colombier 	char *p, *e;
2089a747e4fSDavid du Colombier 	int i;
2099a747e4fSDavid du Colombier 
2109a747e4fSDavid du Colombier 	n--;
2119a747e4fSDavid du Colombier 	p = buf;
2129a747e4fSDavid du Colombier 	for(e = p + n; ; p += i){
2139a747e4fSDavid du Colombier 		i = readline(&hs->b, p, e-p);
2149a747e4fSDavid du Colombier 		if(i < 0)
2159a747e4fSDavid du Colombier 			return i;
2169a747e4fSDavid du Colombier 
2179a747e4fSDavid du Colombier 		if(p == buf){
2189a747e4fSDavid du Colombier 			/* first line */
2199a747e4fSDavid du Colombier 			if(strchr(buf, ':') == nil)
2209a747e4fSDavid du Colombier 				break;		/* end of headers */
2219a747e4fSDavid du Colombier 		} else {
2229a747e4fSDavid du Colombier 			/* continuation line */
2239a747e4fSDavid du Colombier 			if(*p != ' ' && *p != '\t'){
2249a747e4fSDavid du Colombier 				unreadline(&hs->b, p);
2259a747e4fSDavid du Colombier 				*p = 0;
2269a747e4fSDavid du Colombier 				break;		/* end of this header */
2279a747e4fSDavid du Colombier 			}
2289a747e4fSDavid du Colombier 		}
2299a747e4fSDavid du Colombier 	}
2309a747e4fSDavid du Colombier 
2319a747e4fSDavid du Colombier 	if(httpdebug)
2329a747e4fSDavid du Colombier 		fprint(2, "-> %s\n", buf);
2339a747e4fSDavid du Colombier 	return p-buf;
2349a747e4fSDavid du Colombier }
2359a747e4fSDavid du Colombier 
2369a747e4fSDavid du Colombier static int
2379a747e4fSDavid du Colombier httpheaders(HttpState *hs)
2389a747e4fSDavid du Colombier {
2399a747e4fSDavid du Colombier 	char buf[2048];
2409a747e4fSDavid du Colombier 	char *p;
2419a747e4fSDavid du Colombier 	int i, n;
2429a747e4fSDavid du Colombier 
2439a747e4fSDavid du Colombier 	for(;;){
2449a747e4fSDavid du Colombier 		n = getheader(hs, buf, sizeof(buf));
2459a747e4fSDavid du Colombier 		if(n < 0)
2469a747e4fSDavid du Colombier 			return -1;
2479a747e4fSDavid du Colombier 		if(n == 0)
2489a747e4fSDavid du Colombier 			return 0;
2499a747e4fSDavid du Colombier 		//	print("http header: '%.*s'\n", n, buf);
2509a747e4fSDavid du Colombier 		for(i = 0; i < nelem(hdrtab); i++){
2519a747e4fSDavid du Colombier 			n = strlen(hdrtab[i].name);
2529a747e4fSDavid du Colombier 			if(cistrncmp(buf, hdrtab[i].name, n) == 0){
2539a747e4fSDavid du Colombier 				/* skip field name and leading white */
2549a747e4fSDavid du Colombier 				p = buf + n;
2559a747e4fSDavid du Colombier 				while(*p == ' ' || *p == '\t')
2569a747e4fSDavid du Colombier 					p++;
2579a747e4fSDavid du Colombier 				(*hdrtab[i].fn)(hs, p);
2589a747e4fSDavid du Colombier 				break;
2599a747e4fSDavid du Colombier 			}
2609a747e4fSDavid du Colombier 		}
2619a747e4fSDavid du Colombier 	}
2629a747e4fSDavid du Colombier }
2639a747e4fSDavid du Colombier 
2649a747e4fSDavid du Colombier int
2659a747e4fSDavid du Colombier httpopen(Client *c, Url *url)
2669a747e4fSDavid du Colombier {
2679dfc0cb2SDavid du Colombier 	int fd, code, redirect, authenticate;
2689a747e4fSDavid du Colombier 	char *cookies;
2699a747e4fSDavid du Colombier 	Ioproc *io;
2709a747e4fSDavid du Colombier 	HttpState *hs;
271*360053c8SDavid du Colombier 	char *service;
2729a747e4fSDavid du Colombier 
2739a747e4fSDavid du Colombier 	if(httpdebug)
2749a747e4fSDavid du Colombier 		fprint(2, "httpopen\n");
2759a747e4fSDavid du Colombier 	io = c->io;
2769a747e4fSDavid du Colombier 	hs = emalloc(sizeof(*hs));
2779a747e4fSDavid du Colombier 	hs->c = c;
278*360053c8SDavid du Colombier 
279*360053c8SDavid du Colombier 	if(url->port)
280*360053c8SDavid du Colombier 		service = url->port;
281*360053c8SDavid du Colombier 	else
282*360053c8SDavid du Colombier 		service = url->scheme;
283*360053c8SDavid du Colombier 	hs->netaddr = estrdup(netmkaddr(url->host, 0, service));
2849a747e4fSDavid du Colombier 	c->aux = hs;
285*360053c8SDavid du Colombier 	if(httpdebug){
2869a747e4fSDavid du Colombier 		fprint(2, "dial %s\n", hs->netaddr);
287*360053c8SDavid du Colombier 		fprint(2, "dial port: %s\n", url->port);
288*360053c8SDavid du Colombier 	}
2893ff48bf5SDavid du Colombier 	fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps);
2909a747e4fSDavid du Colombier 	if(fd < 0){
2919a747e4fSDavid du Colombier 	Error:
2929a747e4fSDavid du Colombier 		if(httpdebug)
2933ff48bf5SDavid du Colombier 			fprint(2, "iodial: %r\n");
294ac84fd08SDavid du Colombier 		free(hs->location);
295ac84fd08SDavid du Colombier 		free(hs->setcookie);
2969a747e4fSDavid du Colombier 		free(hs->netaddr);
297ac84fd08SDavid du Colombier 		free(hs->credentials);
298ac84fd08SDavid du Colombier 		if(fd >= 0)
299ac84fd08SDavid du Colombier 			ioclose(io, hs->fd);
3009a747e4fSDavid du Colombier 		hs->fd = -1;
301e288d156SDavid du Colombier 		free(hs);
3029a747e4fSDavid du Colombier 		c->aux = nil;
3039a747e4fSDavid du Colombier 		return -1;
3049a747e4fSDavid du Colombier 	}
3059a747e4fSDavid du Colombier 	hs->fd = fd;
3069a747e4fSDavid du Colombier 	if(httpdebug)
3079a747e4fSDavid du Colombier 		fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n",
3089a747e4fSDavid du Colombier 			c->havepostbody? "POST": "GET", url->http.page_spec, url->host);
3093ff48bf5SDavid du Colombier 	ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n",
3109a747e4fSDavid du Colombier 		c->havepostbody? "POST" : "GET", url->http.page_spec, url->host);
3119a747e4fSDavid du Colombier 	if(httpdebug)
3129a747e4fSDavid du Colombier 		fprint(2, "<- User-Agent: %s\n", c->ctl.useragent);
3139a747e4fSDavid du Colombier 	if(c->ctl.useragent)
3143ff48bf5SDavid du Colombier 		ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent);
3159a747e4fSDavid du Colombier 	if(c->ctl.sendcookies){
3169a747e4fSDavid du Colombier 		/* should we use url->page here?  sometimes it is nil. */
3179a747e4fSDavid du Colombier 		cookies = httpcookies(url->host, url->http.page_spec, 0);
3189a747e4fSDavid du Colombier 		if(cookies && cookies[0])
3193ff48bf5SDavid du Colombier 			ioprint(io, fd, "%s", cookies);
3209a747e4fSDavid du Colombier 		if(httpdebug)
3219a747e4fSDavid du Colombier 			fprint(2, "<- %s", cookies);
3229a747e4fSDavid du Colombier 		free(cookies);
3239a747e4fSDavid du Colombier 	}
3249a747e4fSDavid du Colombier 	if(c->havepostbody){
3253ff48bf5SDavid du Colombier 		ioprint(io, fd, "Content-type: %s\r\n", PostContentType);
3263ff48bf5SDavid du Colombier 		ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody);
3279a747e4fSDavid du Colombier 		if(httpdebug){
3289a747e4fSDavid du Colombier 			fprint(2, "<- Content-type: %s\n", PostContentType);
3299a747e4fSDavid du Colombier 			fprint(2, "<- Content-length: %ud\n", c->npostbody);
3309a747e4fSDavid du Colombier 		}
3319a747e4fSDavid du Colombier 	}
3329dfc0cb2SDavid du Colombier 	if(c->authenticate){
3339dfc0cb2SDavid du Colombier 		ioprint(io, fd, "Authorization: %s\r\n", c->authenticate);
3349dfc0cb2SDavid du Colombier 		if(httpdebug)
3359dfc0cb2SDavid du Colombier 			fprint(2, "<- Authorization: %s\n", c->authenticate);
3369dfc0cb2SDavid du Colombier 	}
3373ff48bf5SDavid du Colombier 	ioprint(io, fd, "\r\n");
3389a747e4fSDavid du Colombier 	if(c->havepostbody)
3393ff48bf5SDavid du Colombier 		if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody)
3409a747e4fSDavid du Colombier 			goto Error;
3419a747e4fSDavid du Colombier 
3429a747e4fSDavid du Colombier 	redirect = 0;
3439dfc0cb2SDavid du Colombier 	authenticate = 0;
3449a747e4fSDavid du Colombier 	initibuf(&hs->b, io, fd);
3459a747e4fSDavid du Colombier 	code = httprcode(hs);
3469a747e4fSDavid du Colombier 
3479a747e4fSDavid du Colombier 	switch(code){
3489a747e4fSDavid du Colombier 	case -1:	/* connection timed out */
3499a747e4fSDavid du Colombier 		goto Error;
3509a747e4fSDavid du Colombier 
3519a747e4fSDavid du Colombier /*
3529a747e4fSDavid du Colombier 	case Eof:
3539a747e4fSDavid du Colombier 		werrstr("EOF from HTTP server");
3549a747e4fSDavid du Colombier 		goto Error;
3559a747e4fSDavid du Colombier */
3569a747e4fSDavid du Colombier 
3579a747e4fSDavid du Colombier 	case 200:	/* OK */
3589a747e4fSDavid du Colombier 	case 201:	/* Created */
3599a747e4fSDavid du Colombier 	case 202:	/* Accepted */
3609a747e4fSDavid du Colombier 	case 204:	/* No Content */
361de8abbc9SDavid du Colombier 	case 205: /* Reset Content */
3629a747e4fSDavid du Colombier #ifdef NOT_DEFINED
3639a747e4fSDavid du Colombier 		if(ofile == nil && r->start != 0)
3649a747e4fSDavid du Colombier 			sysfatal("page changed underfoot");
3659a747e4fSDavid du Colombier #endif
3669a747e4fSDavid du Colombier 		break;
3679a747e4fSDavid du Colombier 
3689a747e4fSDavid du Colombier 	case 206:	/* Partial Content */
3699a747e4fSDavid du Colombier 		werrstr("Partial Content (206)");
3709a747e4fSDavid du Colombier 		goto Error;
3719a747e4fSDavid du Colombier 
3729a747e4fSDavid du Colombier 	case 301:	/* Moved Permanently */
3739a747e4fSDavid du Colombier 	case 302:	/* Moved Temporarily */
3747c70c028SDavid du Colombier 	case 303:	/* See Other */
375de8abbc9SDavid du Colombier 	case 307: /* Temporary Redirect  */
3769a747e4fSDavid du Colombier 		redirect = 1;
3779a747e4fSDavid du Colombier 		break;
3789a747e4fSDavid du Colombier 
3799a747e4fSDavid du Colombier 	case 304:	/* Not Modified */
3809a747e4fSDavid du Colombier 		break;
3819a747e4fSDavid du Colombier 
3829a747e4fSDavid du Colombier 	case 400:	/* Bad Request */
3839a747e4fSDavid du Colombier 		werrstr("Bad Request (400)");
3849a747e4fSDavid du Colombier 		goto Error;
3859a747e4fSDavid du Colombier 
3869a747e4fSDavid du Colombier 	case 401:	/* Unauthorized */
3879dfc0cb2SDavid du Colombier 		if(c->authenticate){
3889dfc0cb2SDavid du Colombier 			werrstr("Authentication failed (401)");
3899dfc0cb2SDavid du Colombier 			goto Error;
3909dfc0cb2SDavid du Colombier 		}
3919dfc0cb2SDavid du Colombier 		authenticate = 1;
3929dfc0cb2SDavid du Colombier 		break;
393de8abbc9SDavid du Colombier 	case 402:	/* Payment Required */
394de8abbc9SDavid du Colombier 		werrstr("Payment Required (402)");
3959a747e4fSDavid du Colombier 		goto Error;
3969a747e4fSDavid du Colombier 
3979a747e4fSDavid du Colombier 	case 403:	/* Forbidden */
3989a747e4fSDavid du Colombier 		werrstr("Forbidden by server (403)");
3999a747e4fSDavid du Colombier 		goto Error;
4009a747e4fSDavid du Colombier 
4019a747e4fSDavid du Colombier 	case 404:	/* Not Found */
4029a747e4fSDavid du Colombier 		werrstr("Not found on server (404)");
4039a747e4fSDavid du Colombier 		goto Error;
4049a747e4fSDavid du Colombier 
40535393782SDavid du Colombier 	case 405:	/* Method Not Allowed  */
40635393782SDavid du Colombier 		werrstr("Method not allowed (405)");
40735393782SDavid du Colombier 		goto Error;
40835393782SDavid du Colombier 
409de8abbc9SDavid du Colombier 	case 406: /* Not Acceptable */
410de8abbc9SDavid du Colombier 		werrstr("Not Acceptable (406)");
411de8abbc9SDavid du Colombier 		goto Error;
412de8abbc9SDavid du Colombier 
4139dfc0cb2SDavid du Colombier 	case 407:	/* Proxy auth */
4149dfc0cb2SDavid du Colombier 		werrstr("Proxy authentication required (407)");
4159dfc0cb2SDavid du Colombier 		goto Error;
4169dfc0cb2SDavid du Colombier 
417de8abbc9SDavid du Colombier 	case 408: /* Request Timeout */
418de8abbc9SDavid du Colombier 		werrstr("Request Timeout (408)");
419de8abbc9SDavid du Colombier 		goto Error;
420de8abbc9SDavid du Colombier 
421de8abbc9SDavid du Colombier 	case 409: /* Conflict */
422de8abbc9SDavid du Colombier 		werrstr("Conflict  (409)");
423de8abbc9SDavid du Colombier 		goto Error;
424de8abbc9SDavid du Colombier 
425de8abbc9SDavid du Colombier 	case 410: /* Gone */
426de8abbc9SDavid du Colombier 		werrstr("Gone  (410)");
427de8abbc9SDavid du Colombier 		goto Error;
428de8abbc9SDavid du Colombier 
429de8abbc9SDavid du Colombier 	case 411: /* Length Required */
430de8abbc9SDavid du Colombier 		werrstr("Length Required  (411)");
431de8abbc9SDavid du Colombier 		goto Error;
432de8abbc9SDavid du Colombier 
433de8abbc9SDavid du Colombier 	case 412: /* Precondition Failed */
434de8abbc9SDavid du Colombier 		werrstr("Precondition Failed  (412)");
435de8abbc9SDavid du Colombier 		goto Error;
436de8abbc9SDavid du Colombier 
437de8abbc9SDavid du Colombier 	case 413: /* Request Entity Too Large */
438de8abbc9SDavid du Colombier 		werrstr("Request Entity Too Large  (413)");
439de8abbc9SDavid du Colombier 		goto Error;
440de8abbc9SDavid du Colombier 
441de8abbc9SDavid du Colombier 	case 414: /* Request-URI Too Long */
442de8abbc9SDavid du Colombier 		werrstr("Request-URI Too Long  (414)");
443de8abbc9SDavid du Colombier 		goto Error;
444de8abbc9SDavid du Colombier 
445de8abbc9SDavid du Colombier 	case 415: /* Unsupported Media Type */
446de8abbc9SDavid du Colombier 		werrstr("Unsupported Media Type  (415)");
447de8abbc9SDavid du Colombier 		goto Error;
448de8abbc9SDavid du Colombier 
449de8abbc9SDavid du Colombier 	case 416: /* Requested Range Not Satisfiable */
450de8abbc9SDavid du Colombier 		werrstr("Requested Range Not Satisfiable  (416)");
451de8abbc9SDavid du Colombier 		goto Error;
452de8abbc9SDavid du Colombier 
453de8abbc9SDavid du Colombier 	case 417: /* Expectation Failed */
454de8abbc9SDavid du Colombier 		werrstr("Expectation Failed  (417)");
455de8abbc9SDavid du Colombier 		goto Error;
456de8abbc9SDavid du Colombier 
4579a747e4fSDavid du Colombier 	case 500:	/* Internal server error */
4589a747e4fSDavid du Colombier 		werrstr("Server choked (500)");
4599a747e4fSDavid du Colombier 		goto Error;
4609a747e4fSDavid du Colombier 
4619a747e4fSDavid du Colombier 	case 501:	/* Not implemented */
4629a747e4fSDavid du Colombier 		werrstr("Server can't do it (501)");
4639a747e4fSDavid du Colombier 		goto Error;
4649a747e4fSDavid du Colombier 
4659a747e4fSDavid du Colombier 	case 502:	/* Bad gateway */
4669a747e4fSDavid du Colombier 		werrstr("Bad gateway (502)");
4679a747e4fSDavid du Colombier 		goto Error;
4689a747e4fSDavid du Colombier 
4699a747e4fSDavid du Colombier 	case 503:	/* Service unavailable */
4709a747e4fSDavid du Colombier 		werrstr("Service unavailable (503)");
4719a747e4fSDavid du Colombier 		goto Error;
4729a747e4fSDavid du Colombier 
4739a747e4fSDavid du Colombier 	default:
4749a747e4fSDavid du Colombier 		/* Bogus: we should treat unknown code XYZ as code X00 */
4759a747e4fSDavid du Colombier 		werrstr("Unknown response code %d", code);
4769a747e4fSDavid du Colombier 		goto Error;
4779a747e4fSDavid du Colombier 	}
4789a747e4fSDavid du Colombier 
4799a747e4fSDavid du Colombier 	if(httpheaders(hs) < 0)
4809a747e4fSDavid du Colombier 		goto Error;
4819a747e4fSDavid du Colombier 	if(c->ctl.acceptcookies && hs->setcookie)
4829a747e4fSDavid du Colombier 		httpsetcookie(hs->setcookie, url->host, url->path);
4839dfc0cb2SDavid du Colombier 	if(authenticate){
4849dfc0cb2SDavid du Colombier 		if(!hs->credentials){
4859dfc0cb2SDavid du Colombier 			if(hs->autherror[0])
4869dfc0cb2SDavid du Colombier 				werrstr("%s", hs->autherror);
4879dfc0cb2SDavid du Colombier 			else
4889dfc0cb2SDavid du Colombier 				werrstr("unauthorized; no www-authenticate: header");
489ac84fd08SDavid du Colombier 			goto Error;
4909dfc0cb2SDavid du Colombier 		}
4919dfc0cb2SDavid du Colombier 		c->authenticate = hs->credentials;
4929dfc0cb2SDavid du Colombier 		hs->credentials = nil;
4939dfc0cb2SDavid du Colombier 	}
4949a747e4fSDavid du Colombier 	if(redirect){
4959a747e4fSDavid du Colombier 		if(!hs->location){
4969a747e4fSDavid du Colombier 			werrstr("redirection without Location: header");
497ac84fd08SDavid du Colombier 			goto Error;
4989a747e4fSDavid du Colombier 		}
4999a747e4fSDavid du Colombier 		c->redirect = hs->location;
5009a747e4fSDavid du Colombier 		hs->location = nil;
5019a747e4fSDavid du Colombier 	}
5029a747e4fSDavid du Colombier 	return 0;
5039a747e4fSDavid du Colombier }
5049a747e4fSDavid du Colombier 
5059a747e4fSDavid du Colombier int
5069a747e4fSDavid du Colombier httpread(Client *c, Req *r)
5079a747e4fSDavid du Colombier {
5089a747e4fSDavid du Colombier 	HttpState *hs;
5091066d6deSDavid du Colombier 	long n;
5109a747e4fSDavid du Colombier 
5119a747e4fSDavid du Colombier 	hs = c->aux;
5121066d6deSDavid du Colombier 	n = readibuf(&hs->b, r->ofcall.data, r->ifcall.count);
5131066d6deSDavid du Colombier 	if(n < 0)
5149a747e4fSDavid du Colombier 		return -1;
5151066d6deSDavid du Colombier 
5161066d6deSDavid du Colombier 	r->ofcall.count = n;
5179a747e4fSDavid du Colombier 	return 0;
5189a747e4fSDavid du Colombier }
5199a747e4fSDavid du Colombier 
5209a747e4fSDavid du Colombier void
5219a747e4fSDavid du Colombier httpclose(Client *c)
5229a747e4fSDavid du Colombier {
5239a747e4fSDavid du Colombier 	HttpState *hs;
5249a747e4fSDavid du Colombier 
5259a747e4fSDavid du Colombier 	hs = c->aux;
5266b6b9ac8SDavid du Colombier 	if(hs == nil)
5276b6b9ac8SDavid du Colombier 		return;
528ac84fd08SDavid du Colombier 	if(hs->fd >= 0)
5293ff48bf5SDavid du Colombier 		ioclose(c->io, hs->fd);
5309a747e4fSDavid du Colombier 	hs->fd = -1;
5319a747e4fSDavid du Colombier 	free(hs->location);
5329a747e4fSDavid du Colombier 	free(hs->setcookie);
5339a747e4fSDavid du Colombier 	free(hs->netaddr);
5349dfc0cb2SDavid du Colombier 	free(hs->credentials);
5359a747e4fSDavid du Colombier 	free(hs);
5369a747e4fSDavid du Colombier 	c->aux = nil;
5379a747e4fSDavid du Colombier }
538