LCOV - code coverage report
Current view: top level - libs/http_proto/src - serializer.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 162 224 72.3 %
Date: 2024-02-23 18:36:38 Functions: 12 20 60.0 %

          Line data    Source code
       1             : //
       2             : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
       3             : //
       4             : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5             : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6             : //
       7             : // Official repository: https://github.com/cppalliance/http_proto
       8             : //
       9             : 
      10             : #include <boost/http_proto/serializer.hpp>
      11             : #include <boost/http_proto/message_view_base.hpp>
      12             : #include <boost/http_proto/detail/except.hpp>
      13             : #include <boost/buffers/buffer_copy.hpp>
      14             : #include <boost/buffers/buffer_size.hpp>
      15             : #include <boost/core/ignore_unused.hpp>
      16             : #include <stddef.h>
      17             : 
      18             : namespace boost {
      19             : namespace http_proto {
      20             : 
      21             : //------------------------------------------------
      22             : 
      23             : void
      24           0 : consume_buffers(
      25             :     buffers::const_buffer*& p,
      26             :     std::size_t& n,
      27             :     std::size_t bytes)
      28             : {
      29           0 :     while(n > 0)
      30             :     {
      31           0 :         if(bytes < p->size())
      32             :         {
      33           0 :             *p += bytes;
      34           0 :             return;
      35             :         }
      36           0 :         bytes -= p->size();
      37           0 :         ++p;
      38           0 :         --n;
      39             :     }
      40             : 
      41             :     // Precondition violation
      42           0 :     if(bytes > 0)
      43           0 :         detail::throw_invalid_argument();
      44             : }
      45             : 
      46             : template<class MutableBuffers>
      47             : void
      48           3 : write_chunk_header(
      49             :     MutableBuffers const& dest0,
      50             :     std::size_t size) noexcept
      51             : {
      52             :     static constexpr char hexdig[] =
      53             :         "0123456789ABCDEF";
      54             :     char buf[18];
      55           3 :     auto p = buf + 16;
      56          51 :     for(std::size_t i = 16; i--;)
      57             :     {
      58          48 :         *--p = hexdig[size & 0xf];
      59          48 :         size >>= 4;
      60             :     }
      61           3 :     buf[16] = '\r';
      62           3 :     buf[17] = '\n';
      63           3 :     auto n = buffers::buffer_copy(
      64             :         dest0,
      65             :         buffers::const_buffer(
      66             :             buf, sizeof(buf)));
      67             :     ignore_unused(n);
      68           3 :     BOOST_ASSERT(n == 18);
      69           3 :     BOOST_ASSERT(
      70             :         buffers::buffer_size(dest0) == n);
      71           3 : }
      72             : 
      73             : //------------------------------------------------
      74             : 
      75          12 : serializer::
      76          12 : ~serializer()
      77             : {
      78          12 : }
      79             : 
      80          10 : serializer::
      81          10 : serializer()
      82          10 :     : serializer(65536)
      83             : {
      84          10 : }
      85             : 
      86             : serializer::
      87             : serializer(
      88             :     serializer&&) noexcept = default;
      89             : 
      90          12 : serializer::
      91             : serializer(
      92          12 :     std::size_t buffer_size)
      93          12 :     : ws_(buffer_size)
      94             : {
      95          12 : }
      96             : 
      97             : void
      98           0 : serializer::
      99             : reset() noexcept
     100             : {
     101           0 : }
     102             : 
     103             : //------------------------------------------------
     104             : 
     105             : auto
     106          14 : serializer::
     107             : prepare() ->
     108             :     system::result<
     109             :         const_buffers_type>
     110             : {
     111             :     // Precondition violation
     112          14 :     if(is_done_)
     113           0 :         detail::throw_logic_error();
     114             : 
     115             :     // Expect: 100-continue
     116          14 :     if(is_expect_continue_)
     117             :     {
     118           4 :         if(out_.data() == hp_)
     119           2 :             return const_buffers_type(hp_, 1);
     120           2 :         is_expect_continue_ = false;
     121           2 :         BOOST_HTTP_PROTO_RETURN_EC(
     122             :             error::expect_100_continue);
     123             :     }
     124             : 
     125          10 :     if(st_ == style::empty)
     126             :     {
     127           9 :         return const_buffers_type(
     128           3 :             out_.data(),
     129           3 :             out_.size());
     130             :     }
     131             : 
     132           7 :     if(st_ == style::buffers)
     133             :     {
     134           9 :         return const_buffers_type(
     135           3 :             out_.data(),
     136           3 :             out_.size());
     137             :     }
     138             : 
     139           4 :     if(st_ == style::source)
     140             :     {
     141           4 :         if(! is_chunked_)
     142             :         {
     143           3 :             auto rv = src_->read(
     144           0 :                 tmp0_.prepare(
     145           3 :                     tmp0_.capacity() -
     146           3 :                         tmp0_.size()));
     147           3 :             tmp0_.commit(rv.bytes);
     148           3 :             if(rv.ec.failed())
     149           0 :                 return rv.ec;
     150           3 :             more_ = ! rv.finished;
     151             :         }
     152             :         else
     153             :         {
     154           1 :             if((tmp0_.capacity() -
     155           1 :                     tmp0_.size()) >
     156             :                 chunked_overhead_)
     157             :             {
     158           1 :                 auto dest = tmp0_.prepare(18);
     159           1 :                 write_chunk_header(dest, 0);
     160           1 :                 tmp0_.commit(18);
     161           1 :                 auto rv = src_->read(
     162           0 :                     tmp0_.prepare(
     163           1 :                         tmp0_.capacity() -
     164             :                             2 - // CRLF
     165           1 :                             5 - // final chunk
     166           1 :                             tmp0_.size()));
     167           1 :                 tmp0_.commit(rv.bytes);
     168             :                 // VFALCO FIXME!
     169             :                 //if(rv.bytes == 0)
     170             :                     //tmp0_.uncommit(18); // undo
     171           1 :                 if(rv.ec.failed())
     172           0 :                     return rv.ec;
     173           1 :                 if(rv.bytes > 0)
     174             :                 {
     175             :                     // rewrite with correct size
     176           1 :                     write_chunk_header(
     177             :                         dest, rv.bytes);
     178             :                     // terminate chunk
     179           1 :                     tmp0_.commit(
     180             :                         buffers::buffer_copy(
     181           1 :                             tmp0_.prepare(2),
     182           2 :                             buffers::const_buffer(
     183             :                                 "\r\n", 2)));
     184             :                 }
     185           1 :                 if(rv.finished)
     186             :                 {
     187           1 :                     tmp0_.commit(
     188             :                         buffers::buffer_copy(
     189           1 :                             tmp0_.prepare(5),
     190           2 :                             buffers::const_buffer(
     191             :                                 "0\r\n\r\n", 5)));
     192             :                 }
     193           1 :                 more_ = ! rv.finished;
     194             :             }
     195             :         }
     196             : 
     197           4 :         std::size_t n = 0;
     198           4 :         if(out_.data() == hp_)
     199           3 :             ++n;
     200          12 :         for(buffers::const_buffer const& b : tmp0_.data())
     201           8 :             out_[n++] = b;
     202             : 
     203          12 :         return const_buffers_type(
     204           4 :             out_.data(),
     205           4 :             out_.size());
     206             :     }
     207             : 
     208           0 :     if(st_ == style::stream)
     209             :     {
     210           0 :         std::size_t n = 0;
     211           0 :         if(out_.data() == hp_)
     212           0 :             ++n;
     213           0 :         if(tmp0_.size() == 0 && more_)
     214             :         {
     215           0 :             BOOST_HTTP_PROTO_RETURN_EC(
     216             :                 error::need_data);
     217             :         }
     218           0 :         for(buffers::const_buffer const& b : tmp0_.data())
     219           0 :             out_[n++] = b;
     220             : 
     221           0 :         return const_buffers_type(
     222           0 :             out_.data(),
     223           0 :             out_.size());
     224             :     }
     225             : 
     226             :     // should never get here
     227           0 :     detail::throw_logic_error();
     228             : }
     229             : 
     230             : void
     231          12 : serializer::
     232             : consume(
     233             :     std::size_t n)
     234             : {
     235             :     // Precondition violation
     236          12 :     if(is_done_)
     237           0 :         detail::throw_logic_error();
     238             : 
     239          12 :     if(is_expect_continue_)
     240             :     {
     241             :         // Cannot consume more than
     242             :         // the header on 100-continue
     243           2 :         if(n > hp_->size())
     244           0 :             detail::throw_invalid_argument();
     245             : 
     246           2 :         out_.consume(n);
     247           2 :         return;
     248             :     }
     249          10 :     else if(out_.data() == hp_)
     250             :     {
     251             :         // consume header
     252           8 :         if(n < hp_->size())
     253             :         {
     254           0 :             out_.consume(n);
     255           0 :             return;
     256             :         }
     257           8 :         n -= hp_->size();
     258           8 :         out_.consume(hp_->size());
     259             :     }
     260             : 
     261          10 :     switch(st_)
     262             :     {
     263           3 :     default:
     264             :     case style::empty:
     265           3 :         out_.consume(n);
     266           3 :         if(out_.empty())
     267           3 :             is_done_ = true;
     268           3 :         return;
     269             : 
     270           3 :     case style::buffers:
     271           3 :         out_.consume(n);
     272           3 :         if(out_.empty())
     273           3 :             is_done_ = true;
     274           3 :         return;
     275             : 
     276           4 :     case style::source:
     277             :     case style::stream:
     278           4 :         tmp0_.consume(n);
     279           8 :         if( tmp0_.size() == 0 &&
     280           4 :                 ! more_)
     281           4 :             is_done_ = true;
     282           4 :         return;
     283             :     }
     284             : }
     285             : 
     286             : //------------------------------------------------
     287             : 
     288             : void
     289          14 : serializer::
     290             : copy(
     291             :     buffers::const_buffer* dest,
     292             :     buffers::const_buffer const* src,
     293             :     std::size_t n) noexcept
     294             : {
     295          14 :     while(n--)
     296           7 :         *dest++ = *src++;
     297           7 : }
     298             : 
     299             : void
     300          17 : serializer::
     301             : start_init(
     302             :     message_view_base const& m)
     303             : {
     304          17 :     ws_.clear();
     305             : 
     306             :     // VFALCO what do we do with
     307             :     // metadata error code failures?
     308             :     // m.ph_->md.maybe_throw();
     309             : 
     310          17 :     is_done_ = false;
     311             : 
     312          17 :     is_expect_continue_ =
     313          17 :         m.ph_->md.expect.is_100_continue;
     314             : 
     315             :     // Transfer-Encoding
     316             :     {
     317          17 :         auto const& te =
     318          17 :             m.ph_->md.transfer_encoding;
     319          17 :         is_chunked_ = te.is_chunked;
     320             :     }
     321          17 : }
     322             : 
     323             : void
     324           4 : serializer::
     325             : start_empty(
     326             :     message_view_base const& m)
     327             : {
     328           4 :     start_init(m);
     329             : 
     330           4 :     st_ = style::empty;
     331             : 
     332           4 :     if(! is_chunked_)
     333             :     {
     334             :         out_ = make_array(
     335           3 :             1); // header
     336             :     }
     337             :     else
     338             :     {
     339             :         out_ = make_array(
     340             :             1 + // header
     341           1 :             1); // final chunk
     342             : 
     343             :         // Buffer is too small
     344           1 :         if(ws_.size() < 5)
     345           0 :             detail::throw_length_error();
     346             : 
     347             :         buffers::mutable_buffer dest(
     348           1 :             ws_.data(), 5);
     349           1 :         buffers::buffer_copy(
     350             :             dest,
     351           1 :             buffers::const_buffer(
     352             :                 "0\r\n\r\n", 5));
     353           1 :         out_[1] = dest;
     354             :     }
     355             : 
     356           4 :     hp_ = &out_[0];
     357           4 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     358           4 : }
     359             : 
     360             : void
     361           7 : serializer::
     362             : start_buffers(
     363             :     message_view_base const& m)
     364             : {
     365           7 :     st_ = style::buffers;
     366             : 
     367           7 :     if(! is_chunked_)
     368             :     {
     369             :         //if(! cod_)
     370             :         {
     371             :             out_ = make_array(
     372             :                 1 +             // header
     373           6 :                 buf_.size());   // body
     374          12 :             copy(&out_[1],
     375           6 :                 buf_.data(), buf_.size());
     376             :         }
     377             : #if 0
     378             :         else
     379             :         {
     380             :             out_ = make_array(
     381             :                 1 + // header
     382             :                 2); // tmp1
     383             :         }
     384             : #endif
     385             :     }
     386             :     else
     387             :     {
     388             :         //if(! cod_)
     389             :         {
     390             :             out_ = make_array(
     391             :                 1 +             // header
     392             :                 1 +             // chunk size
     393           1 :                 buf_.size() +   // body
     394           1 :                 1);             // final chunk
     395           2 :             copy(&out_[2],
     396           1 :                 buf_.data(), buf_.size());
     397             : 
     398             :             // Buffer is too small
     399           1 :             if(ws_.size() < 18 + 7)
     400           0 :                 detail::throw_length_error();
     401           1 :             buffers::mutable_buffer s1(ws_.data(), 18);
     402           1 :             buffers::mutable_buffer s2(ws_.data(), 18 + 7);
     403           1 :             s2 += 18; // VFALCO HACK
     404           1 :             write_chunk_header(
     405             :                 s1,
     406           1 :                 buffers::buffer_size(buf_));
     407           1 :             buffers::buffer_copy(s2, buffers::const_buffer(
     408             :                 "\r\n"
     409             :                 "0\r\n"
     410             :                 "\r\n", 7));
     411           1 :             out_[1] = s1;
     412           1 :             out_[out_.size() - 1] = s2;
     413             :         }
     414             : #if 0
     415             :         else
     416             :         {
     417             :             out_ = make_array(
     418             :                 1 +     // header
     419             :                 2);     // tmp1
     420             :         }
     421             : #endif
     422             :     }
     423             : 
     424           7 :     hp_ = &out_[0];
     425           7 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     426           7 : }
     427             : 
     428             : void
     429           6 : serializer::
     430             : start_source(
     431             :     message_view_base const& m,
     432             :     source* src)
     433             : {
     434           6 :     st_ = style::source;
     435           6 :     src_ = src;
     436             :     out_ = make_array(
     437             :         1 + // header
     438           6 :         2); // tmp
     439             :     //if(! cod_)
     440             :     {
     441             :         buffered_base::allocator a(
     442           6 :             ws_.data(), ws_.size()/2, false);
     443           6 :         src->init(a);
     444           6 :         ws_.reserve_front(a.size_used());
     445             : 
     446           6 :         tmp0_ = { ws_.data(), ws_.size() };
     447           6 :         if(tmp0_.capacity() <
     448             :                 18 +    // chunk size
     449             :                 1 +     // body (1 byte)
     450             :                 2 +     // CRLF
     451             :                 5)      // final chunk
     452           0 :             detail::throw_length_error();
     453             :     }
     454             : #if 0
     455             :     else
     456             :     {
     457             :         buffers::buffered_base::allocator a(
     458             :             ws_.data(), ws_.size()/3, false);
     459             :         src->init(a);
     460             :         ws_.reserve(a.size_used());
     461             : 
     462             :         auto const n = ws_.size() / 2;
     463             : 
     464             :         tmp0_ = { ws_.data(), ws_.size() / 2 };
     465             :         ws_.reserve(n);
     466             : 
     467             :         // Buffer is too small
     468             :         if(ws_.size() < 1)
     469             :             detail::throw_length_error();
     470             : 
     471             :         tmp1_ = { ws_.data(), ws_.size() };
     472             :     }
     473             : #endif
     474             : 
     475           6 :     hp_ = &out_[0];
     476           6 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     477           6 : }
     478             : 
     479             : auto
     480           0 : serializer::
     481             : start_stream(
     482             :     message_view_base const& m) ->
     483             :         stream
     484             : {
     485           0 :     start_init(m);
     486             : 
     487           0 :     st_ = style::stream;
     488             :     out_ = make_array(
     489             :         1 + // header
     490           0 :         2); // tmp
     491             :     //if(! cod_)
     492             :     {
     493           0 :         tmp0_ = { ws_.data(), ws_.size() };
     494           0 :         if(tmp0_.capacity() <
     495             :                 18 +    // chunk size
     496             :                 1 +     // body (1 byte)
     497             :                 2 +     // CRLF
     498             :                 5)      // final chunk
     499           0 :             detail::throw_length_error();
     500             :     }
     501             : #if 0
     502             :     else
     503             :     {
     504             :         auto const n = ws_.size() / 2;
     505             :         tmp0_ = { ws_.data(), n };
     506             :         ws_.reserve(n);
     507             : 
     508             :         // Buffer is too small
     509             :         if(ws_.size() < 1)
     510             :             detail::throw_length_error();
     511             : 
     512             :         tmp1_ = { ws_.data(), ws_.size() };
     513             :     }
     514             : #endif
     515             : 
     516           0 :     hp_ = &out_[0];
     517           0 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     518             : 
     519           0 :     more_ = true;
     520             : 
     521           0 :     return stream{*this};
     522             : }
     523             : 
     524             : //------------------------------------------------
     525             : 
     526             : std::size_t
     527           0 : serializer::
     528             : stream::
     529             : capacity() const
     530             : {
     531           0 :     auto const n = 
     532             :         chunked_overhead_ +
     533             :             2 + // CRLF
     534             :             5;  // final chunk
     535           0 :     return sr_->tmp0_.capacity() - n; // VFALCO ?
     536             : }
     537             : 
     538             : std::size_t
     539           0 : serializer::
     540             : stream::
     541             : size() const
     542             : {
     543           0 :     return sr_->tmp0_.size();
     544             : }
     545             : 
     546             : auto
     547           0 : serializer::
     548             : stream::
     549             : prepare(
     550             :     std::size_t n) const ->
     551             :         buffers_type
     552             : {
     553           0 :     return sr_->tmp0_.prepare(n);
     554             : }
     555             : 
     556             : void
     557           0 : serializer::
     558             : stream::
     559             : commit(std::size_t n) const
     560             : {
     561           0 :     sr_->tmp0_.commit(n);
     562           0 : }
     563             : 
     564             : void
     565           0 : serializer::
     566             : stream::
     567             : close() const
     568             : {
     569             :     // Precondition violation
     570           0 :     if(! sr_->more_)
     571           0 :         detail::throw_logic_error();
     572           0 :     sr_->more_ = false;
     573           0 : }
     574             : 
     575             : //------------------------------------------------
     576             : 
     577             : } // http_proto
     578             : } // boost

Generated by: LCOV version 1.15