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
87 changes: 87 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,90 @@ jobs:
- name: Run benchmark
if: matrix.build_type == 'Release'
run: ./build/examples/benchmark

package-consumer-check:
runs-on: ubuntu-24.04
name: package-consumer-check

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
cmake \
ninja-build \
liburing-dev \
libssl-dev \
libnghttp2-dev \
g++-14

- name: Configure CMake (installable build)
run: |
cmake -B build -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER=gcc-14 \
-DCMAKE_CXX_COMPILER=g++-14 \
-DELIO_BUILD_TESTS=OFF \
-DELIO_BUILD_EXAMPLES=OFF \
-DELIO_ENABLE_TLS=ON \
-DELIO_ENABLE_HTTP=ON \
-DELIO_ENABLE_HTTP2=ON

- name: Build and install
run: |
cmake --build build --parallel
cmake --install build --prefix ${{ github.workspace }}/_install

- name: Verify downstream consumption
run: |
mkdir -p consumer
cat > consumer/CMakeLists.txt << 'EOF'
cmake_minimum_required(VERSION 3.20)
project(ElioConsumer LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)

find_package(Elio REQUIRED)

add_executable(consumer main.cpp)
target_link_libraries(consumer PRIVATE Elio::elio)

if(TARGET Elio::elio_tls)
target_link_libraries(consumer PRIVATE Elio::elio_tls)
target_compile_definitions(consumer PRIVATE ELIO_HAS_TLS_TARGET=1)
endif()

if(TARGET Elio::elio_http)
target_link_libraries(consumer PRIVATE Elio::elio_http)
target_compile_definitions(consumer PRIVATE ELIO_HAS_HTTP_TARGET=1)
endif()

if(TARGET Elio::elio_http2)
target_link_libraries(consumer PRIVATE Elio::elio_http2)
target_compile_definitions(consumer PRIVATE ELIO_HAS_HTTP2_TARGET=1)
endif()
EOF

cat > consumer/main.cpp << 'EOF'
#include <elio/elio.hpp>
#if defined(ELIO_HAS_TLS_TARGET)
#include <elio/tls/tls.hpp>
#endif
#if defined(ELIO_HAS_HTTP_TARGET)
#include <elio/http/http.hpp>
#endif
#if defined(ELIO_HAS_HTTP2_TARGET)
#include <elio/http/http2.hpp>
#endif

int main() {
return 0;
}
EOF

cmake -S consumer -B consumer/build -G Ninja \
-DCMAKE_CXX_COMPILER=g++-14 \
-DCMAKE_PREFIX_PATH=${{ github.workspace }}/_install
cmake --build consumer/build --parallel
62 changes: 51 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.20)
project(Elio VERSION 0.3.0 LANGUAGES CXX)

include(CMakePackageConfigHelpers)

# Determine if Elio is the top-level project or included via add_subdirectory/FetchContent
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
set(ELIO_IS_TOP_LEVEL TRUE)
Expand All @@ -14,6 +16,8 @@ option(ELIO_BUILD_EXAMPLES "Build Elio examples" ${ELIO_IS_TOP_LEVEL})
option(ELIO_ENABLE_TLS "Enable TLS support (requires OpenSSL)" ${ELIO_IS_TOP_LEVEL})
option(ELIO_ENABLE_HTTP "Enable HTTP client/server support (requires TLS)" ${ELIO_IS_TOP_LEVEL})
option(ELIO_ENABLE_HTTP2 "Enable HTTP/2 support (requires nghttp2)" ${ELIO_IS_TOP_LEVEL})
option(ELIO_ENABLE_DEVELOPER_WARNINGS "Enable strict warning flags for Elio tests/examples" ${ELIO_IS_TOP_LEVEL})
option(ELIO_WARNINGS_AS_ERRORS "Treat warnings as errors for Elio tests/examples" ${ELIO_IS_TOP_LEVEL})

# Platform check - Linux only
if(NOT UNIX OR APPLE)
Expand All @@ -35,14 +39,6 @@ target_include_directories(elio INTERFACE
$<INSTALL_INTERFACE:include>
)

# Compiler flags for our code only (not dependencies)
target_compile_options(elio INTERFACE
-Wall
-Wextra
-Werror
-Wpedantic
)

# Dependencies via FetchContent
include(FetchContent)

