diff --git a/include/boost/http_proto.hpp b/include/boost/http_proto.hpp index 42335c74..3cd4677d 100644 --- a/include/boost/http_proto.hpp +++ b/include/boost/http_proto.hpp @@ -54,6 +54,7 @@ #include #include -#include +#include +#include #endif diff --git a/include/boost/http_proto/detail/workspace.hpp b/include/boost/http_proto/detail/workspace.hpp index 96794972..20ed68de 100644 --- a/include/boost/http_proto/detail/workspace.hpp +++ b/include/boost/http_proto/detail/workspace.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2019 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) @@ -128,6 +129,15 @@ class workspace reserve_front( std::size_t n); + /** Convert unused storage to reserved storage. + + @return nullptr if n >= this->size() + */ + BOOST_HTTP_PROTO_DECL + unsigned char* + try_reserve_front( + std::size_t n) noexcept; + template typename std::decay::type& emplace(Args&&... args); diff --git a/include/boost/http_proto/message_view_base.hpp b/include/boost/http_proto/message_view_base.hpp index a148a919..475994ef 100644 --- a/include/boost/http_proto/message_view_base.hpp +++ b/include/boost/http_proto/message_view_base.hpp @@ -1,6 +1,7 @@ // // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) // Copyright (c) 2024 Christian Mazakas +// 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) @@ -13,10 +14,6 @@ #include #include -#include -#include -#include -#include namespace boost { namespace http_proto { diff --git a/include/boost/http_proto/parser.hpp b/include/boost/http_proto/parser.hpp index b77b11da..13be40aa 100644 --- a/include/boost/http_proto/parser.hpp +++ b/include/boost/http_proto/parser.hpp @@ -268,7 +268,7 @@ class parser to @ref parse completes with an error code equal to @ref condition::need_more_input. - @returns A non-empty mutable buffer. + @return A non-empty mutable buffer. @see @ref commit, @@ -535,6 +535,8 @@ class parser private: friend class request_parser; friend class response_parser; + friend class parser_service; + class filter; BOOST_HTTP_PROTO_DECL parser(context&, detail::kind); @@ -602,7 +604,7 @@ class parser buffers::mutable_buffer_pair mbp_; buffers::const_buffer_pair cbp_; - detail::filter* filter_; + filter* filter_; buffers::any_dynamic_buffer* eb_; sink* sink_; diff --git a/include/boost/http_proto/serializer.hpp b/include/boost/http_proto/serializer.hpp index 9b4702f0..7beaed74 100644 --- a/include/boost/http_proto/serializer.hpp +++ b/include/boost/http_proto/serializer.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2019 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) @@ -24,7 +25,6 @@ #include #include -#include #include #include #include @@ -34,9 +34,6 @@ namespace http_proto { #ifndef BOOST_HTTP_PROTO_DOCS class message_view_base; -namespace detail { -class filter; -} // namespace detail #endif /** A serializer for HTTP/1 messages @@ -219,8 +216,8 @@ class serializer consume(std::size_t n); private: + class filter; class const_buf_gen_base; - template class const_buf_gen; @@ -288,7 +285,7 @@ class serializer detail::workspace ws_; const_buf_gen_base* buf_gen_; - detail::filter* filter_; + filter* filter_; source* source_; buffers::circular_buffer cb0_; diff --git a/include/boost/http_proto/service/deflate_service.hpp b/include/boost/http_proto/service/deflate_service.hpp index 6a2f12d5..8742db8c 100644 --- a/include/boost/http_proto/service/deflate_service.hpp +++ b/include/boost/http_proto/service/deflate_service.hpp @@ -21,23 +21,26 @@ namespace zlib { /** Provides the ZLib compression API */ -struct deflate_service + +struct BOOST_SYMBOL_VISIBLE +deflate_service + : public http_proto::service { virtual char const* version() const noexcept = 0; - virtual int init(stream_t& st, int level) const = 0; - virtual int init2(stream_t& st, int level, int method, + virtual int init(stream& st, int level) const = 0; + virtual int init2(stream& st, int level, int method, int windowBits, int memLevel, int strategy) const = 0; - virtual int set_dict(stream_t& st, unsigned char const* dict, unsigned len) const = 0; - virtual int get_dict(stream_t& st, unsigned char* dest, unsigned* len) const = 0; - virtual int dup(stream_t& dest, stream_t& src) const = 0; - virtual int deflate(stream_t& st, int flush) const = 0; - virtual int deflate_end(stream_t& st) const = 0; - virtual int reset(stream_t& st) const = 0; - virtual int params(stream_t& st, int level, int strategy) const = 0; - virtual std::size_t bound(stream_t& st, unsigned long sourceLen) const = 0; - virtual int pending(stream_t& st, unsigned* pending, int* bits) const = 0; - virtual int prime(stream_t& st, int bits, int value) const = 0; - virtual int set_header(stream_t& st, void* header) const = 0; + virtual int set_dict(stream& st, unsigned char const* dict, unsigned len) const = 0; + virtual int get_dict(stream& st, unsigned char* dest, unsigned* len) const = 0; + virtual int dup(stream& dest, stream& src) const = 0; + virtual int deflate(stream& st, int flush) const = 0; + virtual int deflate_end(stream& st) const = 0; + virtual int reset(stream& st) const = 0; + virtual int params(stream& st, int level, int strategy) const = 0; + virtual std::size_t bound(stream& st, unsigned long sourceLen) const = 0; + virtual int pending(stream& st, unsigned* pending, int* bits) const = 0; + virtual int prime(stream& st, int bits, int value) const = 0; + virtual int set_header(stream& st, void* header) const = 0; }; BOOST_HTTP_PROTO_ZLIB_DECL diff --git a/include/boost/http_proto/service/inflate_service.hpp b/include/boost/http_proto/service/inflate_service.hpp index 214e55b6..3219eaed 100644 --- a/include/boost/http_proto/service/inflate_service.hpp +++ b/include/boost/http_proto/service/inflate_service.hpp @@ -1,5 +1,6 @@ // // 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) @@ -19,24 +20,26 @@ namespace zlib { /** Provides the ZLib decompression API */ -struct inflate_service +struct BOOST_SYMBOL_VISIBLE +inflate_service + : public http_proto::service { virtual char const* version() const noexcept = 0; - virtual int init(stream_t& st) const = 0; - virtual int init2(stream_t& st, int windowBits) const = 0; - virtual int inflate(stream_t& st, int flush) const = 0; - virtual int inflate_end(stream_t& st) const = 0; - virtual int set_dict(stream_t& st, unsigned char const* dict, unsigned len) const = 0; - virtual int get_dict(stream_t& st, unsigned char* dest, unsigned* len) const = 0; - virtual int sync(stream_t& st) const = 0; - virtual int dup(stream_t& dest, stream_t& src) const = 0; - virtual int reset(stream_t& st) const = 0; - virtual int reset2(stream_t& st, int windowBits) const = 0; - virtual int prime(stream_t& st, int bits, int value) const = 0; - virtual long mark(stream_t& st) const = 0; - virtual int get_header(stream_t& st, void* header) const = 0; - virtual int back_init(stream_t& st, int windowBits, unsigned char* window) const = 0; - virtual int back_end(stream_t& st) const = 0; + virtual int init(stream& st) const = 0; + virtual int init2(stream& st, int windowBits) const = 0; + virtual int inflate(stream& st, int flush) const = 0; + virtual int inflate_end(stream& st) const = 0; + virtual int set_dict(stream& st, unsigned char const* dict, unsigned len) const = 0; + virtual int get_dict(stream& st, unsigned char* dest, unsigned* len) const = 0; + virtual int sync(stream& st) const = 0; + virtual int dup(stream& dest, stream& src) const = 0; + virtual int reset(stream& st) const = 0; + virtual int reset2(stream& st, int windowBits) const = 0; + virtual int prime(stream& st, int bits, int value) const = 0; + virtual long mark(stream& st) const = 0; + virtual int get_header(stream& st, void* header) const = 0; + virtual int back_init(stream& st, int windowBits, unsigned char* window) const = 0; + virtual int back_end(stream& st) const = 0; virtual unsigned long compile_flags() const = 0; }; diff --git a/include/boost/http_proto/service/zlib_service.hpp b/include/boost/http_proto/service/zlib_service.hpp index 403abf9c..d6e83b46 100644 --- a/include/boost/http_proto/service/zlib_service.hpp +++ b/include/boost/http_proto/service/zlib_service.hpp @@ -21,30 +21,30 @@ namespace boost { namespace http_proto { namespace zlib { -struct stream_t +struct stream { using alloc_func = void*(*)(void*, unsigned int, unsigned int); using free_func = void(*)(void*, void*); - unsigned char* next_in; // next input byte - unsigned int avail_in; // number of bytes available at next_in - unsigned long total_in; // total number of input bytes read so far + unsigned char* next_in; // next input byte + unsigned int avail_in; // number of bytes available at next_in + unsigned long total_in; // total number of input bytes read so far - unsigned char* next_out; // next output byte will go here - unsigned int avail_out; // remaining free space at next_out - unsigned long total_out; // total number of bytes output so far + unsigned char* next_out; // next output byte will go here + unsigned int avail_out; // remaining free space at next_out + unsigned long total_out; // total number of bytes output so far - char const* msg; // last error message, NULL if no error - void* state; // not visible by applications + char* msg; // last error message, NULL if no error + void* state; // not visible by applications - alloc_func zalloc; // used to allocate internal state - free_func zfree; // used to deallocate internal state - void* opaque; // private data object passed to zalloc and zfree + alloc_func zalloc; // used to allocate internal state + free_func zfree; // used to deallocate internal state + void* opaque; // private data object passed to zalloc and zfree - int data_type; // best guess about the data type: binary or text + int data_type; // best guess about the data type: binary or text // for deflate, or the decoding state for inflate - unsigned long adler; // Adler-32 or CRC-32 value of the uncompressed data - unsigned long reserved; // reserved for future use + unsigned long adler; // Adler-32 or CRC-32 value of the uncompressed data + unsigned long reserved; // reserved for future use }; /** Error codes returned from compression/decompression functions. @@ -65,140 +65,51 @@ enum class error version_err = -6 }; -/// Flush methods. -enum class flush +/// Flush methods +enum flush { - none, - partial, - sync, - full, - finish, - block, - trees + no_flush = 0, + partial_flush = 1, + sync_flush = 2, + full_flush = 3, + finish = 4, + block = 5, + trees = 6 }; -/** Input and output buffers. - - The application must update `next_in` and `avail_in` when `avail_in` - has dropped to zero. It must update `next_out` and `avail_out` when - `avail_out` has dropped to zero. -*/ -struct params +/// Compression levels +enum compression_level { - /// Next input byte - void const* next_in; - - /// Number of bytes available at `next_in` - std::size_t avail_in; - - /// Next output byte - void* next_out; - - /// Number of bytes remaining free at `next_out` - std::size_t avail_out; + default_compression = -1, + no_compression = 0, + best_speed = 1, + best_compression = 9 }; -/// Abstract interface for deflator/inflator streams. -struct stream +/// Compression strategy +enum compression_strategy { - /** Call the underling compression/decompression algorithm. - - @param p The input and output buffers. - - @param f The flush method. - - @return The result of operation that contains a value - of @ref error. - */ - virtual system::error_code - write(params& p, flush f) noexcept = 0; + default_strategy = 0, + filtered = 1, + huffman_only = 2, + rle = 3, + fixed = 4 }; -/** Provides in-memory compression and decompression functions - using zlib underneath. -*/ -struct BOOST_SYMBOL_VISIBLE - service - : http_proto::service +/// Possible values of the data_type field for deflate +enum data_type { - /** The memory requirements for deflator. - - @param window_bits The window size. - - @param mem_level The memory level. - - @return The memory requirements in bytes. - */ - virtual - std::size_t - deflator_space_needed( - int window_bits, - int mem_level) const noexcept = 0; - - /** The memory requirements for inflator. - - @param window_bits The window size. - - @return The memory requirements in bytes. - */ - virtual - std::size_t - inflator_space_needed( - int window_bits) const noexcept = 0; - - /** Create a deflator stream by calling zlib `deflateInit2()`. - - @param ws A reference to the workspace used for constructing the - deflator stream object and for storage used by zlib. - - @param level The compression level. - - @param window_bits The window size. - - @param mem_level Specifies how much memory should be allocated - for the internal compression state. - - @return A reference to the created deflator stream. - - @throws std::length_error If there is insufficient free space in - @ref `http_proto::detail::workspace`. - */ - virtual stream& - make_deflator( - http_proto::detail::workspace& ws, - int level, - int window_bits, - int mem_level) const = 0; - - /** Create an inflator stream by calling zlib `inflateInit2()`. - - @param ws A reference to the workspace used for constructing the - inflator stream object and for storage used by zlib. - - @param window_bits The window size. - - @return A reference to the created inflator stream. - - @throws std::length_error If there is insufficient free space in - @ref `http_proto::detail::workspace`. - */ - virtual stream& - make_inflator( - http_proto::detail::workspace& ws, - int window_bits) const = 0; + binary = 0, + text = 1, + ascii = 1, + unknown = 2 }; -/** Installs a zlib service on the provided context. - - @param ctx A reference to the @ref context where the service - will be installed. - - @throw std::invalid_argument If the zlib service already - exist on the context. -*/ -BOOST_HTTP_PROTO_ZLIB_DECL -void -install_service(context& ctx); +/// Compression method +enum compression_method +{ + deflated = 8 +}; } // zlib } // http_proto diff --git a/src/detail/filter.hpp b/src/detail/filter.hpp deleted file mode 100644 index fd06d29e..00000000 --- a/src/detail/filter.hpp +++ /dev/null @@ -1,138 +0,0 @@ -// -// Copyright (c) 2023 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/buffers -// - -#ifndef BOOST_HTTP_PROTO_DETAIL_FILTER_HPP -#define BOOST_HTTP_PROTO_DETAIL_FILTER_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace http_proto { -namespace detail { - -/** A filter for buffers -*/ -class BOOST_HTTP_PROTO_DECL - filter -{ - template< - class T, - std::size_t N> - class unrolled; - -public: - /** The results of processing the filter. - */ - struct results - { - /** The number of bytes produced in the output. - - This may be less than the total number - of bytes available for writing in the - destination buffers. - */ - std::size_t out_bytes = 0; - - /** The number of bytes consumed from the input. - - This may be less than the total number - of bytes available for reading in the - source buffers. - */ - std::size_t in_bytes = 0; - - /** The error, if any occurred. - */ - system::error_code ec; - - /** True if there will be no more output. - */ - bool finished = false; - }; - - /** Called to process the filter. - - @par Preconditions - @ref init was called once before any - calls to `process`. - */ - template< - class MutableBufferSequence, - class ConstBufferSequence> - results - process( - MutableBufferSequence const& out, - ConstBufferSequence const& in, - bool more) - { - static_assert( - buffers::is_mutable_buffer_sequence< - MutableBufferSequence>::value, - "Type requirements not met"); - - static_assert( - buffers::is_const_buffer_sequence< - ConstBufferSequence>::value, - "Type requirements not met"); - - return process_impl(out, in, more); - } - -#ifdef BOOST_BUFFERS_DOCS -protected: -#else -private: -#endif - /** Derived class override. - - @par Preconditions - @ref init was called once before any - calls to `process` - */ - virtual - results - on_process( - buffers::mutable_buffer out, - buffers::const_buffer in, - bool more) = 0; - -private: - results - process_impl( - buffers::mutable_buffer const& out, - buffers::const_buffer const& in, - bool more) - { - return on_process(out, in, more); - } - - template< - class MutableBufferSequence, - class ConstBufferSequence> - results - process_impl( - MutableBufferSequence const& out, - ConstBufferSequence const& in, - bool more); -}; - -} // detail -} // http_proto -} // boost - -#include "src/detail/impl/filter.hpp" - -#endif diff --git a/src/detail/impl/filter.hpp b/src/detail/impl/filter.hpp deleted file mode 100644 index 94d5da71..00000000 --- a/src/detail/impl/filter.hpp +++ /dev/null @@ -1,96 +0,0 @@ -// -// Copyright (c) 2023 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_proto -// - -#ifndef BOOST_HTTP_PROTO_DETAIL_IMPL_FILTER_HPP -#define BOOST_HTTP_PROTO_DETAIL_IMPL_FILTER_HPP - -#include -#include - -namespace boost { -namespace http_proto { -namespace detail { - -template< - class MutableBufferSequence, - class ConstBufferSequence> -auto -filter:: -process_impl( - MutableBufferSequence const& out, - ConstBufferSequence const& in, - bool more) -> - results -{ - results rv; - - auto it_o = buffers::begin(out); - auto it_i = buffers::begin(in); - - auto ob = [&]() -> buffers::mutable_buffer - { - if( it_o != buffers::end(out) ) - return *it_o++; - return {}; - }(); - - buffers::const_buffer ib; - - for(;;) - { - while( ib.size() == 0 ) - { - if( it_i == buffers::end(in) ) - { - if( more ) - return rv; - - // if more == false we return only - // when output buffers are full. - break; - } - else - { - ib = *it_i++; - } - } - - // empty input buffers may be passed, and - // this is intentional and valid. - results rs = process_impl( - ob, - ib, - more || it_i != buffers::end(in)); - - rv.out_bytes += rs.out_bytes; - rv.in_bytes += rs.in_bytes; - rv.ec = rs.ec; - rv.finished = rs.finished; - - if( rv.finished || rv.ec ) - return rv; - - ob = buffers::sans_prefix(ob, rs.out_bytes); - ib = buffers::sans_prefix(ib, rs.in_bytes); - - while( ob.size() == 0 ) - { - if( it_o == buffers::end(out) ) - return rv; - ob = *it_o++; - } - } -} - -} // detail -} // http_proto -} // boost - -#endif diff --git a/src/detail/workspace.cpp b/src/detail/workspace.cpp index f025343d..16077c3e 100644 --- a/src/detail/workspace.cpp +++ b/src/detail/workspace.cpp @@ -1,5 +1,6 @@ // // Copyright (c) 2019 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) @@ -105,7 +106,6 @@ workspace:: reserve_front( std::size_t n) { - // // Requested size exceeds available space. // Note you can never reserve the last byte. if(n >= size()) @@ -116,6 +116,21 @@ reserve_front( return p; } +unsigned char* +workspace:: +try_reserve_front( + std::size_t n) noexcept +{ + // Requested size exceeds available space. + // Note you can never reserve the last byte. + if(n >= size()) + return nullptr; + + auto const p = front_; + front_ += n ; + return p; +} + unsigned char* workspace:: reserve_back( diff --git a/src/detail/zlib_filter.hpp b/src/detail/zlib_filter.hpp new file mode 100644 index 00000000..0dd84bbc --- /dev/null +++ b/src/detail/zlib_filter.hpp @@ -0,0 +1,56 @@ +// +// Copyright (c) 2023 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/buffers +// + +#ifndef BOOST_HTTP_PROTO_DETAIL_ZLIB_FILTER_HPP +#define BOOST_HTTP_PROTO_DETAIL_ZLIB_FILTER_HPP + +#include + +namespace boost { +namespace http_proto { +namespace detail { + +/** utilities for zlib filter +*/ +class zlib_filter +{ +protected: + static + void* zalloc( + void* opaque, + unsigned items, + unsigned size) noexcept + { + return reinterpret_cast< + detail::workspace*>(opaque)->try_reserve_front(items * size); + } + + static + void + zfree(void* /* opaque */, void* /* addr */) noexcept + { + // no-op + } + + static + unsigned int + saturate_cast(std::size_t n) noexcept + { + if(n >= std::numeric_limits::max()) + return std::numeric_limits::max(); + return static_cast(n); + } +}; + +} // detail +} // http_proto +} // boost + +#endif diff --git a/src/parser.cpp b/src/parser.cpp index d5312ce3..a9f51187 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -13,18 +13,19 @@ #include #include #include -#include +#include + +#include "src/detail/zlib_filter.hpp" #include #include +#include #include #include #include #include #include -#include "detail/filter.hpp" - namespace boost { namespace http_proto { @@ -112,62 +113,6 @@ Buffer Usage */ namespace { -class inflator_filter - : public http_proto::detail::filter -{ - zlib::stream& inflator_; - -public: - inflator_filter( - context& ctx, - http_proto::detail::workspace& ws, - bool use_gzip) - : inflator_{ ctx.get_service() - .make_inflator(ws, use_gzip ? 31 : 15) } - { - } - - virtual filter::results - on_process( - buffers::mutable_buffer out, - buffers::const_buffer in, - bool more) override - { - auto flush = - more ? zlib::flush::none : zlib::flush::finish; - filter::results results; - - for(;;) - { - auto params = zlib::params{in.data(), in.size(), - out.data(), out.size() }; - auto ec = inflator_.write(params, flush); - - results.in_bytes += in.size() - params.avail_in; - results.out_bytes += out.size() - params.avail_out; - - if( ec.failed() && - ec != zlib::error::buf_err ) - { - results.ec = ec; - return results; - } - - if( ec == zlib::error::stream_end ) - { - results.finished = true; - return results; - } - - in = buffers::suffix(in, params.avail_in); - out = buffers::suffix(out, params.avail_out); - - if( in.size() == 0 || out.size() == 0 ) - return results; - } - } -}; - class chained_sequence { char const* pos_; @@ -351,6 +296,87 @@ clamp( } } // namespace +class parser::filter + : public detail::zlib_filter +{ + zlib::inflate_service& svc_; + zlib::stream strm_; + +public: + struct results + { + std::size_t out_bytes = 0; + std::size_t in_bytes = 0; + bool finished = false; + system::error_code ec; + }; + + filter( + context& ctx, + http_proto::detail::workspace& ws, + bool gzip) + : svc_(ctx.get_service()) + { + strm_.zalloc = &zalloc; + strm_.zfree = &zfree; + strm_.opaque = &ws; + system::error_code ec = static_cast( + svc_.init2(strm_, gzip ? 31 : 15)); + if(ec != zlib::error::ok) + detail::throw_system_error(ec); + } + + results + process( + buffers::mutable_buffer_subspan out, + buffers::const_buffer_pair in, + bool more) + { + results rv; + auto flush = zlib::no_flush; + for(;;) + { + const auto ob = buffers::front(out); + const auto ib = buffers::front(in); + + if(!more && in[1].size() == 0) + flush = zlib::finish; + + strm_.next_in = static_cast(const_cast(ib.data())); + strm_.avail_in = saturate_cast(ib.size()); + strm_.next_out = static_cast(ob.data()); + strm_.avail_out = saturate_cast(ob.size()); + + auto ec = BOOST_HTTP_PROTO_ERR( + static_cast(svc_.inflate(strm_, flush))); + + const std::size_t in_bytes = saturate_cast(ib.size()) - strm_.avail_in; + const std::size_t out_bytes = saturate_cast(ob.size()) - strm_.avail_out; + + rv.in_bytes += in_bytes; + rv.out_bytes += out_bytes; + + if(ec.failed()) + return rv; + + if(ec == zlib::error::stream_end) + { + rv.finished = true; + return rv; + } + + out = buffers::sans_prefix(out, out_bytes); + in = buffers::sans_prefix(in, in_bytes); + + if(buffers::size(out) == 0) + return rv; + + if(buffers::size(in) == 0 && strm_.avail_out != 0) + return rv; + } + } +}; + class parser_service : public service { @@ -358,7 +384,6 @@ class parser_service parser::config_base cfg; std::size_t space_needed = 0; std::size_t max_codec = 0; - zlib::service const* zlib_svc = nullptr; parser_service( context& ctx, @@ -375,7 +400,7 @@ class parser_service parser_service:: parser_service( - context& ctx, + context& /* ctx */, parser::config_base const& cfg_) : cfg(cfg_) { @@ -422,9 +447,20 @@ parser_service( { if(cfg.apply_deflate_decoder) { - auto const n = ctx.get_service< - zlib::service>().inflator_space_needed(15); - if( max_codec < n) + // TODO: Account for the number of allocations and + // their overhead in the workspace. + + // https://www.zlib.net/zlib_tech.html + std::size_t n = + (1 << 15) + // window_bits + (7 * 1024) + + #ifdef __s390x__ + 5768 + + #endif + detail::workspace:: + space_needed(); + + if(max_codec < n) max_codec = n; } } @@ -1031,12 +1067,12 @@ parse( if(svc_.cfg.apply_deflate_decoder && h_.md.content_encoding.encoding == encoding::deflate) { - filter_ = &ws_.emplace(ctx_, ws_, false); + filter_ = &ws_.emplace(ctx_, ws_, false); } else if(svc_.cfg.apply_gzip_decoder && h_.md.content_encoding.encoding == encoding::gzip) { - filter_ = &ws_.emplace(ctx_, ws_, true); + filter_ = &ws_.emplace(ctx_, ws_, true); } else { @@ -1615,7 +1651,7 @@ apply_filter( n = clamp(n, cb1_.capacity()); return filter_->process( - cb1_.prepare(n), + buffers::mutable_buffer_span{ cb1_.prepare(n) }, buffers::prefix( cb0_.data(), payload_avail), diff --git a/src/serializer.cpp b/src/serializer.cpp index 208ed582..1d54cef3 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -12,15 +12,15 @@ #include #include #include -#include +#include -#include "src/detail/filter.hpp" +#include "src/detail/zlib_filter.hpp" #include +#include #include #include #include -#include #include #include @@ -31,78 +31,6 @@ namespace http_proto { namespace { -class deflator_filter - : public http_proto::detail::filter -{ - zlib::stream& deflator_; - -public: - deflator_filter( - context& ctx, - http_proto::detail::workspace& ws, - bool use_gzip) - : deflator_{ ctx.get_service() - .make_deflator(ws, -1, use_gzip ? 31 : 15, 8) } - { - } - - virtual filter::results - on_process( - buffers::mutable_buffer out, - buffers::const_buffer in, - bool more) override - { - auto flush = - more ? zlib::flush::none : zlib::flush::finish; - filter::results results; - - for(;;) - { - auto params = zlib::params{in.data(), in.size(), - out.data(), out.size() }; - auto ec = deflator_.write(params, flush); - - results.in_bytes += in.size() - params.avail_in; - results.out_bytes += out.size() - params.avail_out; - - if( ec.failed() && - ec != zlib::error::buf_err) - { - results.ec = ec; - return results; - } - - if( ec == zlib::error::stream_end ) - { - results.finished = true; - return results; - } - - in = buffers::suffix(in, params.avail_in); - out = buffers::suffix(out, params.avail_out); - - if( out.size() == 0 ) - return results; - - if( in.size() == 0 ) - { - if( results.out_bytes == 0 && - flush == zlib::flush::none ) - { - // TODO: Is flush::block the right choice? - // We might need a filter::flush() interface - // so that the caller can decide when to flush. - flush = zlib::flush::block; - continue; - } - return results; - } - } - } -}; - -//------------------------------------------------ - constexpr std::size_t crlf_len = 2; @@ -257,6 +185,110 @@ class appender //------------------------------------------------ +class serializer::filter + : public detail::zlib_filter +{ + zlib::deflate_service& svc_; + zlib::stream strm_; + +public: + struct results + { + std::size_t out_bytes = 0; + std::size_t in_bytes = 0; + bool out_short = false; + bool finished = false; + system::error_code ec; + }; + + filter( + context& ctx, + http_proto::detail::workspace& ws, + bool gzip) + : svc_(ctx.get_service()) + { + strm_.zalloc = &zalloc; + strm_.zfree = &zfree; + strm_.opaque = &ws; + system::error_code ec = static_cast(svc_.init2( + strm_, + zlib::default_compression, + zlib::deflated, + gzip ? 31 : 15, + 8, + zlib::default_strategy)); + if(ec != zlib::error::ok) + detail::throw_system_error(ec); + } + + results + process( + buffers::mutable_buffer_pair out, + buffers::const_buffer_pair in, + bool more, + bool force_flush = false) + { + results rv; + auto flush = zlib::no_flush; + for(;;) + { + auto ob = buffers::front(out); + auto ib = buffers::front(in); + + if(!more && flush != zlib::finish && in[1].size() == 0) + { + // Prevent deflate from producing + // zero output due to small buffer + if(buffers::size(out) < 8) + { + rv.out_short = true; + return rv; + } + flush = zlib::finish; + } + + strm_.next_in = static_cast(const_cast(ib.data())); + strm_.avail_in = saturate_cast(ib.size()); + strm_.next_out = static_cast(ob.data()); + strm_.avail_out = saturate_cast(ob.size()); + + auto ec = BOOST_HTTP_PROTO_ERR( + static_cast(svc_.deflate(strm_, flush))); + + const std::size_t in_bytes = saturate_cast(ib.size()) - strm_.avail_in; + const std::size_t out_bytes = saturate_cast(ob.size()) - strm_.avail_out; + + rv.in_bytes += in_bytes; + rv.out_bytes += out_bytes; + + if(ec.failed()) + return rv; + + if(ec == zlib::error::stream_end) + { + rv.finished = true; + return rv; + } + + out = buffers::sans_prefix(out, out_bytes); + in = buffers::sans_prefix(in, in_bytes); + + if(buffers::size(out) == 0) + return rv; + + if(buffers::size(in) == 0 && strm_.avail_out != 0) + { + if(force_flush && rv.out_bytes == 0) + { + flush = zlib::block; + continue; + } + return rv; + } + } + } +}; + serializer:: ~serializer() { @@ -415,7 +447,7 @@ prepare() -> const auto rs = filter_->process( apndr.prepare(), - tmp_, + { tmp_, {} }, more_input_); if(rs.ec.failed()) @@ -427,6 +459,9 @@ prepare() -> tmp_ = buffers::sans_prefix(tmp_, rs.in_bytes); apndr.commit(rs.out_bytes, !rs.finished); + if(rs.out_short) + break; + if(rs.finished) filter_done_ = true; } @@ -466,6 +501,9 @@ prepare() -> cb1_.consume(rs.in_bytes); apndr.commit(rs.out_bytes, !rs.finished); + if(rs.out_short) + break; + if(rs.finished) filter_done_ = true; } @@ -493,7 +531,8 @@ prepare() -> const auto rs = filter_->process( apndr.prepare(), cb1_.data(), - more_input_); + more_input_, + prepped_.empty()); // force_flush if(rs.ec.failed()) { @@ -607,12 +646,12 @@ start_init( if(ce.encoding == encoding::deflate) { filter_ = &ws_.emplace< - deflator_filter>(ctx_, ws_, false); + filter>(ctx_, ws_, false); } else if(ce.encoding == encoding::gzip) { filter_ = &ws_.emplace< - deflator_filter>(ctx_, ws_, true); + filter>(ctx_, ws_, true); } } diff --git a/src_zlib/service/deflate_service.cpp b/src_zlib/service/deflate_service.cpp index d9be437a..37aa6670 100644 --- a/src_zlib/service/deflate_service.cpp +++ b/src_zlib/service/deflate_service.cpp @@ -1,5 +1,6 @@ // // 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) @@ -17,14 +18,13 @@ namespace boost { namespace http_proto { namespace zlib { -BOOST_STATIC_ASSERT(sizeof(stream_t) == sizeof(z_stream_s)); -BOOST_STATIC_ASSERT(is_layout_identical()); +BOOST_STATIC_ASSERT(sizeof(stream) == sizeof(z_stream_s)); +BOOST_STATIC_ASSERT(is_layout_identical()); //------------------------------------------------ class deflate_service_impl : public deflate_service - , public http_proto::service { public: using key_type = deflate_service; @@ -47,7 +47,7 @@ class deflate_service_impl int init( - stream_t& st, + stream& st, int level) const override { stream_cast sc(st); @@ -56,7 +56,7 @@ class deflate_service_impl int init2( - stream_t& st, + stream& st, int level, int method, int windowBits, @@ -71,7 +71,7 @@ class deflate_service_impl int set_dict( - stream_t& st, + stream& st, unsigned char const* dict, unsigned len) const override { @@ -81,7 +81,7 @@ class deflate_service_impl int get_dict( - stream_t& st, + stream& st, unsigned char* dest, unsigned* len) const override { @@ -91,8 +91,8 @@ class deflate_service_impl int dup( - stream_t& dest, - stream_t& src) const override + stream& dest, + stream& src) const override { stream_cast sc0(dest); stream_cast sc1(src); @@ -101,7 +101,7 @@ class deflate_service_impl int deflate( - stream_t& st, + stream& st, int flush) const override { stream_cast sc(st); @@ -110,7 +110,7 @@ class deflate_service_impl int deflate_end( - stream_t& st) const override + stream& st) const override { stream_cast sc(st); return deflateEnd(sc.get()); @@ -118,7 +118,7 @@ class deflate_service_impl int reset( - stream_t& st) const override + stream& st) const override { stream_cast sc(st); return deflateReset(sc.get()); @@ -126,7 +126,7 @@ class deflate_service_impl int params( - stream_t& st, + stream& st, int level, int strategy) const override { @@ -136,7 +136,7 @@ class deflate_service_impl std::size_t bound( - stream_t& st, + stream& st, unsigned long sourceLen) const override { stream_cast sc(st); @@ -145,7 +145,7 @@ class deflate_service_impl int pending( - stream_t& st, + stream& st, unsigned* pending, int* bits) const override { @@ -155,7 +155,7 @@ class deflate_service_impl int prime( - stream_t& st, + stream& st, int bits, int value) const override { @@ -165,7 +165,7 @@ class deflate_service_impl int set_header( - stream_t& st, + stream& st, void* header) const override { stream_cast sc(st); diff --git a/src_zlib/service/inflate_service.cpp b/src_zlib/service/inflate_service.cpp index 05645ffb..6651122c 100644 --- a/src_zlib/service/inflate_service.cpp +++ b/src_zlib/service/inflate_service.cpp @@ -1,5 +1,6 @@ // // 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) @@ -19,7 +20,6 @@ namespace zlib { class inflate_service_impl : public inflate_service - , public http_proto::service { public: using key_type = inflate_service; @@ -42,7 +42,7 @@ class inflate_service_impl int init( - stream_t& st) const override + stream& st) const override { stream_cast sc(st); return inflateInit(sc.get()); @@ -50,7 +50,7 @@ class inflate_service_impl int init2( - stream_t& st, + stream& st, int windowBits) const override { stream_cast sc(st); @@ -59,7 +59,7 @@ class inflate_service_impl int inflate( - stream_t& st, + stream& st, int flush) const override { stream_cast sc(st); @@ -68,7 +68,7 @@ class inflate_service_impl int inflate_end( - stream_t& st) const override + stream& st) const override { stream_cast sc(st); return inflateEnd(sc.get()); @@ -76,7 +76,7 @@ class inflate_service_impl int set_dict( - stream_t& st, + stream& st, unsigned char const* dict, unsigned len) const override { @@ -86,7 +86,7 @@ class inflate_service_impl int get_dict( - stream_t& st, + stream& st, unsigned char* dest, unsigned* len) const override { @@ -96,7 +96,7 @@ class inflate_service_impl int sync( - stream_t& st) const override + stream& st) const override { stream_cast sc(st); return inflateSync(sc.get()); @@ -104,8 +104,8 @@ class inflate_service_impl int dup( - stream_t& dest, - stream_t& src) const override + stream& dest, + stream& src) const override { stream_cast sc0(dest); stream_cast sc1(src); @@ -114,7 +114,7 @@ class inflate_service_impl int reset( - stream_t& st) const override + stream& st) const override { stream_cast sc(st); return inflateReset(sc.get()); @@ -122,7 +122,7 @@ class inflate_service_impl int reset2( - stream_t& st, + stream& st, int windowBits) const override { stream_cast sc(st); @@ -131,7 +131,7 @@ class inflate_service_impl int prime( - stream_t& st, + stream& st, int bits, int value) const override { @@ -141,7 +141,7 @@ class inflate_service_impl long mark( - stream_t& st) const override + stream& st) const override { stream_cast sc(st); return inflateMark(sc.get()); @@ -149,7 +149,7 @@ class inflate_service_impl int get_header( - stream_t& st, + stream& st, void* header) const override { stream_cast sc(st); @@ -159,7 +159,7 @@ class inflate_service_impl int back_init( - stream_t& st, + stream& st, int windowBits, unsigned char* window) const override { @@ -169,7 +169,7 @@ class inflate_service_impl int back_end( - stream_t& st) const override + stream& st) const override { stream_cast sc(st); return inflateBackEnd(sc.get()); diff --git a/src_zlib/service/stream_cast.hpp b/src_zlib/service/stream_cast.hpp index cea6e763..487af002 100644 --- a/src_zlib/service/stream_cast.hpp +++ b/src_zlib/service/stream_cast.hpp @@ -1,5 +1,6 @@ // // 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) @@ -52,12 +53,12 @@ is_layout_identical() //------------------------------------------------ template()> + is_layout_identical()> struct stream_cast_impl { explicit stream_cast_impl( - stream_t& st) + stream& st) : pzs_(reinterpret_cast(&st)) , st_(st) { @@ -104,7 +105,7 @@ struct stream_cast_impl private: z_stream_s* pzs_ = nullptr; - stream_t& st_; + stream& st_; z_stream_s zs_; }; @@ -115,7 +116,7 @@ struct stream_cast_impl { explicit stream_cast_impl( - stream_t& st) + stream& st) // VFALCO A pinch of undefined behavior here : pzs_(reinterpret_cast(&st)) { diff --git a/src_zlib/service/zlib_service.cpp b/src_zlib/service/zlib_service.cpp deleted file mode 100644 index 7ffe8ee7..00000000 --- a/src_zlib/service/zlib_service.cpp +++ /dev/null @@ -1,330 +0,0 @@ -// -// 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_proto -// - -#include - -#include -#include -#include -#include -#include -#include - -namespace boost { -namespace http_proto { -namespace zlib { - -//------------------------------------------------ - -namespace { - -BOOST_NOINLINE BOOST_NORETURN -void -throw_zlib_error( - int e, - source_location const& loc = BOOST_CURRENT_LOCATION) -{ - throw_exception( - system::system_error(static_cast(e)), loc); -} - -// probes memory usage for a config -class probe -{ -public: - explicit - probe() noexcept - { - zs_.zalloc = &zalloc; - zs_.zfree = &zfree; - zs_.opaque = this; - } - - system::result - deflate_init( - int level) - { - n_ = 0; - system::error_code ec; - ec = static_cast( - deflateInit(&zs_, level)); - if(ec.failed()) - return ec; - Bytef tmp[24]{}; - zs_.next_in = &tmp[0]; - zs_.avail_in = 1; - zs_.next_out = &tmp[1]; - zs_.avail_out = 23; - ec = static_cast( - deflate(&zs_, - Z_FINISH)); - if( ec.failed() && - ec != error::stream_end) - return ec; - ec = static_cast( - deflateEnd(&zs_)); - if(ec.failed()) - return ec; - return n_; - } - - system::result - deflate_init2( - int level, - int method, - int windowBits, - int memLevel, - int strategy) - { - n_ = 0; - system::error_code ec; - ec = static_cast( - deflateInit2(&zs_, - level, - method, - windowBits, - memLevel, - strategy)); - if(ec.failed()) - return ec; - Bytef tmp[2]; - zs_.next_in = &tmp[0]; - zs_.avail_in = 0; - zs_.next_out = &tmp[1]; - zs_.avail_out = 0; - ec = static_cast( - deflate(&zs_, - Z_FULL_FLUSH)); - if(ec.failed()) - return ec; - ec = static_cast( - deflateEnd(&zs_)); - if(ec.failed()) - return ec; - return n_; - } - -private: - static void* zalloc(void* opaque, - uInt num, uInt size) - { - auto& self = - *reinterpret_cast< - probe*>(opaque); - self.n_ += num * size; - return new char[num * size]; - } - - static void zfree( - void*, void* address) - { - delete[] reinterpret_cast< - char*>(address); - } - - z_stream_s zs_{}; - std::size_t n_ = 0; -}; - -void* zalloc( - void* opaque, - unsigned items, - unsigned size) -{ - try - { - auto n = items * size; - auto* ws = - reinterpret_cast< - http_proto::detail::workspace*>(opaque); - - return ws->reserve_front(n); - } - catch(std::length_error const&) // represents OOM - { - return Z_NULL; - } -} - -void zfree(void* /* opaque */, void* /* addr */) -{ - // we call ws_.clear() before the serializer is reused - // so all the allocations are passively freed -} - -::uInt -clamp(std::size_t x) noexcept -{ - if(x >= (std::numeric_limits<::uInt>::max)()) - return (std::numeric_limits<::uInt>::max)(); - return static_cast<::uInt>(x); -} - -void -sync(z_stream* zs, params const& p) noexcept -{ - zs->next_in = reinterpret_cast<::Bytef*>( - const_cast(p.next_in)); - zs->avail_in = clamp(p.avail_in); - zs->next_out = reinterpret_cast<::Bytef*>(p.next_out); - zs->avail_out = clamp(p.avail_out); -} - -void -sync(z_stream const& zs, params* p) noexcept -{ - p->next_in = zs.next_in; - p->avail_in -= clamp(p->avail_in) - zs.avail_in; - p->next_out = zs.next_out; - p->avail_out -= clamp(p->avail_out) - zs.avail_out; -} - -class deflator - : public stream -{ - z_stream zs_; - -public: - deflator( - http_proto::detail::workspace& ws, - int level, - int window_bits, - int mem_level) - { - zs_.zalloc = &zalloc; - zs_.zfree = &zfree; - zs_.opaque = &ws; - - auto ret = deflateInit2(&zs_, level, Z_DEFLATED, - window_bits, mem_level, Z_DEFAULT_STRATEGY); - if(ret != Z_OK) - throw_zlib_error(ret); - } - - system::error_code - write(params& p, flush f) noexcept override - { - sync(&zs_, p); - auto ret = deflate(&zs_, static_cast(f)); - sync(zs_, &p); - return static_cast(ret); - } -}; - -class inflator - : public stream -{ - z_stream zs_; - -public: - inflator( - http_proto::detail::workspace& ws, - int window_bits) - { - zs_.zalloc = &zalloc; - zs_.zfree = &zfree; - zs_.opaque = &ws; - - auto ret = inflateInit2(&zs_, window_bits); - if(ret != Z_OK) - throw_zlib_error(ret); - } - - system::error_code - write(params& p, flush f) noexcept override - { - sync(&zs_, p); - auto ret = inflate(&zs_, static_cast(f)); - sync(zs_, &p); - return static_cast(ret); - } -}; - -struct service_impl - : public service -{ - using key_type = service; - - explicit - service_impl(context&) noexcept - { - } - - std::size_t - deflator_space_needed( - int window_bits, - int mem_level) const noexcept override - { - // TODO: Account for the number of allocations and - // their overhead in the workspace. - - // https://www.zlib.net/zlib_tech.html - return - (1 << (window_bits + 2)) + - (1 << (mem_level + 9)) + - (6 * 1024) + - #ifdef __s390x__ - 5768 + - #endif - http_proto::detail:: - workspace::space_needed(); - } - - std::size_t - inflator_space_needed( - int window_bits) const noexcept override - { - // TODO: Account for the number of allocations and - // their overhead in the workspace. - - // https://www.zlib.net/zlib_tech.html - return - (1 << window_bits) + - (7 * 1024) + - #ifdef __s390x__ - 5768 + - #endif - http_proto::detail:: - workspace::space_needed(); - } - - stream& - make_deflator( - http_proto::detail::workspace& ws, - int level, - int window_bits, - int mem_level) const override - { - return ws.emplace( - ws, level, window_bits, mem_level); - } - - stream& - make_inflator( - http_proto::detail::workspace& ws, - int window_bits) const override - { - return ws.emplace(ws, window_bits); - } -}; - -} // namespace - -void -install_service(context& ctx) -{ - ctx.make_service(); -} - -//------------------------------------------------ - -} // zlib -} // http_proto -} // boost diff --git a/test/limits/CMakeLists.txt b/test/limits/CMakeLists.txt index 34aa9e42..f1cd903f 100644 --- a/test/limits/CMakeLists.txt +++ b/test/limits/CMakeLists.txt @@ -7,15 +7,15 @@ # Official repository: https://github.com/cppalliance/http_proto # -set(TEST_MAIN ../../../url/extra/test_main.cpp) +set(TEST_MAIN ../../../url/extra/test_suite/test_main.cpp ../../../url/extra/test_suite/test_suite.cpp) file(GLOB_RECURSE LIMITS_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../../src/*.cpp) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES limits.cpp Jamfile) -source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../../url/extra PREFIX "_extra" FILES ${TEST_MAIN}) +source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../../url/extra/test_suite PREFIX "_extra" FILES ${TEST_MAIN}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/../../src PREFIX "_extra" FILES ${LIMITS_SOURCES}) add_executable(boost_http_proto_limits limits.cpp Jamfile ${TEST_MAIN} ${LIMITS_SOURCES}) target_include_directories(boost_http_proto_limits PRIVATE ../../) -target_include_directories(boost_http_proto_limits PRIVATE ../../include ../../../url/extra ../../..) +target_include_directories(boost_http_proto_limits PRIVATE ../../include ../../../url/extra/test_suite ../../..) target_compile_definitions(boost_http_proto_limits PRIVATE BOOST_HTTP_PROTO_MAX_HEADER=20 BOOST_HTTP_PROTO_NO_LIB=1 diff --git a/test/limits/Jamfile b/test/limits/Jamfile index 9091dcb7..0e646535 100644 --- a/test/limits/Jamfile +++ b/test/limits/Jamfile @@ -12,15 +12,16 @@ import testing ; project : requirements $(c11-requires) - ../../../url/extra/test_main.cpp + ../../../url/extra/test_suite/test_main.cpp + ../../../url/extra/test_suite/test_suite.cpp . ../.. - ../../../url/extra + ../../../url/extra/test_suite /boost//buffers /boost//url ; -run limits.cpp ../../../url/extra/test_main.cpp /boost/http_proto//http_proto_sources +run limits.cpp /boost/http_proto//http_proto_sources : requirements BOOST_HTTP_PROTO_MAX_HEADER=20 BOOST_HTTP_PROTO_NO_LIB diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 494dabbf..b648f99e 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -8,8 +8,9 @@ # set(EXTRAFILES - ../../../url/extra/test_main.cpp - ../../../url/extra/test_suite.hpp + ../../../url/extra/test_suite/test_main.cpp + ../../../url/extra/test_suite/test_suite.cpp + ../../../url/extra/test_suite/test_suite.hpp ./test_helpers.hpp ./test_helpers.cpp ) @@ -23,7 +24,7 @@ list(APPEND PFILES source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES ${PFILES}) source_group("_extra" FILES ${EXTRAFILES}) add_executable(boost_http_proto_tests ${PFILES} ${EXTRAFILES}) -target_include_directories(boost_http_proto_tests PRIVATE . ../../../url/extra) +target_include_directories(boost_http_proto_tests PRIVATE . ../../../url/extra/test_suite) target_link_libraries(boost_http_proto_tests PRIVATE Boost::filesystem) if (ZLIB_FOUND) diff --git a/test/unit/Jamfile b/test/unit/Jamfile index df9f87c9..696896e8 100644 --- a/test/unit/Jamfile +++ b/test/unit/Jamfile @@ -17,10 +17,11 @@ project $(c11-requires) /boost/http_proto//boost_http_proto [ ac.check-library /boost/http_proto//boost_http_proto_zlib : /boost/http_proto//boost_http_proto_zlib : ] - ../../../url/extra/test_main.cpp + ../../../url/extra/test_suite/test_main.cpp + ../../../url/extra/test_suite/test_suite.cpp ./test_helpers.cpp . - ../../../url/extra + ../../../url/extra/test_suite extra on darwin,norecover:static diff --git a/test/unit/parser.cpp b/test/unit/parser.cpp index d9d5e8f6..a5e71ac5 100644 --- a/test/unit/parser.cpp +++ b/test/unit/parser.cpp @@ -1,5 +1,6 @@ // // Copyright (c) 2019 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) @@ -13,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -354,7 +355,7 @@ struct parser_test { #ifdef BOOST_HTTP_PROTO_HAS_ZLIB context ctx; - zlib::install_service(ctx); + zlib::install_deflate_service(ctx); request_parser::config_base cfg1; cfg1.apply_deflate_decoder = true; diff --git a/test/unit/service/zlib_service.cpp b/test/unit/service/zlib_service.cpp index fa12b819..286a7956 100644 --- a/test/unit/service/zlib_service.cpp +++ b/test/unit/service/zlib_service.cpp @@ -1,5 +1,6 @@ // // Copyright (c) 2021 Vinnie Falco (vinnie dot falco at gmail dot 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) @@ -8,7 +9,8 @@ // // Test that header file is self-contained. -#include +#include +#include #ifdef BOOST_HTTP_PROTO_HAS_ZLIB @@ -25,7 +27,8 @@ struct zlib_service_test run() { context ctx; - zlib::install_service(ctx); + zlib::install_deflate_service(ctx); + zlib::install_inflate_service(ctx); } }; diff --git a/test/unit/zlib.cpp b/test/unit/zlib.cpp index 89f5ffef..9ea6cd5a 100644 --- a/test/unit/zlib.cpp +++ b/test/unit/zlib.cpp @@ -9,9 +9,7 @@ // #include -#include -#include "boost/buffers/prefix.hpp" #include "test_suite.hpp" #ifndef BOOST_HTTP_PROTO_HAS_ZLIB @@ -35,10 +33,11 @@ TEST_SUITE( #include #include #include -#include #include +#include #include -#include +#include +#include #include #include @@ -78,59 +77,6 @@ namespace http_proto { struct zlib_test { - struct faulty_zlib_service - : public zlib::service - { - using key_type = service; - - struct faulty_stream : zlib::stream - { - system::error_code - write(zlib::params&, zlib::flush) noexcept override - { - return zlib::error::version_err; - } - }; - - explicit - faulty_zlib_service(context&) noexcept - { - } - - std::size_t - deflator_space_needed( - int, - int) const noexcept override - { - return 1; - } - - std::size_t - inflator_space_needed( - int) const noexcept override - { - return 1; - } - - zlib::stream& - make_deflator( - http_proto::detail::workspace& ws, - int, - int, - int) const override - { - return ws.emplace(); - } - - zlib::stream& - make_inflator( - http_proto::detail::workspace& ws, - int) const override - { - return ws.emplace(); - } - }; - std::string deflate( int window_bits, @@ -388,11 +334,10 @@ struct zlib_test bool chunked_encoding) { context ctx; - zlib::install_service(ctx); + zlib::install_deflate_service(ctx); serializer sr( ctx, - ctx.get_service< - zlib::service>().deflator_space_needed(15, 8) + (2 * 1024)); + 512 * 1024); // prove we can reuse the serializer successfully for( int i = 0; i < 2; ++i ) @@ -666,7 +611,7 @@ struct zlib_test test_parser() { context ctx; - zlib::install_service(ctx); + zlib::install_inflate_service(ctx); auto append_chunked = []( std::string& msg, @@ -745,66 +690,10 @@ struct zlib_test pr.reset(); } } - - void - test_serializer_reports_zlib_errors() - { - context ctx; - ctx.make_service(); - serializer sr{ ctx }; - - response res{ - "HTTP/1.1 200 OK\r\n" - "Content-Encoding: gzip\r\n" - "\r\n" }; - std::string buf(1024, '*'); - sr.start(res, buffers::const_buffer{ buf.data(), buf.size() }); - - auto rs = sr.prepare(); - BOOST_TEST(rs.has_error()); - BOOST_TEST_EQ(rs.error(), zlib::error::version_err); - } - - void - test_parser_reports_zlib_errors() - { - context ctx; - ctx.make_service(); - - response_parser::config cfg; - cfg.apply_deflate_decoder = true; - install_parser_service(ctx, cfg); - - response_parser pr{ ctx }; - pr.reset(); - pr.start(); - - std::string msg{ - "HTTP/1.1 200 OK\r\n" - "Content-Encoding: deflate\r\n" - "\r\n" }; - auto n = buffers::copy( - pr.prepare(), - buffers::const_buffer{ msg.data(), msg.size() }); - - BOOST_TEST_EQ(n, msg.size()); - pr.commit(n); - pr.commit_eof(); - - boost::system::error_code ec; - pr.parse(ec); - BOOST_TEST(! ec.failed()); - BOOST_TEST(pr.got_header()); - pr.parse(ec); - BOOST_TEST_EQ(ec, zlib::error::version_err); - } - void run() { test_serializer(); - test_serializer_reports_zlib_errors(); test_parser(); - test_parser_reports_zlib_errors(); } };