1.. _http_server_interface: 2 3HTTP Server 4########### 5 6.. contents:: 7 :local: 8 :depth: 2 9 10Overview 11******** 12 13Zephyr provides an HTTP server library, which allows to register HTTP services 14and HTTP resources associated with those services. The server creates a listening 15socket for every registered service, and handles incoming client connections. 16It's possible to communicate over a plain TCP socket (HTTP) or a TLS socket (HTTPS). 17Both, HTTP/1.1 (RFC 2616) and HTTP/2 (RFC 9113) protocol versions are supported. 18 19The server operation is generally transparent for the application, running in a 20background thread. The application can control the server activity with 21respective API functions. 22 23Certain resource types (for example dynamic resource) provide resource-specific 24application callbacks, allowing the server to interact with the application (for 25instance provide resource content, or process request payload). 26 27Currently, the following resource types are supported: 28 29* Static resources - content defined compile-time, cannot be modified at runtime 30 (:c:enumerator:`HTTP_RESOURCE_TYPE_STATIC`). 31 32* Dynamic resources - content provided at runtime by respective application 33 callback (:c:enumerator:`HTTP_RESOURCE_TYPE_DYNAMIC`). 34 35* Websocket resources - allowing to establish Websocket connections with the 36 server (:c:enumerator:`HTTP_RESOURCE_TYPE_WEBSOCKET`). 37 38Zephyr provides a sample demonstrating HTTP(s) server operation and various 39resource types usage. See :zephyr:code-sample:`sockets-http-server` for more 40information. 41 42Server Setup 43************ 44 45A few prerequisites are needed in order to enable HTTP server functionality in 46the application. 47 48First of all, the HTTP server has to be enabled in applications configuration file 49with :kconfig:option:`CONFIG_HTTP_SERVER` Kconfig option: 50 51.. code-block:: cfg 52 :caption: ``prj.conf`` 53 54 CONFIG_HTTP_SERVER=y 55 56All HTTP services and HTTP resources are placed in a dedicated linker section. 57The linker section for services is predefined locally, however the application 58is responsible for defining linker sections for resources associated with 59respective services. Linker section names for resources should be prefixed with 60``http_resource_desc_``, appended with the service name. 61 62Linker sections for resources should be defined in a linker file. For example, 63for a service named ``my_service``, the linker section shall be defined as follows: 64 65.. code-block:: c 66 :caption: ``sections-rom.ld`` 67 68 #include <zephyr/linker/iterable_sections.h> 69 70 ITERABLE_SECTION_ROM(http_resource_desc_my_service, Z_LINK_ITERABLE_SUBALIGN) 71 72Finally, the linker file and linker section have to be added to your application 73using CMake: 74 75.. code-block:: cmake 76 :caption: ``CMakeLists.txt`` 77 78 zephyr_linker_sources(SECTIONS sections-rom.ld) 79 zephyr_linker_section(NAME http_resource_desc_my_service 80 KVMA RAM_REGION GROUP RODATA_REGION 81 SUBALIGN ${CONFIG_LINKER_ITERABLE_SUBALIGN}) 82 83.. note:: 84 85 You need to define a separate linker section for each HTTP service 86 registered in the system. 87 88Sample Usage 89************ 90 91Services 92======== 93 94The application needs to define an HTTP service (or multiple services), with 95the same name as used for the linker section with :c:macro:`HTTP_SERVICE_DEFINE` 96macro: 97 98.. code-block:: c 99 100 #include <zephyr/net/http/service.h> 101 102 static uint16_t http_service_port = 80; 103 104 HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL); 105 106Alternatively, an HTTPS service can be defined with 107:c:macro:`HTTPS_SERVICE_DEFINE`: 108 109.. code-block:: c 110 111 #include <zephyr/net/http/service.h> 112 #include <zephyr/net/tls_credentials.h> 113 114 #define HTTP_SERVER_CERTIFICATE_TAG 1 115 116 static uint16_t https_service_port = 443; 117 static const sec_tag_t sec_tag_list[] = { 118 HTTP_SERVER_CERTIFICATE_TAG, 119 }; 120 121 HTTPS_SERVICE_DEFINE(my_service, "0.0.0.0", &https_service_port, 1, 10, 122 NULL, sec_tag_list, sizeof(sec_tag_list)); 123 124.. note:: 125 126 HTTPS services rely on TLS credentials being registered in the system. 127 See :ref:`sockets_tls_credentials_subsys` for information on how to 128 configure TLS credentials in the system. 129 130Once HTTP(s) service is defined, resources can be registered for it with 131:c:macro:`HTTP_RESOURCE_DEFINE` macro. 132 133Application can enable resource wildcard support by enabling 134:kconfig:option:`CONFIG_HTTP_SERVER_RESOURCE_WILDCARD` option. When this 135option is set, then it is possible to match several incoming HTTP requests 136with just one resource handler. The `fnmatch() 137<https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html>`__ 138POSIX API function is used to match the pattern in the URL paths. 139 140Example: 141 142.. code-block:: c 143 144 HTTP_RESOURCE_DEFINE(my_resource, my_service, "/foo*", &resource_detail); 145 146This would match all URLs that start with a string ``foo``. See 147`POSIX.2 chapter 2.13 148<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13>`__ 149for pattern matching syntax description. 150 151Static resources 152================ 153 154Static resource content is defined build-time and is immutable. The following 155example shows how gzip compressed webpage can be defined as a static resource 156in the application: 157 158.. code-block:: c 159 160 static const uint8_t index_html_gz[] = { 161 #include "index.html.gz.inc" 162 }; 163 164 struct http_resource_detail_static index_html_gz_resource_detail = { 165 .common = { 166 .type = HTTP_RESOURCE_TYPE_STATIC, 167 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 168 .content_encoding = "gzip", 169 }, 170 .static_data = index_html_gz, 171 .static_data_len = sizeof(index_html_gz), 172 }; 173 174 HTTP_RESOURCE_DEFINE(index_html_gz_resource, my_service, "/", 175 &index_html_gz_resource_detail); 176 177The resource content and content encoding is application specific. For the above 178example, a gzip compressed webpage can be generated during build, by adding the 179following code to the application's ``CMakeLists.txt`` file: 180 181.. code-block:: cmake 182 :caption: ``CMakeLists.txt`` 183 184 set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) 185 set(source_file_index src/index.html) 186 generate_inc_file_for_target(app ${source_file_index} ${gen_dir}/index.html.gz.inc --gzip) 187 188where ``src/index.html`` is the location of the webpage to be compressed. 189 190Static filesystem resources 191=========================== 192 193Static filesystem resource content is defined build-time and is immutable. The following 194example shows how the path can be defined as a static resource in the application: 195 196.. code-block:: c 197 198 struct http_resource_detail_static_fs static_fs_resource_detail = { 199 .common = { 200 .type = HTTP_RESOURCE_TYPE_STATIC_FS, 201 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 202 }, 203 .fs_path = "/lfs1/www", 204 }; 205 206 HTTP_RESOURCE_DEFINE(static_fs_resource, my_service, "*", &static_fs_resource_detail); 207 208All files located in /lfs1/www are made available to the client. If a file is 209gzipped, .gz must be appended to the file name (e.g. index.html.gz), then the 210server delivers index.html.gz when the client requests index.html and adds gzip 211content-encoding to the HTTP header. 212 213The content type is evaluated based on the file extension. The server supports 214.html, .js, .css, .jpg, .png and .svg. More content types can be provided with the 215:c:macro:`HTTP_SERVER_CONTENT_TYPE` macro. All other files are provided with the 216content type text/html. 217 218.. code-block:: c 219 220 HTTP_SERVER_CONTENT_TYPE(json, "application/json") 221 222Dynamic resources 223================= 224 225For dynamic resource, a resource callback is registered to exchange data between 226the server and the application. 227 228The following example code shows how to register a dynamic resource with a simple 229resource handler, which echoes received data back to the client: 230 231.. code-block:: c 232 233 static int dyn_handler(struct http_client_ctx *client, enum http_data_status status, 234 const struct http_request_ctx *request_ctx, 235 struct http_response_ctx *response_ctx, void *user_data) 236 { 237 #define MAX_TEMP_PRINT_LEN 32 238 static char print_str[MAX_TEMP_PRINT_LEN]; 239 enum http_method method = client->method; 240 static size_t processed; 241 242 __ASSERT_NO_MSG(buffer != NULL); 243 244 if (status == HTTP_SERVER_DATA_ABORTED) { 245 LOG_DBG("Transaction aborted after %zd bytes.", processed); 246 processed = 0; 247 return 0; 248 } 249 250 processed += request_ctx->data_len; 251 252 snprintf(print_str, sizeof(print_str), "%s received (%zd bytes)", 253 http_method_str(method), request_ctx->data_len); 254 LOG_HEXDUMP_DBG(request_ctx->data, request_ctx->data_len, print_str); 255 256 if (status == HTTP_SERVER_DATA_FINAL) { 257 LOG_DBG("All data received (%zd bytes).", processed); 258 processed = 0; 259 } 260 261 /* Echo data back to client */ 262 response_ctx->body = request_ctx->data; 263 response_ctx->body_len = request_ctx->data_len; 264 response_ctx->final_chunk = (status == HTTP_SERVER_DATA_FINAL); 265 266 return 0; 267 } 268 269 struct http_resource_detail_dynamic dyn_resource_detail = { 270 .common = { 271 .type = HTTP_RESOURCE_TYPE_DYNAMIC, 272 .bitmask_of_supported_http_methods = 273 BIT(HTTP_GET) | BIT(HTTP_POST), 274 }, 275 .cb = dyn_handler, 276 .user_data = NULL, 277 }; 278 279 HTTP_RESOURCE_DEFINE(dyn_resource, my_service, "/dynamic", 280 &dyn_resource_detail); 281 282 283The resource callback may be called multiple times for a single request, hence 284the application should be able to keep track of the received data progress. 285 286The ``status`` field informs the application about the progress in passing 287request payload from the server to the application. As long as the status 288reports :c:enumerator:`HTTP_SERVER_DATA_MORE`, the application should expect 289more data to be provided in a consecutive callback calls. 290Once all request payload has been passed to the application, the server reports 291:c:enumerator:`HTTP_SERVER_DATA_FINAL` status. In case of communication errors 292during request processing (for example client closed the connection before 293complete payload has been received), the server reports 294:c:enumerator:`HTTP_SERVER_DATA_ABORTED`. Either of the two events indicate that 295the application shall reset any progress recorded for the resource, and await 296a new request to come. The server guarantees that the resource can only be 297accessed by single client at a time. 298 299The ``request_ctx`` parameter is used to pass request data to the application: 300 301* The ``data`` and ``data_len`` fields pass request data to the application. 302 303* The ``headers``, ``header_count`` and ``headers_status`` fields pass request 304 headers to the application, if 305 :kconfig:option:`CONFIG_HTTP_SERVER_CAPTURE_HEADERS` is enabled. 306 307The ``response_ctx`` field is used by the application to pass response data to 308the HTTP server: 309 310* The ``status`` field allows the application to send an HTTP response code. If 311 not populated, the response code will be 200 by default. 312 313* The ``headers`` and ``header_count`` fields can be used for the application to 314 send any arbitrary HTTP headers. If not populated, only Transfer-Encoding and 315 Content-Type are sent by default. The callback may override the Content-Type 316 if desired. 317 318* The ``body`` and ``body_len`` fields are used to send body data. 319 320* The ``final_chunk`` field is used to indicate that the application has no more 321 response data to send. 322 323Headers and/or response codes may only be sent in the first populated 324``response_ctx``, after which only further body data is allowed in subsequent 325callbacks. 326 327The server will call the resource callback until it provided all request data 328to the application, and the application reports there is no more data to include 329in the reply. 330 331Websocket resources 332=================== 333 334Websocket resources register an application callback, which is called when a 335Websocket connection upgrade takes place. The callback is provided with a socket 336descriptor corresponding to the underlying TCP/TLS connection. Once called, 337the application takes full control over the socket, i. e. is responsible to 338release it when done. 339 340.. code-block:: c 341 342 static int ws_socket; 343 static uint8_t ws_recv_buffer[1024]; 344 345 int ws_setup(int sock, void *user_data) 346 { 347 ws_socket = sock; 348 return 0; 349 } 350 351 struct http_resource_detail_websocket ws_resource_detail = { 352 .common = { 353 .type = HTTP_RESOURCE_TYPE_WEBSOCKET, 354 /* We need HTTP/1.1 Get method for upgrading */ 355 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 356 }, 357 .cb = ws_setup, 358 .data_buffer = ws_recv_buffer, 359 .data_buffer_len = sizeof(ws_recv_buffer), 360 .user_data = NULL, /* Fill this for any user specific data */ 361 }; 362 363 HTTP_RESOURCE_DEFINE(ws_resource, my_service, "/", &ws_resource_detail); 364 365The above minimalistic example shows how to register a Websocket resource with 366a simple callback, used only to store the socket descriptor provided. Further 367processing of the Websocket connection is application-specific, hence outside 368of scope of this guide. See :zephyr:code-sample:`sockets-http-server` for an 369example Websocket-based echo service implementation. 370 371Accessing request headers 372========================= 373 374The application can register an interest in any specific HTTP request headers. 375These headers are then stored for each incoming request, and can be accessed 376from within a dynamic resource callback. Request headers are only included in 377the first callback for a given request, and are not passed to any subsequent 378callbacks. 379 380This feature must first be enabled with 381:kconfig:option:`CONFIG_HTTP_SERVER_CAPTURE_HEADERS` Kconfig option. 382 383Then the application can register headers to be captured, and read the values 384from within the dynamic resource callback: 385 386.. code-block:: c 387 388 HTTP_SERVER_REGISTER_HEADER_CAPTURE(capture_user_agent, "User-Agent"); 389 390 static int dyn_handler(struct http_client_ctx *client, enum http_data_status status, 391 uint8_t *buffer, size_t len, void *user_data) 392 { 393 size_t header_count = client->header_capture_ctx.count; 394 const struct http_header *headers = client->header_capture_ctx.headers; 395 396 LOG_INF("Captured %d headers with request", header_count); 397 398 for (uint32_t i = 0; i < header_count; i++) { 399 LOG_INF("Header: '%s: %s'", headers[i].name, headers[i].value); 400 } 401 402 return 0; 403 } 404 405API Reference 406************* 407 408.. doxygengroup:: http_service 409.. doxygengroup:: http_server 410