Example #1
0
int
hsettype(HConnect *c, char *type)
{
	Hio *hout;
	int r;

	r = preq(c);
	if(r < 0)
		return r;

	hout = &c->hout;
	if(c->req.vermaj){
		hokheaders(c);
		hprint(hout, "Content-type: %s\r\n", type);
		if(http11(c))
			hprint(hout, "Transfer-Encoding: chunked\r\n");
		hprint(hout, "\r\n");
	}

	if(http11(c))
		hxferenc(hout, 1);
	else
		c->head.closeit = 1;
	return 0;
}
Example #2
0
/*
 * check time and entity tag conditions.
 */
int
checkreq(HConnect *c, HContent *type, HContent *enc, long mtime, char *etag)
{
	Hio *hout;
	int m;

	hout = &c->hout;
	if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(type, c->head.oktype, "Content-Type", 0))
		return notaccept(c, type, enc, "Content-Type");
	if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(enc, c->head.okencode, "Content-Encoding", 0))
		return notaccept(c, type, enc, "Content-Encoding");

	/*
	 * can use weak match only with get or head;
	 * this always uses strong matches
	 */
	m = etagmatch(1, c->head.ifnomatch, etag);

	if(m && strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0
	|| c->head.ifunmodsince && c->head.ifunmodsince < mtime
	|| c->head.ifmatch != nil && !etagmatch(1, c->head.ifmatch, etag)){
		hprint(hout, "%s 412 Precondition Failed\r\n", hversion);
		hprint(hout, "Server: Plan9\r\n");
		hprint(hout, "Date: %D\r\n", time(nil));
		hprint(hout, "Content-Type: text/html\r\n");
		hprint(hout, "Content-Length: %d\r\n", STRLEN(UNMATCHED));
		if(c->head.closeit)
			hprint(hout, "Connection: close\r\n");
		else if(!http11(c))
			hprint(hout, "Connection: Keep-Alive\r\n");
		hprint(hout, "\r\n");
		if(strcmp(c->req.meth, "HEAD") != 0)
			hprint(hout, "%s", UNMATCHED);
		writelog(c, "Reply: 412 Precondition Failed\n");
		return hflush(hout);
	}

	if(c->head.ifmodsince >= mtime
	&& (m || c->head.ifnomatch == nil)){
		/*
		 * can only send back Date, ETag, Content-Location,
		 * Expires, Cache-Control, and Vary entity-headers
		 */
		hprint(hout, "%s 304 Not Modified\r\n", hversion);
		hprint(hout, "Server: Plan9\r\n");
		hprint(hout, "Date: %D\r\n", time(nil));
		hprint(hout, "ETag: %s\r\n", etag);
		if(c->head.closeit)
			hprint(hout, "Connection: close\r\n");
		else if(!http11(c))
			hprint(hout, "Connection: Keep-Alive\r\n");
		hprint(hout, "\r\n");
		writelog(c, "Reply: 304 Not Modified\n");
		return hflush(hout);
	}
	return 1;
}
Example #3
0
/*
 * write initial part of successful header
 */
void
hokheaders(HConnect *c)
{
	Hio *hout;

	hout = &c->hout;
	hprint(hout, "%s 200 OK\r\n", hversion);
	hprint(hout, "Server: Plan9\r\n");
	hprint(hout, "Date: %D\r\n", time(nil));
	if(c->head.closeit)
		hprint(hout, "Connection: close\r\n");
	else if(!http11(c))
		hprint(hout, "Connection: Keep-Alive\r\n");
}
Example #4
0
static int
herror(HConnect *c)
{
	int n;
	Hio *hout;
	
	hout = &c->hout;
	n = snprint(c->xferbuf, HBufSize, "<html><head><title>Error</title></head>\n<body><h1>Error</h1>\n<pre>%r</pre>\n</body></html>");
	hprint(hout, "%s %s\r\n", hversion, "400 Bad Request");
	hprint(hout, "Date: %D\r\n", time(nil));
	hprint(hout, "Server: Venti\r\n");
	hprint(hout, "Content-Type: text/html\r\n");
	hprint(hout, "Content-Length: %d\r\n", n);
	if(c->head.closeit)
		hprint(hout, "Connection: close\r\n");
	else if(!http11(c))
		hprint(hout, "Connection: Keep-Alive\r\n");
	hprint(hout, "\r\n");

	if(c->req.meth == nil || strcmp(c->req.meth, "HEAD") != 0)
		hwrite(hout, c->xferbuf, n);

	return hflush(hout);
}
Example #5
0
/*
 * send back a nice error message if the content is unacceptable
 * to get this message in ie, go to tools, internet options, advanced,
 * and turn off Show Friendly HTTP Error Messages under the Browsing category
 */
