Skip to content
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
67 changes: 67 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Coverage
on:
push:
branches:
- '**'
pull_request:
branches:
- '**'

jobs:
build-test:
runs-on: ubuntu-24.04
name: ubuntu-coverage
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt update
sudo apt install -y ninja-build cmake clang llvm

- name: Run CMake configuration and build
run: |
cmake examples -B build -G Ninja \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
-DHTTP_BUILD_FUZZERS=ON
cmake --build build --target request_parser response_parser websocket_parser sha1 base64 --parallel

- name: Extract corpus
run: |
tar -zxvf examples/fuzz/seeds.tgz

- name: Run fuzzers
run: |
mkdir -p /tmp/corpus
LLVM_PROFILE_FILE="request.profraw" ./build/request_parser /tmp/corpus/ seeds/request_parser/ -max_total_time=30
LLVM_PROFILE_FILE="response.profraw" ./build/response_parser /tmp/corpus/ seeds/response_parser/ -max_total_time=30
LLVM_PROFILE_FILE="websocket.profraw" ./build/websocket_parser /tmp/corpus/ seeds/websocket_parser/ -max_total_time=30
LLVM_PROFILE_FILE="sha1.profraw" ./build/sha1 -max_total_time=30
LLVM_PROFILE_FILE="base64.profraw" ./build/base64 -max_total_time=30

