diff --git a/include/zephyr/net/http/service.h b/include/zephyr/net/http/service.h index b43de0c73dbfd..e68e7c58dbe10 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/net/net_if.h b/include/zephyr/net/net_if.h index 5570c397b34dd..aaa23d886203d 100644 --- a/include/zephyr/net/net_if.h +++ b/include/zephyr/net/net_if.h @@ -3475,7 +3475,7 @@ extern int net_stats_prometheus_scrape(struct prometheus_collector *collector, }; \ static Z_DECL_ALIGN(struct net_if) \ NET_IF_GET_NAME(dev_id, sfx)[NET_IF_MAX_CONFIGS] \ - __used __in_section(_net_if, static, \ + __used __noasan __in_section(_net_if, static, \ dev_id) = { \ [0 ... (NET_IF_MAX_CONFIGS - 1)] = { \ .if_dev = &(NET_IF_DEV_GET_NAME(dev_id, sfx)), \ diff --git a/subsys/net/lib/http/Kconfig b/subsys/net/lib/http/Kconfig index de676b0344ed4..bd198e9a57afc 100644 --- a/subsys/net/lib/http/Kconfig +++ b/subsys/net/lib/http/Kconfig @@ -44,6 +44,7 @@ if HTTP_SERVER config HTTP_SERVER_STACK_SIZE int "HTTP server thread stack size" + default 4096 if FILE_SYSTEM default 3072 help HTTP server thread stack size for processing RX/TX events. @@ -219,6 +220,17 @@ config HTTP_SERVER_COMPRESSION 5. deflate -> .zz 6. File without compression +config HTTP_SERVER_STATIC_FS_RESPONSE_SIZE + int "Size of static file system response buffer" + depends on FILE_SYSTEM + default 1024 + help + The size of a single chunk when serving static files from the file system. + This config value must be large enough to hold the headers in a single chunk. + If set to 0, the server will use the minimal viable buffer size for the response. + Please note that it is allocated on the stack of the HTTP server thread, + so CONFIG_HTTP_SERVER_STACK_SIZE has to be sufficiently large. + endif # Hidden option to avoid having multiple individual options that are ORed together diff --git a/subsys/net/lib/http/http_server_core.c b/subsys/net/lib/http/http_server_core.c index 27d73d6180ca7..58e2d4ad283c0 100644 --- a/subsys/net/lib/http/http_server_core.c +++ b/subsys/net/lib/http/http_server_core.c @@ -156,7 +156,12 @@ int http_server_init(struct http_server_ctx *ctx) proto = IPPROTO_TCP; } + 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/subsys/net/lib/http/http_server_http1.c b/subsys/net/lib/http/http_server_http1.c index 399ea8b8efdf2..2c8131971ba24 100644 --- a/subsys/net/lib/http/http_server_http1.c +++ b/subsys/net/lib/http/http_server_http1.c @@ -482,12 +482,23 @@ int handle_http1_static_fs_resource(struct http_resource_detail_static_fs *stati sizeof("Content-Length: 01234567890123456789\r\n") #define CONTENT_ENCODING_HEADER_SIZE \ sizeof(CONTENT_ENCODING_HEADER) + HTTP_COMPRESSION_MAX_STRING_LEN + sizeof("\r\n") + +/* Calculate the minimum required size */ #define STATIC_FS_RESPONSE_SIZE \ COND_CODE_1( \ IS_ENABLED(CONFIG_HTTP_SERVER_COMPRESSION), \ (STATIC_FS_RESPONSE_BASE_SIZE + CONTENT_ENCODING_HEADER_SIZE), \ (STATIC_FS_RESPONSE_BASE_SIZE)) +// Issue in Zephyr: https://github.com/zephyrproject-rtos/zephyr/issues/92921 +#if CONFIG_HTTP_SERVER_STATIC_FS_RESPONSE_SIZE > 0 +BUILD_ASSERT(CONFIG_HTTP_SERVER_STATIC_FS_RESPONSE_SIZE >= STATIC_FS_RESPONSE_SIZE, + "CONFIG_HTTP_SERVER_STATIC_FS_RESPONSE_SIZE must be at least " + "large enough to hold HTTP headers"); +#undef STATIC_FS_RESPONSE_SIZE +#define STATIC_FS_RESPONSE_SIZE CONFIG_HTTP_SERVER_STATIC_FS_RESPONSE_SIZE +#endif + enum http_compression chosen_compression = 0; int len; int remaining; @@ -919,7 +930,7 @@ int handle_http1_request(struct http_client_ctx *client) parsed = http_parser_execute(&client->parser, &client->parser_settings, client->cursor, client->data_len); - + if (parsed > client->data_len) { LOG_ERR("HTTP/1 parser error, too much data consumed"); ret = -EBADMSG;