LCOV - code coverage report
Current view: top level - libs/http_proto/src - serializer.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 182 242 75.2 %
Date: 2024-03-19 15:22:00 Functions: 14 22 63.6 %

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

Generated by: LCOV version 1.15