summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2022-01-13 18:42:06 +0100
committerLeah Neukirchen <leah@vuxu.org>2022-01-13 18:42:06 +0100
commit8bf60bbd1de2a329340e88d3fb31ea30e83eed8f (patch)
treefbe8224dd989a90aac58e1e76d5efeb6a4b3c55a
parent39b303bffbcb52ea36b1d5e30d8531e752233505 (diff)
downloadhittpd-8bf60bbd1de2a329340e88d3fb31ea30e83eed8f.tar.gz
hittpd-8bf60bbd1de2a329340e88d3fb31ea30e83eed8f.tar.xz
hittpd-8bf60bbd1de2a329340e88d3fb31ea30e83eed8f.zip
detect and refuse pipelining
Previously, two HTTP requests within the same read() would both
be parsed by http_parser_execute (and corrupted state).
We don't support this, since we distinguish between reading and
writing parts of the server action, and there's no way to let
poll perform the writing part in this case.

Detect pipelining by pausing the parser in on_message_complete and
checking if we parsed fewer bytes than we passed.  Then handle the
first request and drop the connection; a compliant HTTP client must
retry without pipelining.

Found by @duncaen.

Signed-off-by: Leah Neukirchen <leah@vuxu.org>
-rw-r--r--FEATURES2
-rw-r--r--LICENSE2
-rw-r--r--hittpd.c19
3 files changed, 19 insertions, 4 deletions
diff --git a/FEATURES b/FEATURES
index 5524dd8..8afda8a 100644
--- a/FEATURES
+++ b/FEATURES
@@ -6,7 +6,7 @@ Features of hittpd:
 - HTTP 1.0 and HTTP 1.1 support
 - Ranges (basic variants only)
 - If-Modified-Since
-- Keep-Alive
+- Keep-Alive (but no pipelining)
 - IPv6
 - sendfile(2) on Linux
 - Virtual Hosts
diff --git a/LICENSE b/LICENSE
index 5d980d9..cb12f14 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2020, 2021 Leah Neukirchen <leah@vuxu.org>
+Copyright 2020, 2021, 2022 Leah Neukirchen <leah@vuxu.org>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to
diff --git a/hittpd.c b/hittpd.c
index e5114d6..9ddd2c4 100644
--- a/hittpd.c
+++ b/hittpd.c
@@ -1,6 +1,6 @@
 /* hittpd - efficient, no-frills HTTP 1.1 server */
 
-/* Copyright 2020, 2021 Leah Neukirchen <leah@vuxu.org>
+/* Copyright 2020, 2021, 2022 Leah Neukirchen <leah@vuxu.org>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to
@@ -516,6 +516,7 @@ on_message_complete(http_parser *p) {
 	struct conn_data *data = p->data;
 
 	data->state = SENDING;
+	http_parser_pause(p, 1);  // can't do more than one request at a time
 
 	if (p->http_major == 0 && p->http_minor == 9)
 		return send_error(p, 400, "Bad Request");
@@ -919,7 +920,21 @@ read_client(int i)
 	} else if (n == 0) {
 		close_connection(i);
 	} else {
-		http_parser_execute(&parsers[i], &settings, buf, n);
+		size_t r = http_parser_execute(&parsers[i], &settings, buf, n);
+
+		if ((size_t)n == r &&
+		    HTTP_PARSER_ERRNO(&parsers[i]) == HPE_PAUSED) {
+			// we handled a complete request, we can reuse
+			// the parser
+			http_parser_pause(&parsers[i], 0);
+		} else {
+			// the read data was longer than a single request
+			// drop the rest and make sure we close the connection,
+			// so the client tries without pipelining
+			// (this is conformant)
+			http_parser_pause(&parsers[i], 0);
+			parsers[i].flags |= F_CONNECTION_CLOSE;
+		}
 
 		if (parsers[i].http_errno) {
 			printf("err=%s\n",