static int
notaccept(HConnect *c, HContent *type, HContent *enc, char *which)
{
	Hio *hout;
	char *s, *e;

	hout = &c->hout;
	e = &c->xferbuf[HBufSize];
	s = c->xferbuf;
	s = seprint(s, e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n");
	s = seprint(s, e, "<html>\n<title>Unacceptable %s</title>\n<body>\n", which);
	s = seprint(s, e, "Your browser will not accept this data, %H, because of its %s.<br>\n", c->req.uri, which);
	s = seprint(s, e, "Its Content-Type is %s/%s", type->generic, type->specific);
	if(enc != nil)
		s = seprint(s, e, ", and Content-Encoding is %s", enc->generic);
	s = seprint(s, e, ".<br>\n\n");

	s = acceptcont(s, e, c->head.oktype, "Content-Type");
	s = acceptcont(s, e, c->head.okencode, "Content-Encoding");
	s = seprint(s, e, "</body>\n</html>\n");

	hprint(hout, "%s 406 Not Acceptable\r\n", hversion);
	hprint(hout, "Server: Plan9\r\n");
	hprint(hout, "Date: %D\r\n", time(nil));
	hprint(hout, "Content-Type: text/html\r\n");
	hprint(hout, "Content-Length: %lud\r\n", s - c->xferbuf);
	if(c->head.closeit)
		hprint(hout, "Connection: close\r\n");
	else if(!http11(c))
		hprint(hout, "Connection: Keep-Alive\r\n");
	hprint(hout, "\r\n");
	if(strcmp(c->req.meth, "HEAD") != 0)
		hwrite(hout, c->xferbuf, s - c->xferbuf);
	writelog(c, "Reply: 406 Not Acceptable\nReason: %s\n", which);
	return hflush(hout);
}
Example #6
0
/*
 * parse the next request line
 * returns:
 *	1 ok
 *	0 eof
 *	-1 error
 */
int
hparsereq(HConnect *c, int timeout)
{
	Strings ss;
	char *vs, *v, *search, *uri, *origuri, *extra;

	if(c->bin != nil){
		hfail(c, HInternal);
		return -1;
	}

	/*
	 * serve requests until a magic request.
	 * later requests have to come quickly.
	 * only works for http/1.1 or later.
	 */
	if(timeout)
		alarm(timeout);
	if(hgethead(c, 0) < 0)
		return -1;
	if(timeout)
		alarm(0);
	c->reqtime = time(nil);
	c->req.meth = getword(c);
	if(c->req.meth == nil){
		hfail(c, HSyntax);
		return -1;
	}
	uri = getword(c);
	if(uri == nil || strlen(uri) == 0){
		hfail(c, HSyntax);
		return -1;
	}
	v = getword(c);
	if(v == nil){
		if(strcmp(c->req.meth, "GET") != 0){
			hfail(c, HUnimp, c->req.meth);
			return -1;
		}
		c->req.vermaj = 0;
		c->req.vermin = 9;
	}else{
		vs = v;
		if(strncmp(vs, "HTTP/", 5) != 0){
			hfail(c, HUnkVers, vs);
			return -1;
		}
		vs += 5;
		c->req.vermaj = strtoul(vs, &vs, 10);
		if(*vs != '.' || c->req.vermaj != 1){
			hfail(c, HUnkVers, vs);
			return -1;
		}
		vs++;
		c->req.vermin = strtoul(vs, &vs, 10);
		if(*vs != '\0'){
			hfail(c, HUnkVers, vs);
			return -1;
		}

		extra = getword(c);
		if(extra != nil){
			hfail(c, HSyntax);
			return -1;
		}
	}

	/*
	 * the fragment is not supposed to be sent
	 * strip it 'cause some clients send it
	 */
	origuri = uri;
	uri = strchr(origuri, '#');
	if(uri != nil)
		*uri = 0;

	/*
	 * http/1.1 requires the server to accept absolute
	 * or relative uri's.  convert to relative with an absolute path
	 */
	if(http11(c)){
		ss = parseuri(c, origuri);
		uri = ss.s1;
		c->req.urihost = ss.s2;
		if(uri == nil){
			hfail(c, HBadReq, uri);
			return -1;
		}
		origuri = uri;
	}

	/*
	 * munge uri for search, protection, and magic
	 */
	ss = stripsearch(origuri);
	origuri = ss.s1;
	search = ss.s2;
	uri = hurlunesc(c, origuri);
	uri = abspath(c, uri, "/");
	if(uri == nil || uri[0] == '\0'){
		hfail(c, HNotFound, "no object specified");
		return -1;
	}

	c->req.uri = uri;
	c->req.search = search;
	if(search)
		c->req.searchpairs = hparsequery(c, hstrdup(c, search));

	return 1;
}
Example #7
0
void test_nghttp2_npn(void)
{
  http2();
  http11();
  no_overlap();
}
Example #8
0
/*
 * fd references a file which has been authorized & checked for relocations.
 * send back the headers & its contents.
 * includes checks for conditional requests & ranges.
 */
int
sendfd(HConnect *c, int fd, Dir *dir, HContent *type, HContent *enc)
{
	Qid qid;
	HRange *r;
	HContents conts;
	Hio *hout;
	char *boundary, etag[32];
	long mtime;
	ulong tr;
	int n, nw, multir, ok;
	vlong wrote, length;

	hout = &c->hout;
	length = dir->length;
	mtime = dir->mtime;
	qid = dir->qid;
	free(dir);

	/*
	 * figure out the type of file and send headers
	 */
	n = -1;
	r = nil;
	multir = 0;
	boundary = nil;
	if(c->req.vermaj){
		if(type == nil && enc == nil){
			conts = uriclass(c, c->req.uri);
			type = conts.type;
			enc = conts.encoding;
			if(type == nil && enc == nil){
				n = read(fd, c->xferbuf, HBufSize-1);
				if(n > 0){
					c->xferbuf[n] = '\0';
					conts = dataclass(c, c->xferbuf, n);
					type = conts.type;
					enc = conts.encoding;
				}
			}
		}
		if(type == nil)
			type = hmkcontent(c, "application", "octet-stream", nil);

		snprint(etag, sizeof(etag), "\"%lluxv%lux\"", qid.path, qid.vers);
		ok = checkreq(c, type, enc, mtime, etag);
		if(ok <= 0){
			close(fd);
			return ok;
		}

		/*
		 * check for if-range requests
		 */
		if(c->head.range == nil
		|| c->head.ifrangeetag != nil && !etagmatch(1, c->head.ifrangeetag, etag)
		|| c->head.ifrangedate != 0 && c->head.ifrangedate != mtime){
			c->head.range = nil;
			c->head.ifrangeetag = nil;
			c->head.ifrangedate = 0;
		}

		if(c->head.range != nil){
			c->head.range = fixrange(c->head.range, length);
			if(c->head.range == nil){
				if(c->head.ifrangeetag == nil && c->head.ifrangedate == 0){
					hprint(hout, "%s 416 Request range not satisfiable\r\n", hversion);
					hprint(hout, "Date: %D\r\n", time(nil));
					hprint(hout, "Server: Plan9\r\n");
					hprint(hout, "Content-Range: bytes */%lld\r\n", length);
					hprint(hout, "Content-Length: %d\r\n", STRLEN(BADRANGE));
					hprint(hout, "Content-Type: text/html\r\n");
					if(c->head.closeit)
						hprint(hout, "Connection: close\r\n");
					else if(!http11(c))
						hprint(hout, "Connection: Keep-Alive\r\n");
					hprint(hout, "\r\n");
					if(strcmp(c->req.meth, "HEAD") != 0)
						hprint(hout, "%s", BADRANGE);
					hflush(hout);
					writelog(c, "Reply: 416 Request range not satisfiable\n");
					close(fd);
					return 1;
				}
				c->head.ifrangeetag = nil;
				c->head.ifrangedate = 0;
			}
		}
		if(c->head.range == nil)
			hprint(hout, "%s 200 OK\r\n", hversion);
		else
			hprint(hout, "%s 206 Partial Content\r\n", hversion);

		hprint(hout, "Server: Plan9\r\n");
		hprint(hout, "Date: %D\r\n", time(nil));
		hprint(hout, "ETag: %s\r\n", etag);

		/*
		 * can't send some entity headers if partially responding
		 * to an if-range: etag request
		 */
		r = c->head.range;
		if(r == nil)
			hprint(hout, "Content-Length: %lld\r\n", length);
		else if(r->next == nil){
			hprint(hout, "Content-Range: bytes %ld-%ld/%lld\r\n", r->start, r->stop, length);
			hprint(hout, "Content-Length: %ld\r\n", r->stop - r->start);
		}else{
			multir = 1;
			boundary = hmkmimeboundary(c);
			hprint(hout, "Content-Type: multipart/byteranges; boundary=%s\r\n", boundary);
		}
		if(c->head.ifrangeetag == nil){
			hprint(hout, "Last-Modified: %D\r\n", mtime);
			if(!multir)
				printtype(hout, type, enc);
			if(c->head.fresh_thresh)
				hintprint(c, hout, c->req.uri, c->head.fresh_thresh, c->head.fresh_have);
		}

		if(c->head.closeit)
			hprint(hout, "Connection: close\r\n");
		else if(!http11(c))
			hprint(hout, "Connection: Keep-Alive\r\n");
		hprint(hout, "\r\n");
	}
	if(strcmp(c->req.meth, "HEAD") == 0){
		if(c->head.range == nil)
			writelog(c, "Reply: 200 file 0\n");
		else
			writelog(c, "Reply: 206 file 0\n");
		hflush(hout);
		close(fd);
		return 1;
	}

	/*
	 * send the file if it's a normal file
	 */
	if(r == nil){
		hflush(hout);

		wrote = 0;
		if(n > 0)
			wrote = write(hout->fd, c->xferbuf, n);
		if(n <= 0 || wrote == n){
			while((n = read(fd, c->xferbuf, HBufSize)) > 0){
				nw = write(hout->fd, c->xferbuf, n);
				if(nw != n){
					if(nw > 0)
						wrote += nw;
					break;
				}
				wrote += nw;
			}
		}
		writelog(c, "Reply: 200 file %lld %lld\n", length, wrote);
		close(fd);
		if(length == wrote)
			return 1;
		return -1;
	}

	/*
	 * for multipart/byterange messages,
	 * it is not ok for the boundary string to appear within a message part.
	 * however, it probably doesn't matter, since there are lengths for every part.
	 */
	wrote = 0;
	ok = 1;
	for(; r != nil; r = r->next){
		if(multir){
			hprint(hout, "\r\n--%s\r\n", boundary);
			printtype(hout, type, enc);
			hprint(hout, "Content-Range: bytes %ld-%ld/%lld\r\n", r->start, r->stop, length);
			hprint(hout, "Content-Length: %ld\r\n", r->stop - r->start);
			hprint(hout, "\r\n");
		}
		hflush(hout);

		if(seek(fd, r->start, 0) != r->start){
			ok = -1;
			break;
		}
		for(tr = r->stop - r->start + 1; tr; tr -= n){
			n = tr;
			if(n > HBufSize)
				n = HBufSize;
			if(read(fd, c->xferbuf, n) != n){
				ok = -1;
				goto breakout;
			}
			nw = write(hout->fd, c->xferbuf, n);
			if(nw != n){
				if(nw > 0)
					wrote += nw;
				ok = -1;
				goto breakout;
			}
			wrote += nw;
		}
	}
breakout:;
	if(r == nil){
		if(multir){
			hprint(hout, "--%s--\r\n", boundary);
			hflush(hout);
		}
		writelog(c, "Reply: 206 partial content %lld %lld\n", length, wrote);
	}else
		writelog(c, "Reply: 206 partial content, early termination %lld %lld\n", length, wrote);
	close(fd);
	return ok;
}