Expand Down Expand Up @@ -70,7 +66,7 @@ if(URING_LIBRARY AND URING_INCLUDE_DIR)
message(STATUS "Found liburing: ${URING_LIBRARY}")
target_compile_definitions(elio INTERFACE ELIO_HAS_IO_URING=1)
target_include_directories(elio INTERFACE ${URING_INCLUDE_DIR})
target_link_libraries(elio INTERFACE fmt::fmt pthread ${URING_LIBRARY})
target_link_libraries(elio INTERFACE fmt::fmt pthread uring)
else()
message(STATUS "liburing not found, io_uring backend will be disabled")
target_compile_definitions(elio INTERFACE ELIO_HAS_IO_URING=0)
Expand Down Expand Up @@ -139,7 +135,11 @@ if(ELIO_ENABLE_HTTP AND TARGET elio_tls)
$<BUILD_INTERFACE:${nghttp2_BINARY_DIR}/lib/includes>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(elio_http2 INTERFACE elio_http nghttp2_static)
target_link_libraries(elio_http2 INTERFACE
elio_http
$<BUILD_INTERFACE:nghttp2_static>
$<INSTALL_INTERFACE:nghttp2>
)
target_compile_definitions(elio_http2 INTERFACE ELIO_HAS_HTTP2=1)
message(STATUS "HTTP/2 support enabled via nghttp2")
endif()
Expand All @@ -162,13 +162,53 @@ endif()

# Installation
install(DIRECTORY include/ DESTINATION include)
install(TARGETS elio EXPORT ElioTargets)

set(ELIO_EXPORT_TARGETS elio)
if(TARGET elio_tls)
list(APPEND ELIO_EXPORT_TARGETS elio_tls)
endif()
if(TARGET elio_http)
list(APPEND ELIO_EXPORT_TARGETS elio_http)
endif()
if(TARGET elio_http2)
list(APPEND ELIO_EXPORT_TARGETS elio_http2)
endif()

set(ELIO_PACKAGE_NEEDS_OPENSSL OFF)
if(TARGET elio_tls)
set(ELIO_PACKAGE_NEEDS_OPENSSL ON)
endif()

set(ELIO_PACKAGE_NEEDS_NGHTTP2 OFF)
if(TARGET elio_http2)
set(ELIO_PACKAGE_NEEDS_NGHTTP2 ON)
endif()

install(TARGETS ${ELIO_EXPORT_TARGETS} EXPORT ElioTargets)
install(EXPORT ElioTargets
FILE ElioTargets.cmake
NAMESPACE Elio::
DESTINATION lib/cmake/Elio
)

configure_package_config_file(
${CMAKE_CURRENT_SOURCE_DIR}/cmake/ElioConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/ElioConfig.cmake
INSTALL_DESTINATION lib/cmake/Elio
)

write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/ElioConfigVersion.cmake
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMajorVersion
)

install(FILES
${CMAKE_CURRENT_BINARY_DIR}/ElioConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/ElioConfigVersion.cmake
DESTINATION lib/cmake/Elio
)

