LCOV - code coverage report
Current view: top level - libs/http_proto/src/serializer.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 91.3 % 437 399
Test Date: 2025-06-18 09:40:26 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           73 :     deflator_filter(
      41              :         context& ctx,
      42              :         http_proto::detail::workspace& ws,
      43              :         bool use_gzip)
      44          292 :         : deflator_{ ctx.get_service<zlib::service>()
      45           73 :             .make_deflator(ws, -1, use_gzip ? 31 : 15, 8) }
      46              :     {
      47           73 :     }
      48              : 
      49              :     virtual filter::results
      50        10525 :     on_process(
      51              :         buffers::mutable_buffer out,
      52              :         buffers::const_buffer in,
      53              :         bool more) override
      54              :     {
      55        10525 :         auto flush =
      56        10525 :             more ? zlib::flush::none : zlib::flush::finish;
      57        10525 :         filter::results results;
      58              : 
      59              :         for(;;)
      60              :         {
      61        17509 :             auto params = zlib::params{in.data(), in.size(),
      62        17509 :                 out.data(), out.size() };
      63        17509 :             auto ec = deflator_.write(params, flush);
      64              : 
      65        17509 :             results.in_bytes  += in.size() - params.avail_in;
      66        17509 :             results.out_bytes += out.size() - params.avail_out;
      67              : 
      68        17510 :             if( ec.failed() &&
      69        17510 :                 ec != zlib::error::buf_err)
      70              :             {
      71            1 :                 results.ec = ec;
      72        10525 :                 return results;
      73              :             }
      74              : 
      75        17508 :             if( ec == zlib::error::stream_end )
      76              :             {
      77           72 :                 results.finished = true;
      78           72 :                 return results;
      79              :             }
      80              : 
      81        17436 :             in  = buffers::suffix(in, params.avail_in);
      82        17436 :             out = buffers::suffix(out, params.avail_out);
      83              : 
      84        17436 :             if( out.size() == 0 )
      85         3404 :                 return results;
      86              : 
      87        14032 :             if( in.size() == 0 )
      88              :             {
      89        14032 :                 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         6984 :                     flush = zlib::flush::block;
      96         6984 :                     continue;
      97              :                 }
      98         7048 :                 return results;
      99              :             }
     100         6984 :         }
     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         4519 : 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         4519 :     auto p = buf + 16;
     135        76823 :     for(std::size_t i = 16; i--;)
     136              :     {
     137        72304 :         *--p = hexdig[size & 0xf];
     138        72304 :         size >>= 4;
     139              :     }
     140         4519 :     buf[16] = '\r';
     141         4519 :     buf[17] = '\n';
     142         4519 :     auto n = buffers::copy(
     143              :         mbs,
     144         9038 :         buffers::const_buffer(
     145              :             buf, sizeof(buf)));
     146              :     ignore_unused(n);
     147         4519 :     BOOST_ASSERT(n == 18);
     148         4519 : }
     149              : 
     150              : template<class MutableBufferSequence>
     151              : void
     152         4519 : write_crlf(
     153              :     const MutableBufferSequence& mbs) noexcept
     154              : {
     155         4519 :     auto n = buffers::copy(
     156              :         mbs,
     157         9038 :         buffers::const_buffer(
     158              :             "\r\n", 2));
     159              :     ignore_unused(n);
     160         4519 :     BOOST_ASSERT(n == 2);
     161         4519 : }
     162              : 
     163              : template<class MutableBufferSequence>
     164              : void
     165           43 : write_final_chunk(
     166              :     const MutableBufferSequence& mbs) noexcept
     167              : {
     168           43 :     auto n = buffers::copy(
     169              :         mbs,
     170           86 :         buffers::const_buffer(
     171              :             "0\r\n\r\n", 5));
     172              :     ignore_unused(n);
     173           43 :     BOOST_ASSERT(n == 5);
     174           43 : }
     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         8975 :     appender(
     188              :         buffers::circular_buffer& cb,
     189              :         bool is_chunked)
     190         8975 :         : cb_(cb)
     191         8975 :         , mbp_(cb.prepare(cb.capacity()))
     192         8975 :         , is_chunked_(is_chunked)
     193              :     {
     194         8975 :     }
     195              : 
     196              :     bool
     197        13979 :     is_full() const noexcept
     198              :     {
     199        13979 :         auto remaining = cb_.capacity() - n_;
     200        13979 :         if(is_chunked_)
     201         7024 :             return remaining <= chunked_overhead_;
     202              : 
     203         6955 :         return remaining == 0;
     204              :     }
     205              : 
     206              :     buffers::mutable_buffer_pair
     207        10527 :     prepare() noexcept
     208              :     {
     209        10527 :         if(is_chunked_)
     210              :         {
     211         5280 :             return buffers::sans_suffix(
     212         5280 :                 buffers::sans_prefix(
     213         5280 :                     mbp_,
     214         5280 :                     chunk_header_len + n_)
     215         5280 :                 , final_chunk_len + crlf_len);
     216              :         }
     217         5247 :         return buffers::sans_prefix(mbp_, n_);
     218              :     }
     219              : 
     220              :     void
     221        10525 :     commit(std::size_t n, bool more) noexcept
     222              :     {
     223        10525 :         BOOST_ASSERT(more_input_);
     224        10525 :         n_ += n;
     225        10525 :         more_input_ = more;
     226        10525 :     }
     227              : 
     228         8975 :     ~appender()
     229              :     {
     230         8975 :         if(is_chunked_)
     231              :         {
     232         4504 :             if(n_)
     233              :             {
     234         4503 :                 write_chunk_header(mbp_, n_);
     235         4503 :                 cb_.commit(n_ + chunk_header_len);
     236              : 
     237         4503 :                 write_crlf(
     238         4503 :                     cb_.prepare(crlf_len));
     239         4503 :                 cb_.commit(crlf_len);
     240              :             }
     241              : 
     242         4504 :             if(!more_input_)
     243              :             {
     244           38 :                 write_final_chunk(
     245           38 :                     cb_.prepare(final_chunk_len));
     246           38 :                 cb_.commit(final_chunk_len);
     247              :             }
     248              :         }
     249              :         else // is_chunked_ == false
     250              :         {
     251         4471 :             cb_.commit(n_);
     252              :         }
     253         8975 :     }
     254              : };
     255              : 
     256              : } // namespace
     257              : 
     258              : //------------------------------------------------
     259              : 
     260           57 : serializer::
     261              : ~serializer()
     262              : {
     263           57 : }
     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           57 : serializer::
     277              : serializer(
     278              :     context& ctx,
     279           57 :     std::size_t buffer_size)
     280           57 :     : ctx_(ctx)
     281           57 :     , ws_(buffer_size)
     282              : {
     283           57 : }
     284              : 
     285              : void
     286          179 : serializer::
     287              : reset() noexcept
     288              : {
     289          179 :     ws_.clear();
     290          179 :     filter_ = nullptr;
     291          179 :     is_done_ = false;
     292          179 :     is_header_done_ = false;
     293          179 :     filter_done_ = false;
     294          179 : }
     295              : 
     296              : //------------------------------------------------
     297              : 
     298              : auto
     299         9042 : serializer::
     300              : prepare() ->
     301              :     system::result<const_buffers_type>
     302              : {
     303              :     // Precondition violation
     304         9042 :     if(is_done_)
     305            1 :         detail::throw_logic_error();
     306              : 
     307              :     // Expect: 100-continue
     308         9041 :     if(needs_exp100_continue_)
     309              :     {
     310            4 :         if(!is_header_done_)
     311            4 :             return const_buffers_type(
     312            2 :                 prepped_.begin(),
     313            2 :                 1); // limit to header
     314              : 
     315            2 :         needs_exp100_continue_ = false;
     316              : 
     317            2 :         BOOST_HTTP_PROTO_RETURN_EC(
     318              :             error::expect_100_continue);
     319              :     }
     320              : 
     321         9037 :     if(!filter_)
     322              :     {
     323           80 :         switch(st_)
     324              :         {
     325            3 :         case style::empty:
     326            6 :             return const_buffers_type(
     327            3 :                 prepped_.begin(),
     328            6 :                 prepped_.size());
     329              : 
     330           19 :         case style::buffers:
     331              :             // add more buffers if prepped_ is half empty.
     332           29 :             if(more_input_ &&
     333           10 :                 prepped_.capacity() >= prepped_.size())
     334              :             {
     335            2 :                 prepped_.slide_to_front();
     336           15 :                 while(prepped_.capacity() != 0)
     337              :                 {
     338           15 :                     auto buf = buf_gen_->next();
     339           15 :                     if(buf.size() != 0)
     340              :                     {
     341           13 :                         prepped_.append(buf);
     342              :                     }
     343              :                     else // buf_gen_ is empty
     344              :                     {
     345              :                         // append crlf and final chunk
     346            2 :                         if(is_chunked_)
     347              :                         {
     348            1 :                             prepped_.append(tmp_);
     349            1 :                             more_input_ = false;
     350              :                         }
     351            2 :                         break;
     352              :                     }
     353              :                 }
     354            2 :                 if(buf_gen_->is_empty() && !is_chunked_)
     355            1 :                     more_input_ = false;
     356              :             }
     357           38 :             return const_buffers_type(
     358           19 :                 prepped_.begin(),
     359           38 :                 prepped_.size());
     360              : 
     361           23 :         case style::source:
     362              :         {
     363           23 :             if(!more_input_)
     364           22 :                 break;
     365              : 
     366              :             // handles chunked payloads automatically
     367           18 :             appender apndr(cb0_, is_chunked_);
     368              : 
     369           18 :             if(apndr.is_full())
     370            0 :                 break;
     371              : 
     372           18 :             const auto rs = source_->read(
     373           18 :                 apndr.prepare());
     374              : 
     375           18 :             if(rs.ec.failed())
     376              :             {
     377            1 :                 is_done_ = true;
     378            1 :                 return rs.ec;
     379              :             }
     380              : 
     381           17 :             if(rs.finished)
     382            6 :                 more_input_ = false;
     383              : 
     384           17 :             apndr.commit(rs.bytes, more_input_);
     385           17 :             break;
     386           18 :         }
     387              : 
     388           35 :         case style::stream:
     389           35 :             if(is_header_done_ && cb0_.size() == 0)
     390            1 :                 BOOST_HTTP_PROTO_RETURN_EC(
     391              :                     error::need_data);
     392           34 :             break;
     393              :         }
     394              :     }
     395              :     else // filter
     396              :     {
     397         8957 :         switch(st_)
     398              :         {
     399            0 :         case style::empty:
     400            0 :             return const_buffers_type(
     401            0 :                 prepped_.begin(),
     402            0 :                 prepped_.size());
     403              : 
     404         1153 :         case style::buffers:
     405              :         {
     406         1153 :             appender apndr(cb0_, is_chunked_);
     407         2521 :             while(!apndr.is_full() && !filter_done_)
     408              :             {
     409         1369 :                 if(more_input_ && tmp_.size() == 0)
     410              :                 {
     411          401 :                     tmp_ = buf_gen_->next();
     412          401 :                     if(tmp_.size() == 0) // buf_gen_ is empty
     413           16 :                         more_input_ = false;
     414              :                 }
     415              : 
     416         1369 :                 const auto rs = filter_->process(
     417            0 :                     apndr.prepare(),
     418         1369 :                     tmp_,
     419         1369 :                     more_input_);
     420              : 
     421         1369 :                 if(rs.ec.failed())
     422              :                 {
     423            1 :                     is_done_ = true;
     424            1 :                     return rs.ec;
     425              :                 }
     426              : 
     427         1368 :                 tmp_ = buffers::sans_prefix(tmp_, rs.in_bytes);
     428         1368 :                 apndr.commit(rs.out_bytes, !rs.finished);
     429              : 
     430         1368 :                 if(rs.finished)
     431           24 :                     filter_done_ = true;
     432              :             }
     433         1152 :             break;
     434         1153 :         }
     435              : 
     436         2300 :         case style::source:
     437              :         {
     438         2300 :             appender apndr(cb0_, is_chunked_);
     439         5936 :             while(!apndr.is_full() && !filter_done_)
     440              :             {
     441         3636 :                 if(more_input_ && cb1_.capacity() != 0)
     442              :                 {
     443         2696 :                     const auto rs = source_->read(
     444         2696 :                         cb1_.prepare(cb1_.capacity()));
     445         2696 :                     if(rs.ec.failed())
     446              :                     {
     447            0 :                         is_done_ = true;
     448            0 :                         return rs.ec;
     449              :                     }
     450         2696 :                     if(rs.finished)
     451           24 :                         more_input_ = false;
     452         2696 :                     cb1_.commit(rs.bytes);
     453              :                 }
     454              : 
     455         3636 :                 const auto rs = filter_->process(
     456         3636 :                     apndr.prepare(),
     457            0 :                     cb1_.data(),
     458         3636 :                     more_input_);
     459              : 
     460         3636 :                 if(rs.ec.failed())
     461              :                 {
     462            0 :                     is_done_ = true;
     463            0 :                     return rs.ec;
     464              :                 }
     465              : 
     466         3636 :                 cb1_.consume(rs.in_bytes);
     467         3636 :                 apndr.commit(rs.out_bytes, !rs.finished);
     468              : 
     469         3636 :                 if(rs.finished)
     470           24 :                     filter_done_ = true;
     471              :             }
     472         2300 :             break;
     473         2300 :         }
     474              : 
     475         5504 :         case style::stream:
     476              :         {
     477         5504 :             appender apndr(cb0_, is_chunked_);
     478              : 
     479         5504 :             if(apndr.is_full() || filter_done_)
     480            0 :                 break;
     481              : 
     482              :             // The stream object is expected to
     483              :             // have already populated cb1_
     484         5504 :             if(more_input_ && cb1_.size() == 0)
     485              :             {
     486            0 :                 if(!prepped_.empty())
     487            0 :                     break;
     488              : 
     489            0 :                 BOOST_HTTP_PROTO_RETURN_EC(
     490              :                     error::need_data);
     491              :             }
     492              : 
     493         5504 :             const auto rs = filter_->process(
     494         5504 :                 apndr.prepare(),
     495            0 :                 cb1_.data(),
     496         5504 :                 more_input_);
     497              : 
     498         5504 :             if(rs.ec.failed())
     499              :             {
     500            0 :                 is_done_ = true;
     501            0 :                 return rs.ec;
     502              :             }
     503              : 
     504         5504 :             cb1_.consume(rs.in_bytes);
     505         5504 :             apndr.commit(rs.out_bytes, !rs.finished);
     506              : 
     507         5504 :             if(rs.finished)
     508           24 :                 filter_done_ = true;
     509              : 
     510         5504 :             break;
     511         5504 :         }
     512              :         }
     513              :     }
     514              : 
     515         9012 :     prepped_.reset(!is_header_done_);
     516         9012 :     const auto cbp = cb0_.data();
     517         9012 :     if(cbp[0].size() != 0)
     518         9009 :         prepped_.append(cbp[0]);
     519         9012 :     if(cbp[1].size() != 0)
     520           14 :         prepped_.append(cbp[1]);
     521              : 
     522         9012 :     BOOST_ASSERT(
     523              :         buffers::size(prepped_) > 0);
     524              : 
     525        18024 :     return const_buffers_type(
     526         9012 :         prepped_.begin(),
     527        18024 :         prepped_.size());
     528              : }
     529              : 
     530              : void
     531        10775 : serializer::
     532              : consume(
     533              :     std::size_t n)
     534              : {
     535              :     // Precondition violation
     536        10775 :     if(is_done_ && n != 0)
     537            1 :         detail::throw_logic_error();
     538              : 
     539        10774 :     if(!is_header_done_)
     540              :     {
     541              :         const auto header_remain =
     542          100 :             prepped_[0].size();
     543          100 :         if(n < header_remain)
     544              :         {
     545           11 :             prepped_.consume(n);
     546           11 :             return;
     547              :         }
     548           89 :         n -= header_remain;
     549           89 :         prepped_.consume(header_remain);
     550           89 :         is_header_done_ = true;
     551              :     }
     552              : 
     553        10763 :     prepped_.consume(n);
     554              : 
     555              :     // no-op when cb0_ is not in use
     556        10763 :     cb0_.consume(n);
     557              : 
     558        10763 :     if(!prepped_.empty())
     559         1760 :         return;
     560              : 
     561         9003 :     if(needs_exp100_continue_)
     562            1 :         return;
     563              : 
     564         9002 :     if(more_input_)
     565         8897 :         return;
     566              : 
     567          105 :     if(filter_ && !filter_done_)
     568           16 :         return;
     569              : 
     570           89 :     is_done_ = true;
     571              : }
     572              : 
     573              : //------------------------------------------------
     574              : 
     575              : detail::array_of_const_buffers
     576           99 : serializer::
     577              : make_array(std::size_t n)
     578              : {
     579           99 :     if(n > std::numeric_limits<std::uint16_t>::max())
     580            0 :         detail::throw_length_error();
     581              : 
     582              :     return {
     583           99 :         ws_.push_array(n,
     584            0 :             buffers::const_buffer{}),
     585           99 :         static_cast<std::uint16_t>(n) };
     586              : }
     587              : 
     588              : void
     589           99 : serializer::
     590              : start_init(
     591              :     message_view_base const& m)
     592              : {
     593           99 :     reset();
     594              : 
     595              :     // VFALCO what do we do with
     596              :     // metadata error code failures?
     597              :     // m.ph_->md.maybe_throw();
     598              : 
     599           99 :     auto const& md = m.metadata();
     600           99 :     needs_exp100_continue_ = md.expect.is_100_continue;
     601              : 
     602              :     // Transfer-Encoding
     603           99 :     is_chunked_ = md.transfer_encoding.is_chunked;
     604              : 
     605              :     // Content-Encoding
     606           99 :     auto const& ce = md.content_encoding;
     607           99 :     if(ce.encoding == encoding::deflate)
     608              :     {
     609           36 :         filter_ = &ws_.emplace<
     610           36 :             deflator_filter>(ctx_, ws_, false);
     611              :     }
     612           63 :     else if(ce.encoding == encoding::gzip)
     613              :     {
     614           37 :         filter_ = &ws_.emplace<
     615           37 :             deflator_filter>(ctx_, ws_, true);
     616              :     }
     617           99 : }
     618              : 
     619              : void
     620            4 : serializer::
     621              : start_empty(
     622              :     message_view_base const& m)
     623              : {
     624              :     using mutable_buffer =
     625              :         buffers::mutable_buffer;
     626              : 
     627            4 :     start_init(m);
     628            4 :     st_ = style::empty;
     629              : 
     630            4 :     if(!is_chunked_)
     631              :     {
     632            3 :         prepped_ = make_array(
     633              :             1); // header
     634              :     }
     635              :     else
     636              :     {
     637            1 :         prepped_ = make_array(
     638              :             1 + // header
     639              :             1); // final chunk
     640              : 
     641              :         mutable_buffer final_chunk = {
     642            1 :             ws_.reserve_front(
     643              :                 final_chunk_len),
     644            1 :             final_chunk_len };
     645            1 :         write_final_chunk(final_chunk);
     646              : 
     647            1 :         prepped_[1] = final_chunk;
     648              :     }
     649              : 
     650            4 :     prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     651            4 :     more_input_ = false;
     652            4 : }
     653              : 
     654              : void
     655           32 : serializer::
     656              : start_buffers(
     657              :     message_view_base const& m)
     658              : {
     659              :     using mutable_buffer =
     660              :         buffers::mutable_buffer;
     661              : 
     662              :     // start_init() already called 
     663           32 :     st_ = style::buffers;
     664              : 
     665           32 :     const auto buffers_max = (std::min)(
     666           64 :         std::size_t{ 16 },
     667           32 :         buf_gen_->count());
     668              : 
     669           32 :     if(!filter_)
     670              :     {
     671            7 :         if(!is_chunked_)
     672              :         {
     673              :             // no filter and no chunked
     674              : 
     675            6 :             prepped_ = make_array(
     676              :                 1 +            // header
     677              :                 buffers_max ); // buffers
     678              : 
     679            6 :             prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     680           12 :             std::generate(
     681            6 :                 prepped_.begin() + 1,
     682              :                 prepped_.end(),
     683           16 :                 [this](){ return buf_gen_->next(); });
     684            6 :             more_input_ = !buf_gen_->is_empty();
     685            6 :             return;
     686              :         }
     687              : 
     688              :         // no filter and chunked
     689              : 
     690            1 :         if(buf_gen_->is_empty())
     691              :         {
     692            0 :             prepped_ = make_array(
     693              :                 1 + // header
     694              :                 1); // final chunk
     695              : 
     696              :             mutable_buffer final_chunk = {
     697            0 :                 ws_.reserve_front(
     698              :                     final_chunk_len),
     699            0 :                 final_chunk_len };
     700            0 :             write_final_chunk(
     701              :                 final_chunk);
     702              : 
     703            0 :             prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     704            0 :             prepped_[1] = final_chunk;
     705            0 :             more_input_ = false;
     706            0 :             return;
     707              :         }
     708              : 
     709              :         // Write entire buffers as a single chunk
     710              :         // since total size is known
     711              : 
     712              :         mutable_buffer chunk_header = {
     713            1 :             ws_.reserve_front(
     714              :                 chunk_header_len),
     715            1 :             chunk_header_len };
     716              : 
     717            1 :         write_chunk_header(
     718              :             chunk_header,
     719            1 :             buf_gen_->size());
     720              : 
     721              :         mutable_buffer crlf_and_final_chunk = {
     722            1 :                 ws_.reserve_front(
     723              :                     crlf_len + final_chunk_len),
     724            1 :                 crlf_len + final_chunk_len };
     725              : 
     726            1 :         write_crlf(
     727            1 :             buffers::prefix(
     728              :                 crlf_and_final_chunk,
     729              :                 crlf_len));
     730              : 
     731            1 :         write_final_chunk(
     732            1 :             buffers::sans_prefix(
     733              :                 crlf_and_final_chunk,
     734              :                 crlf_len));
     735              : 
     736            1 :         prepped_ = make_array(
     737              :             1 + // header
     738              :             1 + // chunk header
     739              :             buffers_max + // buffers
     740              :             1); // buffer or (crlf and final chunk)
     741              : 
     742            1 :         prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     743            1 :         prepped_[1] = chunk_header;
     744            1 :         std::generate(
     745            1 :             prepped_.begin() + 2,
     746            1 :             prepped_.end() - 1,
     747           16 :             [this](){ return buf_gen_->next(); });
     748              : 
     749            1 :         more_input_ = !buf_gen_->is_empty();
     750              :         // assigning the last slot
     751            1 :         if(more_input_)
     752              :         {
     753            1 :             prepped_[prepped_.size() - 1] =
     754            2 :                 buf_gen_->next();
     755              : 
     756              :             // deferred until buf_gen_ is drained
     757            1 :             tmp_ = crlf_and_final_chunk;
     758              :         }
     759              :         else
     760              :         {
     761            0 :             prepped_[prepped_.size() - 1] =
     762              :                 crlf_and_final_chunk;
     763              :         }
     764            1 :         return;
     765              :     }
     766              : 
     767              :     // filter
     768              : 
     769           25 :     prepped_ = make_array(
     770              :         1 + // header
     771              :         2); // circular buffer
     772              : 
     773           25 :     const auto n = ws_.size() - 1;
     774           25 :     cb0_ = { ws_.reserve_front(n), n };
     775              : 
     776           25 :     if(is_chunked_)
     777              :     {
     778           12 :         if(cb0_.capacity() <= chunked_overhead_)
     779            0 :             detail::throw_length_error();
     780              :     }
     781              :     else
     782              :     {
     783           13 :         if(cb0_.capacity() == 0)
     784            0 :             detail::throw_length_error();
     785              :     }
     786              : 
     787           25 :     prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     788           25 :     tmp_ = {};
     789           25 :     more_input_ = !buf_gen_->is_empty();
     790              : }
     791              : 
     792              : void
     793           33 : serializer::
     794              : start_source(
     795              :     message_view_base const& m)
     796              : {
     797              :     // start_init() already called 
     798           33 :     st_ = style::source;
     799              : 
     800           33 :     prepped_ = make_array(
     801              :         1 + // header
     802              :         2); // circular buffer
     803              : 
     804           33 :     if(filter_)
     805              :     {
     806              :         // TODO: Optimize buffer distribution
     807           24 :         const auto n = (ws_.size() - 1) / 2;
     808           24 :         cb0_ = { ws_.reserve_front(n), n };
     809           24 :         cb1_ = { ws_.reserve_front(n), n };
     810              :     }
     811              :     else
     812              :     {
     813            9 :         const auto n = ws_.size() - 1;
     814            9 :         cb0_ = { ws_.reserve_front(n), n };
     815              :     }
     816              : 
     817           33 :     if(is_chunked_)
     818              :     {
     819           14 :         if(cb0_.capacity() <= chunked_overhead_)
     820            0 :             detail::throw_length_error();
     821              :     }
     822              :     else
     823              :     {
     824           19 :         if(cb0_.capacity() == 0)
     825            0 :             detail::throw_length_error();
     826              :     }
     827              : 
     828           33 :     prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     829           33 :     more_input_ = true;
     830           33 : }
     831              : 
     832              : auto
     833           30 : serializer::
     834              : start_stream(
     835              :     message_view_base const& m) ->
     836              :         stream
     837              : {
     838           30 :     start_init(m);
     839           30 :     st_ = style::stream;
     840              : 
     841           30 :     prepped_ = make_array(
     842              :         1 + // header
     843              :         2); // circular buffer
     844              : 
     845           30 :     if(filter_)
     846              :     {
     847              :         // TODO: Optimize buffer distribution
     848           24 :         const auto n = (ws_.size() - 1) / 2;
     849           24 :         cb0_ = { ws_.reserve_front(n), n };
     850           24 :         cb1_ = { ws_.reserve_front(n), n };
     851              :     }
     852              :     else
     853              :     {
     854            6 :         const auto n = ws_.size() - 1;
     855            6 :         cb0_ = { ws_.reserve_front(n), n };
     856              :     }
     857              : 
     858           30 :     if(is_chunked_)
     859              :     {
     860           15 :         if(cb0_.capacity() <= chunked_overhead_)
     861            0 :             detail::throw_length_error();
     862              :     }
     863              :     else
     864              :     {
     865           15 :         if(cb0_.capacity() == 0)
     866            0 :             detail::throw_length_error();
     867              :     }
     868              : 
     869           30 :     prepped_[0] = { m.ph_->cbuf, m.ph_->size };
     870           30 :     more_input_ = true;
     871           30 :     return stream{ *this };
     872              : }
     873              : 
     874              : //------------------------------------------------
     875              : 
     876              : std::size_t
     877           69 : serializer::
     878              : stream::
     879              : capacity() const noexcept
     880              : {
     881           69 :     if(sr_->filter_)
     882            0 :         return sr_->cb1_.capacity();
     883              : 
     884           69 :     if(!sr_->is_chunked_)
     885           34 :         return sr_->cb0_.capacity();
     886              : 
     887              :     // chunked with no filter
     888           35 :     const auto cap = sr_->cb0_.capacity();
     889           35 :     if(cap > chunked_overhead_)
     890           21 :         return cap - chunked_overhead_;
     891              : 
     892           14 :     return 0;
     893              : }
     894              : 
     895              : bool
     896           61 : serializer::
     897              : stream::
     898              : is_full() const noexcept
     899              : {
     900           61 :     return capacity() == 0;
     901              : }
     902              : 
     903              : auto
     904         5511 : serializer::
     905              : stream::
     906              : prepare() const ->
     907              :     buffers_type
     908              : {
     909         5511 :     if(sr_->filter_)
     910         5480 :         return sr_->cb1_.prepare(
     911        10960 :             sr_->cb1_.capacity());
     912              : 
     913           31 :     if(!sr_->is_chunked_)
     914           15 :         return sr_->cb0_.prepare(
     915           30 :             sr_->cb0_.capacity());
     916              : 
     917              :     // chunked with no filter
     918           16 :     const auto cap = sr_->cb0_.capacity();
     919           16 :     if(cap <= chunked_overhead_)
     920            0 :         detail::throw_length_error();
     921              : 
     922           16 :     return buffers::sans_prefix(
     923           32 :         sr_->cb0_.prepare(
     924              :             cap - crlf_len - final_chunk_len),
     925           16 :         chunk_header_len);
     926              : }
     927              : 
     928              : void
     929         5511 : serializer::
     930              : stream::
     931              : commit(std::size_t n) const
     932              : {
     933         5511 :     if(sr_->filter_)
     934         5480 :         return sr_->cb1_.commit(n);
     935              : 
     936           31 :     if(!sr_->is_chunked_)
     937           15 :         return sr_->cb0_.commit(n);
     938              : 
     939              :     // chunked with no filter
     940           16 :     if(n != 0)
     941              :     {
     942           15 :         write_chunk_header(
     943           15 :             sr_->cb0_.prepare(
     944              :                 chunk_header_len),
     945              :             n);
     946           15 :         sr_->cb0_.commit(
     947              :             chunk_header_len);
     948              : 
     949           15 :         sr_->cb0_.prepare(n);
     950           15 :         sr_->cb0_.commit(n);
     951              : 
     952           15 :         write_crlf(
     953           15 :             sr_->cb0_.prepare(crlf_len));
     954           15 :         sr_->cb0_.commit(crlf_len);
     955              :     }
     956              : }
     957              : 
     958              : void
     959           33 : serializer::
     960              : stream::
     961              : close() const
     962              : {
     963              :     // Precondition violation
     964           33 :     if(!sr_->more_input_)
     965            4 :         detail::throw_logic_error();
     966              : 
     967           29 :     sr_->more_input_ = false;
     968              : 
     969           29 :     if(sr_->filter_)
     970           24 :         return;
     971              : 
     972            5 :     if(!sr_->is_chunked_)
     973            2 :         return;
     974              : 
     975              :     // chunked with no filter
     976            3 :     write_final_chunk(
     977            3 :         sr_->cb0_.prepare(
     978              :             final_chunk_len));
     979            3 :     sr_->cb0_.commit(final_chunk_len);
     980              : }
     981              : 
     982              : //------------------------------------------------
     983              : 
     984              : } // http_proto
     985              : } // boost
        

Generated by: LCOV version 2.1