Line data Source code
1 1 : /*
2 : * Copyright (c) 2023, Emna Rekik
3 : * Copyright (c) 2024 Nordic Semiconductor ASA
4 : *
5 : * SPDX-License-Identifier: Apache-2.0
6 : */
7 :
8 : #ifndef ZEPHYR_INCLUDE_NET_HTTP_SERVER_H_
9 : #define ZEPHYR_INCLUDE_NET_HTTP_SERVER_H_
10 :
11 : /**
12 : * @file server.h
13 : *
14 : * @brief HTTP server API
15 : *
16 : * @defgroup http_server HTTP server API
17 : * @since 3.7
18 : * @version 0.1.0
19 : * @ingroup networking
20 : * @{
21 : */
22 :
23 : #include <stdint.h>
24 :
25 : #include <zephyr/kernel.h>
26 : #include <zephyr/net/http/parser.h>
27 : #include <zephyr/net/http/hpack.h>
28 : #include <zephyr/net/http/status.h>
29 : #include <zephyr/net/socket.h>
30 : #include <zephyr/sys/iterable_sections.h>
31 :
32 : #ifdef __cplusplus
33 : extern "C" {
34 : #endif
35 :
36 : /** @cond INTERNAL_HIDDEN */
37 :
38 : #if defined(CONFIG_HTTP_SERVER)
39 : #define HTTP_SERVER_CLIENT_BUFFER_SIZE CONFIG_HTTP_SERVER_CLIENT_BUFFER_SIZE
40 : #define HTTP_SERVER_MAX_STREAMS CONFIG_HTTP_SERVER_MAX_STREAMS
41 : #define HTTP_SERVER_MAX_CONTENT_TYPE_LEN CONFIG_HTTP_SERVER_MAX_CONTENT_TYPE_LENGTH
42 : #define HTTP_SERVER_MAX_URL_LENGTH CONFIG_HTTP_SERVER_MAX_URL_LENGTH
43 : #define HTTP_SERVER_MAX_HEADER_LEN CONFIG_HTTP_SERVER_MAX_HEADER_LEN
44 : #else
45 : #define HTTP_SERVER_CLIENT_BUFFER_SIZE 0
46 : #define HTTP_SERVER_MAX_STREAMS 0
47 : #define HTTP_SERVER_MAX_CONTENT_TYPE_LEN 0
48 : #define HTTP_SERVER_MAX_URL_LENGTH 0
49 : #define HTTP_SERVER_MAX_HEADER_LEN 0
50 : #endif
51 :
52 : #if defined(CONFIG_HTTP_SERVER_CAPTURE_HEADERS)
53 : #define HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE CONFIG_HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE
54 : #define HTTP_SERVER_CAPTURE_HEADER_COUNT CONFIG_HTTP_SERVER_CAPTURE_HEADER_COUNT
55 : #else
56 : #define HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE 0
57 : #define HTTP_SERVER_CAPTURE_HEADER_COUNT 0
58 : #endif
59 :
60 : #define HTTP2_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
61 :
62 : /** @endcond */
63 :
64 : /**
65 : * @brief HTTP server resource type.
66 : */
67 1 : enum http_resource_type {
68 : /** Static resource, cannot be modified on runtime. */
69 : HTTP_RESOURCE_TYPE_STATIC,
70 :
71 : /** serves static gzipped files from a filesystem */
72 : HTTP_RESOURCE_TYPE_STATIC_FS,
73 :
74 : /** Dynamic resource, server interacts with the application via registered
75 : * @ref http_resource_dynamic_cb_t.
76 : */
77 : HTTP_RESOURCE_TYPE_DYNAMIC,
78 :
79 : /** Websocket resource, application takes control over Websocket connection
80 : * after and upgrade.
81 : */
82 : HTTP_RESOURCE_TYPE_WEBSOCKET,
83 : };
84 :
85 : /**
86 : * @brief Representation of a server resource, common for all resource types.
87 : */
88 1 : struct http_resource_detail {
89 : /** Bitmask of supported HTTP methods (@ref http_method). */
90 1 : uint32_t bitmask_of_supported_http_methods;
91 :
92 : /** Resource type. */
93 1 : enum http_resource_type type;
94 :
95 : /** Length of the URL path. */
96 1 : int path_len;
97 :
98 : /** Content encoding of the resource. */
99 1 : const char *content_encoding;
100 :
101 : /** Content type of the resource. */
102 1 : const char *content_type;
103 : };
104 :
105 : /** @cond INTERNAL_HIDDEN */
106 : BUILD_ASSERT(NUM_BITS(
107 : sizeof(((struct http_resource_detail *)0)->bitmask_of_supported_http_methods))
108 : >= (HTTP_METHOD_END_VALUE - 1));
109 : /** @endcond */
110 :
111 : /**
112 : * @brief Representation of a static server resource.
113 : */
114 1 : struct http_resource_detail_static {
115 : /** Common resource details. */
116 1 : struct http_resource_detail common;
117 :
118 : /** Content of the static resource. */
119 1 : const void *static_data;
120 :
121 : /** Size of the static resource. */
122 1 : size_t static_data_len;
123 : };
124 :
125 : /** @cond INTERNAL_HIDDEN */
126 : /* Make sure that the common is the first in the struct. */
127 : BUILD_ASSERT(offsetof(struct http_resource_detail_static, common) == 0);
128 : /** @endcond */
129 :
130 : /**
131 : * @brief Representation of a static filesystem server resource.
132 : */
133 1 : struct http_resource_detail_static_fs {
134 : /** Common resource details. */
135 1 : struct http_resource_detail common;
136 :
137 : /** Path in the local filesystem */
138 1 : const char *fs_path;
139 : };
140 :
141 : /** @brief HTTP compressions */
142 1 : enum http_compression {
143 : HTTP_NONE = 0, /**< NONE */
144 : HTTP_GZIP = 1, /**< GZIP */
145 : HTTP_COMPRESS = 2, /**< COMPRESS */
146 : HTTP_DEFLATE = 3, /**< DEFLATE */
147 : HTTP_BR = 4, /**< BR */
148 : HTTP_ZSTD = 5 /**< ZSTD */
149 : };
150 :
151 : /** @cond INTERNAL_HIDDEN */
152 : /* Make sure that the common is the first in the struct. */
153 : BUILD_ASSERT(offsetof(struct http_resource_detail_static_fs, common) == 0);
154 : /** @endcond */
155 :
156 0 : struct http_content_type {
157 0 : const char *extension;
158 0 : size_t extension_len;
159 0 : const char *content_type;
160 : };
161 :
162 0 : #define HTTP_SERVER_CONTENT_TYPE(_extension, _content_type) \
163 : const STRUCT_SECTION_ITERABLE(http_content_type, _extension) = { \
164 : .extension = STRINGIFY(_extension), \
165 : .extension_len = sizeof(STRINGIFY(_extension)) - 1, \
166 : .content_type = _content_type, \
167 : };
168 :
169 0 : #define HTTP_SERVER_CONTENT_TYPE_FOREACH(_it) STRUCT_SECTION_FOREACH(http_content_type, _it)
170 :
171 : struct http_client_ctx;
172 :
173 : /** Indicates the status of the currently processed piece of data. */
174 1 : enum http_data_status {
175 : /** Transaction aborted, data incomplete. */
176 : HTTP_SERVER_DATA_ABORTED = -1,
177 : /** Transaction incomplete, more data expected. */
178 : HTTP_SERVER_DATA_MORE = 0,
179 : /** Final data fragment in current transaction. */
180 : HTTP_SERVER_DATA_FINAL = 1,
181 : };
182 :
183 : /** @brief Status of captured request headers */
184 1 : enum http_header_status {
185 : HTTP_HEADER_STATUS_OK, /**< All available headers were successfully captured. */
186 : HTTP_HEADER_STATUS_DROPPED, /**< One or more headers were dropped due to lack of space. */
187 : HTTP_HEADER_STATUS_NONE, /**< No header status is available. */
188 : };
189 :
190 : /** @brief HTTP header representation */
191 1 : struct http_header {
192 1 : const char *name; /**< Pointer to header name NULL-terminated string. */
193 1 : const char *value; /**< Pointer to header value NULL-terminated string. */
194 : };
195 :
196 : /** @brief HTTP request context */
197 1 : struct http_request_ctx {
198 1 : uint8_t *data; /**< HTTP request data */
199 1 : size_t data_len; /**< Length of HTTP request data */
200 1 : struct http_header *headers; /**< Array of HTTP request headers */
201 1 : size_t header_count; /**< Array length of HTTP request headers */
202 1 : enum http_header_status headers_status; /**< Status of HTTP request headers */
203 : };
204 :
205 : /** @brief HTTP response context */
206 1 : struct http_response_ctx {
207 1 : enum http_status status; /**< HTTP status code to include in response */
208 1 : const struct http_header *headers; /**< Array of HTTP headers */
209 1 : size_t header_count; /**< Length of headers array */
210 1 : const uint8_t *body; /**< Pointer to body data */
211 1 : size_t body_len; /**< Length of body data */
212 1 : bool final_chunk; /**< Flag set to true when the application has no more data to send */
213 : };
214 :
215 : /**
216 : * @typedef http_resource_dynamic_cb_t
217 : * @brief Callback used when data is received. Data to be sent to client
218 : * can be specified.
219 : *
220 : * @param client HTTP context information for this client connection.
221 : * @param status HTTP data status, indicate whether more data is expected or not.
222 : * @param request_ctx Request context structure containing HTTP request data that was received.
223 : * @param response_ctx Response context structure for application to populate with response data.
224 : * @param user_data User specified data.
225 : *
226 : * @return 0 success, server can send any response data provided in the response_ctx.
227 : * <0 error, close the connection.
228 : */
229 1 : typedef int (*http_resource_dynamic_cb_t)(struct http_client_ctx *client,
230 : enum http_data_status status,
231 : const struct http_request_ctx *request_ctx,
232 : struct http_response_ctx *response_ctx,
233 : void *user_data);
234 :
235 : /**
236 : * @brief Representation of a dynamic server resource.
237 : */
238 1 : struct http_resource_detail_dynamic {
239 : /** Common resource details. */
240 1 : struct http_resource_detail common;
241 :
242 : /** Resource callback used by the server to interact with the
243 : * application.
244 : */
245 1 : http_resource_dynamic_cb_t cb;
246 :
247 : /** A pointer to the client currently processing resource, used to
248 : * prevent concurrent access to the resource from multiple clients.
249 : */
250 1 : struct http_client_ctx *holder;
251 :
252 : /** A pointer to the user data registered by the application. */
253 1 : void *user_data;
254 : };
255 :
256 : /** @cond INTERNAL_HIDDEN */
257 : BUILD_ASSERT(offsetof(struct http_resource_detail_dynamic, common) == 0);
258 : /** @endcond */
259 :
260 : /**
261 : * @typedef http_resource_websocket_cb_t
262 : * @brief Callback used when a Websocket connection is setup. The application
263 : * will need to handle all functionality related to the connection like
264 : * reading and writing websocket data, and closing the connection.
265 : *
266 : * @param ws_socket A socket for the Websocket data.
267 : * @param request_ctx Request context structure associated with HTTP upgrade request
268 : * @param user_data User specified data.
269 : *
270 : * @return 0 Accepting the connection, HTTP server library will no longer
271 : * handle data to/from the socket and it is application responsibility
272 : * to send and receive data to/from the supplied socket.
273 : * <0 error, close the connection.
274 : */
275 1 : typedef int (*http_resource_websocket_cb_t)(int ws_socket, struct http_request_ctx *request_ctx,
276 : void *user_data);
277 :
278 : /** @brief Representation of a websocket server resource */
279 1 : struct http_resource_detail_websocket {
280 : /** Common resource details. */
281 1 : struct http_resource_detail common;
282 :
283 : /** Websocket socket value */
284 1 : int ws_sock;
285 :
286 : /** Resource callback used by the server to interact with the
287 : * application.
288 : */
289 1 : http_resource_websocket_cb_t cb;
290 :
291 : /** Data buffer used to exchanged data between server and the,
292 : * application.
293 : */
294 1 : uint8_t *data_buffer;
295 :
296 : /** Length of the data in the data buffer. */
297 1 : size_t data_buffer_len;
298 :
299 : /** A pointer to the user data registered by the application. */
300 1 : void *user_data;
301 : };
302 :
303 : /** @cond INTERNAL_HIDDEN */
304 : BUILD_ASSERT(offsetof(struct http_resource_detail_websocket, common) == 0);
305 : /** @endcond */
306 :
307 : /** @cond INTERNAL_HIDDEN */
308 :
309 : enum http2_stream_state {
310 : HTTP2_STREAM_IDLE,
311 : HTTP2_STREAM_RESERVED_LOCAL,
312 : HTTP2_STREAM_RESERVED_REMOTE,
313 : HTTP2_STREAM_OPEN,
314 : HTTP2_STREAM_HALF_CLOSED_LOCAL,
315 : HTTP2_STREAM_HALF_CLOSED_REMOTE,
316 : HTTP2_STREAM_CLOSED
317 : };
318 :
319 : enum http_server_state {
320 : HTTP_SERVER_FRAME_HEADER_STATE,
321 : HTTP_SERVER_PREFACE_STATE,
322 : HTTP_SERVER_REQUEST_STATE,
323 : HTTP_SERVER_FRAME_DATA_STATE,
324 : HTTP_SERVER_FRAME_HEADERS_STATE,
325 : HTTP_SERVER_FRAME_SETTINGS_STATE,
326 : HTTP_SERVER_FRAME_PRIORITY_STATE,
327 : HTTP_SERVER_FRAME_WINDOW_UPDATE_STATE,
328 : HTTP_SERVER_FRAME_CONTINUATION_STATE,
329 : HTTP_SERVER_FRAME_PING_STATE,
330 : HTTP_SERVER_FRAME_RST_STREAM_STATE,
331 : HTTP_SERVER_FRAME_GOAWAY_STATE,
332 : HTTP_SERVER_FRAME_PADDING_STATE,
333 : HTTP_SERVER_DONE_STATE,
334 : };
335 :
336 : enum http1_parser_state {
337 : HTTP1_INIT_HEADER_STATE,
338 : HTTP1_WAITING_HEADER_STATE,
339 : HTTP1_RECEIVING_HEADER_STATE,
340 : HTTP1_RECEIVED_HEADER_STATE,
341 : HTTP1_RECEIVING_DATA_STATE,
342 : HTTP1_MESSAGE_COMPLETE_STATE,
343 : };
344 :
345 : #define HTTP_SERVER_INITIAL_WINDOW_SIZE 65536
346 : #define HTTP_SERVER_WS_MAX_SEC_KEY_LEN 32
347 :
348 : /** @endcond */
349 :
350 : /** @brief HTTP/2 stream representation. */
351 1 : struct http2_stream_ctx {
352 1 : int stream_id; /**< Stream identifier. */
353 1 : enum http2_stream_state stream_state; /**< Stream state. */
354 1 : int window_size; /**< Stream-level window size. */
355 :
356 : /** Currently processed resource detail. */
357 1 : struct http_resource_detail *current_detail;
358 :
359 : /** Flag indicating that headers were sent in the reply. */
360 1 : bool headers_sent : 1;
361 :
362 : /** Flag indicating that END_STREAM flag was sent. */
363 1 : bool end_stream_sent : 1;
364 : };
365 :
366 : /** @brief HTTP/2 frame representation. */
367 1 : struct http2_frame {
368 1 : uint32_t length; /**< Frame payload length. */
369 1 : uint32_t stream_identifier; /**< Stream ID the frame belongs to. */
370 1 : uint8_t type; /**< Frame type. */
371 1 : uint8_t flags; /**< Frame flags. */
372 1 : uint8_t padding_len; /**< Frame padding length. */
373 : };
374 :
375 : /** @cond INTERNAL_HIDDEN */
376 : /** @brief Context for capturing HTTP headers */
377 : struct http_header_capture_ctx {
378 : /** Buffer for HTTP headers captured for application use */
379 : unsigned char buffer[HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE];
380 :
381 : /** Descriptor of each captured HTTP header */
382 : struct http_header headers[HTTP_SERVER_CAPTURE_HEADER_COUNT];
383 :
384 : /** Status of captured headers */
385 : enum http_header_status status;
386 :
387 : /** Number of headers captured */
388 : size_t count;
389 :
390 : /** Current position in buffer */
391 : size_t cursor;
392 :
393 : /** The HTTP2 stream associated with the current headers */
394 : struct http2_stream_ctx *current_stream;
395 :
396 : /** The next HTTP header value should be stored */
397 : bool store_next_value;
398 : };
399 : /** @endcond */
400 :
401 : /** @brief HTTP header name representation */
402 1 : struct http_header_name {
403 1 : const char *name; /**< Pointer to header name NULL-terminated string. */
404 : };
405 :
406 : /**
407 : * @brief Representation of an HTTP client connected to the server.
408 : */
409 1 : struct http_client_ctx {
410 : /** Socket descriptor associated with the server. */
411 1 : int fd;
412 :
413 : /** HTTP service on which the client is connected */
414 1 : const struct http_service_desc *service;
415 :
416 : /** Client data buffer. */
417 1 : unsigned char buffer[HTTP_SERVER_CLIENT_BUFFER_SIZE];
418 :
419 : /** Cursor indicating currently processed byte. */
420 1 : unsigned char *cursor;
421 :
422 : /** Data left to process in the buffer. */
423 1 : size_t data_len;
424 :
425 : /** Connection-level window size. */
426 1 : int window_size;
427 :
428 : /** Server state for the associated client. */
429 1 : enum http_server_state server_state;
430 :
431 : /** Currently processed HTTP/2 frame. */
432 1 : struct http2_frame current_frame;
433 :
434 : /** Currently processed resource detail. */
435 1 : struct http_resource_detail *current_detail;
436 :
437 : /** Currently processed stream. */
438 1 : struct http2_stream_ctx *current_stream;
439 :
440 : /** HTTP/2 header parser context. */
441 1 : struct http_hpack_header_buf header_field;
442 :
443 : /** HTTP/2 streams context. */
444 1 : struct http2_stream_ctx streams[HTTP_SERVER_MAX_STREAMS];
445 :
446 : /** HTTP/1 parser configuration. */
447 1 : struct http_parser_settings parser_settings;
448 :
449 : /** HTTP/1 parser context. */
450 1 : struct http_parser parser;
451 :
452 : /** Header capture context */
453 1 : struct http_header_capture_ctx header_capture_ctx;
454 :
455 : /** Request URL. */
456 1 : unsigned char url_buffer[HTTP_SERVER_MAX_URL_LENGTH];
457 :
458 : /** Request content type. */
459 1 : unsigned char content_type[HTTP_SERVER_MAX_CONTENT_TYPE_LEN];
460 :
461 : /** Temp buffer for currently processed header (HTTP/1 only). */
462 1 : unsigned char header_buffer[HTTP_SERVER_MAX_HEADER_LEN];
463 :
464 : /** Request content length. */
465 1 : size_t content_len;
466 :
467 : /** Request method. */
468 1 : enum http_method method;
469 :
470 : /** HTTP/1 parser state. */
471 1 : enum http1_parser_state parser_state;
472 :
473 : /** Length of the payload length in the currently processed request
474 : * fragment (HTTP/1 only).
475 : */
476 1 : int http1_frag_data_len;
477 :
478 : /** Client inactivity timer. The client connection is closed by the
479 : * server when it expires.
480 : */
481 1 : struct k_work_delayable inactivity_timer;
482 :
483 : /** @cond INTERNAL_HIDDEN */
484 : /** Websocket security key. */
485 : IF_ENABLED(CONFIG_WEBSOCKET, (uint8_t ws_sec_key[HTTP_SERVER_WS_MAX_SEC_KEY_LEN]));
486 : /** @endcond */
487 :
488 : /** @cond INTERNAL_HIDDEN */
489 : /** Client supported compression. */
490 : IF_ENABLED(CONFIG_HTTP_SERVER_COMPRESSION, (uint8_t supported_compression));
491 : /** @endcond */
492 :
493 : /** Flag indicating that HTTP2 preface was sent. */
494 1 : bool preface_sent : 1;
495 :
496 : /** Flag indicating that HTTP1 headers were sent. */
497 1 : bool http1_headers_sent : 1;
498 :
499 : /** Flag indicating that upgrade header was present in the request. */
500 1 : bool has_upgrade_header : 1;
501 :
502 : /** Flag indicating HTTP/2 upgrade takes place. */
503 1 : bool http2_upgrade : 1;
504 :
505 : /** Flag indicating Websocket upgrade takes place. */
506 1 : bool websocket_upgrade : 1;
507 :
508 : /** Flag indicating Websocket key is being processed. */
509 1 : bool websocket_sec_key_next : 1;
510 :
511 : /** Flag indicating accept encoding is being processed. */
512 1 : IF_ENABLED(CONFIG_HTTP_SERVER_COMPRESSION, (bool accept_encoding_next: 1));
513 :
514 : /** The next frame on the stream is expectd to be a continuation frame. */
515 1 : bool expect_continuation : 1;
516 : };
517 :
518 : /**
519 : * @brief Register an HTTP request header to be captured by the server
520 : *
521 : * @param _id variable name for the header capture instance
522 : * @param _header header to be captured, as literal string
523 : */
524 1 : #define HTTP_SERVER_REGISTER_HEADER_CAPTURE(_id, _header) \
525 : BUILD_ASSERT(sizeof(_header) <= CONFIG_HTTP_SERVER_MAX_HEADER_LEN, \
526 : "Header is too long to be captured, try increasing " \
527 : "CONFIG_HTTP_SERVER_MAX_HEADER_LEN"); \
528 : static const char *const _id##_str = _header; \
529 : static const STRUCT_SECTION_ITERABLE(http_header_name, _id) = { \
530 : .name = _id##_str, \
531 : }
532 :
533 : /** @brief Start the HTTP2 server.
534 : *
535 : * The server runs in a background thread. Once started, the server will create
536 : * a server socket for all HTTP services registered in the system and accept
537 : * connections from clients (see @ref HTTP_SERVICE_DEFINE).
538 : */
539 1 : int http_server_start(void);
540 :
541 : /** @brief Stop the HTTP2 server.
542 : *
543 : * All server sockets are closed and the server thread is suspended.
544 : */
545 1 : int http_server_stop(void);
546 :
547 : #ifdef __cplusplus
548 : }
549 : #endif
550 :
551 : /**
552 : * @}
553 : */
554 :
555 : #endif
|