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/except.hpp> 12   #include <boost/http/detail/except.hpp>
13   #include <boost/http/detail/header.hpp> 13   #include <boost/http/detail/header.hpp>
14   #include <boost/http/message_base.hpp> 14   #include <boost/http/message_base.hpp>
15   #include <boost/http/serializer.hpp> 15   #include <boost/http/serializer.hpp>
16   16  
17   #include "src/detail/array_of_const_buffers.hpp" 17   #include "src/detail/array_of_const_buffers.hpp"
18   #include "src/detail/brotli_filter_base.hpp" 18   #include "src/detail/brotli_filter_base.hpp"
19   #include "src/detail/buffer_utils.hpp" 19   #include "src/detail/buffer_utils.hpp"
20   #include "src/detail/zlib_filter_base.hpp" 20   #include "src/detail/zlib_filter_base.hpp"
21   21  
22   #include <boost/capy/buffers/circular_dynamic_buffer.hpp> 22   #include <boost/capy/buffers/circular_dynamic_buffer.hpp>
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   system::error_code ec = static_cast<http::zlib::error>(svc_.init2( 148   system::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   capy::circular_dynamic_buffer out_; 296   capy::circular_dynamic_buffer out_;
297   capy::circular_dynamic_buffer in_; 297   capy::circular_dynamic_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 BOOST_HTTP_RETURN_EC( 354   2 BOOST_HTTP_RETURN_EC(
355   error::expect_100_continue); 355   error::expect_100_continue);
356   } 356   }
357   357  
HITCBC 358   384 if(!filter_) 358   384 if(!filter_)
359   { 359   {
HITCBC 360   384 switch(style_) 360   384 switch(style_)
361   { 361   {
HITCBC 362   6 case style::empty: 362   6 case style::empty:
HITCBC 363   6 break; 363   6 break;
364   364  
HITCBC 365   378 case style::stream: 365   378 case style::stream:
HITCBC 366   378 if(out_.size() == 0 && is_header_done() && more_input_) 366   378 if(out_.size() == 0 && is_header_done() && more_input_)
HITCBC 367   118 BOOST_HTTP_RETURN_EC( 367   118 BOOST_HTTP_RETURN_EC(
368   error::need_data); 368   error::need_data);
HITCBC 369   260 break; 369   260 break;
370   } 370   }
371   } 371   }
372   else // filter 372   else // filter
373   { 373   {
MISUBC 374   switch(style_) 374   switch(style_)
375   { 375   {
MISUBC 376   case style::empty: 376   case style::empty:
377   { 377   {
MISUBC 378   if(out_capacity() == 0 || filter_done_) 378   if(out_capacity() == 0 || filter_done_)
MISUBC 379   break; 379   break;
380   380  
MISUBC 381   const auto rs = filter_->process( 381   const auto rs = filter_->process(
MISUBC 382   detail::make_span(out_prepare()), 382   detail::make_span(out_prepare()),
383   {}, // empty input 383   {}, // empty input
384   false); 384   false);
385   385  
MISUBC 386   if(rs.ec) 386   if(rs.ec)
387   { 387   {
MISUBC 388   ws_.clear(); 388   ws_.clear();
MISUBC 389   state_ = state::reset; 389   state_ = state::reset;
MISUBC 390   return rs.ec; 390   return rs.ec;
391   } 391   }
392   392  
MISUBC 393   out_commit(rs.out_bytes); 393   out_commit(rs.out_bytes);
394   394  
MISUBC 395   if(rs.finished) 395   if(rs.finished)
396   { 396   {
MISUBC 397   filter_done_ = true; 397   filter_done_ = true;
MISUBC 398   out_finish(); 398   out_finish();
399   } 399   }
400   400  
MISUBC 401   break; 401   break;
402   } 402   }
403   403  
MISUBC 404   case style::stream: 404   case style::stream:
405   { 405   {
MISUBC 406   if(out_capacity() == 0 || filter_done_) 406   if(out_capacity() == 0 || filter_done_)
MISUBC 407   break; 407   break;
408   408  
MISUBC 409   const auto rs = filter_->process( 409   const auto rs = filter_->process(
MISUBC 410   detail::make_span(out_prepare()), 410   detail::make_span(out_prepare()),
411   in_.data(), 411   in_.data(),
MISUBC 412   more_input_); 412   more_input_);
413   413  
MISUBC 414   if(rs.ec) 414   if(rs.ec)
415   { 415   {
MISUBC 416   ws_.clear(); 416   ws_.clear();
MISUBC 417   state_ = state::reset; 417   state_ = state::reset;
MISUBC 418   return rs.ec; 418   return rs.ec;
419   } 419   }
420   420  
MISUBC 421   in_.consume(rs.in_bytes); 421   in_.consume(rs.in_bytes);
MISUBC 422   out_commit(rs.out_bytes); 422   out_commit(rs.out_bytes);
423   423  
MISUBC 424   if(rs.finished) 424   if(rs.finished)
425   { 425   {
MISUBC 426   filter_done_ = true; 426   filter_done_ = true;
MISUBC 427   out_finish(); 427   out_finish();
428   } 428   }
429   429  
MISUBC 430   if(out_.size() == 0 && is_header_done() && more_input_) 430   if(out_.size() == 0 && is_header_done() && more_input_)
MISUBC 431   BOOST_HTTP_RETURN_EC( 431   BOOST_HTTP_RETURN_EC(
432   error::need_data); 432   error::need_data);
MISUBC 433   break; 433   break;
434   } 434   }
435   } 435   }
436   } 436   }
437   437  
HITCBC 438   266 prepped_.reset(!is_header_done()); 438   266 prepped_.reset(!is_header_done());
HITCBC 439   798 for(auto const& cb : out_.data()) 439   798 for(auto const& cb : out_.data())
440   { 440   {
HITCBC 441   532 if(cb.size() != 0) 441   532 if(cb.size() != 0)
HITCBC 442   168 prepped_.append(cb); 442   168 prepped_.append(cb);
443   } 443   }
HITCBC 444   266 return detail::make_span(prepped_); 444   266 return detail::make_span(prepped_);
445   } 445   }
446   446  
447   void 447   void
HITCBC 448   1902 consume( 448   1902 consume(
449   std::size_t n) 449   std::size_t n)
450   { 450   {
451   // Precondition violation 451   // Precondition violation
HITCBC 452   1902 if(state_ < state::header) 452   1902 if(state_ < state::header)
HITCBC 453   1 detail::throw_logic_error(); 453   1 detail::throw_logic_error();
454   454  
HITCBC 455   1901 if(!is_header_done()) 455   1901 if(!is_header_done())
456   { 456   {
457   const auto header_remain = 457   const auto header_remain =
HITCBC 458   132 prepped_[0].size(); 458   132 prepped_[0].size();
HITCBC 459   132 if(n < header_remain) 459   132 if(n < header_remain)
460   { 460   {
HITCBC 461   48 prepped_.consume(n); 461   48 prepped_.consume(n);
HITCBC 462   48 return; 462   48 return;
463   } 463   }
HITCBC 464   84 n -= header_remain; 464   84 n -= header_remain;
HITCBC 465   84 prepped_.consume(header_remain); 465   84 prepped_.consume(header_remain);
HITCBC 466   84 state_ = state::body; 466   84 state_ = state::body;
467   } 467   }
468   468  
HITCBC 469   1853 prepped_.consume(n); 469   1853 prepped_.consume(n);
470   470  
471   // no-op when out_ is not in use 471   // no-op when out_ is not in use
HITCBC 472   1853 out_.consume(n); 472   1853 out_.consume(n);
473   473  
HITCBC 474   1853 if(!prepped_.empty()) 474   1853 if(!prepped_.empty())
HITCBC 475   1692 return; 475   1692 return;
476   476  
HITCBC 477   161 if(more_input_) 477   161 if(more_input_)
HITCBC 478   109 return; 478   109 return;
479   479  
HITCBC 480   52 if(filter_ && !filter_done_) 480   52 if(filter_ && !filter_done_)
MISUBC 481   return; 481   return;
482   482  
HITCBC 483   52 if(needs_exp100_continue_) 483   52 if(needs_exp100_continue_)
HITCBC 484   2 return; 484   2 return;
485   485  
486   // ready for next message 486   // ready for next message
HITCBC 487   50 reset(); 487   50 reset();
488   } 488   }
489   489  
490   void 490   void
HITCBC 491   159 start_init( 491   159 start_init(
492   message_base const& m) 492   message_base const& m)
493   { 493   {
494   // Precondition violation 494   // Precondition violation
HITCBC 495   159 if(state_ != state::start) 495   159 if(state_ != state::start)
MISUBC 496   detail::throw_logic_error(); 496   detail::throw_logic_error();
497   497  
498   // TODO: To uphold the strong exception guarantee, 498   // TODO: To uphold the strong exception guarantee,
499   // `state_` must be reset to `state::start` if an 499   // `state_` must be reset to `state::start` if an
500   // exception is thrown during the start operation. 500   // exception is thrown during the start operation.
HITCBC 501   159 state_ = state::header; 501   159 state_ = state::header;
502   502  
503   // VFALCO what do we do with 503   // VFALCO what do we do with
504   // metadata error code failures? 504   // metadata error code failures?
505   // m.h_.md.maybe_throw(); 505   // m.h_.md.maybe_throw();
506   506  
HITCBC 507   159 auto const& md = m.metadata(); 507   159 auto const& md = m.metadata();
HITCBC 508   159 needs_exp100_continue_ = md.expect.is_100_continue; 508   159 needs_exp100_continue_ = md.expect.is_100_continue;
509   509  
510   // Transfer-Encoding 510   // Transfer-Encoding
HITCBC 511   159 is_chunked_ = md.transfer_encoding.is_chunked; 511   159 is_chunked_ = md.transfer_encoding.is_chunked;
512   512  
513   // Content-Encoding 513   // Content-Encoding
HITCBC 514   159 switch (md.content_encoding.coding) 514   159 switch (md.content_encoding.coding)
515   { 515   {
MISUBC 516   case content_coding::deflate: 516   case content_coding::deflate:
MISUBC 517   if(!cfg_->apply_deflate_encoder) 517   if(!cfg_->apply_deflate_encoder)
MISUBC 518   goto no_filter; 518   goto no_filter;
MISUBC 519   if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>()) 519   if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
520   { 520   {
MISUBC 521   filter_.reset(new zlib_filter( 521   filter_.reset(new zlib_filter(
522   *svc, 522   *svc,
MISUBC 523   cfg_->zlib_comp_level, 523   cfg_->zlib_comp_level,
MISUBC 524   cfg_->zlib_window_bits, 524   cfg_->zlib_window_bits,
MISUBC 525   cfg_->zlib_mem_level)); 525   cfg_->zlib_mem_level));
MISUBC 526   filter_done_ = false; 526   filter_done_ = false;
527   } 527   }
MISUBC 528   break; 528   break;
529   529  
MISUBC 530   case content_coding::gzip: 530   case content_coding::gzip:
MISUBC 531   if(!cfg_->apply_gzip_encoder) 531   if(!cfg_->apply_gzip_encoder)
MISUBC 532   goto no_filter; 532   goto no_filter;
MISUBC 533   if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>()) 533   if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
534   { 534   {
MISUBC 535   filter_.reset(new zlib_filter( 535   filter_.reset(new zlib_filter(
536   *svc, 536   *svc,
MISUBC 537   cfg_->zlib_comp_level, 537   cfg_->zlib_comp_level,
MISUBC 538   cfg_->zlib_window_bits + 16, 538   cfg_->zlib_window_bits + 16,
MISUBC 539   cfg_->zlib_mem_level)); 539   cfg_->zlib_mem_level));
MISUBC 540   filter_done_ = false; 540   filter_done_ = false;
541   } 541   }
MISUBC 542   break; 542   break;
543   543  
MISUBC 544   case content_coding::br: 544   case content_coding::br:
MISUBC 545   if(!cfg_->apply_brotli_encoder) 545   if(!cfg_->apply_brotli_encoder)
MISUBC 546   goto no_filter; 546   goto no_filter;
MISUBC 547   if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>()) 547   if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>())
548   { 548   {
MISUBC 549   filter_.reset(new brotli_filter( 549   filter_.reset(new brotli_filter(
550   *svc, 550   *svc,
MISUBC 551   cfg_->brotli_comp_quality, 551   cfg_->brotli_comp_quality,
MISUBC 552   cfg_->brotli_comp_window)); 552   cfg_->brotli_comp_window));
MISUBC 553   filter_done_ = false; 553   filter_done_ = false;
554   } 554   }
MISUBC 555   break; 555   break;
556   556  
MISUBC 557   no_filter: 557   no_filter:
HITCBC 558   159 default: 558   159 default:
HITCBC 559   159 filter_.reset(); 559   159 filter_.reset();
HITCBC 560   159 break; 560   159 break;
561   } 561   }
HITCBC 562   159 } 562   159 }
563   563  
564   void 564   void
HITCBC 565   6 start_empty( 565   6 start_empty(
566   message_base const& m) 566   message_base const& m)
567   { 567   {
HITCBC 568   6 start_init(m); 568   6 start_init(m);
HITCBC 569   6 style_ = style::empty; 569   6 style_ = style::empty;
570   570  
HITCBC 571   6 prepped_ = make_array( 571   6 prepped_ = make_array(
572   1 + // header 572   1 + // header
573   2); // out buffer pairs 573   2); // out buffer pairs
574   574  
HITCBC 575   6 out_init(); 575   6 out_init();
576   576  
HITCBC 577   6 if(!filter_) 577   6 if(!filter_)
HITCBC 578   6 out_finish(); 578   6 out_finish();
579   579  
HITCBC 580   6 prepped_.append({ m.h_.cbuf, m.h_.size }); 580   6 prepped_.append({ m.h_.cbuf, m.h_.size });
HITCBC 581   6 more_input_ = false; 581   6 more_input_ = false;
HITCBC 582   6 } 582   6 }
583   583  
584   void 584   void
HITCBC 585   78 start_stream(message_base const& m) 585   78 start_stream(message_base const& m)
586   { 586   {
HITCBC 587   78 start_init(m); 587   78 start_init(m);
HITCBC 588   78 style_ = style::stream; 588   78 style_ = style::stream;
589   589  
HITCBC 590   78 prepped_ = make_array( 590   78 prepped_ = make_array(
591   1 + // header 591   1 + // header
592   2); // out buffer pairs 592   2); // out buffer pairs
593   593  
HITCBC 594   78 if(filter_) 594   78 if(filter_)
595   { 595   {
596   // TODO: smarter buffer distribution 596   // TODO: smarter buffer distribution
MISUBC 597   auto const n = (ws_.size() - 1) / 2; 597   auto const n = (ws_.size() - 1) / 2;
MISUBC 598   in_ = { ws_.reserve_front(n), n }; 598   in_ = { ws_.reserve_front(n), n };
599   } 599   }
600   600  
HITCBC 601   78 out_init(); 601   78 out_init();
602   602  
HITCBC 603   78 prepped_.append({ m.h_.cbuf, m.h_.size }); 603   78 prepped_.append({ m.h_.cbuf, m.h_.size });
HITCBC 604   78 more_input_ = true; 604   78 more_input_ = true;
HITCBC 605   78 } 605   78 }
606   606  
607   // Like start_stream but without in_ allocation. 607   // Like start_stream but without in_ allocation.
608   // Entire workspace is used for output buffering. 608   // Entire workspace is used for output buffering.
609   void 609   void
HITCBC 610   75 start_buffers_direct(message_base const& m) 610   75 start_buffers_direct(message_base const& m)
611   { 611   {
HITCBC 612   75 start_init(m); 612   75 start_init(m);
HITCBC 613   75 style_ = style::stream; 613   75 style_ = style::stream;
614   614  
HITCBC 615   75 prepped_ = make_array( 615   75 prepped_ = make_array(
616   1 + // header 616   1 + // header
617   2); // out buffer pairs 617   2); // out buffer pairs
618   618  
HITCBC 619   75 out_init(); 619   75 out_init();
620   620  
HITCBC 621   75 prepped_.append({ m.h_.cbuf, m.h_.size }); 621   75 prepped_.append({ m.h_.cbuf, m.h_.size });
HITCBC 622   75 more_input_ = true; 622   75 more_input_ = true;
HITCBC 623   75 } 623   75 }
624   624  
625   std::size_t 625   std::size_t
HITCBC 626   182 stream_capacity() const 626   182 stream_capacity() const
627   { 627   {
HITCBC 628   182 if(filter_) 628   182 if(filter_)
MISUBC 629   return in_.capacity(); 629   return in_.capacity();
HITCBC 630   182 return out_capacity(); 630   182 return out_capacity();
631   } 631   }
632   632  
633   std::array<capy::mutable_buffer, 2> 633   std::array<capy::mutable_buffer, 2>
HITCBC 634   129 stream_prepare() 634   129 stream_prepare()
635   { 635   {
HITCBC 636   129 if(state_ == state::start) 636   129 if(state_ == state::start)
637   { 637   {
MISUBC 638   if(!msg_) 638   if(!msg_)
MISUBC 639   detail::throw_logic_error(); 639   detail::throw_logic_error();
MISUBC 640   start_stream(*msg_); 640   start_stream(*msg_);
641   } 641   }
HITCBC 642   129 if(filter_) 642   129 if(filter_)
MISUBC 643   return in_.prepare(in_.capacity()); 643   return in_.prepare(in_.capacity());
HITCBC 644   129 return out_prepare(); 644   129 return out_prepare();
645   } 645   }
646   646  
647   void 647   void
HITCBC 648   165 stream_commit(std::size_t n) 648   165 stream_commit(std::size_t n)
649   { 649   {
HITCBC 650   165 if(n > stream_capacity()) 650   165 if(n > stream_capacity())
HITCBC 651   1 detail::throw_invalid_argument(); 651   1 detail::throw_invalid_argument();
652   652  
HITCBC 653   164 if(filter_) 653   164 if(filter_)
MISUBC 654   return in_.commit(n); 654   return in_.commit(n);
655   655  
HITCBC 656   164 out_commit(n); 656   164 out_commit(n);
657   } 657   }
658   658  
659   void 659   void
HITCBC 660   75 stream_close() noexcept 660   75 stream_close() noexcept
661   { 661   {
HITCBC 662   75 if(!filter_) 662   75 if(!filter_)
HITCBC 663   75 out_finish(); 663   75 out_finish();
664   664  
HITCBC 665   75 more_input_ = false; 665   75 more_input_ = false;
HITCBC 666   75 } 666   75 }
667   667  
668   bool 668   bool
HITCBC 669   464 is_done() const noexcept 669   464 is_done() const noexcept
670   { 670   {
HITCBC 671   464 return state_ == state::start; 671   464 return state_ == state::start;
672   } 672   }
673   673  
674   bool 674   bool
HITCBC 675   249 is_start() const noexcept 675   249 is_start() const noexcept
676   { 676   {
HITCBC 677   249 return state_ == state::start; 677   249 return state_ == state::start;
678   } 678   }
679   679  
680   detail::workspace& 680   detail::workspace&
MISUBC 681   ws() noexcept 681   ws() noexcept
682   { 682   {
MISUBC 683   return ws_; 683   return ws_;
684   } 684   }
685   685  
686   private: 686   private:
687   bool 687   bool
HITCBC 688   2384 is_header_done() const noexcept 688   2384 is_header_done() const noexcept
689   { 689   {
HITCBC 690   2384 return state_ == state::body; 690   2384 return state_ == state::body;
691   } 691   }
692   692  
693   detail::array_of_const_buffers 693   detail::array_of_const_buffers
HITCBC 694   159 make_array(std::size_t n) 694   159 make_array(std::size_t n)
695   { 695   {
HITCBC 696   159 BOOST_ASSERT(n <= std::uint16_t(-1)); 696   159 BOOST_ASSERT(n <= std::uint16_t(-1));
697   697  
698   return { 698   return {
HITCBC 699   159 ws_.push_array(n, 699   159 ws_.push_array(n,
MISUBC 700   capy::const_buffer{}), 700   capy::const_buffer{}),
HITCBC 701   159 static_cast<std::uint16_t>(n) }; 701   159 static_cast<std::uint16_t>(n) };
702   } 702   }
703   703  
704   void 704   void
HITCBC 705   159 out_init() 705   159 out_init()
706   { 706   {
707   // use all the remaining buffer 707   // use all the remaining buffer
HITCBC 708   159 auto const n = ws_.size() - 1; 708   159 auto const n = ws_.size() - 1;
HITCBC 709   159 out_ = { ws_.reserve_front(n), n }; 709   159 out_ = { ws_.reserve_front(n), n };
HITCBC 710   159 chunk_header_len_ = 710   159 chunk_header_len_ =
HITCBC 711   159 chunk_header_len(out_.capacity()); 711   159 chunk_header_len(out_.capacity());
HITCBC 712   159 if(out_capacity() == 0) 712   159 if(out_capacity() == 0)
MISUBC 713   detail::throw_length_error(); 713   detail::throw_length_error();
HITCBC 714   159 } 714   159 }
715   715  
716   std::array<capy::mutable_buffer, 2> 716   std::array<capy::mutable_buffer, 2>
HITCBC 717   129 out_prepare() noexcept 717   129 out_prepare() noexcept
718   { 718   {
HITCBC 719   129 auto mbp = out_.prepare(out_.capacity()); 719   129 auto mbp = out_.prepare(out_.capacity());
HITCBC 720   129 if(is_chunked_) 720   129 if(is_chunked_)
721   { 721   {
HITCBC 722   69 trim_prefix_pair( 722   69 trim_prefix_pair(
HITCBC 723   69 mbp, chunk_header_len_); 723   69 mbp, chunk_header_len_);
HITCBC 724   69 trim_suffix_pair( 724   69 trim_suffix_pair(
725   mbp, crlf_and_final_chunk.size()); 725   mbp, crlf_and_final_chunk.size());
726   } 726   }
HITCBC 727   129 return mbp; 727   129 return mbp;
728   } 728   }
729   729  
730   void 730   void
HITCBC 731   164 out_commit( 731   164 out_commit(
732   std::size_t n) noexcept 732   std::size_t n) noexcept
733   { 733   {
HITCBC 734   164 if(is_chunked_) 734   164 if(is_chunked_)
735   { 735   {
HITCBC 736   87 if(n == 0) 736   87 if(n == 0)
HITCBC 737   19 return; 737   19 return;
738   738  
HITCBC 739   68 write_chunk_header(out_.prepare(chunk_header_len_), n); 739   68 write_chunk_header(out_.prepare(chunk_header_len_), n);
HITCBC 740   68 out_.commit(chunk_header_len_); 740   68 out_.commit(chunk_header_len_);
741   741  
HITCBC 742   68 out_.prepare(n); 742   68 out_.prepare(n);
HITCBC 743   68 out_.commit(n); 743   68 out_.commit(n);
744   744  
HITCBC 745   68 capy::buffer_copy(out_.prepare(crlf.size()), crlf); 745   68 capy::buffer_copy(out_.prepare(crlf.size()), crlf);
HITCBC 746   68 out_.commit(crlf.size()); 746   68 out_.commit(crlf.size());
747   } 747   }
748   else 748   else
749   { 749   {
HITCBC 750   77 out_.commit(n); 750   77 out_.commit(n);
751   } 751   }
752   } 752   }
753   753  
754   std::size_t 754   std::size_t
HITCBC 755   341 out_capacity() const noexcept 755   341 out_capacity() const noexcept
756   { 756   {
HITCBC 757   341 if(is_chunked_) 757   341 if(is_chunked_)
758   { 758   {
HITCBC 759   174 auto const overhead = chunk_header_len_ + 759   174 auto const overhead = chunk_header_len_ +
HITCBC 760   174 crlf_and_final_chunk.size(); 760   174 crlf_and_final_chunk.size();
HITCBC 761   174 if(out_.capacity() < overhead) 761   174 if(out_.capacity() < overhead)
HITCBC 762   1 return 0; 762   1 return 0;
HITCBC 763   173 return out_.capacity() - overhead; 763   173 return out_.capacity() - overhead;
764   } 764   }
HITCBC 765   167 return out_.capacity(); 765   167 return out_.capacity();
766   } 766   }
767   767  
768   void 768   void
HITCBC 769   81 out_finish() noexcept 769   81 out_finish() noexcept
770   { 770   {
HITCBC 771   81 if(is_chunked_) 771   81 if(is_chunked_)
772   { 772   {
HITCBC 773   41 capy::buffer_copy( 773   41 capy::buffer_copy(
HITCBC 774   41 out_.prepare(final_chunk.size()), final_chunk); 774   41 out_.prepare(final_chunk.size()), final_chunk);
HITCBC 775   41 out_.commit(final_chunk.size()); 775   41 out_.commit(final_chunk.size());
776   } 776   }
HITCBC 777   81 } 777   81 }
778   }; 778   };
779   779  
780   //------------------------------------------------ 780   //------------------------------------------------
781   781  
HITCBC 782   163 serializer:: 782   163 serializer::
783   ~serializer() 783   ~serializer()
784   { 784   {
HITCBC 785   163 delete impl_; 785   163 delete impl_;
HITCBC 786   163 } 786   163 }
787   787  
HITCBC 788   1 serializer:: 788   1 serializer::
HITCBC 789   1 serializer(serializer&& other) noexcept 789   1 serializer(serializer&& other) noexcept
HITCBC 790   1 : impl_(other.impl_) 790   1 : impl_(other.impl_)
791   { 791   {
HITCBC 792   1 other.impl_ = nullptr; 792   1 other.impl_ = nullptr;
HITCBC 793   1 } 793   1 }
794   794  
795   serializer& 795   serializer&
HITCBC 796   2 serializer:: 796   2 serializer::
797   operator=(serializer&& other) noexcept 797   operator=(serializer&& other) noexcept
798   { 798   {
HITCBC 799   2 if(this != &other) 799   2 if(this != &other)
800   { 800   {
HITCBC 801   2 delete impl_; 801   2 delete impl_;
HITCBC 802   2 impl_ = other.impl_; 802   2 impl_ = other.impl_;
HITCBC 803   2 other.impl_ = nullptr; 803   2 other.impl_ = nullptr;
804   } 804   }
HITCBC 805   2 return *this; 805   2 return *this;
806   } 806   }
807   807  
HITCBC 808   158 serializer:: 808   158 serializer::
809   serializer( 809   serializer(
HITCBC 810   158 std::shared_ptr<serializer_config_impl const> cfg) 810   158 std::shared_ptr<serializer_config_impl const> cfg)
HITCBC 811   158 : impl_(new impl(std::move(cfg))) 811   158 : impl_(new impl(std::move(cfg)))
812   { 812   {
HITCBC 813   158 } 813   158 }
814   814  
815   void 815   void
HITCBC 816   4 serializer:: 816   4 serializer::
817   reset() noexcept 817   reset() noexcept
818   { 818   {
HITCBC 819   4 BOOST_ASSERT(impl_); 819   4 BOOST_ASSERT(impl_);
HITCBC 820   4 impl_->reset(); 820   4 impl_->reset();
HITCBC 821   4 } 821   4 }
822   822  
823   void 823   void
HITCBC 824   159 serializer:: 824   159 serializer::
825   set_message(message_base const& m) noexcept 825   set_message(message_base const& m) noexcept
826   { 826   {
HITCBC 827   159 BOOST_ASSERT(impl_); 827   159 BOOST_ASSERT(impl_);
HITCBC 828   159 impl_->msg_ = &m; 828   159 impl_->msg_ = &m;
HITCBC 829   159 } 829   159 }
830   830  
831   void 831   void
HITCBC 832   6 serializer:: 832   6 serializer::
833   start() 833   start()
834   { 834   {
HITCBC 835   6 if(!impl_ || !impl_->msg_) 835   6 if(!impl_ || !impl_->msg_)
MISUBC 836   detail::throw_logic_error(); 836   detail::throw_logic_error();
HITCBC 837   6 impl_->start_empty(*impl_->msg_); 837   6 impl_->start_empty(*impl_->msg_);
HITCBC 838   6 } 838   6 }
839   839  
840   void 840   void
MISUBC 841   serializer:: 841   serializer::
842   start_stream() 842   start_stream()
843   { 843   {
MISUBC 844   if(!impl_ || !impl_->msg_) 844   if(!impl_ || !impl_->msg_)
MISUBC 845   detail::throw_logic_error(); 845   detail::throw_logic_error();
MISUBC 846   impl_->start_stream(*impl_->msg_); 846   impl_->start_stream(*impl_->msg_);
MISUBC 847   } 847   }
848   848  
849   void 849   void
HITCBC 850   78 serializer:: 850   78 serializer::
851   start_writes() 851   start_writes()
852   { 852   {
HITCBC 853   78 if(!impl_ || !impl_->msg_) 853   78 if(!impl_ || !impl_->msg_)
MISUBC 854   detail::throw_logic_error(); 854   detail::throw_logic_error();
HITCBC 855   78 impl_->start_stream(*impl_->msg_); 855   78 impl_->start_stream(*impl_->msg_);
HITCBC 856   78 } 856   78 }
857   857  
858   void 858   void
HITCBC 859   75 serializer:: 859   75 serializer::
860   start_buffers() 860   start_buffers()
861   { 861   {
HITCBC 862   75 if(!impl_ || !impl_->msg_) 862   75 if(!impl_ || !impl_->msg_)
MISUBC 863   detail::throw_logic_error(); 863   detail::throw_logic_error();
HITCBC 864   75 impl_->start_buffers_direct(*impl_->msg_); 864   75 impl_->start_buffers_direct(*impl_->msg_);
HITCBC 865   75 } 865   75 }
866   866  
867   auto 867   auto
HITCBC 868   389 serializer:: 868   389 serializer::
869   prepare() -> 869   prepare() ->
870   system::result<const_buffers_type> 870   system::result<const_buffers_type>
871   { 871   {
HITCBC 872   389 BOOST_ASSERT(impl_); 872   389 BOOST_ASSERT(impl_);
HITCBC 873   389 return impl_->prepare(); 873   389 return impl_->prepare();
874   } 874   }
875   875  
876   void 876   void
HITCBC 877   1902 serializer:: 877   1902 serializer::
878   consume(std::size_t n) 878   consume(std::size_t n)
879   { 879   {
HITCBC 880   1902 BOOST_ASSERT(impl_); 880   1902 BOOST_ASSERT(impl_);
HITCBC 881   1902 impl_->consume(n); 881   1902 impl_->consume(n);
HITCBC 882   1901 } 882   1901 }
883   883  
884   bool 884   bool
HITCBC 885   464 serializer:: 885   464 serializer::
886   is_done() const noexcept 886   is_done() const noexcept
887   { 887   {
HITCBC 888   464 BOOST_ASSERT(impl_); 888   464 BOOST_ASSERT(impl_);
HITCBC 889   464 return impl_->is_done(); 889   464 return impl_->is_done();
890   } 890   }
891   891  
892   bool 892   bool
HITCBC 893   249 serializer:: 893   249 serializer::
894   is_start() const noexcept 894   is_start() const noexcept
895   { 895   {
HITCBC 896   249 BOOST_ASSERT(impl_); 896   249 BOOST_ASSERT(impl_);
HITCBC 897   249 return impl_->is_start(); 897   249 return impl_->is_start();
898   } 898   }
899   899  
900   //------------------------------------------------ 900   //------------------------------------------------
901   901  
902   detail::workspace& 902   detail::workspace&
MISUBC 903   serializer:: 903   serializer::
904   ws() 904   ws()
905   { 905   {
MISUBC 906   BOOST_ASSERT(impl_); 906   BOOST_ASSERT(impl_);
MISUBC 907   return impl_->ws(); 907   return impl_->ws();
908   } 908   }
909   909  
910   //------------------------------------------------ 910   //------------------------------------------------
911   911  
912   std::size_t 912   std::size_t
HITCBC 913   17 serializer:: 913   17 serializer::
914   stream_capacity() const 914   stream_capacity() const
915   { 915   {
HITCBC 916   17 BOOST_ASSERT(impl_); 916   17 BOOST_ASSERT(impl_);
HITCBC 917   17 return impl_->stream_capacity(); 917   17 return impl_->stream_capacity();
918   } 918   }
919   919  
920   auto 920   auto
HITCBC 921   129 serializer:: 921   129 serializer::
922   stream_prepare() -> 922   stream_prepare() ->
923   mutable_buffers_type 923   mutable_buffers_type
924   { 924   {
HITCBC 925   129 BOOST_ASSERT(impl_); 925   129 BOOST_ASSERT(impl_);
HITCBC 926   129 return impl_->stream_prepare(); 926   129 return impl_->stream_prepare();
927   } 927   }
928   928  
929   void 929   void
HITCBC 930   165 serializer:: 930   165 serializer::
931   stream_commit(std::size_t n) 931   stream_commit(std::size_t n)
932   { 932   {
HITCBC 933   165 BOOST_ASSERT(impl_); 933   165 BOOST_ASSERT(impl_);
HITCBC 934   165 impl_->stream_commit(n); 934   165 impl_->stream_commit(n);
HITCBC 935   164 } 935   164 }
936   936  
937   void 937   void
HITCBC 938   75 serializer:: 938   75 serializer::
939   stream_close() noexcept 939   stream_close() noexcept
940   { 940   {
HITCBC 941   75 BOOST_ASSERT(impl_); 941   75 BOOST_ASSERT(impl_);
HITCBC 942   75 impl_->stream_close(); 942   75 impl_->stream_close();
HITCBC 943   75 } 943   75 }
944   944  
945   } // http 945   } // http
946   } // boost 946   } // boost