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-02-25 19:48:06 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             :     result<const_buffers_type>
     111             : {
     112             :     // Precondition violation
     113          14 :     if(is_done_)
     114           0 :         detail::throw_logic_error();
     115             : 
     116             :     // Expect: 100-continue
     117          14 :     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          10 :     if(st_ == style::empty)
     127             :     {
     128           9 :         return const_buffers_type(
     129           3 :             out_.data(),
     130           3 :             out_.size());
     131             :     }
     132             : 
     133           7 :     if(st_ == style::buffers)
     134             :     {
     135           9 :         return const_buffers_type(
     136           3 :             out_.data(),
     137           3 :             out_.size());
     138             :     }
     139             : 
     140           4 :     if(st_ == style::source)
     141             :     {
     142           4 :         if(! is_chunked_)
     143             :         {
     144           3 :             auto rv = src_->read(
     145           0 :                 tmp0_.prepare(
     146           3 :                     tmp0_.capacity() -
     147           3 :                         tmp0_.size()));
     148           3 :             tmp0_.commit(rv.bytes);
     149           3 :             if(rv.ec.failed())
     150           0 :                 return rv.ec;
     151           3 :             more_ = ! rv.finished;
     152             :         }
     153             :         else
     154             :         {
     155           1 :             if((tmp0_.capacity() -
     156           1 :                     tmp0_.size()) >
     157             :                 chunked_overhead_)
     158             :             {
     159           1 :                 auto dest = tmp0_.prepare(18);
     160           1 :                 write_chunk_header(dest, 0);
     161           1 :                 tmp0_.commit(18);
     162           1 :                 auto rv = src_->read(
     163           0 :                     tmp0_.prepare(
     164           1 :                         tmp0_.capacity() -
     165             :                             2 - // CRLF
     166           1 :                             5 - // final chunk
     167           1 :                             tmp0_.size()));
     168           1 :                 tmp0_.commit(rv.bytes);
     169             :                 // VFALCO FIXME!
     170             :                 //if(rv.bytes == 0)
     171             :                     //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             :     }
     322          17 : }
     323             : 
     324             : void
     325           4 : serializer::
     326             : start_empty(
     327             :     message_view_base const& m)
     328             : {
     329           4 :     start_init(m);
     330             : 
     331           4 :     st_ = style::empty;
     332             : 
     333           4 :     if(! is_chunked_)
     334             :     {
     335             :         out_ = make_array(
     336           3 :             1); // header
     337             :     }
     338             :     else
     339             :     {
     340             :         out_ = make_array(
     341             :             1 + // header
     342           1 :             1); // final chunk
     343             : 
     344             :         // Buffer is too small
     345           1 :         if(ws_.size() < 5)
     346           0 :             detail::throw_length_error();
     347             : 
     348             :         buffers::mutable_buffer dest(
     349           1 :             ws_.data(), 5);
     350           1 :         buffers::buffer_copy(
     351             :             dest,
     352           1 :             buffers::const_buffer(
     353             :                 "0\r\n\r\n", 5));
     354           1 :         out_[1] = dest;
     355             :     }
     356             : 
     357           4 :     hp_ = &out_[0];
     358           4 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     359           4 : }
     360             : 
     361             : void
     362           7 : serializer::
     363             : start_buffers(
     364             :     message_view_base const& m)
     365             : {
     366           7 :     st_ = style::buffers;
     367             : 
     368           7 :     if(! is_chunked_)
     369             :     {
     370             :         //if(! cod_)
     371             :         {
     372             :             out_ = make_array(
     373             :                 1 +             // header
     374           6 :                 buf_.size());   // body
     375          12 :             copy(&out_[1],
     376           6 :                 buf_.data(), buf_.size());
     377             :         }
     378             : #if 0
     379             :         else
     380             :         {
     381             :             out_ = make_array(
     382             :                 1 + // header
     383             :                 2); // tmp1
     384             :         }
     385             : #endif
     386             :     }
     387             :     else
     388             :     {
     389             :         //if(! cod_)
     390             :         {
     391             :             out_ = make_array(
     392             :                 1 +             // header
     393             :                 1 +             // chunk size
     394           1 :                 buf_.size() +   // body
     395           1 :                 1);             // final chunk
     396           2 :             copy(&out_[2],
     397           1 :                 buf_.data(), buf_.size());
     398             : 
     399             :             // Buffer is too small
     400           1 :             if(ws_.size() < 18 + 7)
     401           0 :                 detail::throw_length_error();
     402           1 :             buffers::mutable_buffer s1(ws_.data(), 18);
     403           1 :             buffers::mutable_buffer s2(ws_.data(), 18 + 7);
     404           1 :             s2 += 18; // VFALCO HACK
     405           1 :             write_chunk_header(
     406             :                 s1,
     407           1 :                 buffers::buffer_size(buf_));
     408           1 :             buffers::buffer_copy(s2, buffers::const_buffer(
     409             :                 "\r\n"
     410             :                 "0\r\n"
     411             :                 "\r\n", 7));
     412           1 :             out_[1] = s1;
     413           1 :             out_[out_.size() - 1] = s2;
     414             :         }
     415             : #if 0
     416             :         else
     417             :         {
     418             :             out_ = make_array(
     419             :                 1 +     // header
     420             :                 2);     // tmp1
     421             :         }
     422             : #endif
     423             :     }
     424             : 
     425           7 :     hp_ = &out_[0];
     426           7 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     427           7 : }
     428             : 
     429             : void
     430           6 : serializer::
     431             : start_source(
     432             :     message_view_base const& m,
     433             :     buffers::source* src)
     434             : {
     435           6 :     st_ = style::source;
     436           6 :     src_ = src;
     437             :     out_ = make_array(
     438             :         1 + // header
     439           6 :         2); // tmp
     440             :     //if(! cod_)
     441             :     {
     442             :         buffers::buffered_base::allocator a(
     443           6 :             ws_.data(), ws_.size()/2, false);
     444           6 :         src->init(a);
     445           6 :         ws_.reserve_front(a.size_used());
     446             : 
     447           6 :         tmp0_ = { ws_.data(), ws_.size() };
     448           6 :         if(tmp0_.capacity() <
     449             :                 18 +    // chunk size
     450             :                 1 +     // body (1 byte)
     451             :                 2 +     // CRLF
     452             :                 5)      // final chunk
     453           0 :             detail::throw_length_error();
     454             :     }
     455             : #if 0
     456             :     else
     457             :     {
     458             :         buffers::buffered_base::allocator a(
     459             :             ws_.data(), ws_.size()/3, false);
     460             :         src->init(a);
     461             :         ws_.reserve(a.size_used());
     462             : 
     463             :         auto const n = ws_.size() / 2;
     464             : 
     465             :         tmp0_ = { ws_.data(), ws_.size() / 2 };
     466             :         ws_.reserve(n);
     467             : 
     468             :         // Buffer is too small
     469             :         if(ws_.size() < 1)
     470             :             detail::throw_length_error();
     471             : 
     472             :         tmp1_ = { ws_.data(), ws_.size() };
     473             :     }
     474             : #endif
     475             : 
     476           6 :     hp_ = &out_[0];
     477           6 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     478           6 : }
     479             : 
     480             : auto
     481           0 : serializer::
     482             : start_stream(
     483             :     message_view_base const& m) ->
     484             :         stream
     485             : {
     486           0 :     start_init(m);
     487             : 
     488           0 :     st_ = style::stream;
     489             :     out_ = make_array(
     490             :         1 + // header
     491           0 :         2); // tmp
     492             :     //if(! cod_)
     493             :     {
     494           0 :         tmp0_ = { ws_.data(), ws_.size() };
     495           0 :         if(tmp0_.capacity() <
     496             :                 18 +    // chunk size
     497             :                 1 +     // body (1 byte)
     498             :                 2 +     // CRLF
     499             :                 5)      // final chunk
     500           0 :             detail::throw_length_error();
     501             :     }
     502             : #if 0
     503             :     else
     504             :     {
     505             :         auto const n = ws_.size() / 2;
     506             :         tmp0_ = { ws_.data(), n };
     507             :         ws_.reserve(n);
     508             : 
     509             :         // Buffer is too small
     510             :         if(ws_.size() < 1)
     511             :             detail::throw_length_error();
     512             : 
     513             :         tmp1_ = { ws_.data(), ws_.size() };
     514             :     }
     515             : #endif
     516             : 
     517           0 :     hp_ = &out_[0];
     518           0 :     *hp_ = { m.ph_->cbuf, m.ph_->size };
     519             : 
     520           0 :     more_ = true;
     521             : 
     522           0 :     return stream{*this};
     523             : }
     524             : 
     525             : //------------------------------------------------
     526             : 
     527             : std::size_t
     528           0 : serializer::
     529             : stream::
     530             : capacity() const
     531             : {
     532           0 :     auto const n = 
     533             :         chunked_overhead_ +
     534             :             2 + // CRLF
     535             :             5;  // final chunk
     536           0 :     return sr_->tmp0_.capacity() - n; // VFALCO ?
     537             : }
     538             : 
     539             : std::size_t
     540           0 : serializer::
     541             : stream::
     542             : size() const
     543             : {
     544           0 :     return sr_->tmp0_.size();
     545             : }
     546             : 
     547             : auto
     548           0 : serializer::
     549             : stream::
     550             : prepare(
     551             :     std::size_t n) const ->
     552             :         buffers_type
     553             : {
     554           0 :     return sr_->tmp0_.prepare(n);
     555             : }
     556             : 
     557             : void
     558           0 : serializer::
     559             : stream::
     560             : commit(std::size_t n) const
     561             : {
     562           0 :     sr_->tmp0_.commit(n);
     563           0 : }
     564             : 
     565             : void
     566           0 : serializer::
     567             : stream::
     568             : close() const
     569             : {
     570             :     // Precondition violation
     571           0 :     if(! sr_->more_)
     572           0 :         detail::throw_logic_error();
     573           0 :     sr_->more_ = false;
     574           0 : }
     575             : 
     576             : //------------------------------------------------
     577             : 
     578             : } // http_proto
     579             : } // boost
     580             : 
     581             : #endif

Generated by: LCOV version 1.15