net: lib: http_server: Add HPACK and Huffman code encoder/decoder
Add HTTP/2 helper libraries to encode and decode HPACK encoded headers, according to RFC7541. HPACK string encoding requires to support certain set of Huffman codes, therefore implement Huffman encoder/decoder as well. Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
parent
392f0ced2c
commit
318dcb6336
135
include/zephyr/net/http/hpack.h
Normal file
135
include/zephyr/net/http/hpack.h
Normal file
|
@ -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
|
631
subsys/net/lib/http/http_hpack.c
Normal file
631
subsys/net/lib/http/http_hpack.c
Normal file
|
@ -0,0 +1,631 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Emna Rekik
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/net/http/hpack.h>
|
||||
#include <zephyr/net/net_core.h>
|
||||
|
||||
LOG_MODULE_DECLARE(net_http_server, CONFIG_NET_HTTP_SERVER_LOG_LEVEL);
|
||||
|
||||
static inline bool http_hpack_key_is_static(uint32_t key)
|
||||
{
|
||||
return key > HTTP_SERVER_HPACK_INVALID && key <= HTTP_SERVER_HPACK_WWW_AUTHENTICATE;
|
||||
}
|
||||
|
||||
static inline bool http_hpack_key_is_dynamic(uint32_t key)
|
||||
{
|
||||
return key > HTTP_SERVER_HPACK_WWW_AUTHENTICATE;
|
||||
}
|
||||
|
||||
struct hpack_table_entry {
|
||||
const char *name;
|
||||
const char *value;
|
||||
};
|
||||
|
||||
static const struct hpack_table_entry http_hpack_table_static[] = {
|
||||
[HTTP_SERVER_HPACK_AUTHORITY] = { ":authority", NULL },
|
||||
[HTTP_SERVER_HPACK_METHOD_GET] = { ":method", "GET" },
|
||||
[HTTP_SERVER_HPACK_METHOD_POST] = { ":method", "POST" },
|
||||
[HTTP_SERVER_HPACK_PATH_ROOT] = { ":path", "/" },
|
||||
[HTTP_SERVER_HPACK_PATH_INDEX] = { ":path", "/index.html" },
|
||||
[HTTP_SERVER_HPACK_SCHEME_HTTP] = { ":scheme", "http" },
|
||||
[HTTP_SERVER_HPACK_SCHEME_HTTPS] = { ":scheme", "https" },
|
||||
[HTTP_SERVER_HPACK_STATUS_200] = { ":status", "200" },
|
||||
[HTTP_SERVER_HPACK_STATUS_204] = { ":status", "204" },
|
||||
[HTTP_SERVER_HPACK_STATUS_206] = { ":status", "206" },
|
||||
[HTTP_SERVER_HPACK_STATUS_304] = { ":status", "304" },
|
||||
[HTTP_SERVER_HPACK_STATUS_400] = { ":status", "400" },
|
||||
[HTTP_SERVER_HPACK_STATUS_404] = { ":status", "404" },
|
||||
[HTTP_SERVER_HPACK_STATUS_500] = { ":status", "500" },
|
||||
[HTTP_SERVER_HPACK_ACCEPT_CHARSET] = { "accept-charset", NULL },
|
||||
[HTTP_SERVER_HPACK_ACCEPT_ENCODING] = { "accept-encoding", "gzip, deflate" },
|
||||
[HTTP_SERVER_HPACK_ACCEPT_LANGUAGE] = { "accept-language", NULL },
|
||||
[HTTP_SERVER_HPACK_ACCEPT_RANGES] = { "accept-ranges", NULL },
|
||||
[HTTP_SERVER_HPACK_ACCEPT] = { "accept", NULL },
|
||||
[HTTP_SERVER_HPACK_ACCESS_CONTROL_ALLOW_ORIGIN] = { "access-control-allow-origin", NULL },
|
||||
[HTTP_SERVER_HPACK_AGE] = { "age", NULL },
|
||||
[HTTP_SERVER_HPACK_ALLOW] = { "allow", NULL },
|
||||
[HTTP_SERVER_HPACK_AUTHORIZATION] = { "authorization", NULL },
|
||||
[HTTP_SERVER_HPACK_CACHE_CONTROL] = { "cache-control", NULL },
|
||||
[HTTP_SERVER_HPACK_CONTENT_DISPOSITION] = { "content-disposition", NULL },
|
||||
[HTTP_SERVER_HPACK_CONTENT_ENCODING] = { "content-encoding", NULL },
|
||||
[HTTP_SERVER_HPACK_CONTENT_LANGUAGE] = { "content-language", NULL },
|
||||
[HTTP_SERVER_HPACK_CONTENT_LENGTH] = { "content-length", NULL },
|
||||
[HTTP_SERVER_HPACK_CONTENT_LOCATION] = { "content-location", NULL },
|
||||
[HTTP_SERVER_HPACK_CONTENT_RANGE] = { "content-range", NULL },
|
||||
[HTTP_SERVER_HPACK_CONTENT_TYPE] = { "content-type", NULL },
|
||||
[HTTP_SERVER_HPACK_COOKIE] = { "cookie", NULL },
|
||||
[HTTP_SERVER_HPACK_DATE] = { "date", NULL },
|
||||
[HTTP_SERVER_HPACK_ETAG] = { "etag", NULL },
|
||||
[HTTP_SERVER_HPACK_EXPECT] = { "expect", NULL },
|
||||
[HTTP_SERVER_HPACK_EXPIRES] = { "expires", NULL },
|
||||
[HTTP_SERVER_HPACK_FROM] = { "from", NULL },
|
||||
[HTTP_SERVER_HPACK_HOST] = { "host", NULL },
|
||||
[HTTP_SERVER_HPACK_IF_MATCH] = { "if-match", NULL },
|
||||
[HTTP_SERVER_HPACK_IF_MODIFIED_SINCE] = { "if-modified-since", NULL },
|
||||
[HTTP_SERVER_HPACK_IF_NONE_MATCH] = { "if-none-match", NULL },
|
||||
[HTTP_SERVER_HPACK_IF_RANGE] = { "if-range", NULL },
|
||||
[HTTP_SERVER_HPACK_IF_UNMODIFIED_SINCE] = { "if-unmodified-since", NULL },
|
||||
[HTTP_SERVER_HPACK_LAST_MODIFIED] = { "last-modified", NULL },
|
||||
[HTTP_SERVER_HPACK_LINK] = { "link", NULL },
|
||||
[HTTP_SERVER_HPACK_LOCATION] = { "location", NULL },
|
||||
[HTTP_SERVER_HPACK_MAX_FORWARDS] = { "max-forwards", NULL },
|
||||
[HTTP_SERVER_HPACK_PROXY_AUTHENTICATE] = { "proxy-authenticate", NULL },
|
||||
[HTTP_SERVER_HPACK_PROXY_AUTHORIZATION] = { "proxy-authorization", NULL },
|
||||
[HTTP_SERVER_HPACK_RANGE] = { "range", NULL },
|
||||
[HTTP_SERVER_HPACK_REFERER] = { "referer", NULL },
|
||||
[HTTP_SERVER_HPACK_REFRESH] = { "refresh", NULL },
|
||||
[HTTP_SERVER_HPACK_RETRY_AFTER] = { "retry-after", NULL },
|
||||
[HTTP_SERVER_HPACK_SERVER] = { "server", NULL },
|
||||
[HTTP_SERVER_HPACK_SET_COOKIE] = { "set-cookie", NULL },
|
||||
[HTTP_SERVER_HPACK_STRICT_TRANSPORT_SECURITY] = { "strict-transport-security", NULL },
|
||||
[HTTP_SERVER_HPACK_TRANSFER_ENCODING] = { "transfer-encoding", NULL },
|
||||
[HTTP_SERVER_HPACK_USER_AGENT] = { "user-agent", NULL },
|
||||
[HTTP_SERVER_HPACK_VARY] = { "vary", NULL },
|
||||
[HTTP_SERVER_HPACK_VIA] = { "via", NULL },
|
||||
[HTTP_SERVER_HPACK_WWW_AUTHENTICATE] = { "www-authenticate", NULL },
|
||||
};
|
||||
|
||||
const struct hpack_table_entry *http_hpack_table_get(uint32_t key)
|
||||
{
|
||||
if (!http_hpack_key_is_static(key)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &http_hpack_table_static[key];
|
||||
}
|
||||
|
||||
static int http_hpack_find_index(struct http_hpack_header_buf *header,
|
||||
bool *name_only)
|
||||
{
|
||||
const struct hpack_table_entry *entry;
|
||||
int candidate = -1;
|
||||
|
||||
for (int i = HTTP_SERVER_HPACK_AUTHORITY;
|
||||
i <= HTTP_SERVER_HPACK_WWW_AUTHENTICATE; i++) {
|
||||
entry = &http_hpack_table_static[i];
|
||||
|
||||
if (entry->name != NULL &&
|
||||
strlen(entry->name) == header->name_len &&
|
||||
memcmp(entry->name, header->name, header->name_len) == 0) {
|
||||
if (entry->value != NULL &&
|
||||
strlen(entry->value) == header->value_len &&
|
||||
memcmp(entry->value, header->value, header->value_len) == 0) {
|
||||
/* Got exact match. */
|
||||
*name_only = false;
|
||||
return i;
|
||||
}
|
||||
|
||||
if (candidate < 0) {
|
||||
candidate = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidate > 0) {
|
||||
/* Matched name only. */
|
||||
*name_only = true;
|
||||
return candidate;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
#define HPACK_INTEGER_CONTINUATION_FLAG 0x80
|
||||
#define HPACK_STRING_HUFFMAN_FLAG 0x80
|
||||
#define HPACK_STRING_PREFIX_LEN 7
|
||||
|
||||
#define HPACK_PREFIX_INDEXED_MASK 0x80
|
||||
#define HPACK_PREFIX_INDEXED 0x80
|
||||
#define HPACK_PREFIX_LEN_INDEXED 7
|
||||
|
||||
#define HPACK_PREFIX_LITERAL_INDEXING_MASK 0xC0
|
||||
#define HPACK_PREFIX_LITERAL_INDEXING 0x40
|
||||
#define HPACK_PREFIX_LEN_LITERAL_INDEXING 6
|
||||
|
||||
#define HPACK_PREFIX_LITERAL_NO_INDEXING_MASK 0xF0
|
||||
#define HPACK_PREFIX_LITERAL_NO_INDEXING 0x00
|
||||
#define HPACK_PREFIX_LEN_LITERAL_NO_INDEXING 4
|
||||
|
||||
#define HPACK_PREFIX_LITERAL_NEVER_INDEXED_MASK 0xF0
|
||||
#define HPACK_PREFIX_LITERAL_NEVER_INDEXED 0x10
|
||||
#define HPACK_PREFIX_LEN_LITERAL_NEVER_INDEXED 4
|
||||
|
||||
#define HPACK_PREFIX_DYNAMIC_TABLE_SIZE_MASK 0xE0
|
||||
#define HPACK_PREFIX_DYNAMIC_TABLE_SIZE_UPDATE 0x20
|
||||
#define HPACK_PREFIX_LEN_DYNAMIC_TABLE_SIZE_UPDATE 5
|
||||
|
||||
static int hpack_integer_decode(const uint8_t *buf, size_t datalen,
|
||||
uint8_t n, uint32_t *value)
|
||||
{
|
||||
int len = 0;
|
||||
uint8_t m = 0;
|
||||
uint8_t value_mask = (1 << n) - 1;
|
||||
|
||||
NET_ASSERT(n < 8);
|
||||
|
||||
if (datalen == 0) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/* Based on RFC7541, ch 5.1. */
|
||||
len++;
|
||||
*value = *buf & value_mask;
|
||||
if (*value < value_mask) {
|
||||
return len;
|
||||
}
|
||||
|
||||
do {
|
||||
buf++;
|
||||
len++;
|
||||
|
||||
if (--datalen == 0) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (m > sizeof(uint32_t) * 8) {
|
||||
/* Can't handle integer that large. */
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
*value += (*buf & ~HPACK_INTEGER_CONTINUATION_FLAG) * (1 << m);
|
||||
m += 7;
|
||||
|
||||
} while (*buf & HPACK_INTEGER_CONTINUATION_FLAG);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
enum hpack_string_type {
|
||||
HPACK_HEADER_NAME,
|
||||
HPACK_HEADER_VALUE,
|
||||
};
|
||||
|
||||
static int hpack_huffman_decode(const uint8_t *encoded_buf, size_t encoded_len,
|
||||
enum hpack_string_type type,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
uint8_t *buf = header->buf + header->datalen;
|
||||
size_t buflen = sizeof(header->buf) - header->datalen;
|
||||
int ret;
|
||||
|
||||
NET_ASSERT(type == HPACK_HEADER_NAME || type == HPACK_HEADER_VALUE);
|
||||
|
||||
ret = http_hpack_huffman_decode(encoded_buf, encoded_len, buf, buflen);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (type == HPACK_HEADER_NAME) {
|
||||
header->name = buf;
|
||||
header->name_len = ret;
|
||||
} else if (type == HPACK_HEADER_VALUE) {
|
||||
header->value = buf;
|
||||
header->value_len = ret;
|
||||
}
|
||||
|
||||
header->datalen += ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hpack_string_decode(const uint8_t *buf, size_t datalen,
|
||||
enum hpack_string_type type,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
uint32_t str_len;
|
||||
bool huffman;
|
||||
int len = 0;
|
||||
int ret;
|
||||
|
||||
NET_ASSERT(type == HPACK_HEADER_NAME || type == HPACK_HEADER_VALUE);
|
||||
|
||||
if (datalen == 0) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
huffman = *buf & HPACK_STRING_HUFFMAN_FLAG;
|
||||
|
||||
ret = hpack_integer_decode(buf, datalen, HPACK_STRING_PREFIX_LEN,
|
||||
&str_len);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
len += ret;
|
||||
datalen -= ret;
|
||||
buf += ret;
|
||||
|
||||
if (str_len > datalen) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (huffman) {
|
||||
ret = hpack_huffman_decode(buf, str_len, type, header);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
if (type == HPACK_HEADER_NAME) {
|
||||
header->name = buf;
|
||||
header->name_len = str_len;
|
||||
} else if (type == HPACK_HEADER_VALUE) {
|
||||
header->value = buf;
|
||||
header->value_len = str_len;
|
||||
}
|
||||
}
|
||||
|
||||
len += str_len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int hpack_handle_indexed(const uint8_t *buf, size_t datalen,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
const struct hpack_table_entry *entry;
|
||||
uint32_t index;
|
||||
int ret;
|
||||
|
||||
ret = hpack_integer_decode(buf, datalen, HPACK_PREFIX_LEN_INDEXED,
|
||||
&index);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (index == 0) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
entry = http_hpack_table_get(index);
|
||||
if (entry == NULL) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (entry->name == NULL || entry->value == NULL) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
header->name = entry->name;
|
||||
header->name_len = strlen(entry->name);
|
||||
header->value = entry->value;
|
||||
header->value_len = strlen(entry->value);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hpack_handle_literal(const uint8_t *buf, size_t datalen,
|
||||
struct http_hpack_header_buf *header,
|
||||
uint8_t prefix_len)
|
||||
{
|
||||
uint32_t index;
|
||||
int ret, len;
|
||||
|
||||
header->datalen = 0;
|
||||
|
||||
ret = hpack_integer_decode(buf, datalen, prefix_len, &index);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
len = ret;
|
||||
buf += ret;
|
||||
datalen -= ret;
|
||||
|
||||
if (index == 0) {
|
||||
/* Literal name. */
|
||||
ret = hpack_string_decode(buf, datalen, HPACK_HEADER_NAME,
|
||||
header);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
len += ret;
|
||||
buf += ret;
|
||||
datalen -= ret;
|
||||
} else {
|
||||
/* Indexed name. */
|
||||
const struct hpack_table_entry *entry;
|
||||
|
||||
entry = http_hpack_table_get(index);
|
||||
if (entry == NULL) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (entry->name == NULL) {
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
header->name = entry->name;
|
||||
header->name_len = strlen(entry->name);
|
||||
}
|
||||
|
||||
ret = hpack_string_decode(buf, datalen, HPACK_HEADER_VALUE, header);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
len += ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int hpack_handle_literal_index(const uint8_t *buf, size_t datalen,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
/* TODO Add dynamic table support, if needed. */
|
||||
|
||||
return hpack_handle_literal(buf, datalen, header,
|
||||
HPACK_PREFIX_LEN_LITERAL_INDEXING);
|
||||
}
|
||||
|
||||
static int hpack_handle_literal_no_index(const uint8_t *buf, size_t datalen,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
return hpack_handle_literal(buf, datalen, header,
|
||||
HPACK_PREFIX_LEN_LITERAL_NO_INDEXING);
|
||||
}
|
||||
|
||||
static int hpack_handle_dynamic_size_update(const uint8_t *buf, size_t datalen)
|
||||
{
|
||||
uint32_t max_size;
|
||||
int ret;
|
||||
|
||||
ret = hpack_integer_decode(
|
||||
buf, datalen, HPACK_PREFIX_LEN_DYNAMIC_TABLE_SIZE_UPDATE, &max_size);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO Add dynamic table support, if needed. */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int http_hpack_decode_header(const uint8_t *buf, size_t datalen,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
uint8_t prefix = *buf;
|
||||
int ret;
|
||||
|
||||
if (buf == NULL || header == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (datalen == 0) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if ((prefix & HPACK_PREFIX_INDEXED_MASK) == HPACK_PREFIX_INDEXED) {
|
||||
ret = hpack_handle_indexed(buf, datalen, header);
|
||||
} else if ((prefix & HPACK_PREFIX_LITERAL_INDEXING_MASK) ==
|
||||
HPACK_PREFIX_LITERAL_INDEXING) {
|
||||
ret = hpack_handle_literal_index(buf, datalen, header);
|
||||
} else if (((prefix & HPACK_PREFIX_LITERAL_NO_INDEXING_MASK) ==
|
||||
HPACK_PREFIX_LITERAL_NO_INDEXING) ||
|
||||
((prefix & HPACK_PREFIX_LITERAL_NEVER_INDEXED_MASK) ==
|
||||
HPACK_PREFIX_LITERAL_NEVER_INDEXED)) {
|
||||
ret = hpack_handle_literal_no_index(buf, datalen, header);
|
||||
} else if ((prefix & HPACK_PREFIX_DYNAMIC_TABLE_SIZE_MASK) ==
|
||||
HPACK_PREFIX_DYNAMIC_TABLE_SIZE_UPDATE) {
|
||||
ret = hpack_handle_dynamic_size_update(buf, datalen);
|
||||
} else {
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int hpack_integer_encode(uint8_t *buf, size_t buflen, int value,
|
||||
uint8_t prefix, uint8_t n)
|
||||
{
|
||||
uint8_t limit = (1 << n) - 1;
|
||||
int len = 0;
|
||||
|
||||
if (buflen == 0) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
/* Based on RFC7541, ch 5.1. */
|
||||
if (value < limit) {
|
||||
*buf = prefix | (uint8_t)value;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
*buf++ = prefix | limit;
|
||||
len++;
|
||||
value -= limit;
|
||||
|
||||
while (value >= 128) {
|
||||
if (len >= buflen) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
*buf = (uint8_t)((value % 128) + 128);
|
||||
len++;
|
||||
value /= 128;
|
||||
}
|
||||
|
||||
if (len >= buflen) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
*buf = (uint8_t)value;
|
||||
len++;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int hpack_string_encode(uint8_t *buf, size_t buflen,
|
||||
enum hpack_string_type type,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
int ret, len = 0;
|
||||
const char *str;
|
||||
size_t str_len;
|
||||
uint8_t prefix = 0;
|
||||
|
||||
if (type == HPACK_HEADER_NAME) {
|
||||
str = header->name;
|
||||
str_len = header->name_len;
|
||||
} else {
|
||||
str = header->value;
|
||||
str_len = header->value_len;
|
||||
}
|
||||
|
||||
/* Try to encode string into intermediate buffer. */
|
||||
ret = http_hpack_huffman_encode(str, str_len, header->buf,
|
||||
sizeof(header->buf));
|
||||
if (ret > 0 && ret < str_len) {
|
||||
/* Use Huffman encoded string only if smaller than the original. */
|
||||
str = header->buf;
|
||||
str_len = ret;
|
||||
prefix = HPACK_STRING_HUFFMAN_FLAG;
|
||||
}
|
||||
|
||||
/* Encode string length. */
|
||||
ret = hpack_integer_encode(buf, buflen, str_len, prefix,
|
||||
HPACK_STRING_PREFIX_LEN);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
len += ret;
|
||||
|
||||
/* Copy string. */
|
||||
if (str_len > buflen) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
memcpy(buf, str, str_len);
|
||||
len += str_len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int hpack_encode_literal(uint8_t *buf, size_t buflen,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
int ret, len = 0;
|
||||
|
||||
ret = hpack_integer_encode(buf, buflen, 0,
|
||||
HPACK_PREFIX_LITERAL_NEVER_INDEXED,
|
||||
HPACK_PREFIX_LEN_LITERAL_NEVER_INDEXED);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
len += ret;
|
||||
|
||||
ret = hpack_string_encode(buf, buflen, HPACK_HEADER_NAME, header);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
len += ret;
|
||||
|
||||
ret = hpack_string_encode(buf, buflen, HPACK_HEADER_VALUE, header);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
len += ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int hpack_encode_literal_value(uint8_t *buf, size_t buflen, int index,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
int ret, len = 0;
|
||||
|
||||
ret = hpack_integer_encode(buf, buflen, index,
|
||||
HPACK_PREFIX_LITERAL_NEVER_INDEXED,
|
||||
HPACK_PREFIX_LEN_LITERAL_NEVER_INDEXED);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf += ret;
|
||||
buflen -= ret;
|
||||
len += ret;
|
||||
|
||||
ret = hpack_string_encode(buf, buflen, HPACK_HEADER_VALUE, header);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
len += ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int hpack_encode_indexed(uint8_t *buf, size_t buflen, int index)
|
||||
{
|
||||
return hpack_integer_encode(buf, buflen, index, HPACK_PREFIX_INDEXED,
|
||||
HPACK_PREFIX_LEN_INDEXED);
|
||||
}
|
||||
|
||||
int http_hpack_encode_header(uint8_t *buf, size_t buflen,
|
||||
struct http_hpack_header_buf *header)
|
||||
{
|
||||
int ret, len = 0;
|
||||
bool name_only;
|
||||
|
||||
if (buf == NULL || header == NULL ||
|
||||
header->name == NULL || header->name_len == 0 ||
|
||||
header->value == NULL || header->value_len == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (buflen == 0) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
ret = http_hpack_find_index(header, &name_only);
|
||||
if (ret < 0) {
|
||||
/* All literal */
|
||||
len = hpack_encode_literal(buf, buflen, header);
|
||||
} else if (name_only) {
|
||||
/* Literal value */
|
||||
len = hpack_encode_literal_value(buf, buflen, ret, header);
|
||||
} else {
|
||||
/* Indexed */
|
||||
len = hpack_encode_indexed(buf, buflen, ret);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
480
subsys/net/lib/http/http_huffman.c
Normal file
480
subsys/net/lib/http/http_huffman.c
Normal file
|
@ -0,0 +1,480 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
|
||||
LOG_MODULE_DECLARE(net_http_server, CONFIG_NET_HTTP_SERVER_LOG_LEVEL);
|
||||
|
||||
struct decode_elem {
|
||||
uint8_t bitlen;
|
||||
uint8_t symbol;
|
||||
uint8_t code[4];
|
||||
};
|
||||
|
||||
static const struct decode_elem decode_table[] = {
|
||||
{ 5, 48, { 0b00000000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 5, 49, { 0b00001000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 5, 50, { 0b00010000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 5, 97, { 0b00011000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 5, 99, { 0b00100000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 5, 101, { 0b00101000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 5, 105, { 0b00110000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 5, 111, { 0b00111000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 5, 115, { 0b01000000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 5, 116, { 0b01001000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 32, { 0b01010000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 37, { 0b01010100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 45, { 0b01011000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 46, { 0b01011100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 47, { 0b01100000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 51, { 0b01100100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 52, { 0b01101000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 53, { 0b01101100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 54, { 0b01110000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 55, { 0b01110100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 56, { 0b01111000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 57, { 0b01111100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 61, { 0b10000000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 65, { 0b10000100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 95, { 0b10001000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 98, { 0b10001100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 100, { 0b10010000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 102, { 0b10010100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 103, { 0b10011000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 104, { 0b10011100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 108, { 0b10100000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 109, { 0b10100100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 110, { 0b10101000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 112, { 0b10101100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 114, { 0b10110000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 6, 117, { 0b10110100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 58, { 0b10111000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 66, { 0b10111010, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 67, { 0b10111100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 68, { 0b10111110, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 69, { 0b11000000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 70, { 0b11000010, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 71, { 0b11000100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 72, { 0b11000110, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 73, { 0b11001000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 74, { 0b11001010, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 75, { 0b11001100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 76, { 0b11001110, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 77, { 0b11010000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 78, { 0b11010010, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 79, { 0b11010100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 80, { 0b11010110, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 81, { 0b11011000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 82, { 0b11011010, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 83, { 0b11011100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 84, { 0b11011110, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 85, { 0b11100000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 86, { 0b11100010, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 87, { 0b11100100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 89, { 0b11100110, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 106, { 0b11101000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 107, { 0b11101010, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 113, { 0b11101100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 118, { 0b11101110, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 119, { 0b11110000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 120, { 0b11110010, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 121, { 0b11110100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 7, 122, { 0b11110110, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 8, 38, { 0b11111000, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 8, 42, { 0b11111001, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 8, 44, { 0b11111010, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 8, 59, { 0b11111011, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 8, 88, { 0b11111100, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 8, 90, { 0b11111101, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 10, 33, { 0b11111110, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 10, 34, { 0b11111110, 0b01000000, 0b00000000, 0b00000000 } },
|
||||
{ 10, 40, { 0b11111110, 0b10000000, 0b00000000, 0b00000000 } },
|
||||
{ 10, 41, { 0b11111110, 0b11000000, 0b00000000, 0b00000000 } },
|
||||
{ 10, 63, { 0b11111111, 0b00000000, 0b00000000, 0b00000000 } },
|
||||
{ 11, 39, { 0b11111111, 0b01000000, 0b00000000, 0b00000000 } },
|
||||
{ 11, 43, { 0b11111111, 0b01100000, 0b00000000, 0b00000000 } },
|
||||
{ 11, 124, { 0b11111111, 0b10000000, 0b00000000, 0b00000000 } },
|
||||
{ 12, 35, { 0b11111111, 0b10100000, 0b00000000, 0b00000000 } },
|
||||
{ 12, 62, { 0b11111111, 0b10110000, 0b00000000, 0b00000000 } },
|
||||
{ 13, 0, { 0b11111111, 0b11000000, 0b00000000, 0b00000000 } },
|
||||
{ 13, 36, { 0b11111111, 0b11001000, 0b00000000, 0b00000000 } },
|
||||
{ 13, 64, { 0b11111111, 0b11010000, 0b00000000, 0b00000000 } },
|
||||
{ 13, 91, { 0b11111111, 0b11011000, 0b00000000, 0b00000000 } },
|
||||
{ 13, 93, { 0b11111111, 0b11100000, 0b00000000, 0b00000000 } },
|
||||
{ 13, 126, { 0b11111111, 0b11101000, 0b00000000, 0b00000000 } },
|
||||
{ 14, 94, { 0b11111111, 0b11110000, 0b00000000, 0b00000000 } },
|
||||
{ 14, 125, { 0b11111111, 0b11110100, 0b00000000, 0b00000000 } },
|
||||
{ 15, 60, { 0b11111111, 0b11111000, 0b00000000, 0b00000000 } },
|
||||
{ 15, 96, { 0b11111111, 0b11111010, 0b00000000, 0b00000000 } },
|
||||
{ 15, 123, { 0b11111111, 0b11111100, 0b00000000, 0b00000000 } },
|
||||
{ 19, 92, { 0b11111111, 0b11111110, 0b00000000, 0b00000000 } },
|
||||
{ 19, 195, { 0b11111111, 0b11111110, 0b00100000, 0b00000000 } },
|
||||
{ 19, 208, { 0b11111111, 0b11111110, 0b01000000, 0b00000000 } },
|
||||
{ 20, 128, { 0b11111111, 0b11111110, 0b01100000, 0b00000000 } },
|
||||
{ 20, 130, { 0b11111111, 0b11111110, 0b01110000, 0b00000000 } },
|
||||
{ 20, 131, { 0b11111111, 0b11111110, 0b10000000, 0b00000000 } },
|
||||
{ 20, 162, { 0b11111111, 0b11111110, 0b10010000, 0b00000000 } },
|
||||
{ 20, 184, { 0b11111111, 0b11111110, 0b10100000, 0b00000000 } },
|
||||
{ 20, 194, { 0b11111111, 0b11111110, 0b10110000, 0b00000000 } },
|
||||
{ 20, 224, { 0b11111111, 0b11111110, 0b11000000, 0b00000000 } },
|
||||
{ 20, 226, { 0b11111111, 0b11111110, 0b11010000, 0b00000000 } },
|
||||
{ 21, 153, { 0b11111111, 0b11111110, 0b11100000, 0b00000000 } },
|
||||
{ 21, 161, { 0b11111111, 0b11111110, 0b11101000, 0b00000000 } },
|
||||
{ 21, 167, { 0b11111111, 0b11111110, 0b11110000, 0b00000000 } },
|
||||
{ 21, 172, { 0b11111111, 0b11111110, 0b11111000, 0b00000000 } },
|
||||
{ 21, 176, { 0b11111111, 0b11111111, 0b00000000, 0b00000000 } },
|
||||
{ 21, 177, { 0b11111111, 0b11111111, 0b00001000, 0b00000000 } },
|
||||
{ 21, 179, { 0b11111111, 0b11111111, 0b00010000, 0b00000000 } },
|
||||
{ 21, 209, { 0b11111111, 0b11111111, 0b00011000, 0b00000000 } },
|
||||
{ 21, 216, { 0b11111111, 0b11111111, 0b00100000, 0b00000000 } },
|
||||
{ 21, 217, { 0b11111111, 0b11111111, 0b00101000, 0b00000000 } },
|
||||
{ 21, 227, { 0b11111111, 0b11111111, 0b00110000, 0b00000000 } },
|
||||
{ 21, 229, { 0b11111111, 0b11111111, 0b00111000, 0b00000000 } },
|
||||
{ 21, 230, { 0b11111111, 0b11111111, 0b01000000, 0b00000000 } },
|
||||
{ 22, 129, { 0b11111111, 0b11111111, 0b01001000, 0b00000000 } },
|
||||
{ 22, 132, { 0b11111111, 0b11111111, 0b01001100, 0b00000000 } },
|
||||
{ 22, 133, { 0b11111111, 0b11111111, 0b01010000, 0b00000000 } },
|
||||
{ 22, 134, { 0b11111111, 0b11111111, 0b01010100, 0b00000000 } },
|
||||
{ 22, 136, { 0b11111111, 0b11111111, 0b01011000, 0b00000000 } },
|
||||
{ 22, 146, { 0b11111111, 0b11111111, 0b01011100, 0b00000000 } },
|
||||
{ 22, 154, { 0b11111111, 0b11111111, 0b01100000, 0b00000000 } },
|
||||
{ 22, 156, { 0b11111111, 0b11111111, 0b01100100, 0b00000000 } },
|
||||
{ 22, 160, { 0b11111111, 0b11111111, 0b01101000, 0b00000000 } },
|
||||
{ 22, 163, { 0b11111111, 0b11111111, 0b01101100, 0b00000000 } },
|
||||
{ 22, 164, { 0b11111111, 0b11111111, 0b01110000, 0b00000000 } },
|
||||
{ 22, 169, { 0b11111111, 0b11111111, 0b01110100, 0b00000000 } },
|
||||
{ 22, 170, { 0b11111111, 0b11111111, 0b01111000, 0b00000000 } },
|
||||
{ 22, 173, { 0b11111111, 0b11111111, 0b01111100, 0b00000000 } },
|
||||
{ 22, 178, { 0b11111111, 0b11111111, 0b10000000, 0b00000000 } },
|
||||
{ 22, 181, { 0b11111111, 0b11111111, 0b10000100, 0b00000000 } },
|
||||
{ 22, 185, { 0b11111111, 0b11111111, 0b10001000, 0b00000000 } },
|
||||
{ 22, 186, { 0b11111111, 0b11111111, 0b10001100, 0b00000000 } },
|
||||
{ 22, 187, { 0b11111111, 0b11111111, 0b10010000, 0b00000000 } },
|
||||
{ 22, 189, { 0b11111111, 0b11111111, 0b10010100, 0b00000000 } },
|
||||
{ 22, 190, { 0b11111111, 0b11111111, 0b10011000, 0b00000000 } },
|
||||
{ 22, 196, { 0b11111111, 0b11111111, 0b10011100, 0b00000000 } },
|
||||
{ 22, 198, { 0b11111111, 0b11111111, 0b10100000, 0b00000000 } },
|
||||
{ 22, 228, { 0b11111111, 0b11111111, 0b10100100, 0b00000000 } },
|
||||
{ 22, 232, { 0b11111111, 0b11111111, 0b10101000, 0b00000000 } },
|
||||
{ 22, 233, { 0b11111111, 0b11111111, 0b10101100, 0b00000000 } },
|
||||
{ 23, 1, { 0b11111111, 0b11111111, 0b10110000, 0b00000000 } },
|
||||
{ 23, 135, { 0b11111111, 0b11111111, 0b10110010, 0b00000000 } },
|
||||
{ 23, 137, { 0b11111111, 0b11111111, 0b10110100, 0b00000000 } },
|
||||
{ 23, 138, { 0b11111111, 0b11111111, 0b10110110, 0b00000000 } },
|
||||
{ 23, 139, { 0b11111111, 0b11111111, 0b10111000, 0b00000000 } },
|
||||
{ 23, 140, { 0b11111111, 0b11111111, 0b10111010, 0b00000000 } },
|
||||
{ 23, 141, { 0b11111111, 0b11111111, 0b10111100, 0b00000000 } },
|
||||
{ 23, 143, { 0b11111111, 0b11111111, 0b10111110, 0b00000000 } },
|
||||
{ 23, 147, { 0b11111111, 0b11111111, 0b11000000, 0b00000000 } },
|
||||
{ 23, 149, { 0b11111111, 0b11111111, 0b11000010, 0b00000000 } },
|
||||
{ 23, 150, { 0b11111111, 0b11111111, 0b11000100, 0b00000000 } },
|
||||
{ 23, 151, { 0b11111111, 0b11111111, 0b11000110, 0b00000000 } },
|
||||
{ 23, 152, { 0b11111111, 0b11111111, 0b11001000, 0b00000000 } },
|
||||
{ 23, 155, { 0b11111111, 0b11111111, 0b11001010, 0b00000000 } },
|
||||
{ 23, 157, { 0b11111111, 0b11111111, 0b11001100, 0b00000000 } },
|
||||
{ 23, 158, { 0b11111111, 0b11111111, 0b11001110, 0b00000000 } },
|
||||
{ 23, 165, { 0b11111111, 0b11111111, 0b11010000, 0b00000000 } },
|
||||
{ 23, 166, { 0b11111111, 0b11111111, 0b11010010, 0b00000000 } },
|
||||
{ 23, 168, { 0b11111111, 0b11111111, 0b11010100, 0b00000000 } },
|
||||
{ 23, 174, { 0b11111111, 0b11111111, 0b11010110, 0b00000000 } },
|
||||
{ 23, 175, { 0b11111111, 0b11111111, 0b11011000, 0b00000000 } },
|
||||
{ 23, 180, { 0b11111111, 0b11111111, 0b11011010, 0b00000000 } },
|
||||
{ 23, 182, { 0b11111111, 0b11111111, 0b11011100, 0b00000000 } },
|
||||
{ 23, 183, { 0b11111111, 0b11111111, 0b11011110, 0b00000000 } },
|
||||
{ 23, 188, { 0b11111111, 0b11111111, 0b11100000, 0b00000000 } },
|
||||
{ 23, 191, { 0b11111111, 0b11111111, 0b11100010, 0b00000000 } },
|
||||
{ 23, 197, { 0b11111111, 0b11111111, 0b11100100, 0b00000000 } },
|
||||
{ 23, 231, { 0b11111111, 0b11111111, 0b11100110, 0b00000000 } },
|
||||
{ 23, 239, { 0b11111111, 0b11111111, 0b11101000, 0b00000000 } },
|
||||
{ 24, 9, { 0b11111111, 0b11111111, 0b11101010, 0b00000000 } },
|
||||
{ 24, 142, { 0b11111111, 0b11111111, 0b11101011, 0b00000000 } },
|
||||
{ 24, 144, { 0b11111111, 0b11111111, 0b11101100, 0b00000000 } },
|
||||
{ 24, 145, { 0b11111111, 0b11111111, 0b11101101, 0b00000000 } },
|
||||
{ 24, 148, { 0b11111111, 0b11111111, 0b11101110, 0b00000000 } },
|
||||
{ 24, 159, { 0b11111111, 0b11111111, 0b11101111, 0b00000000 } },
|
||||
{ 24, 171, { 0b11111111, 0b11111111, 0b11110000, 0b00000000 } },
|
||||
{ 24, 206, { 0b11111111, 0b11111111, 0b11110001, 0b00000000 } },
|
||||
{ 24, 215, { 0b11111111, 0b11111111, 0b11110010, 0b00000000 } },
|
||||
{ 24, 225, { 0b11111111, 0b11111111, 0b11110011, 0b00000000 } },
|
||||
{ 24, 236, { 0b11111111, 0b11111111, 0b11110100, 0b00000000 } },
|
||||
{ 24, 237, { 0b11111111, 0b11111111, 0b11110101, 0b00000000 } },
|
||||
{ 25, 199, { 0b11111111, 0b11111111, 0b11110110, 0b00000000 } },
|
||||
{ 25, 207, { 0b11111111, 0b11111111, 0b11110110, 0b10000000 } },
|
||||
{ 25, 234, { 0b11111111, 0b11111111, 0b11110111, 0b00000000 } },
|
||||
{ 25, 235, { 0b11111111, 0b11111111, 0b11110111, 0b10000000 } },
|
||||
{ 26, 192, { 0b11111111, 0b11111111, 0b11111000, 0b00000000 } },
|
||||
{ 26, 193, { 0b11111111, 0b11111111, 0b11111000, 0b01000000 } },
|
||||
{ 26, 200, { 0b11111111, 0b11111111, 0b11111000, 0b10000000 } },
|
||||
{ 26, 201, { 0b11111111, 0b11111111, 0b11111000, 0b11000000 } },
|
||||
{ 26, 202, { 0b11111111, 0b11111111, 0b11111001, 0b00000000 } },
|
||||
{ 26, 205, { 0b11111111, 0b11111111, 0b11111001, 0b01000000 } },
|
||||
{ 26, 210, { 0b11111111, 0b11111111, 0b11111001, 0b10000000 } },
|
||||
{ 26, 213, { 0b11111111, 0b11111111, 0b11111001, 0b11000000 } },
|
||||
{ 26, 218, { 0b11111111, 0b11111111, 0b11111010, 0b00000000 } },
|
||||
{ 26, 219, { 0b11111111, 0b11111111, 0b11111010, 0b01000000 } },
|
||||
{ 26, 238, { 0b11111111, 0b11111111, 0b11111010, 0b10000000 } },
|
||||
{ 26, 240, { 0b11111111, 0b11111111, 0b11111010, 0b11000000 } },
|
||||
{ 26, 242, { 0b11111111, 0b11111111, 0b11111011, 0b00000000 } },
|
||||
{ 26, 243, { 0b11111111, 0b11111111, 0b11111011, 0b01000000 } },
|
||||
{ 26, 255, { 0b11111111, 0b11111111, 0b11111011, 0b10000000 } },
|
||||
{ 27, 203, { 0b11111111, 0b11111111, 0b11111011, 0b11000000 } },
|
||||
{ 27, 204, { 0b11111111, 0b11111111, 0b11111011, 0b11100000 } },
|
||||
{ 27, 211, { 0b11111111, 0b11111111, 0b11111100, 0b00000000 } },
|
||||
{ 27, 212, { 0b11111111, 0b11111111, 0b11111100, 0b00100000 } },
|
||||
{ 27, 214, { 0b11111111, 0b11111111, 0b11111100, 0b01000000 } },
|
||||
{ 27, 221, { 0b11111111, 0b11111111, 0b11111100, 0b01100000 } },
|
||||
{ 27, 222, { 0b11111111, 0b11111111, 0b11111100, 0b10000000 } },
|
||||
{ 27, 223, { 0b11111111, 0b11111111, 0b11111100, 0b10100000 } },
|
||||
{ 27, 241, { 0b11111111, 0b11111111, 0b11111100, 0b11000000 } },
|
||||
{ 27, 244, { 0b11111111, 0b11111111, 0b11111100, 0b11100000 } },
|
||||
{ 27, 245, { 0b11111111, 0b11111111, 0b11111101, 0b00000000 } },
|
||||
{ 27, 246, { 0b11111111, 0b11111111, 0b11111101, 0b00100000 } },
|
||||
{ 27, 247, { 0b11111111, 0b11111111, 0b11111101, 0b01000000 } },
|
||||
{ 27, 248, { 0b11111111, 0b11111111, 0b11111101, 0b01100000 } },
|
||||
{ 27, 250, { 0b11111111, 0b11111111, 0b11111101, 0b10000000 } },
|
||||
{ 27, 251, { 0b11111111, 0b11111111, 0b11111101, 0b10100000 } },
|
||||
{ 27, 252, { 0b11111111, 0b11111111, 0b11111101, 0b11000000 } },
|
||||
{ 27, 253, { 0b11111111, 0b11111111, 0b11111101, 0b11100000 } },
|
||||
{ 27, 254, { 0b11111111, 0b11111111, 0b11111110, 0b00000000 } },
|
||||
{ 28, 2, { 0b11111111, 0b11111111, 0b11111110, 0b00100000 } },
|
||||
{ 28, 3, { 0b11111111, 0b11111111, 0b11111110, 0b00110000 } },
|
||||
{ 28, 4, { 0b11111111, 0b11111111, 0b11111110, 0b01000000 } },
|
||||
{ 28, 5, { 0b11111111, 0b11111111, 0b11111110, 0b01010000 } },
|
||||
{ 28, 6, { 0b11111111, 0b11111111, 0b11111110, 0b01100000 } },
|
||||
{ 28, 7, { 0b11111111, 0b11111111, 0b11111110, 0b01110000 } },
|
||||
{ 28, 8, { 0b11111111, 0b11111111, 0b11111110, 0b10000000 } },
|
||||
{ 28, 11, { 0b11111111, 0b11111111, 0b11111110, 0b10010000 } },
|
||||
{ 28, 12, { 0b11111111, 0b11111111, 0b11111110, 0b10100000 } },
|
||||
{ 28, 14, { 0b11111111, 0b11111111, 0b11111110, 0b10110000 } },
|
||||
{ 28, 15, { 0b11111111, 0b11111111, 0b11111110, 0b11000000 } },
|
||||
{ 28, 16, { 0b11111111, 0b11111111, 0b11111110, 0b11010000 } },
|
||||
{ 28, 17, { 0b11111111, 0b11111111, 0b11111110, 0b11100000 } },
|
||||
{ 28, 18, { 0b11111111, 0b11111111, 0b11111110, 0b11110000 } },
|
||||
{ 28, 19, { 0b11111111, 0b11111111, 0b11111111, 0b00000000 } },
|
||||
{ 28, 20, { 0b11111111, 0b11111111, 0b11111111, 0b00010000 } },
|
||||
{ 28, 21, { 0b11111111, 0b11111111, 0b11111111, 0b00100000 } },
|
||||
{ 28, 23, { 0b11111111, 0b11111111, 0b11111111, 0b00110000 } },
|
||||
{ 28, 24, { 0b11111111, 0b11111111, 0b11111111, 0b01000000 } },
|
||||
{ 28, 25, { 0b11111111, 0b11111111, 0b11111111, 0b01010000 } },
|
||||
{ 28, 26, { 0b11111111, 0b11111111, 0b11111111, 0b01100000 } },
|
||||
{ 28, 27, { 0b11111111, 0b11111111, 0b11111111, 0b01110000 } },
|
||||
{ 28, 28, { 0b11111111, 0b11111111, 0b11111111, 0b10000000 } },
|
||||
{ 28, 29, { 0b11111111, 0b11111111, 0b11111111, 0b10010000 } },
|
||||
{ 28, 30, { 0b11111111, 0b11111111, 0b11111111, 0b10100000 } },
|
||||
{ 28, 31, { 0b11111111, 0b11111111, 0b11111111, 0b10110000 } },
|
||||
{ 28, 127, { 0b11111111, 0b11111111, 0b11111111, 0b11000000 } },
|
||||
{ 28, 220, { 0b11111111, 0b11111111, 0b11111111, 0b11010000 } },
|
||||
{ 28, 249, { 0b11111111, 0b11111111, 0b11111111, 0b11100000 } },
|
||||
{ 30, 10, { 0b11111111, 0b11111111, 0b11111111, 0b11110000 } },
|
||||
{ 30, 13, { 0b11111111, 0b11111111, 0b11111111, 0b11110100 } },
|
||||
{ 30, 22, { 0b11111111, 0b11111111, 0b11111111, 0b11111000 } },
|
||||
};
|
||||
|
||||
static const struct decode_elem eos = {
|
||||
30, 0, { 0b11111111, 0b11111111, 0b11111111, 0b11111100 }
|
||||
};
|
||||
|
||||
#define UINT32_BITLEN 32
|
||||
|
||||
#define MSB_MASK(len) (UINT32_MAX << (UINT32_BITLEN - len))
|
||||
#define LSB_MASK(len) ((1UL << len) - 1UL)
|
||||
|
||||
static bool huffman_bits_compare(uint32_t bits, const struct decode_elem *entry)
|
||||
{
|
||||
uint32_t mask = MSB_MASK(entry->bitlen);
|
||||
uint32_t code = sys_get_be32(entry->code);
|
||||
|
||||
if (code == (bits & mask)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct decode_elem *huffman_decode_bits(uint32_t bits)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(decode_table); i++) {
|
||||
if (huffman_bits_compare(bits, &decode_table[i])) {
|
||||
return &decode_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (huffman_bits_compare(bits, &eos)) {
|
||||
return &eos;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct decode_elem *huffman_find_entry(uint8_t symbol)
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(decode_table); i++) {
|
||||
if (decode_table[i].symbol == symbol) {
|
||||
return &decode_table[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define MAX_PADDING_LEN 7
|
||||
|
||||
int http_hpack_huffman_decode(const uint8_t *encoded_buf, size_t encoded_len,
|
||||
uint8_t *buf, size_t buflen)
|
||||
{
|
||||
size_t encoded_bits_len = encoded_len * 8;
|
||||
uint8_t bits_needed = UINT32_BITLEN;
|
||||
const struct decode_elem *decoded;
|
||||
uint8_t bits_in_byte_left = 8;
|
||||
size_t decoded_len = 0;
|
||||
uint32_t bits = 0;
|
||||
|
||||
if (encoded_buf == NULL || buf == NULL || encoded_len == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (encoded_bits_len > 0) {
|
||||
/* Refill the bits variable */
|
||||
while (bits_needed > 0) {
|
||||
if (encoded_len > 0) {
|
||||
if (bits_in_byte_left <= bits_needed) {
|
||||
/* Consume rest of the byte */
|
||||
bits <<= bits_in_byte_left;
|
||||
bits |= *encoded_buf &
|
||||
LSB_MASK(bits_in_byte_left);
|
||||
bits_needed -= bits_in_byte_left;
|
||||
bits_in_byte_left = 0;
|
||||
} else {
|
||||
/* Consume part of the byte */
|
||||
bits <<= bits_needed;
|
||||
bits |= (*encoded_buf >>
|
||||
(bits_in_byte_left - bits_needed)) &
|
||||
LSB_MASK(bits_needed);
|
||||
bits_in_byte_left -= bits_needed;
|
||||
bits_needed = 0;
|
||||
}
|
||||
} else {
|
||||
/* Pad with ones */
|
||||
bits <<= bits_needed;
|
||||
bits |= LSB_MASK(bits_needed);
|
||||
bits_needed = 0;
|
||||
}
|
||||
|
||||
/* Move to next encoded byte */
|
||||
if (bits_in_byte_left == 0) {
|
||||
encoded_buf++;
|
||||
encoded_len--;
|
||||
bits_in_byte_left = 8;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass to decoder */
|
||||
decoded = huffman_decode_bits(bits);
|
||||
if (decoded == NULL) {
|
||||
LOG_ERR("No symbol found");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
if (decoded == &eos) {
|
||||
if (encoded_bits_len > MAX_PADDING_LEN) {
|
||||
LOG_ERR("eos reached prematurely");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (encoded_bits_len < decoded->bitlen) {
|
||||
LOG_ERR("Invalid symbol used for padding");
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
/* Remove consumed bits from bits variable. */
|
||||
bits_needed += decoded->bitlen;
|
||||
encoded_bits_len -= decoded->bitlen;
|
||||
|
||||
/* Store decoded symbol */
|
||||
if (buflen == 0) {
|
||||
LOG_ERR("Not enough buffer to decode string");
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
*buf = decoded->symbol;
|
||||
buf++;
|
||||
buflen--;
|
||||
decoded_len++;
|
||||
}
|
||||
|
||||
return decoded_len;
|
||||
}
|
||||
|
||||
int http_hpack_huffman_encode(const uint8_t *str, size_t str_len,
|
||||
uint8_t *buf, size_t buflen)
|
||||
{
|
||||
const struct decode_elem *entry;
|
||||
size_t buflen_bits = buflen * 8;
|
||||
uint8_t bit_offset = 0;
|
||||
int len = 0;
|
||||
|
||||
if (str == NULL || buf == NULL || str_len == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
while (str_len > 0) {
|
||||
uint32_t code;
|
||||
uint8_t bitlen;
|
||||
|
||||
entry = huffman_find_entry(*str);
|
||||
if (entry == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (entry->bitlen > buflen_bits) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
bitlen = entry->bitlen;
|
||||
code = sys_get_be32(entry->code);
|
||||
|
||||
while (bitlen > 0) {
|
||||
uint8_t to_copy = MIN(8 - bit_offset, bitlen);
|
||||
uint8_t byte = (uint8_t)((code & MSB_MASK(to_copy)) >>
|
||||
(24 + bit_offset));
|
||||
|
||||
/* This is way suboptimal */
|
||||
if (bit_offset == 0) {
|
||||
*buf = byte;
|
||||
} else {
|
||||
*buf |= byte;
|
||||
}
|
||||
|
||||
code <<= to_copy;
|
||||
bitlen -= to_copy;
|
||||
bit_offset = (bit_offset + to_copy) % 8;
|
||||
|
||||
if (bit_offset == 0) {
|
||||
buf++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
buflen_bits -= entry->bitlen;
|
||||
str_len--;
|
||||
str++;
|
||||
}
|
||||
|
||||
/* Pad with ones. */
|
||||
if (bit_offset > 0) {
|
||||
*buf |= LSB_MASK((8 - bit_offset));
|
||||
len++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
Loading…
Reference in a new issue