xref: /plan9/sys/src/cmd/ip/httpd/sendfd.c (revision 593081380c734f70f5bda70998607232e53df600)
17dd7cddfSDavid du Colombier #include <u.h>
27dd7cddfSDavid du Colombier #include <libc.h>
37dd7cddfSDavid du Colombier #include <auth.h>
47dd7cddfSDavid du Colombier #include "httpd.h"
580ee5cbfSDavid du Colombier #include "httpsrv.h"
67dd7cddfSDavid du Colombier 
780ee5cbfSDavid du Colombier static	void		printtype(Hio *hout, HContent *type, HContent *enc);
87dd7cddfSDavid du Colombier 
97dd7cddfSDavid du Colombier /*
107dd7cddfSDavid du Colombier  * these should be done better; see the reponse codes in /lib/rfc/rfc2616 for
117dd7cddfSDavid du Colombier  * more info on what should be included.
127dd7cddfSDavid du Colombier  */
137dd7cddfSDavid du Colombier #define UNAUTHED	"You are not authorized to see this area.\n"
147dd7cddfSDavid du Colombier #define NOCONTENT	"No acceptable type of data is available.\n"
157dd7cddfSDavid du Colombier #define NOENCODE	"No acceptable encoding of the contents is available.\n"
167dd7cddfSDavid du Colombier #define UNMATCHED	"The entity requested does not match the existing entity.\n"
177dd7cddfSDavid du Colombier #define BADRANGE	"No bytes are avaible for the range you requested.\n"
187dd7cddfSDavid du Colombier 
197dd7cddfSDavid du Colombier /*
207dd7cddfSDavid du Colombier  * fd references a file which has been authorized & checked for relocations.
21*59308138SDavid du Colombier  * send back the headers & its contents.
227dd7cddfSDavid du Colombier  * includes checks for conditional requests & ranges.
237dd7cddfSDavid du Colombier  */
247dd7cddfSDavid du Colombier int
sendfd(HConnect * c,int fd,Dir * dir,HContent * type,HContent * enc)2580ee5cbfSDavid du Colombier sendfd(HConnect *c, int fd, Dir *dir, HContent *type, HContent *enc)
267dd7cddfSDavid du Colombier {
279a747e4fSDavid du Colombier 	Qid qid;
2880ee5cbfSDavid du Colombier 	HRange *r;
2980ee5cbfSDavid du Colombier 	HContents conts;
307dd7cddfSDavid du Colombier 	Hio *hout;
317dd7cddfSDavid du Colombier 	char *boundary, etag[32];
329a747e4fSDavid du Colombier 	long mtime;
337dd7cddfSDavid du Colombier 	ulong tr;
347dd7cddfSDavid du Colombier 	int n, nw, multir, ok;
359a747e4fSDavid du Colombier 	vlong wrote, length;
367dd7cddfSDavid du Colombier 
377dd7cddfSDavid du Colombier 	hout = &c->hout;
389a747e4fSDavid du Colombier 	length = dir->length;
399a747e4fSDavid du Colombier 	mtime = dir->mtime;
409a747e4fSDavid du Colombier 	qid = dir->qid;
419a747e4fSDavid du Colombier 	free(dir);
427dd7cddfSDavid du Colombier 
437dd7cddfSDavid du Colombier 	/*
447dd7cddfSDavid du Colombier 	 * figure out the type of file and send headers
457dd7cddfSDavid du Colombier 	 */
467dd7cddfSDavid du Colombier 	n = -1;
477dd7cddfSDavid du Colombier 	r = nil;
487dd7cddfSDavid du Colombier 	multir = 0;
497dd7cddfSDavid du Colombier 	boundary = nil;
507dd7cddfSDavid du Colombier 	if(c->req.vermaj){
5159cc4ca5SDavid du Colombier 		if(type == nil && enc == nil){
5280ee5cbfSDavid du Colombier 			conts = uriclass(c, c->req.uri);
537dd7cddfSDavid du Colombier 			type = conts.type;
547dd7cddfSDavid du Colombier 			enc = conts.encoding;
557dd7cddfSDavid du Colombier 			if(type == nil && enc == nil){
5680ee5cbfSDavid du Colombier 				n = read(fd, c->xferbuf, HBufSize-1);
577dd7cddfSDavid du Colombier 				if(n > 0){
587dd7cddfSDavid du Colombier 					c->xferbuf[n] = '\0';
5980ee5cbfSDavid du Colombier 					conts = dataclass(c, c->xferbuf, n);
607dd7cddfSDavid du Colombier 					type = conts.type;
617dd7cddfSDavid du Colombier 					enc = conts.encoding;
627dd7cddfSDavid du Colombier 				}
637dd7cddfSDavid du Colombier 			}
6459cc4ca5SDavid du Colombier 		}
657dd7cddfSDavid du Colombier 		if(type == nil)
6680ee5cbfSDavid du Colombier 			type = hmkcontent(c, "application", "octet-stream", nil);
677dd7cddfSDavid du Colombier 
689a747e4fSDavid du Colombier 		snprint(etag, sizeof(etag), "\"%lluxv%lux\"", qid.path, qid.vers);
699a747e4fSDavid du Colombier 		ok = checkreq(c, type, enc, mtime, etag);
707dd7cddfSDavid du Colombier 		if(ok <= 0){
717dd7cddfSDavid du Colombier 			close(fd);
727dd7cddfSDavid du Colombier 			return ok;
737dd7cddfSDavid du Colombier 		}
747dd7cddfSDavid du Colombier 
757dd7cddfSDavid du Colombier 		/*
767dd7cddfSDavid du Colombier 		 * check for if-range requests
777dd7cddfSDavid du Colombier 		 */
787dd7cddfSDavid du Colombier 		if(c->head.range == nil
797dd7cddfSDavid du Colombier 		|| c->head.ifrangeetag != nil && !etagmatch(1, c->head.ifrangeetag, etag)
809a747e4fSDavid du Colombier 		|| c->head.ifrangedate != 0 && c->head.ifrangedate != mtime){
817dd7cddfSDavid du Colombier 			c->head.range = nil;
827dd7cddfSDavid du Colombier 			c->head.ifrangeetag = nil;
837dd7cddfSDavid du Colombier 			c->head.ifrangedate = 0;
847dd7cddfSDavid du Colombier 		}
857dd7cddfSDavid du Colombier 
867dd7cddfSDavid du Colombier 		if(c->head.range != nil){
879a747e4fSDavid du Colombier 			c->head.range = fixrange(c->head.range, length);
887dd7cddfSDavid du Colombier 			if(c->head.range == nil){
897dd7cddfSDavid du Colombier 				if(c->head.ifrangeetag == nil && c->head.ifrangedate == 0){
9080ee5cbfSDavid du Colombier 					hprint(hout, "%s 416 Request range not satisfiable\r\n", hversion);
917dd7cddfSDavid du Colombier 					hprint(hout, "Date: %D\r\n", time(nil));
927dd7cddfSDavid du Colombier 					hprint(hout, "Server: Plan9\r\n");
939a747e4fSDavid du Colombier 					hprint(hout, "Content-Range: bytes */%lld\r\n", length);
947dd7cddfSDavid du Colombier 					hprint(hout, "Content-Length: %d\r\n", STRLEN(BADRANGE));
957dd7cddfSDavid du Colombier 					hprint(hout, "Content-Type: text/html\r\n");
967dd7cddfSDavid du Colombier 					if(c->head.closeit)
977dd7cddfSDavid du Colombier 						hprint(hout, "Connection: close\r\n");
987dd7cddfSDavid du Colombier 					else if(!http11(c))
997dd7cddfSDavid du Colombier 						hprint(hout, "Connection: Keep-Alive\r\n");
1007dd7cddfSDavid du Colombier 					hprint(hout, "\r\n");
1017dd7cddfSDavid du Colombier 					if(strcmp(c->req.meth, "HEAD") != 0)
1027dd7cddfSDavid du Colombier 						hprint(hout, "%s", BADRANGE);
1039a747e4fSDavid du Colombier 					hflush(hout);
1047dd7cddfSDavid du Colombier 					writelog(c, "Reply: 416 Request range not satisfiable\n");
1059a747e4fSDavid du Colombier 					close(fd);
1067dd7cddfSDavid du Colombier 					return 1;
1077dd7cddfSDavid du Colombier 				}
1087dd7cddfSDavid du Colombier 				c->head.ifrangeetag = nil;
1097dd7cddfSDavid du Colombier 				c->head.ifrangedate = 0;
1107dd7cddfSDavid du Colombier 			}
1117dd7cddfSDavid du Colombier 		}
1127dd7cddfSDavid du Colombier 		if(c->head.range == nil)
11380ee5cbfSDavid du Colombier 			hprint(hout, "%s 200 OK\r\n", hversion);
1147dd7cddfSDavid du Colombier 		else
11580ee5cbfSDavid du Colombier 			hprint(hout, "%s 206 Partial Content\r\n", hversion);
1167dd7cddfSDavid du Colombier 
1177dd7cddfSDavid du Colombier 		hprint(hout, "Server: Plan9\r\n");
1187dd7cddfSDavid du Colombier 		hprint(hout, "Date: %D\r\n", time(nil));
1197dd7cddfSDavid du Colombier 		hprint(hout, "ETag: %s\r\n", etag);
1207dd7cddfSDavid du Colombier 
1217dd7cddfSDavid du Colombier 		/*
1227dd7cddfSDavid du Colombier 		 * can't send some entity headers if partially responding
1237dd7cddfSDavid du Colombier 		 * to an if-range: etag request
1247dd7cddfSDavid du Colombier 		 */
1257dd7cddfSDavid du Colombier 		r = c->head.range;
1267dd7cddfSDavid du Colombier 		if(r == nil)
1279a747e4fSDavid du Colombier 			hprint(hout, "Content-Length: %lld\r\n", length);
1282110726aSDavid du Colombier 		else if(r->next == nil){
1299a747e4fSDavid du Colombier 			hprint(hout, "Content-Range: bytes %ld-%ld/%lld\r\n", r->start, r->stop, length);
1302110726aSDavid du Colombier 			hprint(hout, "Content-Length: %ld\r\n", r->stop - r->start);
1312110726aSDavid du Colombier 		}else{
1327dd7cddfSDavid du Colombier 			multir = 1;
13380ee5cbfSDavid du Colombier 			boundary = hmkmimeboundary(c);
1347dd7cddfSDavid du Colombier 			hprint(hout, "Content-Type: multipart/byteranges; boundary=%s\r\n", boundary);
1357dd7cddfSDavid du Colombier 		}
1367dd7cddfSDavid du Colombier 		if(c->head.ifrangeetag == nil){
1379a747e4fSDavid du Colombier 			hprint(hout, "Last-Modified: %D\r\n", mtime);
1387dd7cddfSDavid du Colombier 			if(!multir)
1397dd7cddfSDavid du Colombier 				printtype(hout, type, enc);
1407dd7cddfSDavid du Colombier 			if(c->head.fresh_thresh)
14180ee5cbfSDavid du Colombier 				hintprint(c, hout, c->req.uri, c->head.fresh_thresh, c->head.fresh_have);
1427dd7cddfSDavid du Colombier 		}
1437dd7cddfSDavid du Colombier 
1447dd7cddfSDavid du Colombier 		if(c->head.closeit)
1457dd7cddfSDavid du Colombier 			hprint(hout, "Connection: close\r\n");
1467dd7cddfSDavid du Colombier 		else if(!http11(c))
1477dd7cddfSDavid du Colombier 			hprint(hout, "Connection: Keep-Alive\r\n");
1487dd7cddfSDavid du Colombier 		hprint(hout, "\r\n");
1497dd7cddfSDavid du Colombier 	}
1507dd7cddfSDavid du Colombier 	if(strcmp(c->req.meth, "HEAD") == 0){
1517dd7cddfSDavid du Colombier 		if(c->head.range == nil)
1527dd7cddfSDavid du Colombier 			writelog(c, "Reply: 200 file 0\n");
1537dd7cddfSDavid du Colombier 		else
1547dd7cddfSDavid du Colombier 			writelog(c, "Reply: 206 file 0\n");
1559a747e4fSDavid du Colombier 		hflush(hout);
1567dd7cddfSDavid du Colombier 		close(fd);
1577dd7cddfSDavid du Colombier 		return 1;
1587dd7cddfSDavid du Colombier 	}
1597dd7cddfSDavid du Colombier 
1607dd7cddfSDavid du Colombier 	/*
1617dd7cddfSDavid du Colombier 	 * send the file if it's a normal file
1627dd7cddfSDavid du Colombier 	 */
1637dd7cddfSDavid du Colombier 	if(r == nil){
1647dd7cddfSDavid du Colombier 		hflush(hout);
1657dd7cddfSDavid du Colombier 
1667dd7cddfSDavid du Colombier 		wrote = 0;
1677dd7cddfSDavid du Colombier 		if(n > 0)
1689a747e4fSDavid du Colombier 			wrote = write(hout->fd, c->xferbuf, n);
1697dd7cddfSDavid du Colombier 		if(n <= 0 || wrote == n){
17080ee5cbfSDavid du Colombier 			while((n = read(fd, c->xferbuf, HBufSize)) > 0){
1719a747e4fSDavid du Colombier 				nw = write(hout->fd, c->xferbuf, n);
1727dd7cddfSDavid du Colombier 				if(nw != n){
1737dd7cddfSDavid du Colombier 					if(nw > 0)
1747dd7cddfSDavid du Colombier 						wrote += nw;
1757dd7cddfSDavid du Colombier 					break;
1767dd7cddfSDavid du Colombier 				}
1777dd7cddfSDavid du Colombier 				wrote += nw;
1787dd7cddfSDavid du Colombier 			}
1797dd7cddfSDavid du Colombier 		}
1809a747e4fSDavid du Colombier 		writelog(c, "Reply: 200 file %lld %lld\n", length, wrote);
1817dd7cddfSDavid du Colombier 		close(fd);
1829a747e4fSDavid du Colombier 		if(length == wrote)
1837dd7cddfSDavid du Colombier 			return 1;
1847dd7cddfSDavid du Colombier 		return -1;
1857dd7cddfSDavid du Colombier 	}
1867dd7cddfSDavid du Colombier 
1877dd7cddfSDavid du Colombier 	/*
1887dd7cddfSDavid du Colombier 	 * for multipart/byterange messages,
1897dd7cddfSDavid du Colombier 	 * it is not ok for the boundary string to appear within a message part.
1907dd7cddfSDavid du Colombier 	 * however, it probably doesn't matter, since there are lengths for every part.
1917dd7cddfSDavid du Colombier 	 */
1927dd7cddfSDavid du Colombier 	wrote = 0;
1937dd7cddfSDavid du Colombier 	ok = 1;
1947dd7cddfSDavid du Colombier 	for(; r != nil; r = r->next){
1957dd7cddfSDavid du Colombier 		if(multir){
1967dd7cddfSDavid du Colombier 			hprint(hout, "\r\n--%s\r\n", boundary);
1977dd7cddfSDavid du Colombier 			printtype(hout, type, enc);
1989a747e4fSDavid du Colombier 			hprint(hout, "Content-Range: bytes %ld-%ld/%lld\r\n", r->start, r->stop, length);
1992110726aSDavid du Colombier 			hprint(hout, "Content-Length: %ld\r\n", r->stop - r->start);
2007dd7cddfSDavid du Colombier 			hprint(hout, "\r\n");
2017dd7cddfSDavid du Colombier 		}
2027dd7cddfSDavid du Colombier 		hflush(hout);
2037dd7cddfSDavid du Colombier 
2047dd7cddfSDavid du Colombier 		if(seek(fd, r->start, 0) != r->start){
2057dd7cddfSDavid du Colombier 			ok = -1;
2067dd7cddfSDavid du Colombier 			break;
2077dd7cddfSDavid du Colombier 		}
2087dd7cddfSDavid du Colombier 		for(tr = r->stop - r->start + 1; tr; tr -= n){
2097dd7cddfSDavid du Colombier 			n = tr;
21080ee5cbfSDavid du Colombier 			if(n > HBufSize)
21180ee5cbfSDavid du Colombier 				n = HBufSize;
2127dd7cddfSDavid du Colombier 			if(read(fd, c->xferbuf, n) != n){
2137dd7cddfSDavid du Colombier 				ok = -1;
2147dd7cddfSDavid du Colombier 				goto breakout;
2157dd7cddfSDavid du Colombier 			}
2169a747e4fSDavid du Colombier 			nw = write(hout->fd, c->xferbuf, n);
2177dd7cddfSDavid du Colombier 			if(nw != n){
2187dd7cddfSDavid du Colombier 				if(nw > 0)
2197dd7cddfSDavid du Colombier 					wrote += nw;
2207dd7cddfSDavid du Colombier 				ok = -1;
2217dd7cddfSDavid du Colombier 				goto breakout;
2227dd7cddfSDavid du Colombier 			}
2237dd7cddfSDavid du Colombier 			wrote += nw;
2247dd7cddfSDavid du Colombier 		}
2257dd7cddfSDavid du Colombier 	}
2267dd7cddfSDavid du Colombier breakout:;
2277dd7cddfSDavid du Colombier 	if(r == nil){
2287dd7cddfSDavid du Colombier 		if(multir){
2297dd7cddfSDavid du Colombier 			hprint(hout, "--%s--\r\n", boundary);
2307dd7cddfSDavid du Colombier 			hflush(hout);
2317dd7cddfSDavid du Colombier 		}
2329a747e4fSDavid du Colombier 		writelog(c, "Reply: 206 partial content %lld %lld\n", length, wrote);
2337dd7cddfSDavid du Colombier 	}else
2349a747e4fSDavid du Colombier 		writelog(c, "Reply: 206 partial content, early termination %lld %lld\n", length, wrote);
2357dd7cddfSDavid du Colombier 	close(fd);
2367dd7cddfSDavid du Colombier 	return ok;
2377dd7cddfSDavid du Colombier }
2387dd7cddfSDavid du Colombier 
2397dd7cddfSDavid du Colombier static void
printtype(Hio * hout,HContent * type,HContent * enc)24080ee5cbfSDavid du Colombier printtype(Hio *hout, HContent *type, HContent *enc)
2417dd7cddfSDavid du Colombier {
2427dd7cddfSDavid du Colombier 	hprint(hout, "Content-Type: %s/%s", type->generic, type->specific);
2437dd7cddfSDavid du Colombier /*
2447dd7cddfSDavid du Colombier 	if(cistrcmp(type->generic, "text") == 0)
2457dd7cddfSDavid du Colombier 		hprint(hout, ";charset=utf-8");
2467dd7cddfSDavid du Colombier */
2477dd7cddfSDavid du Colombier 	hprint(hout, "\r\n");
2487dd7cddfSDavid du Colombier 	if(enc != nil)
2497dd7cddfSDavid du Colombier 		hprint(hout, "Content-Encoding: %s\r\n", enc->generic);
2507dd7cddfSDavid du Colombier }
2517dd7cddfSDavid du Colombier 
2527dd7cddfSDavid du Colombier int
etagmatch(int strong,HETag * tags,char * e)25380ee5cbfSDavid du Colombier etagmatch(int strong, HETag *tags, char *e)
2547dd7cddfSDavid du Colombier {
2557dd7cddfSDavid du Colombier 	char *s, *t;
2567dd7cddfSDavid du Colombier 
2577dd7cddfSDavid du Colombier 	for(; tags != nil; tags = tags->next){
2587dd7cddfSDavid du Colombier 		if(strong && tags->weak)
2597dd7cddfSDavid du Colombier 			continue;
2607dd7cddfSDavid du Colombier 		s = tags->etag;
2617dd7cddfSDavid du Colombier 		if(s[0] == '*' && s[1] == '\0')
2627dd7cddfSDavid du Colombier 			return 1;
2637dd7cddfSDavid du Colombier 
2647dd7cddfSDavid du Colombier 		t = e + 1;
2657dd7cddfSDavid du Colombier 		while(*t != '"'){
2667dd7cddfSDavid du Colombier 			if(*s != *t)
2677dd7cddfSDavid du Colombier 				break;
2687dd7cddfSDavid du Colombier 			s++;
2697dd7cddfSDavid du Colombier 			t++;
2707dd7cddfSDavid du Colombier 		}
2717dd7cddfSDavid du Colombier 
2727dd7cddfSDavid du Colombier 		if(*s == '\0' && *t == '"')
2737dd7cddfSDavid du Colombier 			return 1;
2747dd7cddfSDavid du Colombier 	}
2757dd7cddfSDavid du Colombier 	return 0;
2767dd7cddfSDavid du Colombier }
2777dd7cddfSDavid du Colombier 
2787dd7cddfSDavid du Colombier static char *
acceptcont(char * s,char * e,HContent * ok,char * which)27980ee5cbfSDavid du Colombier acceptcont(char *s, char *e, HContent *ok, char *which)
2807dd7cddfSDavid du Colombier {
2817dd7cddfSDavid du Colombier 	char *sep;
2827dd7cddfSDavid du Colombier 
2837dd7cddfSDavid du Colombier 	if(ok == nil)
2847dd7cddfSDavid du Colombier 		return seprint(s, e, "Your browser accepts any %s.<br>\n", which);
2857dd7cddfSDavid du Colombier 	s = seprint(s, e, "Your browser accepts %s: ", which);
2867dd7cddfSDavid du Colombier 	sep = "";
2877dd7cddfSDavid du Colombier 	for(; ok != nil; ok = ok->next){
2887dd7cddfSDavid du Colombier 		if(ok->specific)
2897dd7cddfSDavid du Colombier 			s = seprint(s, e, "%s%s/%s", sep, ok->generic, ok->specific);
2907dd7cddfSDavid du Colombier 		else
2917dd7cddfSDavid du Colombier 			s = seprint(s, e, "%s%s", sep, ok->generic);
2927dd7cddfSDavid du Colombier 		sep = ", ";
2937dd7cddfSDavid du Colombier 	}
2947dd7cddfSDavid du Colombier 	return seprint(s, e, ".<br>\n");
2957dd7cddfSDavid du Colombier }
2967dd7cddfSDavid du Colombier 
2977dd7cddfSDavid du Colombier /*
2987dd7cddfSDavid du Colombier  * send back a nice error message if the content is unacceptable
2997dd7cddfSDavid du Colombier  * to get this message in ie, go to tools, internet options, advanced,
3007dd7cddfSDavid du Colombier  * and turn off Show Friendly HTTP Error Messages under the Browsing category
3017dd7cddfSDavid du Colombier  */
3027dd7cddfSDavid du Colombier static int
notaccept(HConnect * c,HContent * type,HContent * enc,char * which)30380ee5cbfSDavid du Colombier notaccept(HConnect *c, HContent *type, HContent *enc, char *which)
3047dd7cddfSDavid du Colombier {
3057dd7cddfSDavid du Colombier 	Hio *hout;
3067dd7cddfSDavid du Colombier 	char *s, *e;
3077dd7cddfSDavid du Colombier 
3087dd7cddfSDavid du Colombier 	hout = &c->hout;
30980ee5cbfSDavid du Colombier 	e = &c->xferbuf[HBufSize];
3107dd7cddfSDavid du Colombier 	s = c->xferbuf;
3117dd7cddfSDavid du Colombier 	s = seprint(s, e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n");
3127dd7cddfSDavid du Colombier 	s = seprint(s, e, "<html>\n<title>Unacceptable %s</title>\n<body>\n", which);
313*59308138SDavid du Colombier 	s = seprint(s, e, "Your browser will not accept this data, %H, because of its %s.<br>\n", c->req.uri, which);
314*59308138SDavid du Colombier 	s = seprint(s, e, "Its Content-Type is %s/%s", type->generic, type->specific);
3157dd7cddfSDavid du Colombier 	if(enc != nil)
3167dd7cddfSDavid du Colombier 		s = seprint(s, e, ", and Content-Encoding is %s", enc->generic);
3177dd7cddfSDavid du Colombier 	s = seprint(s, e, ".<br>\n\n");
3187dd7cddfSDavid du Colombier 
3197dd7cddfSDavid du Colombier 	s = acceptcont(s, e, c->head.oktype, "Content-Type");
3207dd7cddfSDavid du Colombier 	s = acceptcont(s, e, c->head.okencode, "Content-Encoding");
3217dd7cddfSDavid du Colombier 	s = seprint(s, e, "</body>\n</html>\n");
3227dd7cddfSDavid du Colombier 
32380ee5cbfSDavid du Colombier 	hprint(hout, "%s 406 Not Acceptable\r\n", hversion);
3247dd7cddfSDavid du Colombier 	hprint(hout, "Server: Plan9\r\n");
3257dd7cddfSDavid du Colombier 	hprint(hout, "Date: %D\r\n", time(nil));
3267dd7cddfSDavid du Colombier 	hprint(hout, "Content-Type: text/html\r\n");
3277dd7cddfSDavid du Colombier 	hprint(hout, "Content-Length: %lud\r\n", s - c->xferbuf);
3287dd7cddfSDavid du Colombier 	if(c->head.closeit)
3297dd7cddfSDavid du Colombier 		hprint(hout, "Connection: close\r\n");
3307dd7cddfSDavid du Colombier 	else if(!http11(c))
3317dd7cddfSDavid du Colombier 		hprint(hout, "Connection: Keep-Alive\r\n");
3327dd7cddfSDavid du Colombier 	hprint(hout, "\r\n");
3337dd7cddfSDavid du Colombier 	if(strcmp(c->req.meth, "HEAD") != 0)
3347dd7cddfSDavid du Colombier 		hwrite(hout, c->xferbuf, s - c->xferbuf);
3357dd7cddfSDavid du Colombier 	writelog(c, "Reply: 406 Not Acceptable\nReason: %s\n", which);
3367dd7cddfSDavid du Colombier 	return hflush(hout);
3377dd7cddfSDavid du Colombier }
3387dd7cddfSDavid du Colombier 
3397dd7cddfSDavid du Colombier /*
3407dd7cddfSDavid du Colombier  * check time and entity tag conditions.
3417dd7cddfSDavid du Colombier  */
3427dd7cddfSDavid du Colombier int
checkreq(HConnect * c,HContent * type,HContent * enc,long mtime,char * etag)34380ee5cbfSDavid du Colombier checkreq(HConnect *c, HContent *type, HContent *enc, long mtime, char *etag)
3447dd7cddfSDavid du Colombier {
3457dd7cddfSDavid du Colombier 	Hio *hout;
3467dd7cddfSDavid du Colombier 	int m;
3477dd7cddfSDavid du Colombier 
3487dd7cddfSDavid du Colombier 	hout = &c->hout;
34980ee5cbfSDavid du Colombier 	if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(type, c->head.oktype, "Content-Type", 0))
3507dd7cddfSDavid du Colombier 		return notaccept(c, type, enc, "Content-Type");
35180ee5cbfSDavid du Colombier 	if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(enc, c->head.okencode, "Content-Encoding", 0))
3527dd7cddfSDavid du Colombier 		return notaccept(c, type, enc, "Content-Encoding");
3537dd7cddfSDavid du Colombier 
3547dd7cddfSDavid du Colombier 	/*
3557dd7cddfSDavid du Colombier 	 * can use weak match only with get or head;
3567dd7cddfSDavid du Colombier 	 * this always uses strong matches
3577dd7cddfSDavid du Colombier 	 */
3587dd7cddfSDavid du Colombier 	m = etagmatch(1, c->head.ifnomatch, etag);
3597dd7cddfSDavid du Colombier 
3607dd7cddfSDavid du Colombier 	if(m && strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0
3617dd7cddfSDavid du Colombier 	|| c->head.ifunmodsince && c->head.ifunmodsince < mtime
3627dd7cddfSDavid du Colombier 	|| c->head.ifmatch != nil && !etagmatch(1, c->head.ifmatch, etag)){
36380ee5cbfSDavid du Colombier 		hprint(hout, "%s 412 Precondition Failed\r\n", hversion);
3647dd7cddfSDavid du Colombier 		hprint(hout, "Server: Plan9\r\n");
3657dd7cddfSDavid du Colombier 		hprint(hout, "Date: %D\r\n", time(nil));
3667dd7cddfSDavid du Colombier 		hprint(hout, "Content-Type: text/html\r\n");
3677dd7cddfSDavid du Colombier 		hprint(hout, "Content-Length: %d\r\n", STRLEN(UNMATCHED));
3687dd7cddfSDavid du Colombier 		if(c->head.closeit)
3697dd7cddfSDavid du Colombier 			hprint(hout, "Connection: close\r\n");
3707dd7cddfSDavid du Colombier 		else if(!http11(c))
3717dd7cddfSDavid du Colombier 			hprint(hout, "Connection: Keep-Alive\r\n");
3727dd7cddfSDavid du Colombier 		hprint(hout, "\r\n");
3737dd7cddfSDavid du Colombier 		if(strcmp(c->req.meth, "HEAD") != 0)
3747dd7cddfSDavid du Colombier 			hprint(hout, "%s", UNMATCHED);
3757dd7cddfSDavid du Colombier 		writelog(c, "Reply: 412 Precondition Failed\n");
3767dd7cddfSDavid du Colombier 		return hflush(hout);
3777dd7cddfSDavid du Colombier 	}
3787dd7cddfSDavid du Colombier 
3797dd7cddfSDavid du Colombier 	if(c->head.ifmodsince >= mtime
3807dd7cddfSDavid du Colombier 	&& (m || c->head.ifnomatch == nil)){
3817dd7cddfSDavid du Colombier 		/*
3827dd7cddfSDavid du Colombier 		 * can only send back Date, ETag, Content-Location,
3837dd7cddfSDavid du Colombier 		 * Expires, Cache-Control, and Vary entity-headers
3847dd7cddfSDavid du Colombier 		 */
38580ee5cbfSDavid du Colombier 		hprint(hout, "%s 304 Not Modified\r\n", hversion);
3867dd7cddfSDavid du Colombier 		hprint(hout, "Server: Plan9\r\n");
3877dd7cddfSDavid du Colombier 		hprint(hout, "Date: %D\r\n", time(nil));
3887dd7cddfSDavid du Colombier 		hprint(hout, "ETag: %s\r\n", etag);
3897dd7cddfSDavid du Colombier 		if(c->head.closeit)
3907dd7cddfSDavid du Colombier 			hprint(hout, "Connection: close\r\n");
3917dd7cddfSDavid du Colombier 		else if(!http11(c))
3927dd7cddfSDavid du Colombier 			hprint(hout, "Connection: Keep-Alive\r\n");
3937dd7cddfSDavid du Colombier 		hprint(hout, "\r\n");
3947dd7cddfSDavid du Colombier 		writelog(c, "Reply: 304 Not Modified\n");
3957dd7cddfSDavid du Colombier 		return hflush(hout);
3967dd7cddfSDavid du Colombier 	}
3977dd7cddfSDavid du Colombier 	return 1;
3987dd7cddfSDavid du Colombier }
3997dd7cddfSDavid du Colombier 
4007dd7cddfSDavid du Colombier /*
4017dd7cddfSDavid du Colombier  * length is the actual length of the entity requested.
4027dd7cddfSDavid du Colombier  * discard any range requests which are invalid,
4037dd7cddfSDavid du Colombier  * ie start after the end, or have stop before start.
4047dd7cddfSDavid du Colombier  * rewrite suffix requests
4057dd7cddfSDavid du Colombier  */
40680ee5cbfSDavid du Colombier HRange*
fixrange(HRange * h,long length)40780ee5cbfSDavid du Colombier fixrange(HRange *h, long length)
4087dd7cddfSDavid du Colombier {
40980ee5cbfSDavid du Colombier 	HRange *r, *rr;
4107dd7cddfSDavid du Colombier 
4117dd7cddfSDavid du Colombier 	if(length == 0)
4127dd7cddfSDavid du Colombier 		return nil;
4137dd7cddfSDavid du Colombier 
4147dd7cddfSDavid du Colombier 	/*
4157dd7cddfSDavid du Colombier 	 * rewrite each range to reflect the actual length of the file
4167dd7cddfSDavid du Colombier 	 * toss out any invalid ranges
4177dd7cddfSDavid du Colombier 	 */
4187dd7cddfSDavid du Colombier 	rr = nil;
4197dd7cddfSDavid du Colombier 	for(r = h; r != nil; r = r->next){
4207dd7cddfSDavid du Colombier 		if(r->suffix){
4217dd7cddfSDavid du Colombier 			r->start = length - r->stop;
4227dd7cddfSDavid du Colombier 			if(r->start >= length)
4237dd7cddfSDavid du Colombier 				r->start = 0;
4247dd7cddfSDavid du Colombier 			r->stop = length - 1;
4257dd7cddfSDavid du Colombier 			r->suffix = 0;
4267dd7cddfSDavid du Colombier 		}
4277dd7cddfSDavid du Colombier 		if(r->stop >= length)
4287dd7cddfSDavid du Colombier 			r->stop = length - 1;
4297dd7cddfSDavid du Colombier 		if(r->start > r->stop){
4307dd7cddfSDavid du Colombier 			if(rr == nil)
4317dd7cddfSDavid du Colombier 				h = r->next;
4327dd7cddfSDavid du Colombier 			else
4337dd7cddfSDavid du Colombier 				rr->next = r->next;
4347dd7cddfSDavid du Colombier 		}else
4357dd7cddfSDavid du Colombier 			rr = r;
4367dd7cddfSDavid du Colombier 	}
4377dd7cddfSDavid du Colombier 
4387dd7cddfSDavid du Colombier 	/*
4397dd7cddfSDavid du Colombier 	 * merge consecutive overlapping or abutting ranges
4407dd7cddfSDavid du Colombier 	 *
4417dd7cddfSDavid du Colombier 	 * not clear from rfc2616 how much merging needs to be done.
4427dd7cddfSDavid du Colombier 	 * this code merges only if a range is adjacent to a later starting,
4437dd7cddfSDavid du Colombier 	 * over overlapping or abutting range.  this allows a client
4447dd7cddfSDavid du Colombier 	 * to request wanted data first, followed by other data.
4457dd7cddfSDavid du Colombier 	 * this may be useful then fetching part of a page, then the adjacent regions.
4467dd7cddfSDavid du Colombier 	 */
4477dd7cddfSDavid du Colombier 	if(h == nil)
4487dd7cddfSDavid du Colombier 		return h;
4497dd7cddfSDavid du Colombier 	r = h;
4507dd7cddfSDavid du Colombier 	for(;;){
4517dd7cddfSDavid du Colombier 		rr = r->next;
4527dd7cddfSDavid du Colombier 		if(rr == nil)
4537dd7cddfSDavid du Colombier 			break;
4547dd7cddfSDavid du Colombier 		if(r->start <= rr->start && r->stop + 1 >= rr->start){
4557dd7cddfSDavid du Colombier 			if(r->stop < rr->stop)
4567dd7cddfSDavid du Colombier 				r->stop = rr->stop;
4577dd7cddfSDavid du Colombier 			r->next = rr->next;
4587dd7cddfSDavid du Colombier 		}else
4597dd7cddfSDavid du Colombier 			r = rr;
4607dd7cddfSDavid du Colombier 	}
4617dd7cddfSDavid du Colombier 	return h;
4627dd7cddfSDavid du Colombier }
463