Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTP server support #64465

Merged
merged 5 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -16,6 +16,7 @@

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

#if defined(CONFIG_COAP_SERVER)
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
135 changes: 135 additions & 0 deletions include/zephyr/net/http/hpack.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/** @file
* @brief HTTP HPACK
*/

/*
* Copyright (c) 2023 Emna Rekik
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_NET_HTTP_SERVER_HPACK_H_
#define ZEPHYR_INCLUDE_NET_HTTP_SERVER_HPACK_H_

#include <stddef.h>
#include <stdint.h>

/**
* @brief HTTP HPACK
* @defgroup http_hpack HTTP HPACK
* @ingroup networking
* @{
*/

#ifdef __cplusplus
extern "C" {
#endif

enum http_hpack_static_key {
HTTP_SERVER_HPACK_INVALID = 0,
HTTP_SERVER_HPACK_AUTHORITY = 1,
HTTP_SERVER_HPACK_METHOD_GET = 2,
HTTP_SERVER_HPACK_METHOD_POST = 3,
HTTP_SERVER_HPACK_PATH_ROOT = 4,
HTTP_SERVER_HPACK_PATH_INDEX = 5,
HTTP_SERVER_HPACK_SCHEME_HTTP = 6,
HTTP_SERVER_HPACK_SCHEME_HTTPS = 7,
HTTP_SERVER_HPACK_STATUS_200 = 8,
HTTP_SERVER_HPACK_STATUS_204 = 9,
HTTP_SERVER_HPACK_STATUS_206 = 10,
HTTP_SERVER_HPACK_STATUS_304 = 11,
HTTP_SERVER_HPACK_STATUS_400 = 12,
HTTP_SERVER_HPACK_STATUS_404 = 13,
HTTP_SERVER_HPACK_STATUS_500 = 14,
HTTP_SERVER_HPACK_ACCEPT_CHARSET = 15,
HTTP_SERVER_HPACK_ACCEPT_ENCODING = 16,
HTTP_SERVER_HPACK_ACCEPT_LANGUAGE = 17,
HTTP_SERVER_HPACK_ACCEPT_RANGES = 18,
HTTP_SERVER_HPACK_ACCEPT = 19,
HTTP_SERVER_HPACK_ACCESS_CONTROL_ALLOW_ORIGIN = 20,
HTTP_SERVER_HPACK_AGE = 21,
HTTP_SERVER_HPACK_ALLOW = 22,
HTTP_SERVER_HPACK_AUTHORIZATION = 23,
HTTP_SERVER_HPACK_CACHE_CONTROL = 24,
HTTP_SERVER_HPACK_CONTENT_DISPOSITION = 25,
HTTP_SERVER_HPACK_CONTENT_ENCODING = 26,
HTTP_SERVER_HPACK_CONTENT_LANGUAGE = 27,
HTTP_SERVER_HPACK_CONTENT_LENGTH = 28,
HTTP_SERVER_HPACK_CONTENT_LOCATION = 29,
HTTP_SERVER_HPACK_CONTENT_RANGE = 30,
HTTP_SERVER_HPACK_CONTENT_TYPE = 31,
HTTP_SERVER_HPACK_COOKIE = 32,
HTTP_SERVER_HPACK_DATE = 33,
HTTP_SERVER_HPACK_ETAG = 34,
HTTP_SERVER_HPACK_EXPECT = 35,
HTTP_SERVER_HPACK_EXPIRES = 36,
HTTP_SERVER_HPACK_FROM = 37,
HTTP_SERVER_HPACK_HOST = 38,
HTTP_SERVER_HPACK_IF_MATCH = 39,
HTTP_SERVER_HPACK_IF_MODIFIED_SINCE = 40,
HTTP_SERVER_HPACK_IF_NONE_MATCH = 41,
HTTP_SERVER_HPACK_IF_RANGE = 42,
HTTP_SERVER_HPACK_IF_UNMODIFIED_SINCE = 43,
HTTP_SERVER_HPACK_LAST_MODIFIED = 44,
HTTP_SERVER_HPACK_LINK = 45,
HTTP_SERVER_HPACK_LOCATION = 46,
HTTP_SERVER_HPACK_MAX_FORWARDS = 47,
HTTP_SERVER_HPACK_PROXY_AUTHENTICATE = 48,
HTTP_SERVER_HPACK_PROXY_AUTHORIZATION = 49,
HTTP_SERVER_HPACK_RANGE = 50,
HTTP_SERVER_HPACK_REFERER = 51,
HTTP_SERVER_HPACK_REFRESH = 52,
HTTP_SERVER_HPACK_RETRY_AFTER = 53,
HTTP_SERVER_HPACK_SERVER = 54,
HTTP_SERVER_HPACK_SET_COOKIE = 55,
HTTP_SERVER_HPACK_STRICT_TRANSPORT_SECURITY = 56,
HTTP_SERVER_HPACK_TRANSFER_ENCODING = 57,
HTTP_SERVER_HPACK_USER_AGENT = 58,
HTTP_SERVER_HPACK_VARY = 59,
HTTP_SERVER_HPACK_VIA = 60,
HTTP_SERVER_HPACK_WWW_AUTHENTICATE = 61,
};

/* TODO Kconfig */
#define HTTP2_HEADER_FIELD_MAX_LEN 256

/** HTTP2 header field with decoding buffer. */
struct http_hpack_header_buf {
/** A pointer to the decoded header field name. */
const char *name;

/** A pointer to the decoded header field value. */
const char *value;

/** Length of the decoded header field name. */
size_t name_len;

/** Length of the decoded header field value. */
size_t value_len;

/** Encoding/Decoding buffer. Used with Huffman encoding/decoding. */
uint8_t buf[CONFIG_HTTP_SERVER_HUFFMAN_DECODE_BUFFER_SIZE];

/** Length of the data in the decoding buffer. */
size_t datalen;
};

int http_hpack_huffman_decode(const uint8_t *encoded_buf, size_t encoded_len,
uint8_t *buf, size_t buflen);
int http_hpack_huffman_encode(const uint8_t *str, size_t str_len,
uint8_t *buf, size_t buflen);
int http_hpack_decode_header(const uint8_t *buf, size_t datalen,
struct http_hpack_header_buf *header);
int http_hpack_encode_header(uint8_t *buf, size_t buflen,
struct http_hpack_header_buf *header);

#ifdef __cplusplus
}
#endif

/**
* @}
*/

#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
174 changes: 174 additions & 0 deletions include/zephyr/net/http/server.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* 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,
};

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
Loading
Loading