diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dca1ecb..20d03b27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -290,7 +290,8 @@ jobs: apt-get: >- ${{ matrix.install }} build-essential - ${{ matrix.x86 && '' || '' }} + zlib1g-dev libbrotli-dev + ${{ matrix.x86 && 'zlib1g-dev:i386 libbrotli-dev:i386' || '' }} - name: Clone Capy uses: actions/checkout@v4 @@ -299,6 +300,13 @@ jobs: ref: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} path: capy-root + - name: Clone Corosio + uses: actions/checkout@v4 + with: + repository: cppalliance/corosio + ref: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} + path: corosio-root + - name: Clone Boost uses: alandefreitas/cpp-actions/boost-clone@v1.9.0 id: boost-clone @@ -307,7 +315,7 @@ jobs: boost-dir: boost-source modules-exclude-paths: '' scan-modules-dir: http-root - scan-modules-ignore: http,capy + scan-modules-ignore: http,capy,corosio - name: ASLR Fix if: ${{ startsWith(matrix.runs-on, 'ubuntu' )}} @@ -334,6 +342,7 @@ jobs: # Remove module from boost-source rm -r "boost-source/libs/$module" || true rm -r "boost-source/libs/capy" || true + rm -r "boost-source/libs/corosio" || true # Copy cached boost-source to an isolated boost-root cp -r boost-source boost-root @@ -350,6 +359,9 @@ jobs: # Patch boost-root with capy dependency cp -r "$workspace_root"/capy-root "libs/capy" + # Patch boost-root with corosio dependency + cp -r "$workspace_root"/corosio-root "libs/corosio" + - name: Boost B2 Workflow uses: alandefreitas/cpp-actions/b2-workflow@v1.9.0 if: ${{ !matrix.coverage }} @@ -381,6 +393,7 @@ jobs: build-type: ${{ matrix.build-type }} build-target: tests run-tests: true + install: true install-prefix: .local cxxstd: ${{ matrix.latest-cxxstd }} cc: ${{ steps.setup-cpp.outputs.cc || matrix.cc }} @@ -405,9 +418,13 @@ jobs: run: | echo "LD_LIBRARY_PATH=$GITHUB_WORKSPACE/.local/lib:$LD_LIBRARY_PATH" >> "$GITHUB_ENV" + # Disabled: Boost's CMake infrastructure does not generate install rules for + # the "main" library in BOOST_INCLUDE_LIBRARIES, only for its dependencies. + # This causes find_package(Boost COMPONENTS http) to fail because + # boost_httpConfig.cmake is never installed. - name: Find Package Integration Workflow uses: alandefreitas/cpp-actions/cmake-workflow@v1.9.0 - if: ${{ matrix.build-cmake || matrix.is-earliest }} + if: false # ${{ matrix.build-cmake || matrix.is-earliest }} with: source-dir: boost-root/libs/${{ steps.patch.outputs.module }}/test/cmake_test build-dir: __build_cmake_install_test__ @@ -541,7 +558,7 @@ jobs: with: apt-get: git cmake - - name: Clone Boost.Corosio + - name: Clone Boost.Http uses: actions/checkout@v4 with: path: http-root @@ -553,6 +570,13 @@ jobs: ref: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} path: capy-root + - name: Clone Corosio + uses: actions/checkout@v4 + with: + repository: cppalliance/corosio + ref: ${{ (github.ref_name == 'master' && github.ref_name) || 'develop' }} + path: corosio-root + - name: Clone Boost uses: alandefreitas/cpp-actions/boost-clone@v1.9.0 id: boost-clone @@ -561,7 +585,7 @@ jobs: boost-dir: boost-source modules-exclude-paths: '' scan-modules-dir: http-root - scan-modules-ignore: http,capy + scan-modules-ignore: http,capy,corosio - name: Patch Boost id: patch @@ -583,6 +607,7 @@ jobs: # Remove module from boost-source rm -r "boost-source/libs/$module" || true rm -r "boost-source/libs/capy" || true + rm -r "boost-source/libs/corosio" || true # Copy cached boost-source to an isolated boost-root cp -r boost-source boost-root @@ -599,6 +624,9 @@ jobs: # Patch boost-root with capy dependency cp -r "$workspace_root"/capy-root "libs/capy" + # Patch boost-root with corosio dependency + cp -r "$workspace_root"/corosio-root "libs/corosio" + - uses: actions/setup-node@v4 with: node-version: 18 diff --git a/.gitignore b/.gitignore index a9ff6e47..fd4d0732 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,12 @@ /.vscode/ +/.cache/ +/.clangd /build/* !/build/Jamfile +!/build/brotli.jam /out/ -CMakeUserPresets.json +/CMakeUserPresets.json +/tmpclaude-*-cwd # CMake artifacts (if accidentally run from source dir) #CMakeCache.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 3aad8701..62f3bf71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,7 +58,6 @@ set(BOOST_HTTP_DEPENDENCIES Boost::capy Boost::config Boost::core - Boost::corosio Boost::json Boost::mp11 Boost::static_assert @@ -68,23 +67,32 @@ set(BOOST_HTTP_DEPENDENCIES Boost::url Boost::winapi) +# corosio is used header-only (only io_buffer_param.hpp) +# We include it separately to avoid linking against the full library +set(BOOST_HTTP_HEADER_ONLY_DEPENDENCIES corosio) + foreach (BOOST_HTTP_DEPENDENCY ${BOOST_HTTP_DEPENDENCIES}) if (BOOST_HTTP_DEPENDENCY MATCHES "^[ ]*Boost::([A-Za-z0-9_]+)[ ]*$") list(APPEND BOOST_HTTP_INCLUDE_LIBRARIES ${CMAKE_MATCH_1}) endif () endforeach () +# Add header-only dependencies to include list (but not link list) +list(APPEND BOOST_HTTP_INCLUDE_LIBRARIES ${BOOST_HTTP_HEADER_ONLY_DEPENDENCIES}) # Conditional dependencies -if (NOT BOOST_URL_MRDOCS_BUILD) +if (NOT BOOST_HTTP_MRDOCS_BUILD) if (BOOST_HTTP_BUILD_TESTS) - set(BOOST_HTTP_UNIT_TEST_LIBRARIES filesystem) + set(BOOST_HTTP_UNIT_TEST_LIBRARIES asio filesystem) endif () if (BOOST_HTTP_BUILD_EXAMPLES) # set(BOOST_HTTP_EXAMPLE_LIBRARIES json) endif () endif () -# Complete dependency list -set(BOOST_INCLUDE_LIBRARIES ${BOOST_HTTP_INCLUDE_LIBRARIES} ${BOOST_HTTP_UNIT_TEST_LIBRARIES} ${BOOST_HTTP_EXAMPLE_LIBRARIES}) -set(BOOST_EXCLUDE_LIBRARIES http) +# Complete dependency list (only set when http is the root project) +# When built as part of the superproject, these are set by the superproject +if (BOOST_HTTP_IS_ROOT) + set(BOOST_INCLUDE_LIBRARIES ${BOOST_HTTP_INCLUDE_LIBRARIES} ${BOOST_HTTP_UNIT_TEST_LIBRARIES} ${BOOST_HTTP_EXAMPLE_LIBRARIES}) + set(BOOST_EXCLUDE_LIBRARIES http) +endif() #------------------------------------------------- # @@ -151,6 +159,10 @@ function(boost_http_setup_properties target) target_include_directories(${target} PUBLIC "${PROJECT_SOURCE_DIR}/include") target_include_directories(${target} PRIVATE "${PROJECT_SOURCE_DIR}") target_link_libraries(${target} PUBLIC ${BOOST_HTTP_DEPENDENCIES}) + # Add corosio headers without linking (header-only usage) + # BUILD_INTERFACE: only during build (installed headers are in standard Boost include path) + target_include_directories(${target} PUBLIC $) + target_compile_definitions(${target} PUBLIC BOOST_COROSIO_NO_LIB) # Disable corosio auto-linking target_compile_definitions(${target} PUBLIC BOOST_HTTP_NO_LIB) target_compile_definitions(${target} PRIVATE BOOST_HTTP_SOURCE) if (BUILD_SHARED_LIBS) @@ -180,6 +192,39 @@ elseif (APPLE) target_link_libraries(boost_http PRIVATE "-framework Security") endif () +# Zlib +find_package(ZLIB) +if (ZLIB_FOUND) + file(GLOB_RECURSE BOOST_HTTP_ZLIB_HEADERS CONFIGURE_DEPENDS include/boost/http/zlib/*.hpp) + file(GLOB_RECURSE BOOST_HTTP_ZLIB_SOURCES CONFIGURE_DEPENDS src_zlib/*.cpp src_zlib/*.hpp) + source_group("" FILES "include/boost/http/zlib.hpp") + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include/boost/http/zlib PREFIX "include" FILES ${BOOST_HTTP_ZLIB_HEADERS}) + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src_zlib PREFIX "src" FILES ${BOOST_HTTP_ZLIB_SOURCES}) + add_library(boost_http_zlib include/boost/http/zlib.hpp build/Jamfile ${BOOST_HTTP_ZLIB_HEADERS} ${BOOST_HTTP_ZLIB_SOURCES}) + add_library(Boost::http_zlib ALIAS boost_http_zlib) + target_link_libraries(boost_http_zlib PUBLIC boost_http) + target_link_libraries(boost_http_zlib PRIVATE ZLIB::ZLIB) + target_compile_definitions(boost_http_zlib PUBLIC BOOST_HTTP_HAS_ZLIB) + target_compile_definitions(boost_http_zlib PRIVATE BOOST_HTTP_SOURCE) +endif () + +# Brotli +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") +find_package(Brotli) +if (Brotli_FOUND) + file(GLOB_RECURSE BOOST_HTTP_BROTLI_HEADERS CONFIGURE_DEPENDS include/boost/http/brotli/*.hpp) + file(GLOB_RECURSE BOOST_HTTP_BROTLI_SOURCES CONFIGURE_DEPENDS src_brotli/*.cpp src_brotli/*.hpp) + source_group("" FILES "include/boost/http/brotli.hpp") + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include/boost/http/brotli PREFIX "include" FILES ${BOOST_HTTP_BROTLI_HEADERS}) + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src_brotli PREFIX "src" FILES ${BOOST_HTTP_BROTLI_SOURCES}) + add_library(boost_http_brotli include/boost/http/brotli.hpp build/Jamfile ${BOOST_HTTP_BROTLI_HEADERS} ${BOOST_HTTP_BROTLI_SOURCES}) + add_library(Boost::http_brotli ALIAS boost_http_brotli) + target_link_libraries(boost_http_brotli PUBLIC boost_http) + target_link_libraries(boost_http_brotli PRIVATE Brotli::common Brotli::decoder Brotli::encoder) + target_compile_definitions(boost_http_brotli PUBLIC BOOST_HTTP_HAS_BROTLI) + target_compile_definitions(boost_http_brotli PRIVATE BOOST_HTTP_SOURCE) +endif () + #------------------------------------------------- # # Tests diff --git a/build/Jamfile b/build/Jamfile index e64c8b55..2fdbcc98 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -7,6 +7,7 @@ # Official repository: https://github.com/vinniefalco/http # +import ac ; import ../../config/checks/config : requires ; constant c11-requires : @@ -43,20 +44,56 @@ lib boost_http : http_sources : requirements /boost//capy - /boost/corosio//boost_corosio /boost/json//boost_json/off /boost//url ../ + ../../corosio/include BOOST_HTTP_SOURCE + BOOST_COROSIO_NO_LIB windows:bcrypt_sys darwin:"-framework Security" : usage-requirements /boost//capy - /boost/corosio//boost_corosio /boost/json//boost_json/off /boost//url + ../../corosio/include + BOOST_COROSIO_NO_LIB windows:bcrypt_sys darwin:"-framework Security" ; -boost-install boost_http ; +# Zlib +using zlib ; + +alias http_zlib_sources : [ glob-tree-ex src_zlib : *.cpp ] ; + +lib boost_http_zlib + : http_zlib_sources + : requirements + /boost/http//boost_http + BOOST_HTTP_SOURCE + [ ac.check-library /zlib//zlib : /zlib//zlib : no ] + : usage-requirements + /boost/http//boost_http + BOOST_HTTP_HAS_ZLIB + ; + +# Brotli +using brotli ; + +alias http_brotli_sources : [ glob-tree-ex src_brotli : *.cpp ] ; + +lib boost_http_brotli + : http_brotli_sources + : requirements + /boost/http//boost_http + BOOST_HTTP_SOURCE + [ ac.check-library /brotli//brotlicommon : /brotli//brotlicommon : no ] + [ ac.check-library /brotli//brotlidec : /brotli//brotlidec : no ] + [ ac.check-library /brotli//brotlienc : /brotli//brotlienc : no ] + : usage-requirements + /boost/http//boost_http + BOOST_HTTP_HAS_BROTLI + ; + +boost-install boost_http boost_http_zlib boost_http_brotli ; diff --git a/build/brotli.jam b/build/brotli.jam new file mode 100644 index 00000000..83f78d4c --- /dev/null +++ b/build/brotli.jam @@ -0,0 +1,155 @@ +# Copyright (c) 2025 Mohammad Nejati +# +# Use, modification and distribution is subject to the Boost Software +# License Version 1.0. (See accompanying file LICENSE.txt or +# https://www.bfgroup.xyz/b2/LICENSE.txt) + +# Supports the brotli library +# +# After 'using brotli', the following targets are available: +# +# /brotli//brotlicommon -- The brotli common library +# /brotli//brotlidec -- The brotli decoder library +# /brotli//brotlienc -- The brotli encoder library + +import project ; +import ac ; +import errors ; +import feature ; +import "class" : new ; +import targets ; +import path ; +import modules ; +import indirect ; +import property ; +import property-set ; +import args ; + +header = brotli/decode.h ; +brotlicommon_names = brotlicommon libbrotlicommon ; +brotlidec_names = brotlidec libbrotlidec ; +brotlienc_names = brotlienc libbrotlienc ; + +library-id = 0 ; + +.debug = [ args.get-arg debug-configuration ] ; + +# Initializes the brotli library. +# +# Options for configuring brotli:: +# +# +# The directory containing the brotli binaries. +# +# Overrides the default name of brotlicommon library. +# +# Overrides the default name of brotlidec library. +# +# Overrides the default name of brotlienc library. +# +# The directory containing the brotli headers. +# +# Extra directories to add to library search paths of consumers during +# runtime (multiple instances are allowed). +# +# If none of these options is specified, then the environmental +# variables BROTLI_LIBRARY_PATH, BROTLI_NAME, and BROTLI_INCLUDE will +# be used instead. +# +# Examples:: +# +# # Find brotli in the default system location +# using brotli ; +# # Find brotli in /usr/local +# using brotli : 1.1.0 +# : /usr/local/include /usr/local/lib ; +# +rule init ( + version ? + # (currently ignored) + + : options * + # A list of the options to use + + : requirements * + # The requirements for the target + + : is-default ? + ) +{ + local caller = [ project.current ] ; + + if ! $(.initialized) + { + .initialized = true ; + + project.initialize $(__name__) ; + .project = [ project.current ] ; + project brotli ; + } + + local library-path = [ feature.get-values : $(options) ] ; + local include-path = [ feature.get-values : $(options) ] ; + local brotlicommon-name = [ feature.get-values : $(options) ] ; + local brotlidec-name = [ feature.get-values : $(options) ] ; + local brotlienc-name = [ feature.get-values : $(options) ] ; + local dll-paths = [ property.select : $(options) ] ; + + if ! $(options) + { + is-default = true ; + } + + condition = [ property-set.create $(requirements) ] ; + condition = [ property-set.create [ $(condition).base ] ] ; + + if $(.configured.$(condition)) + { + if $(is-default) + { + if $(.debug) + { + ECHO "notice: [brotli] brotli is already configured" ; + } + } + else + { + errors.user-error "brotli is already configured" ; + } + return ; + } + else + { + if $(.debug) + { + ECHO "notice: [brotli] Using pre-installed library" ; + if $(condition) + { + ECHO "notice: [brotli] Condition" [ $(condition).raw ] ; + } + } + + local brotlicommon = [ new ac-library brotlicommon : $(.project) : $(condition) : + $(include-path) : $(library-path) : $(brotlicommon-name) ] ; + $(brotlicommon).set-header $(header) ; + $(brotlicommon).set-default-names $(brotlicommon_names) ; + $(brotlicommon).add-usage-requirements $(dll-paths) ; + + local brotlidec = [ new ac-library brotlidec : $(.project) : $(condition) : + $(include-path) : $(library-path) : $(brotlidec-name) ] ; + $(brotlidec).set-header $(header) ; + $(brotlidec).set-default-names $(brotlidec_names) ; + $(brotlidec).add-usage-requirements $(dll-paths) ; + + local brotlienc = [ new ac-library brotlienc : $(.project) : $(condition) : + $(include-path) : $(library-path) : $(brotlienc-name) ] ; + $(brotlienc).set-header $(header) ; + $(brotlienc).set-default-names $(brotlienc_names) ; + $(brotlienc).add-usage-requirements $(dll-paths) ; + + targets.main-target-alternative $(brotlicommon) ; + targets.main-target-alternative $(brotlidec) ; + targets.main-target-alternative $(brotlienc) ; + } + .configured.$(condition) = true ; +} diff --git a/cmake/FindBrotli.cmake b/cmake/FindBrotli.cmake new file mode 100644 index 00000000..f0e74489 --- /dev/null +++ b/cmake/FindBrotli.cmake @@ -0,0 +1,50 @@ +# +# Copyright (c) 2025 Mohammad Nejati +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/cppalliance/http +# + +# Provides imported targets: +# Brotli::common +# Brotli::decoder +# Brotli::encoder + +find_path(Brotli_INCLUDE_DIR "brotli/decode.h") +find_library(Brotli_COMMON_LIBRARY NAMES "brotlicommon") +find_library(Brotli_DEC_LIBRARY NAMES "brotlidec") +find_library(Brotli_ENC_LIBRARY NAMES "brotlienc") + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Brotli + REQUIRED_VARS + Brotli_INCLUDE_DIR + Brotli_COMMON_LIBRARY + Brotli_DEC_LIBRARY + Brotli_ENC_LIBRARY +) + +if(Brotli_FOUND) + add_library(Brotli::common UNKNOWN IMPORTED) + set_target_properties(Brotli::common PROPERTIES + IMPORTED_LOCATION ${Brotli_COMMON_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${Brotli_INCLUDE_DIR}) + + add_library(Brotli::decoder UNKNOWN IMPORTED) + set_target_properties(Brotli::decoder PROPERTIES + IMPORTED_LOCATION ${Brotli_DEC_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${Brotli_INCLUDE_DIR}) + + add_library(Brotli::encoder UNKNOWN IMPORTED) + set_target_properties(Brotli::encoder PROPERTIES + IMPORTED_LOCATION ${Brotli_ENC_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES ${Brotli_INCLUDE_DIR}) +endif() + +mark_as_advanced( + Brotli_INCLUDE_DIR + Brotli_COMMON_LIBRARY + Brotli_DEC_LIBRARY + Brotli_ENC_LIBRARY) diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index d7c9236b..9b7c433d 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -15,6 +15,9 @@ // ** xref:server/params.adoc[Route Parameters] // ** xref:server/advanced.adoc[Advanced Topics] // ** xref:server/cors.adoc[CORS] +* Compression +** xref:compression/zlib.adoc[ZLib] +** xref:compression/brotli.adoc[Brotli] * Design Requirements ** xref:design_requirements/serializer.adoc[Serializer] ** xref:design_requirements/parser.adoc[Parser] diff --git a/doc/modules/ROOT/pages/compression/brotli.adoc b/doc/modules/ROOT/pages/compression/brotli.adoc new file mode 100644 index 00000000..cdbcb478 --- /dev/null +++ b/doc/modules/ROOT/pages/compression/brotli.adoc @@ -0,0 +1,164 @@ += Brotli Compression +:navtitle: Brotli + +The Brotli module provides high-ratio compression services for HTTP content encoding. + +== Overview + +Brotli is a modern compression algorithm that typically achieves better compression ratios than gzip/deflate, especially for text content. It's widely supported for HTTP `Content-Encoding: br`. + +== Basic Usage + +[source,cpp] +---- +#include + +namespace brotli = boost::http::brotli; + +// Create context and install services +boost::capy::polystore ctx; +auto& encoder = brotli::install_encode_service(ctx); +auto& decoder = brotli::install_decode_service(ctx); +---- + +=== Compression + +[source,cpp] +---- +// Compress data +std::vector input = /* ... */; +std::vector output(brotli::encodeBound(input.size())); + +brotli::encode_result result = encoder.encode( + input.data(), input.size(), + output.data(), output.size() +); + +output.resize(result.bytes_written); +---- + +=== Quality Levels + +Brotli quality ranges from 0 (fastest) to 11 (best compression): + +[source,cpp] +---- +// Set quality (0-11) +encoder.set_quality(6); // Balanced + +// Quality guidelines: +// 0-4: Fast compression, larger output +// 5-6: Balanced (good for real-time) +// 7-9: Slower, better compression +// 10-11: Very slow, best compression (static content) +---- + +=== Decompression + +[source,cpp] +---- +// Decompress data +std::vector compressed = /* ... */; +std::vector output(expected_size); + +brotli::decode_result result = decoder.decode( + compressed.data(), compressed.size(), + output.data(), output.size() +); + +output.resize(result.bytes_written); +---- + +== Shared Dictionary + +Brotli supports shared dictionaries for improved compression of similar content: + +[source,cpp] +---- +// Load custom dictionary +brotli::shared_dictionary dict = load_dictionary(); + +// Install services with dictionary +auto& encoder = brotli::install_encode_service(ctx, dict); +auto& decoder = brotli::install_decode_service(ctx, dict); +---- + +== Error Handling + +[source,cpp] +---- +brotli::encode_result result = encoder.encode(input, output); + +if (result.ec == brotli::error::invalid_input) +{ + // Malformed input +} +---- + +== Integration with HTTP + +The brotli services integrate with HTTP parser and serializer through content encoding: + +[source,cpp] +---- +// Parser automatically decompresses Content-Encoding: br +http::response_parser::config cfg; +cfg.apply_brotli_decoder = true; + +// Serializer automatically compresses when Content-Encoding is set +http::serializer::config ser_cfg; +ser_cfg.apply_brotli_encoder = true; +---- + +== Performance Considerations + +|=== +| Quality | Speed | Ratio | Use Case + +| 0-4 +| Fast +| Lower +| Real-time compression + +| 5-6 +| Medium +| Good +| General purpose + +| 7-9 +| Slow +| Better +| Offline compression + +| 10-11 +| Very slow +| Best +| Static assets +|=== + +== Reference + +=== Functions + +|=== +| Function | Description + +| `brotli::install_encode_service` +| Install compression service + +| `brotli::install_decode_service` +| Install decompression service +|=== + +=== Types + +|=== +| Type | Description + +| `brotli::shared_dictionary` +| Shared dictionary for improved compression +|=== + +== See Also + +* xref:zlib.adoc[ZLib] — DEFLATE/gzip compression diff --git a/doc/modules/ROOT/pages/compression/zlib.adoc b/doc/modules/ROOT/pages/compression/zlib.adoc new file mode 100644 index 00000000..c01e8536 --- /dev/null +++ b/doc/modules/ROOT/pages/compression/zlib.adoc @@ -0,0 +1,217 @@ += ZLib Compression +:navtitle: ZLib + +The ZLib module provides DEFLATE-based compression and decompression services for HTTP content encoding. + +== Overview + +ZLib supports three related formats: + +* Raw DEFLATE (no header) +* zlib format (with header/checksum) +* gzip format (with gzip header) + +All three share the same underlying algorithm but differ in their framing. + +== Basic Usage + +[source,cpp] +---- +#include + +namespace zlib = boost::http::zlib; + +// Create context and install services +boost::capy::polystore ctx; +auto& deflate_svc = zlib::install_deflate_service(ctx); +auto& inflate_svc = zlib::install_inflate_service(ctx); +---- + +=== Compression + +[source,cpp] +---- +// Compress data +std::vector input = /* ... */; +std::vector output(zlib::deflateBound(input.size())); + +zlib::deflate_result result = deflate_svc.deflate( + input.data(), input.size(), + output.data(), output.size() +); + +output.resize(result.bytes_written); +---- + +=== Compression Levels + +[source,cpp] +---- +// Available compression levels +zlib::compression_level::none // 0 - store only +zlib::compression_level::fast // 1 - fastest +zlib::compression_level::default_ // 6 - balanced +zlib::compression_level::best // 9 - smallest output +---- + +=== Compression Strategies + +[source,cpp] +---- +// Optimization strategies +zlib::compression_strategy::default_ // General data +zlib::compression_strategy::filtered // Data with values near a small set +zlib::compression_strategy::huffman // Huffman only, no string matching +zlib::compression_strategy::rle // Run-length encoding, sequential data +zlib::compression_strategy::fixed // Fixed Huffman codes +---- + +=== Decompression + +[source,cpp] +---- +// Decompress data +std::vector compressed = /* ... */; +std::vector output(expected_size); + +zlib::inflate_result result = inflate_svc.inflate( + compressed.data(), compressed.size(), + output.data(), output.size() +); + +output.resize(result.bytes_written); +---- + +== Format Selection + +=== Gzip Format + +For HTTP `Content-Encoding: gzip`: + +[source,cpp] +---- +// Use window_bits + 16 for gzip format +zlib::deflate_result result = deflate_svc.deflate( + input, output, + zlib::compression_level::default_, + 15 + 16 // window_bits + 16 = gzip +); +---- + +For decompression, auto-detect handles both gzip and zlib: + +[source,cpp] +---- +// Use window_bits + 32 for auto-detect +zlib::inflate_result result = inflate_svc.inflate( + compressed, output, + 15 + 32 // window_bits + 32 = auto-detect +); +---- + +== Streaming Interface + +For large data or when memory is constrained: + +[source,cpp] +---- +zlib::stream stream; +stream.next_in = input_ptr; +stream.avail_in = input_size; +stream.next_out = output_ptr; +stream.avail_out = output_size; + +int result = deflate_svc.deflate( + stream, + zlib::flush::no_flush +); + +// Process result, adjust pointers, repeat... +---- + +=== Flush Modes + +|=== +| Mode | Description + +| `zlib::flush::no_flush` +| Normal operation, accumulate data + +| `zlib::flush::sync_flush` +| Flush to byte boundary, can concatenate + +| `zlib::flush::full_flush` +| Like sync but resets state (random access) + +| `zlib::flush::finish` +| Complete the stream +|=== + +== Error Handling + +[source,cpp] +---- +zlib::deflate_result result = deflate_svc.deflate(input, output); + +if (result.ec == zlib::error::data_error) +{ + // Invalid compressed data +} +else if (result.ec == zlib::error::buf_error) +{ + // Output buffer too small +} +---- + +== Integration with HTTP + +The zlib services integrate with HTTP parser and serializer through content encoding: + +[source,cpp] +---- +// Parser automatically decompresses Content-Encoding: deflate/gzip +http::response_parser::config cfg; +cfg.apply_deflate_decoder = true; +cfg.apply_gzip_decoder = true; + +// Serializer automatically compresses when Content-Encoding is set +http::serializer::config ser_cfg; +ser_cfg.apply_deflate_encoder = true; +ser_cfg.apply_gzip_encoder = true; +---- + +== Use Cases + +* HTTP content compression (`Content-Encoding: deflate`, `gzip`) +* Compressing request/response bodies +* Integrating with existing gzip/zlib data + +== Reference + +=== Functions + +|=== +| Function | Description + +| `zlib::install_deflate_service` +| Install compression service + +| `zlib::install_inflate_service` +| Install decompression service +|=== + +=== Types + +|=== +| Type | Description + +| `zlib::stream` +| Streaming state structure + +| `zlib::error` +| Error codes +|=== + +== See Also + +* xref:brotli.adoc[Brotli] — Higher compression ratio diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index 7737b10b..86d235e1 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -127,6 +127,8 @@ Content-Length: 42 * xref:parsing.adoc[Parsing] — parse incoming HTTP messages * xref:serializing.adoc[Serializing] — produce outgoing HTTP messages * xref:router.adoc[Router] — dispatch requests to handlers +* xref:compression/zlib.adoc[ZLib Compression] — DEFLATE and gzip support +* xref:compression/brotli.adoc[Brotli Compression] — high-ratio compression == Acknowledgments diff --git a/doc/modules/ROOT/pages/parsing.adoc b/doc/modules/ROOT/pages/parsing.adoc index aa37afe2..0ac3fc8a 100644 --- a/doc/modules/ROOT/pages/parsing.adoc +++ b/doc/modules/ROOT/pages/parsing.adoc @@ -268,12 +268,17 @@ gzip, deflate, and brotli encoded bodies: request_parser::config cfg; cfg.apply_gzip_decoder = true; cfg.apply_deflate_decoder = true; -// For brotli, also install the brotli decode service +cfg.apply_brotli_decoder = true; // Requires brotli decode service ---- The `Content-Encoding` header is processed automatically. The body you receive is the decoded content. +For detailed information on compression services, see: + +* xref:compression/zlib.adoc[ZLib] — DEFLATE and gzip decompression +* xref:compression/brotli.adoc[Brotli] — Brotli decompression + == Handling Multiple Messages For persistent connections, parse multiple messages in sequence: diff --git a/doc/modules/ROOT/pages/serializing.adoc b/doc/modules/ROOT/pages/serializing.adoc index b94fb5cd..9dead7a4 100644 --- a/doc/modules/ROOT/pages/serializing.adoc +++ b/doc/modules/ROOT/pages/serializing.adoc @@ -226,6 +226,7 @@ compresses the body automatically: // Enable in config serializer::config cfg; cfg.apply_gzip_encoder = true; +cfg.apply_brotli_encoder = true; // Requires brotli encode service // Check Accept-Encoding from request if (request_accepts_gzip(req)) @@ -237,6 +238,11 @@ if (request_accepts_gzip(req)) sr.start(res, large_body); ---- +For detailed information on compression services, see: + +* xref:compression/zlib.adoc[ZLib] — DEFLATE and gzip compression +* xref:compression/brotli.adoc[Brotli] — Brotli compression + == Expect: 100-continue The serializer handles the 100-continue handshake: diff --git a/include/boost/http/brotli.hpp b/include/boost/http/brotli.hpp new file mode 100644 index 00000000..cee56540 --- /dev/null +++ b/include/boost/http/brotli.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +/** @file + Brotli compression and decompression library. + + This header includes all Brotli-related functionality including + encoding, decoding, error handling, and shared dictionary support. + + Brotli is a generic-purpose lossless compression algorithm that compresses + data using a combination of a modern variant of the LZ77 algorithm, Huffman + coding and 2nd order context modeling, with a compression ratio comparable + to the best currently available general-purpose compression methods. + + @code + #include + #include + + // Create a datastore for services + boost::capy::datastore ctx; + + // Install compression and decompression services + auto& encoder = boost::http::brotli::install_encode_service(ctx); + auto& decoder = boost::http::brotli::install_decode_service(ctx); + @endcode +*/ + +#ifndef BOOST_HTTP_BROTLI_HPP +#define BOOST_HTTP_BROTLI_HPP + +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/boost/http/brotli/decode.hpp b/include/boost/http/brotli/decode.hpp new file mode 100644 index 00000000..6c4812c8 --- /dev/null +++ b/include/boost/http/brotli/decode.hpp @@ -0,0 +1,258 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_DECODE_HPP +#define BOOST_HTTP_BROTLI_DECODE_HPP + +#include +#include +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Opaque structure that holds decoder state. */ +struct decoder_state; + +/** Decoder result codes. + + These values indicate the result of decompression operations. +*/ +enum class decoder_result +{ + /** Decompression error occurred. */ + error = 0, + + /** Decompression completed successfully. */ + success = 1, + + /** More input data is needed. */ + needs_more_input = 2, + + /** More output space is needed. */ + needs_more_output = 3 +}; + +/** Decoder parameter identifiers. + + These values identify parameters that can be set + on a decoder instance. +*/ +enum class decoder_param +{ + /** Disable automatic ring buffer reallocation. */ + disable_ring_buffer_reallocation = 0, + + /** Enable large window mode. */ + large_window = 1 +}; + +/** Callback to fire on metadata block start. */ +using metadata_start_func = void (*)(void* opaque, std::size_t size); + +/** Callback to fire on metadata block chunk becomes available. */ +using metadata_chunk_func = void (*)(void* opaque, const std::uint8_t* data, std::size_t size); + +/** Provides the Brotli decompression API. + + This service interface exposes Brotli decoder functionality + through a set of virtual functions. The decoder can operate + in one-shot mode for simple decompression or streaming mode + for processing data in chunks. + + @code + // Example: Simple one-shot decompression + boost::capy::datastore ctx; + auto& decoder = boost::http::brotli::install_decode_service(ctx); + + std::vector compressed_data = get_compressed_data(); + std::vector output(1024 * 1024); // 1MB buffer + std::size_t decoded_size = output.size(); + + auto result = decoder.decompress( + compressed_data.size(), + compressed_data.data(), + &decoded_size, + output.data()); + + if (result == boost::http::brotli::decoder_result::success) + { + output.resize(decoded_size); + // Use decompressed data + } + @endcode + + @code + // Example: Streaming decompression + auto* state = decoder.create_instance(nullptr, nullptr, nullptr); + + std::size_t available_in = compressed_data.size(); + const std::uint8_t* next_in = compressed_data.data(); + std::size_t available_out = output.size(); + std::uint8_t* next_out = output.data(); + std::size_t total_out = 0; + + auto result = decoder.decompress_stream( + state, + &available_in, + &next_in, + &available_out, + &next_out, + &total_out); + + decoder.destroy_instance(state); + @endcode +*/ +struct BOOST_SYMBOL_VISIBLE + decode_service +{ + /** Set a decoder parameter. + @param state The decoder state. + @param param The parameter identifier. + @param value The parameter value. + @return True on success, false on error. + */ + virtual bool + set_parameter( + decoder_state* state, + decoder_param param, + std::uint32_t value) const noexcept = 0; + + /** Create a new decoder instance. + @param alloc_func Allocation function. + @param free_func Deallocation function. + @param opaque Opaque pointer passed to allocation functions. + @return Pointer to decoder state, or nullptr on error. + */ + virtual decoder_state* + create_instance( + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept = 0; + + /** Destroy a decoder instance. + @param state The decoder state to destroy. + */ + virtual void + destroy_instance(decoder_state* state) const noexcept = 0; + + /** Decompress data in one call. + @param encoded_size Input data size. + @param encoded_buffer Input data buffer. + @param decoded_size Pointer to variable receiving output size. + @param decoded_buffer Output buffer. + @return Result code indicating success or failure. + */ + virtual decoder_result + decompress( + std::size_t encoded_size, + const std::uint8_t encoded_buffer[], + std::size_t* decoded_size, + std::uint8_t decoded_buffer[]) const noexcept = 0; + + /** Decompress data in streaming mode. + @param state The decoder state. + @param available_in Pointer to input bytes available. + @param next_in Pointer to pointer to input data. + @param available_out Pointer to output space available. + @param next_out Pointer to pointer to output buffer. + @param total_out Pointer to variable receiving total bytes written. + @return Result code indicating decoder state. + */ + virtual decoder_result + decompress_stream( + decoder_state* state, + std::size_t* available_in, + const std::uint8_t** next_in, + std::size_t* available_out, + std::uint8_t** next_out, + std::size_t* total_out) const noexcept = 0; + + /** Check if more output is available. + @param state The decoder state. + @return True if output is available, false otherwise. + */ + virtual bool + has_more_output(const decoder_state* state) const noexcept = 0; + + /** Return buffered output data. + @param state The decoder state. + @param size Pointer to variable receiving output size. + @return Pointer to output buffer. + */ + virtual const std::uint8_t* + take_output(decoder_state* state, std::size_t* size) const noexcept = 0; + + /** Check if decoder has been used. + @param state The decoder state. + @return True if decoder has processed data, false otherwise. + */ + virtual bool + is_used(const decoder_state* state) const noexcept = 0; + + /** Check if decompression is finished. + @param state The decoder state. + @return True if decompression is complete, false otherwise. + */ + virtual bool + is_finished(const decoder_state* state) const noexcept = 0; + + /** Return the error code from the decoder. + @param state The decoder state. + @return The error code. + */ + virtual error + get_error_code(const decoder_state* state) const noexcept = 0; + + /** Return a string description of an error code. + @param c The error code. + @return Pointer to error description string. + */ + virtual const char* + error_string(error c) const noexcept = 0; + + /** Return the Brotli library version. + @return Version number. + */ + virtual std::uint32_t + version() const noexcept = 0; + +#if 0 + virtual bool + attach_dictionary( + decoder_state* state, + shared_dictionary_type type, + std::size_t data_size, + const std::uint8_t data[]) const noexcept = 0; + + virtual void + set_metadata_callbacks( + decoder_state* state, + metadata_start_func start_func, + metadata_chunk_func chunk_func, + void* opaque) const noexcept = 0; +#endif +}; + +/** Install the decode service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed decode service. +*/ +BOOST_HTTP_DECL +decode_service& +install_decode_service(capy::polystore& ctx); + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/brotli/encode.hpp b/include/boost/http/brotli/encode.hpp new file mode 100644 index 00000000..b888efea --- /dev/null +++ b/include/boost/http/brotli/encode.hpp @@ -0,0 +1,362 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_ENCODE_HPP +#define BOOST_HTTP_BROTLI_ENCODE_HPP + +#include +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Opaque structure that holds encoder state. */ +struct encoder_state; + +/** Opaque type for pointer to different possible internal + structures containing dictionary prepared for the encoder +*/ +struct encoder_prepared_dictionary; + +/** Encoder mode options. + + These values specify the type of input data for + optimization purposes. +*/ +enum class encoder_mode +{ + /** Generic mode for mixed or unknown data. */ + generic = 0, + + /** Mode optimized for UTF-8 text. */ + text = 1, + + /** Mode optimized for WOFF 2.0 fonts. */ + font = 2 +}; + +/** Encoder stream operations. + + These operations control the streaming encoder behavior. +*/ +enum class encoder_operation +{ + /** Process input data. */ + process = 0, + + /** Flush pending output. */ + flush = 1, + + /** Finish encoding and emit trailer. */ + finish = 2, + + /** Emit metadata block. */ + emit_metadata = 3 +}; + +/** Encoder parameter identifiers. + + These values identify parameters that can be set + on an encoder instance. +*/ +enum class encoder_parameter +{ + /** Encoder mode (generic, text, or font). */ + mode = 0, + + /** Compression quality (0-11). */ + quality = 1, + + /** Base-2 logarithm of window size. */ + lgwin = 2, + + /** Base-2 logarithm of input block size. */ + lgblock = 3, + + /** Disable literal context modeling flag. */ + disable_literal_context_modeling = 4, + + /** Expected input size hint. */ + size_hint = 5, + + /** Enable large window mode flag. */ + large_window = 6, + + /** Number of postfix bits for distance codes. */ + npostfix = 7, + + /** Number of direct distance codes. */ + ndirect = 8, + + /** Current stream offset. */ + stream_offset = 9 +}; + +/** Brotli encoder constants. + + These constants define valid ranges and default values + for encoder parameters. +*/ +enum constants +{ + /** Minimum window size (2^10 bytes). */ + min_window_bits = 10, + + /** Maximum standard window size (2^24 bytes). */ + max_window_bits = 24, + + /** Maximum large window size (2^30 bytes). */ + large_max_window_bits = 30, + + /** Minimum input block size (2^16 bytes). */ + min_input_block_bits = 16, + + /** Maximum input block size (2^24 bytes). */ + max_input_block_bits = 24, + + /** Minimum quality level. */ + min_quality = 0, + + /** Maximum quality level. */ + max_quality = 11, + + /** Default quality level. */ + default_quality = 11, + + /** Default window size. */ + default_window = 22, + + /** Default encoder mode. */ + default_mode = 0 +}; + +/** Provides the Brotli compression API. + + This service interface exposes Brotli encoder functionality + through a set of virtual functions. The encoder can operate + in one-shot mode for simple compression or streaming mode + for processing data in chunks. + + The quality parameter ranges from 0 to 11 (see min_quality + and max_quality constants). Quality 0 offers fastest compression + with lower ratio, while quality 11 offers best compression with + slower speed. The default quality is 11. + + @code + // Example: Simple one-shot compression + boost::capy::datastore ctx; + auto& encoder = boost::http::brotli::install_encode_service(ctx); + + std::vector input_data = get_input_data(); + std::size_t max_size = encoder.max_compressed_size(input_data.size()); + std::vector output(max_size); + std::size_t encoded_size = max_size; + + bool success = encoder.compress( + 11, // quality (0-11) + 22, // lgwin (window size = 2^22) + boost::http::brotli::encoder_mode::generic, + input_data.size(), + input_data.data(), + &encoded_size, + output.data()); + + if (success) + { + output.resize(encoded_size); + // Use compressed data + } + @endcode + + @code + // Example: Streaming compression + auto* state = encoder.create_instance(nullptr, nullptr, nullptr); + + // Set parameters + encoder.set_parameter(state, + boost::http::brotli::encoder_parameter::quality, 6); + encoder.set_parameter(state, + boost::http::brotli::encoder_parameter::lgwin, 22); + + std::size_t available_in = input_data.size(); + const std::uint8_t* next_in = input_data.data(); + std::size_t available_out = output.size(); + std::uint8_t* next_out = output.data(); + std::size_t total_out = 0; + + bool success = encoder.compress_stream( + state, + boost::http::brotli::encoder_operation::finish, + &available_in, + &next_in, + &available_out, + &next_out, + &total_out); + + encoder.destroy_instance(state); + @endcode +*/ +struct BOOST_SYMBOL_VISIBLE + encode_service +{ + /** Set an encoder parameter. + @param state The encoder state. + @param param The parameter identifier. + @param value The parameter value. + @return True on success, false on error. + */ + virtual bool + set_parameter( + encoder_state* state, + encoder_parameter param, + std::uint32_t value) const noexcept = 0; + + /** Create a new encoder instance. + @param alloc_func Allocation function. + @param free_func Deallocation function. + @param opaque Opaque pointer passed to allocation functions. + @return Pointer to encoder state, or nullptr on error. + */ + virtual encoder_state* + create_instance( + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept = 0; + + /** Destroy an encoder instance. + @param state The encoder state to destroy. + */ + virtual void + destroy_instance(encoder_state* state) const noexcept = 0; + + /** Return maximum possible compressed size. + @param input_size The input data size. + @return Maximum compressed size in bytes. + */ + virtual std::size_t + max_compressed_size(std::size_t input_size) const noexcept = 0; + + /** Compress data in one call. + @param quality Compression quality (0-11). + @param lgwin Base-2 logarithm of window size. + @param mode Encoder mode. + @param input_size Input data size. + @param input_buffer Input data buffer. + @param encoded_size Pointer to variable receiving output size. + @param encoded_buffer Output buffer. + @return True on success, false on error. + */ + virtual bool + compress( + int quality, + int lgwin, + encoder_mode mode, + std::size_t input_size, + const std::uint8_t input_buffer[], + std::size_t* encoded_size, + std::uint8_t encoded_buffer[]) const noexcept = 0; + + /** Compress data in streaming mode. + @param state The encoder state. + @param op The encoder operation. + @param available_in Pointer to input bytes available. + @param next_in Pointer to pointer to input data. + @param available_out Pointer to output space available. + @param next_out Pointer to pointer to output buffer. + @param total_out Pointer to variable receiving total bytes written. + @return True on success, false on error. + */ + virtual bool + compress_stream( + encoder_state* state, + encoder_operation op, + std::size_t* available_in, + const std::uint8_t** next_in, + std::size_t* available_out, + std::uint8_t** next_out, + std::size_t* total_out) const noexcept = 0; + + /** Check if encoding is finished. + @param state The encoder state. + @return True if encoding is complete, false otherwise. + */ + virtual bool + is_finished(encoder_state* state) const noexcept = 0; + + /** Check if more output is available. + @param state The encoder state. + @return True if output is available, false otherwise. + */ + virtual bool + has_more_output(encoder_state* state) const noexcept = 0; + + /** Return buffered output data. + @param state The encoder state. + @param size Pointer to variable receiving output size. + @return Pointer to output buffer. + */ + virtual const std::uint8_t* + take_output( + encoder_state* state, + std::size_t* size) const noexcept = 0; + + /** Return the Brotli library version. + @return Version number. + */ + virtual std::uint32_t + version() const noexcept = 0; + +#if 0 + virtual encoder_prepared_dictionary* + prepare_dictionary( + shared_dictionary_type type, + std::size_t data_size, + const std::uint8_t data[], + int quality, + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept = 0; + + virtual void + destroy_prepared_dictionary( + encoder_prepared_dictionary* dictionary) const noexcept = 0; + + virtual bool + attach_prepared_dictionary( + encoder_state* state, + const encoder_prepared_dictionary* dictionary) const noexcept = 0; + + virtual std::size_t + estimate_peak_memory_usage( + int quality, + int lgwin, + std::size_t input_size) const noexcept = 0; + + virtual std::size_t + get_prepared_dictionary_size( + const encoder_prepared_dictionary* dictionary) const noexcept = 0; +#endif +}; + +/** Install the encode service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed encode service. +*/ +BOOST_HTTP_DECL +encode_service& +install_encode_service(capy::polystore& ctx); + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/brotli/error.hpp b/include/boost/http/brotli/error.hpp new file mode 100644 index 00000000..a7f2e931 --- /dev/null +++ b/include/boost/http/brotli/error.hpp @@ -0,0 +1,78 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_ERROR_HPP +#define BOOST_HTTP_BROTLI_ERROR_HPP + +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Error codes returned from decode functions. + + Negative values are errors, positive values are used + for special but normal events. +*/ +enum class error +{ + no_error = 0, + + /* Same as decoder_result values */ + success = 1, + needs_more_input = 2, + needs_more_output = 3, + + /* Errors caused by invalid input */ + format_exuberant_nibble = -1, + format_reserved = -2, + format_exuberant_meta_nibbl = -3, + format_simple_huffman_alphabet = -4, + format_simple_huffman_same = -5, + format_cl_space = -6, + format_huffman_space = -7, + format_context_map_repea = -8, + format_block_length_1 = -9, + format_block_length_2 = -10, + format_transform = -11, + format_dictionary = -12, + format_window_bits = -13, + format_padding_1 = -14, + format_padding_2 = -15, + format_distance = -16, + + /* -17 code is reserved */ + + compound_dictionary = -18, + dictionary_not_set = -19, + invalid_arguments = -20, + + /* Memory allocation problems */ + alloc_context_modes = -21, + /* Literal, insert and distance trees together */ + alloc_tree_groups = -22, + /* -23..-24 codes are reserved for distinct tree groups */ + alloc_context_map = -25, + alloc_ring_buffer_1 = -26, + alloc_ring_buffer_2 = -27, + /* -28..-29 codes are reserved for dynamic ring-buffer allocation */ + alloc_block_type_trees = -30, + + /* "Impossible" states */ + unreachable = -31 +}; + +} // brotli +} // http +} // boost + +#include + +#endif diff --git a/include/boost/http/brotli/impl/error.hpp b/include/boost/http/brotli/impl/error.hpp new file mode 100644 index 00000000..8ab27d4b --- /dev/null +++ b/include/boost/http/brotli/impl/error.hpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_IMPL_ERROR_HPP +#define BOOST_HTTP_BROTLI_IMPL_ERROR_HPP + +#include + +#include +#include + +namespace boost { + +namespace system { +template<> +struct is_error_code_enum< + ::boost::http::brotli::error> +{ + static bool const value = true; +}; +} // system + +namespace http { +namespace brotli { + +namespace detail { + +struct BOOST_SYMBOL_VISIBLE + error_cat_type + : system::error_category +{ + BOOST_HTTP_DECL const char* name( + ) const noexcept override; + BOOST_HTTP_DECL bool failed( + int) const noexcept override; + BOOST_HTTP_DECL std::string message( + int) const override; + BOOST_HTTP_DECL char const* message( + int, char*, std::size_t + ) const noexcept override; + BOOST_SYSTEM_CONSTEXPR error_cat_type() + : error_category(0xc38951ab8832fb6f) + { + } +}; + +BOOST_HTTP_DECL extern + error_cat_type error_cat; + +} // detail + +inline +BOOST_SYSTEM_CONSTEXPR +system::error_code +make_error_code( + error ev) noexcept +{ + return system::error_code{ + static_cast::type>(ev), + detail::error_cat}; +} + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/brotli/shared_dictionary.hpp b/include/boost/http/brotli/shared_dictionary.hpp new file mode 100644 index 00000000..3bebe757 --- /dev/null +++ b/include/boost/http/brotli/shared_dictionary.hpp @@ -0,0 +1,78 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_SHARED_DICTIONARY_HPP +#define BOOST_HTTP_BROTLI_SHARED_DICTIONARY_HPP + +#include +#include +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Opaque structure that holds shared dictionary data. */ +struct shared_dictionary; + +/** Shared dictionary data format. + + These values specify the format of dictionary data + being attached to an encoder or decoder. +*/ +enum class shared_dictionary_type +{ + /** Raw dictionary data. */ + raw = 0, + + /** Serialized dictionary format. */ + serialized = 1 +}; + +/** Provides the Brotli shared dictionary API. + + This service interface exposes Brotli shared dictionary + functionality through a set of virtual functions. +*/ +struct BOOST_SYMBOL_VISIBLE + shared_dictionary_service +{ +#if 0 + virtual shared_dictionary* + create_instance( + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept = 0; + + virtual void + destroy_instance( + shared_dictionary* dict) const noexcept = 0; + + virtual bool + attach( + shared_dictionary* dict, + shared_dictionary_type type, + std::size_t data_size, + const uint8_t data[]) const noexcept = 0; +#endif +}; + +/** Install the shared dictionary service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed shared dictionary service. +*/ +BOOST_HTTP_DECL +shared_dictionary_service& +install_shared_dictionary_service(capy::polystore& ctx); + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/brotli/types.hpp b/include/boost/http/brotli/types.hpp new file mode 100644 index 00000000..0446d208 --- /dev/null +++ b/include/boost/http/brotli/types.hpp @@ -0,0 +1,31 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_BROTLI_TYPES_HPP +#define BOOST_HTTP_BROTLI_TYPES_HPP + +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +/** Allocating function pointer type. */ +using alloc_func = void* (*)(void* opaque, std::size_t size); + +/** Deallocating function pointer type. */ +using free_func = void (*)(void* opaque, void* address); + +} // brotli +} // http +} // boost + +#endif diff --git a/include/boost/http/parser.hpp b/include/boost/http/parser.hpp index 68278b2f..70e1af49 100644 --- a/include/boost/http/parser.hpp +++ b/include/boost/http/parser.hpp @@ -670,7 +670,7 @@ struct parser::config_base /** Enable Brotli Content-Encoding decoding. - Requires `boost::capy::brotli::decode_service` to be + Requires `boost::http::brotli::decode_service` to be installed, otherwise an exception is thrown. */ bool apply_brotli_decoder = false; @@ -695,7 +695,7 @@ struct parser::config_base Larger windows improve decompression at the cost of memory. If a larger window is required than allowed, decoding fails with - `capy::zlib::error::data_err`. + `http::zlib::error::data_err`. */ int zlib_window_bits = 15; diff --git a/include/boost/http/serializer.hpp b/include/boost/http/serializer.hpp index 357614a2..1e54dcf4 100644 --- a/include/boost/http/serializer.hpp +++ b/include/boost/http/serializer.hpp @@ -573,7 +573,7 @@ struct serializer::config { /** Enable Brotli Content-Encoding. - Requires `boost::capy::brotli::encode_service` to be + Requires `boost::http::brotli::encode_service` to be installed, otherwise an exception is thrown. */ bool apply_brotli_encoder = false; diff --git a/include/boost/http/zlib.hpp b/include/boost/http/zlib.hpp new file mode 100644 index 00000000..1834868e --- /dev/null +++ b/include/boost/http/zlib.hpp @@ -0,0 +1,49 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +/** @file + ZLib compression and decompression library. + + This header includes all ZLib-related functionality including + deflate, inflate, error handling, and stream management. + + ZLib is a widely-used lossless data compression library that + implements the DEFLATE compression algorithm. It provides both + compression (deflate) and decompression (inflate) functionality + with support for raw, zlib, and gzip formats through the + windowBits parameter. + + @code + #include + #include + + // Create a datastore for services + boost::capy::datastore ctx; + + // Install compression and decompression services + auto& deflate_svc = boost::http::zlib::install_deflate_service(ctx); + auto& inflate_svc = boost::http::zlib::install_inflate_service(ctx); + @endcode +*/ + +#ifndef BOOST_HTTP_ZLIB_HPP +#define BOOST_HTTP_ZLIB_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/boost/http/zlib/compression_level.hpp b/include/boost/http/zlib/compression_level.hpp new file mode 100644 index 00000000..f9fa69e9 --- /dev/null +++ b/include/boost/http/zlib/compression_level.hpp @@ -0,0 +1,61 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_COMPRESSION_LEVEL_HPP +#define BOOST_HTTP_ZLIB_COMPRESSION_LEVEL_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Compression level constants. + + These values control the trade-off between compression + speed and compression ratio. Higher values produce better + compression but take more time. Level 0 disables compression + entirely, storing data uncompressed. Levels 1-9 provide + increasing compression, with 6 being the default compromise + between speed and ratio. + + @code + boost::http::zlib::stream st = {}; + auto& deflate_svc = boost::http::zlib::install_deflate_service(ctx); + + // Use best speed for time-critical operations + deflate_svc.init(st, boost::http::zlib::best_speed); + + // Use best compression for archival storage + deflate_svc.init(st, boost::http::zlib::best_compression); + + // Use default compression for balanced performance + deflate_svc.init(st, boost::http::zlib::default_compression); + @endcode +*/ +enum compression_level +{ + /** Use the default compression level. */ + default_compression = -1, + + /** No compression is performed. */ + no_compression = 0, + + /** Fastest compression speed with minimal compression. */ + best_speed = 1, + + /** Best compression ratio with slower speed. */ + best_compression = 9 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/compression_method.hpp b/include/boost/http/zlib/compression_method.hpp new file mode 100644 index 00000000..5686a8fe --- /dev/null +++ b/include/boost/http/zlib/compression_method.hpp @@ -0,0 +1,33 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_COMPRESSION_METHOD_HPP +#define BOOST_HTTP_ZLIB_COMPRESSION_METHOD_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Compression method constants. + + Specifies the compression algorithm to use. +*/ +enum compression_method +{ + /** The deflate compression method. */ + deflated = 8 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/compression_strategy.hpp b/include/boost/http/zlib/compression_strategy.hpp new file mode 100644 index 00000000..5e668889 --- /dev/null +++ b/include/boost/http/zlib/compression_strategy.hpp @@ -0,0 +1,67 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_COMPRESSION_STRATEGY_HPP +#define BOOST_HTTP_ZLIB_COMPRESSION_STRATEGY_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Compression strategy constants. + + These values tune the compression algorithm for specific + types of input data. The default strategy works well for + most data. Other strategies optimize for specific patterns: + filtered for data with small value differences, huffman_only + for pre-compressed data, rle for image data with many + repeated bytes, and fixed for fastest compression. + + @code + // Example: Using different strategies for different data types + boost::http::zlib::stream st = {}; + + // For PNG image data (many repeated bytes) + deflate_svc.init2(st, 6, boost::http::zlib::deflated, + 15, 8, boost::http::zlib::rle); + + // For small numeric differences (filtered data) + deflate_svc.init2(st, 6, boost::http::zlib::deflated, + 15, 8, boost::http::zlib::filtered); + + // For fastest speed with pre-compressed data + deflate_svc.init2(st, 1, boost::http::zlib::deflated, + 15, 8, boost::http::zlib::huffman_only); + @endcode +*/ +enum compression_strategy +{ + /** Use the default compression strategy. */ + default_strategy = 0, + + /** Strategy optimized for data with small values. */ + filtered = 1, + + /** Force Huffman encoding only (no string match). */ + huffman_only = 2, + + /** Limit match distances to one (run-length encoding). */ + rle = 3, + + /** Prevent use of dynamic Huffman codes. */ + fixed = 4 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/data_type.hpp b/include/boost/http/zlib/data_type.hpp new file mode 100644 index 00000000..1fadee81 --- /dev/null +++ b/include/boost/http/zlib/data_type.hpp @@ -0,0 +1,43 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_DATA_TYPE_HPP +#define BOOST_HTTP_ZLIB_DATA_TYPE_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Data type constants for the stream data_type field. + + These values represent the best guess about the type + of data being compressed or decompressed. +*/ +enum data_type +{ + /** Binary data. */ + binary = 0, + + /** Text data. */ + text = 1, + + /** ASCII text data (same as text). */ + ascii = 1, + + /** Data type is unknown. */ + unknown = 2 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/deflate.hpp b/include/boost/http/zlib/deflate.hpp new file mode 100644 index 00000000..4c6e5d47 --- /dev/null +++ b/include/boost/http/zlib/deflate.hpp @@ -0,0 +1,199 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_DEFLATE_HPP +#define BOOST_HTTP_ZLIB_DEFLATE_HPP + +#include +#include +#include + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Provides the ZLib compression API. + + This service interface exposes the ZLib deflate (compression) + functionality through a set of virtual functions. The deflate + algorithm compresses data by finding repeated byte sequences + and encoding them efficiently using a combination of LZ77 and + Huffman coding. + + The windowBits parameter in init2() controls the format: + - 8..15: zlib format with specified window size + - -8..-15: raw deflate format (no header/trailer) + - 16+windowBits: gzip format + + @code + // Example: Basic compression + boost::capy::datastore ctx; + auto& deflate_svc = boost::http::zlib::install_deflate_service(ctx); + + boost::http::zlib::stream st = {}; + std::vector input_data = get_data(); + std::vector output(input_data.size() * 2); + + st.zalloc = nullptr; + st.zfree = nullptr; + st.opaque = nullptr; + + deflate_svc.init(st, boost::http::zlib::default_compression); + + st.avail_in = input_data.size(); + st.next_in = input_data.data(); + st.avail_out = output.size(); + st.next_out = output.data(); + + deflate_svc.deflate(st, boost::http::zlib::finish); + output.resize(st.total_out); + + deflate_svc.deflate_end(st); + @endcode + + @code + // Example: Gzip compression with custom window size + boost::http::zlib::stream st = {}; + st.zalloc = nullptr; + st.zfree = nullptr; + + // Use gzip format (16 + 15 for max window) + deflate_svc.init2(st, + 6, // level + boost::http::zlib::deflated, // method + 16 + 15, // gzip format + 8, // memLevel + boost::http::zlib::default_strategy); // strategy + + // Compress data... + deflate_svc.deflate_end(st); + @endcode +*/ +struct BOOST_SYMBOL_VISIBLE + deflate_service +{ + /** Return the ZLib version string. */ + virtual char const* version() const noexcept = 0; + + /** Initialize deflate compression. + @param st The stream to initialize. + @param level The compression level. + @return Zero on success, or an error code. + */ + virtual int init(stream& st, int level) const = 0; + + /** Initialize deflate compression with extended parameters. + @param st The stream to initialize. + @param level The compression level. + @param method The compression method. + @param windowBits The base-2 logarithm of the window size. + @param memLevel Memory usage level (1-9). + @param strategy The compression strategy. + @return Zero on success, or an error code. + */ + virtual int init2(stream& st, int level, int method, + int windowBits, int memLevel, int strategy) const = 0; + + /** Set the compression dictionary. + @param st The stream. + @param dict Pointer to the dictionary data. + @param len Length of the dictionary. + @return Zero on success, or an error code. + */ + virtual int set_dict(stream& st, unsigned char const* dict, unsigned len) const = 0; + + /** Return the current compression dictionary. + @param st The stream. + @param dest Destination buffer for the dictionary. + @param len Pointer to variable receiving dictionary length. + @return Zero on success, or an error code. + */ + virtual int get_dict(stream& st, unsigned char* dest, unsigned* len) const = 0; + + /** Duplicate a deflate stream. + @param dest The destination stream. + @param src The source stream to duplicate. + @return Zero on success, or an error code. + */ + virtual int dup(stream& dest, stream& src) const = 0; + + /** Compress data in the stream. + @param st The stream containing data to compress. + @param flush The flush mode. + @return Status code indicating compression state. + */ + virtual int deflate(stream& st, int flush) const = 0; + + /** Release all resources held by the deflate stream. + @param st The stream to finalize. + @return Zero on success, or an error code. + */ + virtual int deflate_end(stream& st) const = 0; + + /** Reset the deflate stream state. + @param st The stream to reset. + @return Zero on success, or an error code. + */ + virtual int reset(stream& st) const = 0; + + /** Dynamically update compression parameters. + @param st The stream. + @param level The new compression level. + @param strategy The new compression strategy. + @return Zero on success, or an error code. + */ + virtual int params(stream& st, int level, int strategy) const = 0; + + /** Return an upper bound on compressed size. + @param st The stream. + @param sourceLen The length of source data. + @return Maximum possible compressed size. + */ + virtual std::size_t bound(stream& st, unsigned long sourceLen) const = 0; + + /** Return the number of pending output bytes. + @param st The stream. + @param pending Pointer to variable receiving pending byte count. + @param bits Pointer to variable receiving pending bit count. + @return Zero on success, or an error code. + */ + virtual int pending(stream& st, unsigned* pending, int* bits) const = 0; + + /** Insert bits into the compressed stream. + @param st The stream. + @param bits Number of bits to insert. + @param value The bit pattern to insert. + @return Zero on success, or an error code. + */ + virtual int prime(stream& st, int bits, int value) const = 0; + + /** Set the gzip header information. + @param st The stream. + @param header Pointer to gzip header structure. + @return Zero on success, or an error code. + */ + virtual int set_header(stream& st, void* header) const = 0; +}; + +/** Install the deflate service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed deflate service. +*/ +BOOST_HTTP_DECL +deflate_service& +install_deflate_service(capy::polystore& ctx); + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/error.hpp b/include/boost/http/zlib/error.hpp new file mode 100644 index 00000000..c31325cf --- /dev/null +++ b/include/boost/http/zlib/error.hpp @@ -0,0 +1,43 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_ERROR_HPP +#define BOOST_HTTP_ZLIB_ERROR_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Error codes returned from compression/decompression functions. + + Negative values are errors, positive values are used + for special but normal events. +*/ +enum class error +{ + ok = 0, + stream_end = 1, + need_dict = 2, + errno_ = -1, + stream_err = -2, + data_err = -3, + mem_err = -4, + buf_err = -5, + version_err = -6 +}; + +} // zlib +} // http +} // boost + +#include + +#endif diff --git a/include/boost/http/zlib/flush.hpp b/include/boost/http/zlib/flush.hpp new file mode 100644 index 00000000..51156840 --- /dev/null +++ b/include/boost/http/zlib/flush.hpp @@ -0,0 +1,77 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_FLUSH_HPP +#define BOOST_HTTP_ZLIB_FLUSH_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Flush method constants. + + These values control how and when compressed data is + flushed from internal buffers during compression operations. + + Use no_flush for maximum compression efficiency when more + input is available. Use sync_flush to force output to a + byte boundary, allowing partial decompression. Use finish + when no more input is available to complete compression + and write the trailer. + + @code + // Example: Streaming compression with periodic flushing + boost::http::zlib::stream st = {}; + // ... initialize stream ... + + while (has_more_data) + { + st.next_in = get_next_chunk(); + st.avail_in = chunk_size; + + // Flush at chunk boundaries for progressive decoding + int flush_mode = has_more_data ? + boost::http::zlib::sync_flush : + boost::http::zlib::finish; + + deflate_svc.deflate(st, flush_mode); + } + @endcode +*/ +enum flush +{ + /** No flushing, allow optimal compression. */ + no_flush = 0, + + /** Flush to byte boundary (deprecated). */ + partial_flush = 1, + + /** Flush to byte boundary for synchronization. */ + sync_flush = 2, + + /** Full flush, reset compression state. */ + full_flush = 3, + + /** Finish compression, emit trailer. */ + finish = 4, + + /** Flush current block to output. */ + block = 5, + + /** Flush up to end of previous block. */ + trees = 6 +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/impl/error.hpp b/include/boost/http/zlib/impl/error.hpp new file mode 100644 index 00000000..212b633c --- /dev/null +++ b/include/boost/http/zlib/impl/error.hpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2024 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_IMPL_ERROR_HPP +#define BOOST_HTTP_ZLIB_IMPL_ERROR_HPP + +#include + +#include +#include + +namespace boost { + +namespace system { +template<> +struct is_error_code_enum< + ::boost::http::zlib::error> +{ + static bool const value = true; +}; +} // system + +namespace http { +namespace zlib { + +namespace detail { + +struct BOOST_SYMBOL_VISIBLE + error_cat_type + : system::error_category +{ + BOOST_HTTP_DECL const char* name( + ) const noexcept override; + BOOST_HTTP_DECL bool failed( + int) const noexcept override; + BOOST_HTTP_DECL std::string message( + int) const override; + BOOST_HTTP_DECL char const* message( + int, char*, std::size_t + ) const noexcept override; + BOOST_SYSTEM_CONSTEXPR error_cat_type() + : error_category(0x43fd42f819852b73) + { + } +}; + +BOOST_HTTP_DECL extern + error_cat_type error_cat; + +} // detail + +inline +BOOST_SYSTEM_CONSTEXPR +system::error_code +make_error_code( + error ev) noexcept +{ + return system::error_code{ + static_cast::type>(ev), + detail::error_cat}; +} + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/inflate.hpp b/include/boost/http/zlib/inflate.hpp new file mode 100644 index 00000000..69ff4067 --- /dev/null +++ b/include/boost/http/zlib/inflate.hpp @@ -0,0 +1,208 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_INFLATE_HPP +#define BOOST_HTTP_ZLIB_INFLATE_HPP + +#include +#include +#include + +namespace boost { +namespace http { +namespace zlib { + +/** Provides the ZLib decompression API. + + This service interface exposes the ZLib inflate (decompression) + functionality through a set of virtual functions. The inflate + algorithm reverses the deflate compression, restoring the + original uncompressed data. + + The windowBits parameter in init2() controls format detection: + - 8..15: zlib format with specified window size + - -8..-15: raw deflate format (no header/trailer) + - 16+windowBits: gzip format only + - 32+windowBits: auto-detect zlib or gzip format + + @code + // Example: Basic decompression + boost::capy::datastore ctx; + auto& inflate_svc = boost::http::zlib::install_inflate_service(ctx); + + boost::http::zlib::stream st = {}; + std::vector compressed_data = get_compressed(); + std::vector output(1024 * 1024); // 1MB buffer + + st.zalloc = nullptr; + st.zfree = nullptr; + st.opaque = nullptr; + + inflate_svc.init(st); + + st.avail_in = compressed_data.size(); + st.next_in = compressed_data.data(); + st.avail_out = output.size(); + st.next_out = output.data(); + + inflate_svc.inflate(st, boost::http::zlib::finish); + output.resize(st.total_out); + + inflate_svc.inflate_end(st); + @endcode + + @code + // Example: Auto-detect gzip or zlib format + boost::http::zlib::stream st = {}; + st.zalloc = nullptr; + st.zfree = nullptr; + + // Auto-detect format (32 + 15 for max window) + inflate_svc.init2(st, 32 + 15); + + st.avail_in = compressed_data.size(); + st.next_in = compressed_data.data(); + st.avail_out = output.size(); + st.next_out = output.data(); + + int result = inflate_svc.inflate(st, boost::http::zlib::no_flush); + // Handle result... + + inflate_svc.inflate_end(st); + @endcode +*/ +struct BOOST_SYMBOL_VISIBLE + inflate_service +{ + /** Return the ZLib version string. */ + virtual char const* version() const noexcept = 0; + + /** Initialize inflate decompression. + @param st The stream to initialize. + @return Zero on success, or an error code. + */ + virtual int init(stream& st) const = 0; + + /** Initialize inflate decompression with extended parameters. + @param st The stream to initialize. + @param windowBits The base-2 logarithm of the window size. + @return Zero on success, or an error code. + */ + virtual int init2(stream& st, int windowBits) const = 0; + + /** Decompress data in the stream. + @param st The stream containing data to decompress. + @param flush The flush mode. + @return Status code indicating decompression state. + */ + virtual int inflate(stream& st, int flush) const = 0; + + /** Release all resources held by the inflate stream. + @param st The stream to finalize. + @return Zero on success, or an error code. + */ + virtual int inflate_end(stream& st) const = 0; + + /** Set the decompression dictionary. + @param st The stream. + @param dict Pointer to the dictionary data. + @param len Length of the dictionary. + @return Zero on success, or an error code. + */ + virtual int set_dict(stream& st, unsigned char const* dict, unsigned len) const = 0; + + /** Return the current decompression dictionary. + @param st The stream. + @param dest Destination buffer for the dictionary. + @param len Pointer to variable receiving dictionary length. + @return Zero on success, or an error code. + */ + virtual int get_dict(stream& st, unsigned char* dest, unsigned* len) const = 0; + + /** Synchronize the decompression state. + @param st The stream to synchronize. + @return Zero on success, or an error code. + */ + virtual int sync(stream& st) const = 0; + + /** Duplicate an inflate stream. + @param dest The destination stream. + @param src The source stream to duplicate. + @return Zero on success, or an error code. + */ + virtual int dup(stream& dest, stream& src) const = 0; + + /** Reset the inflate stream state. + @param st The stream to reset. + @return Zero on success, or an error code. + */ + virtual int reset(stream& st) const = 0; + + /** Reset the inflate stream state with new window size. + @param st The stream to reset. + @param windowBits The base-2 logarithm of the window size. + @return Zero on success, or an error code. + */ + virtual int reset2(stream& st, int windowBits) const = 0; + + /** Insert bits into the input stream. + @param st The stream. + @param bits Number of bits to insert. + @param value The bit pattern to insert. + @return Zero on success, or an error code. + */ + virtual int prime(stream& st, int bits, int value) const = 0; + + /** Return the current inflate mark. + @param st The stream. + @return The mark value, or -1 on error. + */ + virtual long mark(stream& st) const = 0; + + /** Return the gzip header information. + @param st The stream. + @param header Pointer to gzip header structure. + @return Zero on success, or an error code. + */ + virtual int get_header(stream& st, void* header) const = 0; + + /** Initialize backward inflate decompression. + @param st The stream to initialize. + @param windowBits The base-2 logarithm of the window size. + @param window Pointer to the window buffer. + @return Zero on success, or an error code. + */ + virtual int back_init(stream& st, int windowBits, unsigned char* window) const = 0; + + /** Release resources held by backward inflate stream. + @param st The stream to finalize. + @return Zero on success, or an error code. + */ + virtual int back_end(stream& st) const = 0; + + /** Return ZLib compile-time flags. + @return Bit flags indicating compile-time options. + */ + virtual unsigned long compile_flags() const = 0; +}; + +/** Install the inflate service into a polystore. + @param ctx The polystore to install the service into. + @return A reference to the installed inflate service. +*/ +BOOST_HTTP_DECL +inflate_service& +install_inflate_service(capy::polystore& ctx); + +} // zlib +} // http +} // boost + +#endif diff --git a/include/boost/http/zlib/stream.hpp b/include/boost/http/zlib/stream.hpp new file mode 100644 index 00000000..bf8e1209 --- /dev/null +++ b/include/boost/http/zlib/stream.hpp @@ -0,0 +1,107 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef BOOST_HTTP_ZLIB_STREAM_HPP +#define BOOST_HTTP_ZLIB_STREAM_HPP + +#include + +namespace boost { +namespace http { +namespace zlib { + +/** ZLib stream state structure. + + This structure maintains the state for compression and + decompression operations, including input/output buffers, + statistics, and internal state. Applications provide input + data through next_in/avail_in and receive output through + next_out/avail_out. The service updates these fields as + data is processed. + + Before use, initialize zalloc, zfree, and opaque. Set them + to nullptr to use the default allocator. The state field + is managed internally and should not be modified. + + @code + // Example: Initialize a stream for compression + boost::http::zlib::stream st = {}; + st.zalloc = nullptr; // Use default allocator + st.zfree = nullptr; + st.opaque = nullptr; + + // Set up input and output buffers + st.next_in = input_buffer; + st.avail_in = input_size; + st.next_out = output_buffer; + st.avail_out = output_size; + + // After deflate/inflate operations: + // - next_in and next_out are updated to point past processed data + // - avail_in and avail_out are decreased by bytes processed + // - total_in and total_out track cumulative totals + @endcode +*/ +struct stream +{ + /** Allocating function pointer type. */ + using alloc_func = void*(*)(void*, unsigned int, unsigned int); + + /** Deallocating function pointer type. */ + using free_func = void(*)(void*, void*); + + /** Pointer to next input byte. */ + unsigned char* next_in; + + /** Number of bytes available at next_in. */ + unsigned int avail_in; + + /** Total number of input bytes read so far. */ + unsigned long total_in; + + /** Pointer where next output byte will be placed. */ + unsigned char* next_out; + + /** Remaining free space at next_out. */ + unsigned int avail_out; + + /** Total number of bytes output so far. */ + unsigned long total_out; + + /** Last error message, NULL if no error. */ + char* msg; + + /** Internal state, not visible to applications. */ + void* state; + + /** Function used to allocate internal state. */ + alloc_func zalloc; + + /** Function used to deallocate internal state. */ + free_func zfree; + + /** Private data object passed to zalloc and zfree. */ + void* opaque; + + /** Best guess about data type (binary or text for deflate, decoding state for inflate). */ + int data_type; + + /** Adler-32 or CRC-32 value of the uncompressed data. */ + unsigned long adler; + + /** Reserved for future use. */ + unsigned long reserved; +}; + +} // zlib +} // http +} // boost + +#endif diff --git a/src/brotli/error.cpp b/src/brotli/error.cpp new file mode 100644 index 00000000..9f5a3459 --- /dev/null +++ b/src/brotli/error.cpp @@ -0,0 +1,104 @@ +// +// Copyright (c) 2024 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include + +namespace boost { +namespace http { +namespace brotli { +namespace detail { + +const char* +error_cat_type:: +name() const noexcept +{ + return "boost.http.brotli"; +} + +bool +error_cat_type:: +failed(int ev) const noexcept +{ + return ev < 0; +} + +std::string +error_cat_type:: +message(int ev) const +{ + return message(ev, nullptr, 0); +} + +char const* +error_cat_type:: +message( + int ev, + char*, + std::size_t) const noexcept +{ + switch(static_cast(ev)) + { + + case error::no_error: return "no_error"; + case error::success: return "success"; + case error::needs_more_input: return "needs_more_input"; + case error::needs_more_output: return "needs_more_output"; + case error::format_exuberant_nibble: return "format_exuberant_nibble"; + case error::format_reserved: return "format_reserved"; + case error::format_exuberant_meta_nibbl: return "format_exuberant_meta_nibbl"; + case error::format_simple_huffman_alphabet: return "format_simple_huffman_alphabet"; + case error::format_simple_huffman_same: return "format_simple_huffman_same"; + case error::format_cl_space: return "format_cl_space"; + case error::format_huffman_space: return "format_huffman_space"; + case error::format_context_map_repea: return "format_context_map_repea"; + case error::format_block_length_1: return "format_block_length_1"; + case error::format_block_length_2: return "format_block_length_2"; + case error::format_transform: return "format_transform"; + case error::format_dictionary: return "format_dictionary"; + case error::format_window_bits: return "format_window_bits"; + case error::format_padding_1: return "format_padding_1"; + case error::format_padding_2: return "format_padding_2"; + case error::format_distance: return "format_distance"; + case error::compound_dictionary: return "compound_dictionary"; + case error::dictionary_not_set: return "dictionary_not_set"; + case error::invalid_arguments: return "invalid_arguments"; + case error::alloc_context_modes: return "alloc_context_modes"; + case error::alloc_tree_groups: return "alloc_tree_groups"; + case error::alloc_context_map: return "alloc_context_map"; + case error::alloc_ring_buffer_1: return "alloc_ring_buffer_1"; + case error::alloc_ring_buffer_2: return "alloc_ring_buffer_2"; + case error::alloc_block_type_trees: return "alloc_block_type_trees"; + case error::unreachable: return "unreachable"; + default: + return "unknown"; + } +} + +// msvc 14.0 has a bug that warns about inability +// to use constexpr construction here, even though +// there's no constexpr construction +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( push ) +# pragma warning( disable : 4592 ) +#endif + +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +constinit error_cat_type error_cat; +#else +error_cat_type error_cat; +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( pop ) +#endif + +} // detail +} // brotli +} // http +} // boost diff --git a/src/detail/zlib_filter_base.hpp b/src/detail/zlib_filter_base.hpp index 110fae2d..a6634016 100644 --- a/src/detail/zlib_filter_base.hpp +++ b/src/detail/zlib_filter_base.hpp @@ -15,8 +15,8 @@ #include "src/detail/filter.hpp" -#include -#include +#include +#include namespace boost { namespace http { @@ -35,7 +35,7 @@ class zlib_filter_base : public filter } protected: - capy::zlib::stream strm_; + http::zlib::stream strm_; static unsigned int diff --git a/src/parser.cpp b/src/parser.cpp index 4900637c..1cab1e53 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -20,10 +20,10 @@ #include #include #include -#include +#include #include -#include -#include +#include +#include #include #include #include @@ -305,7 +305,7 @@ clamp( class zlib_filter : public detail::zlib_filter_base { - capy::zlib::inflate_service& svc_; + http::zlib::inflate_service& svc_; public: zlib_filter( @@ -313,11 +313,11 @@ class zlib_filter http::detail::workspace& ws, int window_bits) : zlib_filter_base(ws) - , svc_(ctx.get()) + , svc_(ctx.get()) { - system::error_code ec = static_cast( + system::error_code ec = static_cast( svc_.init2(strm_, window_bits)); - if(ec != capy::zlib::error::ok) + if(ec != http::zlib::error::ok) detail::throw_system_error(ec); } @@ -334,17 +334,17 @@ class zlib_filter strm_.next_in = static_cast(const_cast(in.data())); strm_.avail_in = saturate_cast(in.size()); - auto rs = static_cast( + auto rs = static_cast( svc_.inflate( strm_, - more ? capy::zlib::no_flush : capy::zlib::finish)); + more ? http::zlib::no_flush : http::zlib::finish)); results rv; rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out; rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in; - rv.finished = (rs == capy::zlib::error::stream_end); + rv.finished = (rs == http::zlib::error::stream_end); - if(rs < capy::zlib::error::ok && rs != capy::zlib::error::buf_err) + if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err) rv.ec = rs; return rv; @@ -354,14 +354,14 @@ class zlib_filter class brotli_filter : public detail::brotli_filter_base { - capy::brotli::decode_service& svc_; - capy::brotli::decoder_state* state_; + http::brotli::decode_service& svc_; + http::brotli::decoder_state* state_; public: brotli_filter( const capy::polystore& ctx, http::detail::workspace&) - : svc_(ctx.get()) + : svc_(ctx.get()) { // TODO: use custom allocator state_ = svc_.create_instance(nullptr, nullptr, nullptr); @@ -401,10 +401,10 @@ class brotli_filter rv.out_bytes = out.size() - available_out; rv.finished = svc_.is_finished(state_); - if(!more && rs == capy::brotli::decoder_result::needs_more_input) + if(!more && rs == http::brotli::decoder_result::needs_more_input) rv.ec = BOOST_HTTP_ERR(error::bad_payload); - if(rs == capy::brotli::decoder_result::error) + if(rs == http::brotli::decoder_result::error) rv.ec = BOOST_HTTP_ERR( svc_.get_error_code(state_)); diff --git a/src/serializer.cpp b/src/serializer.cpp index a384c69c..57355eb7 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -23,13 +23,13 @@ #include #include #include -#include +#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include @@ -90,7 +90,7 @@ write_chunk_header( class zlib_filter : public detail::zlib_filter_base { - capy::zlib::deflate_service& svc_; + http::zlib::deflate_service& svc_; public: zlib_filter( @@ -100,16 +100,16 @@ class zlib_filter int window_bits, int mem_level) : zlib_filter_base(ws) - , svc_(ctx.get()) + , svc_(ctx.get()) { - system::error_code ec = static_cast(svc_.init2( + system::error_code ec = static_cast(svc_.init2( strm_, comp_level, - capy::zlib::deflated, + http::zlib::deflated, window_bits, mem_level, - capy::zlib::default_strategy)); - if(ec != capy::zlib::error::ok) + http::zlib::default_strategy)); + if(ec != http::zlib::error::ok) detail::throw_system_error(ec); } @@ -135,17 +135,17 @@ class zlib_filter strm_.next_in = static_cast(const_cast(in.data())); strm_.avail_in = saturate_cast(in.size()); - auto rs = static_cast( + auto rs = static_cast( svc_.deflate( strm_, - more ? capy::zlib::no_flush : capy::zlib::finish)); + more ? http::zlib::no_flush : http::zlib::finish)); results rv; rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out; rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in; - rv.finished = (rs == capy::zlib::error::stream_end); + rv.finished = (rs == http::zlib::error::stream_end); - if(rs < capy::zlib::error::ok && rs != capy::zlib::error::buf_err) + if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err) rv.ec = rs; return rv; @@ -155,8 +155,8 @@ class zlib_filter class brotli_filter : public detail::brotli_filter_base { - capy::brotli::encode_service& svc_; - capy::brotli::encoder_state* state_; + http::brotli::encode_service& svc_; + http::brotli::encoder_state* state_; public: brotli_filter( @@ -164,13 +164,13 @@ class brotli_filter http::detail::workspace&, std::uint32_t comp_quality, std::uint32_t comp_window) - : svc_(ctx.get()) + : svc_(ctx.get()) { // TODO: use custom allocator state_ = svc_.create_instance(nullptr, nullptr, nullptr); if(!state_) detail::throw_bad_alloc(); - using encoder_parameter = capy::brotli::encoder_parameter; + using encoder_parameter = http::brotli::encoder_parameter; svc_.set_parameter(state_, encoder_parameter::quality, comp_quality); svc_.set_parameter(state_, encoder_parameter::lgwin, comp_window); } @@ -194,7 +194,7 @@ class brotli_filter auto available_out = out.size(); using encoder_operation = - capy::brotli::encoder_operation; + http::brotli::encoder_operation; bool rs = svc_.compress_stream( state_, diff --git a/src/zlib/error.cpp b/src/zlib/error.cpp new file mode 100644 index 00000000..d09599cf --- /dev/null +++ b/src/zlib/error.cpp @@ -0,0 +1,82 @@ +// +// Copyright (c) 2024 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include + +namespace boost { +namespace http { +namespace zlib { +namespace detail { + +const char* +error_cat_type:: +name() const noexcept +{ + return "boost.http.zlib"; +} + +bool +error_cat_type:: +failed(int ev) const noexcept +{ + return ev < 0; +} + +std::string +error_cat_type:: +message(int ev) const +{ + return message(ev, nullptr, 0); +} + +char const* +error_cat_type:: +message( + int ev, + char*, + std::size_t) const noexcept +{ + switch(static_cast(ev)) + { + case error::ok: return "Z_OK"; + case error::stream_end: return "Z_STREAM_END"; + case error::need_dict: return "Z_NEED_DICT"; + case error::errno_: return "Z_ERRNO"; + case error::stream_err: return "Z_STREAM_ERROR"; + case error::data_err: return "Z_DATA_ERROR"; + case error::mem_err: return "Z_MEM_ERROR"; + case error::buf_err: return "Z_BUF_ERROR"; + case error::version_err: return "Z_VERSION_ERROR"; + default: + return "unknown"; + } +} + +// msvc 14.0 has a bug that warns about inability +// to use constexpr construction here, even though +// there's no constexpr construction +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( push ) +# pragma warning( disable : 4592 ) +#endif + +#if defined(__cpp_constinit) && __cpp_constinit >= 201907L +constinit error_cat_type error_cat; +#else +error_cat_type error_cat; +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +# pragma warning( pop ) +#endif + +} // detail +} // zlib +} // http +} // boost diff --git a/src_brotli/decode.cpp b/src_brotli/decode.cpp new file mode 100644 index 00000000..b2d16b40 --- /dev/null +++ b/src_brotli/decode.cpp @@ -0,0 +1,188 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +class decode_service_impl + : public decode_service +{ +public: + using key_type = decode_service; + + explicit + decode_service_impl( + capy::polystore&) noexcept + { + } + + ~decode_service_impl() + { + } + + bool + set_parameter( + decoder_state* state, + decoder_param param, + std::uint32_t value) const noexcept override + { + return BrotliDecoderSetParameter( + reinterpret_cast(state), + static_cast(param), + value); + } + + decoder_state* + create_instance(alloc_func alloc_func, free_func free_func, void* opaque) + const noexcept override + { + return reinterpret_cast( + BrotliDecoderCreateInstance( + alloc_func, + free_func, + opaque)); + } + + void + destroy_instance(decoder_state* state) const noexcept override + { + BrotliDecoderDestroyInstance( + reinterpret_cast(state)); + } + + decoder_result + decompress( + std::size_t encoded_size, + const std::uint8_t encoded_buffer[], + std::size_t* decoded_size, + std::uint8_t decoded_buffer[]) const noexcept override + { + return static_cast( + BrotliDecoderDecompress( + encoded_size, + encoded_buffer, + decoded_size, + decoded_buffer)); + } + + decoder_result + decompress_stream( + decoder_state* state, + std::size_t* available_in, + const std::uint8_t** next_in, + std::size_t* available_out, + std::uint8_t** next_out, + std::size_t* total_out) const noexcept override + { + return static_cast( + BrotliDecoderDecompressStream( + reinterpret_cast(state), + available_in, + next_in, + available_out, + next_out, + total_out)); + } + + bool + has_more_output(const decoder_state* state) const noexcept override + { + return BrotliDecoderHasMoreOutput( + reinterpret_cast(state)); + } + + const std::uint8_t* + take_output(decoder_state* state, std::size_t* size) const noexcept override + { + return BrotliDecoderTakeOutput( + reinterpret_cast(state), + size); + } + + bool + is_used(const decoder_state* state) const noexcept override + { + return BrotliDecoderIsUsed( + reinterpret_cast(state)); + } + + bool + is_finished(const decoder_state* state) const noexcept override + { + return BrotliDecoderIsFinished( + reinterpret_cast(state)); + } + + error + get_error_code(const decoder_state* state) const noexcept override + { + return static_cast( + BrotliDecoderGetErrorCode( + reinterpret_cast(state))); + } + + const char* + error_string(error c) const noexcept override + { + return BrotliDecoderErrorString( + static_cast(c)); + } + + std::uint32_t + version() const noexcept override + { + return BrotliDecoderVersion(); + } + +#if 0 + bool + attach_dictionary( + decoder_state* state, + shared_dictionary_type type, + std::size_t data_size, + const std::uint8_t data[]) const noexcept override + { + return BrotliDecoderAttachDictionary( + reinterpret_cast(state), + static_cast(type), + data_size, + data); + } + + void + set_metadata_callbacks( + decoder_state* state, + metadata_start_func start_func, + metadata_chunk_func chunk_func, + void* opaque) const noexcept override + { + BrotliDecoderSetMetadataCallbacks( + reinterpret_cast(state), + start_func, + chunk_func, + opaque); + } +#endif +}; + +decode_service& +install_decode_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // brotli +} // http +} // boost diff --git a/src_brotli/encode.cpp b/src_brotli/encode.cpp new file mode 100644 index 00000000..b0bedcde --- /dev/null +++ b/src_brotli/encode.cpp @@ -0,0 +1,207 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include + +namespace boost { +namespace http { +namespace brotli { + +class encode_service_impl + : public encode_service +{ +public: + using key_type = encode_service; + + explicit + encode_service_impl( + capy::polystore&) noexcept + { + } + + ~encode_service_impl() + { + } + + bool + set_parameter( + encoder_state* state, + encoder_parameter param, + std::uint32_t value) const noexcept override + { + return BrotliEncoderSetParameter( + reinterpret_cast(state), + static_cast(param), + value); + } + + encoder_state* + create_instance(alloc_func alloc_func, free_func free_func, void* opaque) + const noexcept override + { + return reinterpret_cast( + BrotliEncoderCreateInstance( + alloc_func, + free_func, + opaque)); + } + + void + destroy_instance(encoder_state* state) const noexcept override + { + BrotliEncoderDestroyInstance( + reinterpret_cast(state)); + } + + std::size_t + max_compressed_size(std::size_t input_size) const noexcept override + { + return BrotliEncoderMaxCompressedSize(input_size); + } + + bool + compress( + int quality, + int lgwin, + encoder_mode mode, + std::size_t input_size, + const std::uint8_t input_buffer[], + std::size_t* encoded_size, + std::uint8_t encoded_buffer[]) const noexcept override + { + return BrotliEncoderCompress( + quality, + lgwin, + static_cast(mode), + input_size, + input_buffer, + encoded_size, + encoded_buffer); + } + + bool + compress_stream( + encoder_state* state, + encoder_operation op, + std::size_t* available_in, + const std::uint8_t** next_in, + std::size_t* available_out, + std::uint8_t** next_out, + std::size_t* total_out) const noexcept override + { + return BrotliEncoderCompressStream( + reinterpret_cast(state), + static_cast(op), + available_in, + next_in, + available_out, + next_out, + total_out); + } + + bool + is_finished(encoder_state* state) const noexcept override + { + return BrotliEncoderIsFinished( + reinterpret_cast(state)); + } + + bool + has_more_output(encoder_state* state) const noexcept override + { + return BrotliEncoderHasMoreOutput( + reinterpret_cast(state)); + } + + const std::uint8_t* + take_output(encoder_state* state, std::size_t* size) const noexcept override + { + return BrotliEncoderTakeOutput( + reinterpret_cast(state), + size); + } + + std::uint32_t + version() const noexcept override + { + return BrotliEncoderVersion(); + } + +#if 0 + encoder_prepared_dictionary* + prepare_dictionary( + shared_dictionary_type type, + std::size_t data_size, + const std::uint8_t data[], + int quality, + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept override + { + return reinterpret_cast( + BrotliEncoderPrepareDictionary( + static_cast(type), + data_size, + data, + quality, + alloc_func, + free_func, + opaque)); + } + + void + destroy_prepared_dictionary( + encoder_prepared_dictionary* dictionary) const noexcept override + { + BrotliEncoderDestroyPreparedDictionary( + reinterpret_cast(dictionary)); + } + + bool + attach_prepared_dictionary( + encoder_state* state, + const encoder_prepared_dictionary* dictionary) const noexcept override + { + return BrotliEncoderAttachPreparedDictionary( + reinterpret_cast(state), + reinterpret_cast(dictionary)); + } + + std::size_t + estimate_peak_memory_usage(int quality, int lgwin, std::size_t input_size) + const noexcept override + { + return BrotliEncoderEstimatePeakMemoryUsage( + quality, + lgwin, + input_size); + } + + std::size_t + get_prepared_dictionary_size( + const encoder_prepared_dictionary* dictionary) const noexcept override + { + return BrotliEncoderGetPreparedDictionarySize( + reinterpret_cast(dictionary)); + } +#endif +}; + +encode_service& +install_encode_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // brotli +} // http +} // boost diff --git a/src_brotli/shared_dictionary.cpp b/src_brotli/shared_dictionary.cpp new file mode 100644 index 00000000..812a8434 --- /dev/null +++ b/src_brotli/shared_dictionary.cpp @@ -0,0 +1,82 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#if 0 +#include +#endif + +namespace boost { +namespace http { +namespace brotli { + +class shared_dictionary_service_impl + : public shared_dictionary_service +{ +public: + using key_type = shared_dictionary_service; + + explicit + shared_dictionary_service_impl( + capy::polystore&) noexcept + { + } + + ~shared_dictionary_service_impl() + { + } +#if 0 + shared_dictionary* + create_instance( + alloc_func alloc_func, + free_func free_func, + void* opaque) const noexcept override + { + return reinterpret_cast( + BrotliSharedDictionaryCreateInstance( + alloc_func, + free_func, + opaque)); + } + + void + destroy_instance( + shared_dictionary* dict) const noexcept override + { + BrotliSharedDictionaryDestroyInstance( + reinterpret_cast(dict)); + } + + bool + attach( + shared_dictionary* dict, + shared_dictionary_type type, + std::size_t data_size, + const uint8_t data[]) const noexcept override + { + return BrotliSharedDictionaryAttach( + reinterpret_cast(dict), + static_cast(type), + data_size, + data); + } +#endif +}; + +shared_dictionary_service& +install_shared_dictionary_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // brotli +} // http +} // boost diff --git a/src_zlib/deflate.cpp b/src_zlib/deflate.cpp new file mode 100644 index 00000000..44147517 --- /dev/null +++ b/src_zlib/deflate.cpp @@ -0,0 +1,188 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include "stream_cast.hpp" + +#include + +#include + +namespace boost { +namespace http { +namespace zlib { + +BOOST_CORE_STATIC_ASSERT(sizeof(stream) == sizeof(z_stream_s)); +BOOST_CORE_STATIC_ASSERT(is_layout_identical()); + +//------------------------------------------------ + +class deflate_service_impl + : public deflate_service +{ +public: + using key_type = deflate_service; + + explicit + deflate_service_impl( + capy::polystore&) noexcept + { + } + + ~deflate_service_impl() + { + } + + char const* + version() const noexcept override + { + return zlibVersion(); + } + + int + init( + stream& st, + int level) const override + { + stream_cast sc(st); + return deflateInit(sc.get(), level); + } + + int + init2( + stream& st, + int level, + int method, + int windowBits, + int memLevel, + int strategy) const override + { + stream_cast sc(st); + return deflateInit2(sc.get(), + level, method, windowBits, + memLevel, strategy); + } + + int + set_dict( + stream& st, + unsigned char const* dict, + unsigned len) const override + { + stream_cast sc(st); + return deflateSetDictionary(sc.get(), dict, len); + } + + int + get_dict( + stream& st, + unsigned char* dest, + unsigned* len) const override + { + stream_cast sc(st); + return deflateGetDictionary(sc.get(), dest, len); + } + + int + dup( + stream& dest, + stream& src) const override + { + stream_cast sc0(dest); + stream_cast sc1(src); + return deflateCopy(sc0.get(), sc1.get()); + } + + int + deflate( + stream& st, + int flush) const override + { + stream_cast sc(st); + return ::deflate(sc.get(), flush); + } + + int + deflate_end( + stream& st) const override + { + stream_cast sc(st); + return deflateEnd(sc.get()); + } + + int + reset( + stream& st) const override + { + stream_cast sc(st); + return deflateReset(sc.get()); + } + + int + params( + stream& st, + int level, + int strategy) const override + { + stream_cast sc(st); + return deflateParams(sc.get(), level, strategy); + } + + std::size_t + bound( + stream& st, + unsigned long sourceLen) const override + { + stream_cast sc(st); + return deflateBound(sc.get(), sourceLen); + } + + int + pending( + stream& st, + unsigned* pending, + int* bits) const override + { + stream_cast sc(st); + return deflatePending(sc.get(), pending, bits); + } + + int + prime( + stream& st, + int bits, + int value) const override + { + stream_cast sc(st); + return deflatePrime(sc.get(), bits, value); + } + + int + set_header( + stream& st, + void* header) const override + { + stream_cast sc(st); + return deflateSetHeader(sc.get(), + reinterpret_cast(header)); + } +}; + +deflate_service& +install_deflate_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // zlib +} // http +} // boost diff --git a/src_zlib/inflate.cpp b/src_zlib/inflate.cpp new file mode 100644 index 00000000..2553716e --- /dev/null +++ b/src_zlib/inflate.cpp @@ -0,0 +1,201 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include "stream_cast.hpp" + +#include + +#include + +namespace boost { +namespace http { +namespace zlib { + +BOOST_CORE_STATIC_ASSERT(sizeof(stream) == sizeof(z_stream_s)); +BOOST_CORE_STATIC_ASSERT(is_layout_identical()); + +//------------------------------------------------ + +class inflate_service_impl + : public inflate_service +{ +public: + using key_type = inflate_service; + + explicit + inflate_service_impl( + capy::polystore&) noexcept + { + } + + ~inflate_service_impl() + { + } + + char const* + version() const noexcept override + { + return zlibVersion(); + } + + int + init( + stream& st) const override + { + stream_cast sc(st); + return inflateInit(sc.get()); + } + + int + init2( + stream& st, + int windowBits) const override + { + stream_cast sc(st); + return inflateInit2(sc.get(), windowBits); + } + + int + inflate( + stream& st, + int flush) const override + { + stream_cast sc(st); + return ::inflate(sc.get(), flush); + } + + int + inflate_end( + stream& st) const override + { + stream_cast sc(st); + return inflateEnd(sc.get()); + } + + int + set_dict( + stream& st, + unsigned char const* dict, + unsigned len) const override + { + stream_cast sc(st); + return inflateSetDictionary(sc.get(), dict, len); + } + + int + get_dict( + stream& st, + unsigned char* dest, + unsigned* len) const override + { + stream_cast sc(st); + return inflateGetDictionary(sc.get(), dest, len); + } + + int + sync( + stream& st) const override + { + stream_cast sc(st); + return inflateSync(sc.get()); + } + + int + dup( + stream& dest, + stream& src) const override + { + stream_cast sc0(dest); + stream_cast sc1(src); + return inflateCopy(sc0.get(), sc1.get()); + } + + int + reset( + stream& st) const override + { + stream_cast sc(st); + return inflateReset(sc.get()); + } + + int + reset2( + stream& st, + int windowBits) const override + { + stream_cast sc(st); + return inflateReset2(sc.get(), windowBits); + } + + int + prime( + stream& st, + int bits, + int value) const override + { + stream_cast sc(st); + return inflatePrime(sc.get(), bits, value); + } + + long + mark( + stream& st) const override + { + stream_cast sc(st); + return inflateMark(sc.get()); + } + + int + get_header( + stream& st, + void* header) const override + { + stream_cast sc(st); + return inflateGetHeader(sc.get(), + reinterpret_cast(header)); + } + + int + back_init( + stream& st, + int windowBits, + unsigned char* window) const override + { + stream_cast sc(st); + return inflateBackInit(sc.get(), windowBits, window); + } + + int + back_end( + stream& st) const override + { + stream_cast sc(st); + return inflateBackEnd(sc.get()); + } + + unsigned long + compile_flags() const override + { + return zlibCompileFlags(); + } +}; + +inflate_service& +install_inflate_service(capy::polystore& ctx) +{ + return ctx.emplace(ctx); +} + +} // zlib +} // http +} // boost diff --git a/src_zlib/stream_cast.hpp b/src_zlib/stream_cast.hpp new file mode 100644 index 00000000..5772f4e1 --- /dev/null +++ b/src_zlib/stream_cast.hpp @@ -0,0 +1,145 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#ifndef SRC_HTTP_ZLIB_STREAM_CAST_HPP +#define SRC_HTTP_ZLIB_STREAM_CAST_HPP + +#include + +#include + +#include + +namespace boost { +namespace http { +namespace zlib { + +//------------------------------------------------ + +#define SAME_FIELD(T1,T2,M) \ + offsetof(T1,M)==offsetof(T2,M) && \ + sizeof(decltype(T1::M)) == sizeof(decltype(T2::M)) + +template +constexpr +bool +is_layout_identical() +{ + return + sizeof(T1) == sizeof(T2) && + SAME_FIELD(T1, T2, next_in) && + SAME_FIELD(T1, T2, avail_in) && + SAME_FIELD(T1, T2, total_in) && + SAME_FIELD(T1, T2, next_out) && + SAME_FIELD(T1, T2, avail_out) && + SAME_FIELD(T1, T2, total_out) && + SAME_FIELD(T1, T2, msg) && + SAME_FIELD(T1, T2, state) && + SAME_FIELD(T1, T2, zalloc) && + SAME_FIELD(T1, T2, zfree) && + SAME_FIELD(T1, T2, opaque) && + SAME_FIELD(T1, T2, data_type) && + SAME_FIELD(T1, T2, adler) && + SAME_FIELD(T1, T2, reserved) + ; +} + +//------------------------------------------------ + +template()> +struct stream_cast_impl +{ + explicit + stream_cast_impl( + stream& st) + : pzs_(reinterpret_cast(&st)) + , st_(st) + { + zs_.next_in = st_.next_in; + zs_.avail_in = st_.avail_in; + zs_.total_in = st_.total_in; + zs_.next_out = st_.next_out; + zs_.avail_out = st_.avail_out; + zs_.total_out = st_.total_out; + zs_.msg = nullptr; + zs_.state = reinterpret_cast< + internal_state*>(st_.state); + zs_.zalloc = st_.zalloc; + zs_.zfree = st_.zfree; + zs_.opaque = st_.opaque; + zs_.data_type = st_.data_type; + zs_.adler = st_.adler; + zs_.reserved = st_.reserved; + } + + ~stream_cast_impl() + { + st_.next_in = zs_.next_in; + st_.avail_in = zs_.avail_in; + st_.total_in = zs_.total_in; + st_.next_out = zs_.next_out; + st_.avail_out = zs_.avail_out; + st_.total_out = zs_.total_out; + st_.msg = zs_.msg; + st_.state = zs_.state; + st_.zalloc = zs_.zalloc; + st_.zfree = zs_.zfree; + st_.opaque = zs_.opaque; + st_.data_type = zs_.data_type; + st_.adler = zs_.adler; + st_.reserved = zs_.reserved; + } + + z_stream_s* + get() noexcept + { + return pzs_; + } + +private: + z_stream_s* pzs_ = nullptr; + stream& st_; + z_stream_s zs_; +}; + +//------------------------------------------------ + +template<> +struct stream_cast_impl +{ + explicit + stream_cast_impl( + stream& st) + // VFALCO A pinch of undefined behavior here + : pzs_(reinterpret_cast(&st)) + { + + } + + z_stream_s* + get() noexcept + { + return pzs_; + } + +private: + z_stream_s* pzs_; +}; + +//------------------------------------------------ + +using stream_cast = stream_cast_impl<>; + +} // zlib +} // http +} // boost + +#endif diff --git a/test/limits/CMakeLists.txt b/test/limits/CMakeLists.txt index 806ace39..94cccb29 100644 --- a/test/limits/CMakeLists.txt +++ b/test/limits/CMakeLists.txt @@ -21,6 +21,7 @@ target_compile_definitions(boost_http_limits PRIVATE BOOST_HTTP_TEST_LIMITS BOOST_HTTP_NO_LIB BOOST_HTTP_STATIC_LINK + BOOST_COROSIO_NO_LIB ) target_link_libraries(boost_http_limits PRIVATE Boost::align @@ -32,6 +33,8 @@ target_link_libraries(boost_http_limits PRIVATE Boost::throw_exception Boost::url Boost::utility) +# Add corosio headers without linking (header-only usage) +target_include_directories(boost_http_limits PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/../../../corosio/include") target_link_libraries(boost_http_limits INTERFACE Boost::http) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index f49361e0..8de1aeb4 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -26,12 +26,12 @@ target_link_libraries( Boost::http Boost::filesystem) -if (TARGET Boost::capy_zlib) - target_link_libraries(boost_http_tests PRIVATE Boost::capy_zlib) +if (TARGET Boost::http_zlib) + target_link_libraries(boost_http_tests PRIVATE Boost::http_zlib) endif () -if (TARGET Boost::capy_brotli) - target_link_libraries(boost_http_tests PRIVATE Boost::capy_brotli) +if (TARGET Boost::http_brotli) + target_link_libraries(boost_http_tests PRIVATE Boost::http_brotli) endif () # Register individual tests with CTest diff --git a/test/unit/Jamfile b/test/unit/Jamfile index 9f31c989..1d4d66ff 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -17,8 +17,8 @@ project $(c11-requires) /boost/http//boost_http /boost/url//boost_url - [ ac.check-library /boost/capy//boost_capy_zlib : /boost/capy//boost_capy_zlib : ] - [ ac.check-library /boost/capy//boost_capy_brotli : /boost/capy//boost_capy_brotli : ] + [ ac.check-library /boost/http//boost_http_zlib : /boost/http//boost_http_zlib : ] + [ ac.check-library /boost/http//boost_http_brotli : /boost/http//boost_http_brotli : ] ../../../url/extra/test_suite/test_main.cpp ../../../url/extra/test_suite/test_suite.cpp ./test_helpers.cpp diff --git a/test/unit/bcrypt/error.cpp b/test/unit/bcrypt/bcrypt_error.cpp similarity index 100% rename from test/unit/bcrypt/error.cpp rename to test/unit/bcrypt/bcrypt_error.cpp diff --git a/test/unit/bcrypt/hash.cpp b/test/unit/bcrypt/bcrypt_hash.cpp similarity index 100% rename from test/unit/bcrypt/hash.cpp rename to test/unit/bcrypt/bcrypt_hash.cpp diff --git a/test/unit/bcrypt/result.cpp b/test/unit/bcrypt/bcrypt_result.cpp similarity index 100% rename from test/unit/bcrypt/result.cpp rename to test/unit/bcrypt/bcrypt_result.cpp diff --git a/test/unit/bcrypt/version.cpp b/test/unit/bcrypt/bcrypt_version.cpp similarity index 100% rename from test/unit/bcrypt/version.cpp rename to test/unit/bcrypt/bcrypt_version.cpp diff --git a/test/unit/brotli.cpp b/test/unit/brotli.cpp new file mode 100644 index 00000000..fa30c0cb --- /dev/null +++ b/test/unit/brotli.cpp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include "test_helpers.hpp" + +namespace boost { +namespace http { + +struct brotli_test +{ + void + test_error_code() + { + // TODO + boost::system::error_code ec{ brotli::error::no_error }; + } + + void + test_decode() + { + // TODO + capy::polystore ctx; + brotli::install_decode_service(ctx); + } + + void + test_encode() + { + // TODO + capy::polystore ctx; + brotli::install_encode_service(ctx); + } + + void + run() + { + test_error_code(); + #ifdef BOOST_HTTP_HAS_BROTLI + test_decode(); + test_encode(); + #endif + } +}; + +TEST_SUITE(brotli_test, "boost.http.brotli"); + +} // namespace http +} // namespace boost diff --git a/test/unit/brotli/brotli_error.cpp b/test/unit/brotli/brotli_error.cpp new file mode 100644 index 00000000..07bd1b9f --- /dev/null +++ b/test/unit/brotli/brotli_error.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/brotli/decode.cpp b/test/unit/brotli/decode.cpp new file mode 100644 index 00000000..90befaf8 --- /dev/null +++ b/test/unit/brotli/decode.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/brotli/encode.cpp b/test/unit/brotli/encode.cpp new file mode 100644 index 00000000..72662299 --- /dev/null +++ b/test/unit/brotli/encode.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/brotli/shared_dictionary.cpp b/test/unit/brotli/shared_dictionary.cpp new file mode 100644 index 00000000..2cae9966 --- /dev/null +++ b/test/unit/brotli/shared_dictionary.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/brotli/types.cpp b/test/unit/brotli/types.cpp new file mode 100644 index 00000000..770edadc --- /dev/null +++ b/test/unit/brotli/types.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/compression.cpp b/test/unit/compression.cpp index cf89c902..65815367 100644 --- a/test/unit/compression.cpp +++ b/test/unit/compression.cpp @@ -18,9 +18,9 @@ #include #include #include -#include +#include #include -#include +#include #include "test_helpers.hpp" @@ -67,7 +67,7 @@ struct zlib_test if(encoding == "deflate" || encoding == "gzip") { - namespace zlib = capy::zlib; + namespace zlib = http::zlib; auto& svc = ctx.get(); zlib::stream zs{}; @@ -103,7 +103,7 @@ struct zlib_test } else if(encoding == "br") { - namespace brotli = capy::brotli; + namespace brotli = http::brotli; auto& svc = ctx.get(); brotli::encoder_state* state = @@ -155,7 +155,7 @@ struct zlib_test { if(encoding == "deflate" || encoding == "gzip") { - namespace zlib = capy::zlib; + namespace zlib = http::zlib; auto& svc = ctx.get(); zlib::stream zs{}; @@ -194,7 +194,7 @@ struct zlib_test } else if(encoding == "br") { - namespace brotli = capy::brotli; + namespace brotli = http::brotli; auto& svc = ctx.get(); brotli::decoder_state* state = @@ -383,18 +383,18 @@ struct zlib_test std::vector encodings; serializer::config cfg; - #ifdef BOOST_CAPY_HAS_ZLIB + #ifdef BOOST_HTTP_HAS_ZLIB cfg.apply_deflate_encoder = true; cfg.apply_gzip_encoder = true; - capy::zlib::install_deflate_service(ctx); - capy::zlib::install_inflate_service(ctx); + http::zlib::install_deflate_service(ctx); + http::zlib::install_inflate_service(ctx); encodings.push_back("gzip"); encodings.push_back("deflate"); #endif - #ifdef BOOST_CAPY_HAS_BROTLI + #ifdef BOOST_HTTP_HAS_BROTLI cfg.apply_brotli_encoder = true; - capy::brotli::install_encode_service(ctx); - capy::brotli::install_decode_service(ctx); + http::brotli::install_encode_service(ctx); + http::brotli::install_decode_service(ctx); encodings.push_back("br"); #endif @@ -619,18 +619,18 @@ struct zlib_test std::vector encodings; response_parser::config cfg; - #ifdef BOOST_CAPY_HAS_ZLIB + #ifdef BOOST_HTTP_HAS_ZLIB cfg.apply_deflate_decoder = true; cfg.apply_gzip_decoder = true; - capy::zlib::install_deflate_service(ctx); - capy::zlib::install_inflate_service(ctx); + http::zlib::install_deflate_service(ctx); + http::zlib::install_inflate_service(ctx); encodings.push_back("gzip"); encodings.push_back("deflate"); #endif - #ifdef BOOST_CAPY_HAS_BROTLI + #ifdef BOOST_HTTP_HAS_BROTLI cfg.apply_brotli_decoder = true; - capy::brotli::install_encode_service(ctx); - capy::brotli::install_decode_service(ctx); + http::brotli::install_encode_service(ctx); + http::brotli::install_decode_service(ctx); encodings.push_back("br"); #endif diff --git a/test/unit/zlib.cpp b/test/unit/zlib.cpp new file mode 100644 index 00000000..62881f4a --- /dev/null +++ b/test/unit/zlib.cpp @@ -0,0 +1,57 @@ +// +// Copyright (c) 2025 Mohammad Nejati +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +#include +#include + +#include "test_helpers.hpp" + +namespace boost { +namespace http { + +struct zlib_test +{ + void + test_error_code() + { + // TODO + boost::system::error_code ec{ zlib::error::buf_err }; + } + + void + test_deflate() + { + // TODO + capy::polystore ctx; + zlib::install_deflate_service(ctx); + } + + void + test_inflate() + { + // TODO + capy::polystore ctx; + zlib::install_inflate_service(ctx); + } + + void + run() + { + test_error_code(); + #ifdef BOOST_HTTP_HAS_ZLIB + test_deflate(); + test_inflate(); + #endif + } +}; + +TEST_SUITE(zlib_test, "boost.http.zlib"); + +} // namespace http +} // namespace boost diff --git a/test/unit/zlib/compression_level.cpp b/test/unit/zlib/compression_level.cpp new file mode 100644 index 00000000..461f0ab6 --- /dev/null +++ b/test/unit/zlib/compression_level.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/compression_method.cpp b/test/unit/zlib/compression_method.cpp new file mode 100644 index 00000000..a9250889 --- /dev/null +++ b/test/unit/zlib/compression_method.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/compression_strategy.cpp b/test/unit/zlib/compression_strategy.cpp new file mode 100644 index 00000000..1e7daf52 --- /dev/null +++ b/test/unit/zlib/compression_strategy.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/data_type.cpp b/test/unit/zlib/data_type.cpp new file mode 100644 index 00000000..708788a9 --- /dev/null +++ b/test/unit/zlib/data_type.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/deflate.cpp b/test/unit/zlib/deflate.cpp new file mode 100644 index 00000000..87b82f24 --- /dev/null +++ b/test/unit/zlib/deflate.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/flush.cpp b/test/unit/zlib/flush.cpp new file mode 100644 index 00000000..12611fca --- /dev/null +++ b/test/unit/zlib/flush.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/inflate.cpp b/test/unit/zlib/inflate.cpp new file mode 100644 index 00000000..015001d7 --- /dev/null +++ b/test/unit/zlib/inflate.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/stream.cpp b/test/unit/zlib/stream.cpp new file mode 100644 index 00000000..51162c43 --- /dev/null +++ b/test/unit/zlib/stream.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include diff --git a/test/unit/zlib/zlib_error.cpp b/test/unit/zlib/zlib_error.cpp new file mode 100644 index 00000000..ed3f8867 --- /dev/null +++ b/test/unit/zlib/zlib_error.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/http +// + +// Test that header file is self-contained. +#include