From b1dc3e1e0619c24262b6752adcbfd35c94876947 Mon Sep 17 00:00:00 2001 From: Matt Rodgers Date: Wed, 1 Jan 2025 12:41:48 +0000 Subject: [PATCH 1/2] net: websocket: pass HTTP upgrade request context to user callback Passing HTTP upgrade request context to the user callback allows the user to decide whether to accept or reject the websocket connection based on the HTTP headers in the request. The primary reason for this is to enable authentication of the websocket connection (e.g. via cookies or Authorization header). Signed-off-by: Matt Rodgers --- include/zephyr/net/http/server.h | 3 ++- include/zephyr/shell/shell_websocket.h | 4 ++-- samples/net/sockets/http_server/src/ws.c | 4 ++-- samples/net/sockets/http_server/src/ws.h | 8 ++++++-- subsys/net/lib/http/http_server_http1.c | 1 + subsys/net/lib/http/http_server_ws.c | 10 +++++++++- subsys/shell/backends/shell_websocket.c | 2 +- 7 files changed, 23 insertions(+), 9 deletions(-) diff --git a/include/zephyr/net/http/server.h b/include/zephyr/net/http/server.h index 4cb40e52eb6de5..7ad5c5f1101787 100644 --- a/include/zephyr/net/http/server.h +++ b/include/zephyr/net/http/server.h @@ -254,6 +254,7 @@ BUILD_ASSERT(offsetof(struct http_resource_detail_dynamic, common) == 0); * reading and writing websocket data, and closing the connection. * * @param ws_socket A socket for the Websocket data. + * @param request_ctx Request context structure associated with HTTP upgrade request * @param user_data User specified data. * * @return 0 Accepting the connection, HTTP server library will no longer @@ -261,7 +262,7 @@ BUILD_ASSERT(offsetof(struct http_resource_detail_dynamic, common) == 0); * to send and receive data to/from the supplied socket. * <0 error, close the connection. */ -typedef int (*http_resource_websocket_cb_t)(int ws_socket, +typedef int (*http_resource_websocket_cb_t)(int ws_socket, struct http_request_ctx *request_ctx, void *user_data); /** @brief Representation of a websocket server resource */ diff --git a/include/zephyr/shell/shell_websocket.h b/include/zephyr/shell/shell_websocket.h index 12bd49727a2498..1e63c22bac4b38 100644 --- a/include/zephyr/shell/shell_websocket.h +++ b/include/zephyr/shell/shell_websocket.h @@ -62,8 +62,8 @@ struct shell_websocket { }; extern const struct shell_transport_api shell_websocket_transport_api; -extern int shell_websocket_setup(int ws_socket, void *user_data); -extern int shell_websocket_enable(const struct shell *sh); +int shell_websocket_setup(int ws_socket, struct http_request_ctx *request_ctx, void *user_data); +int shell_websocket_enable(const struct shell *sh); #define GET_WS_NAME(_service) ws_ctx_##_service #define GET_WS_SHELL_NAME(_name) shell_websocket_##_name diff --git a/samples/net/sockets/http_server/src/ws.c b/samples/net/sockets/http_server/src/ws.c index 7215aeefb62b47..1338e437419d1b 100644 --- a/samples/net/sockets/http_server/src/ws.c +++ b/samples/net/sockets/http_server/src/ws.c @@ -295,7 +295,7 @@ int ws_netstats_init(void) } SYS_INIT(ws_netstats_init, APPLICATION, 0); -int ws_echo_setup(int ws_socket, void *user_data) +int ws_echo_setup(int ws_socket, struct http_request_ctx *request_ctx, void *user_data) { int slot; @@ -331,7 +331,7 @@ int ws_echo_setup(int ws_socket, void *user_data) return 0; } -int ws_netstats_setup(int ws_socket, void *user_data) +int ws_netstats_setup(int ws_socket, struct http_request_ctx *request_ctx, void *user_data) { int ret; int slot; diff --git a/samples/net/sockets/http_server/src/ws.h b/samples/net/sockets/http_server/src/ws.h index 85ba61bbe16770..0afdd6287f095c 100644 --- a/samples/net/sockets/http_server/src/ws.h +++ b/samples/net/sockets/http_server/src/ws.h @@ -4,22 +4,26 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include + /** * @brief Setup websocket for echoing data back to client * * @param ws_socket Socket file descriptor associated with websocket + * @param request_ctx Request context associated with websocket HTTP upgrade request * @param user_data User data pointer * * @return 0 on success */ -int ws_echo_setup(int ws_socket, void *user_data); +int ws_echo_setup(int ws_socket, struct http_request_ctx *request_ctx, void *user_data); /** * @brief Setup websocket for sending net statistics to client * * @param ws_socket Socket file descriptor associated with websocket + * @param request_ctx Request context associated with websocket HTTP upgrade request * @param user_data User data pointer * * @return 0 on success */ -int ws_netstats_setup(int ws_socket, void *user_data); +int ws_netstats_setup(int ws_socket, struct http_request_ctx *request_ctx, void *user_data); diff --git a/subsys/net/lib/http/http_server_http1.c b/subsys/net/lib/http/http_server_http1.c index e5ea4d478d2811..e241cfc2f79344 100644 --- a/subsys/net/lib/http/http_server_http1.c +++ b/subsys/net/lib/http/http_server_http1.c @@ -865,6 +865,7 @@ int handle_http1_request(struct http_client_ctx *client) goto not_found; } + detail->path_len = path_len; client->current_detail = detail; return handle_http1_to_websocket_upgrade(client); } diff --git a/subsys/net/lib/http/http_server_ws.c b/subsys/net/lib/http/http_server_ws.c index 4cf1a7d1a6c98a..8c1398c43e6e8b 100644 --- a/subsys/net/lib/http/http_server_ws.c +++ b/subsys/net/lib/http/http_server_ws.c @@ -93,7 +93,10 @@ int handle_http1_to_websocket_upgrade(struct http_client_ctx *client) */ if (client->parser_state == HTTP1_MESSAGE_COMPLETE_STATE) { struct http_resource_detail_websocket *ws_detail; + struct http_request_ctx request_ctx; int ws_sock; + char *params; + size_t params_len; ws_detail = (struct http_resource_detail_websocket *)client->current_detail; @@ -105,9 +108,14 @@ int handle_http1_to_websocket_upgrade(struct http_client_ctx *client) goto error; } + memset(&request_ctx, 0, sizeof(request_ctx)); + params = &client->url_buffer[client->current_detail->path_len]; + params_len = strlen(params); + populate_request_ctx(&request_ctx, params, params_len, &client->header_capture_ctx); + + ret = ws_detail->cb(ws_sock, &request_ctx, ws_detail->user_data); http_server_release_client(client); - ret = ws_detail->cb(ws_sock, ws_detail->user_data); if (ret < 0) { NET_DBG("WS connection failed (%d)", ret); websocket_unregister(ws_sock); diff --git a/subsys/shell/backends/shell_websocket.c b/subsys/shell/backends/shell_websocket.c index 62b8dc9a5f8e15..fd0bc4d2b1f8b8 100644 --- a/subsys/shell/backends/shell_websocket.c +++ b/subsys/shell/backends/shell_websocket.c @@ -377,7 +377,7 @@ const struct shell_transport_api shell_websocket_transport_api = { .read = sh_read }; -int shell_websocket_setup(int ws_socket, void *user_data) +int shell_websocket_setup(int ws_socket, struct http_request_ctx *request_ctx, void *user_data) { struct shell_websocket *ws = user_data; From 15f48a342f91eeaa252b2b98f025a6e03552d82c Mon Sep 17 00:00:00 2001 From: Matt Rodgers Date: Wed, 1 Jan 2025 13:09:16 +0000 Subject: [PATCH 2/2] doc: websocket: update docs to describe additional callback parameter Update HTTP server documentation and migration guide to account for added request_ctx parameter to the http_resource_websocket_cb_t callback. Signed-off-by: Matt Rodgers --- doc/connectivity/networking/api/http_server.rst | 2 +- doc/releases/migration-guide-4.1.rst | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/connectivity/networking/api/http_server.rst b/doc/connectivity/networking/api/http_server.rst index 9514e5ab8f7122..b59d7d6896c140 100644 --- a/doc/connectivity/networking/api/http_server.rst +++ b/doc/connectivity/networking/api/http_server.rst @@ -342,7 +342,7 @@ release it when done. static int ws_socket; static uint8_t ws_recv_buffer[1024]; - int ws_setup(int sock, void *user_data) + int ws_setup(int sock, struct http_request_ctx *request_ctx, void *user_data) { ws_socket = sock; return 0; diff --git a/doc/releases/migration-guide-4.1.rst b/doc/releases/migration-guide-4.1.rst index fce9ffa3c13cce..cdbd595fcb20b6 100644 --- a/doc/releases/migration-guide-4.1.rst +++ b/doc/releases/migration-guide-4.1.rst @@ -367,6 +367,11 @@ Networking rather than directly in the :c:struct:`http_client_ctx` to correctly handle concurrent requests on different HTTP/2 streams. +* The HTTP server public API function signature for the :c:type:`http_resource_websocket_cb_t` has + changed, a :c:struct:`http_request_ctx` parameter has been added. The application may use this to + access the request headers of the HTTP upgrade request, which may be useful in deciding whether + to accept or reject a websocket connection. + * The :kconfig:option:`CONFIG_NET_L2_OPENTHREAD` symbol no longer implies the :kconfig:option:`CONFIG_NVS` Kconfig option. Platforms using OpenThread must explicitly enable either the :kconfig:option:`CONFIG_NVS` or :kconfig:option:`CONFIG_ZMS` Kconfig option.