-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit fe19a04
Showing
22 changed files
with
2,913 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
BasedOnStyle: Google | ||
ColumnLimit : 110 | ||
AllowShortFunctionsOnASingleLine: false | ||
AllowShortLambdasOnASingleLine: true | ||
AllowShortIfStatementsOnASingleLine: false | ||
AllowShortLoopsOnASingleLine: false | ||
SortIncludes: false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# These are supported funding model platforms | ||
|
||
github: [kelbon] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
name: build_and_test | ||
|
||
on: push | ||
|
||
jobs: | ||
build: | ||
strategy: | ||
matrix: | ||
os: [ubuntu-22.04] | ||
compiler: [g++-12, clang++-14] | ||
cpp_standard: [20] | ||
build_type: [Debug, Release] | ||
runs-on: ${{matrix.os}} | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
|
||
- name: Install dependencies | ||
run: | | ||
sudo apt-get update | ||
sudo apt-get install ninja-build lld gcc-12 clang-14 | ||
sudo ln -sf /usr/local/bin/ld /usr/bin/lld | ||
- name: Configure CMake | ||
run: | | ||
cmake . -DHPACK_ENABLE_TESTING=ON \ | ||
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \ | ||
-DCMAKE_CXX_COMPILER=${{matrix.compiler}} \ | ||
-DCMAKE_CXX_STANDARD=${{matrix.cpp_standard}} \ | ||
-B build -G "Ninja" | ||
- name: Build | ||
run: | ||
cmake --build build | ||
|
||
- name: Test | ||
run: | | ||
cd build | ||
ctest --output-on-failure -C ${{matrix.build_type}} -V |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/build* | ||
/.cache | ||
/.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
cmake_minimum_required(VERSION 3.05) | ||
project(HPACK LANGUAGES CXX) | ||
|
||
### options ### | ||
|
||
option(HPACK_ENABLE_TESTING "enables testing" OFF) | ||
|
||
### dependecies ### | ||
|
||
include(cmake/get_cpm.cmake) | ||
|
||
set(BOOST_INCLUDE_LIBRARIES intrusive) | ||
CPMAddPackage( | ||
NAME Boost | ||
VERSION 1.84.0 | ||
URL https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.xz | ||
OPTIONS "BOOST_ENABLE_CMAKE ON" | ||
) | ||
unset(BOOST_INCLUDE_LIBRARIES) | ||
find_package(Boost 1.84 COMPONENTS intrusive REQUIRED) | ||
|
||
### hpacklib ### | ||
|
||
add_library(hpacklib STATIC | ||
"${CMAKE_CURRENT_SOURCE_DIR}/src/decoder.cpp" | ||
"${CMAKE_CURRENT_SOURCE_DIR}/src/dynamic_table.cpp" | ||
"${CMAKE_CURRENT_SOURCE_DIR}/src/huffman.cpp" | ||
"${CMAKE_CURRENT_SOURCE_DIR}/src/static_table.cpp") | ||
|
||
target_include_directories(hpacklib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") | ||
|
||
target_link_libraries(hpacklib PUBLIC Boost::intrusive) | ||
|
||
set_target_properties(hpacklib PROPERTIES | ||
CMAKE_CXX_EXTENSIONS OFF | ||
LINKER_LANGUAGE CXX | ||
CMAKE_CXX_STANDARD_REQUIRED ON | ||
CXX_STANDARD 20) | ||
|
||
if(HPACK_ENABLE_TESTING) | ||
include(CTest) | ||
add_subdirectory(tests) | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# SPDX-License-Identifier: MIT | ||
# | ||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors | ||
|
||
set(CPM_DOWNLOAD_VERSION 0.40.2) | ||
set(CPM_HASH_SUM "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d") | ||
|
||
if(CPM_SOURCE_CACHE) | ||
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") | ||
elseif(DEFINED ENV{CPM_SOURCE_CACHE}) | ||
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") | ||
else() | ||
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") | ||
endif() | ||
|
||
# Expand relative path. This is important if the provided path contains a tilde (~) | ||
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) | ||
|
||
file(DOWNLOAD | ||
https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake | ||
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} | ||
) | ||
|
||
include(${CPM_DOWNLOAD_LOCATION}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
#pragma once | ||
|
||
#include <cstdint> | ||
#include <iterator> | ||
#include <string_view> | ||
|
||
#ifndef KELBON_HPACK_HANDLE_PROTOCOL_ERROR | ||
|
||
#include <exception> | ||
|
||
namespace hpack { | ||
struct protocol_error : std::exception {}; | ||
} // namespace hpack | ||
|
||
#define KELBON_HPACK_HANDLE_PROTOCOL_ERROR \ | ||
throw ::hpack::protocol_error { \ | ||
} | ||
#endif | ||
|
||
namespace hpack { | ||
|
||
struct sym_info_t { | ||
uint32_t bits; | ||
uint8_t bit_count; | ||
}; | ||
extern const sym_info_t huffman_table[257]; | ||
|
||
// uint16_t(-1) if not found | ||
uint16_t huffman_decode_table_find(sym_info_t info); | ||
|
||
// integer/string len | ||
using size_type = uint32_t; | ||
// header index | ||
using index_type = uint32_t; | ||
using byte_t = unsigned char; | ||
using In = const byte_t*; | ||
|
||
template <typename T> | ||
concept Out = std::output_iterator<T, byte_t>; | ||
|
||
[[noreturn]] inline void handle_protocol_error() { | ||
KELBON_HPACK_HANDLE_PROTOCOL_ERROR; | ||
} | ||
[[noreturn]] inline void handle_size_error() { | ||
KELBON_HPACK_HANDLE_PROTOCOL_ERROR; | ||
} | ||
|
||
namespace noexport { | ||
|
||
template <typename T> | ||
struct adapted_output_iterator { | ||
T base_it; | ||
mutable byte_t byte = 0; | ||
|
||
using iterator_category = std::output_iterator_tag; | ||
using value_type = byte_t; | ||
using difference_type = std::ptrdiff_t; | ||
|
||
constexpr value_type& operator*() const noexcept { | ||
return byte; | ||
} | ||
constexpr adapted_output_iterator& operator++() { | ||
*base_it = byte; | ||
++base_it; | ||
return *this; | ||
} | ||
constexpr adapted_output_iterator operator++(int) { | ||
auto cpy = *this; | ||
++(*this); | ||
return cpy; | ||
} | ||
}; | ||
|
||
template <Out O> | ||
auto adapt_output_iterator(O it) { | ||
return adapted_output_iterator<O>(it); | ||
} | ||
template <typename T> | ||
auto adapt_output_iterator(adapted_output_iterator<T> it) { | ||
return it; | ||
} | ||
|
||
inline byte_t* adapt_output_iterator(byte_t* ptr) { | ||
return ptr; | ||
} | ||
inline byte_t* adapt_output_iterator(std::byte* ptr) { | ||
return reinterpret_cast<byte_t*>(ptr); | ||
} | ||
inline byte_t* adapt_output_iterator(char* ptr) { | ||
return reinterpret_cast<byte_t*>(ptr); | ||
} | ||
|
||
template <typename Original, typename T> | ||
Original unadapt(adapted_output_iterator<T> it) { | ||
return Original(std::move(it.base_it)); | ||
} | ||
|
||
template <typename Original> | ||
Original unadapt(byte_t* ptr) { | ||
static_assert(std::is_pointer_v<Original>); | ||
return reinterpret_cast<Original>(ptr); | ||
} | ||
|
||
} // namespace noexport | ||
|
||
struct table_entry { | ||
std::string_view name; // empty if not found | ||
std::string_view value; // empty if no | ||
|
||
constexpr explicit operator bool() const noexcept { | ||
return !name.empty(); | ||
} | ||
auto operator<=>(const table_entry&) const = default; | ||
}; | ||
|
||
struct find_result_t { | ||
// not found by default | ||
index_type header_name_index = 0; | ||
bool value_indexed = false; | ||
|
||
constexpr explicit operator bool() const noexcept { | ||
return header_name_index != 0; | ||
} | ||
}; | ||
|
||
} // namespace hpack |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
#pragma once | ||
|
||
#include "hpack/basic_types.hpp" | ||
#include "hpack/dynamic_table.hpp" | ||
|
||
namespace hpack { | ||
|
||
struct decoded_string { | ||
private: | ||
const char* data = nullptr; | ||
size_type sz = 0; | ||
uint8_t allocated_sz_log2 = 0; // != 0 after decoding huffman str | ||
|
||
friend void decode_string(In&, In, decoded_string&); | ||
|
||
void set_huffman(const char* ptr, size_type len); | ||
|
||
public: | ||
decoded_string() = default; | ||
|
||
decoded_string(const char* ptr, size_type len, bool is_huffman_encoded) : data(ptr), sz(len) { | ||
if (!is_huffman_encoded) | ||
return; | ||
set_huffman(ptr, len); | ||
} | ||
|
||
// precondition: str.size() less then max of size_type | ||
decoded_string(std::string_view str, bool is_huffman_encoded) | ||
: decoded_string(str.data(), str.size(), is_huffman_encoded) { | ||
assert(std::in_range<size_type>(str.size())); | ||
} | ||
|
||
decoded_string(decoded_string&& other) noexcept { | ||
swap(other); | ||
} | ||
|
||
decoded_string& operator=(decoded_string&& other) noexcept { | ||
swap(other); | ||
return *this; | ||
} | ||
|
||
// not huffman encoded string | ||
decoded_string& operator=(std::string_view str) noexcept { | ||
assert(std::in_range<size_type>(str.size())); | ||
reset(); | ||
data = str.data(); | ||
sz = str.size(); | ||
return *this; | ||
} | ||
|
||
void swap(decoded_string& other) noexcept { | ||
std::swap(data, other.data); | ||
std::swap(sz, other.sz); | ||
std::swap(allocated_sz_log2, other.allocated_sz_log2); | ||
} | ||
|
||
friend void swap(decoded_string& l, decoded_string& r) noexcept { | ||
l.swap(r); | ||
} | ||
|
||
~decoded_string() { | ||
reset(); | ||
} | ||
|
||
void reset() noexcept { | ||
if (allocated_sz_log2) | ||
free((void*)data); | ||
data = nullptr; | ||
sz = 0; | ||
allocated_sz_log2 = 0; | ||
} | ||
|
||
[[nodiscard]] size_t bytes_allocated() const noexcept { | ||
if (!allocated_sz_log2) | ||
return 0; | ||
return 1 << allocated_sz_log2; | ||
} | ||
|
||
[[nodiscard]] std::string_view str() const noexcept { | ||
return std::string_view(data, sz); | ||
} | ||
|
||
// true if not empty | ||
explicit operator bool() const noexcept { | ||
return sz != 0; | ||
} | ||
|
||
bool operator==(const decoded_string& other) const noexcept { | ||
return str() == other.str(); | ||
} | ||
bool operator==(std::string_view other) const noexcept { | ||
return str() == other; | ||
} | ||
|
||
std::strong_ordering operator<=>(const decoded_string& other) const noexcept { | ||
return str() <=> other.str(); | ||
} | ||
|
||
std::strong_ordering operator<=>(std::string_view other) const noexcept { | ||
return str() <=> other; | ||
} | ||
}; | ||
|
||
// note: decoding next header invalidates previous header | ||
struct header_view { | ||
decoded_string name; | ||
decoded_string value; | ||
|
||
// header may be not present if default contructed or table_size_update happen instead of header | ||
explicit operator bool() const noexcept { | ||
return name || value; | ||
} | ||
|
||
header_view& operator=(header_view&&) = default; | ||
header_view& operator=(table_entry entry) { | ||
name = entry.name; | ||
value = entry.value; | ||
return *this; | ||
} | ||
}; | ||
|
||
void decode_string(In& in, In e, decoded_string& out); | ||
|
||
struct decoder { | ||
dynamic_table_t dyntab; | ||
|
||
// 4096 - default size in HTTP/2 | ||
explicit decoder(size_type max_dyntab_size = 4096, | ||
std::pmr::memory_resource* resource = std::pmr::get_default_resource()) | ||
: dyntab(max_dyntab_size, resource) { | ||
} | ||
|
||
decoder(decoder&&) = default; | ||
decoder& operator=(decoder&&) noexcept = default; | ||
/* | ||
Note: this function ignores special 'cookie' header case | ||
https://www.rfc-editor.org/rfc/rfc7540#section-8.1.2.5 | ||
and protocol error if decoded header name is not lowercase | ||
*/ | ||
void decode_header(In& in, In e, header_view& out); | ||
|
||
// returns status code | ||
int decode_response_status(In& in, In e); | ||
}; | ||
|
||
} // namespace hpack |
Oops, something went wrong.