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

Generated by: LCOV version 1.15