feat(http_server): improve websocket server handling

1. Adds post handshake callback
2. Removes requirement to handle HTTP_GET message in websocket handler

Closes https://github.com/espressif/esp-idf/issues/18215
This commit is contained in:
Ashish Sharma
2026-02-11 17:07:08 +08:00
parent 799c800494
commit 6f392e6fd6
7 changed files with 104 additions and 15 deletions

View File

@@ -9,4 +9,15 @@ menu "Example Configuration"
This will allow the server to register a callback function that will be
called before the WebSocket handshake is processed.
config EXAMPLE_ENABLE_WS_POST_HANDSHAKE_CB
bool "Enable WebSocket post-handshake callback"
select HTTPD_WS_POST_HANDSHAKE_CB_SUPPORT
default y
help
Enable this option to use WebSocket post-handshake callback.
This will allow the server to register a callback function that will be
called after the WebSocket handshake is processed.
In this example, the post-handshake callback is used to send a welcome message
to the client after the handshake is complete.
endmenu

View File

@@ -87,16 +87,36 @@ static esp_err_t ws_pre_handshake_cb(httpd_req_t *req)
}
#endif
#ifdef CONFIG_EXAMPLE_ENABLE_WS_POST_HANDSHAKE_CB
static esp_err_t ws_post_handshake_cb(httpd_req_t *req)
{
ESP_LOGI(TAG, "=== ws_post_handshake_cb called ===");
// Get the URI with query string
const char *uri = req->uri;
ESP_LOGI(TAG, "WebSocket connection established for URI: %s", uri ? uri : "NULL");
// Send a welcome message to the client
httpd_ws_frame_t ws_pkt;
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
ws_pkt.type = HTTPD_WS_TYPE_TEXT;
ws_pkt.payload = (uint8_t *)"Welcome to the WebSocket Echo Server (post-handshake)!";
ws_pkt.len = strlen((char *)ws_pkt.payload);
esp_err_t ret = httpd_ws_send_frame(req, &ws_pkt);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "httpd_ws_send_frame failed with %d", ret);
return ret;
}
return ESP_OK;
}
#endif /* CONFIG_EXAMPLE_ENABLE_WS_POST_HANDSHAKE_CB */
/*
* This handler echos back the received ws data
* and triggers an async send if certain message received
*/
static esp_err_t echo_handler(httpd_req_t *req)
{
if (req->method == HTTP_GET) {
ESP_LOGI(TAG, "Handshake done, the new connection was opened");
return ESP_OK;
}
httpd_ws_frame_t ws_pkt;
uint8_t *buf = NULL;
memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t));
@@ -156,8 +176,11 @@ static const httpd_uri_t ws_auth = {
.user_ctx = NULL,
.is_websocket = true,
#ifdef CONFIG_EXAMPLE_ENABLE_WS_PRE_HANDSHAKE_CB
.ws_pre_handshake_cb = ws_pre_handshake_cb
#endif
.ws_pre_handshake_cb = ws_pre_handshake_cb,
#endif /* CONFIG_EXAMPLE_ENABLE_WS_PRE_HANDSHAKE_CB */
#ifdef CONFIG_EXAMPLE_ENABLE_WS_POST_HANDSHAKE_CB
.ws_post_handshake_cb = ws_post_handshake_cb,
#endif /* CONFIG_EXAMPLE_ENABLE_WS_POST_HANDSHAKE_CB */
};

View File

@@ -119,11 +119,30 @@ def test_ws_auth_handshake(dut: Dut) -> None:
handshake_success = False
try:
# Attempt to use WSClient, expecting it to fail handshake
with WsClient(got_ip, int(got_port), uri='auth?token=valid') as ws: # type: ignore # noqa: F841
with WsClient(got_ip, int(got_port), uri='auth?token=invalid') as ws: # type: ignore # noqa: F841
handshake_success = True
except Exception as e:
logging.info(f'WebSocket handshake failed: {e}')
handshake_success = False
if handshake_success is False:
if handshake_success is True:
raise RuntimeError('WebSocket handshake succeeded, but it should have been rejected by ws_pre_handshake_cb')
try:
# Attempt to use WSClient, expecting it to succeed handshake
with WsClient(got_ip, int(got_port), uri='auth?token=valid') as ws: # type: ignore # noqa: F841
handshake_success = True
dut.expect(r'ws_pre_handshake_cb called', timeout=10)
dut.expect(r'Valid token found, accepting handshake', timeout=10)
opcode, data = ws.read()
logging.info(f'Received opcode:{opcode}, data:{data}')
if opcode != OPCODE_TEXT or data.decode() != 'Welcome to the WebSocket Echo Server (post-handshake)!':
raise RuntimeError(
f'Failed to receive correct welcome message after handshake. Opcode:{opcode}, data:{data}'
)
except Exception as e:
logging.info(f'WebSocket handshake failed: {e}')
handshake_success = False
if handshake_success is False:
raise RuntimeError('WebSocket handshake failed, but it should have succeeded with valid token')