- name: Generate coverage report
run: |
llvm-profdata merge -o coverage.profdata \
request.profraw \
response.profraw \
websocket.profraw \
sha1.profraw \
base64.profraw
llvm-cov export -format=lcov -instr-profile coverage.profdata \
-object ./build/request_parser \
-object ./build/response_parser \
-object ./build/websocket_parser \
-object ./build/sha1 \
-object ./build/base64 \
-sources ./src \
> coverage_${{github.sha}}.txt

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: pfeatherstone/https
files: coverage_${{github.sha}}.txt
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
**/build
**/.vscode
**/.vscode
**/*.profraw
**/*.profdata
**/seeds/**
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
![Ubuntu](https://github.com/pfeatherstone/https/actions/workflows/ubuntu.yml/badge.svg)
![MacOS](https://github.com/pfeatherstone/https/actions/workflows/macos.yml/badge.svg)
![Windows](https://github.com/pfeatherstone/https/actions/workflows/windows.yml/badge.svg)
[![codecov](https://codecov.io/gh/pfeatherstone/https/branch/main/graph/badge.svg)](https://codecov.io/gh/pfeatherstone/https)

# https

Expand Down
56 changes: 40 additions & 16 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
cmake_minimum_required(VERSION 3.13)
project(Http)

# Policies
if(POLICY CMP0127)
cmake_policy(SET CMP0127 NEW)
endif()
if(POLICY CMP0135)
cmake_policy(SET CMP0135 OLD)
endif()

# Deps
include(FetchContent)
include(CMakeDependentOption)
find_package(Threads REQUIRED)
find_package(OpenSSL REQUIRED)

if(POLICY CMP0135)
cmake_policy(SET CMP0135 OLD)
endif()
set(BOOST_INCLUDE_LIBRARIES compat asio)
set(BOOST_ENABLE_CMAKE ON)
FetchContent_Declare(
Expand All @@ -17,30 +23,32 @@ FetchContent_Declare(
URL_HASH MD5=3edffaacd2cfe63c240ef1b99497c74f)
FetchContent_MakeAvailable(Boost)

# Options
cmake_dependent_option(HTTP_BUILD_FUZZERS "Builds Fuzz tests" OFF "CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\"" OFF)

# Lib
add_library(http ${CMAKE_CURRENT_SOURCE_DIR}/../src/http.cpp)
target_compile_features(http PUBLIC cxx_std_17)
target_include_directories(http PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../src)
target_link_libraries(http PUBLIC OpenSSL::SSL OpenSSL::Crypto Boost::asio)
target_compile_options(http PRIVATE $<$<BOOL:${HTTP_BUILD_FUZZERS}>:-fprofile-instr-generate -fcoverage-mapping>)

# Examples
function(add_executable_20 target_name)
add_executable(${target_name} ${ARGN})
target_compile_features(${target_name} PUBLIC cxx_std_20)
target_link_options(${target_name} PUBLIC $<$<CONFIG:Release>:-s>)
target_link_libraries(${target_name} PUBLIC Threads::Threads http)
endfunction()

function(add_executable_coro target_name)
function(add_example target_name)
add_executable(${target_name} ${ARGN})
target_link_options(${target_name} PRIVATE $<$<CONFIG:Release>:-s>)
target_link_libraries(${target_name} PRIVATE Threads::Threads Boost::compat http)
endfunction()

add_executable_20(server ${CMAKE_CURRENT_SOURCE_DIR}/server.cpp)
add_executable_20(client_http ${CMAKE_CURRENT_SOURCE_DIR}/client_http.cpp ${CMAKE_CURRENT_SOURCE_DIR}/extra/yyjson.c)
add_executable_20(client_ws_awaitable ${CMAKE_CURRENT_SOURCE_DIR}/client_ws_awaitable.cpp)
add_executable_coro(client_ws_coro ${CMAKE_CURRENT_SOURCE_DIR}/client_ws_coro.cpp)
function(add_example_20 target_name)
add_example(${target_name} ${ARGN})
target_compile_features(${target_name} PUBLIC cxx_std_20)
endfunction()

add_example_20(server ${CMAKE_CURRENT_SOURCE_DIR}/server.cpp)
add_example_20(client_http ${CMAKE_CURRENT_SOURCE_DIR}/client_http.cpp ${CMAKE_CURRENT_SOURCE_DIR}/extra/yyjson.c)
add_example_20(client_ws_awaitable ${CMAKE_CURRENT_SOURCE_DIR}/client_ws_awaitable.cpp)
add_example(client_ws_coro ${CMAKE_CURRENT_SOURCE_DIR}/client_ws_coro.cpp)

# Unit tests
add_executable(tests
Expand All @@ -54,4 +62,20 @@ add_executable(tests
target_compile_features(tests PUBLIC cxx_std_20)
target_link_options(tests PRIVATE $<$<CONFIG:Release>:-s>)
target_include_directories(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/extra)
target_link_libraries(tests PRIVATE http)
target_link_libraries(tests PRIVATE http)

# Fuzz tests
if (HTTP_BUILD_FUZZERS)
function(add_fuzz target_name)
add_executable(${target_name} ${ARGN})
target_link_libraries(${target_name} PRIVATE http)
target_compile_options(${target_name} PRIVATE -fprofile-instr-generate -fcoverage-mapping)
target_link_options(${target_name} PRIVATE -fsanitize=fuzzer -fprofile-instr-generate -fcoverage-mapping)
endfunction()

add_fuzz(request_parser fuzz/request_parser.cpp)
add_fuzz(response_parser fuzz/response_parser.cpp)
add_fuzz(websocket_parser fuzz/websocket_parser.cpp)
add_fuzz(sha1 fuzz/sha1.cpp)
add_fuzz(base64 fuzz/base64.cpp)
endif()
13 changes: 13 additions & 0 deletions examples/fuzz/base64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include <cstdint>
#include <cstddef>
#include <algorithm>
#include <http.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
auto encoded = http::base64_encode(size, data);
auto decoded = http::base64_decode(encoded);
encoded.erase(std::remove(begin(encoded), end(encoded), '='), end(encoded));
auto decoded2 = http::base64_decode(encoded);
return 0;
}
22 changes: 22 additions & 0 deletions examples/fuzz/request_parser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <cstdint>
#include <cstddef>
#include <http.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::string buf(reinterpret_cast<const char*>(data), size);
http::request req{};
std::error_code ec{};
http::parser_request parser;
parser.parse(req, buf, ec);
bool is_keep_alive = req.keep_alive();
bool is_websocket_req = req.is_websocket_req();
std::string ec_msg = ec ? ec.message() : "";
const auto& ec_cat = ec.category();
buf.clear();
ec = {};
http::serialize_header(req, buf, ec);
req.clear();
ec_msg = ec ? ec.message() : "";
return 0;
}
22 changes: 22 additions & 0 deletions examples/fuzz/response_parser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <cstdint>
#include <cstddef>
#include <http.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::string buf(reinterpret_cast<const char*>(data), size);
http::response resp{};
std::error_code ec{};
http::parser_response parser;
parser.parse(resp, buf, ec);
buf.clear();
std::string ec_msg = ec ? ec.message() : "";
const auto& ec_cat = ec.category();
ec = {};
http::serialize_header(resp, buf, ec);
resp.keep_alive(true);
bool is_websocket = resp.is_websocket_response();
resp.clear();
ec_msg = ec ? ec.message() : "";
return 0;
}
Binary file added examples/fuzz/seeds.tgz
Binary file not shown.
9 changes: 9 additions & 0 deletions examples/fuzz/sha1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <cstdint>
#include <cstddef>
#include <http.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
auto sum = http::sha1{}.push(size, data).finish();
return 0;
}
40 changes: 40 additions & 0 deletions examples/fuzz/websocket_parser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include <cstdint>
#include <cstddef>
#include <http.h>

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
{
std::string buf(reinterpret_cast<const char*>(data), size);

http::request req{};
std::error_code ec{};
http::parser_request parser;
parser.parse(req, buf, ec);
bool is_websocket_req = req.is_websocket_req();
std::string ec_msg = ec ? ec.message() : "";
const auto& ec_cat = ec.category();

std::vector<char> msg;
http::dynamic_buffer view(msg);
http::websocket_parser ws_parser;
ws_parser.parse(view, buf, ec);
const auto opcode = ws_parser.get_opcode();
const auto is_server = ws_parser.is_server();
const auto current_size = view.size();
const auto current_data = view.data();
const auto current_buf = view.buffer();
ec_msg = ec ? ec.message() : "";

view.resize(10);
view.clear();
msg.assign(data, data+size);
buf.clear();
http::serialize_websocket_message(boost::asio::buffer(msg), http::WS_OPCODE_DATA_BINARY, true, buf);
buf.clear();
http::serialize_websocket_message(boost::asio::buffer(msg), http::WS_OPCODE_DATA_BINARY, false, buf);
buf.clear();
http::serialize_websocket_message(boost::asio::buffer(msg), http::WS_OPCODE_DATA_TEXT, true, buf);
buf.clear();
http::serialize_websocket_message(boost::asio::buffer(msg), http::WS_OPCODE_DATA_TEXT, false, buf);
return 0;
}
Loading
Loading