Index | Thread | Search

From:
Kirill A. Korinsky <kirill@korins.ky>
Subject:
Re: relayd: handle HEAD responses as bodyless
To:
Rafael Sadowski <rafael@sizeofvoid.org>
Cc:
OpenBSD tech <tech@openbsd.org>
Date:
Thu, 07 May 2026 18:45:59 +0200

Download raw body.

Thread
On Thu, 07 May 2026 07:22:56 +0200,
Rafael Sadowski <rafael@sizeofvoid.org> wrote:
> 
> On Thu May 07, 2026 at 03:17:06AM +0200, Kirill A. Korinsky wrote:
> > tech@,
> > 
> > RFC 9110 defines HEAD as GET without response content; RFC 9112 Section
> > 6.3 makes the HTTP/1.1 framing rule explicit: any response to a HEAD
> > request is terminated by the empty line after the header section,
> > regardless of Content-Length or Transfer-Encoding.
> 
> I think here is more:
> 
> "Any response to a HEAD request and any response with a 1xx
> (Informational), 204 (No Content), or 304 (Not Modified) status code is
> always terminated by the first empty line after the header fields,
> regardless of the header fields present in the message, and thus cannot
> contain a message body or trailer section."
> -- https://www.rfc-editor.org/rfc/rfc9112.html
>

Yes, I missed that. That leads to this diff with your tweaks.

Index: usr.sbin/relayd/relay_http.c
===================================================================
RCS file: /home/cvs/src/usr.sbin/relayd/relay_http.c,v
diff -u -p -r1.96 relay_http.c
--- usr.sbin/relayd/relay_http.c	2 Apr 2026 13:35:36 -0000	1.96
+++ usr.sbin/relayd/relay_http.c	7 May 2026 16:42:51 -0000
@@ -196,6 +196,7 @@ relay_read_http(struct bufferevent *bev,
 	struct kv		*upgrade = NULL, *upgrade_ws = NULL;
 	struct kv		*connection_close = NULL;
 	int			 ws_response = 0;
+	int			 headers_only = 0;
 	enum httpmethod		 request_method = HTTP_METHOD_NONE;
 
 	getmonotime(&con->se_tv_last);
@@ -480,6 +481,16 @@ relay_read_http(struct bufferevent *bev,
 
 		connection_close = kv_find_value(&desc->http_headers,
 		    "Connection", "close", ",");
+		/*
+		 * RFC 9112 section 6.3: these responses end at the empty
+		 * line after the header section. 101 upgrades become streams.
+		 */
+		headers_only = cre->dir == RELAY_DIR_RESPONSE && !ws_response &&
+		    (request_method == HTTP_METHOD_HEAD ||
+		    (desc->http_status >= 100 && desc->http_status < 200) ||
+		    desc->http_status == 204 || desc->http_status == 304);
+		if (headers_only)
+			cre->toread = 0;
 
 		switch (desc->http_method) {
 		case HTTP_METHOD_CONNECT:
@@ -539,7 +550,7 @@ relay_read_http(struct bufferevent *bev,
 			bev->readcb = relay_read_http;
 			break;
 		}
-		if (desc->http_chunked) {
+		if (desc->http_chunked && !headers_only) {
 			/* Chunked transfer encoding */
 			cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
 			bev->readcb = relay_read_httpchunks;


-- 
wbr, Kirill