commit 05a099a8f45a2d2132479e114f734065e2e12f45 from: kirill date: Sat May 16 15:25:28 2026 UTC usr.sbin/relayd: handle HTTP responses without bodies RFC 9112 section 6.3 specifies that responses to HEAD requests, and responses with 1xx, 204, or 304 status codes, are terminated by the empty line after the header section regardless of Content-Length or Transfer-Encoding. They cannot contain a message body or trailer section. Teach relayd to apply that framing rule before deciding whether a response body is bounded. Otherwise relayd treats these responses as unbounded, adds Connection: close, and can forward both the backend's Connection: keep-alive and its own Connection: close. Tweaks and OK: rsadowski@ commit - cc2ec1f7a082a95f019ba80d9be7f628ebafcc6a commit + 05a099a8f45a2d2132479e114f734065e2e12f45 blob - d6960e83eb7a11678a5e4211687e018a2e127c2c blob + 2570940fbbab47edc60a7dc9d72bf35ff61b4e0a --- relay_http.c +++ relay_http.c @@ -1,4 +1,4 @@ -/* $OpenBSD: relay_http.c,v 1.96 2026/04/02 13:35:36 tb Exp $ */ +/* $OpenBSD: relay_http.c,v 1.97 2026/05/16 15:25:28 kirill Exp $ */ /* * Copyright (c) 2006 - 2016 Reyk Floeter @@ -196,6 +196,7 @@ relay_read_http(struct bufferevent *bev, void *arg) 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, void *arg) 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, void *arg) 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;