Skip to content

Commit

Permalink
net: lib: http_server: Initial HTTP server support
Browse files Browse the repository at this point in the history
Original code developed as a GSoC 2023 project by Emna Rekik.

Code refactored in order to provide better bisectability
as the origical commits were not bisectable.

The server supports static and dynamic resources, managed by
HTTP_SERVICE/HTTP_RESOURCE macros.

Fixes zephyrproject-rtos#59685
Fixes zephyrproject-rtos#59686
Fixes zephyrproject-rtos#59688
Fixes zephyrproject-rtos#59690
Fixes zephyrproject-rtos#59670
Fixes zephyrproject-rtos#59700
Fixes zephyrproject-rtos#59684
Fixes zephyrproject-rtos#59693
Fixes zephyrproject-rtos#59693
Fixes zephyrproject-rtos#59694
Fixes zephyrproject-rtos#59699
Fixes zephyrproject-rtos#59696
Fixes zephyrproject-rtos#59688
Fixes zephyrproject-rtos#59690
Fixes zephyrproject-rtos#59670
Fixes zephyrproject-rtos#59700
Fixes zephyrproject-rtos#59685
Fixes zephyrproject-rtos#59686
Fixes zephyrproject-rtos#59688
Fixes zephyrproject-rtos#59691

Signed-off-by: Emna Rekik <emna.rekik007@gmail.com>
Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no>
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
  • Loading branch information
jukkar authored and rlubos committed Apr 26, 2024
1 parent d46928b commit 340063b
Show file tree
Hide file tree
Showing 14 changed files with 2,851 additions and 12 deletions.
1 change: 1 addition & 0 deletions include/zephyr/linker/common-rom/common-rom-net.ld
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#endif

#if defined(CONFIG_HTTP_SERVER)
ITERABLE_SECTION_ROM(http_resource_desc, 4)
ITERABLE_SECTION_ROM(http_service_desc, 4)
#endif

Expand Down
52 changes: 52 additions & 0 deletions include/zephyr/net/http/frame.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2023, Emna Rekik
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_NET_HTTP_SERVER_FRAME_H_
#define ZEPHYR_INCLUDE_NET_HTTP_SERVER_FRAME_H_

#include <stdint.h>

enum http_frame_type {
HTTP_SERVER_DATA_FRAME = 0x00,
HTTP_SERVER_HEADERS_FRAME = 0x01,
HTTP_SERVER_PRIORITY_FRAME = 0x02,
HTTP_SERVER_RST_STREAM_FRAME = 0x03,
HTTP_SERVER_SETTINGS_FRAME = 0x04,
HTTP_SERVER_PUSH_PROMISE_FRAME = 0x05,
HTTP_SERVER_PING_FRAME = 0x06,
HTTP_SERVER_GOAWAY_FRAME = 0x07,
HTTP_SERVER_WINDOW_UPDATE_FRAME = 0x08,
HTTP_SERVER_CONTINUATION_FRAME = 0x09
};

#define HTTP_SERVER_HPACK_METHOD 0
#define HTTP_SERVER_HPACK_PATH 1

#define HTTP_SERVER_FLAG_SETTINGS_ACK 0x1
#define HTTP_SERVER_FLAG_END_HEADERS 0x4
#define HTTP_SERVER_FLAG_END_STREAM 0x1

#define HTTP_SERVER_FRAME_HEADER_SIZE 9
#define HTTP_SERVER_FRAME_LENGTH_OFFSET 0
#define HTTP_SERVER_FRAME_TYPE_OFFSET 3
#define HTTP_SERVER_FRAME_FLAGS_OFFSET 4
#define HTTP_SERVER_FRAME_STREAM_ID_OFFSET 5

struct http_settings_field {
uint16_t id;
uint32_t value;
} __packed;

enum http_settings {
HTTP_SETTINGS_HEADER_TABLE_SIZE = 1,
HTTP_SETTINGS_ENABLE_PUSH = 2,
HTTP_SETTINGS_MAX_CONCURRENT_STREAMS = 3,
HTTP_SETTINGS_INITIAL_WINDOW_SIZE = 4,
HTTP_SETTINGS_MAX_FRAME_SIZE = 5,
HTTP_SETTINGS_MAX_HEADER_LIST_SIZE = 6,
};

#endif
2 changes: 2 additions & 0 deletions include/zephyr/net/http/method.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ enum http_method {
HTTP_MKCALENDAR = 30, /**< MKCALENDAR */
HTTP_LINK = 31, /**< LINK */
HTTP_UNLINK = 32, /**< UNLINK */

HTTP_METHOD_END_VALUE /* keep this the last value */
};

