LCOV - code coverage report
Current view: top level - http_proto/impl - serializer.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 181 268 67.5 %
Date: 2023-01-26 23:44:13 Functions: 15 24 62.5 %

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

Generated by: LCOV version 1.15