62.85% Lines (247/393) 82.35% Functions (42/51)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3   // Copyright (c) 2024 Christian Mazakas 3   // Copyright (c) 2024 Christian Mazakas
4   // Copyright (c) 2024 Mohammad Nejati 4   // Copyright (c) 2024 Mohammad Nejati
5   // 5   //
6   // Distributed under the Boost Software License, Version 1.0. (See accompanying 6   // Distributed under the Boost Software License, Version 1.0. (See accompanying
7   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8   // 8   //
9   // Official repository: https://github.com/cppalliance/http 9   // Official repository: https://github.com/cppalliance/http
10   // 10   //
11   11  
12   #include <boost/http/detail/circular_buffer.hpp> 12   #include <boost/http/detail/circular_buffer.hpp>
13   #include <boost/http/detail/except.hpp> 13   #include <boost/http/detail/except.hpp>
14   #include <boost/http/detail/header.hpp> 14   #include <boost/http/detail/header.hpp>
15   #include <boost/http/message_base.hpp> 15   #include <boost/http/message_base.hpp>
16   #include <boost/http/serializer.hpp> 16   #include <boost/http/serializer.hpp>
17   17  
18   #include "src/detail/array_of_const_buffers.hpp" 18   #include "src/detail/array_of_const_buffers.hpp"
19   #include "src/detail/brotli_filter_base.hpp" 19   #include "src/detail/brotli_filter_base.hpp"
20   #include "src/detail/buffer_utils.hpp" 20   #include "src/detail/buffer_utils.hpp"
21   #include "src/detail/zlib_filter_base.hpp" 21   #include "src/detail/zlib_filter_base.hpp"
22   22  
23   #include <boost/capy/buffers/buffer_copy.hpp> 23   #include <boost/capy/buffers/buffer_copy.hpp>
24   #include <boost/capy/ex/system_context.hpp> 24   #include <boost/capy/ex/system_context.hpp>
25   #include <boost/core/bit.hpp> 25   #include <boost/core/bit.hpp>
26   #include <boost/core/ignore_unused.hpp> 26   #include <boost/core/ignore_unused.hpp>
27   #include <boost/http/brotli/encode.hpp> 27   #include <boost/http/brotli/encode.hpp>
28   #include <boost/http/zlib/compression_method.hpp> 28   #include <boost/http/zlib/compression_method.hpp>
29   #include <boost/http/zlib/compression_strategy.hpp> 29   #include <boost/http/zlib/compression_strategy.hpp>
30   #include <boost/http/zlib/deflate.hpp> 30   #include <boost/http/zlib/deflate.hpp>
31   #include <boost/http/zlib/error.hpp> 31   #include <boost/http/zlib/error.hpp>
32   #include <boost/http/zlib/flush.hpp> 32   #include <boost/http/zlib/flush.hpp>
33   33  
34   #include <array> 34   #include <array>
35   #include <memory> 35   #include <memory>
36   #include <stddef.h> 36   #include <stddef.h>
37   37  
38   namespace boost { 38   namespace boost {
39   namespace http { 39   namespace http {
40   40  
41   namespace { 41   namespace {
42   42  
43   // Trim n bytes from the front of a 2-element buffer pair, in place. 43   // Trim n bytes from the front of a 2-element buffer pair, in place.
44   // Replaces the pre-#262 `capy::remove_prefix(pair, n)` idiom on a 44   // Replaces the pre-#262 `capy::remove_prefix(pair, n)` idiom on a
45   // 2-element buffer sequence. 45   // 2-element buffer sequence.
46   template<class Buf> 46   template<class Buf>
47   inline void 47   inline void
HITCBC 48   69 trim_prefix_pair(std::array<Buf, 2>& a, std::size_t n) noexcept 48   69 trim_prefix_pair(std::array<Buf, 2>& a, std::size_t n) noexcept
49   { 49   {
HITCBC 50   69 if(n >= a[0].size()) 50   69 if(n >= a[0].size())
51   { 51   {
MISUBC 52   n -= a[0].size(); 52   n -= a[0].size();
MISUBC 53   a[0] = Buf(); 53   a[0] = Buf();
MISUBC 54   if(n >= a[1].size()) 54   if(n >= a[1].size())
MISUBC 55   a[1] = Buf(); 55   a[1] = Buf();
56   else 56   else
MISUBC 57   a[1] = Buf( 57   a[1] = Buf(
58   static_cast<char*>(const_cast<void*>( 58   static_cast<char*>(const_cast<void*>(
MISUBC 59   static_cast<void const*>(a[1].data()))) + n, 59   static_cast<void const*>(a[1].data()))) + n,
MISUBC 60   a[1].size() - n); 60   a[1].size() - n);
61   } 61   }
62   else 62   else
63   { 63   {
HITCBC 64   69 a[0] += n; 64   69 a[0] += n;
65   } 65   }
HITCBC 66   69 } 66   69 }
67   67  
68   // Trim n bytes from the back of a 2-element buffer pair, in place. 68   // Trim n bytes from the back of a 2-element buffer pair, in place.
69   template<class Buf> 69   template<class Buf>
70   inline void 70   inline void
HITCBC 71   69 trim_suffix_pair(std::array<Buf, 2>& a, std::size_t n) noexcept 71   69 trim_suffix_pair(std::array<Buf, 2>& a, std::size_t n) noexcept
72   { 72   {
HITCBC 73   69 if(n >= a[1].size()) 73   69 if(n >= a[1].size())
74   { 74   {
HITCBC 75   69 n -= a[1].size(); 75   69 n -= a[1].size();
HITCBC 76   69 a[1] = Buf(); 76   69 a[1] = Buf();
HITCBC 77   69 if(n >= a[0].size()) 77   69 if(n >= a[0].size())
MISUBC 78   a[0] = Buf(); 78   a[0] = Buf();
79   else 79   else
HITCBC 80   69 a[0] = Buf(a[0].data(), a[0].size() - n); 80   69 a[0] = Buf(a[0].data(), a[0].size() - n);
81   } 81   }
82   else 82   else
83   { 83   {
MISUBC 84   a[1] = Buf(a[1].data(), a[1].size() - n); 84   a[1] = Buf(a[1].data(), a[1].size() - n);
85   } 85   }
HITCBC 86   69 } 86   69 }
87   87  
88   const 88   const
89   capy::const_buffer 89   capy::const_buffer
90   crlf_and_final_chunk = {"\r\n0\r\n\r\n", 7}; 90   crlf_and_final_chunk = {"\r\n0\r\n\r\n", 7};
91   91  
92   const 92   const
93   capy::const_buffer 93   capy::const_buffer
94   crlf = {"\r\n", 2}; 94   crlf = {"\r\n", 2};
95   95  
96   const 96   const
97   capy::const_buffer 97   capy::const_buffer
98   final_chunk = {"0\r\n\r\n", 5}; 98   final_chunk = {"0\r\n\r\n", 5};
99   99  
100   constexpr 100   constexpr
101   std::uint8_t 101   std::uint8_t
HITCBC 102   159 chunk_header_len( 102   159 chunk_header_len(
103   std::size_t max_chunk_size) noexcept 103   std::size_t max_chunk_size) noexcept
104   { 104   {
105   return 105   return
106   static_cast<uint8_t>( 106   static_cast<uint8_t>(
HITCBC 107   159 (core::bit_width(max_chunk_size) + 3) / 4 + 107   159 (core::bit_width(max_chunk_size) + 3) / 4 +
HITCBC 108   159 2); // crlf 108   159 2); // crlf
109   }; 109   };
110   110  
111   void 111   void
HITCBC 112   68 write_chunk_header( 112   68 write_chunk_header(
113   const std::array<capy::mutable_buffer, 2>& mbs, 113   const std::array<capy::mutable_buffer, 2>& mbs,
114   std::size_t size) noexcept 114   std::size_t size) noexcept
115   { 115   {
116   static constexpr char hexdig[] = 116   static constexpr char hexdig[] =
117   "0123456789ABCDEF"; 117   "0123456789ABCDEF";
118   char buf[18]; 118   char buf[18];
HITCBC 119   68 auto p = buf + 16; 119   68 auto p = buf + 16;
HITCBC 120   68 auto const n = capy::buffer_size(mbs); 120   68 auto const n = capy::buffer_size(mbs);
HITCBC 121   340 for(std::size_t i = n - 2; i--;) 121   340 for(std::size_t i = n - 2; i--;)
122   { 122   {
HITCBC 123   272 *--p = hexdig[size & 0xf]; 123   272 *--p = hexdig[size & 0xf];
HITCBC 124   272 size >>= 4; 124   272 size >>= 4;
125   } 125   }
HITCBC 126   68 buf[16] = '\r'; 126   68 buf[16] = '\r';
HITCBC 127   68 buf[17] = '\n'; 127   68 buf[17] = '\n';
HITCBC 128   68 auto copied = capy::buffer_copy( 128   68 auto copied = capy::buffer_copy(
129   mbs, 129   mbs,
HITCBC 130   136 capy::const_buffer(p, n)); 130   136 capy::const_buffer(p, n));
131   ignore_unused(copied); 131   ignore_unused(copied);
HITCBC 132   68 BOOST_ASSERT(copied == n); 132   68 BOOST_ASSERT(copied == n);
HITCBC 133   68 } 133   68 }
134   134  
135   class zlib_filter 135   class zlib_filter
136   : public detail::zlib_filter_base 136   : public detail::zlib_filter_base
137   { 137   {
138   http::zlib::deflate_service& svc_; 138   http::zlib::deflate_service& svc_;
139   139  
140   public: 140   public:
MISUBC 141   zlib_filter( 141   zlib_filter(
142   http::zlib::deflate_service& svc, 142   http::zlib::deflate_service& svc,
143   int comp_level, 143   int comp_level,
144   int window_bits, 144   int window_bits,
145   int mem_level) 145   int mem_level)
MISUBC 146   : svc_(svc) 146   : svc_(svc)
147   { 147   {
MISUBC 148   std::error_code ec = static_cast<http::zlib::error>(svc_.init2( 148   std::error_code ec = static_cast<http::zlib::error>(svc_.init2(
MISUBC 149   strm_, 149   strm_,
150   comp_level, 150   comp_level,
151   http::zlib::deflated, 151   http::zlib::deflated,
152   window_bits, 152   window_bits,
153   mem_level, 153   mem_level,
MISUBC 154   http::zlib::default_strategy)); 154   http::zlib::default_strategy));
MISUBC 155   if(ec != http::zlib::error::ok) 155   if(ec != http::zlib::error::ok)
MISUBC 156   detail::throw_system_error(ec); 156   detail::throw_system_error(ec);
MISUBC 157   } 157   }
158   158  
159   private: 159   private:
160   virtual 160   virtual
161   std::size_t 161   std::size_t
MISUBC 162   min_out_buffer() const noexcept override 162   min_out_buffer() const noexcept override
163   { 163   {
MISUBC 164   return 8; 164   return 8;
165   } 165   }
166   166  
167   virtual 167   virtual
168   results 168   results
MISUBC 169   do_process( 169   do_process(
170   capy::mutable_buffer out, 170   capy::mutable_buffer out,
171   capy::const_buffer in, 171   capy::const_buffer in,
172   bool more) noexcept override 172   bool more) noexcept override
173   { 173   {
MISUBC 174   strm_.next_out = static_cast<unsigned char*>(out.data()); 174   strm_.next_out = static_cast<unsigned char*>(out.data());
MISUBC 175   strm_.avail_out = saturate_cast(out.size()); 175   strm_.avail_out = saturate_cast(out.size());
MISUBC 176   strm_.next_in = static_cast<unsigned char*>(const_cast<void *>(in.data())); 176   strm_.next_in = static_cast<unsigned char*>(const_cast<void *>(in.data()));
MISUBC 177   strm_.avail_in = saturate_cast(in.size()); 177   strm_.avail_in = saturate_cast(in.size());
178   178  
179   auto rs = static_cast<http::zlib::error>( 179   auto rs = static_cast<http::zlib::error>(
MISUBC 180   svc_.deflate( 180   svc_.deflate(
MISUBC 181   strm_, 181   strm_,
182   more ? http::zlib::no_flush : http::zlib::finish)); 182   more ? http::zlib::no_flush : http::zlib::finish));
183   183  
MISUBC 184   results rv; 184   results rv;
MISUBC 185   rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out; 185   rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
MISUBC 186   rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in; 186   rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in;
MISUBC 187   rv.finished = (rs == http::zlib::error::stream_end); 187   rv.finished = (rs == http::zlib::error::stream_end);
188   188  
MISUBC 189   if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err) 189   if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err)
MISUBC 190   rv.ec = rs; 190   rv.ec = rs;
191   191  
MISUBC 192   return rv; 192   return rv;
193   } 193   }
194   }; 194   };
195   195  
196   class brotli_filter 196   class brotli_filter
197   : public detail::brotli_filter_base 197   : public detail::brotli_filter_base
198   { 198   {
199   http::brotli::encode_service& svc_; 199   http::brotli::encode_service& svc_;
200   http::brotli::encoder_state* state_; 200   http::brotli::encoder_state* state_;
201   201  
202   public: 202   public:
MISUBC 203   brotli_filter( 203   brotli_filter(
204   http::brotli::encode_service& svc, 204   http::brotli::encode_service& svc,
205   std::uint32_t comp_quality, 205   std::uint32_t comp_quality,
206   std::uint32_t comp_window) 206   std::uint32_t comp_window)
MISUBC 207   : svc_(svc) 207   : svc_(svc)
208   { 208   {
MISUBC 209   state_ = svc_.create_instance(nullptr, nullptr, nullptr); 209   state_ = svc_.create_instance(nullptr, nullptr, nullptr);
MISUBC 210   if(!state_) 210   if(!state_)
MISUBC 211   detail::throw_bad_alloc(); 211   detail::throw_bad_alloc();
212   using encoder_parameter = http::brotli::encoder_parameter; 212   using encoder_parameter = http::brotli::encoder_parameter;
MISUBC 213   svc_.set_parameter(state_, encoder_parameter::quality, comp_quality); 213   svc_.set_parameter(state_, encoder_parameter::quality, comp_quality);
MISUBC 214   svc_.set_parameter(state_, encoder_parameter::lgwin, comp_window); 214   svc_.set_parameter(state_, encoder_parameter::lgwin, comp_window);
MISUBC 215   } 215   }
216   216  
MISUBC 217   ~brotli_filter() 217   ~brotli_filter()
MISUBC 218   { 218   {
MISUBC 219   svc_.destroy_instance(state_); 219   svc_.destroy_instance(state_);
MISUBC 220   } 220   }
221   221  
222   private: 222   private:
223   virtual 223   virtual
224   results 224   results
MISUBC 225   do_process( 225   do_process(
226   capy::mutable_buffer out, 226   capy::mutable_buffer out,
227   capy::const_buffer in, 227   capy::const_buffer in,
228   bool more) noexcept override 228   bool more) noexcept override
229   { 229   {
MISUBC 230   auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data()); 230   auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
MISUBC 231   auto available_in = in.size(); 231   auto available_in = in.size();
MISUBC 232   auto* next_out = reinterpret_cast<std::uint8_t*>(out.data()); 232   auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
MISUBC 233   auto available_out = out.size(); 233   auto available_out = out.size();
234   234  
235   using encoder_operation = 235   using encoder_operation =
236   http::brotli::encoder_operation; 236   http::brotli::encoder_operation;
237   237  
MISUBC 238   bool rs = svc_.compress_stream( 238   bool rs = svc_.compress_stream(
239   state_, 239   state_,
240   more ? encoder_operation::process : encoder_operation::finish, 240   more ? encoder_operation::process : encoder_operation::finish,
241   &available_in, 241   &available_in,
242   &next_in, 242   &next_in,
243   &available_out, 243   &available_out,
244   &next_out, 244   &next_out,
245   nullptr); 245   nullptr);
246   246  
MISUBC 247   results rv; 247   results rv;
MISUBC 248   rv.in_bytes = in.size() - available_in; 248   rv.in_bytes = in.size() - available_in;
MISUBC 249   rv.out_bytes = out.size() - available_out; 249   rv.out_bytes = out.size() - available_out;
MISUBC 250   rv.finished = svc_.is_finished(state_); 250   rv.finished = svc_.is_finished(state_);
251   251  
MISUBC 252   if(rs == false) 252   if(rs == false)
MISUBC 253   rv.ec = error::bad_payload; 253   rv.ec = error::bad_payload;
254   254  
MISUBC 255   return rv; 255   return rv;
256   } 256   }
257   }; 257   };
258   258  
259   template<class UInt> 259   template<class UInt>
260   std::size_t 260   std::size_t
261   clamp( 261   clamp(
262   UInt x, 262   UInt x,
263   std::size_t limit = (std::numeric_limits< 263   std::size_t limit = (std::numeric_limits<
264   std::size_t>::max)()) noexcept 264   std::size_t>::max)()) noexcept
265   { 265   {
266   if(x >= limit) 266   if(x >= limit)
267   return limit; 267   return limit;
268   return static_cast<std::size_t>(x); 268   return static_cast<std::size_t>(x);
269   } 269   }
270   270  
271   } // namespace 271   } // namespace
272   272  
273   //------------------------------------------------ 273   //------------------------------------------------
274   274  
275   class serializer::impl 275   class serializer::impl
276   { 276   {
277   enum class state 277   enum class state
278   { 278   {
279   reset, 279   reset,
280   start, 280   start,
281   header, 281   header,
282   body 282   body
283   }; 283   };
284   284  
285   enum class style 285   enum class style
286   { 286   {
287   empty, 287   empty,
288   stream 288   stream
289   }; 289   };
290   290  
291   std::shared_ptr<serializer_config_impl const> cfg_; 291   std::shared_ptr<serializer_config_impl const> cfg_;
292   detail::workspace ws_; 292   detail::workspace ws_;
293   293  
294   std::unique_ptr<detail::filter> filter_; 294   std::unique_ptr<detail::filter> filter_;
295   295  
296   detail::circular_buffer out_; 296   detail::circular_buffer out_;
297   detail::circular_buffer in_; 297   detail::circular_buffer in_;
298   detail::array_of_const_buffers prepped_; 298   detail::array_of_const_buffers prepped_;
299   capy::const_buffer tmp_; 299   capy::const_buffer tmp_;
300   300  
301   state state_ = state::start; 301   state state_ = state::start;
302   style style_ = style::empty; 302   style style_ = style::empty;
303   uint8_t chunk_header_len_ = 0; 303   uint8_t chunk_header_len_ = 0;
304   bool more_input_ = false; 304   bool more_input_ = false;
305   bool is_chunked_ = false; 305   bool is_chunked_ = false;
306   bool needs_exp100_continue_ = false; 306   bool needs_exp100_continue_ = false;
307   bool filter_done_ = false; 307   bool filter_done_ = false;
308   308  
309   public: 309   public:
310   message_base const* msg_ = nullptr; 310   message_base const* msg_ = nullptr;
311   311  
312   explicit 312   explicit
HITCBC 313   158 impl(std::shared_ptr<serializer_config_impl const> cfg) 313   158 impl(std::shared_ptr<serializer_config_impl const> cfg)
HITCBC 314   158 : cfg_(std::move(cfg)) 314   158 : cfg_(std::move(cfg))
HITCBC 315   158 , ws_(cfg_->space_needed) 315   158 , ws_(cfg_->space_needed)
316   { 316   {
HITCBC 317   158 } 317   158 }
318   318  
319   impl( 319   impl(
320   std::shared_ptr<serializer_config_impl const> cfg, 320   std::shared_ptr<serializer_config_impl const> cfg,
321   message_base const& msg) 321   message_base const& msg)
322   : cfg_(std::move(cfg)) 322   : cfg_(std::move(cfg))
323   , ws_(cfg_->space_needed) 323   , ws_(cfg_->space_needed)
324   , msg_(&msg) 324   , msg_(&msg)
325   { 325   {
326   } 326   }
327   327  
328   void 328   void
HITCBC 329   54 reset() noexcept 329   54 reset() noexcept
330   { 330   {
HITCBC 331   54 filter_.reset(); 331   54 filter_.reset();
HITCBC 332   54 ws_.clear(); 332   54 ws_.clear();
HITCBC 333   54 state_ = state::start; 333   54 state_ = state::start;
HITCBC 334   54 } 334   54 }
335   335  
336   auto 336   auto
HITCBC 337   389 prepare() -> 337   389 prepare() ->
338   system::result<const_buffers_type> 338   system::result<const_buffers_type>
339   { 339   {
340   // Precondition violation 340   // Precondition violation
HITCBC 341   389 if(state_ < state::header) 341   389 if(state_ < state::header)
HITCBC 342   1 detail::throw_logic_error(); 342   1 detail::throw_logic_error();
343   343  
344   // Expect: 100-continue 344   // Expect: 100-continue
HITCBC 345   388 if(needs_exp100_continue_) 345   388 if(needs_exp100_continue_)
346   { 346   {
HITCBC 347   4 if(!is_header_done()) 347   4 if(!is_header_done())
HITCBC 348   4 return const_buffers_type( 348   4 return const_buffers_type(
349   prepped_.begin(), 349   prepped_.begin(),
HITCBC 350   2 1); // limit to header 350   2 1); // limit to header
351   351  
HITCBC 352   2 needs_exp100_continue_ = false; 352   2 needs_exp100_continue_ = false;
353   353  
HITCBC 354   2 return error::expect_100_continue; 354   2 return error::expect_100_continue;
355   } 355   }
356   356  
HITCBC 357   384 if(!filter_) 357   384 if(!filter_)
358   { 358   {
HITCBC 359   384 switch(style_) 359   384 switch(style_)
360   { 360   {
HITCBC 361   6 case style::empty: 361   6 case style::empty:
HITCBC 362   6 break; 362   6 break;
363   363  
HITCBC 364   378 case style::stream: 364   378 case style::stream:
HITCBC 365   378 if(out_.size() == 0 && is_header_done() && more_input_) 365   378 if(out_.size() == 0 && is_header_done() && more_input_)
HITCBC 366   118 return error::need_data; 366   118 return error::need_data;
HITCBC 367   260 break; 367   260 break;
368   } 368   }
369   } 369   }
370   else // filter 370   else // filter
371   { 371   {
MISUBC 372   switch(style_) 372   switch(style_)
373   { 373   {
MISUBC 374   case style::empty: 374   case style::empty:
375   { 375   {
MISUBC 376   if(out_capacity() == 0 || filter_done_) 376   if(out_capacity() == 0 || filter_done_)
MISUBC 377   break; 377   break;
378   378  
MISUBC 379   const auto rs = filter_->process( 379   const auto rs = filter_->process(
MISUBC 380   detail::make_span(out_prepare()), 380   detail::make_span(out_prepare()),
381   {}, // empty input 381   {}, // empty input
382   false); 382   false);
383   383  
MISUBC 384   if(rs.ec) 384   if(rs.ec)
385   { 385   {
MISUBC 386   ws_.clear(); 386   ws_.clear();
MISUBC 387   state_ = state::reset; 387   state_ = state::reset;
MISUBC 388   return rs.ec; 388   return rs.ec;
389   } 389   }
390   390  
MISUBC 391   out_commit(rs.out_bytes); 391   out_commit(rs.out_bytes);
392   392  
MISUBC 393   if(rs.finished) 393   if(rs.finished)
394   { 394   {
MISUBC 395   filter_done_ = true; 395   filter_done_ = true;
MISUBC 396   out_finish(); 396   out_finish();
397   } 397   }
398   398  
MISUBC 399   break; 399   break;
400   } 400   }
401   401  
MISUBC 402   case style::stream: 402   case style::stream:
403   { 403   {
MISUBC 404   if(out_capacity() == 0 || filter_done_) 404   if(out_capacity() == 0 || filter_done_)
MISUBC 405   break; 405   break;
406   406  
MISUBC 407   const auto rs = filter_->process( 407   const auto rs = filter_->process(
MISUBC 408   detail::make_span(out_prepare()), 408   detail::make_span(out_prepare()),
409   in_.data(), 409   in_.data(),
MISUBC 410   more_input_); 410   more_input_);
411   411  
MISUBC 412   if(rs.ec) 412   if(rs.ec)
413   { 413   {
MISUBC 414   ws_.clear(); 414   ws_.clear();
MISUBC 415   state_ = state::reset; 415   state_ = state::reset;
MISUBC 416   return rs.ec; 416   return rs.ec;
417   } 417   }
418   418  
MISUBC 419   in_.consume(rs.in_bytes); 419   in_.consume(rs.in_bytes);
MISUBC 420   out_commit(rs.out_bytes); 420   out_commit(rs.out_bytes);
421   421  
MISUBC 422   if(rs.finished) 422   if(rs.finished)
423   { 423   {
MISUBC 424   filter_done_ = true; 424   filter_done_ = true;
MISUBC 425   out_finish(); 425   out_finish();
426   } 426   }
427   427  
MISUBC 428   if(out_.size() == 0 && is_header_done() && more_input_) 428   if(out_.size() == 0 && is_header_done() && more_input_)
MISUBC 429   return error::need_data; 429   return error::need_data;
MISUBC 430   break; 430   break;
431   } 431   }
432   } 432   }
433   } 433   }
434   434  
HITCBC 435   266 prepped_.reset(!is_header_done()); 435   266 prepped_.reset(!is_header_done());
HITCBC 436   798 for(auto const& cb : out_.data()) 436   798 for(auto const& cb : out_.data())
437   { 437   {
HITCBC 438   532 if(cb.size() != 0) 438   532 if(cb.size() != 0)
HITCBC 439   168 prepped_.append(cb); 439   168 prepped_.append(cb);
440   } 440   }
HITCBC 441   266 return detail::make_span(prepped_); 441   266 return detail::make_span(prepped_);
442   } 442   }
443   443  
444   void 444   void
HITCBC 445   1902 consume( 445   1902 consume(
446   std::size_t n) 446   std::size_t n)
447   { 447   {
448   // Precondition violation 448   // Precondition violation
HITCBC 449   1902 if(state_ < state::header) 449   1902 if(state_ < state::header)
HITCBC 450   1 detail::throw_logic_error(); 450   1 detail::throw_logic_error();
451   451  
HITCBC 452   1901 if(!is_header_done()) 452   1901 if(!is_header_done())
453   { 453   {
454   const auto header_remain = 454   const auto header_remain =
HITCBC 455   132 prepped_[0].size(); 455   132 prepped_[0].size();
HITCBC 456   132 if(n < header_remain) 456   132 if(n < header_remain)
457   { 457   {
HITCBC 458   48 prepped_.consume(n); 458   48 prepped_.consume(n);
HITCBC 459   48 return; 459   48 return;
460   } 460   }
HITCBC 461   84 n -= header_remain; 461   84 n -= header_remain;
HITCBC 462   84 prepped_.consume(header_remain); 462   84 prepped_.consume(header_remain);
HITCBC 463   84 state_ = state::body; 463   84 state_ = state::body;
464   } 464   }
465   465  
HITCBC 466   1853 prepped_.consume(n); 466   1853 prepped_.consume(n);
467   467  
468   // no-op when out_ is not in use 468   // no-op when out_ is not in use
HITCBC 469   1853 out_.consume(n); 469   1853 out_.consume(n);
470   470  
HITCBC 471   1853 if(!prepped_.empty()) 471   1853 if(!prepped_.empty())
HITCBC 472   1692 return; 472   1692 return;
473   473  
HITCBC 474   161 if(more_input_) 474   161 if(more_input_)
HITCBC 475   109 return; 475   109 return;
476   476  
HITCBC 477   52 if(filter_ && !filter_done_) 477   52 if(filter_ && !filter_done_)
MISUBC 478   return; 478   return;
479   479  
HITCBC 480   52 if(needs_exp100_continue_) 480   52 if(needs_exp100_continue_)
HITCBC 481   2 return; 481   2 return;
482   482  
483   // ready for next message 483   // ready for next message
HITCBC 484   50 reset(); 484   50 reset();
485   } 485   }
486   486  
487   void 487   void
HITCBC 488   159 start_init( 488   159 start_init(
489   message_base const& m) 489   message_base const& m)
490   { 490   {
491   // Precondition violation 491   // Precondition violation
HITCBC 492   159 if(state_ != state::start) 492   159 if(state_ != state::start)
MISUBC 493   detail::throw_logic_error(); 493   detail::throw_logic_error();
494   494  
495   // TODO: To uphold the strong exception guarantee, 495   // TODO: To uphold the strong exception guarantee,
496   // `state_` must be reset to `state::start` if an 496   // `state_` must be reset to `state::start` if an
497   // exception is thrown during the start operation. 497   // exception is thrown during the start operation.
HITCBC 498   159 state_ = state::header; 498   159 state_ = state::header;
499   499  
500   // VFALCO what do we do with 500   // VFALCO what do we do with
501   // metadata error code failures? 501   // metadata error code failures?
502   // m.h_.md.maybe_throw(); 502   // m.h_.md.maybe_throw();
503   503  
HITCBC 504   159 auto const& md = m.metadata(); 504   159 auto const& md = m.metadata();
HITCBC 505   159 needs_exp100_continue_ = md.expect.is_100_continue; 505   159 needs_exp100_continue_ = md.expect.is_100_continue;
506   506  
507   // Transfer-Encoding 507   // Transfer-Encoding
HITCBC 508   159 is_chunked_ = md.transfer_encoding.is_chunked; 508   159 is_chunked_ = md.transfer_encoding.is_chunked;
509   509  
510   // Content-Encoding 510   // Content-Encoding
HITCBC 511   159 switch (md.content_encoding.coding) 511   159 switch (md.content_encoding.coding)
512   { 512   {
MISUBC 513   case content_coding::deflate: 513   case content_coding::deflate:
MISUBC 514   if(!cfg_->apply_deflate_encoder) 514   if(!cfg_->apply_deflate_encoder)
MISUBC 515   goto no_filter; 515   goto no_filter;
MISUBC 516   if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>()) 516   if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
517   { 517   {
MISUBC 518   filter_.reset(new zlib_filter( 518   filter_.reset(new zlib_filter(
519   *svc, 519   *svc,
MISUBC 520   cfg_->zlib_comp_level, 520   cfg_->zlib_comp_level,
MISUBC 521   cfg_->zlib_window_bits, 521   cfg_->zlib_window_bits,
MISUBC 522   cfg_->zlib_mem_level)); 522   cfg_->zlib_mem_level));
MISUBC 523   filter_done_ = false; 523   filter_done_ = false;
524   } 524   }
MISUBC 525   break; 525   break;
526   526  
MISUBC 527   case content_coding::gzip: 527   case content_coding::gzip:
MISUBC 528   if(!cfg_->apply_gzip_encoder) 528   if(!cfg_->apply_gzip_encoder)
MISUBC 529   goto no_filter; 529   goto no_filter;
MISUBC 530   if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>()) 530   if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
531   { 531   {
MISUBC 532   filter_.reset(new zlib_filter( 532   filter_.reset(new zlib_filter(
533   *svc, 533   *svc,
MISUBC 534   cfg_->zlib_comp_level, 534   cfg_->zlib_comp_level,
MISUBC 535   cfg_->zlib_window_bits + 16, 535   cfg_->zlib_window_bits + 16,
MISUBC 536   cfg_->zlib_mem_level)); 536   cfg_->zlib_mem_level));
MISUBC 537   filter_done_ = false; 537   filter_done_ = false;
538   } 538   }
MISUBC 539   break; 539   break;
540   540  
MISUBC 541   case content_coding::br: 541   case content_coding::br:
MISUBC 542   if(!cfg_->apply_brotli_encoder) 542   if(!cfg_->apply_brotli_encoder)
MISUBC 543   goto no_filter; 543   goto no_filter;
MISUBC 544   if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>()) 544   if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>())
545   { 545   {
MISUBC 546   filter_.reset(new brotli_filter( 546   filter_.reset(new brotli_filter(
547   *svc, 547   *svc,
MISUBC 548   cfg_->brotli_comp_quality, 548   cfg_->brotli_comp_quality,
MISUBC 549   cfg_->brotli_comp_window)); 549   cfg_->brotli_comp_window));
MISUBC 550   filter_done_ = false; 550   filter_done_ = false;
551   } 551   }
MISUBC 552   break; 552   break;
553   553  
MISUBC 554   no_filter: 554   no_filter:
HITCBC 555   159 default: 555   159 default:
HITCBC 556   159 filter_.reset(); 556   159 filter_.reset();
HITCBC 557   159 break; 557   159 break;
558   } 558   }
HITCBC 559   159 } 559   159 }
560   560  
561   void 561   void
HITCBC 562   6 start_empty( 562   6 start_empty(
563   message_base const& m) 563   message_base const& m)
564   { 564   {
HITCBC 565   6 start_init(m); 565   6 start_init(m);
HITCBC 566   6 style_ = style::empty; 566   6 style_ = style::empty;
567   567  
HITCBC 568   6 prepped_ = make_array( 568   6 prepped_ = make_array(
569   1 + // header 569   1 + // header
570   2); // out buffer pairs 570   2); // out buffer pairs
571   571  
HITCBC 572   6 out_init(); 572   6 out_init();
573   573  
HITCBC 574   6 if(!filter_) 574   6 if(!filter_)
HITCBC 575   6 out_finish(); 575   6 out_finish();
576   576  
HITCBC 577   6 prepped_.append({ m.h_.cbuf, m.h_.size }); 577   6 prepped_.append({ m.h_.cbuf, m.h_.size });
HITCBC 578   6 more_input_ = false; 578   6 more_input_ = false;
HITCBC 579   6 } 579   6 }
580   580  
581   void 581   void
HITCBC 582   78 start_stream(message_base const& m) 582   78 start_stream(message_base const& m)
583   { 583   {
HITCBC 584   78 start_init(m); 584   78 start_init(m);
HITCBC 585   78 style_ = style::stream; 585   78 style_ = style::stream;
586   586  
HITCBC 587   78 prepped_ = make_array( 587   78 prepped_ = make_array(
588   1 + // header 588   1 + // header
589   2); // out buffer pairs 589   2); // out buffer pairs
590   590  
HITCBC 591   78 if(filter_) 591   78 if(filter_)
592   { 592   {
593   // TODO: smarter buffer distribution 593   // TODO: smarter buffer distribution
MISUBC 594   auto const n = (ws_.size() - 1) / 2; 594   auto const n = (ws_.size() - 1) / 2;
MISUBC 595   in_ = { ws_.reserve_front(n), n }; 595   in_ = { ws_.reserve_front(n), n };
596   } 596   }
597   597  
HITCBC 598   78 out_init(); 598   78 out_init();
599   599  
HITCBC 600   78 prepped_.append({ m.h_.cbuf, m.h_.size }); 600   78 prepped_.append({ m.h_.cbuf, m.h_.size });
HITCBC 601   78 more_input_ = true; 601   78 more_input_ = true;
HITCBC 602   78 } 602   78 }
603   603  
604   // Like start_stream but without in_ allocation. 604   // Like start_stream but without in_ allocation.
605   // Entire workspace is used for output buffering. 605   // Entire workspace is used for output buffering.
606   void 606   void
HITCBC 607   75 start_buffers_direct(message_base const& m) 607   75 start_buffers_direct(message_base const& m)
608   { 608   {
HITCBC 609   75 start_init(m); 609   75 start_init(m);
HITCBC 610   75 style_ = style::stream; 610   75 style_ = style::stream;
611   611  
HITCBC 612   75 prepped_ = make_array( 612   75 prepped_ = make_array(
613   1 + // header 613   1 + // header
614   2); // out buffer pairs 614   2); // out buffer pairs
615   615  
HITCBC 616   75 out_init(); 616   75 out_init();
617   617  
HITCBC 618   75 prepped_.append({ m.h_.cbuf, m.h_.size }); 618   75 prepped_.append({ m.h_.cbuf, m.h_.size });
HITCBC 619   75 more_input_ = true; 619   75 more_input_ = true;
HITCBC 620   75 } 620   75 }
621   621  
622   std::size_t 622   std::size_t
HITCBC 623   182 stream_capacity() const 623   182 stream_capacity() const
624   { 624   {
HITCBC 625   182 if(filter_) 625   182 if(filter_)
MISUBC 626   return in_.capacity(); 626   return in_.capacity();
HITCBC 627   182 return out_capacity(); 627   182 return out_capacity();
628   } 628   }
629   629  
630   std::array<capy::mutable_buffer, 2> 630   std::array<capy::mutable_buffer, 2>
HITCBC 631   129 stream_prepare() 631   129 stream_prepare()
632   { 632   {
HITCBC 633   129 if(state_ == state::start) 633   129 if(state_ == state::start)
634   { 634   {
MISUBC 635   if(!msg_) 635   if(!msg_)
MISUBC 636   detail::throw_logic_error(); 636   detail::throw_logic_error();
MISUBC 637   start_stream(*msg_); 637   start_stream(*msg_);
638   } 638   }
HITCBC 639   129 if(filter_) 639   129 if(filter_)
MISUBC 640   return in_.prepare(in_.capacity()); 640   return in_.prepare(in_.capacity());
HITCBC 641   129 return out_prepare(); 641   129 return out_prepare();
642   } 642   }
643   643  
644   void 644   void
HITCBC 645   165 stream_commit(std::size_t n) 645   165 stream_commit(std::size_t n)
646   { 646   {
HITCBC 647   165 if(n > stream_capacity()) 647   165 if(n > stream_capacity())
HITCBC 648   1 detail::throw_invalid_argument(); 648   1 detail::throw_invalid_argument();
649   649  
HITCBC 650   164 if(filter_) 650   164 if(filter_)
MISUBC 651   return in_.commit(n); 651   return in_.commit(n);
652   652  
HITCBC 653   164 out_commit(n); 653   164 out_commit(n);
654   } 654   }
655   655  
656   void 656   void
HITCBC 657   75 stream_close() noexcept 657   75 stream_close() noexcept
658   { 658   {
HITCBC 659   75 if(!filter_) 659   75 if(!filter_)
HITCBC 660   75 out_finish(); 660   75 out_finish();
661   661  
HITCBC 662   75 more_input_ = false; 662   75 more_input_ = false;
HITCBC 663   75 } 663   75 }
664   664  
665   bool 665   bool
HITCBC 666   464 is_done() const noexcept 666   464 is_done() const noexcept
667   { 667   {
HITCBC 668   464 return state_ == state::start; 668   464 return state_ == state::start;
669   } 669   }
670   670  
671   bool 671   bool
HITCBC 672   249 is_start() const noexcept 672   249 is_start() const noexcept
673   { 673   {
HITCBC 674   249 return state_ == state::start; 674   249 return state_ == state::start;
675   } 675   }
676   676  
677   detail::workspace& 677   detail::workspace&
MISUBC 678   ws() noexcept 678   ws() noexcept
679   { 679   {
MISUBC 680   return ws_; 680   return ws_;
681   } 681   }
682   682  
683   private: 683   private:
684   bool 684   bool
HITCBC 685   2384 is_header_done() const noexcept 685   2384 is_header_done() const noexcept
686   { 686   {
HITCBC 687   2384 return state_ == state::body; 687   2384 return state_ == state::body;
688   } 688   }
689   689  
690   detail::array_of_const_buffers 690   detail::array_of_const_buffers
HITCBC 691   159 make_array(std::size_t n) 691   159 make_array(std::size_t n)
692   { 692   {
HITCBC 693   159 BOOST_ASSERT(n <= std::uint16_t(-1)); 693   159 BOOST_ASSERT(n <= std::uint16_t(-1));
694   694  
695   return { 695   return {
HITCBC 696   159 ws_.push_array(n, 696   159 ws_.push_array(n,
MISUBC 697   capy::const_buffer{}), 697   capy::const_buffer{}),
HITCBC 698   159 static_cast<std::uint16_t>(n) }; 698   159 static_cast<std::uint16_t>(n) };
699   } 699   }
700   700  
701   void 701   void
HITCBC 702   159 out_init() 702   159 out_init()
703   { 703   {
704   // use all the remaining buffer 704   // use all the remaining buffer
HITCBC 705   159 auto const n = ws_.size() - 1; 705   159 auto const n = ws_.size() - 1;
HITCBC 706   159 out_ = { ws_.reserve_front(n), n }; 706   159 out_ = { ws_.reserve_front(n), n };
HITCBC 707   159 chunk_header_len_ = 707   159 chunk_header_len_ =
HITCBC 708   159 chunk_header_len(out_.capacity()); 708   159 chunk_header_len(out_.capacity());
HITCBC 709   159 if(out_capacity() == 0) 709   159 if(out_capacity() == 0)
MISUBC 710   detail::throw_length_error(); 710   detail::throw_length_error();
HITCBC 711   159 } 711   159 }
712   712  
713   std::array<capy::mutable_buffer, 2> 713   std::array<capy::mutable_buffer, 2>
HITCBC 714   129 out_prepare() noexcept 714   129 out_prepare() noexcept
715   { 715   {
HITCBC 716   129 auto mbp = out_.prepare(out_.capacity()); 716   129 auto mbp = out_.prepare(out_.capacity());
HITCBC 717   129 if(is_chunked_) 717   129 if(is_chunked_)
718   { 718   {
HITCBC 719   69 trim_prefix_pair( 719   69 trim_prefix_pair(
HITCBC 720   69 mbp, chunk_header_len_); 720   69 mbp, chunk_header_len_);
HITCBC 721   69 trim_suffix_pair( 721   69 trim_suffix_pair(
722   mbp, crlf_and_final_chunk.size()); 722   mbp, crlf_and_final_chunk.size());
723   } 723   }
HITCBC 724   129 return mbp; 724   129 return mbp;
725   } 725   }
726   726  
727   void 727   void
HITCBC 728   164 out_commit( 728   164 out_commit(
729   std::size_t n) noexcept 729   std::size_t n) noexcept
730   { 730   {
HITCBC 731   164 if(is_chunked_) 731   164 if(is_chunked_)
732   { 732   {
HITCBC 733   87 if(n == 0) 733   87 if(n == 0)
HITCBC 734   19 return; 734   19 return;
735   735  
HITCBC 736   68 write_chunk_header(out_.prepare(chunk_header_len_), n); 736   68 write_chunk_header(out_.prepare(chunk_header_len_), n);
HITCBC 737   68 out_.commit(chunk_header_len_); 737   68 out_.commit(chunk_header_len_);
738   738  
HITCBC 739   68 out_.prepare(n); 739   68 out_.prepare(n);
HITCBC 740   68 out_.commit(n); 740   68 out_.commit(n);
741   741  
HITCBC 742   68 capy::buffer_copy(out_.prepare(crlf.size()), crlf); 742   68 capy::buffer_copy(out_.prepare(crlf.size()), crlf);
HITCBC 743   68 out_.commit(crlf.size()); 743   68 out_.commit(crlf.size());
744   } 744   }
745   else 745   else
746   { 746   {
HITCBC 747   77 out_.commit(n); 747   77 out_.commit(n);
748   } 748   }
749   } 749   }
750   750  
751   std::size_t 751   std::size_t
HITCBC 752   341 out_capacity() const noexcept 752   341 out_capacity() const noexcept
753   { 753   {
HITCBC 754   341 if(is_chunked_) 754   341 if(is_chunked_)
755   { 755   {
HITCBC 756   174 auto const overhead = chunk_header_len_ + 756   174 auto const overhead = chunk_header_len_ +
HITCBC 757   174 crlf_and_final_chunk.size(); 757   174 crlf_and_final_chunk.size();
HITCBC 758   174 if(out_.capacity() < overhead) 758   174 if(out_.capacity() < overhead)
HITCBC 759   1 return 0; 759   1 return 0;
HITCBC 760   173 return out_.capacity() - overhead; 760   173 return out_.capacity() - overhead;
761   } 761   }
HITCBC 762   167 return out_.capacity(); 762   167 return out_.capacity();
763   } 763   }
764   764  
765   void 765   void
HITCBC 766   81 out_finish() noexcept 766   81 out_finish() noexcept
767   { 767   {
HITCBC 768   81 if(is_chunked_) 768   81 if(is_chunked_)
769   { 769   {
HITCBC 770   41 capy::buffer_copy( 770   41 capy::buffer_copy(
HITCBC 771   41 out_.prepare(final_chunk.size()), final_chunk); 771   41 out_.prepare(final_chunk.size()), final_chunk);
HITCBC 772   41 out_.commit(final_chunk.size()); 772   41 out_.commit(final_chunk.size());
773   } 773   }
HITCBC 774   81 } 774   81 }
775   }; 775   };
776   776  
777   //------------------------------------------------ 777   //------------------------------------------------
778   778  
HITCBC 779   163 serializer:: 779   163 serializer::
780   ~serializer() 780   ~serializer()
781   { 781   {
HITCBC 782   163 delete impl_; 782   163 delete impl_;
HITCBC 783   163 } 783   163 }
784   784  
HITCBC 785   1 serializer:: 785   1 serializer::
HITCBC 786   1 serializer(serializer&& other) noexcept 786   1 serializer(serializer&& other) noexcept
HITCBC 787   1 : impl_(other.impl_) 787   1 : impl_(other.impl_)
788   { 788   {
HITCBC 789   1 other.impl_ = nullptr; 789   1 other.impl_ = nullptr;
HITCBC 790   1 } 790   1 }
791   791  
792   serializer& 792   serializer&
HITCBC 793   2 serializer:: 793   2 serializer::
794   operator=(serializer&& other) noexcept 794   operator=(serializer&& other) noexcept
795   { 795   {
HITCBC 796   2 if(this != &other) 796   2 if(this != &other)
797   { 797   {
HITCBC 798   2 delete impl_; 798   2 delete impl_;
HITCBC 799   2 impl_ = other.impl_; 799   2 impl_ = other.impl_;
HITCBC 800   2 other.impl_ = nullptr; 800   2 other.impl_ = nullptr;
801   } 801   }
HITCBC 802   2 return *this; 802   2 return *this;
803   } 803   }
804   804  
HITCBC 805   158 serializer:: 805   158 serializer::
806   serializer( 806   serializer(
HITCBC 807   158 std::shared_ptr<serializer_config_impl const> cfg) 807   158 std::shared_ptr<serializer_config_impl const> cfg)
HITCBC 808   158 : impl_(new impl(std::move(cfg))) 808   158 : impl_(new impl(std::move(cfg)))
809   { 809   {
HITCBC 810   158 } 810   158 }
811   811  
812   void 812   void
HITCBC 813   4 serializer:: 813   4 serializer::
814   reset() noexcept 814   reset() noexcept
815   { 815   {
HITCBC 816   4 BOOST_ASSERT(impl_); 816   4 BOOST_ASSERT(impl_);
HITCBC 817   4 impl_->reset(); 817   4 impl_->reset();
HITCBC 818   4 } 818   4 }
819   819  
820   void 820   void
HITCBC 821   159 serializer:: 821   159 serializer::
822   set_message(message_base const& m) noexcept 822   set_message(message_base const& m) noexcept
823   { 823   {
HITCBC 824   159 BOOST_ASSERT(impl_); 824   159 BOOST_ASSERT(impl_);
HITCBC 825   159 impl_->msg_ = &m; 825   159 impl_->msg_ = &m;
HITCBC 826   159 } 826   159 }
827   827  
828   void 828   void
HITCBC 829   6 serializer:: 829   6 serializer::
830   start() 830   start()
831   { 831   {
HITCBC 832   6 if(!impl_ || !impl_->msg_) 832   6 if(!impl_ || !impl_->msg_)
MISUBC 833   detail::throw_logic_error(); 833   detail::throw_logic_error();
HITCBC 834   6 impl_->start_empty(*impl_->msg_); 834   6 impl_->start_empty(*impl_->msg_);
HITCBC 835   6 } 835   6 }
836   836  
837   void 837   void
MISUBC 838   serializer:: 838   serializer::
839   start_stream() 839   start_stream()
840   { 840   {
MISUBC 841   if(!impl_ || !impl_->msg_) 841   if(!impl_ || !impl_->msg_)
MISUBC 842   detail::throw_logic_error(); 842   detail::throw_logic_error();
MISUBC 843   impl_->start_stream(*impl_->msg_); 843   impl_->start_stream(*impl_->msg_);
MISUBC 844   } 844   }
845   845  
846   void 846   void
HITCBC 847   78 serializer:: 847   78 serializer::
848   start_writes() 848   start_writes()
849   { 849   {
HITCBC 850   78 if(!impl_ || !impl_->msg_) 850   78 if(!impl_ || !impl_->msg_)
MISUBC 851   detail::throw_logic_error(); 851   detail::throw_logic_error();
HITCBC 852   78 impl_->start_stream(*impl_->msg_); 852   78 impl_->start_stream(*impl_->msg_);
HITCBC 853   78 } 853   78 }
854   854  
855   void 855   void
HITCBC 856   75 serializer:: 856   75 serializer::
857   start_buffers() 857   start_buffers()
858   { 858   {
HITCBC 859   75 if(!impl_ || !impl_->msg_) 859   75 if(!impl_ || !impl_->msg_)
MISUBC 860   detail::throw_logic_error(); 860   detail::throw_logic_error();
HITCBC 861   75 impl_->start_buffers_direct(*impl_->msg_); 861   75 impl_->start_buffers_direct(*impl_->msg_);
HITCBC 862   75 } 862   75 }
863   863  
864   auto 864   auto
HITCBC 865   389 serializer:: 865   389 serializer::
866   prepare() -> 866   prepare() ->
867   system::result<const_buffers_type> 867   system::result<const_buffers_type>
868   { 868   {
HITCBC 869   389 BOOST_ASSERT(impl_); 869   389 BOOST_ASSERT(impl_);
HITCBC 870   389 return impl_->prepare(); 870   389 return impl_->prepare();
871   } 871   }
872   872  
873   void 873   void
HITCBC 874   1902 serializer:: 874   1902 serializer::
875   consume(std::size_t n) 875   consume(std::size_t n)
876   { 876   {
HITCBC 877   1902 BOOST_ASSERT(impl_); 877   1902 BOOST_ASSERT(impl_);
HITCBC 878   1902 impl_->consume(n); 878   1902 impl_->consume(n);
HITCBC 879   1901 } 879   1901 }
880   880  
881   bool 881   bool
HITCBC 882   464 serializer:: 882   464 serializer::
883   is_done() const noexcept 883   is_done() const noexcept
884   { 884   {
HITCBC 885   464 BOOST_ASSERT(impl_); 885   464 BOOST_ASSERT(impl_);
HITCBC 886   464 return impl_->is_done(); 886   464 return impl_->is_done();
887   } 887   }
888   888  
889   bool 889   bool
HITCBC 890   249 serializer:: 890   249 serializer::
891   is_start() const noexcept 891   is_start() const noexcept
892   { 892   {
HITCBC 893   249 BOOST_ASSERT(impl_); 893   249 BOOST_ASSERT(impl_);
HITCBC 894   249 return impl_->is_start(); 894   249 return impl_->is_start();
895   } 895   }
896   896  
897   //------------------------------------------------ 897   //------------------------------------------------
898   898  
899   detail::workspace& 899   detail::workspace&
MISUBC 900   serializer:: 900   serializer::
901   ws() 901   ws()
902   { 902   {
MISUBC 903   BOOST_ASSERT(impl_); 903   BOOST_ASSERT(impl_);
MISUBC 904   return impl_->ws(); 904   return impl_->ws();
905   } 905   }
906   906  
907   //------------------------------------------------ 907   //------------------------------------------------
908   908  
909   std::size_t 909   std::size_t
HITCBC 910   17 serializer:: 910   17 serializer::
911   stream_capacity() const 911   stream_capacity() const
912   { 912   {
HITCBC 913   17 BOOST_ASSERT(impl_); 913   17 BOOST_ASSERT(impl_);
HITCBC 914   17 return impl_->stream_capacity(); 914   17 return impl_->stream_capacity();
915   } 915   }
916   916  
917   auto 917   auto
HITCBC 918   129 serializer:: 918   129 serializer::
919   stream_prepare() -> 919   stream_prepare() ->
920   mutable_buffers_type 920   mutable_buffers_type
921   { 921   {
HITCBC 922   129 BOOST_ASSERT(impl_); 922   129 BOOST_ASSERT(impl_);
HITCBC 923   129 return impl_->stream_prepare(); 923   129 return impl_->stream_prepare();
924   } 924   }
925   925  
926   void 926   void
HITCBC 927   165 serializer:: 927   165 serializer::
928   stream_commit(std::size_t n) 928   stream_commit(std::size_t n)
929   { 929   {
HITCBC 930   165 BOOST_ASSERT(impl_); 930   165 BOOST_ASSERT(impl_);
HITCBC 931   165 impl_->stream_commit(n); 931   165 impl_->stream_commit(n);
HITCBC 932   164 } 932   164 }
933   933  
934   void 934   void
HITCBC 935   75 serializer:: 935   75 serializer::
936   stream_close() noexcept 936   stream_close() noexcept
937   { 937   {
HITCBC 938   75 BOOST_ASSERT(impl_); 938   75 BOOST_ASSERT(impl_);
HITCBC 939   75 impl_->stream_close(); 939   75 impl_->stream_close();
HITCBC 940   75 } 940   75 }
941   941  
942   } // http 942   } // http
943   } // boost 943   } // boost