#ifdef __cplusplus
Expand Down
175 changes: 175 additions & 0 deletions include/zephyr/net/http/server.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
* Copyright (c) 2023, Emna Rekik
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_NET_HTTP_SERVER_H_
#define ZEPHYR_INCLUDE_NET_HTTP_SERVER_H_

#include <stdint.h>

#include <zephyr/kernel.h>
#include <zephyr/net/http/parser.h>
#include <zephyr/net/http/hpack.h>
#include <zephyr/net/socket.h>

#define HTTP_SERVER_CLIENT_BUFFER_SIZE CONFIG_HTTP_SERVER_CLIENT_BUFFER_SIZE
#define HTTP_SERVER_MAX_STREAMS CONFIG_HTTP_SERVER_MAX_STREAMS
#define HTTP_SERVER_MAX_CONTENT_TYPE_LEN CONFIG_HTTP_SERVER_MAX_CONTENT_TYPE_LENGTH

#define HTTP2_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"

enum http_resource_type {
HTTP_RESOURCE_TYPE_STATIC,
HTTP_RESOURCE_TYPE_DYNAMIC,
HTTP_RESOURCE_TYPE_REST
};

struct http_resource_detail {
uint32_t bitmask_of_supported_http_methods;
enum http_resource_type type;
int path_len; /* length of the URL path */
const char *content_encoding;
};
BUILD_ASSERT(NUM_BITS(
sizeof(((struct http_resource_detail *)0)->bitmask_of_supported_http_methods))
>= (HTTP_METHOD_END_VALUE - 1));

struct http_resource_detail_static {
struct http_resource_detail common;
const void *static_data;
size_t static_data_len;
};

struct http_client_ctx;

/** Indicates the status of the currently processed piece of data. */
enum http_data_status {
/** Transaction aborted, data incomplete. */
HTTP_SERVER_DATA_ABORTED = -1,
/** Transaction incomplete, more data expected. */
HTTP_SERVER_DATA_MORE = 0,
/** Final data fragment in current transaction. */
HTTP_SERVER_DATA_FINAL = 1,
};

/**
* @typedef http_resource_dynamic_cb_t
* @brief Callback used when data is received. Data to be sent to client
* can be specified.
*
* @param client HTTP context information for this client connection.
* @param status HTTP data status, indicate whether more data is expected or not.
* @param data_buffer Data received.
* @param data_len Amount of data received.
* @param user_data User specified data.
*
* @return >0 amount of data to be sent to client, let server to call this
* function again when new data is received.
* 0 nothing to sent to client, close the connection
* <0 error, close the connection.
*/
typedef int (*http_resource_dynamic_cb_t)(struct http_client_ctx *client,
enum http_data_status status,
uint8_t *data_buffer,
size_t data_len,
void *user_data);

struct http_resource_detail_dynamic {
struct http_resource_detail common;
http_resource_dynamic_cb_t cb;
uint8_t *data_buffer;
size_t data_buffer_len;
struct http_client_ctx *holder;
void *user_data;
};

struct http_resource_detail_rest {
struct http_resource_detail common;
};

enum http_stream_state {
HTTP_SERVER_STREAM_IDLE,
HTTP_SERVER_STREAM_RESERVED_LOCAL,
HTTP_SERVER_STREAM_RESERVED_REMOTE,
HTTP_SERVER_STREAM_OPEN,
HTTP_SERVER_STREAM_HALF_CLOSED_LOCAL,
HTTP_SERVER_STREAM_HALF_CLOSED_REMOTE,
HTTP_SERVER_STREAM_CLOSED
};

enum http_server_state {
HTTP_SERVER_FRAME_HEADER_STATE,
HTTP_SERVER_PREFACE_STATE,
HTTP_SERVER_REQUEST_STATE,
HTTP_SERVER_FRAME_DATA_STATE,
HTTP_SERVER_FRAME_HEADERS_STATE,
HTTP_SERVER_FRAME_SETTINGS_STATE,
HTTP_SERVER_FRAME_PRIORITY_STATE,
HTTP_SERVER_FRAME_WINDOW_UPDATE_STATE,
HTTP_SERVER_FRAME_CONTINUATION_STATE,
HTTP_SERVER_FRAME_PING_STATE,
HTTP_SERVER_FRAME_RST_STREAM_STATE,
HTTP_SERVER_FRAME_GOAWAY_STATE,
HTTP_SERVER_DONE_STATE,
};

enum http1_parser_state {
HTTP1_INIT_HEADER_STATE,
HTTP1_WAITING_HEADER_STATE,
HTTP1_RECEIVING_HEADER_STATE,
HTTP1_RECEIVED_HEADER_STATE,
HTTP1_RECEIVING_DATA_STATE,
HTTP1_MESSAGE_COMPLETE_STATE,
};

#define HTTP_SERVER_INITIAL_WINDOW_SIZE 65536

