LCOV - code coverage report
Current view: top level - libs/http_proto/src/serializer.cpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 92.0 % 251 231
Test Date: 2024-06-05 19:28:02 Functions: 87.5 % 24 21

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

Generated by: LCOV version 2.1