LCOV - code coverage report
Current view: top level - libs/http_proto/src/serializer.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 94.2 % 415 391
Test Date: 2025-06-15 05:10:37 Functions: 97.0 % 33 32

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3              : // Copyright (c) 2024 Christian Mazakas
       4              : // Copyright (c) 2024 Mohammad Nejati
       5              : //
       6              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       7              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       8              : //
       9              : // Official repository: https://github.com/cppalliance/http_proto
      10              : //
      11              : 
      12              : #include <boost/http_proto/detail/except.hpp>
      13              : #include <boost/http_proto/message_view_base.hpp>
      14              : #include <boost/http_proto/serializer.hpp>
      15              : #include <boost/http_proto/service/zlib_service.hpp>
      16              : 
      17              : #include "src/detail/filter.hpp"
      18              : 
      19              : #include <boost/buffers/copy.hpp>
      20              : #include <boost/buffers/prefix.hpp>
      21              : #include <boost/buffers/sans_prefix.hpp>
      22              : #include <boost/buffers/sans_suffix.hpp>
      23              : #include <boost/buffers/suffix.hpp>
      24              : #include <boost/buffers/size.hpp>
      25              : #include <boost/core/ignore_unused.hpp>
      26              : 
      27              : #include <stddef.h>
      28              : 
      29              : namespace boost {
      30              : namespace http_proto {
      31              : 
      32              : namespace {
      33              : 
      34              : class deflator_filter
      35              :     : public http_proto::detail::filter
      36              : {
      37              :     zlib::stream& deflator_;
      38              : 
      39              : public:
      40           49 :     deflator_filter(
      41              :         context& ctx,
      42              :         http_proto::detail::workspace& ws,
      43              :         bool use_gzip)
      44          196 :         : deflator_{ ctx.get_service<zlib::service>()
      45           49 :             .make_deflator(ws, -1, use_gzip ? 31 : 15, 8) }
      46              :     {
      47           49 :     }
      48              : 
      49              :     virtual filter::results
      50        21237 :     on_process(
      51              :         buffers::mutable_buffer out,
      52              :         buffers::const_buffer in,
      53              :         bool more) override
      54              :     {
      55        21237 :         auto flush =
      56        21237 :             more ? zlib::flush::none : zlib::flush::finish;
      57        21237 :         filter::results results;
      58              : 
      59              :         for(;;)
      60              :         {
      61        38871 :             auto params = zlib::params{in.data(), in.size(),
      62        38871 :                 out.data(), out.size() };
      63        38871 :             auto ec = deflator_.write(params, flush);
      64              : 
      65        38871 :             results.in_bytes  += in.size() - params.avail_in;
      66        38871 :             results.out_bytes += out.size() - params.avail_out;
      67              : 
      68        47796 :             if( ec.failed() &&
      69        47796 :                 ec != zlib::error::buf_err)
      70              :             {
      71            1 :                 results.ec = ec;
      72        21237 :                 return results;
      73              :             }
      74              : 
      75        38870 :             if( ec == zlib::error::stream_end )
      76              :             {
      77           48 :                 results.finished = true;
      78           48 :                 return results;
      79              :             }
      80              : 
      81        38822 :             in  = buffers::suffix(in, params.avail_in);
      82        38822 :             out = buffers::suffix(out, params.avail_out);
      83              : 
      84        38822 :             if( out.size() == 0 )
      85         3524 :                 return results;
      86              : 
      87        35298 :             if( in.size() == 0 )
      88              :             {
      89        35298 :                 if( results.out_bytes == 0 &&
      90              :                     flush == zlib::flush::none )
      91              :                 {
      92              :                     // TODO: Is flush::block the right choice?
      93              :                     // We might need a filter::flush() interface
      94              :                     // so that the caller can decide when to flush.
      95        17634 :                     flush = zlib::flush::block;
      96        17634 :                     continue;
      97              :                 }
      98        17664 :                 return results;
      99              :             }
     100        17634 :         }
     101              :     }
     102              : };
     103              : 
     104              : //------------------------------------------------
     105              : 
     106              : constexpr
     107              : std::size_t
     108              : crlf_len = 2;
     109              : 
     110              : constexpr
     111              : std::size_t
     112              : chunk_header_len = 16 + crlf_len;
     113              : 
     114              : constexpr
     115              : std::size_t
     116              : final_chunk_len = 1 + crlf_len + crlf_len;
     117              : 
     118              : constexpr
     119              : std::size_t
     120              : chunked_overhead_ =
     121              :     chunk_header_len +
     122              :     crlf_len +
     123              :     final_chunk_len;
     124              : 
     125              : template<class MutableBufferSequence>
     126              : void
     127         4567 : write_chunk_header(
     128              :     const MutableBufferSequence& mbs,
     129              :     std::size_t size) noexcept
     130              : {
     131              :     static constexpr char hexdig[] =
     132              :         "0123456789ABCDEF";
     133              :     char buf[18];
     134         4567 :     auto p = buf + 16;
     135        77639 :     for(std::size_t i = 16; i--;)
     136              :     {
     137        73072 :         *--p = hexdig[size & 0xf];
     138        73072 :         size >>= 4;
     139              :     }
     140         4567 :     buf[16] = '\r';
     141         4567 :     buf[17] = '\n';
     142         4567 :     auto n = buffers::copy(
     143              :         mbs,
     144         9134 :         buffers::const_buffer(
     145              :             buf, sizeof(buf)));
     146              :     ignore_unused(n);
     147         4567 :     BOOST_ASSERT(n == 18);
     148         4567 : }
     149              : 
     150              : template<class MutableBufferSequence>
     151              : void
     152         4567 : write_crlf(
     153              :     const MutableBufferSequence& mbs) noexcept
     154              : {
     155         4567 :     auto n = buffers::copy(
     156              :         mbs,
     157         9134 :         buffers::const_buffer(
     158              :             "\r\n", 2));
     159              :     ignore_unused(n);
     160         4567 :     BOOST_ASSERT(n == 2);
     161         4567 : }
     162              : 
     163              : template<class MutableBufferSequence>
     164              : void
     165           31 : write_final_chunk(
     166              :     const MutableBufferSequence& mbs) noexcept
     167              : {
     168           31 :     auto n = buffers::copy(
     169              :         mbs,
     170           62 :         buffers::const_buffer(
     171              :             "0\r\n\r\n", 5));
     172              :     ignore_unused(n);
     173           31 :     BOOST_ASSERT(n == 5);
     174           31 : }
     175              : 
     176              : //------------------------------------------------
     177              : 
     178              : class appender
     179              : {
     180              :     buffers::circular_buffer& cb_;
     181              :     buffers::mutable_buffer_pair mbp_;
     182              :     std::size_t n_ = 0;
     183              :     bool is_chunked_ = false;
     184              :     bool more_input_ = true;
     185              : 
     186              : public:
     187         9071 :     appender(
     188              :         buffers::circular_buffer& cb,
     189              :         bool is_chunked)
     190         9071 :         : cb_(cb)
     191         9071 :         , mbp_(cb.prepare(cb.capacity()))
     192         9071 :         , is_chunked_(is_chunked)
     193              :     {
     194         9071 :     }
     195              : 
     196              :     bool
     197        21335 :     is_full() const noexcept
     198              :     {
     199        21335 :         auto remaining = cb_.capacity() - n_;
     200        21335 :         if(is_chunked_)
     201        10688 :             return remaining <= chunked_overhead_;
     202              : 
     203        10647 :         return remaining == 0;
     204              :     }
     205              : 
     206              :     buffers::mutable_buffer_pair
     207        12331 :     prepare() noexcept
     208              :     {
     209        12331 :         if(is_chunked_)
     210              :         {
     211         6168 :             return buffers::sans_suffix(
     212         6168 :                 buffers::sans_prefix(
     213         6168 :                     mbp_,
     214         6168 :                     chunk_header_len + n_)
     215         6168 :                 , final_chunk_len + crlf_len);
     216              :         }
     217         6163 :         return buffers::sans_prefix(mbp_, n_);
     218              :     }
     219              : 
     220              :     void
     221        12329 :     commit(std::size_t n, bool more) noexcept
     222              :     {
     223        12329 :         BOOST_ASSERT(more_input_);
     224        12329 :         n_ += n;
     225        12329 :         more_input_ = more;
     226        12329 :     }
     227              : 
     228         9071 :     ~appender()
     229              :     {
     230         9071 :         if(is_chunked_)
     231              :         {
     232         4552 :             if(n_)
     233              :             {
     234         4551 :                 write_chunk_header(mbp_, n_);
     235         4551 :                 cb_.commit(n_ + chunk_header_len);
     236              : 
     237         4551 :                 write_crlf(
     238         4551 :                     cb_.prepare(crlf_len));
     239         4551 :                 cb_.commit(crlf_len);
     240              :             }
     241              : 
     242         4552 :             if(!more_input_)
     243              :             {
     244           26 :                 write_final_chunk(
     245           26 :                     cb_.prepare(final_chunk_len));
     246           26 :                 cb_.commit(final_chunk_len);
     247              :             }
     248              :         }
     249              :         else // is_chunked_ == false
     250              :         {
     251         4519 :             cb_.commit(n_);
     252              :         }
     253         9071 :     }
     254              : };
     255              : 
     256              : } // namespace
     257              : 
     258              : //------------------------------------------------
     259              : 
     260           45 : serializer::
     261              : ~serializer()
     262              : {
     263           45 : }
     264              : 
     265            0 : serializer::
     266              : serializer(
     267              :     serializer&&) noexcept = default;
     268              : 
     269           11 : serializer::
     270              : serializer(
     271           11 :     context& ctx)
     272           11 :     : serializer(ctx, 65536)
     273              : {
     274           11 : }
     275              : 
     276           45 : serializer::
     277              : serializer(
     278              :     context& ctx,
     279           45 :     std::size_t buffer_size)
     280           45 :     : ctx_(ctx)
     281           45 :     , ws_(buffer_size)
     282              : {
     283           45 : }
     284              : 
     285              : void
     286          131 : serializer::
     287              : reset() noexcept
     288              : {
     289          131 :     filter_ = nullptr;
     290              : 
     291          131 :     cb0_ = {};
     292          131 :     tmp_ = {};
     293              : 
     294          131 :     more_input_ = false;
     295          131 :     is_done_ = false;
     296          131 :     is_header_done_ = false;
     297          131 :     is_chunked_ = false;
     298          131 :     needs_exp100_continue_ = false;
     299          131 :     filter_done_ = false;
     300              : 
     301          131 :     ws_.clear();
     302          131 : }
     303              : 
     304              : //------------------------------------------------
     305              : 
     306              : auto
     307         9138 : serializer::
     308              : prepare() ->
     309              :     system::result<const_buffers_type>
     310              : {
     311              :     // Precondition violation
     312         9138 :     if(is_done_)
     313            1 :         detail::throw_logic_error();
     314              : 
     315              :     // Expect: 100-continue
     316         9137 :     if(needs_exp100_continue_)
     317              :     {
     318            4 :         if(!is_header_done_)
     319            4 :             return const_buffers_type(
     320            2 :                 prepped_.begin(),
     321            2 :                 1); // limit to header
     322              : 
     323            2 :         needs_exp100_continue_ = false;
     324              : 
     325            2 :         BOOST_HTTP_PROTO_RETURN_EC(
     326              :             error::expect_100_continue);
     327              :     }
     328              : 
     329         9133 :     if(!filter_)
     330              :     {
     331           80 :         switch(st_)
     332              :         {
     333            3 :         case style::empty:
     334            6 :             return const_buffers_type(
     335            3 :                 prepped_.begin(),
     336            6 :                 prepped_.size());
     337              : 
     338           19 :         case style::buffers:
     339              :             // add more buffers if prepped_ is half empty.
     340           29 :             if(more_input_ &&
     341           10 :                 prepped_.capacity() >= prepped_.size())
     342              :             {
     343            2 :                 prepped_.slide_to_front();
     344           15 :                 while(prepped_.capacity() != 0)
     345              :                 {
     346           15 :                     auto buf = buf_gen_->operator()();
     347           15 :                     if(buf.size() != 0)
     348              :                     {
     349           13 :                         prepped_.append(buf);
     350              :                     }
     351              :                     else // buf_gen_ is empty
     352              :                     {
     353              :                         // crlf and final chunk
     354            2 :                         if(tmp_.size() != 0)
     355              :                         {
     356            1 :                             prepped_.append(tmp_);
     357            1 :                             tmp_ = {};
     358              :                         }
     359            2 :                         break;
     360              :                     }
     361              :                 }
     362            2 :                 if(buf_gen_->is_empty() && tmp_.size() == 0)
     363            2 :                     more_input_ = false;
     364              :             }
     365           38 :             return const_buffers_type(
     366           19 :                 prepped_.begin(),
     367           38 :                 prepped_.size());
     368              : 
     369           23 :         case style::source:
     370              :         {
     371           23 :             if(!more_input_)
     372           22 :                 break;
     373              : 
     374              :             // handles chunked payloads automatically
     375           18 :             appender apndr(cb0_, is_chunked_);
     376              : 
     377           18 :             if(apndr.is_full())
     378            0 :                 break;
     379              : 
     380           18 :             auto rs = source_->read(
     381           18 :                 apndr.prepare());
     382              : 
     383           18 :             if(rs.ec.failed())
     384              :             {
     385            1 :                 is_done_ = true;
     386            1 :                 BOOST_HTTP_PROTO_RETURN_EC(rs.ec);
     387              :             }
     388              : 
     389           17 :             more_input_ = !rs.finished;
     390           17 :             apndr.commit(rs.bytes, more_input_);
     391           17 :             break;
     392           18 :         }
     393              : 
     394           35 :         case style::stream:
     395           35 :             if(is_header_done_ && cb0_.size() == 0)
     396            1 :                 BOOST_HTTP_PROTO_RETURN_EC(
     397              :                     error::need_data);
     398           34 :             break;
     399              :         }
     400              :     }
     401              :     else // filter
     402              :     {
     403         9053 :         if(st_ == style::empty)
     404            0 :             return const_buffers_type(
     405            0 :                 prepped_.begin(),
     406            0 :                 prepped_.size());
     407              : 
     408        17793 :         auto get_input = [&]()
     409              :         {
     410        17793 :             if(st_ == style::buffers)
     411              :             {
     412              :                 // TODO: for efficiency of deflator, we might
     413              :                 // need to return multiple buffers at once
     414         1329 :                 if(tmp_.size() == 0)
     415              :                 {
     416          385 :                     tmp_ = buf_gen_->operator()();
     417          385 :                     more_input_ = !buf_gen_->is_empty();
     418              :                 }
     419              :                 return buffers::
     420         1329 :                     const_buffer_pair{ tmp_, {} };
     421              :             }
     422              : 
     423        16464 :             BOOST_ASSERT(
     424              :                 st_ == style::source ||
     425              :                 st_ == style::stream);
     426              : 
     427        38416 :             if(st_ == style::source &&
     428        21944 :                 more_input_ &&
     429         5480 :                 cb1_.capacity() != 0)
     430              :             {
     431              :                 // TODO: handle source error
     432         5480 :                 auto rs = source_->read(
     433         5480 :                     cb1_.prepare(cb1_.capacity()));
     434         5480 :                 if(rs.finished)
     435           16 :                     more_input_ = false;
     436         5480 :                 cb1_.commit(rs.bytes);
     437              :             }
     438              : 
     439        16464 :             return cb1_.data();
     440         9053 :         };
     441              : 
     442        12312 :         auto consume = [&](std::size_t n)
     443              :         {
     444        12312 :             if(st_ == style::buffers)
     445              :             {
     446         1328 :                 tmp_ = buffers::sans_prefix(
     447         1328 :                     tmp_, n);
     448         1328 :                 return;
     449              :             }
     450        10984 :             BOOST_ASSERT(
     451              :                 st_ == style::source ||
     452              :                 st_ == style::stream);
     453        10984 :             cb1_.consume(n);
     454         9053 :         };
     455              : 
     456              :         // handles chunked payloads automatically
     457         9053 :         appender apndr(cb0_, is_chunked_);
     458              :         for(;;)
     459              :         {
     460        21317 :             if(apndr.is_full())
     461         3524 :                 break;
     462              : 
     463        17793 :             auto cbs = get_input();
     464              : 
     465        17793 :             if(more_input_ && buffers::size(cbs) == 0)
     466         5480 :                 break;
     467              : 
     468        12313 :             auto rs = filter_->process(
     469            0 :                 apndr.prepare(),
     470              :                 cbs,
     471        12313 :                 more_input_);
     472              : 
     473        12313 :             if(rs.ec.failed())
     474              :             {
     475            1 :                 is_done_ = true;
     476            1 :                 BOOST_HTTP_PROTO_RETURN_EC(rs.ec);
     477              :             }
     478              : 
     479        12312 :             consume(rs.in_bytes);
     480        12312 :             apndr.commit(rs.out_bytes, !rs.finished);
     481              : 
     482        12312 :             if(rs.finished)
     483              :             {
     484           48 :                 filter_done_ = true;
     485           48 :                 break;
     486              :             }
     487        12264 :         }
     488         9053 :     }
     489              : 
     490         9108 :     prepped_.reset(!is_header_done_);
     491         9108 :     const auto cbp = cb0_.data();
     492         9108 :     if(cbp[0].size() != 0)
     493         9105 :         prepped_.append(cbp[0]);
     494         9108 :     if(cbp[1].size() != 0)
     495           14 :         prepped_.append(cbp[1]);
     496              : 
     497         9108 :     BOOST_ASSERT(
     498              :         buffers::size(prepped_) > 0);
     499              : 
     500        18216 :     return const_buffers_type(
     501         9108 :         prepped_.begin(),
     502        18216 :         prepped_.size());
     503              : }
     504              : 
     505              : void
     506        10871 : serializer::
     507              : consume(
     508              :     std::size_t n)
     509              : {
     510              :     // Precondition violation
     511        10871 :     if(is_done_ && n != 0)
     512            1 :         detail::throw_logic_error();
     513              : 
     514        10870 :     if(!is_header_done_)
     515              :     {
     516              :         const auto header_remain =
     517           76 :             prepped_[0].size();
     518           76 :         if(n < header_remain)
     519              :         {
     520           11 :             prepped_.consume(n);
     521           11 :             return;
     522              :         }
     523           65 :         n -= header_remain;
     524           65 :         prepped_.consume(header_remain);
     525           65 :         is_header_done_ = true;
     526              :     }
     527              : 
     528        10859 :     prepped_.consume(n);
     529              : 
     530              :     // no-op when cb0_ is not in use
     531        10859 :     cb0_.consume(n);
     532              : 
     533        10859 :     if(!prepped_.empty())
     534         1760 :         return;
     535              : 
     536         9099 :     if(needs_exp100_continue_)
     537            1 :         return;
     538              : 
     539         9098 :     if(more_input_)
     540         8981 :         return;
     541              : 
     542          117 :     if(filter_ && !filter_done_)
     543           52 :         return;
     544              : 
     545           65 :     is_done_ = true;
     546              : }
     547              : 
     548              : //------------------------------------------------
     549              : 
     550              : detail::array_of_const_buffers
     551           75 : serializer::
     552              : make_array(std::size_t n)
     553              : {
     554           75 :     if(n > std::numeric_limits<std::uint16_t>::max())
     555            0 :         detail::throw_length_error();
     556              : 
     557              :     return {
     558           75 :         ws_.push_array(n,
     559            0 :             buffers::const_buffer{}),
     560           75 :         static_cast<std::uint16_t>(n) };
     561              : }
     562              : 
     563              : void
     564           75 : serializer::
     565              : start_init(
     566              :     message_view_base const& m)
     567              : {
     568           75 :     reset();
     569              : 
     570              :     // VFALCO what do we do with
     571              :     // metadata error code failures?
     572              :     // m.ph_->md.maybe_throw();
     573              : 
     574           75 :     auto const& md = m.metadata();
     575           75 :     needs_exp100_continue_ = md.expect.is_100_continue;
     576              : 
     577              :     // Transfer-Encoding
     578           75 :     is_chunked_ = md.transfer_encoding.is_chunked;
     579              : 
     580              :     // Content-Encoding
     581           75 :     auto const& ce = md.content_encoding;
     582           75 :     if(ce.encoding == encoding::deflate)
     583              :     {
     584           24 :         filter_ = &ws_.emplace<
     585           24 :             deflator_filter>(ctx_, ws_, false);
     586              :     }
     587           51 :     else if(ce.encoding == encoding::gzip)
     588              :     {
     589           25 :         filter_ = &ws_.emplace<
     590           25 :             deflator_filter>(ctx_, ws_, true);
     591              :     }
     592           75 : }
     593              : 
     594              : void
     595            4 : serializer::
     596              : start_empty(
     597              :     message_view_base const& m)
     598              : {
     599              :     using mutable_buffer =
     600              :         buffers::mutable_buffer;
     601              : 
     602            4 :     start_init(m);
     603            4 :     st_ = style::empty;
     604              : 
     605            4 :     if(!is_chunked_)
     606              :     {
     607            3 :         prepped_ = make_array(
     608              :             1); // header
     609              :     }
     610              :     else
     611              :     {
     612            1 :         prepped_ = make_array(
     613              :             1 + // header
     614              :             1); // final chunk
     615              : 
     616              :         mutable_buffer final_chunk = {
     617            1 :             ws_.reserve_front(
     618              :                 final_chunk_len),
     619            1 :             final_chunk_len };
     620            1 :         write_final_chunk(final_chunk);
     621              : 
     622            1 :         prepped_[1] = final_chunk;
     623              :     }
     624              : 
     625            4 :     prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     626            4 : }
     627              : 
     628              : void
     629           24 : serializer::
     630              : start_buffers(
     631              :     message_view_base const& m)
     632              : {
     633              :     using mutable_buffer =
     634              :         buffers::mutable_buffer;
     635              : 
     636              :     // start_init() already called 
     637           24 :     st_ = style::buffers;
     638              : 
     639           24 :     const auto buffers_max = (std::min)(
     640           48 :         std::size_t{ 16 },
     641           24 :         buf_gen_->count());
     642              : 
     643           24 :     if(!filter_)
     644              :     {
     645            7 :         if(!is_chunked_)
     646              :         {
     647              :             // no filter and no chunked
     648              : 
     649            6 :             prepped_ = make_array(
     650              :                 1 +            // header
     651              :                 buffers_max ); // buffers
     652              : 
     653            6 :             prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     654           12 :             std::generate(
     655            6 :                 prepped_.begin() + 1,
     656              :                 prepped_.end(),
     657            6 :                 std::ref(*buf_gen_));
     658            6 :             more_input_ = !buf_gen_->is_empty();
     659            6 :             return;
     660              :         }
     661              : 
     662              :         // no filter and chunked
     663              : 
     664            1 :         if(buf_gen_->is_empty())
     665              :         {
     666            0 :             prepped_ = make_array(
     667              :                 1 + // header
     668              :                 1); // final chunk
     669              : 
     670              :             mutable_buffer final_chunk = {
     671            0 :                 ws_.reserve_front(
     672              :                     final_chunk_len),
     673            0 :                 final_chunk_len };
     674            0 :             write_final_chunk(
     675              :                 final_chunk);
     676              : 
     677            0 :             prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     678            0 :             prepped_[1] = final_chunk;
     679            0 :             return;
     680              :         }
     681              : 
     682              :         // Write entire buffers as a single chunk
     683              :         // since total size is known
     684              : 
     685              :         mutable_buffer chunk_header = {
     686            1 :             ws_.reserve_front(
     687              :                 chunk_header_len),
     688            1 :             chunk_header_len };
     689              : 
     690            1 :         write_chunk_header(
     691              :             chunk_header,
     692            1 :             buf_gen_->size());
     693              : 
     694              :         mutable_buffer crlf_and_final_chunk = {
     695            1 :                 ws_.reserve_front(
     696              :                     crlf_len + final_chunk_len),
     697            1 :                 crlf_len + final_chunk_len };
     698              : 
     699            1 :         write_crlf(
     700            1 :             buffers::prefix(
     701              :                 crlf_and_final_chunk,
     702              :                 crlf_len));
     703              : 
     704            1 :         write_final_chunk(
     705            1 :             buffers::sans_prefix(
     706              :                 crlf_and_final_chunk,
     707              :                 crlf_len));
     708              : 
     709            1 :         prepped_ = make_array(
     710              :             1 + // header
     711              :             1 + // chunk header
     712              :             buffers_max + // buffers
     713              :             1); // buffer or (crlf and final chunk)
     714              : 
     715            1 :         prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     716            1 :         prepped_[1] = chunk_header;
     717            1 :         std::generate(
     718            1 :             prepped_.begin() + 2,
     719            1 :             prepped_.end() - 1,
     720            1 :             std::ref(*buf_gen_));
     721              : 
     722            1 :         more_input_ = !buf_gen_->is_empty();
     723              :         // assigning the last slot
     724            1 :         if(more_input_)
     725              :         {
     726            1 :             prepped_[prepped_.size() - 1] =
     727            2 :                 buf_gen_->operator()();
     728              : 
     729              :             // deferred until buf_gen_ is drained
     730            1 :             tmp_ = crlf_and_final_chunk;
     731              :         }
     732              :         else
     733              :         {
     734            0 :             prepped_[prepped_.size() - 1] =
     735              :                 crlf_and_final_chunk;
     736              :         }
     737            1 :         return;
     738              :     }
     739              : 
     740              :     // filter
     741              : 
     742           17 :     prepped_ = make_array(
     743              :         1 + // header
     744              :         2); // circular buffer
     745              : 
     746           17 :     const auto n = ws_.size() - 1;
     747           17 :     cb0_ = { ws_.reserve_front(n), n };
     748              : 
     749           17 :     if(is_chunked_)
     750              :     {
     751            8 :         if(cb0_.capacity() <= chunked_overhead_)
     752            0 :             detail::throw_length_error();
     753              :     }
     754              :     else
     755              :     {
     756            9 :         if(cb0_.capacity() == 0)
     757            0 :             detail::throw_length_error();
     758              :     }
     759              : 
     760           17 :     prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     761           17 :     more_input_ = !buf_gen_->is_empty();
     762              : }
     763              : 
     764              : void
     765           25 : serializer::
     766              : start_source(
     767              :     message_view_base const& m)
     768              : {
     769              :     // start_init() already called 
     770           25 :     st_ = style::source;
     771              : 
     772           25 :     prepped_ = make_array(
     773              :         1 + // header
     774              :         2); // circular buffer
     775              : 
     776           25 :     if(filter_)
     777              :     {
     778              :         // TODO: Optimize buffer distribution
     779           16 :         const auto n = (ws_.size() - 1) / 2;
     780           16 :         cb0_ = { ws_.reserve_front(n), n };
     781           16 :         cb1_ = { ws_.reserve_front(n), n };
     782              :     }
     783              :     else
     784              :     {
     785            9 :         const auto n = ws_.size() - 1;
     786            9 :         cb0_ = { ws_.reserve_front(n), n };
     787              :     }
     788              : 
     789           25 :     if(is_chunked_)
     790              :     {
     791           10 :         if(cb0_.capacity() <= chunked_overhead_)
     792            0 :             detail::throw_length_error();
     793              :     }
     794              :     else
     795              :     {
     796           15 :         if(cb0_.capacity() == 0)
     797            0 :             detail::throw_length_error();
     798              :     }
     799              : 
     800           25 :     prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     801           25 :     more_input_ = true;
     802           25 : }
     803              : 
     804              : auto
     805           22 : serializer::
     806              : start_stream(
     807              :     message_view_base const& m) ->
     808              :         stream
     809              : {
     810           22 :     start_init(m);
     811           22 :     st_ = style::stream;
     812              : 
     813           22 :     prepped_ = make_array(
     814              :         1 + // header
     815              :         2); // circular buffer
     816              : 
     817           22 :     if(filter_)
     818              :     {
     819              :         // TODO: Optimize buffer distribution
     820           16 :         const auto n = (ws_.size() - 1) / 2;
     821           16 :         cb0_ = { ws_.reserve_front(n), n };
     822           16 :         cb1_ = { ws_.reserve_front(n), n };
     823              :     }
     824              :     else
     825              :     {
     826            6 :         const auto n = ws_.size() - 1;
     827            6 :         cb0_ = { ws_.reserve_front(n), n };
     828              :     }
     829              : 
     830           22 :     if(is_chunked_)
     831              :     {
     832           11 :         if(cb0_.capacity() <= chunked_overhead_)
     833            0 :             detail::throw_length_error();
     834              :     }
     835              :     else
     836              :     {
     837           11 :         if(cb0_.capacity() == 0)
     838            0 :             detail::throw_length_error();
     839              :     }
     840              : 
     841           22 :     prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     842           22 :     more_input_ = true;
     843           22 :     return stream{ *this };
     844              : }
     845              : 
     846              : //------------------------------------------------
     847              : 
     848              : std::size_t
     849           69 : serializer::
     850              : stream::
     851              : capacity() const noexcept
     852              : {
     853           69 :     if(sr_->filter_)
     854            0 :         return sr_->cb1_.capacity();
     855              : 
     856           69 :     if(!sr_->is_chunked_)
     857           34 :         return sr_->cb0_.capacity();
     858              : 
     859              :     // chunked with no filter
     860           35 :     const auto cap = sr_->cb0_.capacity();
     861           35 :     if(cap > chunked_overhead_)
     862           21 :         return cap - chunked_overhead_;
     863              : 
     864           14 :     return 0;
     865              : }
     866              : 
     867              : bool
     868           61 : serializer::
     869              : stream::
     870              : is_full() const noexcept
     871              : {
     872           61 :     return capacity() == 0;
     873              : }
     874              : 
     875              : auto
     876         5511 : serializer::
     877              : stream::
     878              : prepare() const ->
     879              :     buffers_type
     880              : {
     881         5511 :     if(sr_->filter_)
     882         5480 :         return sr_->cb1_.prepare(
     883        10960 :             sr_->cb1_.capacity());
     884              : 
     885           31 :     if(!sr_->is_chunked_)
     886           15 :         return sr_->cb0_.prepare(
     887           30 :             sr_->cb0_.capacity());
     888              : 
     889              :     // chunked with no filter
     890           16 :     const auto cap = sr_->cb0_.capacity();
     891           16 :     if(cap <= chunked_overhead_)
     892            0 :         detail::throw_length_error();
     893              : 
     894           16 :     return buffers::sans_prefix(
     895           32 :         sr_->cb0_.prepare(
     896              :             cap - crlf_len - final_chunk_len),
     897           16 :         chunk_header_len);
     898              : }
     899              : 
     900              : void
     901         5511 : serializer::
     902              : stream::
     903              : commit(std::size_t n) const
     904              : {
     905         5511 :     if(sr_->filter_)
     906         5480 :         return sr_->cb1_.commit(n);
     907              : 
     908           31 :     if(!sr_->is_chunked_)
     909           15 :         return sr_->cb0_.commit(n);
     910              : 
     911              :     // chunked with no filter
     912           16 :     if(n != 0)
     913              :     {
     914           15 :         write_chunk_header(
     915           15 :             sr_->cb0_.prepare(
     916              :                 chunk_header_len),
     917              :             n);
     918           15 :         sr_->cb0_.commit(
     919              :             chunk_header_len);
     920              : 
     921           15 :         sr_->cb0_.prepare(n);
     922           15 :         sr_->cb0_.commit(n);
     923              : 
     924           15 :         write_crlf(
     925           15 :             sr_->cb0_.prepare(crlf_len));
     926           15 :         sr_->cb0_.commit(crlf_len);
     927              :     }
     928              : }
     929              : 
     930              : void
     931           25 : serializer::
     932              : stream::
     933              : close() const
     934              : {
     935              :     // Precondition violation
     936           25 :     if(!sr_->more_input_)
     937            4 :         detail::throw_logic_error();
     938              : 
     939           21 :     sr_->more_input_ = false;
     940              : 
     941           21 :     if(sr_->filter_)
     942           16 :         return;
     943              : 
     944            5 :     if(!sr_->is_chunked_)
     945            2 :         return;
     946              : 
     947              :     // chunked with no filter
     948            3 :     write_final_chunk(
     949            3 :         sr_->cb0_.prepare(
     950              :             final_chunk_len));
     951            3 :     sr_->cb0_.commit(final_chunk_len);
     952              : }
     953              : 
     954              : //------------------------------------------------
     955              : 
     956              : } // http_proto
     957              : } // boost
        

Generated by: LCOV version 2.1