struct http_stream_ctx {
int stream_id;
enum http_stream_state stream_state;
int window_size; /**< Stream-level window size. */
};

struct http_frame {
uint32_t length;
uint32_t stream_identifier;
uint8_t type;
uint8_t flags;
uint8_t *payload;
};

struct http_client_ctx {
int fd;
bool preface_sent;
bool has_upgrade_header;
unsigned char buffer[HTTP_SERVER_CLIENT_BUFFER_SIZE];
unsigned char *cursor; /**< Cursor indicating currently processed byte. */
size_t data_len; /**< Data left to process in the buffer. */
int window_size; /**< Connection-level window size. */
enum http_server_state server_state;
struct http_frame current_frame;
struct http_resource_detail *current_detail;
struct http_hpack_header_buf header_field;
struct http_stream_ctx streams[HTTP_SERVER_MAX_STREAMS];
struct http_parser_settings parser_settings;
struct http_parser parser;
unsigned char url_buffer[CONFIG_HTTP_SERVER_MAX_URL_LENGTH];
unsigned char content_type[CONFIG_HTTP_SERVER_MAX_CONTENT_TYPE_LENGTH];
size_t content_len;
enum http_method method;
enum http1_parser_state parser_state;
int http1_frag_data_len;
bool headers_sent;
struct k_work_delayable inactivity_timer;
};

/* Starts the HTTP2 server */
int http_server_start(void);

/* Stops the HTTP2 server */
int http_server_stop(void);

#endif
18 changes: 11 additions & 7 deletions include/zephyr/net/http/service.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ struct http_resource_desc {
const STRUCT_SECTION_ITERABLE_ALTERNATE(http_resource_desc_##_service, http_resource_desc, \
_name) = { \
.resource = _resource, \
.detail = (_detail), \
.detail = (void *)(_detail), \
}

struct http_service_desc {
Expand All @@ -55,12 +55,14 @@ struct http_service_desc {
size_t backlog;
struct http_resource_desc *res_begin;
struct http_resource_desc *res_end;
#if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS)
const sec_tag_t *sec_tag_list;
size_t sec_tag_list_size;
#endif
};

#define __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, _res_begin, \
_res_end, ...) \
#define __z_http_service_define(_name, _host, _port, _concurrent, _backlog, _detail, _res_begin, \
_res_end, ...) \
static const STRUCT_SECTION_ITERABLE(http_service_desc, _name) = { \
.host = _host, \
.port = (uint16_t *)(_port), \
Expand All @@ -69,10 +71,12 @@ struct http_service_desc {
.backlog = (_backlog), \
.res_begin = (_res_begin), \
.res_end = (_res_end), \
.sec_tag_list = COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (NULL), \
(GET_ARG_N(1,__VA_ARGS__))), \
.sec_tag_list_size = COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (0), \
(GET_ARG_N(1, GET_ARGS_LESS_N(1, __VA_ARGS__)))), \
COND_CODE_1(CONFIG_NET_SOCKETS_SOCKOPT_TLS, \
(.sec_tag_list = COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (NULL), \
(GET_ARG_N(1, __VA_ARGS__))),), ()) \
COND_CODE_1(CONFIG_NET_SOCKETS_SOCKOPT_TLS, \
(.sec_tag_list_size = COND_CODE_0(NUM_VA_ARGS_LESS_1(__VA_ARGS__), (0),\
(GET_ARG_N(1, GET_ARGS_LESS_N(1, __VA_ARGS__))))), ())\
}

/**
Expand Down
2 changes: 1 addition & 1 deletion subsys/net/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ if (CONFIG_DNS_RESOLVER
add_subdirectory(dns)
endif()

if(CONFIG_HTTP_PARSER_URL OR CONFIG_HTTP_PARSER OR CONFIG_HTTP_CLIENT)
if(CONFIG_HTTP_PARSER_URL OR CONFIG_HTTP_PARSER OR CONFIG_HTTP_CLIENT OR CONFIG_HTTP_SERVER)
add_subdirectory(http)
endif()

Expand Down
8 changes: 8 additions & 0 deletions subsys/net/lib/http/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@ zephyr_include_directories(${ZEPHYR_BASE}/subsys/net/ip)
zephyr_library_sources_ifdef(CONFIG_HTTP_PARSER http_parser.c)
zephyr_library_sources_ifdef(CONFIG_HTTP_PARSER_URL http_parser_url.c)
zephyr_library_sources_ifdef(CONFIG_HTTP_CLIENT http_client.c)
zephyr_library_sources_ifdef(
CONFIG_HTTP_SERVER
http_server_core.c
http_server_http1.c
http_server_http2.c
http_hpack.c
http_huffman.c
)
Loading

0 comments on commit 340063b

Please sign in to comment.