LCOV - code coverage report
Current view: top level - libs/http_proto/src/serializer.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 60.8 % 390 237
Test Date: 2024-05-23 18:56:44 Functions: 75.0 % 28 21

            Line data    Source code
       1              : //
       2              : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3              : // Copyright (c) 2024 Christian Mazakas
       4              : //
       5              : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       6              : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       7              : //
       8              : // Official repository: https://github.com/cppalliance/http_proto
       9              : //
      10              : 
      11              : #include <boost/http_proto/serializer.hpp>
      12              : #include <boost/http_proto/message_view_base.hpp>
      13              : #include <boost/http_proto/filter.hpp>
      14              : #include <boost/http_proto/service/zlib_service.hpp>
      15              : #include <boost/http_proto/detail/except.hpp>
      16              : #include <boost/buffers/algorithm.hpp>
      17              : #include <boost/buffers/buffer_copy.hpp>
      18              : #include <boost/buffers/buffer_size.hpp>
      19              : #include <boost/core/ignore_unused.hpp>
      20              : #include <stddef.h>
      21              : #include <iostream>
      22              : 
      23              : namespace boost {
      24              : namespace http_proto {
      25              : 
      26              : //------------------------------------------------
      27              : 
      28              : void
      29            0 : consume_buffers(
      30              :     buffers::const_buffer*& p,
      31              :     std::size_t& n,
      32              :     std::size_t bytes)
      33              : {
      34            0 :     while(n > 0)
      35              :     {
      36            0 :         if(bytes < p->size())
      37              :         {
      38            0 :             *p += bytes;
      39            0 :             return;
      40              :         }
      41            0 :         bytes -= p->size();
      42            0 :         ++p;
      43            0 :         --n;
      44              :     }
      45              : 
      46              :     // Precondition violation
      47            0 :     if(bytes > 0)
      48            0 :         detail::throw_invalid_argument();
      49              : }
      50              : 
      51              : template<class MutableBuffers>
      52              : void
      53           27 : write_chunk_header(
      54              :     MutableBuffers const& dest0,
      55              :     std::size_t size) noexcept
      56              : {
      57              :     static constexpr char hexdig[] =
      58              :         "0123456789ABCDEF";
      59              :     char buf[18];
      60           27 :     auto p = buf + 16;
      61          459 :     for(std::size_t i = 16; i--;)
      62              :     {
      63          432 :         *--p = hexdig[size & 0xf];
      64          432 :         size >>= 4;
      65              :     }
      66           27 :     buf[16] = '\r';
      67           27 :     buf[17] = '\n';
      68           27 :     auto n = buffers::buffer_copy(
      69              :         dest0,
      70           54 :         buffers::const_buffer(
      71              :             buf, sizeof(buf)));
      72              :     ignore_unused(n);
      73           27 :     BOOST_ASSERT(n == 18);
      74           27 :     BOOST_ASSERT(
      75              :         buffers::buffer_size(dest0) == n);
      76           27 : }
      77              : 
      78              : template<class DynamicBuffer>
      79              : void
      80           19 : write_chunk_close(DynamicBuffer& db)
      81              : {
      82           19 :     db.commit(
      83              :         buffers::buffer_copy(
      84           19 :             db.prepare(2),
      85           19 :             buffers::const_buffer("\r\n", 2)));
      86           19 : }
      87              : 
      88              : template<class DynamicBuffer>
      89              : void
      90            3 : write_last_chunk(DynamicBuffer& db)
      91              : {
      92            3 :     db.commit(
      93              :         buffers::buffer_copy(
      94            3 :             db.prepare(5),
      95            3 :             buffers::const_buffer("0\r\n\r\n", 5)));
      96            3 : }
      97              : 
      98              : //------------------------------------------------
      99              : 
     100           20 : serializer::
     101              : ~serializer()
     102              : {
     103           20 : }
     104              : 
     105            9 : serializer::
     106            9 : serializer()
     107            9 :     : serializer(65536)
     108              : {
     109            9 : }
     110              : 
     111            0 : serializer::
     112              : serializer(
     113              :     serializer&&) noexcept = default;
     114              : 
     115           20 : serializer::
     116              : serializer(
     117           20 :     std::size_t buffer_size)
     118           20 :     : ws_(buffer_size)
     119              : {
     120           20 : }
     121              : 
     122            0 : serializer::
     123              : serializer(
     124              :     context& ctx,
     125            0 :     std::size_t buffer_size)
     126            0 :     : ws_(buffer_size)
     127            0 :     , ctx_(&ctx)
     128              : {
     129            0 :     if( ctx_->has_service<zlib::deflate_decoder_service>() )
     130              :     {
     131            0 :         filter_ws_.allocate(1024);
     132            0 :         tmp1_ = { filter_ws_.data(), filter_ws_.size() };
     133              :     }
     134            0 : }
     135              : 
     136              : void
     137            0 : serializer::
     138              : reset() noexcept
     139              : {
     140            0 : }
     141              : 
     142              : //------------------------------------------------
     143              : 
     144              : auto
     145           67 : serializer::
     146              : prepare() ->
     147              :     system::result<
     148              :         const_buffers_type>
     149              : {
     150              :     // Precondition violation
     151           67 :     if(is_done_)
     152            1 :         detail::throw_logic_error();
     153              : 
     154              :     // Expect: 100-continue
     155           66 :     if(is_expect_continue_)
     156              :     {
     157            4 :         if(out_.data() == hp_)
     158            2 :             return const_buffers_type(hp_, 1);
     159            2 :         is_expect_continue_ = false;
     160            2 :         BOOST_HTTP_PROTO_RETURN_EC(
     161              :             error::expect_100_continue);
     162              :     }
     163              : 
     164           62 :     if( is_compressed_ )
     165              :     {
     166            0 :         auto on_end = [&]
     167              :         {
     168            0 :             std::size_t n = 0;
     169            0 :             if(out_.data() == hp_)
     170            0 :                 ++n;
     171              : 
     172            0 :             for(buffers::const_buffer const& b : tmp0_.data())
     173            0 :                 out_[n++] = b;
     174              : 
     175              :             auto cbs = const_buffers_type(
     176            0 :                 out_.data(), out_.size());
     177              : 
     178            0 :             BOOST_ASSERT(buffers::buffer_size(cbs) > 0);
     179              : 
     180            0 :             return cbs;
     181            0 :         };
     182              : 
     183            0 :         if(! zlib_filter_ )
     184              :         {
     185              :             auto& svc =
     186            0 :                 ctx_->get_service<
     187            0 :                     zlib::deflate_decoder_service>();
     188              : 
     189            0 :             zlib_filter_ = &svc.make_filter(filter_ws_);
     190            0 :             tmp1_ =
     191            0 :                 { filter_ws_.data(), filter_ws_.size() };
     192              :         }
     193              : 
     194            0 :         auto& zbuf = tmp1_;
     195              : 
     196            0 :         if( st_ == style::source )
     197              :         {
     198            0 :             auto results = src_->read(
     199            0 :                 zbuf.prepare(zbuf.capacity()));
     200            0 :             more_ = !results.finished;
     201            0 :             zbuf.commit(results.bytes);
     202              :         }
     203              : 
     204            0 :         buffers::mutable_buffer chunk_header;
     205            0 :         if( is_chunked_ )
     206              :         {
     207            0 :             auto mbs = tmp0_.prepare(detail::chunk_header_len_);
     208            0 :             auto buf = *mbs.begin();
     209            0 :             if( buf.size() == 0 )
     210              :             {
     211            0 :                 auto p = mbs.begin();
     212            0 :                 ++p;
     213            0 :                 buf = *p;
     214              :             }
     215              : 
     216            0 :             if( buf.size() < detail::chunk_header_len_ )
     217            0 :                 detail::throw_length_error();
     218              : 
     219            0 :             std::memset(
     220              :                 buf.data(), 0x00, detail::chunk_header_len_);
     221            0 :             tmp0_.commit(detail::chunk_header_len_);
     222              : 
     223            0 :             chunk_header = buf;
     224              :         }
     225              : 
     226            0 :         auto get_output = [&]() -> buffers::mutable_buffer
     227              :         {
     228            0 :             auto mbs = tmp0_.prepare(tmp0_.capacity());
     229            0 :             auto buf = *mbs.begin();
     230            0 :             if( buf.size() == 0 )
     231              :             {
     232            0 :                 auto p = mbs.begin();
     233            0 :                 ++p;
     234            0 :                 buf = *p;
     235              :             }
     236              : 
     237            0 :             if( is_chunked_ )
     238              :             {
     239            0 :                 if( buf.size() <
     240              :                     detail::crlf_len_ + detail::last_chunk_len_ + 1 )
     241            0 :                     return {};
     242              : 
     243              :                 auto n =
     244            0 :                     buf.size() -
     245              :                     detail::crlf_len_ -
     246            0 :                     detail::last_chunk_len_;
     247              : 
     248            0 :                 buf = { buf.data(), n };
     249              :             }
     250              : 
     251              :             // BOOST_ASSERT(buf.size() > 0);
     252            0 :             return buf;
     253            0 :         };
     254              : 
     255            0 :         auto get_input = [&]() -> buffers::const_buffer
     256              :         {
     257            0 :             if( st_ == style::buffers )
     258              :             {
     259            0 :                 if( buffers::buffer_size(buf_) == 0 )
     260            0 :                     return {};
     261              : 
     262            0 :                 auto buf = *(buf_.data());
     263            0 :                 BOOST_ASSERT(buf.size() > 0);
     264            0 :                 return buf;
     265              :             }
     266              :             else
     267              :             {
     268            0 :                 if( zbuf.size() == 0 )
     269            0 :                     return {};
     270              : 
     271            0 :                 auto cbs = zbuf.data();
     272            0 :                 auto buf = *cbs.begin();
     273            0 :                 if( buf.size() == 0 )
     274              :                 {
     275            0 :                     auto p = cbs.begin();
     276            0 :                     ++p;
     277            0 :                     buf = *p;
     278              :                 }
     279            0 :                 BOOST_ASSERT(buf.size() > 0);
     280            0 :                 return buf;
     281              :             }
     282            0 :         };
     283              : 
     284            0 :         std::size_t num_written = 0;
     285              :         while( true )
     286              :         {
     287            0 :             auto in = get_input();
     288            0 :             auto out = get_output();
     289            0 :             if( out.size() < 6 + 1 )
     290              :             {
     291            0 :                 BOOST_ASSERT(tmp0_.size() > 0);
     292            0 :                 break;
     293              :             }
     294              : 
     295            0 :             auto results = zlib_filter_->on_process(
     296            0 :                 out, in, more_);
     297              : 
     298            0 :             if( results.finished )
     299            0 :                 filter_done_ = true;
     300              : 
     301            0 :             if( st_ == style::buffers )
     302              :             {
     303            0 :                 buf_.consume(results.in_bytes);
     304            0 :                 if( buffers::buffer_size(buf_) == 0 )
     305            0 :                     more_ = false;
     306              :             }
     307              :             else
     308            0 :                 zbuf.consume(results.in_bytes);
     309              : 
     310            0 :             if( results.out_bytes == 0 )
     311            0 :                 break;
     312              : 
     313            0 :             BOOST_ASSERT(results.out_bytes > 0);
     314              : 
     315            0 :             num_written += results.out_bytes;
     316            0 :             tmp0_.commit(results.out_bytes);
     317            0 :         }
     318              : 
     319            0 :         BOOST_ASSERT(tmp0_.size() > 0);
     320            0 :         if( is_chunked_ )
     321              :         {
     322            0 :             write_chunk_header(
     323              :                 chunk_header, num_written);
     324              : 
     325            0 :             buffers::buffer_copy(
     326            0 :                 tmp0_.prepare(2),
     327            0 :                 buffers::const_buffer("\r\n", 2));
     328            0 :             tmp0_.commit(2);
     329              : 
     330            0 :             if( filter_done_ )
     331              :             {
     332            0 :                 buffers::buffer_copy(
     333            0 :                     tmp0_.prepare(5),
     334            0 :                     buffers::const_buffer(
     335              :                         "0\r\n\r\n", 5));
     336            0 :                 tmp0_.commit(5);
     337              :             }
     338              :         }
     339            0 :         return on_end();
     340              :     }
     341              : 
     342           62 :     if(st_ == style::empty)
     343              :     {
     344            9 :         return const_buffers_type(
     345            3 :             out_.data(),
     346            3 :             out_.size());
     347              :     }
     348              : 
     349           59 :     if(st_ == style::buffers)
     350              :     {
     351            9 :         return const_buffers_type(
     352            3 :             out_.data(),
     353            3 :             out_.size());
     354              :     }
     355              : 
     356           56 :     if(st_ == style::source)
     357              :     {
     358           22 :         if(more_)
     359              :         {
     360           17 :             if(! is_chunked_)
     361              :             {
     362            9 :                 auto rv = src_->read(
     363            9 :                     tmp0_.prepare(tmp0_.capacity()));
     364            9 :                 tmp0_.commit(rv.bytes);
     365            9 :                 if(rv.ec.failed())
     366            0 :                     return rv.ec;
     367            9 :                 more_ = ! rv.finished;
     368              :             }
     369              :             else
     370              :             {
     371            8 :                 if(tmp0_.capacity() > detail::chunked_overhead_)
     372              :                 {
     373            8 :                     auto dest = tmp0_.prepare(
     374            8 :                         tmp0_.capacity() -
     375              :                         2 - // CRLF
     376              :                         5); // final chunk
     377              : 
     378            8 :                     auto rv = src_->read(
     379            8 :                         buffers::sans_prefix(dest, 18));
     380              : 
     381            8 :                     if(rv.ec.failed())
     382            0 :                         return rv.ec;
     383              : 
     384            8 :                     if(rv.bytes != 0)
     385              :                     {
     386            7 :                         write_chunk_header(
     387            7 :                             buffers::prefix(dest, 18), rv.bytes);
     388            7 :                         tmp0_.commit(rv.bytes + 18);
     389              :                         // terminate chunk
     390            7 :                         tmp0_.commit(
     391              :                             buffers::buffer_copy(
     392            7 :                                 tmp0_.prepare(2),
     393           14 :                                 buffers::const_buffer(
     394              :                                     "\r\n", 2)));
     395              :                     }
     396              : 
     397            8 :                     if(rv.finished)
     398              :                     {
     399            2 :                         tmp0_.commit(
     400              :                             buffers::buffer_copy(
     401            2 :                                 tmp0_.prepare(5),
     402            2 :                                 buffers::const_buffer(
     403              :                                     "0\r\n\r\n", 5)));
     404            2 :                         more_ = false;
     405              :                     }
     406              :                 }
     407              :             }
     408              :         }
     409              : 
     410           22 :         std::size_t n = 0;
     411           22 :         if(out_.data() == hp_)
     412            5 :             ++n;
     413           66 :         for(buffers::const_buffer const& b : tmp0_.data())
     414           44 :             out_[n++] = b;
     415              : 
     416           66 :         return const_buffers_type(
     417           22 :             out_.data(),
     418           22 :             out_.size());
     419              :     }
     420              : 
     421           34 :     if(st_ == style::stream)
     422              :     {
     423           34 :         std::size_t n = 0;
     424           34 :         if(out_.data() == hp_)
     425            6 :             ++n;
     426           34 :         if(tmp0_.size() == 0 && more_)
     427              :         {
     428            1 :             BOOST_HTTP_PROTO_RETURN_EC(
     429              :                 error::need_data);
     430              :         }
     431           99 :         for(buffers::const_buffer const& b : tmp0_.data())
     432           66 :             out_[n++] = b;
     433              : 
     434           99 :         return const_buffers_type(
     435           33 :             out_.data(),
     436           33 :             out_.size());
     437              :     }
     438              : 
     439              :     // should never get here
     440            0 :     detail::throw_logic_error();
     441              : }
     442              : 
     443              : void
     444         1745 : serializer::
     445              : consume(
     446              :     std::size_t n)
     447              : {
     448              :     // Precondition violation
     449         1745 :     if(is_done_)
     450            1 :         detail::throw_logic_error();
     451              : 
     452         1744 :     if(is_expect_continue_)
     453              :     {
     454              :         // Cannot consume more than
     455              :         // the header on 100-continue
     456            3 :         if(n > hp_->size())
     457            1 :             detail::throw_invalid_argument();
     458              : 
     459            2 :         out_.consume(n);
     460            2 :         return;
     461              :     }
     462         1741 :     else if(out_.data() == hp_)
     463              :     {
     464              :         // consume header
     465           26 :         if(n < hp_->size())
     466              :         {
     467           11 :             out_.consume(n);
     468           11 :             return;
     469              :         }
     470           15 :         n -= hp_->size();
     471           15 :         out_.consume(hp_->size());
     472              :     }
     473              : 
     474         1730 :     switch(st_)
     475              :     {
     476            3 :     default:
     477              :     case style::empty:
     478            3 :         out_.consume(n);
     479            3 :         if(out_.empty())
     480            3 :             is_done_ = true;
     481            3 :         return;
     482              : 
     483            3 :     case style::buffers:
     484            3 :         if( is_compressed_ )
     485              :         {
     486            0 :             tmp0_.consume(n);
     487            0 :             if( tmp0_.size() == 0 &&
     488            0 :                 filter_done_ )
     489            0 :                 is_done_ = true;
     490            0 :             return;
     491              :         }
     492            3 :         out_.consume(n);
     493            3 :         if(out_.empty())
     494            3 :             is_done_ = true;
     495            3 :         return;
     496              : 
     497         1724 :     case style::source:
     498              :     case style::stream:
     499         1724 :         tmp0_.consume(n);
     500         1724 :         if( !is_compressed_ &&
     501         3448 :             tmp0_.size() == 0 &&
     502           39 :             ! more_)
     503           11 :             is_done_ = true;
     504              : 
     505            0 :         if( is_compressed_ &&
     506         1724 :             tmp0_.size() == 0 &&
     507            0 :             filter_done_ )
     508            0 :             is_done_ = true;
     509         1724 :         return;
     510              :     }
     511              : }
     512              : 
     513              : //------------------------------------------------
     514              : 
     515              : void
     516            7 : serializer::
     517              : copy(
     518              :     buffers::const_buffer* dest,
     519              :     buffers::const_buffer const* src,
     520              :     std::size_t n) noexcept
     521              : {
     522           14 :     while(n--)
     523            7 :         *dest++ = *src++;
     524            7 : }
     525              : 
     526              : void
     527           26 : serializer::
     528              : start_init(
     529              :     message_view_base const& m)
     530              : {
     531           26 :     ws_.clear();
     532              : 
     533              :     // VFALCO what do we do with
     534              :     // metadata error code failures?
     535              :     // m.ph_->md.maybe_throw();
     536              : 
     537           26 :     is_done_ = false;
     538              : 
     539           26 :     is_expect_continue_ =
     540           26 :         m.ph_->md.expect.is_100_continue;
     541              : 
     542              :     // Transfer-Encoding
     543              :     {
     544           26 :         auto const& te =
     545           26 :             m.ph_->md.transfer_encoding;
     546           26 :         is_chunked_ = te.is_chunked;
     547              :     }
     548              : 
     549           26 :     if( m.compressed() )
     550              :     {
     551            0 :         is_compressed_ = true;
     552              :         auto& svc =
     553            0 :             ctx_->get_service<
     554            0 :                 zlib::deflate_decoder_service>();
     555              : 
     556            0 :         BOOST_ASSERT(!zlib_filter_);
     557            0 :         zlib_filter_ = &svc.make_filter(filter_ws_);
     558              :     }
     559           26 : }
     560              : 
     561              : void
     562            4 : serializer::
     563              : start_empty(
     564              :     message_view_base const& m)
     565              : {
     566            4 :     start_init(m);
     567              : 
     568            4 :     st_ = style::empty;
     569              : 
     570            4 :     if(! is_chunked_)
     571              :     {
     572            3 :         out_ = make_array(
     573              :             1); // header
     574              :     }
     575              :     else
     576              :     {
     577            1 :         out_ = make_array(
     578              :             1 + // header
     579              :             1); // final chunk
     580              : 
     581              :         // Buffer is too small
     582            1 :         if(ws_.size() < 5)
     583            0 :             detail::throw_length_error();
     584              : 
     585              :         buffers::mutable_buffer dest(
     586            1 :             ws_.data(), 5);
     587            1 :         buffers::buffer_copy(
     588              :             dest,
     589            1 :             buffers::const_buffer(
     590              :                 "0\r\n\r\n", 5));
     591            1 :         out_[1] = dest;
     592              :     }
     593              : 
     594            4 :     hp_ = &out_[0];
     595            4 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     596            4 : }
     597              : 
     598              : void
     599            7 : serializer::
     600              : start_buffers(
     601              :     message_view_base const& m)
     602              : {
     603            7 :     st_ = style::buffers;
     604            7 :     if( is_compressed_ )
     605              :     {
     606            0 :         out_ = make_array(
     607              :             1 + // header
     608              :             2); // tmp
     609              : 
     610            0 :         hp_ = &out_[0];
     611            0 :         *hp_ = { m.ph_->cbuf, m.ph_->size };
     612            0 :         tmp0_ = { ws_.data(), ws_.size() };
     613            0 :         more_ = true;
     614            0 :         return;
     615              :     }
     616              : 
     617            7 :     if(! is_chunked_)
     618              :     {
     619              :         //if(! cod_)
     620              :         {
     621            6 :             out_ = make_array(
     622              :                 1 +             // header
     623            6 :                 buf_.size());   // body
     624           12 :             copy(&out_[1],
     625            6 :                 buf_.data(), buf_.size());
     626              :         }
     627              : #if 0
     628              :         else
     629              :         {
     630              :             out_ = make_array(
     631              :                 1 + // header
     632              :                 2); // tmp1
     633              :         }
     634              : #endif
     635              :     }
     636              :     else
     637              :     {
     638              :         //if(! cod_)
     639              :         {
     640            1 :             out_ = make_array(
     641              :                 1 +             // header
     642              :                 1 +             // chunk size
     643            1 :                 buf_.size() +   // body
     644              :                 1);             // final chunk
     645            2 :             copy(&out_[2],
     646            1 :                 buf_.data(), buf_.size());
     647              : 
     648              :             // Buffer is too small
     649            1 :             if(ws_.size() < 18 + 7)
     650            0 :                 detail::throw_length_error();
     651            1 :             buffers::mutable_buffer s1(ws_.data(), 18);
     652            1 :             buffers::mutable_buffer s2(ws_.data(), 18 + 7);
     653            1 :             s2 += 18; // VFALCO HACK
     654            1 :             write_chunk_header(
     655              :                 s1,
     656            1 :                 buffers::buffer_size(buf_));
     657            1 :             buffers::buffer_copy(s2, buffers::const_buffer(
     658              :                 "\r\n"
     659              :                 "0\r\n"
     660              :                 "\r\n", 7));
     661            1 :             out_[1] = s1;
     662            1 :             out_[out_.size() - 1] = s2;
     663              :         }
     664              : #if 0
     665              :         else
     666              :         {
     667              :             out_ = make_array(
     668              :                 1 +     // header
     669              :                 2);     // tmp1
     670              :         }
     671              : #endif
     672              :     }
     673              : 
     674            7 :     hp_ = &out_[0];
     675            7 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     676              : }
     677              : 
     678              : void
     679            8 : serializer::
     680              : start_source(
     681              :     message_view_base const& m,
     682              :     source* src)
     683              : {
     684            8 :     st_ = style::source;
     685            8 :     src_ = src;
     686            8 :     out_ = make_array(
     687              :         1 + // header
     688              :         2); // tmp
     689              :     //if(! cod_)
     690              :     {
     691            8 :         tmp0_ = { ws_.data(), ws_.size() };
     692            8 :         if(tmp0_.capacity() <
     693              :                 18 +    // chunk size
     694              :                 1 +     // body (1 byte)
     695              :                 2 +     // CRLF
     696              :                 5)      // final chunk
     697            0 :             detail::throw_length_error();
     698              :     }
     699              : #if 0
     700              :     else
     701              :     {
     702              :         buffers::buffered_base::allocator a(
     703              :             ws_.data(), ws_.size()/3, false);
     704              :         src->init(a);
     705              :         ws_.reserve(a.size_used());
     706              : 
     707              :         auto const n = ws_.size() / 2;
     708              : 
     709              :         tmp0_ = { ws_.data(), ws_.size() / 2 };
     710              :         ws_.reserve(n);
     711              : 
     712              :         // Buffer is too small
     713              :         if(ws_.size() < 1)
     714              :             detail::throw_length_error();
     715              : 
     716              :         tmp1_ = { ws_.data(), ws_.size() };
     717              :     }
     718              : #endif
     719              : 
     720            8 :     hp_ = &out_[0];
     721            8 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     722            8 :     more_ = true;
     723            8 : }
     724              : 
     725              : auto
     726            7 : serializer::
     727              : start_stream(
     728              :     message_view_base const& m) ->
     729              :         stream
     730              : {
     731            7 :     start_init(m);
     732              : 
     733            7 :     st_ = style::stream;
     734            7 :     out_ = make_array(
     735              :         1 + // header
     736              :         2); // tmp
     737              :     //if(! cod_)
     738              :     {
     739            7 :         tmp0_ = { ws_.data(), ws_.size() };
     740            7 :         if(tmp0_.capacity() <
     741              :                 18 +    // chunk size
     742              :                 1 +     // body (1 byte)
     743              :                 2 +     // CRLF
     744              :                 5)      // final chunk
     745            0 :             detail::throw_length_error();
     746              :     }
     747              : #if 0
     748              :     else
     749              :     {
     750              :         auto const n = ws_.size() / 2;
     751              :         tmp0_ = { ws_.data(), n };
     752              :         ws_.reserve(n);
     753              : 
     754              :         // Buffer is too small
     755              :         if(ws_.size() < 1)
     756              :             detail::throw_length_error();
     757              : 
     758              :         tmp1_ = { ws_.data(), ws_.size() };
     759              :     }
     760              : #endif
     761              : 
     762            7 :     hp_ = &out_[0];
     763            7 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     764              : 
     765            7 :     more_ = true;
     766              : 
     767            7 :     return stream{*this};
     768              : }
     769              : 
     770              : //------------------------------------------------
     771              : 
     772              : std::size_t
     773          140 : serializer::
     774              : stream::
     775              : capacity() const noexcept
     776              : {
     777          140 :     return sr_->tmp0_.capacity();
     778              : }
     779              : 
     780              : std::size_t
     781          132 : serializer::
     782              : stream::
     783              : size() const noexcept
     784              : {
     785          132 :     return sr_->tmp0_.size();
     786              : }
     787              : 
     788              : bool
     789           66 : serializer::
     790              : stream::
     791              : is_full() const noexcept
     792              : {
     793           66 :     if( sr_->is_chunked_ )
     794           36 :         return capacity() < detail::chunked_overhead_ + 1;
     795              : 
     796           30 :     return capacity() == 0;
     797              : }
     798              : 
     799              : auto
     800           36 : serializer::
     801              : stream::
     802              : prepare() const ->
     803              :     buffers_type
     804              : {
     805           36 :     if( sr_->is_compressed_ )
     806            0 :         return sr_->tmp1_.prepare(
     807            0 :             sr_->tmp1_.capacity());
     808              : 
     809           36 :     auto n = sr_->tmp0_.capacity();
     810           36 :     if( sr_->is_chunked_ )
     811              :     {
     812              :         // for chunked encoding, we want to unconditionally
     813              :         // reserve space for the complete chunk and the
     814              :         // last-chunk
     815              :         // this enables users to call:
     816              :         //
     817              :         //     stream.commit(n); stream.close();
     818              :         //
     819              :         // without needing to worry about draining the
     820              :         // serializer via `consume()` calls
     821           21 :         if( n < detail::chunked_overhead_ + 1 )
     822            1 :             detail::throw_length_error();
     823              : 
     824           20 :         n -= detail::chunked_overhead_;
     825           20 :         return buffers::sans_prefix(
     826           40 :             sr_->tmp0_.prepare(
     827              :                 detail::chunk_header_len_ + n),
     828           20 :             detail::chunk_header_len_);
     829              :     }
     830              : 
     831           15 :     return sr_->tmp0_.prepare(n);
     832              : }
     833              : 
     834              : void
     835           35 : serializer::
     836              : stream::
     837              : commit(std::size_t n) const
     838              : {
     839           35 :     if( sr_->is_compressed_ )
     840              :     {
     841            0 :         sr_->tmp1_.commit(n);
     842            0 :         return;
     843              :     }
     844              : 
     845           35 :     if(! sr_->is_chunked_ )
     846              :     {
     847           15 :         sr_->tmp0_.commit(n);
     848              :     }
     849              :     else
     850              :     {
     851              :         // Zero sized chunks are not valid. Call close()
     852              :         // if the intent is to signal the end of the body.
     853           20 :         if( n == 0 )
     854            1 :             detail::throw_logic_error();
     855              : 
     856              :         // TODO: this needs to be relocated
     857              :         //
     858           19 :         auto m = n + detail::chunk_header_len_;
     859           19 :         auto dest = sr_->tmp0_.prepare(m);
     860           19 :         write_chunk_header(
     861           19 :             buffers::prefix(
     862              :                 dest, detail::chunk_header_len_),
     863              :             n);
     864           19 :         sr_->tmp0_.commit(m);
     865           19 :         write_chunk_close(sr_->tmp0_);
     866              :     }
     867              : }
     868              : 
     869              : void
     870            9 : serializer::
     871              : stream::
     872              : close() const
     873              : {
     874              :     // Precondition violation
     875            9 :     if(! sr_->more_ )
     876            4 :         detail::throw_logic_error();
     877              : 
     878            5 :     if( sr_->is_chunked_ && !sr_->is_compressed_ )
     879            3 :         write_last_chunk(sr_->tmp0_);
     880              : 
     881            5 :     sr_->more_ = false;
     882            5 : }
     883              : 
     884              : //------------------------------------------------
     885              : 
     886              : } // http_proto
     887              : } // boost
        

Generated by: LCOV version 2.1