diff --git a/include/boost/http_proto/detail/managed.hpp b/include/boost/http_proto/detail/managed.hpp deleted file mode 100644 index 3bbd01d3..00000000 --- a/include/boost/http_proto/detail/managed.hpp +++ /dev/null @@ -1,54 +0,0 @@ -// -// Copyright (c) 2025 Mohammad Nejati -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/http_proto -// - -#ifndef BOOST_HTTP_PROTO_DETAIL_MANAGED_HPP -#define BOOST_HTTP_PROTO_DETAIL_MANAGED_HPP - -namespace boost { -namespace http_proto { -namespace detail { - -template -class managed -{ - T value_ = Default; - -public: - managed() = default; - - managed(T s) noexcept - : value_(s) - { - } - - managed(managed&& other) noexcept - : value_(other.value_) - { - other.value_ = Default; - } - - managed& - operator=(managed&& other) noexcept - { - value_ = other.value_; - other.value_ = Default; - return *this; - } - - operator T() const noexcept - { - return value_; - } -}; - -} // detail -} // http_proto -} // boost - -#endif diff --git a/include/boost/http_proto/impl/serializer.hpp b/include/boost/http_proto/impl/serializer.hpp index ca1622c6..f3273fe7 100644 --- a/include/boost/http_proto/impl/serializer.hpp +++ b/include/boost/http_proto/impl/serializer.hpp @@ -13,8 +13,6 @@ #include -#include - namespace boost { namespace http_proto { @@ -127,11 +125,11 @@ start( "ConstBufferSequence type requirements not met"); start_init(m); - cbs_gen_ = std::addressof( - ws_.emplace::type>>( + start_buffers( + m, + ws().emplace::type>>( std::forward(cbs))); - start_buffers(m); } template< @@ -152,11 +150,10 @@ start( "The Source cannot be constructed with the given arguments"); start_init(m); - auto& src = construct_source( + auto& source = ws().emplace( std::forward(args)...); - source_ = std::addressof(src); - start_source(m); - return src; + start_source(m, source); + return source; } } // http_proto diff --git a/include/boost/http_proto/parser.hpp b/include/boost/http_proto/parser.hpp index af613dfe..3943c8d7 100644 --- a/include/boost/http_proto/parser.hpp +++ b/include/boost/http_proto/parser.hpp @@ -621,9 +621,6 @@ class parser BOOST_HTTP_PROTO_DECL parser(parser&& other) noexcept; - BOOST_HTTP_PROTO_DECL - parser& operator=(parser&& other) noexcept; - BOOST_HTTP_PROTO_DECL ~parser(); diff --git a/include/boost/http_proto/request_parser.hpp b/include/boost/http_proto/request_parser.hpp index 1eb031ae..e329e67e 100644 --- a/include/boost/http_proto/request_parser.hpp +++ b/include/boost/http_proto/request_parser.hpp @@ -107,29 +107,6 @@ class request_parser request_parser( request_parser&& other) noexcept = default; - /** Assignment. - - The states of `other` are transferred to - `this`, including the allocated buffer. - The previous states of `this` are - destroyed. After assignment, the only - valid operations on the moved-from object - are destruction and assignment. - - Buffer sequences previously obtained - using @ref prepare or @ref pull_body - remain valid. - - @par Complexity - Constant. - - @return A reference to this object. - - @param other The parser to assign from. - */ - request_parser& operator=( - request_parser&& other) noexcept = default; - /** Destructor. Any views or buffers obtained from this diff --git a/include/boost/http_proto/response_parser.hpp b/include/boost/http_proto/response_parser.hpp index d2cc4e46..00e0b772 100644 --- a/include/boost/http_proto/response_parser.hpp +++ b/include/boost/http_proto/response_parser.hpp @@ -107,29 +107,6 @@ class response_parser response_parser( response_parser&& other) noexcept = default; - /** Assignment. - - The states of `other` are transferred to - `this`, including the allocated buffer. - The previous states of `this` are - destroyed. After assignment, the only - valid operations on the moved-from object - are destruction and assignemt. - - Buffer sequences previously obtained - using @ref prepare or @ref pull_body - remain valid. - - @par Complexity - Constant. - - @return A reference to this object. - - @param other The parser to assign from. - */ - response_parser& operator=( - response_parser&& other) noexcept = default; - /** Destructor. Any views or buffers obtained from this diff --git a/include/boost/http_proto/serializer.hpp b/include/boost/http_proto/serializer.hpp index 083232e5..8337b978 100644 --- a/include/boost/http_proto/serializer.hpp +++ b/include/boost/http_proto/serializer.hpp @@ -11,15 +11,12 @@ #ifndef BOOST_HTTP_PROTO_SERIALIZER_HPP #define BOOST_HTTP_PROTO_SERIALIZER_HPP -#include #include -#include -#include #include #include -#include #include +#include #include #include #include @@ -32,10 +29,6 @@ namespace http_proto { // Forward declaration class message_view_base; -namespace detail { -class serializer_service; -class filter; -} // detail /** A serializer for HTTP/1 messages @@ -129,8 +122,9 @@ class serializer The states of `other` are transferred to the newly constructed object, which includes the allocated buffer. - After construction, the moved-from object - left in a valid but unspecified state. + After construction, the only valid + operations on the moved-from object + are destruction and assignment. Buffer sequences previously obtained using @ref prepare or @ref stream::prepare @@ -150,30 +144,6 @@ class serializer serializer( serializer&& other) noexcept; - /** Assignment. - - The states of `other` are transferred - `this`, which includes the allocated buffer. - The previous state of `this` are destroyed. - After assignment, the moved-from object - left in a valid but unspecified state. - - @par Postconditions - @code - other.is_done() == true - @endcode - - @par Complexity - Constant. - - @param other The serializer to assign from. - @return A reference to this object. - */ - BOOST_HTTP_PROTO_DECL - serializer& - operator=( - serializer&& other) noexcept; - /** Destructor */ BOOST_HTTP_PROTO_DECL @@ -225,11 +195,8 @@ class serializer @ref message_view_base. */ void - start( - message_view_base const& m) - { - start_empty(m); - } + BOOST_HTTP_PROTO_DECL + start(message_view_base const& m); /** Prepare the serializer for a new message with a ConstBufferSequence body. @@ -544,124 +511,38 @@ class serializer /** Return true if serialization is complete. */ + BOOST_HTTP_PROTO_DECL bool - is_done() const noexcept - { - return state_ == state::start; - } + is_done() const noexcept; private: + class impl; class cbs_gen; template class cbs_gen_impl; - detail::array_of_const_buffers - make_array(std::size_t n); - - template< - class Source, - class... Args, - typename std::enable_if< - std::is_constructible< - Source, - Args...>::value>::type* = nullptr> - Source& - construct_source(Args&&... args) - { - return ws_.emplace( - std::forward(args)...); - } - - template< - class Source, - class... Args, - typename std::enable_if< - std::is_constructible< - Source, - detail::workspace&, - Args...>::value>::type* = nullptr> - Source& - construct_source(Args&&... args) - { - return ws_.emplace( - ws_, std::forward(args)...); - } - BOOST_HTTP_PROTO_DECL - void - start_init( - message_view_base const&); + detail::workspace& + ws(); BOOST_HTTP_PROTO_DECL void - start_empty( + start_init( message_view_base const&); BOOST_HTTP_PROTO_DECL void start_buffers( - message_view_base const&); + message_view_base const&, + cbs_gen&); BOOST_HTTP_PROTO_DECL void start_source( - message_view_base const&); - - bool - is_header_done() const noexcept; - - void - out_init(); + message_view_base const&, + source&); - buffers::mutable_buffer_pair - out_prepare() noexcept; - - void - out_commit(std::size_t) noexcept; - - std::size_t - out_capacity() const noexcept; - - void - out_finish() noexcept; - - enum class state - { - reset, - start, - header, - body - }; - - enum class style - { - empty, - buffers, - source, - stream - }; - - const rts::context* ctx_; - detail::serializer_service* svc_; - detail::workspace ws_; - - detail::filter* filter_ = nullptr; - cbs_gen* cbs_gen_ = nullptr; - source* source_ = nullptr; - - buffers::circular_buffer out_; - buffers::circular_buffer in_; - detail::array_of_const_buffers prepped_; - buffers::const_buffer tmp_; - - detail::managed< - state, state::start> state_; - style style_ = style::empty; - uint8_t chunk_header_len_ = 0; - bool more_input_ = false; - bool is_chunked_ = false; - bool needs_exp100_continue_ = false; - bool filter_done_ = false; + impl* impl_; }; /** Serializer configuration settings. @@ -828,9 +709,9 @@ class serializer::stream @param other The object to move from. */ stream(stream&& other) noexcept - : sr_(other.sr_) + : impl_(other.impl_) { - other.sr_ = nullptr; + other.impl_ = nullptr; } /** Move assignment. @@ -849,15 +730,17 @@ class serializer::stream stream& operator=(stream&& other) noexcept { - std::swap(sr_, other.sr_); + std::swap(impl_, other.impl_); return *this; } /** Return true if the stream is open. */ - BOOST_HTTP_PROTO_DECL bool - is_open() const noexcept; + is_open() const noexcept + { + return impl_ != nullptr; + } /** Return the available capacity. @@ -961,20 +844,21 @@ class serializer::stream Closes the stream if open. */ - BOOST_HTTP_PROTO_DECL - ~stream(); + ~stream() + { + close(); + } private: friend class serializer; explicit - stream( - serializer& sr) noexcept - : sr_(&sr) + stream(serializer::impl* impl) noexcept + : impl_(impl) { } - serializer* sr_ = nullptr; + serializer::impl* impl_ = nullptr; }; } // http_proto diff --git a/src/detail/array_of_const_buffers.cpp b/src/detail/array_of_const_buffers.cpp index b10c0368..91a51bc2 100644 --- a/src/detail/array_of_const_buffers.cpp +++ b/src/detail/array_of_const_buffers.cpp @@ -8,7 +8,8 @@ // Official repository: https://github.com/cppalliance/http_proto // -#include +#include "src/detail/array_of_const_buffers.hpp" + #include #include diff --git a/include/boost/http_proto/detail/array_of_const_buffers.hpp b/src/detail/array_of_const_buffers.hpp similarity index 100% rename from include/boost/http_proto/detail/array_of_const_buffers.hpp rename to src/detail/array_of_const_buffers.hpp diff --git a/src/parser.cpp b/src/parser.cpp index 2930507e..4df14db1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1857,16 +1857,6 @@ parser(parser&& other) noexcept other.impl_ = nullptr; } -parser& -parser:: -operator=(parser&&other) noexcept -{ - delete impl_; - impl_ = other.impl_; - other.impl_ = nullptr; - return *this; -} - parser:: ~parser() { diff --git a/src/serializer.cpp b/src/serializer.cpp index 4dc2c1be..82813edf 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -10,12 +10,15 @@ // #include +#include #include #include +#include "src/detail/array_of_const_buffers.hpp" #include "src/detail/brotli_filter_base.hpp" #include "src/detail/zlib_filter_base.hpp" +#include #include #include #include @@ -230,10 +233,6 @@ clamp( return static_cast(x); } -} // namespace - -namespace detail { - class serializer_service : public rts::service { @@ -267,7 +266,7 @@ class serializer_service } }; -} // detail +} // namespace //------------------------------------------------ @@ -276,195 +275,179 @@ install_serializer_service( rts::context& ctx, serializer::config const& cfg) { - ctx.make_service< - detail::serializer_service>(cfg); + ctx.make_service(cfg); } //------------------------------------------------ -serializer:: -~serializer() +class serializer::impl { -} + friend stream; -// TODO: use an indirection for stream -// interface so it stays valid after move. -serializer:: -serializer(serializer&&) noexcept = default; + enum class state + { + reset, + start, + header, + body + }; -// TODO: use an indirection for stream -// interface so it stays valid after move. -serializer& -serializer:: -operator=(serializer&&) noexcept = default; + enum class style + { + empty, + buffers, + source, + stream + }; -serializer:: -serializer(const rts::context& ctx) - : ctx_(&ctx) - , svc_(&ctx_->get_service< - detail::serializer_service>()) - , ws_(svc_->space_needed) -{ -} + const rts::context& ctx_; + serializer_service& svc_; + detail::workspace ws_; -void -serializer:: -reset() noexcept -{ - ws_.clear(); - state_ = state::start; -} + detail::filter* filter_ = nullptr; + cbs_gen* cbs_gen_ = nullptr; + source* source_ = nullptr; -//------------------------------------------------ + buffers::circular_buffer out_; + buffers::circular_buffer in_; + detail::array_of_const_buffers prepped_; + buffers::const_buffer tmp_; -auto -serializer:: -prepare() -> - system::result -{ - // Precondition violation - if(state_ < state::header) - detail::throw_logic_error(); + state state_ = state::start; + style style_ = style::empty; + uint8_t chunk_header_len_ = 0; + bool more_input_ = false; + bool is_chunked_ = false; + bool needs_exp100_continue_ = false; + bool filter_done_ = false; - // Expect: 100-continue - if(needs_exp100_continue_) +public: + impl(const rts::context& ctx) + : ctx_(ctx) + , svc_(ctx_.get_service()) + , ws_(svc_.space_needed) { - if(!is_header_done()) - return const_buffers_type( - prepped_.begin(), - 1); // limit to header - - needs_exp100_continue_ = false; + } - BOOST_HTTP_PROTO_RETURN_EC( - error::expect_100_continue); + void + reset() noexcept + { + ws_.clear(); + state_ = state::start; } - if(!filter_) + auto + prepare() -> + system::result { - switch(style_) + // Precondition violation + if(state_ < state::header) + detail::throw_logic_error(); + + // Expect: 100-continue + if(needs_exp100_continue_) { - case style::empty: - break; + if(!is_header_done()) + return const_buffers_type( + prepped_.begin(), + 1); // limit to header + + needs_exp100_continue_ = false; + + BOOST_HTTP_PROTO_RETURN_EC( + error::expect_100_continue); + } - case style::buffers: + if(!filter_) { - // add more buffers if prepped_ is half empty. - if(more_input_ && - prepped_.capacity() >= prepped_.size()) + switch(style_) { - prepped_.slide_to_front(); - while(prepped_.capacity() != 0) - { - auto buf = cbs_gen_->next(); - if(buf.size() == 0) - break; - prepped_.append(buf); - } - if(cbs_gen_->is_empty()) + case style::empty: + break; + + case style::buffers: + { + // add more buffers if prepped_ is half empty. + if(more_input_ && + prepped_.capacity() >= prepped_.size()) { - if(is_chunked_) + prepped_.slide_to_front(); + while(prepped_.capacity() != 0) + { + auto buf = cbs_gen_->next(); + if(buf.size() == 0) + break; + prepped_.append(buf); + } + if(cbs_gen_->is_empty()) { - if(prepped_.capacity() != 0) + if(is_chunked_) + { + if(prepped_.capacity() != 0) + { + prepped_.append( + crlf_and_final_chunk); + more_input_ = false; + } + } + else { - prepped_.append( - crlf_and_final_chunk); more_input_ = false; } } - else - { - more_input_ = false; - } } + return const_buffers_type( + prepped_.begin(), + prepped_.size()); } - return const_buffers_type( - prepped_.begin(), - prepped_.size()); - } - - case style::source: - { - if(out_capacity() == 0 || !more_input_) - break; - const auto rs = source_->read( - out_prepare()); - - out_commit(rs.bytes); - - if(rs.ec.failed()) + case style::source: { - ws_.clear(); - state_ = state::reset; - return rs.ec; - } + if(out_capacity() == 0 || !more_input_) + break; - if(rs.finished) - { - more_input_ = false; - out_finish(); - } + const auto rs = source_->read( + out_prepare()); - break; - } + out_commit(rs.bytes); - case style::stream: - if(out_.size() == 0 && is_header_done() && more_input_) - BOOST_HTTP_PROTO_RETURN_EC( - error::need_data); - break; - } - } - else // filter - { - switch(style_) - { - case style::empty: - { - if(out_capacity() == 0 || filter_done_) - break; + if(rs.ec.failed()) + { + ws_.clear(); + state_ = state::reset; + return rs.ec; + } - const auto rs = filter_->process( - buffers::mutable_buffer_span( - out_prepare()), - {}, // empty input - false); + if(rs.finished) + { + more_input_ = false; + out_finish(); + } - if(rs.ec.failed()) - { - ws_.clear(); - state_ = state::reset; - return rs.ec; + break; } - out_commit(rs.out_bytes); - - if(rs.finished) - { - filter_done_ = true; - out_finish(); + case style::stream: + if(out_.size() == 0 && is_header_done() && more_input_) + BOOST_HTTP_PROTO_RETURN_EC( + error::need_data); + break; } - - break; } - - case style::buffers: + else // filter { - while(out_capacity() != 0 && !filter_done_) + switch(style_) { - if(more_input_ && tmp_.size() == 0) - { - tmp_ = cbs_gen_->next(); - if(tmp_.size() == 0) // cbs_gen_ is empty - more_input_ = false; - } + case style::empty: + { + if(out_capacity() == 0 || filter_done_) + break; const auto rs = filter_->process( buffers::mutable_buffer_span( out_prepare()), - { tmp_, {} }, - more_input_); + {}, // empty input + false); if(rs.ec.failed()) { @@ -473,40 +456,108 @@ prepare() -> return rs.ec; } - tmp_ = buffers::sans_prefix( - tmp_, rs.in_bytes); out_commit(rs.out_bytes); - if(rs.out_short) - break; - if(rs.finished) { filter_done_ = true; out_finish(); } + + break; } - break; - } - case style::source: - { - while(out_capacity() != 0 && !filter_done_) + case style::buffers: { - if(more_input_ && in_.capacity() != 0) + while(out_capacity() != 0 && !filter_done_) { - const auto rs = source_->read( - in_.prepare(in_.capacity())); + if(more_input_ && tmp_.size() == 0) + { + tmp_ = cbs_gen_->next(); + if(tmp_.size() == 0) // cbs_gen_ is empty + more_input_ = false; + } + + const auto rs = filter_->process( + buffers::mutable_buffer_span( + out_prepare()), + { tmp_, {} }, + more_input_); + + if(rs.ec.failed()) + { + ws_.clear(); + state_ = state::reset; + return rs.ec; + } + + tmp_ = buffers::sans_prefix( + tmp_, rs.in_bytes); + out_commit(rs.out_bytes); + + if(rs.out_short) + break; + + if(rs.finished) + { + filter_done_ = true; + out_finish(); + } + } + break; + } + + case style::source: + { + while(out_capacity() != 0 && !filter_done_) + { + if(more_input_ && in_.capacity() != 0) + { + const auto rs = source_->read( + in_.prepare(in_.capacity())); + if(rs.ec.failed()) + { + ws_.clear(); + state_ = state::reset; + return rs.ec; + } + if(rs.finished) + more_input_ = false; + in_.commit(rs.bytes); + } + + const auto rs = filter_->process( + buffers::mutable_buffer_span( + out_prepare()), + in_.data(), + more_input_); + if(rs.ec.failed()) { ws_.clear(); state_ = state::reset; return rs.ec; } + + in_.consume(rs.in_bytes); + out_commit(rs.out_bytes); + + if(rs.out_short) + break; + if(rs.finished) - more_input_ = false; - in_.commit(rs.bytes); + { + filter_done_ = true; + out_finish(); + } } + break; + } + + case style::stream: + { + if(out_capacity() == 0 || filter_done_) + break; const auto rs = filter_->process( buffers::mutable_buffer_span( @@ -524,416 +575,485 @@ prepare() -> in_.consume(rs.in_bytes); out_commit(rs.out_bytes); - if(rs.out_short) - break; - if(rs.finished) { filter_done_ = true; out_finish(); } - } - break; - } - case style::stream: - { - if(out_capacity() == 0 || filter_done_) + if(out_.size() == 0 && is_header_done() && more_input_) + BOOST_HTTP_PROTO_RETURN_EC( + error::need_data); break; + } + } + } - const auto rs = filter_->process( - buffers::mutable_buffer_span( - out_prepare()), - in_.data(), - more_input_); + prepped_.reset(!is_header_done()); + const auto cbp = out_.data(); + if(cbp[0].size() != 0) + prepped_.append(cbp[0]); + if(cbp[1].size() != 0) + prepped_.append(cbp[1]); - if(rs.ec.failed()) - { - ws_.clear(); - state_ = state::reset; - return rs.ec; - } + return const_buffers_type( + prepped_.begin(), + prepped_.size()); + } - in_.consume(rs.in_bytes); - out_commit(rs.out_bytes); + void + consume( + std::size_t n) + { + // Precondition violation + if(state_ < state::header) + detail::throw_logic_error(); - if(rs.finished) + if(!is_header_done()) + { + const auto header_remain = + prepped_[0].size(); + if(n < header_remain) { - filter_done_ = true; - out_finish(); + prepped_.consume(n); + return; } - - if(out_.size() == 0 && is_header_done() && more_input_) - BOOST_HTTP_PROTO_RETURN_EC( - error::need_data); - break; - } + n -= header_remain; + prepped_.consume(header_remain); + state_ = state::body; } - } - prepped_.reset(!is_header_done()); - const auto cbp = out_.data(); - if(cbp[0].size() != 0) - prepped_.append(cbp[0]); - if(cbp[1].size() != 0) - prepped_.append(cbp[1]); + prepped_.consume(n); - return const_buffers_type( - prepped_.begin(), - prepped_.size()); -} + // no-op when out_ is not in use + out_.consume(n); -void -serializer:: -consume( - std::size_t n) -{ - // Precondition violation - if(state_ < state::header) - detail::throw_logic_error(); + if(!prepped_.empty()) + return; - if(!is_header_done()) - { - const auto header_remain = - prepped_[0].size(); - if(n < header_remain) - { - prepped_.consume(n); + if(more_input_) return; - } - n -= header_remain; - prepped_.consume(header_remain); - state_ = state::body; - } - prepped_.consume(n); + if(filter_ && !filter_done_) + return; - // no-op when out_ is not in use - out_.consume(n); + if(needs_exp100_continue_) + return; - if(!prepped_.empty()) - return; + // ready for next message + reset(); + } - if(more_input_) - return; + void + start_init( + message_view_base const& m) + { + // Precondition violation + if(state_ != state::start) + detail::throw_logic_error(); - if(filter_ && !filter_done_) - return; + // TODO: To uphold the strong exception guarantee, + // `state_` must be reset to `state::start` if an + // exception is thrown during the start operation. + state_ = state::header; - if(needs_exp100_continue_) - return; + // VFALCO what do we do with + // metadata error code failures? + // m.ph_->md.maybe_throw(); - // ready for next message - reset(); -} + auto const& md = m.metadata(); + needs_exp100_continue_ = md.expect.is_100_continue; -//------------------------------------------------ + // Transfer-Encoding + is_chunked_ = md.transfer_encoding.is_chunked; -detail::array_of_const_buffers -serializer:: -make_array(std::size_t n) -{ - BOOST_ASSERT(n <= std::uint16_t(-1)); + // Content-Encoding + switch (md.content_encoding.coding) + { + case content_coding::deflate: + if(!svc_.cfg.apply_deflate_encoder) + goto no_filter; + filter_ = &ws_.emplace( + ctx_, + ws_, + svc_.cfg.zlib_comp_level, + svc_.cfg.zlib_window_bits, + svc_.cfg.zlib_mem_level); + filter_done_ = false; + break; - return { - ws_.push_array(n, - buffers::const_buffer{}), - static_cast(n) }; -} + case content_coding::gzip: + if(!svc_.cfg.apply_gzip_encoder) + goto no_filter; + filter_ = &ws_.emplace( + ctx_, + ws_, + svc_.cfg.zlib_comp_level, + svc_.cfg.zlib_window_bits + 16, + svc_.cfg.zlib_mem_level); + filter_done_ = false; + break; -void -serializer:: -start_init( - message_view_base const& m) -{ - // Precondition violation - if(state_ != state::start) - detail::throw_logic_error(); + case content_coding::br: + if(!svc_.cfg.apply_brotli_encoder) + goto no_filter; + filter_ = &ws_.emplace( + ctx_, + ws_, + svc_.cfg.brotli_comp_quality, + svc_.cfg.brotli_comp_window); + filter_done_ = false; + break; - // TODO: To uphold the strong exception guarantee, - // `state_` must be reset to `state::start` if an - // exception is thrown during the start operation. - state_ = state::header; + no_filter: + default: + filter_ = nullptr; + break; + } + } - // VFALCO what do we do with - // metadata error code failures? - // m.ph_->md.maybe_throw(); + void + start_empty( + message_view_base const& m) + { + start_init(m); + style_ = style::empty; - auto const& md = m.metadata(); - needs_exp100_continue_ = md.expect.is_100_continue; + prepped_ = make_array( + 1 + // header + 2); // out buffer pairs - // Transfer-Encoding - is_chunked_ = md.transfer_encoding.is_chunked; + out_init(); - // Content-Encoding - switch (md.content_encoding.coding) - { - case content_coding::deflate: - if(!svc_->cfg.apply_deflate_encoder) - goto no_filter; - filter_ = &ws_.emplace( - *ctx_, - ws_, - svc_->cfg.zlib_comp_level, - svc_->cfg.zlib_window_bits, - svc_->cfg.zlib_mem_level); - filter_done_ = false; - break; - - case content_coding::gzip: - if(!svc_->cfg.apply_gzip_encoder) - goto no_filter; - filter_ = &ws_.emplace( - *ctx_, - ws_, - svc_->cfg.zlib_comp_level, - svc_->cfg.zlib_window_bits + 16, - svc_->cfg.zlib_mem_level); - filter_done_ = false; - break; - - case content_coding::br: - if(!svc_->cfg.apply_brotli_encoder) - goto no_filter; - filter_ = &ws_.emplace( - *ctx_, - ws_, - svc_->cfg.brotli_comp_quality, - svc_->cfg.brotli_comp_window); - filter_done_ = false; - break; - - no_filter: - default: - filter_ = nullptr; - break; + if(!filter_) + out_finish(); + + prepped_.append({ m.ph_->cbuf, m.ph_->size }); + more_input_ = false; } -} -void -serializer:: -start_empty( - message_view_base const& m) -{ - start_init(m); - style_ = style::empty; + void + start_buffers( + message_view_base const& m, + cbs_gen& cbs_gen) + { + // start_init() already called + style_ = style::buffers; + cbs_gen_ = &cbs_gen; - prepped_ = make_array( - 1 + // header - 2); // out buffer pairs + if(!filter_) + { + auto stats = cbs_gen_->stats(); + auto batch_size = clamp(stats.count, 16); - out_init(); + prepped_ = make_array( + 1 + // header + batch_size + // buffers + (is_chunked_ ? 2 : 0)); // chunk header + final chunk - if(!filter_) - out_finish(); + prepped_.append({ m.ph_->cbuf, m.ph_->size }); + more_input_ = (batch_size != 0); - prepped_.append({ m.ph_->cbuf, m.ph_->size }); - more_input_ = false; -} + if(is_chunked_) + { + if(!more_input_) + { + prepped_.append(final_chunk); + } + else + { + auto h_len = chunk_header_len(stats.size); + buffers::mutable_buffer mb( + ws_.reserve_front(h_len), h_len); + write_chunk_header({ mb, {} }, stats.size); + prepped_.append(mb); + } + } + return; + } -void -serializer:: -start_buffers( - message_view_base const& m) -{ - using mutable_buffer = - buffers::mutable_buffer; + // filter + + prepped_ = make_array( + 1 + // header + 2); // out buffer pairs - // start_init() already called - style_ = style::buffers; + out_init(); - if(!filter_) + prepped_.append({ m.ph_->cbuf, m.ph_->size }); + tmp_ = {}; + more_input_ = true; + } + + void + start_source( + message_view_base const& m, + source& source) { - auto stats = cbs_gen_->stats(); - auto batch_size = clamp(stats.count, 16); + // start_init() already called + style_ = style::source; + source_ = &source; prepped_ = make_array( 1 + // header - batch_size + // buffers - (is_chunked_ ? 2 : 0)); // chunk header + final chunk + 2); // out buffer pairs + + if(filter_) + { + // TODO: smarter buffer distribution + auto const n = (ws_.size() - 1) / 2; + in_ = { ws_.reserve_front(n), n }; + } + + out_init(); prepped_.append({ m.ph_->cbuf, m.ph_->size }); - more_input_ = (batch_size != 0); + more_input_ = true; + } - if(is_chunked_) + stream + start_stream(message_view_base const& m) + { + start_init(m); + style_ = style::stream; + + prepped_ = make_array( + 1 + // header + 2); // out buffer pairs + + if(filter_) { - if(!more_input_) - { - prepped_.append(final_chunk); - } - else - { - auto h_len = chunk_header_len(stats.size); - mutable_buffer mb(ws_.reserve_front(h_len), h_len); - write_chunk_header({ mb, {} }, stats.size); - prepped_.append(mb); - } + // TODO: smarter buffer distribution + auto const n = (ws_.size() - 1) / 2; + in_ = { ws_.reserve_front(n), n }; } - return; + + out_init(); + + prepped_.append({ m.ph_->cbuf, m.ph_->size }); + more_input_ = true; + return stream{ this }; } - // filter + bool + is_done() const noexcept + { + return state_ == state::start; + } - prepped_ = make_array( - 1 + // header - 2); // out buffer pairs + detail::workspace& + ws() noexcept + { + return ws_; + } - out_init(); +private: + bool + is_header_done() const noexcept + { + return state_ == state::body; + } - prepped_.append({ m.ph_->cbuf, m.ph_->size }); - tmp_ = {}; - more_input_ = true; -} + detail::array_of_const_buffers + make_array(std::size_t n) + { + BOOST_ASSERT(n <= std::uint16_t(-1)); -void -serializer:: -start_source( - message_view_base const& m) -{ - // start_init() already called - style_ = style::source; + return { + ws_.push_array(n, + buffers::const_buffer{}), + static_cast(n) }; + } - prepped_ = make_array( - 1 + // header - 2); // out buffer pairs + void + out_init() + { + // use all the remaining buffer + auto const n = ws_.size() - 1; + out_ = { ws_.reserve_front(n), n }; + chunk_header_len_ = + chunk_header_len(out_.capacity()); + if(out_capacity() == 0) + detail::throw_length_error(); + } - if(filter_) + buffers::mutable_buffer_pair + out_prepare() noexcept { - // TODO: smarter buffer distribution - auto const n = (ws_.size() - 1) / 2; - in_ = { ws_.reserve_front(n), n }; + if(is_chunked_) + { + return buffers::sans_suffix( + buffers::sans_prefix( + out_.prepare(out_.capacity()), + chunk_header_len_), + crlf_and_final_chunk.size()); + } + return out_.prepare(out_.capacity()); } - out_init(); + void + out_commit( + std::size_t n) noexcept + { + if(is_chunked_) + { + if(n == 0) + return; - prepped_.append({ m.ph_->cbuf, m.ph_->size }); - more_input_ = true; -} + write_chunk_header(out_.prepare(chunk_header_len_), n); + out_.commit(chunk_header_len_); -auto -serializer:: -start_stream( - message_view_base const& m) -> - stream -{ - start_init(m); - style_ = style::stream; + out_.prepare(n); + out_.commit(n); - prepped_ = make_array( - 1 + // header - 2); // out buffer pairs + buffers::copy(out_.prepare(crlf.size()), crlf); + out_.commit(crlf.size()); + } + else + { + out_.commit(n); + } + } - if(filter_) + std::size_t + out_capacity() const noexcept { - // TODO: smarter buffer distribution - auto const n = (ws_.size() - 1) / 2; - in_ = { ws_.reserve_front(n), n }; + if(is_chunked_) + { + auto const overhead = chunk_header_len_ + + crlf_and_final_chunk.size(); + if(out_.capacity() < overhead) + return 0; + return out_.capacity() - overhead; + } + return out_.capacity(); } - out_init(); + void + out_finish() noexcept + { + if(is_chunked_) + { + buffers::copy( + out_.prepare(final_chunk.size()), final_chunk); + out_.commit(final_chunk.size()); + } + } +}; - prepped_.append({ m.ph_->cbuf, m.ph_->size }); - more_input_ = true; - return stream{ *this }; +//------------------------------------------------ + +serializer:: +serializer(const rts::context& ctx) + : impl_(new impl(ctx)) +{ + // TODO: use a single allocation for + // impl and workspace buffer. } -bool serializer:: -is_header_done() const noexcept +serializer(serializer&& other) noexcept + : impl_(other.impl_) { - return state_ == state::body; + other.impl_ = nullptr; } -void serializer:: -out_init() +~serializer() { - // use all the remaining buffer - auto const n = ws_.size() - 1; - out_ = { ws_.reserve_front(n), n }; - chunk_header_len_ = - chunk_header_len(out_.capacity()); - if(out_capacity() == 0) - detail::throw_length_error(); + delete impl_; } -buffers::mutable_buffer_pair +void serializer:: -out_prepare() noexcept +reset() noexcept { - if(is_chunked_) - { - return buffers::sans_suffix( - buffers::sans_prefix( - out_.prepare(out_.capacity()), - chunk_header_len_), - crlf_and_final_chunk.size()); - } - return out_.prepare(out_.capacity()); + BOOST_ASSERT(impl_); + impl_->reset(); } void serializer:: -out_commit( - std::size_t n) noexcept +start(message_view_base const& m) { - if(is_chunked_) - { - if(n == 0) - return; + BOOST_ASSERT(impl_); + impl_->start_empty(m); +} + +auto +serializer:: +start_stream( + message_view_base const& m) -> stream +{ + BOOST_ASSERT(impl_); + return impl_->start_stream(m); +} - write_chunk_header(out_.prepare(chunk_header_len_), n); - out_.commit(chunk_header_len_); +auto +serializer:: +prepare() -> + system::result +{ + BOOST_ASSERT(impl_); + return impl_->prepare(); +} - out_.prepare(n); - out_.commit(n); +void +serializer:: +consume(std::size_t n) +{ + BOOST_ASSERT(impl_); + impl_->consume(n); +} - buffers::copy(out_.prepare(crlf.size()), crlf); - out_.commit(crlf.size()); - } - else - { - out_.commit(n); - } +bool +serializer:: +is_done() const noexcept +{ + BOOST_ASSERT(impl_); + return impl_->is_done(); } -std::size_t +//------------------------------------------------ + +detail::workspace& serializer:: -out_capacity() const noexcept +ws() { - if(is_chunked_) - { - auto const overhead = chunk_header_len_ + - crlf_and_final_chunk.size(); - if(out_.capacity() < overhead) - return 0; - return out_.capacity() - overhead; - } - return out_.capacity(); + BOOST_ASSERT(impl_); + return impl_->ws(); } void serializer:: -out_finish() noexcept +start_init(message_view_base const& m) { - if(is_chunked_) - { - buffers::copy( - out_.prepare(final_chunk.size()), final_chunk); - out_.commit(final_chunk.size()); - } + BOOST_ASSERT(impl_); + impl_->start_init(m); } -//------------------------------------------------ +void +serializer:: +start_buffers( + message_view_base const& m, + cbs_gen& cbs_gen) +{ + BOOST_ASSERT(impl_); + impl_->start_buffers(m, cbs_gen); +} -bool +void serializer:: -stream:: -is_open() const noexcept +start_source( + message_view_base const& m, + source& source) { - return sr_ != nullptr; + BOOST_ASSERT(impl_); + impl_->start_source(m, source); } +//------------------------------------------------ + std::size_t serializer:: stream:: @@ -943,10 +1063,10 @@ capacity() const if(!is_open()) detail::throw_logic_error(); - if(sr_->filter_) - return sr_->in_.capacity(); + if(impl_->filter_) + return impl_->in_.capacity(); - return sr_->out_capacity(); + return impl_->out_capacity(); } auto @@ -959,11 +1079,11 @@ prepare() -> if(!is_open()) detail::throw_logic_error(); - if(sr_->filter_) - return sr_->in_.prepare( - sr_->in_.capacity()); + if(impl_->filter_) + return impl_->in_.prepare( + impl_->in_.capacity()); - return sr_->out_prepare(); + return impl_->out_prepare(); } void @@ -979,10 +1099,10 @@ commit(std::size_t n) if(n > capacity()) detail::throw_invalid_argument(); - if(sr_->filter_) - return sr_->in_.commit(n); + if(impl_->filter_) + return impl_->in_.commit(n); - sr_->out_commit(n); + impl_->out_commit(n); } void @@ -993,18 +1113,11 @@ close() noexcept if(!is_open()) return; // no-op; - if(!sr_->filter_) - sr_->out_finish(); + if(!impl_->filter_) + impl_->out_finish(); - sr_->more_input_ = false; - sr_ = nullptr; -} - -serializer:: -stream:: -~stream() -{ - close(); + impl_->more_input_ = false; + impl_ = nullptr; } } // http_proto diff --git a/test/unit/parser.cpp b/test/unit/parser.cpp index e823beef..d1e446d7 100644 --- a/test/unit/parser.cpp +++ b/test/unit/parser.cpp @@ -363,34 +363,6 @@ struct parser_test BOOST_TEST_EQ(pr2.body(), body); } - // parser& operator=(parser&&) - { - rts::context ctx; - install_parser_service(ctx, {}); - - core::string_view header = - "POST / HTTP/1.1\r\n" - "Content-Length: 3\r\n" - "\r\n"; - core::string_view body = "123"; - pieces in = { header, body }; - - request_parser pr1(ctx); - request_parser pr2(ctx); - - pr1.reset(); - pr1.start(); - system::error_code ec; - read_header(pr1, in, ec); - - pr2 = std::move(pr1); - - BOOST_TEST_EQ(pr2.get().buffer(), header); - read(pr2, in, ec); - BOOST_TEST_NOT(ec.failed()); - BOOST_TEST_EQ(pr2.body(), body); - } - // ~parser { request_parser pr(ctx_); diff --git a/test/unit/request_parser.cpp b/test/unit/request_parser.cpp index 3979cc83..68f520c0 100644 --- a/test/unit/request_parser.cpp +++ b/test/unit/request_parser.cpp @@ -215,15 +215,6 @@ struct request_parser_test request_parser pr1(ctx); request_parser pr2(std::move(pr1)); } - - // request_parser& operator=(request_parser&&) - { - rts::context ctx; - install_parser_service(ctx, {}); - request_parser pr1(ctx); - request_parser pr2(ctx); - pr2 = std::move(pr1); - } } //-------------------------------------------- diff --git a/test/unit/response_parser.cpp b/test/unit/response_parser.cpp index dbf922d5..a56b250d 100644 --- a/test/unit/response_parser.cpp +++ b/test/unit/response_parser.cpp @@ -38,15 +38,6 @@ class response_parser_test response_parser pr1(ctx); response_parser pr2(std::move(pr1)); } - - // response_parser& operator=(response_parser&&) - { - rts::context ctx; - install_parser_service(ctx, {}); - response_parser pr1(ctx); - response_parser pr2(ctx); - pr2 = std::move(pr1); - } } void diff --git a/test/unit/serializer.cpp b/test/unit/serializer.cpp index 8e3021b3..e7976f37 100644 --- a/test/unit/serializer.cpp +++ b/test/unit/serializer.cpp @@ -240,51 +240,6 @@ struct serializer_test serializer sr2(std::move(sr1)); - // valid moved-from state - BOOST_TEST(sr1.is_done()); - BOOST_TEST_THROWS( - sr1.start(res), - std::length_error); - - // consume the reset from sr2 - { - auto cbs = sr2.prepare().value(); - auto n = buffers::copy( - buf.prepare(buffers::size(cbs)), - cbs); - sr2.consume(n); - buf.commit(n); - } - - BOOST_TEST(sr2.is_done()); - BOOST_TEST(message == expected); - } - - // serializer& operator=(serializer&&) - { - std::string message; - buffers::string_buffer buf(&message); - serializer sr1(ctx); - sr1.start(res); - - // consume 5 bytes - { - auto cbs = sr1.prepare().value(); - auto n = buffers::copy(buf.prepare(5), cbs); - sr1.consume(n); - buf.commit(n); - BOOST_TEST_EQ(n, 5); - } - - serializer sr2(ctx); - sr2 = std::move(sr1); - - // valid moved-from state - BOOST_TEST(sr1.is_done()); - BOOST_TEST_THROWS( - sr1.start(res), - std::length_error); - // consume the reset from sr2 { auto cbs = sr2.prepare().value();