LCOV - code coverage report
Current view: top level - http_proto/impl - serializer.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 167 248 67.3 %
Date: 2023-02-10 23:49:12 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/codec.hpp>
      15             : #include <boost/http_proto/detail/except.hpp>
      16             : #include <boost/buffers/buffer_copy.hpp>
      17             : #include <boost/buffers/buffer_size.hpp>
      18             : #include <boost/core/ignore_unused.hpp>
      19             : #include <stddef.h>
      20             : 
      21             : namespace boost {
      22             : namespace http_proto {
      23             : 
      24             : //------------------------------------------------
      25             : 
      26             : void
      27           0 : consume_buffers(
      28             :     buffers::const_buffer*& p,
      29             :     std::size_t& n,
      30             :     std::size_t bytes)
      31             : {
      32           0 :     while(n > 0)
      33             :     {
      34           0 :         if(bytes < p->size())
      35             :         {
      36           0 :             *p += bytes;
      37           0 :             return;
      38             :         }
      39           0 :         bytes -= p->size();
      40           0 :         ++p;
      41           0 :         --n;
      42             :     }
      43             : 
      44             :     // Precondition violation
      45           0 :     if(bytes > 0)
      46           0 :         detail::throw_invalid_argument();
      47             : }
      48             : 
      49             : template<class MutableBuffers>
      50             : void
      51           3 : write_chunk_header(
      52             :     MutableBuffers const& dest0,
      53             :     std::size_t size) noexcept
      54             : {
      55             :     static constexpr char hexdig[] =
      56             :         "0123456789ABCDEF";
      57             :     char buf[18];
      58           3 :     auto p = buf + 16;
      59          51 :     for(std::size_t i = 16; i--;)
      60             :     {
      61          48 :         *--p = hexdig[size & 0xf];
      62          48 :         size >>= 4;
      63             :     }
      64           3 :     buf[16] = '\r';
      65           3 :     buf[17] = '\n';
      66           3 :     auto n = buffers::buffer_copy(
      67             :         dest0,
      68             :         buffers::const_buffer(
      69             :             buf, sizeof(buf)));
      70             :     ignore_unused(n);
      71           3 :     BOOST_ASSERT(n == 18);
      72           3 :     BOOST_ASSERT(
      73             :         buffers::buffer_size(dest0) == n);
      74           3 : }
      75             : 
      76             : //------------------------------------------------
      77             : 
      78          11 : serializer::
      79          77 : ~serializer()
      80             : {
      81          11 : }
      82             : 
      83          10 : serializer::
      84          10 : serializer()
      85          10 :     : serializer(65536)
      86             : {
      87          10 : }
      88             : 
      89             : serializer::
      90             : serializer(
      91             :     serializer&&) noexcept = default;
      92             : 
      93          11 : serializer::
      94             : serializer(
      95          11 :     std::size_t buffer_size)
      96          11 :     : ws_(buffer_size)
      97             : {
      98          11 : }
      99             : 
     100             : void
     101           0 : serializer::
     102             : reset() noexcept
     103             : {
     104           0 : }
     105             : 
     106             : //------------------------------------------------
     107             : 
     108             : auto
     109          14 : serializer::
     110             : prepare() ->
     111             :     result<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           1 :                 if(rv.bytes == 0)
     171           0 :                     tmp0_.uncommit(18); // undo
     172           1 :                 if(rv.ec.failed())
     173           0 :                     return rv.ec;
     174           1 :                 if(rv.bytes > 0)
     175             :                 {
     176             :                     // rewrite with correct size
     177           1 :                     write_chunk_header(
     178             :                         dest, rv.bytes);
     179             :                     // terminate chunk
     180           1 :                     tmp0_.commit(
     181             :                         buffers::buffer_copy(
     182           1 :                             tmp0_.prepare(2),
     183           2 :                             buffers::const_buffer(
     184             :                                 "\r\n", 2)));
     185             :                 }
     186           1 :                 if(rv.finished)
     187             :                 {
     188           1 :                     tmp0_.commit(
     189             :                         buffers::buffer_copy(
     190           1 :                             tmp0_.prepare(5),
     191           2 :                             buffers::const_buffer(
     192             :                                 "0\r\n\r\n", 5)));
     193             :                 }
     194           1 :                 more_ = ! rv.finished;
     195             :             }
     196             :         }
     197             : 
     198           4 :         std::size_t n = 0;
     199           4 :         if(out_.data() == hp_)
     200           3 :             ++n;
     201          12 :         for(buffers::const_buffer const& b : tmp0_.data())
     202           8 :             out_[n++] = b;
     203             : 
     204          12 :         return const_buffers_type(
     205           4 :             out_.data(),
     206           4 :             out_.size());
     207             :     }
     208             : 
     209           0 :     if(st_ == style::stream)
     210             :     {
     211           0 :         std::size_t n = 0;
     212           0 :         if(out_.data() == hp_)
     213           0 :             ++n;
     214           0 :         if(tmp0_.size() == 0 && more_)
     215             :         {
     216           0 :             BOOST_HTTP_PROTO_RETURN_EC(
     217             :                 error::need_data);
     218             :         }
     219           0 :         for(buffers::const_buffer const& b : tmp0_.data())
     220           0 :             out_[n++] = b;
     221             : 
     222           0 :         return const_buffers_type(
     223           0 :             out_.data(),
     224           0 :             out_.size());
     225             :     }
     226             : 
     227             :     // should never get here
     228           0 :     detail::throw_logic_error();
     229             : }
     230             : 
     231             : void
     232          12 : serializer::
     233             : consume(
     234             :     std::size_t n)
     235             : {
     236             :     // Precondition violation
     237          12 :     if(is_done_)
     238           0 :         detail::throw_logic_error();
     239             : 
     240          12 :     if(is_expect_continue_)
     241             :     {
     242             :         // Cannot consume more than
     243             :         // the header on 100-continue
     244           2 :         if(n > hp_->size())
     245           0 :             detail::throw_invalid_argument();
     246             : 
     247           2 :         out_.consume(n);
     248           2 :         return;
     249             :     }
     250          10 :     else if(out_.data() == hp_)
     251             :     {
     252             :         // consume header
     253           8 :         if(n < hp_->size())
     254             :         {
     255           0 :             out_.consume(n);
     256           0 :             return;
     257             :         }
     258           8 :         n -= hp_->size();
     259           8 :         out_.consume(hp_->size());
     260             :     }
     261             : 
     262          10 :     switch(st_)
     263             :     {
     264           3 :     default:
     265             :     case style::empty:
     266           3 :         out_.consume(n);
     267           3 :         if(out_.empty())
     268           3 :             is_done_ = true;
     269           3 :         return;
     270             : 
     271           3 :     case style::buffers:
     272           3 :         out_.consume(n);
     273           3 :         if(out_.empty())
     274           3 :             is_done_ = true;
     275           3 :         return;
     276             : 
     277           4 :     case style::source:
     278             :     case style::stream:
     279           4 :         tmp0_.consume(n);
     280           8 :         if( tmp0_.size() == 0 &&
     281           4 :                 ! more_)
     282           4 :             is_done_ = true;
     283           4 :         return;
     284             :     }
     285             : }
     286             : 
     287             : //------------------------------------------------
     288             : 
     289             : void
     290          14 : serializer::
     291             : copy(
     292             :     buffers::const_buffer* dest,
     293             :     buffers::const_buffer const* src,
     294             :     std::size_t n) noexcept
     295             : {
     296          14 :     while(n--)
     297           7 :         *dest++ = *src++;
     298           7 : }
     299             : 
     300             : void
     301          17 : serializer::
     302             : start_init(
     303             :     message_view_base const& m)
     304             : {
     305          17 :     ws_.clear();
     306             : 
     307             :     // VFALCO what do we do with
     308             :     // metadata error code failures?
     309             :     // m.ph_->md.maybe_throw();
     310             : 
     311          17 :     is_done_ = false;
     312             : 
     313          17 :     is_expect_continue_ =
     314          17 :         m.ph_->md.expect.is_100_continue;
     315             : 
     316             :     // Transfer-Encoding
     317             :     {
     318          17 :         auto const& te =
     319          17 :             m.ph_->md.transfer_encoding;
     320          17 :         is_chunked_ = te.is_chunked;
     321          17 :         cod_ = nullptr;
     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           6 :         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             :         else
     380             :         {
     381             :             out_ = make_array(
     382             :                 1 + // header
     383           0 :                 2); // tmp1
     384             :         }
     385             :     }
     386             :     else
     387             :     {
     388           1 :         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             :         else
     415             :         {
     416             :             out_ = make_array(
     417             :                 1 +     // header
     418           0 :                 2);     // tmp1
     419             :         }
     420             :     }
     421             : 
     422           7 :     hp_ = &out_[0];
     423           7 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     424           7 : }
     425             : 
     426             : void
     427           6 : serializer::
     428             : start_source(
     429             :     message_view_base const& m,
     430             :     buffers::source* src)
     431             : {
     432           6 :     st_ = style::source;
     433           6 :     src_ = src;
     434             :     out_ = make_array(
     435             :         1 + // header
     436           6 :         2); // tmp
     437           6 :     if(! cod_)
     438             :     {
     439             :         buffers::buffered_base::allocator a(
     440           6 :             ws_.data(), ws_.size()/2, false);
     441           6 :         src->init(a);
     442           6 :         ws_.reserve(a.size_used());
     443             : 
     444           6 :         tmp0_ = { ws_.data(), ws_.size() };
     445           6 :         if(tmp0_.capacity() <
     446             :                 18 +    // chunk size
     447             :                 1 +     // body (1 byte)
     448             :                 2 +     // CRLF
     449             :                 5)      // final chunk
     450           0 :             detail::throw_length_error();
     451             :     }
     452             :     else
     453             :     {
     454             :         buffers::buffered_base::allocator a(
     455           0 :             ws_.data(), ws_.size()/3, false);
     456           0 :         src->init(a);
     457           0 :         ws_.reserve(a.size_used());
     458             : 
     459           0 :         auto const n = ws_.size() / 2;
     460             : 
     461           0 :         tmp0_ = { ws_.data(), ws_.size() / 2 };
     462           0 :         ws_.reserve(n);
     463             : 
     464             :         // Buffer is too small
     465           0 :         if(ws_.size() < 1)
     466           0 :             detail::throw_length_error();
     467             : 
     468           0 :         tmp1_ = { ws_.data(), ws_.size() };
     469             :     }
     470             : 
     471           6 :     hp_ = &out_[0];
     472           6 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     473           6 : }
     474             : 
     475             : auto
     476           0 : serializer::
     477             : start_stream(
     478             :     message_view_base const& m) ->
     479             :         stream
     480             : {
     481           0 :     start_init(m);
     482             : 
     483           0 :     st_ = style::stream;
     484             :     out_ = make_array(
     485             :         1 + // header
     486           0 :         2); // tmp
     487           0 :     if(! cod_)
     488             :     {
     489           0 :         tmp0_ = { ws_.data(), ws_.size() };
     490           0 :         if(tmp0_.capacity() <
     491             :                 18 +    // chunk size
     492             :                 1 +     // body (1 byte)
     493             :                 2 +     // CRLF
     494             :                 5)      // final chunk
     495           0 :             detail::throw_length_error();
     496             :     }
     497             :     else
     498             :     {
     499           0 :         auto const n = ws_.size() / 2;
     500           0 :         tmp0_ = { ws_.data(), n };
     501           0 :         ws_.reserve(n);
     502             : 
     503             :         // Buffer is too small
     504           0 :         if(ws_.size() < 1)
     505           0 :             detail::throw_length_error();
     506             : 
     507           0 :         tmp1_ = { ws_.data(), ws_.size() };
     508             :     }
     509             : 
     510           0 :     hp_ = &out_[0];
     511           0 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     512             : 
     513           0 :     more_ = true;
     514             : 
     515           0 :     return stream{*this};
     516             : }
     517             : 
     518             : //------------------------------------------------
     519             : 
     520             : std::size_t
     521           0 : serializer::
     522             : stream::
     523             : capacity() const
     524             : {
     525           0 :     auto const n = 
     526             :         chunked_overhead_ +
     527             :             2 + // CRLF
     528             :             5;  // final chunk
     529           0 :     return sr_->tmp0_.capacity() - n; // VFALCO ?
     530             : }
     531             : 
     532             : std::size_t
     533           0 : serializer::
     534             : stream::
     535             : size() const
     536             : {
     537           0 :     return sr_->tmp0_.size();
     538             : }
     539             : 
     540             : auto
     541           0 : serializer::
     542             : stream::
     543             : prepare(
     544             :     std::size_t n) const ->
     545             :         buffers_type
     546             : {
     547           0 :     return sr_->tmp0_.prepare(n);
     548             : }
     549             : 
     550             : void
     551           0 : serializer::
     552             : stream::
     553             : commit(std::size_t n) const
     554             : {
     555           0 :     sr_->tmp0_.commit(n);
     556           0 : }
     557             : 
     558             : void
     559           0 : serializer::
     560             : stream::
     561             : close() const
     562             : {
     563             :     // Precondition violation
     564           0 :     if(! sr_->more_)
     565           0 :         detail::throw_logic_error();
     566           0 :     sr_->more_ = false;
     567           0 : }
     568             : 
     569             : //------------------------------------------------
     570             : 
     571             : } // http_proto
     572             : } // boost
     573             : 
     574             : #endif

Generated by: LCOV version 1.15