# Install debug tools
install(PROGRAMS
tools/elio-pstack
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,37 @@ cmake ..
cmake --build .
```

### CMake Options

```bash
# Core toggles
cmake .. -DELIO_BUILD_TESTS=ON -DELIO_BUILD_EXAMPLES=ON
cmake .. -DELIO_ENABLE_TLS=ON -DELIO_ENABLE_HTTP=ON -DELIO_ENABLE_HTTP2=ON

# Warning policy for repository-local targets (tests/examples only)
cmake .. -DELIO_ENABLE_DEVELOPER_WARNINGS=ON -DELIO_WARNINGS_AS_ERRORS=ON
```

Note: strict warning flags are applied only to Elio's tests/examples targets and are not propagated through exported interface targets.

### Install And Use As A Package

```bash
cmake --install build --prefix /your/prefix
```

Then in another CMake project:

```cmake
find_package(Elio REQUIRED)
target_link_libraries(your_target PRIVATE Elio::elio)

# Optional targets when enabled during Elio build
# Elio::elio_tls
# Elio::elio_http
# Elio::elio_http2
```

### Running Tests

```bash
Expand Down
18 changes: 18 additions & 0 deletions cmake/ElioConfig.cmake.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
@PACKAGE_INIT@

include(CMakeFindDependencyMacro)

find_dependency(fmt REQUIRED)

if(@ELIO_PACKAGE_NEEDS_OPENSSL@)
find_dependency(OpenSSL REQUIRED)
endif()

if(@ELIO_PACKAGE_NEEDS_NGHTTP2@)
find_library(ELIO_NGHTTP2_LIBRARY nghttp2)
if(NOT ELIO_NGHTTP2_LIBRARY)
set(ELIO_NGHTTP2_LIBRARY nghttp2)
endif()
endif()

include(${CMAKE_CURRENT_LIST_DIR}/ElioTargets.cmake)
7 changes: 7 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Example programs

if(ELIO_ENABLE_DEVELOPER_WARNINGS)
add_compile_options(-Wall -Wextra -Wpedantic)
if(ELIO_WARNINGS_AS_ERRORS)
add_compile_options(-Werror)
endif()
endif()

# Static linking for libgcc and libstdc++ for portability
set(STATIC_LINK_FLAGS -static-libgcc -static-libstdc++)

Expand Down
11 changes: 8 additions & 3 deletions include/elio/http/websocket_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,11 @@ class ws_client {

// Process control frames
while (parser_.has_control_frame()) {
auto [op, payload] = *parser_.get_control_frame();
co_await handle_control_frame(op, payload);
auto control_frame = parser_.get_control_frame();
if (!control_frame) {
break;
}
co_await handle_control_frame(control_frame->first, control_frame->second);
}

// Check for errors
Expand Down Expand Up @@ -459,7 +462,9 @@ class ws_client {
break;

case opcode::close: {
auto [code, reason] = parse_close_payload(payload);
auto close_info = parse_close_payload(payload);
auto code = close_info.first;
auto& reason = close_info.second;
ELIO_LOG_DEBUG("WebSocket close received: {} {}",
static_cast<uint16_t>(code), reason);

Expand Down
11 changes: 8 additions & 3 deletions include/elio/http/websocket_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,11 @@ class ws_connection {

// Process control frames
while (parser_.has_control_frame()) {
auto [op, payload] = *parser_.get_control_frame();
co_await handle_control_frame(op, payload);
auto control_frame = parser_.get_control_frame();
if (!control_frame) {
break;
}
co_await handle_control_frame(control_frame->first, control_frame->second);
}

// Check for errors
Expand Down Expand Up @@ -251,7 +254,9 @@ class ws_connection {
break;

case opcode::close: {
auto [code, reason] = parse_close_payload(payload);
auto close_info = parse_close_payload(payload);
auto code = close_info.first;
auto& reason = close_info.second;
ELIO_LOG_DEBUG("WebSocket close received: {} {}",
static_cast<uint16_t>(code), reason);

Expand Down
8 changes: 4 additions & 4 deletions include/elio/rpc/rpc_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,13 +231,13 @@ class rpc_client : public std::enable_shared_from_this<rpc_client<Stream>> {
// Build and send request
auto timeout_ms = static_cast<uint32_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count());
auto [header, payload] = build_request(request_id, Method::id, request, timeout_ms);
auto request_frame = build_request(request_id, Method::id, request, timeout_ms);

{
co_await send_mutex_.lock();
sync::lock_guard send_guard(send_mutex_);

bool sent = co_await write_frame(stream_, header, payload);
bool sent = co_await write_frame(stream_, request_frame.first, request_frame.second);
if (!sent) {
std::lock_guard<std::mutex> lock(pending_mutex_);
pending_requests_.erase(request_id);
Expand Down Expand Up @@ -314,12 +314,12 @@ class rpc_client : public std::enable_shared_from_this<rpc_client<Stream>> {
}

uint32_t request_id = id_generator_.next();
auto [header, payload] = build_request(request_id, Method::id, request);
auto request_frame = build_request(request_id, Method::id, request);

co_await send_mutex_.lock();
sync::lock_guard send_guard(send_mutex_);

co_return co_await write_frame(stream_, header, payload);
co_return co_await write_frame(stream_, request_frame.first, request_frame.second);
}

/// Send a ping and wait for pong
Expand Down
8 changes: 4 additions & 4 deletions include/elio/rpc/rpc_server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,12 @@ class rpc_session : public std::enable_shared_from_this<rpc_session<Stream>> {
auto it = handlers_.find(header.method_id);
if (it == handlers_.end()) {
ELIO_LOG_WARNING("RPC session: method {} not found", header.method_id);
auto [err_header, err_payload] = build_error_response(
auto error_frame = build_error_response(
header.request_id,
rpc_error::method_not_found,
"Method not found"
);
co_await send_response(err_header, err_payload);
co_await send_response(error_frame.first, error_frame.second);
co_return;
}

Expand Down Expand Up @@ -218,12 +218,12 @@ class rpc_session : public std::enable_shared_from_this<rpc_session<Stream>> {

co_await send_response(resp_header, response_payload);
} else {
auto [err_header, err_payload] = build_error_response(
auto error_frame = build_error_response(
header.request_id,
error_code,
error_message
);
co_await send_response(err_header, err_payload);
co_await send_response(error_frame.first, error_frame.second);
}

// Invoke cleanup callback after response is sent
Expand Down
7 changes: 7 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# Test sources
if(ELIO_ENABLE_DEVELOPER_WARNINGS)
add_compile_options(-Wall -Wextra -Wpedantic)
if(ELIO_WARNINGS_AS_ERRORS)
add_compile_options(-Werror)
endif()
endif()

set(TEST_SOURCES
test_main.cpp
unit/test_logger.cpp
Expand Down
Loading