From 0dc2add556cf26b8cabfb5ab4439553ada9765ca Mon Sep 17 00:00:00 2001 From: Andrey Dodonov Date: Fri, 11 Jul 2025 08:44:03 +0200 Subject: [PATCH] net: http: service: extend HTTP service with config Update the HTTP service API to allow setting per-service configuration, currently it is only custom socket creation callback, but in the future it can be extended with other parameter Signed-off-by: Andrey Dodonov --- .../networking/api/http_server.rst | 61 ++++++++++++++++++- doc/releases/migration-guide-4.3.rst | 5 ++ include/zephyr/net/http/service.h | 37 ++++++++--- include/zephyr/shell/shell_websocket.h | 3 +- samples/net/prometheus/src/main.c | 4 +- samples/net/sockets/http_server/src/main.c | 4 +- subsys/net/lib/http/http_server_core.c | 6 +- tests/net/lib/http_client/src/main.c | 2 +- tests/net/lib/http_server/common/src/main.c | 10 +-- tests/net/lib/http_server/core/src/main.c | 2 +- tests/net/lib/http_server/crime/src/main.c | 2 +- tests/net/lib/http_server/tls/src/main.c | 2 +- 12 files changed, 111 insertions(+), 27 deletions(-) diff --git a/doc/connectivity/networking/api/http_server.rst b/doc/connectivity/networking/api/http_server.rst index 6de138dab98a..ed04c5539d86 100644 --- a/doc/connectivity/networking/api/http_server.rst +++ b/doc/connectivity/networking/api/http_server.rst @@ -106,7 +106,7 @@ macro: static uint16_t http_service_port = 80; - HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL, NULL); + HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL, NULL, NULL); Alternatively, an HTTPS service can be defined with :c:macro:`HTTPS_SERVICE_DEFINE`: @@ -124,7 +124,62 @@ Alternatively, an HTTPS service can be defined with }; HTTPS_SERVICE_DEFINE(my_service, "0.0.0.0", &https_service_port, 1, 10, - NULL, NULL, sec_tag_list, sizeof(sec_tag_list)); + NULL, NULL, NULL, sec_tag_list, sizeof(sec_tag_list)); + +Per-service configuration +========================= + +HTTP services support individual service configuration, +for now it includes only socket creation through +the ``http_service_config`` structure. This allows applications to customize +socket creation behavior, for example to set specific socket options or use +custom socket types. + +To use custom socket creation: + +.. code-block:: c + + static int my_socket_create(const struct http_service_desc *svc, int af, int proto) + { + int fd; + + /* Create socket with custom parameters */ + fd = zsock_socket(af, SOCK_STREAM, proto); + if (fd < 0) { + return fd; + } + + /* Set custom socket options */ + /* Add any other custom socket configuration */ + + return fd; + } + + static const struct http_service_config my_service_config = { + .socket_create = my_socket_create, + }; + + static uint16_t http_service_port = 80; + + HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, + NULL, NULL, &my_service_config); + +The custom socket creation function receives: +- ``svc``: Pointer to the service descriptor +- ``af``: Address family (AF_INET or AF_INET6) +- ``proto``: Protocol (IPPROTO_TCP or IPPROTO_TLS_1_2 for HTTPS) + +The function should return the socket file descriptor on success, or a negative error code on failure. + +If no custom configuration is needed, simply pass ``NULL`` for the config parameter: + +.. code-block:: c + + HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, + NULL, NULL, NULL); + +Fallback Resources +================== The ``_res_fallback`` parameter can be used when defining an HTTP/HTTPS service to specify a fallback resource which will be used if no other resource matches the @@ -160,7 +215,7 @@ customised 404 response. }; /* Register a fallback resource to handle any unknown path */ - HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL, &default_detail); + HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL, &default_detail, NULL); .. note:: diff --git a/doc/releases/migration-guide-4.3.rst b/doc/releases/migration-guide-4.3.rst index f668d9ef6f56..01c0a5625498 100644 --- a/doc/releases/migration-guide-4.3.rst +++ b/doc/releases/migration-guide-4.3.rst @@ -38,6 +38,11 @@ Bluetooth Networking ********** +* The HTTP server now respects the configured ``_config`` value. Check that + you provide applicable value to :c:macro:`HTTP_SERVICE_DEFINE_EMPTY`, + :c:macro:`HTTPS_SERVICE_DEFINE_EMPTY`, :c:macro:`HTTP_SERVICE_DEFINE` and + :c:macro:`HTTPS_SERVICE_DEFINE`. + Other subsystems **************** diff --git a/include/zephyr/net/http/service.h b/include/zephyr/net/http/service.h index b43de0c73dbf..e68e7c58dbe1 100644 --- a/include/zephyr/net/http/service.h +++ b/include/zephyr/net/http/service.h @@ -69,6 +69,18 @@ struct http_service_runtime_data { int num_clients; }; +struct http_service_desc; + +/** Custom socket creation function type */ +typedef int (*http_socket_create_fn)(const struct http_service_desc *svc, int af, int proto); + +/** HTTP service configuration */ +struct http_service_config { + /** Custom socket creation for the service if needed */ + http_socket_create_fn socket_create; + /* If any more service-specific configuration is needed, it can be added here. */ +}; + struct http_service_desc { const char *host; uint16_t *port; @@ -80,6 +92,7 @@ struct http_service_desc { struct http_resource_desc *res_begin; struct http_resource_desc *res_end; struct http_resource_detail *res_fallback; + const struct http_service_config *config; #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) const sec_tag_t *sec_tag_list; size_t sec_tag_list_size; @@ -87,7 +100,7 @@ struct http_service_desc { }; #define __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, \ - _res_fallback, _res_begin, _res_end, ...) \ + _res_fallback, _res_begin, _res_end, _config, ...) \ BUILD_ASSERT(_concurrent <= CONFIG_HTTP_SERVER_MAX_CLIENTS, \ "can't accept more then MAX_CLIENTS"); \ BUILD_ASSERT(_backlog > 0, "backlog can't be 0"); \ @@ -104,6 +117,7 @@ struct http_service_desc { .res_begin = (_res_begin), \ .res_end = (_res_end), \ .res_fallback = (_res_fallback), \ + .config = (_config), \ COND_CODE_1(CONFIG_NET_SOCKETS_SOCKOPT_TLS, \ (.sec_tag_list = COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (NULL), \ (GET_ARG_N(1, __VA_ARGS__))),), ()) \ @@ -133,11 +147,12 @@ struct http_service_desc { * @param _backlog Maximum number of queued connections. (min. 1) * @param _detail User-defined detail associated with the service. * @param _res_fallback Fallback resource to be served if no other resource matches path + * @param _config Pointer to http_service_config structure (can be NULL for default behavior) */ #define HTTP_SERVICE_DEFINE_EMPTY(_name, _host, _port, _concurrent, _backlog, _detail, \ - _res_fallback) \ + _res_fallback, _config) \ __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, \ - _res_fallback, NULL, NULL) + _res_fallback, NULL, NULL, _config) /** * @brief Define an HTTPS service without static resources. @@ -158,13 +173,14 @@ struct http_service_desc { * @param _backlog Maximum number of queued connections. (min. 1) * @param _detail User-defined detail associated with the service. * @param _res_fallback Fallback resource to be served if no other resource matches path + * @param _config Pointer to http_service_config structure (can be NULL for default behavior) * @param _sec_tag_list TLS security tag list used to setup a HTTPS socket. * @param _sec_tag_list_size TLS security tag list size used to setup a HTTPS socket. */ #define HTTPS_SERVICE_DEFINE_EMPTY(_name, _host, _port, _concurrent, _backlog, _detail, \ - _res_fallback, _sec_tag_list, _sec_tag_list_size) \ + _res_fallback, _config, _sec_tag_list, _sec_tag_list_size) \ __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, \ - _res_fallback, NULL, NULL, \ + _res_fallback, NULL, NULL, _config, \ _sec_tag_list, _sec_tag_list_size); \ BUILD_ASSERT(IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS), \ "TLS is required for HTTP secure (CONFIG_NET_SOCKETS_SOCKOPT_TLS)") @@ -188,14 +204,16 @@ struct http_service_desc { * @param _backlog Maximum number of queued connections. (min. 1) * @param _detail User-defined detail associated with the service. * @param _res_fallback Fallback resource to be served if no other resource matches path + * @param _config Pointer to http_service_config structure (can be NULL for default behavior) */ -#define HTTP_SERVICE_DEFINE(_name, _host, _port, _concurrent, _backlog, _detail, _res_fallback) \ +#define HTTP_SERVICE_DEFINE(_name, _host, _port, _concurrent, _backlog, _detail, _res_fallback, \ + _config) \ extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_start)[]; \ extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_end)[]; \ __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, \ _res_fallback, \ &_CONCAT(_http_resource_desc_##_name, _list_start)[0], \ - &_CONCAT(_http_resource_desc_##_name, _list_end)[0]); + &_CONCAT(_http_resource_desc_##_name, _list_end)[0], _config); /** * @brief Define an HTTPS service with static resources. @@ -216,17 +234,18 @@ struct http_service_desc { * @param _backlog Maximum number of queued connections. (min. 1) * @param _detail User-defined detail associated with the service. * @param _res_fallback Fallback resource to be served if no other resource matches path + * @param _config Pointer to http_service_config structure (can be NULL for default behavior) * @param _sec_tag_list TLS security tag list used to setup a HTTPS socket. * @param _sec_tag_list_size TLS security tag list size used to setup a HTTPS socket. */ #define HTTPS_SERVICE_DEFINE(_name, _host, _port, _concurrent, _backlog, _detail, \ - _res_fallback, _sec_tag_list, _sec_tag_list_size) \ + _res_fallback, _config, _sec_tag_list, _sec_tag_list_size) \ extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_start)[]; \ extern struct http_resource_desc _CONCAT(_http_resource_desc_##_name, _list_end)[]; \ __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, \ _res_fallback, \ &_CONCAT(_http_resource_desc_##_name, _list_start)[0], \ - &_CONCAT(_http_resource_desc_##_name, _list_end)[0], \ + &_CONCAT(_http_resource_desc_##_name, _list_end)[0], _config, \ _sec_tag_list, _sec_tag_list_size); \ BUILD_ASSERT(IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS), \ "TLS is required for HTTP secure (CONFIG_NET_SOCKETS_SOCKOPT_TLS)") diff --git a/include/zephyr/shell/shell_websocket.h b/include/zephyr/shell/shell_websocket.h index dd93e6913bae..1d5b79f443f1 100644 --- a/include/zephyr/shell/shell_websocket.h +++ b/include/zephyr/shell/shell_websocket.h @@ -122,6 +122,7 @@ int shell_websocket_enable(const struct shell *sh); SHELL_WEBSOCKET_SERVICE_COUNT, \ NULL, \ NULL, \ + NULL, \ _sec_tag_list, \ _sec_tag_list_size); \ DEFINE_WEBSOCKET_SERVICE(_service); \ @@ -137,7 +138,7 @@ int shell_websocket_enable(const struct shell *sh); &SHELL_WS_PORT_NAME(_service), \ SHELL_WEBSOCKET_SERVICE_COUNT, \ SHELL_WEBSOCKET_SERVICE_COUNT, \ - NULL, NULL); \ + NULL, NULL, NULL); \ DEFINE_WEBSOCKET_SERVICE(_service) #endif /* CONFIG_NET_SOCKETS_SOCKOPT_TLS */ diff --git a/samples/net/prometheus/src/main.c b/samples/net/prometheus/src/main.c index 61aa362cc328..f4380ef45bed 100644 --- a/samples/net/prometheus/src/main.c +++ b/samples/net/prometheus/src/main.c @@ -40,7 +40,7 @@ struct app_context { #if defined(CONFIG_NET_SAMPLE_HTTP_SERVICE) static uint16_t test_http_service_port = CONFIG_NET_SAMPLE_HTTP_SERVER_SERVICE_PORT; HTTP_SERVICE_DEFINE(test_http_service, CONFIG_NET_CONFIG_MY_IPV4_ADDR, &test_http_service_port, - CONFIG_HTTP_SERVER_MAX_CLIENTS, 10, NULL, NULL); + CONFIG_HTTP_SERVER_MAX_CLIENTS, 10, NULL, NULL, NULL); static int dyn_handler(struct http_client_ctx *client, enum http_data_status status, const struct http_request_ctx *request_ctx, @@ -99,7 +99,7 @@ const sec_tag_t sec_tag_list_verify_none[] = { static uint16_t test_https_service_port = CONFIG_NET_SAMPLE_HTTPS_SERVER_SERVICE_PORT; HTTPS_SERVICE_DEFINE(test_https_service, CONFIG_NET_CONFIG_MY_IPV4_ADDR, &test_https_service_port, - CONFIG_HTTP_SERVER_MAX_CLIENTS, 10, NULL, NULL, sec_tag_list_verify_none, + CONFIG_HTTP_SERVER_MAX_CLIENTS, 10, NULL, NULL, NULL, sec_tag_list_verify_none, sizeof(sec_tag_list_verify_none)); HTTP_RESOURCE_DEFINE(index_html_gz_resource_https, test_https_service, "/metrics", diff --git a/samples/net/sockets/http_server/src/main.c b/samples/net/sockets/http_server/src/main.c index 4615d0dcb248..66fde7beddc5 100644 --- a/samples/net/sockets/http_server/src/main.c +++ b/samples/net/sockets/http_server/src/main.c @@ -248,7 +248,7 @@ struct http_resource_detail_websocket ws_netstats_resource_detail = { #if defined(CONFIG_NET_SAMPLE_HTTP_SERVICE) static uint16_t test_http_service_port = CONFIG_NET_SAMPLE_HTTP_SERVER_SERVICE_PORT; HTTP_SERVICE_DEFINE(test_http_service, NULL, &test_http_service_port, - CONFIG_HTTP_SERVER_MAX_CLIENTS, 10, NULL, NULL); + CONFIG_HTTP_SERVER_MAX_CLIENTS, 10, NULL, NULL, NULL); HTTP_RESOURCE_DEFINE(index_html_gz_resource, test_http_service, "/", &index_html_gz_resource_detail); @@ -281,7 +281,7 @@ static const sec_tag_t sec_tag_list_verify_none[] = { static uint16_t test_https_service_port = CONFIG_NET_SAMPLE_HTTPS_SERVER_SERVICE_PORT; HTTPS_SERVICE_DEFINE(test_https_service, NULL, &test_https_service_port, - CONFIG_HTTP_SERVER_MAX_CLIENTS, 10, NULL, NULL, sec_tag_list_verify_none, + CONFIG_HTTP_SERVER_MAX_CLIENTS, 10, NULL, NULL, NULL, sec_tag_list_verify_none, sizeof(sec_tag_list_verify_none)); HTTP_RESOURCE_DEFINE(index_html_gz_resource_https, test_https_service, "/", diff --git a/subsys/net/lib/http/http_server_core.c b/subsys/net/lib/http/http_server_core.c index 27d73d6180ca..c8157117bc0a 100644 --- a/subsys/net/lib/http/http_server_core.c +++ b/subsys/net/lib/http/http_server_core.c @@ -156,7 +156,11 @@ int http_server_init(struct http_server_ctx *ctx) proto = IPPROTO_TCP; } - fd = zsock_socket(af, SOCK_STREAM, proto); + if (svc->config != NULL && svc->config->socket_create != NULL) { + fd = svc->config->socket_create(svc, af, proto); + } else { + fd = zsock_socket(af, SOCK_STREAM, proto); + } if (fd < 0) { LOG_ERR("socket: %d", errno); failed++; diff --git a/tests/net/lib/http_client/src/main.c b/tests/net/lib/http_client/src/main.c index 23947e774afa..a7737dad5652 100644 --- a/tests/net/lib/http_client/src/main.c +++ b/tests/net/lib/http_client/src/main.c @@ -15,7 +15,7 @@ static uint16_t test_http_service_port = SERVER_PORT; HTTP_SERVICE_DEFINE(test_http_service, SERVER_IPV6_ADDR, - &test_http_service_port, 1, 10, NULL, NULL); + &test_http_service_port, 1, 10, NULL, NULL, NULL); static const char static_resource_payload[] = LOREM_IPSUM_SHORT; struct http_resource_detail_static static_resource_detail = { diff --git a/tests/net/lib/http_server/common/src/main.c b/tests/net/lib/http_server/common/src/main.c index 4b532af44afa..fcaaa6055b2e 100644 --- a/tests/net/lib/http_server/common/src/main.c +++ b/tests/net/lib/http_server/common/src/main.c @@ -50,14 +50,14 @@ static struct http_resource_detail detail[] = { * the paths (and implementation-specific details) are known at compile time. */ static const uint16_t service_A_port = 4242; -HTTP_SERVICE_DEFINE(service_A, "a.service.com", &service_A_port, 4, 2, DETAIL(0), NULL); +HTTP_SERVICE_DEFINE(service_A, "a.service.com", &service_A_port, 4, 2, DETAIL(0), NULL, NULL); HTTP_RESOURCE_DEFINE(resource_0, service_A, "/", RES(0)); HTTP_RESOURCE_DEFINE(resource_1, service_A, "/index.html", RES(1)); HTTP_RESOURCE_DEFINE(resource_2, service_A, "/fs/*", RES(5)); /* ephemeral port of 0 */ static uint16_t service_B_port; -HTTP_SERVICE_DEFINE(service_B, "b.service.com", &service_B_port, 7, 3, DETAIL(1), NULL); +HTTP_SERVICE_DEFINE(service_B, "b.service.com", &service_B_port, 7, 3, DETAIL(1), NULL, NULL); HTTP_RESOURCE_DEFINE(resource_3, service_B, "/foo.htm", RES(2)); HTTP_RESOURCE_DEFINE(resource_4, service_B, "/bar/baz.php", RES(3)); @@ -67,11 +67,11 @@ HTTP_RESOURCE_DEFINE(resource_4, service_B, "/bar/baz.php", RES(3)); * runtime. */ static const uint16_t service_C_port = 5959; -HTTP_SERVICE_DEFINE_EMPTY(service_C, "192.168.1.1", &service_C_port, 5, 9, DETAIL(2), NULL); +HTTP_SERVICE_DEFINE_EMPTY(service_C, "192.168.1.1", &service_C_port, 5, 9, DETAIL(2), NULL, NULL); /* Wildcard resources */ static uint16_t service_D_port = service_A_port + 1; -HTTP_SERVICE_DEFINE(service_D, "2001:db8::1", &service_D_port, 7, 3, DETAIL(3), NULL); +HTTP_SERVICE_DEFINE(service_D, "2001:db8::1", &service_D_port, 7, 3, DETAIL(3), NULL, NULL); HTTP_RESOURCE_DEFINE(resource_5, service_D, "/foo1.htm*", RES(0)); HTTP_RESOURCE_DEFINE(resource_6, service_D, "/fo*", RES(1)); HTTP_RESOURCE_DEFINE(resource_7, service_D, "/f[ob]o3.html", RES(1)); @@ -82,7 +82,7 @@ HTTP_RESOURCE_DEFINE(resource_12, service_D, "/foo/b?r", RES(3)); /* Default resource in case of no match */ static uint16_t service_E_port = 8080; -HTTP_SERVICE_DEFINE(service_E, "192.0.2.1", &service_E_port, 1, 1, NULL, DETAIL(0)); +HTTP_SERVICE_DEFINE(service_E, "192.0.2.1", &service_E_port, 1, 1, NULL, DETAIL(0), NULL); HTTP_RESOURCE_DEFINE(resource_10, service_E, "/index.html", RES(4)); ZTEST(http_service, test_HTTP_SERVICE_DEFINE) diff --git a/tests/net/lib/http_server/core/src/main.c b/tests/net/lib/http_server/core/src/main.c index 0ace8f23c9db..5e0f08ddee76 100644 --- a/tests/net/lib/http_server/core/src/main.c +++ b/tests/net/lib/http_server/core/src/main.c @@ -235,7 +235,7 @@ BUILD_ASSERT(sizeof(long_payload) - 1 > CONFIG_HTTP_SERVER_CLIENT_BUFFER_SIZE, static uint16_t test_http_service_port = SERVER_PORT; HTTP_SERVICE_DEFINE(test_http_service, SERVER_IPV4_ADDR, - &test_http_service_port, 1, 10, NULL, NULL); + &test_http_service_port, 1, 10, NULL, NULL, NULL); static const char static_resource_payload[] = TEST_STATIC_PAYLOAD; struct http_resource_detail_static static_resource_detail = { diff --git a/tests/net/lib/http_server/crime/src/main.c b/tests/net/lib/http_server/crime/src/main.c index 3d84b64db4f0..a00118ed1c55 100644 --- a/tests/net/lib/http_server/crime/src/main.c +++ b/tests/net/lib/http_server/crime/src/main.c @@ -46,7 +46,7 @@ static const unsigned char compressed_inc_file[] = { static uint16_t test_http_service_port = SERVER_PORT; HTTP_SERVICE_DEFINE(test_http_service, MY_IPV4_ADDR, &test_http_service_port, 1, - 10, NULL, NULL); + 10, NULL, NULL, NULL); struct http_resource_detail_static index_html_gz_resource_detail = { .common = { diff --git a/tests/net/lib/http_server/tls/src/main.c b/tests/net/lib/http_server/tls/src/main.c index 9361fb1770de..6d710eb350eb 100644 --- a/tests/net/lib/http_server/tls/src/main.c +++ b/tests/net/lib/http_server/tls/src/main.c @@ -43,7 +43,7 @@ static const sec_tag_t server_tag_list_verify[] = { static uint16_t test_http_service_port = SERVER_PORT; HTTPS_SERVICE_DEFINE(test_http_service, MY_IPV4_ADDR, &test_http_service_port, - 1, 10, NULL, NULL, server_tag_list_verify, + 1, 10, NULL, NULL, NULL, server_tag_list_verify, sizeof(server_tag_list_verify)); static const unsigned char ca[] = {