From 85568f9de0f2b734302706b5e4e6390823b8b13d Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Thu, 20 Nov 2025 15:44:48 -0800 Subject: [PATCH] chore: refactor parser and serializer config --- include/boost/http_proto/parser.hpp | 130 +-------- include/boost/http_proto/parser_config.hpp | 209 +++++++++++++++ include/boost/http_proto/request_parser.hpp | 25 +- include/boost/http_proto/response_parser.hpp | 94 +++---- src/parser.cpp | 136 ++++------ src/parser_config.cpp | 117 +++++++++ src/request_parser.cpp | 6 +- src/response_parser.cpp | 6 +- src/zlib_filter.hpp | 68 +++++ test/unit/compression.cpp | 5 +- test/unit/parser.cpp | 262 +++++++------------ test/unit/parser_config.cpp | 31 +++ test/unit/request_parser.cpp | 142 +++++----- test/unit/response_parser.cpp | 40 ++- 14 files changed, 708 insertions(+), 563 deletions(-) create mode 100644 include/boost/http_proto/parser_config.hpp create mode 100644 src/parser_config.cpp create mode 100644 src/zlib_filter.hpp create mode 100644 test/unit/parser_config.cpp diff --git a/include/boost/http_proto/parser.hpp b/include/boost/http_proto/parser.hpp index 6af8c8dd..e883c27f 100644 --- a/include/boost/http_proto/parser.hpp +++ b/include/boost/http_proto/parser.hpp @@ -12,10 +12,10 @@ #define BOOST_HTTP_PROTO_PARSER_HPP #include +#include #include #include #include -#include #include #include @@ -35,6 +35,8 @@ class response_parser; class static_request; class static_response; +//----------------------------------------------- + /** A parser for HTTP/1 messages. This parser uses a single block of memory allocated @@ -73,8 +75,6 @@ class static_response; class parser { public: - struct config_base; - /** The type of buffer returned from @ref prepare. */ using mutable_buffers_type = @@ -617,34 +617,16 @@ class parser BOOST_HTTP_PROTO_DECL ~parser(); BOOST_HTTP_PROTO_DECL parser() noexcept; BOOST_HTTP_PROTO_DECL parser(parser&& other) noexcept; - BOOST_HTTP_PROTO_DECL parser(capy::polystore&, detail::kind); + BOOST_HTTP_PROTO_DECL parser(detail::kind, prepared_parser_config); BOOST_HTTP_PROTO_DECL void assign(parser&& other) noexcept; - - BOOST_HTTP_PROTO_DECL - void - start_impl(bool); - - static_request const& - safe_get_request() const; - - static_response const& - safe_get_response() const; - - BOOST_HTTP_PROTO_DECL - detail::workspace& - ws() noexcept; - - BOOST_HTTP_PROTO_DECL - bool - is_body_set() const noexcept; - - BOOST_HTTP_PROTO_DECL - void - set_body_impl(buffers::any_dynamic_buffer&) noexcept; - - BOOST_HTTP_PROTO_DECL - void - set_body_impl(sink&) noexcept; + BOOST_HTTP_PROTO_DECL void start_impl(bool); + static_request const& safe_get_request() const; + static_response const& safe_get_response() const; + BOOST_HTTP_PROTO_DECL detail::workspace& ws() noexcept; + BOOST_HTTP_PROTO_DECL bool is_body_set() const noexcept; + BOOST_HTTP_PROTO_DECL void set_body_impl( + buffers::any_dynamic_buffer&) noexcept; + BOOST_HTTP_PROTO_DECL void set_body_impl(sink&) noexcept; static constexpr unsigned buffers_N = 8; impl* impl_; @@ -652,92 +634,6 @@ class parser //------------------------------------------------ -/** Parser configuration settings. -*/ -struct parser::config_base -{ - /** Configurable limits for HTTP headers. - */ - header_limits headers; - - /** Maximum allowed size of the content body. - - Measured after decoding. - */ - std::uint64_t body_limit = 64 * 1024; - - /** Enable Brotli Content-Encoding decoding. - - Requires `boost::capy::brotli::decode_service` to be - installed, otherwise an exception is thrown. - */ - bool apply_brotli_decoder = false; - - /** Enable Deflate Content-Encoding decoding. - - Requires `boost::zlib::inflate_service` to be - installed, otherwise an exception is thrown. - */ - bool apply_deflate_decoder = false; - - /** Enable Gzip Content-Encoding decoding. - - Requires `boost::zlib::inflate_service` to be - installed, otherwise an exception is thrown. - */ - bool apply_gzip_decoder = false; - - /** Zlib window bits (9–15). - - Must be >= the value used during compression. - 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`. - */ - int zlib_window_bits = 15; - - /** Minimum space for payload buffering. - - This value controls the following - settings: - - @li The smallest allocated size of - the buffers used for reading - and decoding the payload. - - @li The lowest guaranteed size of - an in-place body. - - @li The largest size used to reserve - space in dynamic buffer bodies - when the payload size is not - known ahead of time. - - This cannot be zero. - */ - std::size_t min_buffer = 4096; - - /** Largest permissible output size in prepare. - - This cannot be zero. - */ - std::size_t max_prepare = std::size_t(-1); - - /** Space to reserve for type-erasure. - - This space is used for the following - purposes: - - @li Storing an instance of the user-provided - @ref sink objects. - - @li Storing an instance of the user-provided - ElasticBuffer. - */ - std::size_t max_type_erase = 1024; -}; - /** Install the parser service. @par Example @@ -770,7 +666,7 @@ BOOST_HTTP_PROTO_DECL void install_parser_service( capy::polystore& ctx, - parser::config_base const& cfg); + parser_config const& cfg); } // http_proto } // boost diff --git a/include/boost/http_proto/parser_config.hpp b/include/boost/http_proto/parser_config.hpp new file mode 100644 index 00000000..4bc619d3 --- /dev/null +++ b/include/boost/http_proto/parser_config.hpp @@ -0,0 +1,209 @@ +// +// Copyright (c) 2019 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_PARSERCONFIG_HPP +#define BOOST_HTTP_PROTO_PARSERCONFIG_HPP + +#include +#include +#include +#include +#include + +namespace boost { + +// VFALCO these should go in capy/zlib_fwd and capy/brotli_fwd +namespace capy { +namespace brotli { +struct decode_service; +struct encode_service; +} // brotli +namespace zlib { +struct deflate_service; +struct inflate_service; +} // zlib +} // capy + +namespace http_proto { + +class parser; + +enum class role +{ + client, + server +}; + +struct prepared_parser_config; + +//----------------------------------------------- + +/** Parser configuration settings. +*/ +struct parser_config +{ + /** Configurable limits for HTTP headers. + */ + header_limits headers; + + /** Maximum allowed size of the content body. + + Measured after decoding. + */ + std::uint64_t body_limit = 64 * 1024; + + /** Enable Brotli Content-Encoding decoding. + + Requires `boost::capy::brotli::decode_service` to be + installed, otherwise an exception is thrown. + */ + bool apply_brotli_decoder = false; + + /** Enable Deflate Content-Encoding decoding. + + Requires `boost::zlib::inflate_service` to be + installed, otherwise an exception is thrown. + */ + bool apply_deflate_decoder = false; + + /** Enable Gzip Content-Encoding decoding. + + Requires `boost::zlib::inflate_service` to be + installed, otherwise an exception is thrown. + */ + bool apply_gzip_decoder = false; + + /** Zlib window bits (9–15). + + Must be >= the value used during compression. + 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`. + */ + int zlib_window_bits = 15; + + /** Minimum space for payload buffering. + + This value controls the following + settings: + + @li The smallest allocated size of + the buffers used for reading + and decoding the payload. + + @li The lowest guaranteed size of + an in-place body. + + @li The largest size used to reserve + space in dynamic buffer bodies + when the payload size is not + known ahead of time. + + This cannot be zero. + */ + std::size_t min_buffer = 4096; + + /** Largest permissible output size in prepare. + + This cannot be zero. + */ + std::size_t max_prepare = std::size_t(-1); + + /** Space to reserve for type-erasure. + + This space is used for the following + purposes: + + @li Storing an instance of the user-provided + @ref sink objects. + + @li Storing an instance of the user-provided + ElasticBuffer. + */ + std::size_t max_type_erase = 1024; + + /** Constructor. + @param role Indicates whether the parser + will be used for client or server messages. + @param ps The polystore to obtain services from. + */ + BOOST_HTTP_PROTO_DECL + parser_config(role role, capy::polystore& ps); + + auto prepare() const -> + prepared_parser_config; + +private: + friend class parser; + BOOST_HTTP_PROTO_DECL static void prepare( + std::shared_ptr sp); + + // VFALCO remove this, its ugly here + std::size_t max_overread() const noexcept + { + return + headers.max_size + + min_buffer; + } + + capy::polystore& ps_; + capy::brotli::decode_service* brotli_decode = nullptr; + capy::brotli::encode_service* brotli_encode = nullptr; + capy::zlib::inflate_service* zlib_deflate = nullptr; + capy::zlib::inflate_service* zlib_inflate = nullptr; + std::size_t space_needed = 0; + std::size_t max_codec = 0; +}; + +//----------------------------------------------- + +struct prepared_parser_config +{ + parser_config const* + operator->() const noexcept + { + return sp_.get(); + } + +private: + friend class parser; + friend struct parser_config; + +// VFALCO REMOVE THIS +prepared_parser_config() = default; + + prepared_parser_config( + std::shared_ptr sp) + : sp_(std::move(sp)) + { + } + + std::shared_ptr sp_; +}; + +//----------------------------------------------- + +inline +auto +parser_config:: +prepare() const -> + prepared_parser_config +{ + auto sp = std::make_shared< + parser_config>(std::move(*this)); + prepare(sp); + return prepared_parser_config(std::move(sp)); +} + +} // http_proto +} // boost + +#endif diff --git a/include/boost/http_proto/request_parser.hpp b/include/boost/http_proto/request_parser.hpp index 9c4ef457..83a807b5 100644 --- a/include/boost/http_proto/request_parser.hpp +++ b/include/boost/http_proto/request_parser.hpp @@ -27,22 +27,6 @@ class request_parser : public parser { public: - /** Configuration settings for request_parser. - - @see - @ref install_parser_service, - @ref request_parser. - */ - struct config : config_base - { - /** Constructor - */ - config() noexcept - { - body_limit = 64 * 1024; - } - }; - /** Destructor. Any views or buffers obtained from this @@ -128,11 +112,7 @@ class request_parser @par Exception Safety Calls to allocate may throw. - @param ctx Context from which the - request_parser will access registered - services. The caller is responsible for - ensuring that the provided ctx remains - valid for the lifetime of the request_parser. + @param cfg Configuration settings. @see @ref install_parser_service, @@ -140,7 +120,8 @@ class request_parser */ BOOST_HTTP_PROTO_DECL explicit - request_parser(capy::polystore& ctx); + request_parser( + prepared_parser_config cfg); /** Return a reference to the parsed request headers. diff --git a/include/boost/http_proto/response_parser.hpp b/include/boost/http_proto/response_parser.hpp index 97f935c5..e4b5a18b 100644 --- a/include/boost/http_proto/response_parser.hpp +++ b/include/boost/http_proto/response_parser.hpp @@ -27,22 +27,6 @@ class response_parser : public parser { public: - /** Configuration settings for response_parser. - - @see - @ref install_parser_service, - @ref response_parser. - */ - struct config : config_base - { - /** Constructor. - */ - config() noexcept - { - body_limit = 1024 * 1024; - } - }; - /** Destructor. Any views or buffers obtained from this @@ -57,6 +41,41 @@ class response_parser */ response_parser() = default; + /** Constructor. + + Constructs a parser that uses the @ref + config parameters installed on the + provided `ctx`. + + The parser will attempt to allocate + the required space on startup, with the + amount depending on the @ref config + parameters, and will not perform any + further allocations, except for Brotli + decoder instances, if enabled. + + Depending on which compression algorithms + are enabled in the @ref config, the parser + will attempt to access the corresponding + decoder services on the same `ctx`. + + @par Example + @code + response_parser sr(ctx); + @endcode + + @par Complexity + Constant. + + @par Exception Safety + Calls to allocate may throw. + + @param cfg Configuration settings. + */ + BOOST_HTTP_PROTO_DECL + explicit + response_parser(prepared_parser_config cfg); + /** Constructor. The states of `other` are transferred @@ -99,48 +118,7 @@ class response_parser return *this; } - /** Constructor. - - Constructs a parser that uses the @ref - config parameters installed on the - provided `ctx`. - - The parser will attempt to allocate - the required space on startup, with the - amount depending on the @ref config - parameters, and will not perform any - further allocations, except for Brotli - decoder instances, if enabled. - - Depending on which compression algorithms - are enabled in the @ref config, the parser - will attempt to access the corresponding - decoder services on the same `ctx`. - - @par Example - @code - response_parser sr(ctx); - @endcode - - @par Complexity - Constant. - - @par Exception Safety - Calls to allocate may throw. - - @param ctx Context from which the - response_parser will access registered - services. The caller is responsible for - ensuring that the provided ctx remains - valid for the lifetime of the response_parser. - - @see - @ref install_parser_service, - @ref config. - */ - BOOST_HTTP_PROTO_DECL - explicit - response_parser(capy::polystore& ctx); + //------------------------------------------- /** Prepare for the next message on the stream. diff --git a/src/parser.cpp b/src/parser.cpp index 1e98c8c2..3c8b0493 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -28,6 +28,7 @@ #include #include +#include "src/zlib_filter.hpp" #include "src/detail/brotli_filter_base.hpp" #include "src/detail/buffer_utils.hpp" #include "src/detail/zlib_filter_base.hpp" @@ -302,55 +303,6 @@ clamp( return static_cast(x); } -class zlib_filter - : public detail::zlib_filter_base -{ - capy::zlib::inflate_service& svc_; - -public: - zlib_filter( - const capy::polystore& ctx, - http_proto::detail::workspace& ws, - int window_bits) - : zlib_filter_base(ws) - , svc_(ctx.get()) - { - system::error_code ec = static_cast( - svc_.init2(strm_, window_bits)); - if(ec != capy::zlib::error::ok) - detail::throw_system_error(ec); - } - -private: - virtual - results - do_process( - buffers::mutable_buffer out, - buffers::const_buffer in, - bool more) noexcept override - { - strm_.next_out = static_cast(out.data()); - strm_.avail_out = saturate_cast(out.size()); - strm_.next_in = static_cast(const_cast(in.data())); - strm_.avail_in = saturate_cast(in.size()); - - auto rs = static_cast( - svc_.inflate( - strm_, - more ? capy::zlib::no_flush : capy::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); - - if(rs < capy::zlib::error::ok && rs != capy::zlib::error::buf_err) - rv.ec = rs; - - return rv; - } -}; - class brotli_filter : public detail::brotli_filter_base { @@ -415,12 +367,12 @@ class brotli_filter class parser_service { public: - parser::config_base cfg; + parser_config cfg; std::size_t space_needed = 0; - std::size_t max_codec = 0; + std::size_t max_codec = 0; parser_service( - parser::config_base const& cfg_) + parser_config const& cfg_) : cfg(cfg_) { /* @@ -444,9 +396,9 @@ class parser_service // VFALCO TODO OVERFLOW CHECING { //fb_.size() - h_.size + - //svc_.cfg.min_buffer + - //svc_.cfg.min_buffer + - //svc_.max_codec; + //cfg_->min_buffer + + //cfg_->min_buffer + + //cfg_->max_codec; } // VFALCO OVERFLOW CHECKING ON THIS @@ -506,7 +458,7 @@ class parser_service void install_parser_service( capy::polystore& ctx, - parser::config_base const& cfg) + parser_config const& cfg) { ctx.emplace(cfg); } @@ -534,8 +486,7 @@ class parser::impl elastic, }; - const capy::polystore& ctx_; - parser_service& svc_; + prepared_parser_config cfg_; detail::workspace ws_; static_request m_; @@ -567,10 +518,11 @@ class parser::impl bool chunked_body_ended; public: - impl(const capy::polystore& ctx, detail::kind k) - : ctx_(ctx) - , svc_(ctx.get()) - , ws_(svc_.space_needed) + impl( + detail::kind k, + prepared_parser_config cfg) + : cfg_(std::move(cfg)) + , ws_(cfg_->space_needed) , m_(ws_.data(), ws_.size()) , state_(state::reset) , got_header_(false) @@ -714,11 +666,11 @@ class parser::impl fb_ = { ws_.data(), - svc_.cfg.headers.max_size + svc_.cfg.min_buffer, + cfg_->headers.max_size + cfg_->min_buffer, leftover }; BOOST_ASSERT( - fb_.capacity() == svc_.max_overread() - leftover); + fb_.capacity() == cfg_->max_overread() - leftover); BOOST_ASSERT( head_response == false || @@ -733,7 +685,7 @@ class parser::impl style_ = style::in_place; // reset to the configured default - body_limit_ = svc_.cfg.body_limit; + body_limit_ = cfg_->body_limit; body_total_ = 0; payload_remain_ = 0; @@ -772,10 +724,10 @@ class parser::impl case state::header: { BOOST_ASSERT( - m_.h_.size < svc_.cfg.headers.max_size); + m_.h_.size < cfg_->headers.max_size); std::size_t n = fb_.capacity() - fb_.size(); - BOOST_ASSERT(n <= svc_.max_overread()); - n = clamp(n, svc_.cfg.max_prepare); + BOOST_ASSERT(n <= cfg_->max_overread()); + n = clamp(n, cfg_->max_prepare); mbp_[0] = fb_.prepare(n); nprepare_ = n; return mutable_buffers_type(&mbp_[0], 1); @@ -797,7 +749,7 @@ class parser::impl { // buffered payload std::size_t n = cb0_.capacity(); - n = clamp(n, svc_.cfg.max_prepare); + n = clamp(n, cfg_->max_prepare); nprepare_ = n; mbp_ = cb0_.prepare(n); return detail::make_span(mbp_); @@ -811,7 +763,7 @@ class parser::impl case style::sink: { std::size_t n = cb0_.capacity(); - n = clamp(n, svc_.cfg.max_prepare); + n = clamp(n, cfg_->max_prepare); if(m_.payload() == payload::size) { @@ -819,9 +771,9 @@ class parser::impl { std::size_t overread = n - static_cast(payload_remain_); - if(overread > svc_.max_overread()) + if(overread > cfg_->max_overread()) n = static_cast(payload_remain_) + - svc_.max_overread(); + cfg_->max_overread(); } } else @@ -847,7 +799,7 @@ class parser::impl BOOST_ASSERT(cb0_.size() == 0); BOOST_ASSERT(body_avail_ == 0); - std::size_t n = svc_.cfg.min_buffer; + std::size_t n = cfg_->min_buffer; if(m_.payload() == payload::size) { @@ -886,7 +838,7 @@ class parser::impl } } - n = clamp(n, svc_.cfg.max_prepare); + n = clamp(n, cfg_->max_prepare); BOOST_ASSERT(n != 0); nprepare_ = n; return eb_->prepare(n); @@ -1067,7 +1019,7 @@ class parser::impl BOOST_ASSERT(m_.h_.cbuf == static_cast< void const*>(ws_.data())); - m_.h_.parse(fb_.size(), svc_.cfg.headers, ec); + m_.h_.parse(fb_.size(), cfg_->headers, ec); if(ec == condition::need_more_input) { @@ -1144,10 +1096,10 @@ class parser::impl // current message or octets of the next message in the // stream, e.g. pipelining is being used auto const overread = fb_.size() - m_.h_.size; - BOOST_ASSERT(overread <= svc_.max_overread()); + BOOST_ASSERT(overread <= cfg_->max_overread()); auto cap = fb_.capacity() + overread + - svc_.cfg.min_buffer; + cfg_->min_buffer; // reserve body buffers first, as the decoder // must be installed after them. @@ -1156,30 +1108,30 @@ class parser::impl switch(m_.metadata().content_encoding.coding) { case content_coding::deflate: - if(!svc_.cfg.apply_deflate_decoder) + if(!cfg_->apply_deflate_decoder) goto no_filter; filter_ = &ws_.emplace( - ctx_, ws_, svc_.cfg.zlib_window_bits); + cfg_->ps_, ws_, cfg_->zlib_window_bits); break; case content_coding::gzip: - if(!svc_.cfg.apply_gzip_decoder) + if(!cfg_->apply_gzip_decoder) goto no_filter; filter_ = &ws_.emplace( - ctx_, ws_, svc_.cfg.zlib_window_bits + 16); + cfg_->ps_, ws_, cfg_->zlib_window_bits + 16); break; case content_coding::br: - if(!svc_.cfg.apply_brotli_decoder) + if(!cfg_->apply_brotli_decoder) goto no_filter; filter_ = &ws_.emplace( - ctx_, ws_); + cfg_->ps_, ws_); break; no_filter: default: - cap += svc_.max_codec; - ws_.reserve_front(svc_.max_codec); + cap += cfg_->max_codec; + ws_.reserve_front(cfg_->max_codec); break; } @@ -1191,10 +1143,10 @@ class parser::impl else { // buffered payload - std::size_t n0 = (overread > svc_.cfg.min_buffer) + std::size_t n0 = (overread > cfg_->min_buffer) ? overread - : svc_.cfg.min_buffer; - std::size_t n1 = svc_.cfg.min_buffer; + : cfg_->min_buffer; + std::size_t n1 = cfg_->min_buffer; cb0_ = { p , n0, overread }; cb1_ = { p + n0 , n1 }; @@ -1731,7 +1683,7 @@ class parser::impl if(style_ == style::elastic) { std::size_t n = clamp(body_limit_remain()); - n = clamp(n, svc_.cfg.min_buffer); + n = clamp(n, cfg_->min_buffer); n = clamp(n, eb_->max_size() - eb_->size()); // fill capacity first to avoid @@ -1866,9 +1818,9 @@ parser(parser&& other) noexcept parser:: parser( - capy::polystore& ctx, - detail::kind k) - : impl_(new impl(ctx, k)) + detail::kind k, + prepared_parser_config cfg) + : impl_(new impl(k, std::move(cfg))) { // TODO: use a single allocation for // impl and workspace buffer. diff --git a/src/parser_config.cpp b/src/parser_config.cpp new file mode 100644 index 00000000..2e7eb626 --- /dev/null +++ b/src/parser_config.cpp @@ -0,0 +1,117 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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_proto +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace http_proto { + +parser_config:: +parser_config( + role role, + capy::polystore& ps) + : ps_(ps) +{ + if(role == http_proto::role::client) + { + body_limit = 64 * 1024; + } + else + { + body_limit = 64 * 1024; + } +} + +void +parser_config:: +prepare( + std::shared_ptr sp) +{ + sp->ps_.find(sp->brotli_decode); + sp->ps_.find(sp->brotli_encode); + sp->ps_.find(sp->zlib_deflate); + sp->ps_.find(sp->zlib_inflate); + + /* + | fb | cb0 | cb1 | C | T | f | + + fb flat_buffer headers.max_size + cb0 circular_buffer min_buffer + cb1 circular_buffer min_buffer + C codec max_codec + T body max_type_erase + f table max_table_space + + */ + // validate + //if(sp->min_prepare > sp->max_prepare) + //detail::throw_invalid_argument(); + + if(sp->max_prepare < 1) + detail::throw_invalid_argument(); + + // VFALCO TODO OVERFLOW CHECING + { + //fb_.size() - h_.size + + //svc_.sp->min_buffer + + //svc_.sp->min_buffer + + //svc_.max_codec; + } + + // VFALCO OVERFLOW CHECKING ON THIS + sp->space_needed += + sp->headers.valid_space_needed(); + + // cb0_, cb1_ + // VFALCO OVERFLOW CHECKING ON THIS + sp->space_needed += + sp->min_buffer + + sp->min_buffer; + + // T + sp->space_needed += sp->max_type_erase; + + // max_codec + if(sp->apply_deflate_decoder || sp->apply_gzip_decoder) + { + // 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 << sp->zlib_window_bits) + + (7 * 1024) + + #ifdef __s390x__ + 5768 + + #endif + detail::workspace::space_needed< + zlib_filter>(); + + if( sp->max_codec < n) + sp->max_codec = n; + } + sp->space_needed += sp->max_codec; + + // round up to alignof(detail::header::entry) + auto const al = alignof( + detail::header::entry); + sp->space_needed = al * (( + sp->space_needed + al - 1) / al); +} + +} // http_proto +} // boost diff --git a/src/request_parser.cpp b/src/request_parser.cpp index 64c65edb..e46e6135 100644 --- a/src/request_parser.cpp +++ b/src/request_parser.cpp @@ -14,10 +14,10 @@ namespace http_proto { request_parser:: request_parser( - capy::polystore& ctx) + prepared_parser_config cfg) : parser( - ctx, - detail::kind::request) + detail::kind::request, + std::move(cfg)) { } diff --git a/src/response_parser.cpp b/src/response_parser.cpp index 277ba43e..c6341a43 100644 --- a/src/response_parser.cpp +++ b/src/response_parser.cpp @@ -14,10 +14,10 @@ namespace http_proto { response_parser:: response_parser( - capy::polystore& ctx) + prepared_parser_config cfg) : parser( - ctx, - detail::kind::response) + detail::kind::response, + std::move(cfg)) { } diff --git a/src/zlib_filter.hpp b/src/zlib_filter.hpp new file mode 100644 index 00000000..baf35811 --- /dev/null +++ b/src/zlib_filter.hpp @@ -0,0 +1,68 @@ +// +// Copyright (c) 2019 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 "src/detail/zlib_filter_base.hpp" + +namespace boost { +namespace http_proto { + +class zlib_filter + : public detail::zlib_filter_base +{ + capy::zlib::inflate_service& svc_; + +public: + zlib_filter( + capy::polystore& ctx, + http_proto::detail::workspace& ws, + int window_bits) + : zlib_filter_base(ws) + , svc_(ctx.get()) + { + system::error_code ec = static_cast( + svc_.init2(strm_, window_bits)); + if(ec != capy::zlib::error::ok) + detail::throw_system_error(ec); + } + +private: + virtual + results + do_process( + buffers::mutable_buffer out, + buffers::const_buffer in, + bool more) noexcept override + { + strm_.next_out = static_cast(out.data()); + strm_.avail_out = saturate_cast(out.size()); + strm_.next_in = static_cast(const_cast(in.data())); + strm_.avail_in = saturate_cast(in.size()); + + auto rs = static_cast( + svc_.inflate( + strm_, + more ? capy::zlib::no_flush : capy::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); + + if(rs < capy::zlib::error::ok && rs != capy::zlib::error::buf_err) + rv.ec = rs; + + return rv; + } +}; + +} // http_proto +} // boost diff --git a/test/unit/compression.cpp b/test/unit/compression.cpp index da37690c..7f5eda30 100644 --- a/test/unit/compression.cpp +++ b/test/unit/compression.cpp @@ -614,7 +614,7 @@ struct zlib_test { capy::polystore ctx; std::vector encodings; - response_parser::config cfg; + parser_config cfg(role::server, ctx); #ifdef BOOST_CAPY_HAS_ZLIB cfg.apply_deflate_decoder = true; @@ -632,8 +632,7 @@ struct zlib_test #endif cfg.body_limit = 1024 * 1024; - install_parser_service(ctx, cfg); - response_parser pr(ctx); + response_parser pr(cfg.prepare()); pr.reset(); auto append_chunked = []( diff --git a/test/unit/parser.cpp b/test/unit/parser.cpp index 340a52bf..74a299aa 100644 --- a/test/unit/parser.cpp +++ b/test/unit/parser.cpp @@ -96,6 +96,11 @@ namespace http_proto { struct parser_test { + // default objects for ease of use + capy::polystore ctx_; + parser_config const cfg_; + prepared_parser_config pcfg_; + template class opt { @@ -194,7 +199,6 @@ struct parser_test using pieces = std::vector< core::string_view>; - capy::polystore ctx_; core::string_view sh_; core::string_view sb_; request_parser req_pr_; @@ -206,17 +210,17 @@ struct parser_test pieces in_; parser_test() - : ctx_() + : cfg_(role::client, ctx_) + , pcfg_(cfg_.prepare()) , req_pr_( - [&]() -> capy::polystore& + [&]() { - request_parser::config cfg; + parser_config cfg(cfg_); cfg.body_limit = 5; cfg.min_buffer = 3; - install_parser_service(ctx_, cfg); - return ctx_; + return cfg.prepare(); }()) - , res_pr_(ctx_) + , res_pr_(pcfg_) { in_.reserve(5); req_pr_.reset(); @@ -323,10 +327,7 @@ struct parser_test in_ = init; BOOST_ASSERT(init.size() > 0); BOOST_ASSERT(! init.begin()->empty()); - request_parser::config cfg; - ctx_opt_.emplace(); - install_parser_service(*ctx_opt_, cfg); - req_pr_opt_.emplace(*ctx_opt_); + req_pr_opt_.emplace(pcfg_); return *req_pr_opt_; } @@ -337,9 +338,6 @@ struct parser_test { // parser(parser&&) { - capy::polystore ctx; - install_parser_service(ctx, {}); - core::string_view header = "POST / HTTP/1.1\r\n" "Content-Length: 3\r\n" @@ -347,7 +345,7 @@ struct parser_test core::string_view body = "123"; pieces in = { header, body }; - request_parser pr1(ctx); + request_parser pr1(pcfg_); pr1.reset(); pr1.start(); system::error_code ec; @@ -364,10 +362,10 @@ struct parser_test // ~parser { - request_parser pr(ctx_); + request_parser pr(pcfg_); } { - response_parser pr(ctx_); + response_parser pr(pcfg_); } } @@ -379,13 +377,9 @@ struct parser_test void testStart() { - capy::polystore ctx; - request_parser::config_base cfg; - install_parser_service(ctx, cfg); - { // missing reset - request_parser pr(ctx); + request_parser pr(pcfg_); BOOST_TEST_THROWS( pr.start(), std::logic_error); @@ -393,7 +387,7 @@ struct parser_test { // missing reset - request_parser pr(ctx); + request_parser pr(pcfg_); pr.reset(); pr.start(); pr.commit_eof(); @@ -404,7 +398,7 @@ struct parser_test { // start called twice - request_parser pr(ctx); + request_parser pr(pcfg_); pr.reset(); pr.start(); BOOST_TEST_THROWS( @@ -414,7 +408,7 @@ struct parser_test { // incomplete message - request_parser pr(ctx); + request_parser pr(pcfg_); prep(pr, "GET /"); BOOST_TEST_THROWS( pr.start(), @@ -423,7 +417,7 @@ struct parser_test { // incomplete message - request_parser pr(ctx); + request_parser pr(pcfg_); prep(pr, "POST / HTTP/1.1\r\n" "Content-Length: 5\r\n" @@ -441,7 +435,7 @@ struct parser_test { { // missing reset - request_parser pr(ctx_); + request_parser pr(pcfg_); BOOST_TEST_THROWS( pr.prepare(), std::logic_error); @@ -449,7 +443,7 @@ struct parser_test { // missing start - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); BOOST_TEST_THROWS( pr.prepare(), @@ -457,13 +451,10 @@ struct parser_test } auto const check_header = []( - request_parser::config const& cfg, + parser_config const& cfg, std::size_t n) { - capy::polystore ctx; - install_parser_service(ctx, cfg); - - request_parser pr(ctx); + request_parser pr(cfg.prepare()); pr.reset(); pr.start(); parser::mutable_buffers_type dest; @@ -479,7 +470,7 @@ struct parser_test { // normal - request_parser::config cfg; + parser_config cfg(cfg_); cfg.headers.max_size = 40; cfg.min_buffer = 3; check_header(cfg, @@ -489,7 +480,7 @@ struct parser_test { // max_prepare - request_parser::config cfg; + parser_config cfg(cfg_); cfg.max_prepare = 2; check_header(cfg, cfg.max_prepare); } @@ -498,18 +489,16 @@ struct parser_test // in_place body // - auto const check_in_place = []( - request_parser::config const& cfg, + auto const check_in_place = [&]( + parser_config const& cfg, std::size_t n, core::string_view s) { - capy::polystore ctx; - install_parser_service(ctx, cfg); - system::error_code ec; - request_parser pr(ctx); + request_parser pr(cfg.prepare()); pr.reset(); pr.start(); pieces in({ s }); + system::error_code ec; read_header(pr, in, ec); if( ! BOOST_TEST(! ec.failed()) || ! BOOST_TEST(pr.got_header()) || @@ -526,8 +515,7 @@ struct parser_test { // normal - request_parser::config cfg; - check_in_place(cfg, 12291, + check_in_place(cfg_, 12291, "POST / HTTP/1.1\r\n" "Content-Length: 3\r\n" "\r\n"); @@ -535,7 +523,7 @@ struct parser_test { // max_prepare - request_parser::config cfg; + parser_config cfg(cfg_); cfg.max_prepare = 10; check_in_place(cfg, 10, "POST / HTTP/1.1\r\n" @@ -551,17 +539,15 @@ struct parser_test dynamic_max_size = 7; auto const check_dynamic = [&]( - request_parser::config const& cfg, + parser_config const& cfg, std::size_t n, core::string_view s, system::error_code ex = error::need_data) { - capy::polystore ctx; - install_parser_service(ctx, cfg); - system::error_code ec; - request_parser req_pr(ctx); - response_parser res_pr(ctx); + request_parser req_pr(cfg.prepare()); + response_parser res_pr(cfg.prepare()); BOOST_ASSERT(! s.empty()); + system::error_code ec; parser* pr; if(s.front() != 'H') { @@ -611,8 +597,7 @@ struct parser_test { // Content-Length - request_parser::config cfg; - check_dynamic(cfg, 3, + check_dynamic(cfg_, 3, "POST / HTTP/1.1\r\n" "Content-Length: 3\r\n" "\r\n"); @@ -620,7 +605,7 @@ struct parser_test { // Content-Length, no overread - request_parser::config cfg; + parser_config cfg(cfg_); cfg.max_prepare = 10; check_dynamic(cfg, 5, "POST / HTTP/1.1\r\n" @@ -630,7 +615,7 @@ struct parser_test { // Content-Length, max_prepare - request_parser::config cfg; + parser_config cfg(cfg_); cfg.max_prepare = 3; check_dynamic(cfg, 3, "POST / HTTP/1.1\r\n" @@ -640,8 +625,7 @@ struct parser_test { // to_eof - request_parser::config cfg; - check_dynamic(cfg, + check_dynamic(cfg_, dynamic_max_size, "HTTP/1.1 200 OK\r\n" "\r\n"); @@ -649,7 +633,7 @@ struct parser_test { // to_eof, max_prepare - request_parser::config cfg; + parser_config cfg(cfg_); cfg.max_prepare = 3; BOOST_TEST(cfg.max_prepare < dynamic_max_size); @@ -661,7 +645,7 @@ struct parser_test { // to_eof, max_prepare - request_parser::config cfg; + parser_config cfg(cfg_); BOOST_TEST( dynamic_max_size == 7); check_dynamic(cfg, @@ -673,16 +657,13 @@ struct parser_test { // fill capacity first - capy::polystore ctx; - request_parser::config cfg; - install_parser_service(ctx, cfg); - system::error_code ec; - response_parser pr(ctx); + response_parser pr(pcfg_); pr.reset(); pr.start(); pieces in({ "HTTP/1.1 200 OK\r\n" "\r\n" }); + system::error_code ec; read_header(pr, in, ec); std::unique_ptr ps(new std::string()); auto &s = *ps; @@ -701,10 +682,9 @@ struct parser_test { // set_body, no room - request_parser::config cfg; - BOOST_TEST( - dynamic_max_size == 7); - check_dynamic(cfg, + // VFALCO dynamic_max_size is a constant? + BOOST_TEST(dynamic_max_size == 7); + check_dynamic(cfg_, 0, "HTTP/1.1 200 OK\r\n" "Content-Length: 10\r\n" @@ -715,15 +695,12 @@ struct parser_test // prepare when complete { - request_parser::config cfg; pieces in({ "GET / HTTP/1.1\r\n\r\n"}); - capy::polystore ctx; - install_parser_service(ctx, cfg); - system::error_code ec; - request_parser pr(ctx); + request_parser pr(pcfg_); pr.reset(); pr.start(); + system::error_code ec; read_header(pr, in, ec); BOOST_TEST(! ec.failed()); BOOST_TEST(pr.is_complete()); @@ -739,7 +716,7 @@ struct parser_test { { // missing reset - request_parser pr(ctx_); + request_parser pr(pcfg_); BOOST_TEST_THROWS( pr.commit(0), std::logic_error); @@ -747,7 +724,7 @@ struct parser_test { // missing start - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); BOOST_TEST_THROWS( pr.commit(0), @@ -760,7 +737,7 @@ struct parser_test { // commit too large - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); pr.start(); auto dest = pr.prepare(); @@ -772,7 +749,7 @@ struct parser_test { // commit after EOF - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); pr.start(); pr.commit_eof(); @@ -783,7 +760,7 @@ struct parser_test { // 0-byte commit - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); pr.start(); BOOST_TEST_NO_THROW( @@ -792,7 +769,7 @@ struct parser_test { // 1-byte commit - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); pr.start(); auto dest = pr.prepare(); @@ -808,7 +785,7 @@ struct parser_test { // commit too large - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); pr.start(); system::error_code ec; @@ -826,38 +803,20 @@ struct parser_test std::invalid_argument); } -#if 0 - // VFALCO missing chunked implementation - { - // buffered payload - capy::polystore ctx; - request_parser::config cfg; - install_parser_service(ctx, cfg); - request_parser pr(ctx_); - - check_body(pr, - "POST / HTTP/1.1\r\n" - "Transfer-Encoding: chunked\r\n" - "\r\n"); - } -#endif - // // in-place // auto const check_in_place = []( - request_parser::config const& cfg, + parser_config const& cfg, system::error_code ex, bool is_complete, pieces&& in) { - capy::polystore ctx; - install_parser_service(ctx, cfg); - system::error_code ec; - response_parser pr(ctx); + response_parser pr(cfg.prepare()); pr.reset(); pr.start(); + system::error_code ec; read_header(pr, in, ec); if(ex.failed() && ec == ex) { @@ -872,8 +831,7 @@ struct parser_test // Content-Length, incomplete { - request_parser::config cfg; - check_in_place(cfg, + check_in_place(cfg_, error::need_data, false, { "HTTP/1.1 200 OK\r\n" "Content-Length: 3\r\n" @@ -883,8 +841,7 @@ struct parser_test // Content-Length, complete { - request_parser::config cfg; - check_in_place(cfg, + check_in_place(cfg_, {}, true, { "HTTP/1.1 200 OK\r\n" "Content-Length: 3\r\n" @@ -894,7 +851,7 @@ struct parser_test // to_eof { - request_parser::config cfg; + parser_config cfg(cfg_); cfg.min_buffer = 3; check_in_place(cfg, error::need_data, false, { @@ -917,13 +874,10 @@ struct parser_test { // no-op - capy::polystore ctx; - response_parser::config cfg; - install_parser_service(ctx, cfg); - system::error_code ec; - response_parser pr(ctx); + response_parser pr(pcfg_); pr.reset(); pr.start(); + system::error_code ec; pieces in = { "HTTP/1.1 200 OK\r\n" "Content-Length: 3\r\n" @@ -943,11 +897,7 @@ struct parser_test { // commit too large - capy::polystore ctx; - response_parser::config cfg; - install_parser_service(ctx, cfg); - system::error_code ec; - response_parser pr(ctx); + response_parser pr(pcfg_); pr.reset(); pr.start(); pieces in = { @@ -955,6 +905,7 @@ struct parser_test "Content-Length: 3\r\n" "\r\n" "123" }; + system::error_code ec; read(pr, in, ec); BOOST_TEST(! ec.failed()); BOOST_TEST(pr.is_complete()); @@ -975,7 +926,7 @@ struct parser_test { // 0-byte commit - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); pr.start(); system::error_code ec; @@ -996,7 +947,7 @@ struct parser_test { // commit too large - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); pr.start(); system::error_code ec; @@ -1024,7 +975,7 @@ struct parser_test { { // missing reset - request_parser pr(ctx_); + request_parser pr(pcfg_); BOOST_TEST_THROWS( pr.commit_eof(), std::logic_error); @@ -1032,7 +983,7 @@ struct parser_test { // missing start - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); BOOST_TEST_THROWS( pr.commit_eof(), @@ -1041,7 +992,7 @@ struct parser_test { // empty stream - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); pr.start(); BOOST_TEST_NO_THROW( @@ -1051,7 +1002,7 @@ struct parser_test { // body - response_parser pr(ctx_); + response_parser pr(pcfg_); pr.reset(); pr.start(); system::error_code ec; @@ -1070,7 +1021,7 @@ struct parser_test { // set_body - response_parser pr(ctx_); + response_parser pr(pcfg_); pr.reset(); pr.start(); system::error_code ec; @@ -1093,7 +1044,7 @@ struct parser_test { // complete - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); pr.start(); system::error_code ec; @@ -1114,7 +1065,7 @@ struct parser_test { { // missing reset - request_parser pr(ctx_); + request_parser pr(pcfg_); system::error_code ec; BOOST_TEST_THROWS( pr.parse(ec), @@ -1123,7 +1074,7 @@ struct parser_test { // missing start - request_parser pr(ctx_); + request_parser pr(pcfg_); pr.reset(); system::error_code ec; BOOST_TEST_THROWS( @@ -1136,18 +1087,16 @@ struct parser_test // auto const check_in_place = []( - response_parser::config const& cfg, + parser_config const& cfg, bool some, system::error_code ex, bool is_complete, pieces&& in) { - capy::polystore ctx; - install_parser_service(ctx, cfg); - system::error_code ec; - response_parser pr(ctx); + response_parser pr(cfg.prepare()); pr.reset(); pr.start(); + system::error_code ec; read_header(pr, in, ec); if(ex.failed() && ec == ex) { @@ -1165,7 +1114,7 @@ struct parser_test { // Content-Length, incomplete - response_parser::config cfg; + parser_config cfg(cfg_); core::string_view const h = "HTTP/1.1 200 OK\r\n" "Content-Length: 5\r\n" @@ -1179,7 +1128,7 @@ struct parser_test { // Content-Length, in_place limit - response_parser::config cfg; + parser_config cfg(cfg_); core::string_view const h = "HTTP/1.1 200 OK\r\n" "Content-Length: 10\r\n" @@ -1193,8 +1142,7 @@ struct parser_test { // Content-Length, need data - response_parser::config cfg; - check_in_place(cfg, true, + check_in_place(cfg_, true, error::need_data, false, { "HTTP/1.1 200 OK\r\n" "Content-Length: 10\r\n" @@ -1204,8 +1152,7 @@ struct parser_test { // Content-Length, complete - response_parser::config cfg; - check_in_place(cfg, false, + check_in_place(cfg_, false, {}, true, { "HTTP/1.1 200 OK\r\n" "Content-Length: 3\r\n" @@ -1215,7 +1162,7 @@ struct parser_test { // to_eof, body too large - response_parser::config cfg; + parser_config cfg(cfg_); cfg.body_limit = 3; cfg.min_buffer = 3; check_in_place(cfg, true, @@ -1227,8 +1174,7 @@ struct parser_test { // chunked, need data - response_parser::config cfg; - check_in_place(cfg, true, + check_in_place(cfg_, true, error::need_data, false, { "HTTP/1.1 200 OK\r\n" "Transfer-Encoding: chunked\r\n" @@ -1238,8 +1184,7 @@ struct parser_test { // to_eof, complete - response_parser::config cfg; - check_in_place(cfg, false, + check_in_place(cfg_, false, {}, true, { "HTTP/1.1 200 OK\r\n" "\r\n", @@ -1625,11 +1570,7 @@ struct parser_test void testChunkedInPlace() { - capy::polystore ctx; - response_parser::config cfg; - install_parser_service(ctx, cfg); - - response_parser pr(ctx); + response_parser pr(pcfg_); { // initial hello world parse for chunked @@ -1860,17 +1801,12 @@ struct parser_test void testMultipleMessageInPlace() { - request_parser::config cfg; - capy::polystore ctx; - + parser_config cfg(cfg_); cfg.headers.max_size = 500; cfg.min_buffer = 500; - - install_parser_service(ctx, cfg); + request_parser pr(cfg.prepare()); system::error_code ec; - request_parser pr(ctx); - auto make_header = [](std::size_t n) -> std::string { return @@ -1971,17 +1907,14 @@ struct parser_test return header; }; - request_parser::config cfg; + parser_config cfg(cfg_); capy::polystore ctx; - cfg.min_buffer = 500; cfg.headers.max_size = 500; - - install_parser_service(ctx, cfg); - system::error_code ec; - request_parser pr(ctx); + request_parser pr(cfg.prepare()); pr.reset(); + system::error_code ec; for( size_t i = 0; i < 2000 ; i += 1 ) { pr.start(); @@ -2053,11 +1986,9 @@ struct parser_test testSetBodyLimit() { capy::polystore ctx; - response_parser::config cfg; + parser_config cfg(cfg_); cfg.body_limit = 7; - install_parser_service(ctx, cfg); - - response_parser pr(ctx); + response_parser pr(cfg.prepare()); // before reset BOOST_TEST_THROWS( @@ -2140,10 +2071,7 @@ struct parser_test // the parsed header must remain valid and // accessible if an error occurs during body parsing. - capy::polystore ctx; - response_parser::config cfg; - install_parser_service(ctx, cfg); - response_parser pr(ctx); + response_parser pr(pcfg_); pr.reset(); pr.start(); diff --git a/test/unit/parser_config.cpp b/test/unit/parser_config.cpp new file mode 100644 index 00000000..efed9efb --- /dev/null +++ b/test/unit/parser_config.cpp @@ -0,0 +1,31 @@ +// +// Copyright (c) 2021 Vinnie Falco (vinnie dot falco at gmail dot com) +// Copyright (c) 2024 Christian Mazakas +// +// 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 +// + +// Test that header file is self-contained. +#include + +#include "test_suite.hpp" + +namespace boost { +namespace http_proto { + +struct parser_config_test +{ + void run() + { + } +}; + +TEST_SUITE( + parser_config_test, + "boost.http_proto.parser_config"); + +} // http_proto +} // boost diff --git a/test/unit/request_parser.cpp b/test/unit/request_parser.cpp index c48f898f..e3534181 100644 --- a/test/unit/request_parser.cpp +++ b/test/unit/request_parser.cpp @@ -86,14 +86,14 @@ struct request_parser_test void good( - capy::polystore& ctx, + prepared_parser_config cfg, core::string_view s, core::string_view expected = {}) { for(std::size_t nmax = 1; nmax < s.size(); ++nmax) { - request_parser pr(ctx); + request_parser pr(cfg); if( BOOST_TEST(valid(pr, s, nmax)) ) { BOOST_TEST( pr.got_header() ); @@ -107,12 +107,12 @@ struct request_parser_test } void - bad(capy::polystore& ctx, core::string_view s) + bad(prepared_parser_config cfg, core::string_view s) { for(std::size_t nmax = 1; nmax < s.size(); ++nmax) { - request_parser pr(ctx); + request_parser pr(cfg); BOOST_TEST(! valid(pr, s, nmax)); } } @@ -135,13 +135,12 @@ struct request_parser_test BOOST_TEST(req.version() == v); }; - capy::polystore ctx; - request_parser::config cfg; - install_parser_service(ctx, cfg); + capy::polystore ps; + auto cfg = parser_config(role::client, ps).prepare(); // single buffer { - request_parser pr(ctx); + request_parser pr(cfg); pr.reset(); pr.start(); auto const b = *pr.prepare().begin(); @@ -163,7 +162,7 @@ struct request_parser_test for(std::size_t i = 1; i < s.size(); ++i) { - request_parser pr(ctx); + request_parser pr(cfg); pr.reset(); pr.start(); // first buffer @@ -200,45 +199,39 @@ struct request_parser_test void testSpecial() { + capy::polystore ctx; + auto cfg = parser_config(role::server, ctx).prepare(); + + // ~request_parser() // request_parser() { BOOST_TEST_NO_THROW(request_parser()); } - // operator=(request_parser&&) - { - request_parser pr; - BOOST_TEST_NO_THROW(pr = request_parser()); - } + // request_parser(prepared_parser_config) { - request_parser pr; - - capy::polystore ctx; - request_parser::config cfg; - install_parser_service(ctx, cfg); - - BOOST_TEST_NO_THROW(pr = request_parser(ctx)); - } - - // request_parser(capy::polystore&) - { - capy::polystore ctx; - request_parser::config cfg; - install_parser_service(ctx, cfg); - request_parser pr(ctx); + request_parser pr(cfg); } // request_parser(request_parser&&) { - capy::polystore ctx; - install_parser_service(ctx, {}); - request_parser pr1(ctx); + request_parser pr1(cfg); request_parser pr2(std::move(pr1)); } { BOOST_TEST_NO_THROW( request_parser(request_parser())); } + + // operator=(request_parser&&) + { + request_parser pr; + BOOST_TEST_NO_THROW(pr = request_parser(cfg)); + } + { + request_parser pr; + BOOST_TEST_NO_THROW(pr = request_parser()); + } } //-------------------------------------------- @@ -267,68 +260,67 @@ struct request_parser_test }; capy::polystore ctx; - request_parser::config cfg; - install_parser_service(ctx, cfg); - - bad(ctx, f(":")); - bad(ctx, f(" :")); - bad(ctx, f(" x:")); - bad(ctx, f("x :")); - bad(ctx, f("x@")); - bad(ctx, f("x@:")); - bad(ctx, f("\ra")); - bad(ctx, f("x: \r")); - bad(ctx, f("x: \ra")); - bad(ctx, f("x: \r\nabcd")); - bad(ctx, f("x: a\x01""bcd")); - - good(ctx, f("")); - good(ctx, f("x:")); - good(ctx, f("x: ")); - good(ctx, f("x:\t ")); - good(ctx, f("x:y")); - good(ctx, f("x: y")); - good(ctx, f("x:y ")); - good(ctx, f("x: y ")); - good(ctx, f("x:yy")); - good(ctx, f("x: yy")); - good(ctx, f("x:yy ")); - good(ctx, f("x: y y ")); - good(ctx, f("x:")); - good(ctx, f("x: \r\n"), + + auto cfg = parser_config(role::server, ctx).prepare(); + + bad(cfg, f(":")); + bad(cfg, f(" :")); + bad(cfg, f(" x:")); + bad(cfg, f("x :")); + bad(cfg, f("x@")); + bad(cfg, f("x@:")); + bad(cfg, f("\ra")); + bad(cfg, f("x: \r")); + bad(cfg, f("x: \ra")); + bad(cfg, f("x: \r\nabcd")); + bad(cfg, f("x: a\x01""bcd")); + + good(cfg, f("")); + good(cfg, f("x:")); + good(cfg, f("x: ")); + good(cfg, f("x:\t ")); + good(cfg, f("x:y")); + good(cfg, f("x: y")); + good(cfg, f("x:y ")); + good(cfg, f("x: y ")); + good(cfg, f("x:yy")); + good(cfg, f("x: yy")); + good(cfg, f("x:yy ")); + good(cfg, f("x: y y ")); + good(cfg, f("x:")); + good(cfg, f("x: \r\n"), f("x: ")); - good(ctx, f("x:\r\n"), + good(cfg, f("x:\r\n"), f("x:")); // obs-fold handling - good(ctx, f("x: \r\n "), + good(cfg, f("x: \r\n "), f("x: ")); - good(ctx, f("x: \r\n x"), + good(cfg, f("x: \r\n x"), f("x: x")); - good(ctx, f("x: \r\n \t\r\n \r\n\t "), + good(cfg, f("x: \r\n \t\r\n \r\n\t "), f("x: \t \t ")); - good(ctx, f("x: \r\n \t\r\n x"), + good(cfg, f("x: \r\n \t\r\n x"), f("x: \t x")); - good(ctx, f("x: y \r\n "), + good(cfg, f("x: y \r\n "), f("x: y ")); - good(ctx, f("x: \t\r\n abc def \r\n\t "), + good(cfg, f("x: \t\r\n abc def \r\n\t "), f("x: \t abc def \t ")); - good(ctx, f("x: abc\r\n def"), + good(cfg, f("x: abc\r\n def"), f("x: abc def")); // errata eid4189 - good(ctx, f("x: , , ,")); - good(ctx, f("x: abrowser/0.001 (C O M M E N T)")); - good(ctx, f("x: gzip , chunked")); + good(cfg, f("x: , , ,")); + good(cfg, f("x: abrowser/0.001 (C O M M E N T)")); + good(cfg, f("x: gzip , chunked")); } void testGet() { capy::polystore ctx; - request_parser::config cfg; - install_parser_service(ctx, cfg); - request_parser pr(ctx); + auto cfg = parser_config(role::server, ctx).prepare(); + request_parser pr(cfg); core::string_view s = "GET / HTTP/1.1\r\n" "Accept: *\r\n" diff --git a/test/unit/response_parser.cpp b/test/unit/response_parser.cpp index 164413ea..699dd2ce 100644 --- a/test/unit/response_parser.cpp +++ b/test/unit/response_parser.cpp @@ -23,45 +23,39 @@ class response_parser_test void testSpecial() { + capy::polystore ctx; + auto cfg = parser_config(role::server, ctx).prepare(); + + // ~response_parser() // response_parser() { BOOST_TEST_NO_THROW(response_parser()); } - // operator=(response_parser&&) + // response_parser(prepared_parser_config) { - response_parser pr; - - capy::polystore ctx; - response_parser::config cfg; - install_parser_service(ctx, cfg); - - BOOST_TEST_NO_THROW(pr = response_parser(ctx)); - } - { - response_parser pr; - BOOST_TEST_NO_THROW(pr = response_parser()); - } - - // response_parser(capy::polystore&) - { - capy::polystore ctx; - response_parser::config cfg; - install_parser_service(ctx, cfg); - response_parser pr(ctx); + response_parser pr(cfg); } // response_parser(response_parser&&) { - capy::polystore ctx; - install_parser_service(ctx, {}); - response_parser pr1(ctx); + response_parser pr1(cfg); response_parser pr2(std::move(pr1)); } { BOOST_TEST_NO_THROW( response_parser(response_parser())); } + + // operator=(response_parser&&) + { + response_parser pr; + BOOST_TEST_NO_THROW(pr = response_parser(cfg)); + } + { + response_parser pr; + BOOST_TEST_NO_THROW(pr = response_parser()); + } } void