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* Static file system resources - the path at which the filesystem is mounted, 33 and the URL at which the filesystem is made available are fixed at build time, 34 but the content within the filesystem can be changed dynamically. This means that 35 the files can be created, modified or deleted by some other code outside the HTTP 36 server (:c:enumerator:`HTTP_RESOURCE_TYPE_STATIC_FS`). 37 38* Dynamic resources - content provided at runtime by respective application 39 callback (:c:enumerator:`HTTP_RESOURCE_TYPE_DYNAMIC`). 40 41* Websocket resources - allowing to establish Websocket connections with the 42 server (:c:enumerator:`HTTP_RESOURCE_TYPE_WEBSOCKET`). 43 44Zephyr provides a sample demonstrating HTTP(s) server operation and various 45resource types usage. See :zephyr:code-sample:`sockets-http-server` for more 46information. 47 48Server Setup 49************ 50 51A few prerequisites are needed in order to enable HTTP server functionality in 52the application. 53 54First of all, the HTTP server has to be enabled in applications configuration file 55with :kconfig:option:`CONFIG_HTTP_SERVER` Kconfig option: 56 57.. code-block:: cfg 58 :caption: ``prj.conf`` 59 60 CONFIG_HTTP_SERVER=y 61 62All HTTP services and HTTP resources are placed in a dedicated linker section. 63The linker section for services is predefined locally, however the application 64is responsible for defining linker sections for resources associated with 65respective services. Linker section names for resources should be prefixed with 66``http_resource_desc_``, appended with the service name. 67 68Linker sections for resources should be defined in a linker file. For example, 69for a service named ``my_service``, the linker section shall be defined as follows: 70 71.. code-block:: c 72 :caption: ``sections-rom.ld`` 73 74 #include <zephyr/linker/iterable_sections.h> 75 76 ITERABLE_SECTION_ROM(http_resource_desc_my_service, Z_LINK_ITERABLE_SUBALIGN) 77 78Finally, the linker file and linker section have to be added to your application 79using CMake: 80 81.. code-block:: cmake 82 :caption: ``CMakeLists.txt`` 83 84 zephyr_linker_sources(SECTIONS sections-rom.ld) 85 zephyr_linker_section(NAME http_resource_desc_my_service 86 KVMA RAM_REGION GROUP RODATA_REGION) 87 88.. note:: 89 90 You need to define a separate linker section for each HTTP service 91 registered in the system. 92 93Sample Usage 94************ 95 96Services 97======== 98 99The application needs to define an HTTP service (or multiple services), with 100the same name as used for the linker section with :c:macro:`HTTP_SERVICE_DEFINE` 101macro: 102 103.. code-block:: c 104 105 #include <zephyr/net/http/service.h> 106 107 static uint16_t http_service_port = 80; 108 109 HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL, NULL, NULL); 110 111Alternatively, an HTTPS service can be defined with 112:c:macro:`HTTPS_SERVICE_DEFINE`: 113 114.. code-block:: c 115 116 #include <zephyr/net/http/service.h> 117 #include <zephyr/net/tls_credentials.h> 118 119 #define HTTP_SERVER_CERTIFICATE_TAG 1 120 121 static uint16_t https_service_port = 443; 122 static const sec_tag_t sec_tag_list[] = { 123 HTTP_SERVER_CERTIFICATE_TAG, 124 }; 125 126 HTTPS_SERVICE_DEFINE(my_service, "0.0.0.0", &https_service_port, 1, 10, 127 NULL, NULL, NULL, sec_tag_list, sizeof(sec_tag_list)); 128 129Per-service configuration 130========================= 131 132HTTP services support individual service configuration, 133for now it includes only socket creation through 134the ``http_service_config`` structure. This allows applications to customize 135socket creation behavior, for example to set specific socket options or use 136custom socket types. 137 138To use custom socket creation: 139 140.. code-block:: c 141 142 static int my_socket_create(const struct http_service_desc *svc, int af, int proto) 143 { 144 int fd; 145 146 /* Create socket with custom parameters */ 147 fd = zsock_socket(af, SOCK_STREAM, proto); 148 if (fd < 0) { 149 return fd; 150 } 151 152 /* Set custom socket options */ 153 /* Add any other custom socket configuration */ 154 155 return fd; 156 } 157 158 static const struct http_service_config my_service_config = { 159 .socket_create = my_socket_create, 160 }; 161 162 static uint16_t http_service_port = 80; 163 164 HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, 165 NULL, NULL, &my_service_config); 166 167The custom socket creation function receives: 168- ``svc``: Pointer to the service descriptor 169- ``af``: Address family (AF_INET or AF_INET6) 170- ``proto``: Protocol (IPPROTO_TCP or IPPROTO_TLS_1_2 for HTTPS) 171 172The function should return the socket file descriptor on success, or a negative error code on failure. 173 174If no custom configuration is needed, simply pass ``NULL`` for the config parameter: 175 176.. code-block:: c 177 178 HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, 179 NULL, NULL, NULL); 180 181Fallback Resources 182================== 183 184The ``_res_fallback`` parameter can be used when defining an HTTP/HTTPS service to 185specify a fallback resource which will be used if no other resource matches the 186URL. This can be used for example to serve an index page for all unknown paths 187(useful for a single-page app which handles routing in the frontend), or for a 188customised 404 response. 189 190.. code-block:: c 191 192 static int default_handler(struct http_client_ctx *client, enum http_data_status status, 193 const struct http_request_ctx *request_ctx, 194 struct http_response_ctx *response_ctx, void *user_data) 195 { 196 static const char response_404[] = "Oops, page not found!"; 197 198 if (status == HTTP_SERVER_DATA_FINAL) { 199 response_ctx->status = 404; 200 response_ctx->body = response_404; 201 response_ctx->body_len = sizeof(response_404) - 1; 202 response_ctx->final_chunk = true; 203 } 204 205 return 0; 206 } 207 208 static struct http_resource_detail_dynamic default_detail = { 209 .common = { 210 .type = HTTP_RESOURCE_TYPE_DYNAMIC, 211 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 212 }, 213 .cb = default_handler, 214 .user_data = NULL, 215 }; 216 217 /* Register a fallback resource to handle any unknown path */ 218 HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL, &default_detail, NULL); 219 220.. note:: 221 222 HTTPS services rely on TLS credentials being registered in the system. 223 See :ref:`sockets_tls_credentials_subsys` for information on how to 224 configure TLS credentials in the system. 225 226Once HTTP(s) service is defined, resources can be registered for it with 227:c:macro:`HTTP_RESOURCE_DEFINE` macro. 228 229Application can enable resource wildcard support by enabling 230:kconfig:option:`CONFIG_HTTP_SERVER_RESOURCE_WILDCARD` option. When this 231option is set, then it is possible to match several incoming HTTP requests 232with just one resource handler. The `fnmatch() 233<https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html>`__ 234POSIX API function is used to match the pattern in the URL paths. 235 236Example: 237 238.. code-block:: c 239 240 HTTP_RESOURCE_DEFINE(my_resource, my_service, "/foo*", &resource_detail); 241 242This would match all URLs that start with a string ``foo``. See 243`POSIX.2 chapter 2.13 244<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13>`__ 245for pattern matching syntax description. 246 247Static resources 248================ 249 250Static resource content is defined build-time and is immutable. The following 251example shows how gzip compressed webpage can be defined as a static resource 252in the application: 253 254.. code-block:: c 255 256 static const uint8_t index_html_gz[] = { 257 #include "index.html.gz.inc" 258 }; 259 260 struct http_resource_detail_static index_html_gz_resource_detail = { 261 .common = { 262 .type = HTTP_RESOURCE_TYPE_STATIC, 263 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 264 .content_encoding = "gzip", 265 }, 266 .static_data = index_html_gz, 267 .static_data_len = sizeof(index_html_gz), 268 }; 269 270 HTTP_RESOURCE_DEFINE(index_html_gz_resource, my_service, "/", 271 &index_html_gz_resource_detail); 272 273The resource content and content encoding is application specific. For the above 274example, a gzip compressed webpage can be generated during build, by adding the 275following code to the application's ``CMakeLists.txt`` file: 276 277.. code-block:: cmake 278 :caption: ``CMakeLists.txt`` 279 280 set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) 281 set(source_file_index src/index.html) 282 generate_inc_file_for_target(app ${source_file_index} ${gen_dir}/index.html.gz.inc --gzip) 283 284where ``src/index.html`` is the location of the webpage to be compressed. 285 286Static filesystem resources 287=========================== 288 289Static filesystem resource content is defined build-time and is immutable. Note that only 290``GET`` operation is supported, user is not able to upload files to the filesystem. The following 291example shows how the path can be defined as a static resource in the application: 292 293.. code-block:: c 294 295 struct http_resource_detail_static_fs static_fs_resource_detail = { 296 .common = { 297 .type = HTTP_RESOURCE_TYPE_STATIC_FS, 298 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 299 }, 300 .fs_path = "/lfs1/www", 301 }; 302 303 HTTP_RESOURCE_DEFINE(static_fs_resource, my_service, "*", &static_fs_resource_detail); 304 305All files located in /lfs1/www are made available to the client. If a file is 306gzipped, .gz must be appended to the file name (e.g. index.html.gz), then the 307server delivers index.html.gz when the client requests index.html and adds gzip 308content-encoding to the HTTP header. 309 310The content type is evaluated based on the file extension. The server supports 311.html, .js, .css, .jpg, .png and .svg. More content types can be provided with the 312:c:macro:`HTTP_SERVER_CONTENT_TYPE` macro. All other files are provided with the 313content type text/html. 314 315.. code-block:: c 316 317 HTTP_SERVER_CONTENT_TYPE(json, "application/json") 318 319When serving files from a static filesystem, the response chunk size can be configured 320using the :kconfig:option:`CONFIG_HTTP_SERVER_STATIC_FS_RESPONSE_SIZE` Kconfig option. 321This determines the size of individual chunks when transmitting file content to clients. 322 323Dynamic resources 324================= 325 326For dynamic resource, a resource callback is registered to exchange data between 327the server and the application. 328 329The following example code shows how to register a dynamic resource with a simple 330resource handler, which echoes received data back to the client: 331 332.. code-block:: c 333 334 static int dyn_handler(struct http_client_ctx *client, enum http_data_status status, 335 const struct http_request_ctx *request_ctx, 336 struct http_response_ctx *response_ctx, void *user_data) 337 { 338 #define MAX_TEMP_PRINT_LEN 32 339 static char print_str[MAX_TEMP_PRINT_LEN]; 340 enum http_method method = client->method; 341 static size_t processed; 342 343 __ASSERT_NO_MSG(buffer != NULL); 344 345 if (status == HTTP_SERVER_DATA_ABORTED) { 346 LOG_DBG("Transaction aborted after %zd bytes.", processed); 347 processed = 0; 348 return 0; 349 } 350 351 processed += request_ctx->data_len; 352 353 snprintf(print_str, sizeof(print_str), "%s received (%zd bytes)", 354 http_method_str(method), request_ctx->data_len); 355 LOG_HEXDUMP_DBG(request_ctx->data, request_ctx->data_len, print_str); 356 357 if (status == HTTP_SERVER_DATA_FINAL) { 358 LOG_DBG("All data received (%zd bytes).", processed); 359 processed = 0; 360 } 361 362 /* Echo data back to client */ 363 response_ctx->body = request_ctx->data; 364 response_ctx->body_len = request_ctx->data_len; 365 response_ctx->final_chunk = (status == HTTP_SERVER_DATA_FINAL); 366 367 return 0; 368 } 369 370 struct http_resource_detail_dynamic dyn_resource_detail = { 371 .common = { 372 .type = HTTP_RESOURCE_TYPE_DYNAMIC, 373 .bitmask_of_supported_http_methods = 374 BIT(HTTP_GET) | BIT(HTTP_POST), 375 }, 376 .cb = dyn_handler, 377 .user_data = NULL, 378 }; 379 380 HTTP_RESOURCE_DEFINE(dyn_resource, my_service, "/dynamic", 381 &dyn_resource_detail); 382 383 384The resource callback may be called multiple times for a single request, hence 385the application should be able to keep track of the received data progress. 386 387The ``status`` field informs the application about the progress in passing 388request payload from the server to the application. As long as the status 389reports :c:enumerator:`HTTP_SERVER_DATA_MORE`, the application should expect 390more data to be provided in a consecutive callback calls. 391Once all request payload has been passed to the application, the server reports 392:c:enumerator:`HTTP_SERVER_DATA_FINAL` status. In case of communication errors 393during request processing (for example client closed the connection before 394complete payload has been received), the server reports 395:c:enumerator:`HTTP_SERVER_DATA_ABORTED`. Either of the two events indicate that 396the application shall reset any progress recorded for the resource, and await 397a new request to come. The server guarantees that the resource can only be 398accessed by single client at a time. 399 400The ``request_ctx`` parameter is used to pass request data to the application: 401 402* The ``data`` and ``data_len`` fields pass request data to the application. 403 404* The ``headers``, ``header_count`` and ``headers_status`` fields pass request 405 headers to the application, if 406 :kconfig:option:`CONFIG_HTTP_SERVER_CAPTURE_HEADERS` is enabled. 407 408The ``response_ctx`` field is used by the application to pass response data to 409the HTTP server: 410 411* The ``status`` field allows the application to send an HTTP response code. If 412 not populated, the response code will be 200 by default. 413 414* The ``headers`` and ``header_count`` fields can be used for the application to 415 send any arbitrary HTTP headers. If not populated, only Transfer-Encoding and 416 Content-Type are sent by default. The callback may override the Content-Type 417 if desired. 418 419* The ``body`` and ``body_len`` fields are used to send body data. 420 421* The ``final_chunk`` field is used to indicate that the application has no more 422 response data to send. 423 424Headers and/or response codes may only be sent in the first populated 425``response_ctx``, after which only further body data is allowed in subsequent 426callbacks. 427 428The server will call the resource callback until it provided all request data 429to the application, and the application reports there is no more data to include 430in the reply. 431 432Websocket resources 433=================== 434 435Websocket resources register an application callback, which is called when a 436Websocket connection upgrade takes place. The callback is provided with a socket 437descriptor corresponding to the underlying TCP/TLS connection. Once called, 438the application takes full control over the socket, i. e. is responsible to 439release it when done. 440 441.. code-block:: c 442 443 static int ws_socket; 444 static uint8_t ws_recv_buffer[1024]; 445 446 int ws_setup(int sock, struct http_request_ctx *request_ctx, void *user_data) 447 { 448 ws_socket = sock; 449 return 0; 450 } 451 452 struct http_resource_detail_websocket ws_resource_detail = { 453 .common = { 454 .type = HTTP_RESOURCE_TYPE_WEBSOCKET, 455 /* We need HTTP/1.1 Get method for upgrading */ 456 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 457 }, 458 .cb = ws_setup, 459 .data_buffer = ws_recv_buffer, 460 .data_buffer_len = sizeof(ws_recv_buffer), 461 .user_data = NULL, /* Fill this for any user specific data */ 462 }; 463 464 HTTP_RESOURCE_DEFINE(ws_resource, my_service, "/", &ws_resource_detail); 465 466The above minimalistic example shows how to register a Websocket resource with 467a simple callback, used only to store the socket descriptor provided. Further 468processing of the Websocket connection is application-specific, hence outside 469of scope of this guide. See :zephyr:code-sample:`sockets-http-server` for an 470example Websocket-based echo service implementation. 471 472Accessing request headers 473========================= 474 475The application can register an interest in any specific HTTP request headers. 476These headers are then stored for each incoming request, and can be accessed 477from within a dynamic resource callback. Request headers are only included in 478the first callback for a given request, and are not passed to any subsequent 479callbacks. 480 481This feature must first be enabled with 482:kconfig:option:`CONFIG_HTTP_SERVER_CAPTURE_HEADERS` Kconfig option. 483 484Then the application can register headers to be captured, and read the values 485from within the dynamic resource callback: 486 487.. code-block:: c 488 489 HTTP_SERVER_REGISTER_HEADER_CAPTURE(capture_user_agent, "User-Agent"); 490 491 static int dyn_handler(struct http_client_ctx *client, enum http_data_status status, 492 uint8_t *buffer, size_t len, void *user_data) 493 { 494 size_t header_count = client->header_capture_ctx.count; 495 const struct http_header *headers = client->header_capture_ctx.headers; 496 497 LOG_INF("Captured %d headers with request", header_count); 498 499 for (uint32_t i = 0; i < header_count; i++) { 500 LOG_INF("Header: '%s: %s'", headers[i].name, headers[i].value); 501 } 502 503 return 0; 504 } 505 506API Reference 507************* 508 509.. doxygengroup:: http_service 510.. doxygengroup:: http_server 511