From 9eaeac69cf2ed5ba56c98d05fc79a097745efc0e Mon Sep 17 00:00:00 2001 From: Ashish Sharma Date: Thu, 27 Aug 2026 19:31:16 +0800 Subject: [PATCH] fix(esp_http_server): fail the connection on an oversized WebSocket frame --- components/esp_http_server/src/httpd_ws.c | 3 ++ .../test_apps/main/test_http_server.c | 29 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/components/esp_http_server/src/httpd_ws.c b/components/esp_http_server/src/httpd_ws.c index 6eb45f5bfb2..222aceaf657 100644 --- a/components/esp_http_server/src/httpd_ws.c +++ b/components/esp_http_server/src/httpd_ws.c @@ -673,6 +673,9 @@ static esp_err_t httpd_ws_recv_frame_internal(httpd_req_t *req, httpd_ws_frame_t /* When reading entire packet at once, we only accept the incoming packet length that is smaller than the max_len (or it will overflow the buffer!) */ if (!partial) { ESP_LOGW(TAG, LOG_FMT("WS Message too long")); + /* The payload is still queued in the socket. Fail the connection + * (RFC 6455 §7.4.1, close code 1009) so the session is torn down. */ + httpd_ws_fail_connection(req, HTTPD_WS_CLOSE_CODE_TOO_BIG); return ESP_ERR_INVALID_SIZE; } ESP_LOGD(TAG, LOG_FMT("WS Message too long. User will have to call read again")); diff --git a/components/esp_http_server/test_apps/main/test_http_server.c b/components/esp_http_server/test_apps/main/test_http_server.c index c5334b0f7c7..e4a06632978 100644 --- a/components/esp_http_server/test_apps/main/test_http_server.c +++ b/components/esp_http_server/test_apps/main/test_http_server.c @@ -1072,6 +1072,35 @@ TEST_CASE("WS auto CLOSE reply echoes received payload", "[HTTP SERVER][websocke TEST_ASSERT_EQUAL_UINT8_ARRAY(expected_reply, ws_send_capture_ctx.data, sizeof(expected_reply)); } +TEST_CASE("WS recv oversized frame fails connection with CLOSE 1009", "[HTTP SERVER][websocket]") +{ + /* A masked TEXT frame with a 4-byte payload, read with max_len = 2. The frame + * does not fit, and the payload is still queued in the socket. The library + * must fail the connection (CLOSE 1009). The scripted stream carries a second TEXT frame + * after the payload to represent that queued data. */ + static const uint8_t ws_frame[] = { + 0x84, 0x00, 0x00, 0x00, 0x00, 'd', 'a', 't', 'a', /* first frame, len 4, zero mask */ + 0x81, 0x80, 0x00, 0x00, 0x00, 0x00 /* a second frame that must never be read */ + }; + struct httpd_data hd = {0}; + httpd_req_t req = {0}; + struct httpd_req_aux aux = {0}; + struct sock_db session = {0}; + httpd_ws_frame_t frame = {0}; + + ws_setup_recv_fixture(&hd, &req, &aux, &session, ws_frame, sizeof(ws_frame)); + aux.ws_type = HTTPD_WS_TYPE_TEXT; + aux.ws_final = true; + + TEST_ASSERT_EQUAL(ESP_ERR_INVALID_SIZE, httpd_ws_recv_frame(&req, &frame, 2)); + ws_assert_close_sent(&session, 1009); + + /* The library must fail, not drain: only the length byte and the 4 mask + * bytes are consumed. The 4 payload bytes and the whole second frame stay + * unread. */ + TEST_ASSERT_EQUAL(5, ws_scripted_recv_ctx.offset); +} + TEST_CASE("WS recv rejects CLOSE frame with 1-byte payload", "[HTTP SERVER][websocket]") { /* CLOSE with 1-byte body — always invalid */