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 : /** @cond INTERNAL_HIDDEN */
142 : /* Make sure that the common is the first in the struct. */
143 : BUILD_ASSERT(offsetof(struct http_resource_detail_static_fs, common) == 0);
144 : /** @endcond */
145 :
146 0 : struct http_content_type {
147 0 : const char *extension;
148 0 : size_t extension_len;
149 0 : const char *content_type;
150 : };
151 :
152 0 : #define HTTP_SERVER_CONTENT_TYPE(_extension, _content_type) \
153 : const STRUCT_SECTION_ITERABLE(http_content_type, _extension) = { \
154 : .extension = STRINGIFY(_extension), \
155 : .extension_len = sizeof(STRINGIFY(_extension)) - 1, \
156 : .content_type = _content_type, \
157 : };
158 :
159 0 : #define HTTP_SERVER_CONTENT_TYPE_FOREACH(_it) STRUCT_SECTION_FOREACH(http_content_type, _it)
160 :
161 : struct http_client_ctx;
162 :
163 : /** Indicates the status of the currently processed piece of data. */
164 1 : enum http_data_status {
165 : /** Transaction aborted, data incomplete. */
166 : HTTP_SERVER_DATA_ABORTED = -1,
167 : /** Transaction incomplete, more data expected. */
168 : HTTP_SERVER_DATA_MORE = 0,
169 : /** Final data fragment in current transaction. */
170 : HTTP_SERVER_DATA_FINAL = 1,
171 : };
172 :
173 : /** @brief Status of captured request headers */
174 1 : enum http_header_status {
175 : HTTP_HEADER_STATUS_OK, /**< All available headers were successfully captured. */
176 : HTTP_HEADER_STATUS_DROPPED, /**< One or more headers were dropped due to lack of space. */
177 : HTTP_HEADER_STATUS_NONE, /**< No header status is available. */
178 : };
179 :
180 : /** @brief HTTP header representation */
181 1 : struct http_header {
182 1 : const char *name; /**< Pointer to header name NULL-terminated string. */
183 1 : const char *value; /**< Pointer to header value NULL-terminated string. */
184 : };
185 :
186 : /** @brief HTTP request context */
187 1 : struct http_request_ctx {
188 1 : uint8_t *data; /**< HTTP request data */
189 1 : size_t data_len; /**< Length of HTTP request data */
190 1 : struct http_header *headers; /**< Array of HTTP request headers */
191 1 : size_t header_count; /**< Array length of HTTP request headers */
192 1 : enum http_header_status headers_status; /**< Status of HTTP request headers */
193 : };
194 :
195 : /** @brief HTTP response context */
196 1 : struct http_response_ctx {
197 1 : enum http_status status; /**< HTTP status code to include in response */
198 1 : const struct http_header *headers; /**< Array of HTTP headers */
199 1 : size_t header_count; /**< Length of headers array */
200 1 : const uint8_t *body; /**< Pointer to body data */
201 1 : size_t body_len; /**< Length of body data */
202 1 : bool final_chunk; /**< Flag set to true when the application has no more data to send */
203 : };
204 :
205 : /**
206 : * @typedef http_resource_dynamic_cb_t
207 : * @brief Callback used when data is received. Data to be sent to client
208 : * can be specified.
209 : *
210 : * @param client HTTP context information for this client connection.
211 : * @param status HTTP data status, indicate whether more data is expected or not.
212 : * @param request_ctx Request context structure containing HTTP request data that was received.
213 : * @param response_ctx Response context structure for application to populate with response data.
214 : * @param user_data User specified data.
215 : *
216 : * @return 0 success, server can send any response data provided in the response_ctx.
217 : * <0 error, close the connection.
218 : */
219 1 : typedef int (*http_resource_dynamic_cb_t)(struct http_client_ctx *client,
220 : enum http_data_status status,
221 : const struct http_request_ctx *request_ctx,
222 : struct http_response_ctx *response_ctx,
223 : void *user_data);
224 :
225 : /**
226 : * @brief Representation of a dynamic server resource.
227 : */
228 1 : struct http_resource_detail_dynamic {
229 : /** Common resource details. */
230 1 : struct http_resource_detail common;
231 :
232 : /** Resource callback used by the server to interact with the
233 : * application.
234 : */
235 1 : http_resource_dynamic_cb_t cb;
236 :
237 : /** A pointer to the client currently processing resource, used to
238 : * prevent concurrent access to the resource from multiple clients.
239 : */
240 1 : struct http_client_ctx *holder;
241 :
242 : /** A pointer to the user data registered by the application. */
243 1 : void *user_data;
244 : };
245 :
246 : /** @cond INTERNAL_HIDDEN */
247 : BUILD_ASSERT(offsetof(struct http_resource_detail_dynamic, common) == 0);
248 : /** @endcond */
249 :
250 : /**
251 : * @typedef http_resource_websocket_cb_t
252 : * @brief Callback used when a Websocket connection is setup. The application
253 : * will need to handle all functionality related to the connection like
254 : * reading and writing websocket data, and closing the connection.
255 : *
256 : * @param ws_socket A socket for the Websocket data.
257 : * @param user_data User specified data.
258 : *
259 : * @return 0 Accepting the connection, HTTP server library will no longer
260 : * handle data to/from the socket and it is application responsibility
261 : * to send and receive data to/from the supplied socket.
262 : * <0 error, close the connection.
263 : */
264 1 : typedef int (*http_resource_websocket_cb_t)(int ws_socket,
265 : void *user_data);
266 :
267 : /** @brief Representation of a websocket server resource */
268 1 : struct http_resource_detail_websocket {
269 : /** Common resource details. */
270 1 : struct http_resource_detail common;
271 :
272 : /** Websocket socket value */
273 1 : int ws_sock;
274 :
275 : /** Resource callback used by the server to interact with the
276 : * application.
277 : */
278 1 : http_resource_websocket_cb_t cb;
279 :
280 : /** Data buffer used to exchanged data between server and the,
281 : * application.
282 : */
283 1 : uint8_t *data_buffer;
284 :
285 : /** Length of the data in the data buffer. */
286 1 : size_t data_buffer_len;
287 :
288 : /** A pointer to the user data registered by the application. */
289 1 : void *user_data;
290 : };
291 :
292 : /** @cond INTERNAL_HIDDEN */
293 : BUILD_ASSERT(offsetof(struct http_resource_detail_websocket, common) == 0);
294 : /** @endcond */
295 :
296 : /** @cond INTERNAL_HIDDEN */
297 :
298 : enum http2_stream_state {
299 : HTTP2_STREAM_IDLE,
300 : HTTP2_STREAM_RESERVED_LOCAL,
301 : HTTP2_STREAM_RESERVED_REMOTE,
302 : HTTP2_STREAM_OPEN,
303 : HTTP2_STREAM_HALF_CLOSED_LOCAL,
304 : HTTP2_STREAM_HALF_CLOSED_REMOTE,
305 : HTTP2_STREAM_CLOSED
306 : };
307 :
308 : enum http_server_state {
309 : HTTP_SERVER_FRAME_HEADER_STATE,
310 : HTTP_SERVER_PREFACE_STATE,
311 : HTTP_SERVER_REQUEST_STATE,
312 : HTTP_SERVER_FRAME_DATA_STATE,
313 : HTTP_SERVER_FRAME_HEADERS_STATE,
314 : HTTP_SERVER_FRAME_SETTINGS_STATE,
315 : HTTP_SERVER_FRAME_PRIORITY_STATE,
316 : HTTP_SERVER_FRAME_WINDOW_UPDATE_STATE,
317 : HTTP_SERVER_FRAME_CONTINUATION_STATE,
318 : HTTP_SERVER_FRAME_PING_STATE,
319 : HTTP_SERVER_FRAME_RST_STREAM_STATE,
320 : HTTP_SERVER_FRAME_GOAWAY_STATE,
321 : HTTP_SERVER_FRAME_PADDING_STATE,
322 : HTTP_SERVER_DONE_STATE,
323 : };
324 :
325 : enum http1_parser_state {
326 : HTTP1_INIT_HEADER_STATE,
327 : HTTP1_WAITING_HEADER_STATE,
328 : HTTP1_RECEIVING_HEADER_STATE,
329 : HTTP1_RECEIVED_HEADER_STATE,
330 : HTTP1_RECEIVING_DATA_STATE,
331 : HTTP1_MESSAGE_COMPLETE_STATE,
332 : };
333 :
334 : #define HTTP_SERVER_INITIAL_WINDOW_SIZE 65536
335 : #define HTTP_SERVER_WS_MAX_SEC_KEY_LEN 32
336 :
337 : /** @endcond */
338 :
339 : /** @brief HTTP/2 stream representation. */
340 1 : struct http2_stream_ctx {
341 1 : int stream_id; /**< Stream identifier. */
342 1 : enum http2_stream_state stream_state; /**< Stream state. */
343 1 : int window_size; /**< Stream-level window size. */
344 :
345 : /** Currently processed resource detail. */
346 1 : struct http_resource_detail *current_detail;
347 :
348 : /** Flag indicating that headers were sent in the reply. */
349 1 : bool headers_sent : 1;
350 :
351 : /** Flag indicating that END_STREAM flag was sent. */
352 1 : bool end_stream_sent : 1;
353 : };
354 :
355 : /** @brief HTTP/2 frame representation. */
356 1 : struct http2_frame {
357 1 : uint32_t length; /**< Frame payload length. */
358 1 : uint32_t stream_identifier; /**< Stream ID the frame belongs to. */
359 1 : uint8_t type; /**< Frame type. */
360 1 : uint8_t flags; /**< Frame flags. */
361 1 : uint8_t padding_len; /**< Frame padding length. */
362 : };
363 :
364 : /** @cond INTERNAL_HIDDEN */
365 : /** @brief Context for capturing HTTP headers */
366 : struct http_header_capture_ctx {
367 : /** Buffer for HTTP headers captured for application use */
368 : unsigned char buffer[HTTP_SERVER_CAPTURE_HEADER_BUFFER_SIZE];
369 :
370 : /** Descriptor of each captured HTTP header */
371 : struct http_header headers[HTTP_SERVER_CAPTURE_HEADER_COUNT];
372 :
373 : /** Status of captured headers */
374 : enum http_header_status status;
375 :
376 : /** Number of headers captured */
377 : size_t count;
378 :
379 : /** Current position in buffer */
380 : size_t cursor;
381 :
382 : /** The HTTP2 stream associated with the current headers */
383 : struct http2_stream_ctx *current_stream;
384 :
385 : /** The next HTTP header value should be stored */
386 : bool store_next_value;
387 : };
388 : /** @endcond */
389 :
390 : /** @brief HTTP header name representation */
391 1 : struct http_header_name {
392 1 : const char *name; /**< Pointer to header name NULL-terminated string. */
393 : };
394 :
395 : /**
396 : * @brief Representation of an HTTP client connected to the server.
397 : */
398 1 : struct http_client_ctx {
399 : /** Socket descriptor associated with the server. */
400 1 : int fd;
401 :
402 : /** Client data buffer. */
403 1 : unsigned char buffer[HTTP_SERVER_CLIENT_BUFFER_SIZE];
404 :
405 : /** Cursor indicating currently processed byte. */
406 1 : unsigned char *cursor;
407 :
408 : /** Data left to process in the buffer. */
409 1 : size_t data_len;
410 :
411 : /** Connection-level window size. */
412 1 : int window_size;
413 :
414 : /** Server state for the associated client. */
415 1 : enum http_server_state server_state;
416 :
417 : /** Currently processed HTTP/2 frame. */
418 1 : struct http2_frame current_frame;
419 :
420 : /** Currently processed resource detail. */
421 1 : struct http_resource_detail *current_detail;
422 :
423 : /** Currently processed stream. */
424 1 : struct http2_stream_ctx *current_stream;
425 :
426 : /** HTTP/2 header parser context. */
427 1 : struct http_hpack_header_buf header_field;
428 :
429 : /** HTTP/2 streams context. */
430 1 : struct http2_stream_ctx streams[HTTP_SERVER_MAX_STREAMS];
431 :
432 : /** HTTP/1 parser configuration. */
433 1 : struct http_parser_settings parser_settings;
434 :
435 : /** HTTP/1 parser context. */
436 1 : struct http_parser parser;
437 :
438 : /** Header capture context */
439 1 : struct http_header_capture_ctx header_capture_ctx;
440 :
441 : /** Request URL. */
442 1 : unsigned char url_buffer[HTTP_SERVER_MAX_URL_LENGTH];
443 :
444 : /** Request content type. */
445 1 : unsigned char content_type[HTTP_SERVER_MAX_CONTENT_TYPE_LEN];
446 :
447 : /** Temp buffer for currently processed header (HTTP/1 only). */
448 1 : unsigned char header_buffer[HTTP_SERVER_MAX_HEADER_LEN];
449 :
450 : /** Request content length. */
451 1 : size_t content_len;
452 :
453 : /** Request method. */
454 1 : enum http_method method;
455 :
456 : /** HTTP/1 parser state. */
457 1 : enum http1_parser_state parser_state;
458 :
459 : /** Length of the payload length in the currently processed request
460 : * fragment (HTTP/1 only).
461 : */
462 1 : int http1_frag_data_len;
463 :
464 : /** Client inactivity timer. The client connection is closed by the
465 : * server when it expires.
466 : */
467 1 : struct k_work_delayable inactivity_timer;
468 :
469 : /** @cond INTERNAL_HIDDEN */
470 : /** Websocket security key. */
471 : IF_ENABLED(CONFIG_WEBSOCKET, (uint8_t ws_sec_key[HTTP_SERVER_WS_MAX_SEC_KEY_LEN]));
472 : /** @endcond */
473 :
474 : /** Flag indicating that HTTP2 preface was sent. */
475 1 : bool preface_sent : 1;
476 :
477 : /** Flag indicating that HTTP1 headers were sent. */
478 1 : bool http1_headers_sent : 1;
479 :
480 : /** Flag indicating that upgrade header was present in the request. */
481 1 : bool has_upgrade_header : 1;
482 :
483 : /** Flag indicating HTTP/2 upgrade takes place. */
484 1 : bool http2_upgrade : 1;
485 :
486 : /** Flag indicating Websocket upgrade takes place. */
487 1 : bool websocket_upgrade : 1;
488 :
489 : /** Flag indicating Websocket key is being processed. */
490 1 : bool websocket_sec_key_next : 1;
491 :
492 : /** The next frame on the stream is expectd to be a continuation frame. */
493 1 : bool expect_continuation : 1;
494 : };
495 :
496 : /**
497 : * @brief Register an HTTP request header to be captured by the server
498 : *
499 : * @param _id variable name for the header capture instance
500 : * @param _header header to be captured, as literal string
501 : */
502 1 : #define HTTP_SERVER_REGISTER_HEADER_CAPTURE(_id, _header) \
503 : BUILD_ASSERT(sizeof(_header) <= CONFIG_HTTP_SERVER_MAX_HEADER_LEN, \
504 : "Header is too long to be captured, try increasing " \
505 : "CONFIG_HTTP_SERVER_MAX_HEADER_LEN"); \
506 : static const char *const _id##_str = _header; \
507 : static const STRUCT_SECTION_ITERABLE(http_header_name, _id) = { \
508 : .name = _id##_str, \
509 : }
510 :
511 : /** @brief Start the HTTP2 server.
512 : *
513 : * The server runs in a background thread. Once started, the server will create
514 : * a server socket for all HTTP services registered in the system and accept
515 : * connections from clients (see @ref HTTP_SERVICE_DEFINE).
516 : */
517 1 : int http_server_start(void);
518 :
519 : /** @brief Stop the HTTP2 server.
520 : *
521 : * All server sockets are closed and the server thread is suspended.
522 : */
523 1 : int http_server_stop(void);
524 :
525 : #ifdef __cplusplus
526 : }
527 : #endif
528 :
529 : /**
530 : * @}
531 : */
532 :
533 : #endif
|