LCOV - code coverage report
Current view: top level - http_proto/impl - serializer.ipp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 208 286 72.7 %
Date: 2023-01-15 07:18:31 Functions: 15 26 57.7 %

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

Generated by: LCOV version 1.15