xref: /plan9/sys/src/cmd/webfs/http.c (revision 9dfc0cb2ef9edafc5a26f7e8dbe4ef1ca2b37021)
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>
9*9dfc0cb2SDavid du Colombier #include <libsec.h>
10*9dfc0cb2SDavid du Colombier #include <auth.h>
119a747e4fSDavid du Colombier #include "dat.h"
129a747e4fSDavid du Colombier #include "fns.h"
139a747e4fSDavid du Colombier 
149a747e4fSDavid du Colombier char PostContentType[] = "application/octet-stream";
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;
25*9dfc0cb2SDavid du Colombier 	char *credentials;
26*9dfc0cb2SDavid 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 {
409a747e4fSDavid du Colombier 	if(hs->c->contenttype == nil)
419a747e4fSDavid du Colombier 		hs->c->contenttype = estrdup(value);
429a747e4fSDavid du Colombier }
439a747e4fSDavid du Colombier 
449a747e4fSDavid du Colombier static void
459a747e4fSDavid du Colombier setcookie(HttpState *hs, char *value)
469a747e4fSDavid du Colombier {
479a747e4fSDavid du Colombier 	char *s, *t;
489a747e4fSDavid du Colombier 	Fmt f;
499a747e4fSDavid du Colombier 
509a747e4fSDavid du Colombier 	s = hs->setcookie;
519a747e4fSDavid du Colombier 	fmtstrinit(&f);
529a747e4fSDavid du Colombier 	if(s)
539a747e4fSDavid du Colombier 		fmtprint(&f, "%s", s);
549a747e4fSDavid du Colombier 	fmtprint(&f, "set-cookie: ");
559a747e4fSDavid du Colombier 	fmtprint(&f, "%s", value);
569a747e4fSDavid du Colombier 	fmtprint(&f, "\n");
579a747e4fSDavid du Colombier 	t = fmtstrflush(&f);
589a747e4fSDavid du Colombier 	if(t){
599a747e4fSDavid du Colombier 		free(s);
609a747e4fSDavid du Colombier 		hs->setcookie = t;
619a747e4fSDavid du Colombier 	}
629a747e4fSDavid du Colombier }
639a747e4fSDavid du Colombier 
64*9dfc0cb2SDavid du Colombier static char*
65*9dfc0cb2SDavid du Colombier unquote(char *s, char **ps)
66*9dfc0cb2SDavid du Colombier {
67*9dfc0cb2SDavid du Colombier 	char *p;
68*9dfc0cb2SDavid du Colombier 
69*9dfc0cb2SDavid du Colombier 	if(*s != '"'){
70*9dfc0cb2SDavid du Colombier 		p = strpbrk(s, " \t\r\n");
71*9dfc0cb2SDavid du Colombier 		*p++ = 0;
72*9dfc0cb2SDavid du Colombier 		*ps = p;
73*9dfc0cb2SDavid du Colombier 		return s;
74*9dfc0cb2SDavid du Colombier 	}
75*9dfc0cb2SDavid du Colombier 	for(p=s+1; *p; p++){
76*9dfc0cb2SDavid du Colombier 		if(*p == '\"'){
77*9dfc0cb2SDavid du Colombier 			*p++ = 0;
78*9dfc0cb2SDavid du Colombier 			break;
79*9dfc0cb2SDavid du Colombier 		}
80*9dfc0cb2SDavid du Colombier 		if(*p == '\\' && *(p+1)){
81*9dfc0cb2SDavid du Colombier 			p++;
82*9dfc0cb2SDavid du Colombier 			continue;
83*9dfc0cb2SDavid du Colombier 		}
84*9dfc0cb2SDavid du Colombier 	}
85*9dfc0cb2SDavid du Colombier 	memmove(s, s+1, p-(s+1));
86*9dfc0cb2SDavid du Colombier 	s[p-(s+1)] = 0;
87*9dfc0cb2SDavid du Colombier 	*ps = p;
88*9dfc0cb2SDavid du Colombier 	return s;
89*9dfc0cb2SDavid du Colombier }
90*9dfc0cb2SDavid du Colombier 
91*9dfc0cb2SDavid du Colombier static char*
92*9dfc0cb2SDavid du Colombier servername(char *addr)
93*9dfc0cb2SDavid du Colombier {
94*9dfc0cb2SDavid du Colombier 	char *p;
95*9dfc0cb2SDavid du Colombier 
96*9dfc0cb2SDavid du Colombier 	if(strncmp(addr, "tcp!", 4) == 0
97*9dfc0cb2SDavid du Colombier 	|| strncmp(addr, "net!", 4) == 0)
98*9dfc0cb2SDavid du Colombier 		addr += 4;
99*9dfc0cb2SDavid du Colombier 	addr = estrdup(addr);
100*9dfc0cb2SDavid du Colombier 	p = addr+strlen(addr);
101*9dfc0cb2SDavid du Colombier 	if(p>addr && *(p-1) == 's')
102*9dfc0cb2SDavid du Colombier 		p--;
103*9dfc0cb2SDavid du Colombier 	if(p>addr+5 && strcmp(p-5, "!http") == 0)
104*9dfc0cb2SDavid du Colombier 		p[-5] = 0;
105*9dfc0cb2SDavid du Colombier 	return addr;
106*9dfc0cb2SDavid du Colombier }
107*9dfc0cb2SDavid du Colombier 
108*9dfc0cb2SDavid du Colombier void
109*9dfc0cb2SDavid du Colombier wwwauthenticate(HttpState *hs, char *line)
110*9dfc0cb2SDavid du Colombier {
111*9dfc0cb2SDavid du Colombier 	char cred[64], *user, *pass, *realm, *s, *spec, *name;
112*9dfc0cb2SDavid du Colombier 	Fmt fmt;
113*9dfc0cb2SDavid du Colombier 	UserPasswd *up;
114*9dfc0cb2SDavid du Colombier 
115*9dfc0cb2SDavid du Colombier 	spec = nil;
116*9dfc0cb2SDavid du Colombier 	up = nil;
117*9dfc0cb2SDavid du Colombier 	cred[0] = 0;
118*9dfc0cb2SDavid du Colombier 	hs->autherror[0] = 0;
119*9dfc0cb2SDavid du Colombier 	if(cistrncmp(line, "basic ", 6) != 0){
120*9dfc0cb2SDavid du Colombier 		werrstr("unknown auth: %s", line);
121*9dfc0cb2SDavid du Colombier 		goto error;
122*9dfc0cb2SDavid du Colombier 	}
123*9dfc0cb2SDavid du Colombier 	line += 6;
124*9dfc0cb2SDavid du Colombier 	if(cistrncmp(line, "realm=", 6) != 0){
125*9dfc0cb2SDavid du Colombier 		werrstr("missing realm: %s", line);
126*9dfc0cb2SDavid du Colombier 		goto error;
127*9dfc0cb2SDavid du Colombier 	}
128*9dfc0cb2SDavid du Colombier 	line += 6;
129*9dfc0cb2SDavid du Colombier 	user = hs->c->url->user;
130*9dfc0cb2SDavid du Colombier 	pass = hs->c->url->passwd;
131*9dfc0cb2SDavid du Colombier 	if(user==nil || pass==nil){
132*9dfc0cb2SDavid du Colombier 		realm = unquote(line, &line);
133*9dfc0cb2SDavid du Colombier 		fmtstrinit(&fmt);
134*9dfc0cb2SDavid du Colombier 		name = servername(hs->netaddr);
135*9dfc0cb2SDavid du Colombier 		fmtprint(&fmt, "proto=pass service=http server=%q realm=%q", name, realm);
136*9dfc0cb2SDavid du Colombier 		free(name);
137*9dfc0cb2SDavid du Colombier 		if(hs->c->url->user)
138*9dfc0cb2SDavid du Colombier 			fmtprint(&fmt, " user=%q", hs->c->url->user);
139*9dfc0cb2SDavid du Colombier 		spec = fmtstrflush(&fmt);
140*9dfc0cb2SDavid du Colombier 		if(spec == nil)
141*9dfc0cb2SDavid du Colombier 			goto error;
142*9dfc0cb2SDavid du Colombier 		if((up = auth_getuserpasswd(nil, "%s", spec)) == nil)
143*9dfc0cb2SDavid du Colombier 			goto error;
144*9dfc0cb2SDavid du Colombier 		user = up->user;
145*9dfc0cb2SDavid du Colombier 		pass = up->passwd;
146*9dfc0cb2SDavid du Colombier 	}
147*9dfc0cb2SDavid du Colombier 	if((s = smprint("%s:%s", user, pass)) == nil)
148*9dfc0cb2SDavid du Colombier 		goto error;
149*9dfc0cb2SDavid du Colombier 	free(up);
150*9dfc0cb2SDavid du Colombier 	enc64(cred, sizeof(cred), (uchar*)s, strlen(s));
151*9dfc0cb2SDavid du Colombier 	memset(s, 0, strlen(s));
152*9dfc0cb2SDavid du Colombier 	free(s);
153*9dfc0cb2SDavid du Colombier 	hs->credentials = smprint("Basic %s", cred);
154*9dfc0cb2SDavid du Colombier 	if(hs->credentials == nil)
155*9dfc0cb2SDavid du Colombier 		goto error;
156*9dfc0cb2SDavid du Colombier 	return;
157*9dfc0cb2SDavid du Colombier 
158*9dfc0cb2SDavid du Colombier error:
159*9dfc0cb2SDavid du Colombier 	free(up);
160*9dfc0cb2SDavid du Colombier 	free(spec);
161*9dfc0cb2SDavid du Colombier 	snprint(hs->autherror, sizeof hs->autherror, "%r");
162*9dfc0cb2SDavid du Colombier }
163*9dfc0cb2SDavid du Colombier 
1649a747e4fSDavid du Colombier struct {
1659a747e4fSDavid du Colombier 	char *name;									/* Case-insensitive */
1669a747e4fSDavid du Colombier 	void (*fn)(HttpState *hs, char *value);
1679a747e4fSDavid du Colombier } hdrtab[] = {
1689a747e4fSDavid du Colombier 	{ "location:", location },
1699a747e4fSDavid du Colombier 	{ "content-type:", contenttype },
1709a747e4fSDavid du Colombier 	{ "set-cookie:", setcookie },
171*9dfc0cb2SDavid du Colombier 	{ "www-authenticate:", wwwauthenticate },
1729a747e4fSDavid du Colombier };
1739a747e4fSDavid du Colombier 
1749a747e4fSDavid du Colombier static int
1759a747e4fSDavid du Colombier httprcode(HttpState *hs)
1769a747e4fSDavid du Colombier {
1779a747e4fSDavid du Colombier 	int n;
1789a747e4fSDavid du Colombier 	char *p;
1799a747e4fSDavid du Colombier 	char buf[256];
1809a747e4fSDavid du Colombier 
1819a747e4fSDavid du Colombier 	n = readline(&hs->b, buf, sizeof(buf)-1);
1829a747e4fSDavid du Colombier 	if(n <= 0)
1839a747e4fSDavid du Colombier 		return n;
1849a747e4fSDavid du Colombier 	if(httpdebug)
1859a747e4fSDavid du Colombier 		fprint(2, "-> %s\n", buf);
1869a747e4fSDavid du Colombier 	p = strchr(buf, ' ');
1879a747e4fSDavid du Colombier 	if(memcmp(buf, "HTTP/", 5) != 0 || p == nil){
1889a747e4fSDavid du Colombier 		werrstr("bad response from server");
1899a747e4fSDavid du Colombier 		return -1;
1909a747e4fSDavid du Colombier 	}
1919a747e4fSDavid du Colombier 	buf[n] = 0;
1929a747e4fSDavid du Colombier 	return atoi(p+1);
1939a747e4fSDavid du Colombier }
1949a747e4fSDavid du Colombier 
1959a747e4fSDavid du Colombier /*
1969a747e4fSDavid du Colombier  *  read a single mime header, collect continuations.
1979a747e4fSDavid du Colombier  *
1989a747e4fSDavid du Colombier  *  this routine assumes that there is a blank line twixt
1999a747e4fSDavid du Colombier  *  the header and the message body, otherwise bytes will
2009a747e4fSDavid du Colombier  *  be lost.
2019a747e4fSDavid du Colombier  */
2029a747e4fSDavid du Colombier static int
2039a747e4fSDavid du Colombier getheader(HttpState *hs, char *buf, int n)
2049a747e4fSDavid du Colombier {
2059a747e4fSDavid du Colombier 	char *p, *e;
2069a747e4fSDavid du Colombier 	int i;
2079a747e4fSDavid du Colombier 
2089a747e4fSDavid du Colombier 	n--;
2099a747e4fSDavid du Colombier 	p = buf;
2109a747e4fSDavid du Colombier 	for(e = p + n; ; p += i){
2119a747e4fSDavid du Colombier 		i = readline(&hs->b, p, e-p);
2129a747e4fSDavid du Colombier 		if(i < 0)
2139a747e4fSDavid du Colombier 			return i;
2149a747e4fSDavid du Colombier 
2159a747e4fSDavid du Colombier 		if(p == buf){
2169a747e4fSDavid du Colombier 			/* first line */
2179a747e4fSDavid du Colombier 			if(strchr(buf, ':') == nil)
2189a747e4fSDavid du Colombier 				break;		/* end of headers */
2199a747e4fSDavid du Colombier 		} else {
2209a747e4fSDavid du Colombier 			/* continuation line */
2219a747e4fSDavid du Colombier 			if(*p != ' ' && *p != '\t'){
2229a747e4fSDavid du Colombier 				unreadline(&hs->b, p);
2239a747e4fSDavid du Colombier 				*p = 0;
2249a747e4fSDavid du Colombier 				break;		/* end of this header */
2259a747e4fSDavid du Colombier 			}
2269a747e4fSDavid du Colombier 		}
2279a747e4fSDavid du Colombier 	}
2289a747e4fSDavid du Colombier 
2299a747e4fSDavid du Colombier 	if(httpdebug)
2309a747e4fSDavid du Colombier 		fprint(2, "-> %s\n", buf);
2319a747e4fSDavid du Colombier 	return p-buf;
2329a747e4fSDavid du Colombier }
2339a747e4fSDavid du Colombier 
2349a747e4fSDavid du Colombier static int
2359a747e4fSDavid du Colombier httpheaders(HttpState *hs)
2369a747e4fSDavid du Colombier {
2379a747e4fSDavid du Colombier 	char buf[2048];
2389a747e4fSDavid du Colombier 	char *p;
2399a747e4fSDavid du Colombier 	int i, n;
2409a747e4fSDavid du Colombier 
2419a747e4fSDavid du Colombier 	for(;;){
2429a747e4fSDavid du Colombier 		n = getheader(hs, buf, sizeof(buf));
2439a747e4fSDavid du Colombier 		if(n < 0)
2449a747e4fSDavid du Colombier 			return -1;
2459a747e4fSDavid du Colombier 		if(n == 0)
2469a747e4fSDavid du Colombier 			return 0;
2479a747e4fSDavid du Colombier 		//	print("http header: '%.*s'\n", n, buf);
2489a747e4fSDavid du Colombier 		for(i = 0; i < nelem(hdrtab); i++){
2499a747e4fSDavid du Colombier 			n = strlen(hdrtab[i].name);
2509a747e4fSDavid du Colombier 			if(cistrncmp(buf, hdrtab[i].name, n) == 0){
2519a747e4fSDavid du Colombier 				/* skip field name and leading white */
2529a747e4fSDavid du Colombier 				p = buf + n;
2539a747e4fSDavid du Colombier 				while(*p == ' ' || *p == '\t')
2549a747e4fSDavid du Colombier 					p++;
2559a747e4fSDavid du Colombier 				(*hdrtab[i].fn)(hs, p);
2569a747e4fSDavid du Colombier 				break;
2579a747e4fSDavid du Colombier 			}
2589a747e4fSDavid du Colombier 		}
2599a747e4fSDavid du Colombier 	}
2609a747e4fSDavid du Colombier 	return 0;
2619a747e4fSDavid du Colombier }
2629a747e4fSDavid du Colombier 
2639a747e4fSDavid du Colombier int
2649a747e4fSDavid du Colombier httpopen(Client *c, Url *url)
2659a747e4fSDavid du Colombier {
266*9dfc0cb2SDavid du Colombier 	int fd, code, redirect, authenticate;
2679a747e4fSDavid du Colombier 	char *cookies;
2689a747e4fSDavid du Colombier 	Ioproc *io;
2699a747e4fSDavid du Colombier 	HttpState *hs;
2709a747e4fSDavid du Colombier 
2719a747e4fSDavid du Colombier 	if(httpdebug)
2729a747e4fSDavid du Colombier 		fprint(2, "httpopen\n");
2739a747e4fSDavid du Colombier 	io = c->io;
2749a747e4fSDavid du Colombier 	hs = emalloc(sizeof(*hs));
2759a747e4fSDavid du Colombier 	hs->c = c;
2769a747e4fSDavid du Colombier 	hs->netaddr = estrdup(netmkaddr(url->host, 0, url->scheme));
2779a747e4fSDavid du Colombier 	c->aux = hs;
2789a747e4fSDavid du Colombier 	if(httpdebug)
2799a747e4fSDavid du Colombier 		fprint(2, "dial %s\n", hs->netaddr);
2803ff48bf5SDavid du Colombier 	fd = iotlsdial(io, hs->netaddr, 0, 0, 0, url->ischeme==UShttps);
2819a747e4fSDavid du Colombier 	if(fd < 0){
2829a747e4fSDavid du Colombier 	Error:
2839a747e4fSDavid du Colombier 		if(httpdebug)
2843ff48bf5SDavid du Colombier 			fprint(2, "iodial: %r\n");
2859a747e4fSDavid du Colombier 		free(hs->netaddr);
2869a747e4fSDavid du Colombier 		close(hs->fd);
2879a747e4fSDavid du Colombier 		hs->fd = -1;
288e288d156SDavid du Colombier 		free(hs);
2899a747e4fSDavid du Colombier 		c->aux = nil;
2909a747e4fSDavid du Colombier 		return -1;
2919a747e4fSDavid du Colombier 	}
2929a747e4fSDavid du Colombier 	hs->fd = fd;
2939a747e4fSDavid du Colombier 	if(httpdebug)
2949a747e4fSDavid du Colombier 		fprint(2, "<- %s %s HTTP/1.0\n<- Host: %s\n",
2959a747e4fSDavid du Colombier 			c->havepostbody? "POST": " GET", url->http.page_spec, url->host);
2963ff48bf5SDavid du Colombier 	ioprint(io, fd, "%s %s HTTP/1.0\r\nHost: %s\r\n",
2979a747e4fSDavid du Colombier 		c->havepostbody? "POST" : "GET", url->http.page_spec, url->host);
2989a747e4fSDavid du Colombier 	if(httpdebug)
2999a747e4fSDavid du Colombier 		fprint(2, "<- User-Agent: %s\n", c->ctl.useragent);
3009a747e4fSDavid du Colombier 	if(c->ctl.useragent)
3013ff48bf5SDavid du Colombier 		ioprint(io, fd, "User-Agent: %s\r\n", c->ctl.useragent);
3029a747e4fSDavid du Colombier 	if(c->ctl.sendcookies){
3039a747e4fSDavid du Colombier 		/* should we use url->page here?  sometimes it is nil. */
3049a747e4fSDavid du Colombier 		cookies = httpcookies(url->host, url->http.page_spec, 0);
3059a747e4fSDavid du Colombier 		if(cookies && cookies[0])
3063ff48bf5SDavid du Colombier 			ioprint(io, fd, "%s", cookies);
3079a747e4fSDavid du Colombier 		if(httpdebug)
3089a747e4fSDavid du Colombier 			fprint(2, "<- %s", cookies);
3099a747e4fSDavid du Colombier 		free(cookies);
3109a747e4fSDavid du Colombier 	}
3119a747e4fSDavid du Colombier 	if(c->havepostbody){
3123ff48bf5SDavid du Colombier 		ioprint(io, fd, "Content-type: %s\r\n", PostContentType);
3133ff48bf5SDavid du Colombier 		ioprint(io, fd, "Content-length: %ud\r\n", c->npostbody);
3149a747e4fSDavid du Colombier 		if(httpdebug){
3159a747e4fSDavid du Colombier 			fprint(2, "<- Content-type: %s\n", PostContentType);
3169a747e4fSDavid du Colombier 			fprint(2, "<- Content-length: %ud\n", c->npostbody);
3179a747e4fSDavid du Colombier 		}
3189a747e4fSDavid du Colombier 	}
319*9dfc0cb2SDavid du Colombier 	if(c->authenticate){
320*9dfc0cb2SDavid du Colombier 		ioprint(io, fd, "Authorization: %s\r\n", c->authenticate);
321*9dfc0cb2SDavid du Colombier 		if(httpdebug)
322*9dfc0cb2SDavid du Colombier 			fprint(2, "<- Authorization: %s\n", c->authenticate);
323*9dfc0cb2SDavid du Colombier 	}
3243ff48bf5SDavid du Colombier 	ioprint(io, fd, "\r\n");
3259a747e4fSDavid du Colombier 	if(c->havepostbody)
3263ff48bf5SDavid du Colombier 		if(iowrite(io, fd, c->postbody, c->npostbody) != c->npostbody)
3279a747e4fSDavid du Colombier 			goto Error;
3289a747e4fSDavid du Colombier 
3299a747e4fSDavid du Colombier 	c->havepostbody = 0;
3309a747e4fSDavid du Colombier 	redirect = 0;
331*9dfc0cb2SDavid du Colombier 	authenticate = 0;
3329a747e4fSDavid du Colombier 	initibuf(&hs->b, io, fd);
3339a747e4fSDavid du Colombier 	code = httprcode(hs);
3349a747e4fSDavid du Colombier 
3359a747e4fSDavid du Colombier 	switch(code){
3369a747e4fSDavid du Colombier 	case -1:	/* connection timed out */
3379a747e4fSDavid du Colombier 		goto Error;
3389a747e4fSDavid du Colombier 
3399a747e4fSDavid du Colombier /*
3409a747e4fSDavid du Colombier 	case Eof:
3419a747e4fSDavid du Colombier 		werrstr("EOF from HTTP server");
3429a747e4fSDavid du Colombier 		goto Error;
3439a747e4fSDavid du Colombier */
3449a747e4fSDavid du Colombier 
3459a747e4fSDavid du Colombier 	case 200:	/* OK */
3469a747e4fSDavid du Colombier 	case 201:	/* Created */
3479a747e4fSDavid du Colombier 	case 202:	/* Accepted */
3489a747e4fSDavid du Colombier 	case 204:	/* No Content */
3499a747e4fSDavid du Colombier #ifdef NOT_DEFINED
3509a747e4fSDavid du Colombier 		if(ofile == nil && r->start != 0)
3519a747e4fSDavid du Colombier 			sysfatal("page changed underfoot");
3529a747e4fSDavid du Colombier #endif
3539a747e4fSDavid du Colombier 		break;
3549a747e4fSDavid du Colombier 
3559a747e4fSDavid du Colombier 	case 206:	/* Partial Content */
3569a747e4fSDavid du Colombier 		werrstr("Partial Content (206)");
3579a747e4fSDavid du Colombier 		goto Error;
3589a747e4fSDavid du Colombier 
3599a747e4fSDavid du Colombier 	case 301:	/* Moved Permanently */
3609a747e4fSDavid du Colombier 	case 302:	/* Moved Temporarily */
3619a747e4fSDavid du Colombier 		redirect = 1;
3629a747e4fSDavid du Colombier 		break;
3639a747e4fSDavid du Colombier 
3649a747e4fSDavid du Colombier 	case 304:	/* Not Modified */
3659a747e4fSDavid du Colombier 		break;
3669a747e4fSDavid du Colombier 
3679a747e4fSDavid du Colombier 	case 400:	/* Bad Request */
3689a747e4fSDavid du Colombier 		werrstr("Bad Request (400)");
3699a747e4fSDavid du Colombier 		goto Error;
3709a747e4fSDavid du Colombier 
3719a747e4fSDavid du Colombier 	case 401:	/* Unauthorized */
372*9dfc0cb2SDavid du Colombier 		if(c->authenticate){
373*9dfc0cb2SDavid du Colombier 			werrstr("Authentication failed (401)");
374*9dfc0cb2SDavid du Colombier 			goto Error;
375*9dfc0cb2SDavid du Colombier 		}
376*9dfc0cb2SDavid du Colombier 		authenticate = 1;
377*9dfc0cb2SDavid du Colombier 		break;
3789a747e4fSDavid du Colombier 	case 402:	/* ??? */
379*9dfc0cb2SDavid du Colombier 		werrstr("Unauthorized (402)");
3809a747e4fSDavid du Colombier 		goto Error;
3819a747e4fSDavid du Colombier 
3829a747e4fSDavid du Colombier 	case 403:	/* Forbidden */
3839a747e4fSDavid du Colombier 		werrstr("Forbidden by server (403)");
3849a747e4fSDavid du Colombier 		goto Error;
3859a747e4fSDavid du Colombier 
3869a747e4fSDavid du Colombier 	case 404:	/* Not Found */
3879a747e4fSDavid du Colombier 		werrstr("Not found on server (404)");
3889a747e4fSDavid du Colombier 		goto Error;
3899a747e4fSDavid du Colombier 
390*9dfc0cb2SDavid du Colombier 	case 407:	/* Proxy auth */
391*9dfc0cb2SDavid du Colombier 		werrstr("Proxy authentication required (407)");
392*9dfc0cb2SDavid du Colombier 		goto Error;
393*9dfc0cb2SDavid du Colombier 
3949a747e4fSDavid du Colombier 	case 500:	/* Internal server error */
3959a747e4fSDavid du Colombier 		werrstr("Server choked (500)");
3969a747e4fSDavid du Colombier 		goto Error;
3979a747e4fSDavid du Colombier 
3989a747e4fSDavid du Colombier 	case 501:	/* Not implemented */
3999a747e4fSDavid du Colombier 		werrstr("Server can't do it (501)");
4009a747e4fSDavid du Colombier 		goto Error;
4019a747e4fSDavid du Colombier 
4029a747e4fSDavid du Colombier 	case 502:	/* Bad gateway */
4039a747e4fSDavid du Colombier 		werrstr("Bad gateway (502)");
4049a747e4fSDavid du Colombier 		goto Error;
4059a747e4fSDavid du Colombier 
4069a747e4fSDavid du Colombier 	case 503:	/* Service unavailable */
4079a747e4fSDavid du Colombier 		werrstr("Service unavailable (503)");
4089a747e4fSDavid du Colombier 		goto Error;
4099a747e4fSDavid du Colombier 
4109a747e4fSDavid du Colombier 	default:
4119a747e4fSDavid du Colombier 		/* Bogus: we should treat unknown code XYZ as code X00 */
4129a747e4fSDavid du Colombier 		werrstr("Unknown response code %d", code);
4139a747e4fSDavid du Colombier 		goto Error;
4149a747e4fSDavid du Colombier 	}
4159a747e4fSDavid du Colombier 
4169a747e4fSDavid du Colombier 	if(httpheaders(hs) < 0)
4179a747e4fSDavid du Colombier 		goto Error;
4189a747e4fSDavid du Colombier 	if(c->ctl.acceptcookies && hs->setcookie)
4199a747e4fSDavid du Colombier 		httpsetcookie(hs->setcookie, url->host, url->path);
420*9dfc0cb2SDavid du Colombier 	if(authenticate){
421*9dfc0cb2SDavid du Colombier 		if(!hs->credentials){
422*9dfc0cb2SDavid du Colombier 			if(hs->autherror[0])
423*9dfc0cb2SDavid du Colombier 				werrstr("%s", hs->autherror);
424*9dfc0cb2SDavid du Colombier 			else
425*9dfc0cb2SDavid du Colombier 				werrstr("unauthorized; no www-authenticate: header");
426*9dfc0cb2SDavid du Colombier 			return -1;
427*9dfc0cb2SDavid du Colombier 		}
428*9dfc0cb2SDavid du Colombier 		c->authenticate = hs->credentials;
429*9dfc0cb2SDavid du Colombier 		hs->credentials = nil;
430*9dfc0cb2SDavid du Colombier 	}
4319a747e4fSDavid du Colombier 	if(redirect){
4329a747e4fSDavid du Colombier 		if(!hs->location){
4339a747e4fSDavid du Colombier 			werrstr("redirection without Location: header");
4349a747e4fSDavid du Colombier 			return -1;
4359a747e4fSDavid du Colombier 		}
4369a747e4fSDavid du Colombier 		c->redirect = hs->location;
4379a747e4fSDavid du Colombier 		hs->location = nil;
4389a747e4fSDavid du Colombier 	}
4399a747e4fSDavid du Colombier 	return 0;
4409a747e4fSDavid du Colombier }
4419a747e4fSDavid du Colombier 
4429a747e4fSDavid du Colombier int
4439a747e4fSDavid du Colombier httpread(Client *c, Req *r)
4449a747e4fSDavid du Colombier {
4459a747e4fSDavid du Colombier 	char *dst;
4469a747e4fSDavid du Colombier 	HttpState *hs;
4479a747e4fSDavid du Colombier 	int n;
4489a747e4fSDavid du Colombier 	long rlen, tot, len;
4499a747e4fSDavid du Colombier 
4509a747e4fSDavid du Colombier 	hs = c->aux;
4519a747e4fSDavid du Colombier 	dst = r->ofcall.data;
4529a747e4fSDavid du Colombier 	len = r->ifcall.count;
4539a747e4fSDavid du Colombier 	tot = 0;
4549a747e4fSDavid du Colombier 	while (tot < len){
4559a747e4fSDavid du Colombier 		rlen = len - tot;
4569a747e4fSDavid du Colombier 		n = readibuf(&hs->b, dst + tot, rlen);
4579a747e4fSDavid du Colombier 		if(n == 0)
4589a747e4fSDavid du Colombier 			break;
4599a747e4fSDavid du Colombier 		else if(n < 0){
4609a747e4fSDavid du Colombier 			if(tot == 0)
4619a747e4fSDavid du Colombier 				return -1;
4629a747e4fSDavid du Colombier 			else
4639a747e4fSDavid du Colombier 				return tot;
4649a747e4fSDavid du Colombier 		}
4659a747e4fSDavid du Colombier 		tot += n;
4669a747e4fSDavid du Colombier 	}
4679a747e4fSDavid du Colombier 	r->ofcall.count = tot;
4689a747e4fSDavid du Colombier 	return 0;
4699a747e4fSDavid du Colombier }
4709a747e4fSDavid du Colombier 
4719a747e4fSDavid du Colombier void
4729a747e4fSDavid du Colombier httpclose(Client *c)
4739a747e4fSDavid du Colombier {
4749a747e4fSDavid du Colombier 	HttpState *hs;
4759a747e4fSDavid du Colombier 
4769a747e4fSDavid du Colombier 	hs = c->aux;
4776b6b9ac8SDavid du Colombier 	if(hs == nil)
4786b6b9ac8SDavid du Colombier 		return;
4793ff48bf5SDavid du Colombier 	ioclose(c->io, hs->fd);
4809a747e4fSDavid du Colombier 	hs->fd = -1;
4819a747e4fSDavid du Colombier 	free(hs->location);
4829a747e4fSDavid du Colombier 	free(hs->setcookie);
4839a747e4fSDavid du Colombier 	free(hs->netaddr);
484*9dfc0cb2SDavid du Colombier 	free(hs->credentials);
4859a747e4fSDavid du Colombier 	free(hs);
4869a747e4fSDavid du Colombier 	c->aux = nil;
4879a747e4fSDavid du Colombier }
4889a747e4fSDavid du Colombier 
489