Download raw body.
relayd: handle HEAD responses as bodyless
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.
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;
+ if (head_response)
+ 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