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

Generated by: LCOV version 1.15