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

Generated by: LCOV version 2.1