Download raw body.
relayd: handle HEAD responses as bodyless
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
>
> Follow that semantic in relayd instead of switching the response to an
> unbounded body and adding Connection: close.
>
> This avoids forwarding both Connection: keep-alive from the backend and
> relayd's added Connection: close.
>
> Ok?
>
> 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 01:05:52 -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 head_response = 0;
> enum httpmethod request_method = HTTP_METHOD_NONE;
>
> getmonotime(&con->se_tv_last);
> @@ -480,6 +481,10 @@ relay_read_http(struct bufferevent *bev,
>
> connection_close = kv_find_value(&desc->http_headers,
> "Connection", "close", ",");
> + head_response = cre->dir == RELAY_DIR_RESPONSE &&
> + request_method == HTTP_METHOD_HEAD;
Accorting to the rfc9112 above, something like this (pre-second coffee,
untested)?
if (cre->dir == RELAY_DIR_RESPONSE &&
(request_method == HTTP_METHOD_HEAD ||
(desc->http_status >= 100 && desc->http_status < 200) ||
desc->http_status == 204 ||
desc->http_status == 304))
If you agree, a brief comment in the code would also be nice.
> + if (head_response)
http_headers would then be the wrong name.
> + cre->toread = 0;
>
> switch (desc->http_method) {
> case HTTP_METHOD_CONNECT:
> @@ -539,7 +544,7 @@ relay_read_http(struct bufferevent *bev,
> bev->readcb = relay_read_http;
> break;
> }
> - if (desc->http_chunked) {
> + if (desc->http_chunked && !head_response) {
> /* Chunked transfer encoding */
> cre->toread = TOREAD_HTTP_CHUNK_LENGTH;
> bev->readcb = relay_read_httpchunks;
>
>
>
> --
> wbr, Kirill
>
relayd: handle HEAD responses as bodyless