LCOV - code coverage report
Current view: top level - libs/http_proto/src/serializer.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 92.3 % 377 348
Test Date: 2025-01-06 18:34:48 Functions: 92.9 % 28 26

            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 "detail/filter.hpp"
      18              : 
      19              : #include <boost/buffers/algorithm.hpp>
      20              : #include <boost/buffers/buffer_copy.hpp>
      21              : #include <boost/buffers/buffer_size.hpp>
      22              : #include <boost/core/ignore_unused.hpp>
      23              : 
      24              : #include <stddef.h>
      25              : 
      26              : namespace boost {
      27              : namespace http_proto {
      28              : 
      29              : namespace {
      30              : class deflator_filter
      31              :     : public http_proto::detail::filter
      32              : {
      33              :     zlib::stream& deflator_;
      34              : 
      35              : public:
      36           49 :     deflator_filter(
      37              :         context& ctx,
      38              :         http_proto::detail::workspace& ws,
      39              :         bool use_gzip)
      40          196 :         : deflator_{ ctx.get_service<zlib::service>()
      41           49 :             .make_deflator(ws, -1, use_gzip ? 31 : 15, 8) }
      42              :     {
      43           49 :     }
      44              : 
      45              :     virtual filter::results
      46        23641 :     on_process(
      47              :         buffers::mutable_buffer out,
      48              :         buffers::const_buffer in,
      49              :         bool more) override
      50              :     {
      51        23641 :         auto flush =
      52        23641 :             more ? zlib::flush::none : zlib::flush::finish;
      53        23641 :         filter::results results;
      54              : 
      55              :         for(;;)
      56              :         {
      57        45713 :             auto params = zlib::params{in.data(), in.size(),
      58        45713 :                 out.data(), out.size() };
      59        45713 :             auto ec = deflator_.write(params, flush);
      60              : 
      61        45713 :             results.in_bytes  += in.size() - params.avail_in;
      62        45713 :             results.out_bytes += out.size() - params.avail_out;
      63              : 
      64        56682 :             if( ec.failed() &&
      65        56682 :                 ec != zlib::error::buf_err)
      66              :             {
      67            1 :                 results.ec = ec;
      68        23641 :                 return results;
      69              :             }
      70              : 
      71        45712 :             if( ec == zlib::error::stream_end )
      72              :             {
      73           96 :                 results.finished = true;
      74           96 :                 return results;
      75              :             }
      76              : 
      77        45616 :             in  = buffers::suffix(in, params.avail_in);
      78        45616 :             out = buffers::suffix(out, params.avail_out);
      79              : 
      80        45616 :             if( out.size() == 0 )
      81         1416 :                 return results;
      82              : 
      83        44200 :             if( in.size() == 0 )
      84              :             {
      85        44200 :                 if( results.out_bytes == 0 &&
      86              :                     flush == zlib::flush::none )
      87              :                 {
      88              :                     // TODO: Is flush::block the right choice?
      89              :                     // We might need a filter::flush() interface
      90              :                     // so that the caller can decide when to flush.
      91        22072 :                     flush = zlib::flush::block;
      92        22072 :                     continue;
      93              :                 }
      94        22128 :                 return results;
      95              :             }
      96        22072 :         }
      97              :     }
      98              : };
      99              : } // namespace
     100              : 
     101              : void
     102            0 : consume_buffers(
     103              :     buffers::const_buffer*& p,
     104              :     std::size_t& n,
     105              :     std::size_t bytes)
     106              : {
     107            0 :     while(n > 0)
     108              :     {
     109            0 :         if(bytes < p->size())
     110              :         {
     111            0 :             *p += bytes;
     112            0 :             return;
     113              :         }
     114            0 :         bytes -= p->size();
     115            0 :         ++p;
     116            0 :         --n;
     117              :     }
     118              : 
     119              :     // Precondition violation
     120            0 :     if(bytes > 0)
     121            0 :         detail::throw_invalid_argument();
     122              : }
     123              : 
     124              : template<class MutableBuffers>
     125              : void
     126         6240 : write_chunk_header(
     127              :     MutableBuffers const& dest0,
     128              :     std::size_t size) noexcept
     129              : {
     130              :     static constexpr char hexdig[] =
     131              :         "0123456789ABCDEF";
     132              :     char buf[18];
     133         6240 :     auto p = buf + 16;
     134       106080 :     for(std::size_t i = 16; i--;)
     135              :     {
     136        99840 :         *--p = hexdig[size & 0xf];
     137        99840 :         size >>= 4;
     138              :     }
     139         6240 :     buf[16] = '\r';
     140         6240 :     buf[17] = '\n';
     141         6240 :     auto n = buffers::buffer_copy(
     142              :         dest0,
     143        12480 :         buffers::const_buffer(
     144              :             buf, sizeof(buf)));
     145              :     ignore_unused(n);
     146         6240 :     BOOST_ASSERT(n == 18);
     147         6240 :     BOOST_ASSERT(
     148              :         buffers::buffer_size(dest0) == n);
     149         6240 : }
     150              : 
     151              : template<class DynamicBuffer>
     152              : void
     153              : write_chunk_close(DynamicBuffer& db)
     154              : {
     155              :     db.commit(
     156              :         buffers::buffer_copy(
     157              :             db.prepare(2),
     158              :             buffers::const_buffer("\r\n", 2)));
     159              : }
     160              : 
     161              : template<class DynamicBuffer>
     162              : void
     163              : write_last_chunk(DynamicBuffer& db)
     164              : {
     165              :     db.commit(
     166              :         buffers::buffer_copy(
     167              :             db.prepare(5),
     168              :             buffers::const_buffer("0\r\n\r\n", 5)));
     169              : }
     170              : 
     171              : //------------------------------------------------
     172              : 
     173           45 : serializer::
     174              : ~serializer()
     175              : {
     176           45 : }
     177              : 
     178            0 : serializer::
     179              : serializer(
     180              :     serializer&&) noexcept = default;
     181              : 
     182           11 : serializer::
     183              : serializer(
     184           11 :     context& ctx)
     185           11 :     : serializer(ctx, 65536)
     186              : {
     187           11 : }
     188              : 
     189           45 : serializer::
     190              : serializer(
     191              :     context& ctx,
     192           45 :     std::size_t buffer_size)
     193           45 :     : ws_(buffer_size)
     194           45 :     , ctx_(ctx)
     195              : {
     196           45 : }
     197              : 
     198              : void
     199           56 : serializer::
     200              : reset() noexcept
     201              : {
     202           56 :     chunk_header_ = {};
     203           56 :     chunk_close_ = {};
     204           56 :     last_chunk_ = {};
     205           56 :     filter_ = nullptr;
     206           56 :     more_ = false;
     207           56 :     is_done_ = false;
     208           56 :     is_chunked_ = false;
     209           56 :     is_expect_continue_ = false;
     210           56 :     is_compressed_ = false;
     211           56 :     filter_done_ = false;
     212           56 :     in_ = nullptr;
     213           56 :     out_ = nullptr;
     214           56 :     ws_.clear();
     215           56 : }
     216              : 
     217              : //------------------------------------------------
     218              : 
     219              : auto
     220        12479 : serializer::
     221              : prepare() ->
     222              :     system::result<
     223              :         const_buffers_type>
     224              : {
     225              :     // Precondition violation
     226        12479 :     if( is_done_ )
     227            1 :         detail::throw_logic_error();
     228              : 
     229              :     // Expect: 100-continue
     230        12478 :     if( is_expect_continue_ )
     231              :     {
     232            4 :         if( !is_header_done_ )
     233            2 :             return const_buffers_type(hp_, 1);
     234            2 :         is_expect_continue_ = false;
     235            2 :         BOOST_HTTP_PROTO_RETURN_EC(
     236              :             error::expect_100_continue);
     237              :     }
     238              : 
     239        12474 :     if( st_ == style::empty )
     240            9 :         return const_buffers_type(
     241            6 :             prepped_.data(), prepped_.size());
     242              : 
     243        12471 :     if( st_ == style::buffers && !filter_ )
     244            9 :         return const_buffers_type(
     245            6 :             prepped_.data(), prepped_.size());
     246              : 
     247              :     // TODO: This is a temporary solution until we refactor
     248              :     // the implementation for efficient partial buffer consumption.
     249        12468 :     if( is_chunked_ && buffers::buffer_size(prepped_) && is_header_done_ )
     250           21 :         return const_buffers_type(
     251           14 :             prepped_.data(), prepped_.size());
     252              : 
     253        12461 :     auto& input = *in_;
     254        12461 :     auto& output = *out_;
     255        12461 :     if( st_ == style::source && more_ )
     256              :     {
     257         5494 :         auto results = src_->read(
     258         5494 :             input.prepare(input.capacity()));
     259         5494 :         if(results.ec.failed())
     260              :         {
     261            1 :             is_done_ = true;
     262            1 :             return results.ec;
     263              :         }
     264         5493 :         more_ = !results.finished;
     265         5493 :         input.commit(results.bytes);
     266              :     }
     267              : 
     268        30451 :     if( st_ == style::stream &&
     269        17970 :         more_ &&
     270         5510 :         in_->size() == 0 )
     271            1 :         BOOST_HTTP_PROTO_RETURN_EC(error::need_data);
     272              : 
     273              :     bool has_avail_out =
     274        24873 :         ((!filter_ && (more_ || input.size() > 0)) ||
     275        12414 :         (filter_ && !filter_done_));
     276              : 
     277        25057 :     auto get_input = [&]() -> buffers::const_buffer
     278              :     {
     279        25057 :         if( st_ == style::buffers )
     280              :         {
     281         3081 :             if( buffers::buffer_size(buf_) == 0 )
     282           48 :                 return {};
     283              : 
     284         3033 :             auto buf = *(buf_.data());
     285         3033 :             BOOST_ASSERT(buf.size() > 0);
     286         3033 :             return buf;
     287              :         }
     288              :         else
     289              :         {
     290        21976 :             if( input.size() == 0 )
     291        11016 :                 return {};
     292              : 
     293        10960 :             auto cbs = input.data();
     294        10960 :             auto buf = *cbs.begin();
     295        10960 :             if( buf.size() == 0 )
     296              :             {
     297            0 :                 auto p = cbs.begin();
     298            0 :                 ++p;
     299            0 :                 buf = *p;
     300              :             }
     301        10960 :             if( buf.size() == 0 )
     302            0 :                 detail::throw_logic_error();
     303        10960 :             return buf;
     304              :         }
     305        12459 :     };
     306              : 
     307        25057 :     auto get_output = [&]() -> buffers::mutable_buffer
     308              :     {
     309        25057 :         auto mbs = output.prepare(output.capacity());
     310        25057 :         auto buf = *mbs.begin();
     311        25057 :         if( buf.size() == 0 )
     312              :         {
     313         1416 :             auto p = mbs.begin();
     314         1416 :             ++p;
     315         1416 :             buf = *p;
     316              :         }
     317        25057 :         return buf;
     318        12459 :     };
     319              : 
     320        23640 :     auto consume = [&](std::size_t n)
     321              :     {
     322        23640 :         if( st_ == style::buffers )
     323              :         {
     324         1664 :             buf_.consume(n);
     325         1664 :             if( buffers::buffer_size(buf_) == 0 )
     326           56 :                 more_ = false;
     327              :         }
     328              :         else
     329        21976 :             input.consume(n);
     330        36099 :     };
     331              : 
     332        12459 :     std::size_t num_written = 0;
     333        12459 :     if( !filter_ )
     334           50 :         num_written += input.size();
     335              :     else
     336              :     {
     337              :         for(;;)
     338              :         {
     339        25057 :             auto in = get_input();
     340        25057 :             auto out = get_output();
     341        25057 :             if( out.size() == 0 )
     342              :             {
     343         1416 :                 if( output.size() == 0 )
     344            0 :                     detail::throw_logic_error();
     345        12408 :                 break;
     346              :             }
     347              : 
     348        23641 :             auto rs = filter_->process(
     349        23641 :                 out, in, more_);
     350              : 
     351        23641 :             if(rs.ec.failed())
     352              :             {
     353            1 :                 is_done_ = true;
     354            1 :                 return rs.ec;
     355              :             }
     356              : 
     357        23640 :             if( rs.finished )
     358           96 :                 filter_done_ = true;
     359              : 
     360        23640 :             consume(rs.in_bytes);
     361              : 
     362        23640 :             if( rs.out_bytes == 0 )
     363        10992 :                 break;
     364              : 
     365        12648 :             num_written += rs.out_bytes;
     366        12648 :             output.commit(rs.out_bytes);
     367        12648 :         }
     368              :     }
     369              : 
     370              :     // end:
     371        12458 :     std::size_t n = 0;
     372        12458 :     if( !is_header_done_ )
     373              :     {
     374           58 :         BOOST_ASSERT(hp_ == &prepped_[0]);
     375           58 :         ++n;
     376              :     }
     377              :     else
     378        12400 :         prepped_.reset(prepped_.capacity());
     379              : 
     380        12458 :     if( !is_chunked_ )
     381              :     {
     382        18648 :         for(buffers::const_buffer const& b : output.data())
     383        12432 :             prepped_[n++] = b;
     384              :     }
     385              :     else
     386              :     {
     387         6242 :         if( has_avail_out )
     388              :         {
     389         6239 :             write_chunk_header(
     390         6239 :                 chunk_header_, num_written);
     391         6239 :             prepped_[n++] = chunk_header_;
     392              : 
     393        18717 :             for(buffers::const_buffer const& b : output.data())
     394        12478 :                 prepped_[n++] = b;
     395              : 
     396         6239 :             prepped_[n++] = chunk_close_;
     397              :         }
     398              : 
     399         6242 :         if( (filter_ && filter_done_) ||
     400         6218 :             (!filter_ && !more_) )
     401           29 :             prepped_[n++] = last_chunk_;
     402              :     }
     403              : 
     404              :     auto cbs = const_buffers_type(
     405        12458 :         prepped_.data(), prepped_.size());
     406              : 
     407        12458 :     BOOST_ASSERT(buffers::buffer_size(cbs) > 0);
     408        12458 :     return cbs;
     409              : }
     410              : 
     411              : void
     412        14224 : serializer::
     413              : consume(
     414              :     std::size_t n)
     415              : {
     416              :     // Precondition violation
     417        14224 :     if( is_done_ && n != 0 )
     418            1 :         detail::throw_logic_error();
     419              : 
     420        14223 :     if( is_expect_continue_ )
     421              :     {
     422              :         // Cannot consume more than
     423              :         // the header on 100-continue
     424            3 :         if( n > hp_->size() )
     425            1 :             detail::throw_invalid_argument();
     426              :     }
     427              : 
     428        14222 :     if( !is_header_done_ )
     429              :     {
     430              :         // consume header
     431           76 :         if( n < hp_->size() )
     432              :         {
     433           11 :             prepped_.consume(n);
     434           11 :             return;
     435              :         }
     436           65 :         n -= hp_->size();
     437           65 :         prepped_.consume(hp_->size());
     438           65 :         is_header_done_ = true;
     439              :     }
     440              : 
     441        14211 :     prepped_.consume(n);
     442        14211 :     if( out_ )
     443              :     {
     444        14197 :         BOOST_ASSERT(st_ != style::empty);
     445        14197 :         out_->consume(n);
     446              :     }
     447        14211 :     auto is_empty = (buffers::buffer_size(prepped_) == 0);
     448              : 
     449        14211 :     if( st_ == style::buffers && !filter_ && is_empty )
     450            4 :         more_ = false;
     451              : 
     452        14211 :     if( st_ == style::empty &&
     453            4 :         is_empty &&
     454            4 :         !is_expect_continue_ )
     455            3 :         more_ = false;
     456              : 
     457        14211 :     if( is_empty )
     458        12472 :         is_done_ = filter_ ? filter_done_ : !more_;
     459              : }
     460              : 
     461              : void
     462           24 : serializer::
     463              : use_deflate_encoding()
     464              : {
     465              :     // can only apply one encoding
     466           24 :     if(filter_)
     467            0 :         detail::throw_logic_error();
     468              : 
     469           24 :     is_compressed_ = true;
     470           24 :     filter_ = &ws_.emplace<deflator_filter>(ctx_, ws_, false);
     471           24 : }
     472              : 
     473              : void
     474           25 : serializer::
     475              : use_gzip_encoding()
     476              : {
     477              :     // can only apply one encoding
     478           25 :     if( filter_ )
     479            0 :         detail::throw_logic_error();
     480              : 
     481           25 :     is_compressed_ = true;
     482           25 :     filter_ = &ws_.emplace<deflator_filter>(ctx_, ws_, true);
     483           25 : }
     484              : 
     485              : //------------------------------------------------
     486              : 
     487              : void
     488            7 : serializer::
     489              : copy(
     490              :     buffers::const_buffer* dest,
     491              :     buffers::const_buffer const* src,
     492              :     std::size_t n) noexcept
     493              : {
     494           14 :     while(n--)
     495            7 :         *dest++ = *src++;
     496            7 : }
     497              : 
     498              : void
     499           75 : serializer::
     500              : start_init(
     501              :     message_view_base const& m)
     502              : {
     503              :     // VFALCO what do we do with
     504              :     // metadata error code failures?
     505              :     // m.ph_->md.maybe_throw();
     506              : 
     507           75 :     auto const& md = m.metadata();
     508              : 
     509           75 :     is_done_ = false;
     510           75 :     is_header_done_ = false;
     511           75 :     is_expect_continue_ = md.expect.is_100_continue;
     512              : 
     513              :     // Transfer-Encoding
     514              :     {
     515           75 :         auto const& te = md.transfer_encoding;
     516           75 :         is_chunked_ = te.is_chunked;
     517              :     }
     518              : 
     519           75 :     if( is_chunked_)
     520              :     {
     521           31 :         auto* p = ws_.reserve_front(chunked_overhead_);
     522           31 :         chunk_header_ =
     523           31 :             buffers::mutable_buffer(p, chunk_header_len_);
     524           31 :         chunk_close_ =
     525           62 :             buffers::mutable_buffer(
     526           31 :                 p + chunk_header_len_, crlf_len_);
     527           31 :         last_chunk_ =
     528           62 :             buffers::mutable_buffer(
     529           31 :                 p + chunk_header_len_ + crlf_len_,
     530              :                 last_chunk_len_);
     531              : 
     532           31 :         buffers::buffer_copy(
     533           31 :             chunk_close_, buffers::const_buffer("\r\n", 2));
     534           31 :         buffers::buffer_copy(
     535           31 :             last_chunk_,
     536           62 :             buffers::const_buffer("0\r\n\r\n", 5));
     537              :     }
     538           75 : }
     539              : 
     540              : void
     541            4 : serializer::
     542              : start_empty(
     543              :     message_view_base const& m)
     544              : {
     545            4 :     start_init(m);
     546              : 
     547            4 :     st_ = style::empty;
     548            4 :     more_ = true;
     549              : 
     550            4 :     if(! is_chunked_)
     551              :     {
     552            3 :         prepped_ = make_array(
     553              :             1); // header
     554              :     }
     555              :     else
     556              :     {
     557            1 :         prepped_ = make_array(
     558              :             1 + // header
     559              :             1); // final chunk
     560              : 
     561              :         // Buffer is too small
     562            1 :         if(ws_.size() < 5)
     563            0 :             detail::throw_length_error();
     564              : 
     565              :         buffers::mutable_buffer dest(
     566            1 :             ws_.data(), 5);
     567            1 :         buffers::buffer_copy(
     568              :             dest,
     569            1 :             buffers::const_buffer(
     570              :                 "0\r\n\r\n", 5));
     571            1 :         prepped_[1] = dest;
     572              :     }
     573              : 
     574            4 :     hp_ = &prepped_[0];
     575            4 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     576            4 : }
     577              : 
     578              : void
     579           24 : serializer::
     580              : start_buffers(
     581              :     message_view_base const& m)
     582              : {
     583           24 :     st_ = style::buffers;
     584           24 :     tmp1_ = {};
     585              : 
     586           24 :     if( !filter_ && !is_chunked_ )
     587              :     {
     588            6 :         prepped_ = make_array(
     589              :             1 +           // header
     590            6 :             buf_.size()); // user input
     591              : 
     592            6 :         hp_ = &prepped_[0];
     593            6 :         *hp_ = { m.ph_->cbuf, m.ph_->size };
     594              : 
     595            6 :         copy(&prepped_[1], buf_.data(), buf_.size());
     596              : 
     597            6 :         more_ = (buffers::buffer_size(buf_) > 0);
     598            6 :         return;
     599              :     }
     600              : 
     601           18 :     if( !filter_ && is_chunked_ )
     602              :     {
     603            1 :         if( buffers::buffer_size(buf_) == 0 )
     604              :         {
     605            0 :             prepped_ = make_array(
     606              :                 1 +           // header
     607              :                 1);           // last chunk
     608              : 
     609            0 :             hp_ = &prepped_[0];
     610            0 :             *hp_ = { m.ph_->cbuf, m.ph_->size };
     611            0 :             prepped_[1] = last_chunk_;
     612            0 :             more_ = false;
     613            0 :             return;
     614              :         }
     615              : 
     616            2 :         write_chunk_header(
     617            1 :             chunk_header_, buffers::buffer_size(buf_));
     618              : 
     619            1 :         prepped_ = make_array(
     620              :             1 +           // header
     621              :             1 +           // chunk header
     622            1 :             buf_.size() + // user input
     623              :             1 +           // chunk close
     624              :             1);           // last chunk
     625              : 
     626            1 :         hp_ = &prepped_[0];
     627            1 :         *hp_ = { m.ph_->cbuf, m.ph_->size };
     628            1 :         prepped_[1] = chunk_header_;
     629            1 :         copy(&prepped_[2], buf_.data(), buf_.size());
     630              : 
     631            1 :         prepped_[prepped_.size() - 2] = chunk_close_;
     632            1 :         prepped_[prepped_.size() - 1] = last_chunk_;
     633            1 :         more_ = true;
     634            1 :         return;
     635              :     }
     636              : 
     637           17 :     if( is_chunked_ )
     638              :     {
     639            8 :         prepped_ = make_array(
     640              :             1 + // header
     641              :             1 + // chunk header
     642              :             2 + // tmp
     643              :             1 + // chunk close
     644              :             1); // last chunk
     645              :     }
     646              :     else
     647            9 :         prepped_ = make_array(
     648              :             1 + // header
     649              :             2); // tmp
     650              : 
     651           17 :     hp_ = &prepped_[0];
     652           17 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     653           17 :     tmp0_ = { ws_.data(), ws_.size() };
     654           17 :     out_ = &tmp0_;
     655           17 :     in_ = out_;
     656           17 :     more_ = true;
     657              : }
     658              : 
     659              : void
     660           25 : serializer::
     661              : start_source(
     662              :     message_view_base const& m,
     663              :     source* src)
     664              : {
     665           25 :     st_ = style::source;
     666           25 :     src_ = src;
     667              : 
     668           25 :     if( is_chunked_ )
     669              :     {
     670           10 :         prepped_ = make_array(
     671              :             1 + // header
     672              :             1 + // chunk header
     673              :             2 + // tmp
     674              :             1 + // chunk close
     675              :             1); // last chunk
     676              :     }
     677              :     else
     678           15 :         prepped_ = make_array(
     679              :             1 + // header
     680              :             2); // tmp
     681              : 
     682           25 :     if( !filter_ )
     683              :     {
     684            9 :         tmp0_ = { ws_.data(), ws_.size() };
     685            9 :         if( tmp0_.capacity() < 1 )
     686            0 :             detail::throw_length_error();
     687              : 
     688            9 :         in_ = &tmp0_;
     689            9 :         out_ = &tmp0_;
     690              :     }
     691              :     else
     692              :     {
     693           16 :         auto n = ws_.size() / 2;
     694           16 :         auto* p = ws_.reserve_front(n);
     695           16 :         tmp1_ = buffers::circular_buffer(p, n);
     696              : 
     697           16 :         tmp0_ = { ws_.data(), ws_.size() };
     698           16 :         if( tmp0_.capacity() < 1 )
     699            0 :             detail::throw_length_error();
     700              : 
     701           16 :         in_ = &tmp1_;
     702           16 :         out_ = &tmp0_;
     703              :     }
     704              : 
     705           25 :     hp_ = &prepped_[0];
     706           25 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     707           25 :     more_ = true;
     708           25 : }
     709              : 
     710              : auto
     711           22 : serializer::
     712              : start_stream(
     713              :     message_view_base const& m) ->
     714              :         stream
     715              : {
     716           22 :     start_init(m);
     717              : 
     718           22 :     st_ = style::stream;
     719           22 :     if( is_chunked_ )
     720              :     {
     721           11 :         prepped_ = make_array(
     722              :             1 + // header
     723              :             1 + // chunk header
     724              :             2 + // tmp
     725              :             1 + // chunk close
     726              :             1); // last chunk
     727              :     }
     728              :     else
     729           11 :         prepped_ = make_array(
     730              :             1 + // header
     731              :             2); // tmp
     732              : 
     733           22 :     if( !filter_ )
     734              :     {
     735            6 :         tmp0_ = { ws_.data(), ws_.size() };
     736            6 :         if( tmp0_.capacity() < 1 )
     737            0 :             detail::throw_length_error();
     738              : 
     739            6 :         in_ = &tmp0_;
     740            6 :         out_ = &tmp0_;
     741              :     }
     742              :     else
     743              :     {
     744           16 :         auto n = ws_.size() / 2;
     745           16 :         auto* p = ws_.reserve_front(n);
     746           16 :         tmp1_ = buffers::circular_buffer(p, n);
     747              : 
     748           16 :         tmp0_ = { ws_.data(), ws_.size() };
     749           16 :         if( tmp0_.capacity() < 1 )
     750            0 :             detail::throw_length_error();
     751              : 
     752           16 :         in_ = &tmp1_;
     753           16 :         out_ = &tmp0_;
     754              :     }
     755              : 
     756           22 :     hp_ = &prepped_[0];
     757           22 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     758           22 :     more_ = true;
     759           22 :     return stream{*this};
     760              : }
     761              : 
     762              : //------------------------------------------------
     763              : 
     764              : std::size_t
     765          139 : serializer::
     766              : stream::
     767              : capacity() const noexcept
     768              : {
     769          139 :     return sr_->in_->capacity();
     770              : }
     771              : 
     772              : std::size_t
     773           72 : serializer::
     774              : stream::
     775              : size() const noexcept
     776              : {
     777           72 :     return sr_->in_->size();
     778              : }
     779              : 
     780              : bool
     781           63 : serializer::
     782              : stream::
     783              : is_full() const noexcept
     784              : {
     785           63 :     return capacity() == 0;
     786              : }
     787              : 
     788              : auto
     789         5512 : serializer::
     790              : stream::
     791              : prepare() const ->
     792              :     buffers_type
     793              : {
     794         5512 :     return sr_->in_->prepare(sr_->in_->capacity());
     795              : }
     796              : 
     797              : void
     798         5512 : serializer::
     799              : stream::
     800              : commit(std::size_t n) const
     801              : {
     802              :     // the stream must make a non-zero amount of bytes
     803              :     // available to the serializer
     804         5512 :     if( n == 0 )
     805            1 :         detail::throw_logic_error();
     806              : 
     807         5511 :     sr_->in_->commit(n);
     808         5511 : }
     809              : 
     810              : void
     811           25 : serializer::
     812              : stream::
     813              : close() const
     814              : {
     815              :     // Precondition violation
     816           25 :     if(! sr_->more_ )
     817            4 :         detail::throw_logic_error();
     818           21 :     sr_->more_ = false;
     819           21 : }
     820              : 
     821              : //------------------------------------------------
     822              : 
     823              : } // http_proto
     824              : } // boost
        

Generated by: LCOV version 2.1