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 SUBALIGN ${CONFIG_LINKER_ITERABLE_SUBALIGN}) 88 89.. note:: 90 91 You need to define a separate linker section for each HTTP service 92 registered in the system. 93 94Sample Usage 95************ 96 97Services 98======== 99 100The application needs to define an HTTP service (or multiple services), with 101the same name as used for the linker section with :c:macro:`HTTP_SERVICE_DEFINE` 102macro: 103 104.. code-block:: c 105 106 #include <zephyr/net/http/service.h> 107 108 static uint16_t http_service_port = 80; 109 110 HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL, NULL); 111 112Alternatively, an HTTPS service can be defined with 113:c:macro:`HTTPS_SERVICE_DEFINE`: 114 115.. code-block:: c 116 117 #include <zephyr/net/http/service.h> 118 #include <zephyr/net/tls_credentials.h> 119 120 #define HTTP_SERVER_CERTIFICATE_TAG 1 121 122 static uint16_t https_service_port = 443; 123 static const sec_tag_t sec_tag_list[] = { 124 HTTP_SERVER_CERTIFICATE_TAG, 125 }; 126 127 HTTPS_SERVICE_DEFINE(my_service, "0.0.0.0", &https_service_port, 1, 10, 128 NULL, NULL, sec_tag_list, sizeof(sec_tag_list)); 129 130The ``_res_fallback`` parameter can be used when defining an HTTP/HTTPS service to 131specify a fallback resource which will be used if no other resource matches the 132URL. This can be used for example to serve an index page for all unknown paths 133(useful for a single-page app which handles routing in the frontend), or for a 134customised 404 response. 135 136.. code-block:: c 137 138 static int default_handler(struct http_client_ctx *client, enum http_data_status status, 139 const struct http_request_ctx *request_ctx, 140 struct http_response_ctx *response_ctx, void *user_data) 141 { 142 static const char response_404[] = "Oops, page not found!"; 143 144 if (status == HTTP_SERVER_DATA_FINAL) { 145 response_ctx->status = 404; 146 response_ctx->body = response_404; 147 response_ctx->body_len = sizeof(response_404) - 1; 148 response_ctx->final_chunk = true; 149 } 150 151 return 0; 152 } 153 154 static struct http_resource_detail_dynamic default_detail = { 155 .common = { 156 .type = HTTP_RESOURCE_TYPE_DYNAMIC, 157 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 158 }, 159 .cb = default_handler, 160 .user_data = NULL, 161 }; 162 163 /* Register a fallback resource to handle any unknown path */ 164 HTTP_SERVICE_DEFINE(my_service, "0.0.0.0", &http_service_port, 1, 10, NULL, &default_detail); 165 166.. note:: 167 168 HTTPS services rely on TLS credentials being registered in the system. 169 See :ref:`sockets_tls_credentials_subsys` for information on how to 170 configure TLS credentials in the system. 171 172Once HTTP(s) service is defined, resources can be registered for it with 173:c:macro:`HTTP_RESOURCE_DEFINE` macro. 174 175Application can enable resource wildcard support by enabling 176:kconfig:option:`CONFIG_HTTP_SERVER_RESOURCE_WILDCARD` option. When this 177option is set, then it is possible to match several incoming HTTP requests 178with just one resource handler. The `fnmatch() 179<https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html>`__ 180POSIX API function is used to match the pattern in the URL paths. 181 182Example: 183 184.. code-block:: c 185 186 HTTP_RESOURCE_DEFINE(my_resource, my_service, "/foo*", &resource_detail); 187 188This would match all URLs that start with a string ``foo``. See 189`POSIX.2 chapter 2.13 190<https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13>`__ 191for pattern matching syntax description. 192 193Static resources 194================ 195 196Static resource content is defined build-time and is immutable. The following 197example shows how gzip compressed webpage can be defined as a static resource 198in the application: 199 200.. code-block:: c 201 202 static const uint8_t index_html_gz[] = { 203 #include "index.html.gz.inc" 204 }; 205 206 struct http_resource_detail_static index_html_gz_resource_detail = { 207 .common = { 208 .type = HTTP_RESOURCE_TYPE_STATIC, 209 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 210 .content_encoding = "gzip", 211 }, 212 .static_data = index_html_gz, 213 .static_data_len = sizeof(index_html_gz), 214 }; 215 216 HTTP_RESOURCE_DEFINE(index_html_gz_resource, my_service, "/", 217 &index_html_gz_resource_detail); 218 219The resource content and content encoding is application specific. For the above 220example, a gzip compressed webpage can be generated during build, by adding the 221following code to the application's ``CMakeLists.txt`` file: 222 223.. code-block:: cmake 224 :caption: ``CMakeLists.txt`` 225 226 set(gen_dir ${ZEPHYR_BINARY_DIR}/include/generated/) 227 set(source_file_index src/index.html) 228 generate_inc_file_for_target(app ${source_file_index} ${gen_dir}/index.html.gz.inc --gzip) 229 230where ``src/index.html`` is the location of the webpage to be compressed. 231 232Static filesystem resources 233=========================== 234 235Static filesystem resource content is defined build-time and is immutable. Note that only 236``GET`` operation is supported, user is not able to upload files to the filesystem. The following 237example shows how the path can be defined as a static resource in the application: 238 239.. code-block:: c 240 241 struct http_resource_detail_static_fs static_fs_resource_detail = { 242 .common = { 243 .type = HTTP_RESOURCE_TYPE_STATIC_FS, 244 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 245 }, 246 .fs_path = "/lfs1/www", 247 }; 248 249 HTTP_RESOURCE_DEFINE(static_fs_resource, my_service, "*", &static_fs_resource_detail); 250 251All files located in /lfs1/www are made available to the client. If a file is 252gzipped, .gz must be appended to the file name (e.g. index.html.gz), then the 253server delivers index.html.gz when the client requests index.html and adds gzip 254content-encoding to the HTTP header. 255 256The content type is evaluated based on the file extension. The server supports 257.html, .js, .css, .jpg, .png and .svg. More content types can be provided with the 258:c:macro:`HTTP_SERVER_CONTENT_TYPE` macro. All other files are provided with the 259content type text/html. 260 261.. code-block:: c 262 263 HTTP_SERVER_CONTENT_TYPE(json, "application/json") 264 265Dynamic resources 266================= 267 268For dynamic resource, a resource callback is registered to exchange data between 269the server and the application. 270 271The following example code shows how to register a dynamic resource with a simple 272resource handler, which echoes received data back to the client: 273 274.. code-block:: c 275 276 static int dyn_handler(struct http_client_ctx *client, enum http_data_status status, 277 const struct http_request_ctx *request_ctx, 278 struct http_response_ctx *response_ctx, void *user_data) 279 { 280 #define MAX_TEMP_PRINT_LEN 32 281 static char print_str[MAX_TEMP_PRINT_LEN]; 282 enum http_method method = client->method; 283 static size_t processed; 284 285 __ASSERT_NO_MSG(buffer != NULL); 286 287 if (status == HTTP_SERVER_DATA_ABORTED) { 288 LOG_DBG("Transaction aborted after %zd bytes.", processed); 289 processed = 0; 290 return 0; 291 } 292 293 processed += request_ctx->data_len; 294 295 snprintf(print_str, sizeof(print_str), "%s received (%zd bytes)", 296 http_method_str(method), request_ctx->data_len); 297 LOG_HEXDUMP_DBG(request_ctx->data, request_ctx->data_len, print_str); 298 299 if (status == HTTP_SERVER_DATA_FINAL) { 300 LOG_DBG("All data received (%zd bytes).", processed); 301 processed = 0; 302 } 303 304 /* Echo data back to client */ 305 response_ctx->body = request_ctx->data; 306 response_ctx->body_len = request_ctx->data_len; 307 response_ctx->final_chunk = (status == HTTP_SERVER_DATA_FINAL); 308 309 return 0; 310 } 311 312 struct http_resource_detail_dynamic dyn_resource_detail = { 313 .common = { 314 .type = HTTP_RESOURCE_TYPE_DYNAMIC, 315 .bitmask_of_supported_http_methods = 316 BIT(HTTP_GET) | BIT(HTTP_POST), 317 }, 318 .cb = dyn_handler, 319 .user_data = NULL, 320 }; 321 322 HTTP_RESOURCE_DEFINE(dyn_resource, my_service, "/dynamic", 323 &dyn_resource_detail); 324 325 326The resource callback may be called multiple times for a single request, hence 327the application should be able to keep track of the received data progress. 328 329The ``status`` field informs the application about the progress in passing 330request payload from the server to the application. As long as the status 331reports :c:enumerator:`HTTP_SERVER_DATA_MORE`, the application should expect 332more data to be provided in a consecutive callback calls. 333Once all request payload has been passed to the application, the server reports 334:c:enumerator:`HTTP_SERVER_DATA_FINAL` status. In case of communication errors 335during request processing (for example client closed the connection before 336complete payload has been received), the server reports 337:c:enumerator:`HTTP_SERVER_DATA_ABORTED`. Either of the two events indicate that 338the application shall reset any progress recorded for the resource, and await 339a new request to come. The server guarantees that the resource can only be 340accessed by single client at a time. 341 342The ``request_ctx`` parameter is used to pass request data to the application: 343 344* The ``data`` and ``data_len`` fields pass request data to the application. 345 346* The ``headers``, ``header_count`` and ``headers_status`` fields pass request 347 headers to the application, if 348 :kconfig:option:`CONFIG_HTTP_SERVER_CAPTURE_HEADERS` is enabled. 349 350The ``response_ctx`` field is used by the application to pass response data to 351the HTTP server: 352 353* The ``status`` field allows the application to send an HTTP response code. If 354 not populated, the response code will be 200 by default. 355 356* The ``headers`` and ``header_count`` fields can be used for the application to 357 send any arbitrary HTTP headers. If not populated, only Transfer-Encoding and 358 Content-Type are sent by default. The callback may override the Content-Type 359 if desired. 360 361* The ``body`` and ``body_len`` fields are used to send body data. 362 363* The ``final_chunk`` field is used to indicate that the application has no more 364 response data to send. 365 366Headers and/or response codes may only be sent in the first populated 367``response_ctx``, after which only further body data is allowed in subsequent 368callbacks. 369 370The server will call the resource callback until it provided all request data 371to the application, and the application reports there is no more data to include 372in the reply. 373 374Websocket resources 375=================== 376 377Websocket resources register an application callback, which is called when a 378Websocket connection upgrade takes place. The callback is provided with a socket 379descriptor corresponding to the underlying TCP/TLS connection. Once called, 380the application takes full control over the socket, i. e. is responsible to 381release it when done. 382 383.. code-block:: c 384 385 static int ws_socket; 386 static uint8_t ws_recv_buffer[1024]; 387 388 int ws_setup(int sock, struct http_request_ctx *request_ctx, void *user_data) 389 { 390 ws_socket = sock; 391 return 0; 392 } 393 394 struct http_resource_detail_websocket ws_resource_detail = { 395 .common = { 396 .type = HTTP_RESOURCE_TYPE_WEBSOCKET, 397 /* We need HTTP/1.1 Get method for upgrading */ 398 .bitmask_of_supported_http_methods = BIT(HTTP_GET), 399 }, 400 .cb = ws_setup, 401 .data_buffer = ws_recv_buffer, 402 .data_buffer_len = sizeof(ws_recv_buffer), 403 .user_data = NULL, /* Fill this for any user specific data */ 404 }; 405 406 HTTP_RESOURCE_DEFINE(ws_resource, my_service, "/", &ws_resource_detail); 407 408The above minimalistic example shows how to register a Websocket resource with 409a simple callback, used only to store the socket descriptor provided. Further 410processing of the Websocket connection is application-specific, hence outside 411of scope of this guide. See :zephyr:code-sample:`sockets-http-server` for an 412example Websocket-based echo service implementation. 413 414Accessing request headers 415========================= 416 417The application can register an interest in any specific HTTP request headers. 418These headers are then stored for each incoming request, and can be accessed 419from within a dynamic resource callback. Request headers are only included in 420the first callback for a given request, and are not passed to any subsequent 421callbacks. 422 423This feature must first be enabled with 424:kconfig:option:`CONFIG_HTTP_SERVER_CAPTURE_HEADERS` Kconfig option. 425 426Then the application can register headers to be captured, and read the values 427from within the dynamic resource callback: 428 429.. code-block:: c 430 431 HTTP_SERVER_REGISTER_HEADER_CAPTURE(capture_user_agent, "User-Agent"); 432 433 static int dyn_handler(struct http_client_ctx *client, enum http_data_status status, 434 uint8_t *buffer, size_t len, void *user_data) 435 { 436 size_t header_count = client->header_capture_ctx.count; 437 const struct http_header *headers = client->header_capture_ctx.headers; 438 439 LOG_INF("Captured %d headers with request", header_count); 440 441 for (uint32_t i = 0; i < header_count; i++) { 442 LOG_INF("Header: '%s: %s'", headers[i].name, headers[i].value); 443 } 444 445 return 0; 446 } 447 448API Reference 449************* 450 451.. doxygengroup:: http_service 452.. doxygengroup:: http_server 453