100.00% Lines (27/27) 100.00% Functions (9/9)
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) 2025 Mohammad Nejati 3   // Copyright (c) 2025 Mohammad Nejati
4   // 4   //
5   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7   // 7   //
8   // Official repository: https://github.com/cppalliance/http 8   // Official repository: https://github.com/cppalliance/http
9   // 9   //
10   10  
11   #ifndef BOOST_HTTP_SERIALIZER_HPP 11   #ifndef BOOST_HTTP_SERIALIZER_HPP
12   #define BOOST_HTTP_SERIALIZER_HPP 12   #define BOOST_HTTP_SERIALIZER_HPP
13   13  
14   #include <boost/http/config.hpp> 14   #include <boost/http/config.hpp>
15   #include <boost/http/detail/workspace.hpp> 15   #include <boost/http/detail/workspace.hpp>
16   #include <boost/http/error.hpp> 16   #include <boost/http/error.hpp>
17   17  
18   #include <boost/capy/buffers.hpp> 18   #include <boost/capy/buffers.hpp>
19   #include <boost/capy/concept/buffer_sink.hpp> 19   #include <boost/capy/concept/buffer_sink.hpp>
20   #include <boost/capy/concept/write_stream.hpp> 20   #include <boost/capy/concept/write_stream.hpp>
21   #include <boost/capy/io_task.hpp> 21   #include <boost/capy/io_task.hpp>
22   #include <boost/core/span.hpp> 22   #include <boost/core/span.hpp>
23   #include <boost/system/result.hpp> 23   #include <boost/system/result.hpp>
24   24  
25   #include <array> 25   #include <array>
26   #include <cstddef> 26   #include <cstddef>
27   #include <cstring> 27   #include <cstring>
28   #include <type_traits> 28   #include <type_traits>
29   #include <utility> 29   #include <utility>
30   30  
31   namespace boost { 31   namespace boost {
32   namespace http { 32   namespace http {
33   33  
34   // Forward declaration 34   // Forward declaration
35   class message_base; 35   class message_base;
36   36  
37   //------------------------------------------------ 37   //------------------------------------------------
38   38  
39   /** A serializer for HTTP/1 messages. 39   /** A serializer for HTTP/1 messages.
40   40  
41   Transforms one or more HTTP/1 messages into bytes for 41   Transforms one or more HTTP/1 messages into bytes for
42   transmission. Each message consists of a required header 42   transmission. Each message consists of a required header
43   followed by an optional body. 43   followed by an optional body.
44   44  
45   Use @ref set_message to associate a message, then choose 45   Use @ref set_message to associate a message, then choose
46   a body mode: 46   a body mode:
47   47  
48   @li @ref start — empty body (header only) 48   @li @ref start — empty body (header only)
49   @li @ref start_writes — body via internal buffer 49   @li @ref start_writes — body via internal buffer
50   (BufferSink path) 50   (BufferSink path)
51   @li @ref start_buffers — body via caller-owned buffers 51   @li @ref start_buffers — body via caller-owned buffers
52   (WriteSink path) 52   (WriteSink path)
53   53  
54   Alternatively, obtain a @ref sink via @ref sink_for and 54   Alternatively, obtain a @ref sink via @ref sink_for and
55   let it start the serializer lazily on first use. 55   let it start the serializer lazily on first use.
56   56  
57   The caller must ensure that the associated message is not 57   The caller must ensure that the associated message is not
58   changed or destroyed until @ref is_done returns true, 58   changed or destroyed until @ref is_done returns true,
59   @ref reset is called, or the serializer is destroyed. 59   @ref reset is called, or the serializer is destroyed.
60   60  
61   @par Example 61   @par Example
62   @code 62   @code
63   http::serializer sr(cfg); 63   http::serializer sr(cfg);
64   http::response res; 64   http::response res;
65   res.set_payload_size(5); 65   res.set_payload_size(5);
66   sr.set_message(res); 66   sr.set_message(res);
67   67  
68   auto sink = sr.sink_for(socket); 68   auto sink = sr.sink_for(socket);
69   co_await sink.write_eof( 69   co_await sink.write_eof(
70   capy::make_buffer(std::string_view("hello"))); 70   capy::make_buffer(std::string_view("hello")));
71   @endcode 71   @endcode
72   72  
73   @see @ref sink, @ref set_message. 73   @see @ref sink, @ref set_message.
74   */ 74   */
75   class serializer 75   class serializer
76   { 76   {
77   public: 77   public:
78   template<capy::WriteStream Stream> 78   template<capy::WriteStream Stream>
79   class sink; 79   class sink;
80   80  
81   /** The type used to represent a sequence 81   /** The type used to represent a sequence
82   of mutable buffers for streaming. 82   of mutable buffers for streaming.
83   */ 83   */
84   using mutable_buffers_type = 84   using mutable_buffers_type =
85   std::array<capy::mutable_buffer, 2>; 85   std::array<capy::mutable_buffer, 2>;
86   86  
87   /** The type used to represent a sequence of 87   /** The type used to represent a sequence of
88   constant buffers that refers to the output 88   constant buffers that refers to the output
89   area. 89   area.
90   */ 90   */
91   using const_buffers_type = 91   using const_buffers_type =
92   boost::span<capy::const_buffer const>; 92   boost::span<capy::const_buffer const>;
93   93  
94   /** Destructor 94   /** Destructor
95   */ 95   */
96   BOOST_HTTP_DECL 96   BOOST_HTTP_DECL
97   ~serializer(); 97   ~serializer();
98   98  
99   /** Default constructor. 99   /** Default constructor.
100   100  
101   Constructs a serializer with no allocated state. 101   Constructs a serializer with no allocated state.
102   The serializer must be assigned from a valid 102   The serializer must be assigned from a valid
103   serializer before use. 103   serializer before use.
104   104  
105   @par Postconditions 105   @par Postconditions
106   The serializer has no allocated state. 106   The serializer has no allocated state.
107   */ 107   */
HITCBC 108   2 serializer() = default; 108   2 serializer() = default;
109   109  
110   /** Constructor. 110   /** Constructor.
111   111  
112   Constructs a serializer with the provided configuration. 112   Constructs a serializer with the provided configuration.
113   113  
114   @par Postconditions 114   @par Postconditions
115   @code 115   @code
116   this->is_done() == true 116   this->is_done() == true
117   @endcode 117   @endcode
118   118  
119   @param cfg Shared pointer to serializer configuration. 119   @param cfg Shared pointer to serializer configuration.
120   120  
121   @see @ref make_serializer_config, @ref serializer_config. 121   @see @ref make_serializer_config, @ref serializer_config.
122   */ 122   */
123   BOOST_HTTP_DECL 123   BOOST_HTTP_DECL
124   explicit 124   explicit
125   serializer( 125   serializer(
126   std::shared_ptr<serializer_config_impl const> cfg); 126   std::shared_ptr<serializer_config_impl const> cfg);
127   127  
128   /** Constructor. 128   /** Constructor.
129   129  
130   The states of `other` are transferred 130   The states of `other` are transferred
131   to the newly constructed object, 131   to the newly constructed object,
132   which includes the allocated buffer. 132   which includes the allocated buffer.
133   After construction, the only valid 133   After construction, the only valid
134   operations on the moved-from object 134   operations on the moved-from object
135   are destruction and assignment. 135   are destruction and assignment.
136   136  
137   Buffer sequences previously obtained 137   Buffer sequences previously obtained
138   using @ref prepare remain valid. 138   using @ref prepare remain valid.
139   139  
140   @par Postconditions 140   @par Postconditions
141   @code 141   @code
142   other.is_done() == true 142   other.is_done() == true
143   @endcode 143   @endcode
144   144  
145   @par Complexity 145   @par Complexity
146   Constant. 146   Constant.
147   147  
148   @param other The serializer to move from. 148   @param other The serializer to move from.
149   */ 149   */
150   BOOST_HTTP_DECL 150   BOOST_HTTP_DECL
151   serializer( 151   serializer(
152   serializer&& other) noexcept; 152   serializer&& other) noexcept;
153   153  
154   /** Assignment. 154   /** Assignment.
155   The states of `other` are transferred 155   The states of `other` are transferred
156   to this object, which includes the 156   to this object, which includes the
157   allocated buffer. After assignment, 157   allocated buffer. After assignment,
158   the only valid operations on the 158   the only valid operations on the
159   moved-from object are destruction and 159   moved-from object are destruction and
160   assignment. 160   assignment.
161   Buffer sequences previously obtained 161   Buffer sequences previously obtained
162   using @ref prepare remain valid. 162   using @ref prepare remain valid.
163   @par Complexity 163   @par Complexity
164   Constant. 164   Constant.
165   @param other The serializer to move from. 165   @param other The serializer to move from.
166   @return A reference to this object. 166   @return A reference to this object.
167   */ 167   */
168   BOOST_HTTP_DECL 168   BOOST_HTTP_DECL
169   serializer& 169   serializer&
170   operator=(serializer&& other) noexcept; 170   operator=(serializer&& other) noexcept;
171   171  
172   /** Reset the serializer for a new message. 172   /** Reset the serializer for a new message.
173   173  
174   Aborts any ongoing serialization and 174   Aborts any ongoing serialization and
175   prepares the serializer to start 175   prepares the serializer to start
176   serialization of a new message. 176   serialization of a new message.
177   */ 177   */
178   BOOST_HTTP_DECL 178   BOOST_HTTP_DECL
179   void 179   void
180   reset() noexcept; 180   reset() noexcept;
181   181  
182   /** Set the message to serialize. 182   /** Set the message to serialize.
183   183  
184   Associates a message with the serializer for subsequent 184   Associates a message with the serializer for subsequent
185   streaming operations. The message is not copied; the caller 185   streaming operations. The message is not copied; the caller
186   must ensure it remains valid until serialization completes. 186   must ensure it remains valid until serialization completes.
187   187  
188   @param m The message to associate. 188   @param m The message to associate.
189   */ 189   */
190   BOOST_HTTP_DECL 190   BOOST_HTTP_DECL
191   void 191   void
192   set_message(message_base const& m) noexcept; 192   set_message(message_base const& m) noexcept;
193   193  
194   /** Start serializing the associated message with an empty body. 194   /** Start serializing the associated message with an empty body.
195   195  
196   The message must be set beforehand using @ref set_message. 196   The message must be set beforehand using @ref set_message.
197   Use the prepare/consume loop to pull output bytes. 197   Use the prepare/consume loop to pull output bytes.
198   198  
199   @par Preconditions 199   @par Preconditions
200   A message was associated via @ref set_message. 200   A message was associated via @ref set_message.
201   201  
202   @par Exception Safety 202   @par Exception Safety
203   Strong guarantee. 203   Strong guarantee.
204   204  
205   @throw std::logic_error if no message is associated or 205   @throw std::logic_error if no message is associated or
206   `this->is_done() == false`. 206   `this->is_done() == false`.
207   207  
208   @throw std::length_error if there is insufficient internal buffer 208   @throw std::length_error if there is insufficient internal buffer
209   space to start the operation. 209   space to start the operation.
210   210  
211   @see @ref set_message, @ref prepare, @ref consume. 211   @see @ref set_message, @ref prepare, @ref consume.
212   */ 212   */
213   void 213   void
214   BOOST_HTTP_DECL 214   BOOST_HTTP_DECL
215   start(); 215   start();
216   216  
217   /** Start streaming the associated message. 217   /** Start streaming the associated message.
218   218  
219   Low-level entry point equivalent to @ref start_writes. 219   Low-level entry point equivalent to @ref start_writes.
220   Prefer using a @ref sink which starts lazily. 220   Prefer using a @ref sink which starts lazily.
221   221  
222   @par Preconditions 222   @par Preconditions
223   A message was associated via @ref set_message. 223   A message was associated via @ref set_message.
224   224  
225   @par Exception Safety 225   @par Exception Safety
226   Strong guarantee. 226   Strong guarantee.
227   227  
228   @throw std::logic_error if no message is associated or 228   @throw std::logic_error if no message is associated or
229   `this->is_done() == false`. 229   `this->is_done() == false`.
230   230  
231   @throw std::length_error if there is insufficient internal buffer 231   @throw std::length_error if there is insufficient internal buffer
232   space to start the operation. 232   space to start the operation.
233   233  
234   @see @ref start_writes, @ref sink. 234   @see @ref start_writes, @ref sink.
235   */ 235   */
236   BOOST_HTTP_DECL 236   BOOST_HTTP_DECL
237   void 237   void
238   start_stream(); 238   start_stream();
239   239  
240   /** Start the serializer in write mode. 240   /** Start the serializer in write mode.
241   241  
242   Prepares the serializer for write-mode streaming 242   Prepares the serializer for write-mode streaming
243   using the message previously set via @ref set_message. 243   using the message previously set via @ref set_message.
244   In this mode, the workspace is split into an input 244   In this mode, the workspace is split into an input
245   buffer and an output buffer. Use @ref stream_prepare, 245   buffer and an output buffer. Use @ref stream_prepare,
246   @ref stream_commit, and @ref stream_close to write 246   @ref stream_commit, and @ref stream_close to write
247   body data, or use the sink's BufferSink interface. 247   body data, or use the sink's BufferSink interface.
248   248  
249   @par Preconditions 249   @par Preconditions
250   A message was associated via @ref set_message. 250   A message was associated via @ref set_message.
251   @code 251   @code
252   this->is_done() == true 252   this->is_done() == true
253   @endcode 253   @endcode
254   254  
255   @par Exception Safety 255   @par Exception Safety
256   Strong guarantee. 256   Strong guarantee.
257   257  
258   @throw std::logic_error if no message is associated. 258   @throw std::logic_error if no message is associated.
259   259  
260   @throw std::length_error if there is insufficient internal buffer 260   @throw std::length_error if there is insufficient internal buffer
261   space to start the operation. 261   space to start the operation.
262   262  
263   @see @ref set_message, @ref sink. 263   @see @ref set_message, @ref sink.
264   */ 264   */
265   BOOST_HTTP_DECL 265   BOOST_HTTP_DECL
266   void 266   void
267   start_writes(); 267   start_writes();
268   268  
269   /** Start the serializer in buffer mode. 269   /** Start the serializer in buffer mode.
270   270  
271   Prepares the serializer for buffer-mode streaming 271   Prepares the serializer for buffer-mode streaming
272   using the message previously set via @ref set_message. 272   using the message previously set via @ref set_message.
273   In this mode, the entire workspace is used for output 273   In this mode, the entire workspace is used for output
274   buffering. The caller provides body data through the 274   buffering. The caller provides body data through the
275   sink's WriteSink methods (write, write_eof), passing 275   sink's WriteSink methods (write, write_eof), passing
276   their own buffers directly. 276   their own buffers directly.
277   277  
278   @par Preconditions 278   @par Preconditions
279   A message was associated via @ref set_message. 279   A message was associated via @ref set_message.
280   @code 280   @code
281   this->is_done() == true 281   this->is_done() == true
282   @endcode 282   @endcode
283   283  
284   @par Exception Safety 284   @par Exception Safety
285   Strong guarantee. 285   Strong guarantee.
286   286  
287   @throw std::logic_error if no message is associated. 287   @throw std::logic_error if no message is associated.
288   288  
289   @throw std::length_error if there is insufficient internal buffer 289   @throw std::length_error if there is insufficient internal buffer
290   space to start the operation. 290   space to start the operation.
291   291  
292   @see @ref set_message, @ref sink. 292   @see @ref set_message, @ref sink.
293   */ 293   */
294   BOOST_HTTP_DECL 294   BOOST_HTTP_DECL
295   void 295   void
296   start_buffers(); 296   start_buffers();
297   297  
298   /** Create a sink for writing body data. 298   /** Create a sink for writing body data.
299   299  
300   Returns a lightweight @ref sink handle that writes 300   Returns a lightweight @ref sink handle that writes
301   serialized body data to the provided stream. The sink 301   serialized body data to the provided stream. The sink
302   starts the serializer lazily on first use, so neither 302   starts the serializer lazily on first use, so neither
303   @ref start_writes nor @ref start_buffers need to be 303   @ref start_writes nor @ref start_buffers need to be
304   called beforehand. 304   called beforehand.
305   305  
306   The sink can be created once and reused across multiple 306   The sink can be created once and reused across multiple
307   messages. The serializer must outlive the sink. 307   messages. The serializer must outlive the sink.
308   308  
309   @par Example 309   @par Example
310   @code 310   @code
311   http::serializer sr(cfg); 311   http::serializer sr(cfg);
312   auto sink = sr.sink_for(socket); 312   auto sink = sr.sink_for(socket);
313   313  
314   http::response res; 314   http::response res;
315   res.set_payload_size(5); 315   res.set_payload_size(5);
316   sr.set_message(res); 316   sr.set_message(res);
317   co_await sink.write_eof( 317   co_await sink.write_eof(
318   capy::make_buffer(std::string_view("hello"))); 318   capy::make_buffer(std::string_view("hello")));
319   @endcode 319   @endcode
320   320  
321   @tparam Stream The output stream type satisfying 321   @tparam Stream The output stream type satisfying
322   @ref capy::WriteStream. 322   @ref capy::WriteStream.
323   323  
324   @param ws The output stream to write serialized data to. 324   @param ws The output stream to write serialized data to.
325   325  
326   @return A @ref sink object for writing body data. 326   @return A @ref sink object for writing body data.
327   327  
328   @see @ref sink, @ref set_message. 328   @see @ref sink, @ref set_message.
329   */ 329   */
330   template<capy::WriteStream Stream> 330   template<capy::WriteStream Stream>
331   sink<Stream> 331   sink<Stream>
332   sink_for(Stream& ws) noexcept; 332   sink_for(Stream& ws) noexcept;
333   333  
334   /** Return the output area. 334   /** Return the output area.
335   335  
336   This function serializes some or all of 336   This function serializes some or all of
337   the message and returns the corresponding 337   the message and returns the corresponding
338   output buffers. Afterward, a call to @ref 338   output buffers. Afterward, a call to @ref
339   consume is required to report the number 339   consume is required to report the number
340   of bytes used, if any. 340   of bytes used, if any.
341   341  
342   If the message includes an 342   If the message includes an
343   `Expect: 100-continue` header and the 343   `Expect: 100-continue` header and the
344   header section of the message has been 344   header section of the message has been
345   consumed, the returned result will contain 345   consumed, the returned result will contain
346   @ref error::expect_100_continue to 346   @ref error::expect_100_continue to
347   indicate that the header part of the 347   indicate that the header part of the
348   message is complete. The next call to @ref 348   message is complete. The next call to @ref
349   prepare will produce output. 349   prepare will produce output.
350   350  
351   When the serializer is in streaming mode, 351   When the serializer is in streaming mode,
352   the result may contain @ref error::need_data 352   the result may contain @ref error::need_data
353   to indicate that additional input is required 353   to indicate that additional input is required
354   to produce output. 354   to produce output.
355   355  
356   @par Preconditions 356   @par Preconditions
357   @code 357   @code
358   this->is_done() == false 358   this->is_done() == false
359   @endcode 359   @endcode
360   No unrecoverable error reported from previous calls. 360   No unrecoverable error reported from previous calls.
361   361  
362   @par Exception Safety 362   @par Exception Safety
363   Strong guarantee. 363   Strong guarantee.
364   364  
365   @throw std::logic_error 365   @throw std::logic_error
366   `this->is_done() == true`. 366   `this->is_done() == true`.
367   367  
368   @return A result containing @ref 368   @return A result containing @ref
369   const_buffers_type that represents the 369   const_buffers_type that represents the
370   output area or an error if any occurred. 370   output area or an error if any occurred.
371   371  
372   @see 372   @see
373   @ref consume, 373   @ref consume,
374   @ref is_done, 374   @ref is_done,
375   @ref const_buffers_type. 375   @ref const_buffers_type.
376   */ 376   */
377   BOOST_HTTP_DECL 377   BOOST_HTTP_DECL
378   auto 378   auto
379   prepare() -> 379   prepare() ->
380   system::result< 380   system::result<
381   const_buffers_type>; 381   const_buffers_type>;
382   382  
383   /** Consume bytes from the output area. 383   /** Consume bytes from the output area.
384   384  
385   This function should be called after one 385   This function should be called after one
386   or more bytes contained in the buffers 386   or more bytes contained in the buffers
387   provided in the prior call to @ref prepare 387   provided in the prior call to @ref prepare
388   have been used. 388   have been used.
389   389  
390   After a call to @ref consume, callers 390   After a call to @ref consume, callers
391   should check the return value of @ref 391   should check the return value of @ref
392   is_done to determine if the entire message 392   is_done to determine if the entire message
393   has been serialized. 393   has been serialized.
394   394  
395   @par Preconditions 395   @par Preconditions
396   @code 396   @code
397   this->is_done() == false 397   this->is_done() == false
398   @endcode 398   @endcode
399   399  
400   @par Exception Safety 400   @par Exception Safety
401   Strong guarantee. 401   Strong guarantee.
402   402  
403   @throw std::logic_error 403   @throw std::logic_error
404   `this->is_done() == true`. 404   `this->is_done() == true`.
405   405  
406   @param n The number of bytes to consume. 406   @param n The number of bytes to consume.
407   If `n` is greater than the size of the 407   If `n` is greater than the size of the
408   buffer returned from @ref prepared the 408   buffer returned from @ref prepared the
409   entire output sequence is consumed and no 409   entire output sequence is consumed and no
410   error is issued. 410   error is issued.
411   411  
412   @see 412   @see
413   @ref prepare, 413   @ref prepare,
414   @ref is_done, 414   @ref is_done,
415   @ref const_buffers_type. 415   @ref const_buffers_type.
416   */ 416   */
417   BOOST_HTTP_DECL 417   BOOST_HTTP_DECL
418   void 418   void
419   consume(std::size_t n); 419   consume(std::size_t n);
420   420  
421   /** Return true if serialization is complete. 421   /** Return true if serialization is complete.
422   */ 422   */
423   BOOST_HTTP_DECL 423   BOOST_HTTP_DECL
424   bool 424   bool
425   is_done() const noexcept; 425   is_done() const noexcept;
426   426  
427   /** Return true if serialization has not yet started. 427   /** Return true if serialization has not yet started.
428   */ 428   */
429   BOOST_HTTP_DECL 429   BOOST_HTTP_DECL
430   bool 430   bool
431   is_start() const noexcept; 431   is_start() const noexcept;
432   432  
433   /** Return the available capacity for streaming. 433   /** Return the available capacity for streaming.
434   434  
435   Returns the number of bytes that can be written 435   Returns the number of bytes that can be written
436   to the serializer's internal buffer. 436   to the serializer's internal buffer.
437   437  
438   @par Preconditions 438   @par Preconditions
439   The serializer is in streaming mode (after calling 439   The serializer is in streaming mode (after calling
440   @ref start_stream). 440   @ref start_stream).
441   441  
442   @par Exception Safety 442   @par Exception Safety
443   Strong guarantee. 443   Strong guarantee.
444   444  
445   @throw std::logic_error if not in streaming mode. 445   @throw std::logic_error if not in streaming mode.
446   */ 446   */
447   BOOST_HTTP_DECL 447   BOOST_HTTP_DECL
448   std::size_t 448   std::size_t
449   stream_capacity() const; 449   stream_capacity() const;
450   450  
451   /** Prepare a buffer for writing stream data. 451   /** Prepare a buffer for writing stream data.
452   452  
453   Returns a mutable buffer sequence representing 453   Returns a mutable buffer sequence representing
454   the writable bytes. Use @ref stream_commit to make the 454   the writable bytes. Use @ref stream_commit to make the
455   written data available to the serializer. 455   written data available to the serializer.
456   456  
457   All buffer sequences previously obtained 457   All buffer sequences previously obtained
458   using @ref stream_prepare are invalidated. 458   using @ref stream_prepare are invalidated.
459   459  
460   @par Preconditions 460   @par Preconditions
461   The serializer is in streaming mode. 461   The serializer is in streaming mode.
462   462  
463   @par Exception Safety 463   @par Exception Safety
464   Strong guarantee. 464   Strong guarantee.
465   465  
466   @return An instance of @ref mutable_buffers_type. 466   @return An instance of @ref mutable_buffers_type.
467   The underlying memory is owned by the serializer. 467   The underlying memory is owned by the serializer.
468   468  
469   @throw std::logic_error if not in streaming mode. 469   @throw std::logic_error if not in streaming mode.
470   470  
471   @see 471   @see
472   @ref stream_commit, 472   @ref stream_commit,
473   @ref stream_capacity. 473   @ref stream_capacity.
474   */ 474   */
475   BOOST_HTTP_DECL 475   BOOST_HTTP_DECL
476   mutable_buffers_type 476   mutable_buffers_type
477   stream_prepare(); 477   stream_prepare();
478   478  
479   /** Commit data to the serializer stream. 479   /** Commit data to the serializer stream.
480   480  
481   Makes `n` bytes available to the serializer. 481   Makes `n` bytes available to the serializer.
482   482  
483   All buffer sequences previously obtained 483   All buffer sequences previously obtained
484   using @ref stream_prepare are invalidated. 484   using @ref stream_prepare are invalidated.
485   485  
486   @par Preconditions 486   @par Preconditions
487   The serializer is in streaming mode and 487   The serializer is in streaming mode and
488   `n <= stream_capacity()`. 488   `n <= stream_capacity()`.
489   489  
490   @par Exception Safety 490   @par Exception Safety
491   Strong guarantee. 491   Strong guarantee.
492   Exceptions thrown on invalid input. 492   Exceptions thrown on invalid input.
493   493  
494   @param n The number of bytes to commit. 494   @param n The number of bytes to commit.
495   495  
496   @throw std::invalid_argument if `n > stream_capacity()`. 496   @throw std::invalid_argument if `n > stream_capacity()`.
497   497  
498   @throw std::logic_error if not in streaming mode. 498   @throw std::logic_error if not in streaming mode.
499   499  
500   @see 500   @see
501   @ref stream_prepare, 501   @ref stream_prepare,
502   @ref stream_capacity. 502   @ref stream_capacity.
503   */ 503   */
504   BOOST_HTTP_DECL 504   BOOST_HTTP_DECL
505   void 505   void
506   stream_commit(std::size_t n); 506   stream_commit(std::size_t n);
507   507  
508   /** Close the stream. 508   /** Close the stream.
509   509  
510   Notifies the serializer that the message body 510   Notifies the serializer that the message body
511   has ended. After calling this function, no more 511   has ended. After calling this function, no more
512   data can be written to the stream. 512   data can be written to the stream.
513   513  
514   @par Preconditions 514   @par Preconditions
515   The serializer is in streaming mode. 515   The serializer is in streaming mode.
516   516  
517   @par Postconditions 517   @par Postconditions
518   The stream is closed. 518   The stream is closed.
519   */ 519   */
520   BOOST_HTTP_DECL 520   BOOST_HTTP_DECL
521   void 521   void
522   stream_close() noexcept; 522   stream_close() noexcept;
523   523  
524   private: 524   private:
525   class impl; 525   class impl;
526   526  
527   BOOST_HTTP_DECL 527   BOOST_HTTP_DECL
528   detail::workspace& 528   detail::workspace&
529   ws(); 529   ws();
530   530  
531   impl* impl_ = nullptr; 531   impl* impl_ = nullptr;
532   }; 532   };
533   533  
534   //------------------------------------------------ 534   //------------------------------------------------
535   535  
536   /** A sink adapter for writing HTTP message bodies. 536   /** A sink adapter for writing HTTP message bodies.
537   537  
538   Wraps a @ref serializer and a @ref capy::WriteStream to 538   Wraps a @ref serializer and a @ref capy::WriteStream to
539   provide two interfaces for body writing: 539   provide two interfaces for body writing:
540   540  
541   @li **BufferSink** (@ref prepare / @ref commit / 541   @li **BufferSink** (@ref prepare / @ref commit /
542   @ref commit_eof) — write directly into the serializer's 542   @ref commit_eof) — write directly into the serializer's
543   internal buffer (zero-copy). Triggers @ref start_writes 543   internal buffer (zero-copy). Triggers @ref start_writes
544   lazily. 544   lazily.
545   @li **WriteSink** (@ref write / @ref write_eof) — pass 545   @li **WriteSink** (@ref write / @ref write_eof) — pass
546   caller-owned buffers; the sink copies data through the 546   caller-owned buffers; the sink copies data through the
547   serializer. Triggers @ref start_buffers lazily. 547   serializer. Triggers @ref start_buffers lazily.
548   548  
549   Both interfaces handle chunked framing, compression, and 549   Both interfaces handle chunked framing, compression, and
550   Content-Length validation automatically. 550   Content-Length validation automatically.
551   551  
552   The sink is a lightweight handle that can be created once 552   The sink is a lightweight handle that can be created once
553   and reused across multiple messages. The serializer and 553   and reused across multiple messages. The serializer and
554   stream must outlive the sink. 554   stream must outlive the sink.
555   555  
556   @tparam Stream The underlying stream type satisfying 556   @tparam Stream The underlying stream type satisfying
557   @ref capy::WriteStream. 557   @ref capy::WriteStream.
558   558  
559   @par Thread Safety 559   @par Thread Safety
560   Distinct objects: Safe. 560   Distinct objects: Safe.
561   Shared objects: Unsafe. 561   Shared objects: Unsafe.
562   562  
563   @par Example 563   @par Example
564   @code 564   @code
565   capy::task<> 565   capy::task<>
566   send_response(capy::WriteStream auto& socket) 566   send_response(capy::WriteStream auto& socket)
567   { 567   {
568   http::serializer sr(cfg); 568   http::serializer sr(cfg);
569   auto sink = sr.sink_for(socket); 569   auto sink = sr.sink_for(socket);
570   570  
571   http::response res; 571   http::response res;
572   res.set_payload_size(5); 572   res.set_payload_size(5);
573   sr.set_message(res); 573   sr.set_message(res);
574   574  
575   // WriteSink: pass your own buffer 575   // WriteSink: pass your own buffer
576   co_await sink.write_eof( 576   co_await sink.write_eof(
577   capy::make_buffer(std::string_view("hello"))); 577   capy::make_buffer(std::string_view("hello")));
578   } 578   }
579   @endcode 579   @endcode
580   580  
581   @see @ref capy::BufferSink, @ref capy::any_buffer_sink, 581   @see @ref capy::BufferSink, @ref capy::any_buffer_sink,
582   @ref serializer. 582   @ref serializer.
583   */ 583   */
584   template<capy::WriteStream Stream> 584   template<capy::WriteStream Stream>
585   class serializer::sink 585   class serializer::sink
586   { 586   {
587   Stream* stream_ = nullptr; 587   Stream* stream_ = nullptr;
588   serializer* sr_ = nullptr; 588   serializer* sr_ = nullptr;
589   589  
590   public: 590   public:
591   /** Constructor. 591   /** Constructor.
592   592  
593   A default-constructed sink is in an empty state. 593   A default-constructed sink is in an empty state.
594   */ 594   */
595   sink() noexcept = default; 595   sink() noexcept = default;
596   596  
597   /** Constructor. 597   /** Constructor.
598   598  
599   @param stream The underlying stream to write serialized data to. 599   @param stream The underlying stream to write serialized data to.
600   @param sr The serializer performing HTTP framing. 600   @param sr The serializer performing HTTP framing.
601   */ 601   */
HITCBC 602   143 sink( 602   143 sink(
603   Stream& stream, 603   Stream& stream,
604   serializer& sr) noexcept 604   serializer& sr) noexcept
HITCBC 605   143 : stream_(&stream) 605   143 : stream_(&stream)
HITCBC 606   143 , sr_(&sr) 606   143 , sr_(&sr)
607   { 607   {
HITCBC 608   143 } 608   143 }
609   609  
610   /** Prepare writable buffers. 610   /** Prepare writable buffers.
611   611  
612   Fills the provided span with mutable buffer descriptors 612   Fills the provided span with mutable buffer descriptors
613   pointing to the serializer's internal storage. This 613   pointing to the serializer's internal storage. This
614   operation is synchronous. Lazily starts the serializer 614   operation is synchronous. Lazily starts the serializer
615   in write mode if not already started. 615   in write mode if not already started.
616   616  
617   @param dest Span of mutable_buffer to fill. 617   @param dest Span of mutable_buffer to fill.
618   618  
619   @return A span of filled buffers. 619   @return A span of filled buffers.
620   */ 620   */
621   std::span<capy::mutable_buffer> 621   std::span<capy::mutable_buffer>
HITCBC 622   63 prepare(std::span<capy::mutable_buffer> dest) 622   63 prepare(std::span<capy::mutable_buffer> dest)
623   { 623   {
HITCBC 624   63 if(sr_->is_start()) 624   63 if(sr_->is_start())
HITCBC 625   57 sr_->start_writes(); 625   57 sr_->start_writes();
HITCBC 626   63 auto bufs = sr_->stream_prepare(); 626   63 auto bufs = sr_->stream_prepare();
HITCBC 627   63 std::size_t count = 0; 627   63 std::size_t count = 0;
HITCBC 628   126 for(auto const& b : bufs) 628   126 for(auto const& b : bufs)
629   { 629   {
HITCBC 630   126 if(count >= dest.size() || b.size() == 0) 630   126 if(count >= dest.size() || b.size() == 0)
HITCBC 631   63 break; 631   63 break;
HITCBC 632   63 dest[count++] = b; 632   63 dest[count++] = b;
633   } 633   }
HITCBC 634   126 return dest.first(count); 634   126 return dest.first(count);
635   } 635   }
636   636  
637   /** Commit bytes written to the prepared buffers. 637   /** Commit bytes written to the prepared buffers.
638   638  
639   Commits `n` bytes written to the buffers returned by the 639   Commits `n` bytes written to the buffers returned by the
640   most recent call to @ref prepare. The operation flushes 640   most recent call to @ref prepare. The operation flushes
641   serialized output to the underlying stream. 641   serialized output to the underlying stream.
642   642  
643   @param n The number of bytes to commit. 643   @param n The number of bytes to commit.
644   644  
645   @return An awaitable yielding `(error_code)`. 645   @return An awaitable yielding `(error_code)`.
646   */ 646   */
647   auto 647   auto
HITCBC 648   63 commit(std::size_t n) 648   63 commit(std::size_t n)
649   -> capy::io_task<> 649   -> capy::io_task<>
650   { 650   {
651   if(sr_->is_start()) 651   if(sr_->is_start())
652   sr_->start_writes(); 652   sr_->start_writes();
653   sr_->stream_commit(n); 653   sr_->stream_commit(n);
654   654  
655   while(!sr_->is_done()) 655   while(!sr_->is_done())
656   { 656   {
657   auto cbs = sr_->prepare(); 657   auto cbs = sr_->prepare();
658   if(cbs.has_error()) 658   if(cbs.has_error())
659   { 659   {
660   if(cbs.error() == error::need_data) 660   if(cbs.error() == error::need_data)
661   break; 661   break;
662   co_return {std::error_code(cbs.error())}; 662   co_return {std::error_code(cbs.error())};
663   } 663   }
664   664  
665   if(capy::buffer_empty(*cbs)) 665   if(capy::buffer_empty(*cbs))
666   { 666   {
667   // advance state machine 667   // advance state machine
668   sr_->consume(0); 668   sr_->consume(0);
669   continue; 669   continue;
670   } 670   }
671   671  
672   auto [ec, written] = co_await stream_->write_some(*cbs); 672   auto [ec, written] = co_await stream_->write_some(*cbs);
673   sr_->consume(written); 673   sr_->consume(written);
674   674  
675   if(ec) 675   if(ec)
676   co_return {ec}; 676   co_return {ec};
677   } 677   }
678   678  
679   co_return {}; 679   co_return {};
HITCBC 680   126 } 680   126 }
681   681  
682   /** Commit final bytes and signal end-of-stream. 682   /** Commit final bytes and signal end-of-stream.
683   683  
684   Commits `n` bytes written to the buffers returned by the 684   Commits `n` bytes written to the buffers returned by the
685   most recent call to @ref prepare and closes the body stream, 685   most recent call to @ref prepare and closes the body stream,
686   flushing any remaining serializer output to the underlying 686   flushing any remaining serializer output to the underlying
687   stream. For chunked encoding, this writes the final 687   stream. For chunked encoding, this writes the final
688   zero-length chunk. 688   zero-length chunk.
689   689  
690   @param n The number of bytes to commit. 690   @param n The number of bytes to commit.
691   691  
692   @return An awaitable yielding `(error_code)`. 692   @return An awaitable yielding `(error_code)`.
693   693  
694   @post The serializer's `is_done()` returns `true` on success. 694   @post The serializer's `is_done()` returns `true` on success.
695   */ 695   */
696   auto 696   auto
HITCBC 697   35 commit_eof(std::size_t n) 697   35 commit_eof(std::size_t n)
698   -> capy::io_task<> 698   -> capy::io_task<>
699   { 699   {
700   if(sr_->is_start()) 700   if(sr_->is_start())
701   sr_->start_writes(); 701   sr_->start_writes();
702   sr_->stream_commit(n); 702   sr_->stream_commit(n);
703   sr_->stream_close(); 703   sr_->stream_close();
704   704  
705   while(!sr_->is_done()) 705   while(!sr_->is_done())
706   { 706   {
707   auto cbs = sr_->prepare(); 707   auto cbs = sr_->prepare();
708   if(cbs.has_error()) 708   if(cbs.has_error())
709   { 709   {
710   if(cbs.error() == error::need_data) 710   if(cbs.error() == error::need_data)
711   continue; 711   continue;
712   co_return {std::error_code(cbs.error())}; 712   co_return {std::error_code(cbs.error())};
713   } 713   }
714   714  
715   if(capy::buffer_empty(*cbs)) 715   if(capy::buffer_empty(*cbs))
716   { 716   {
717   // advance state machine 717   // advance state machine
718   sr_->consume(0); 718   sr_->consume(0);
719   continue; 719   continue;
720   } 720   }
721   721  
722   auto [ec, written] = co_await stream_->write_some(*cbs); 722   auto [ec, written] = co_await stream_->write_some(*cbs);
723   sr_->consume(written); 723   sr_->consume(written);
724   724  
725   if(ec) 725   if(ec)
726   co_return {ec}; 726   co_return {ec};
727   } 727   }
728   728  
729   co_return {}; 729   co_return {};
HITCBC 730   70 } 730   70 }
731   731  
732   /** Write body data from caller-owned buffers. 732   /** Write body data from caller-owned buffers.
733   733  
734   Lazily starts the serializer in buffer mode if not 734   Lazily starts the serializer in buffer mode if not
735   already started. Writes all data from the provided 735   already started. Writes all data from the provided
736   buffers through the serializer to the underlying stream. 736   buffers through the serializer to the underlying stream.
737   737  
738   @param buffers The buffer sequence containing body data. 738   @param buffers The buffer sequence containing body data.
739   739  
740   @return An awaitable yielding `(error_code, std::size_t)`. 740   @return An awaitable yielding `(error_code, std::size_t)`.
741   The size_t is the total number of body bytes written. 741   The size_t is the total number of body bytes written.
742   */ 742   */
743   template<class ConstBufferSequence> 743   template<class ConstBufferSequence>
744   auto 744   auto
HITCBC 745   72 write(ConstBufferSequence const& buffers) 745   72 write(ConstBufferSequence const& buffers)
746   -> capy::io_task<std::size_t> 746   -> capy::io_task<std::size_t>
747   { 747   {
748   if(sr_->is_start()) 748   if(sr_->is_start())
749   sr_->start_buffers(); 749   sr_->start_buffers();
750   750  
751   // Drain header first 751   // Drain header first
752   while(!sr_->is_done()) 752   while(!sr_->is_done())
753   { 753   {
754   auto cbs = sr_->prepare(); 754   auto cbs = sr_->prepare();
755   if(cbs.has_error()) 755   if(cbs.has_error())
756   { 756   {
757   if(cbs.error() == error::need_data) 757   if(cbs.error() == error::need_data)
758   break; 758   break;
759   co_return {cbs.error(), 0}; 759   co_return {cbs.error(), 0};
760   } 760   }
761   761  
762   if(capy::buffer_empty(*cbs)) 762   if(capy::buffer_empty(*cbs))
763   { 763   {
764   // advance state machine 764   // advance state machine
765   sr_->consume(0); 765   sr_->consume(0);
766   continue; 766   continue;
767   } 767   }
768   768  
769   auto [ec, written] = co_await stream_->write_some(*cbs); 769   auto [ec, written] = co_await stream_->write_some(*cbs);
770   sr_->consume(written); 770   sr_->consume(written);
771   771  
772   if(ec) 772   if(ec)
773   co_return {ec, 0}; 773   co_return {ec, 0};
774   } 774   }
775   775  
776   // Write body data through stream_prepare/commit 776   // Write body data through stream_prepare/commit
777   std::size_t total = 0; 777   std::size_t total = 0;
778   for(auto it = capy::begin(buffers); 778   for(auto it = capy::begin(buffers);
779   it != capy::end(buffers); ++it) 779   it != capy::end(buffers); ++it)
780   { 780   {
781   capy::const_buffer src = *it; 781   capy::const_buffer src = *it;
782   while(src.size() != 0) 782   while(src.size() != 0)
783   { 783   {
784   auto mbp = sr_->stream_prepare(); 784   auto mbp = sr_->stream_prepare();
785   std::size_t copied = 0; 785   std::size_t copied = 0;
786   for(auto const& mb : mbp) 786   for(auto const& mb : mbp)
787   { 787   {
788   auto chunk = (std::min)( 788   auto chunk = (std::min)(
789   mb.size(), src.size()); 789   mb.size(), src.size());
790   if(chunk == 0) 790   if(chunk == 0)
791   break; 791   break;
792   std::memcpy(mb.data(), 792   std::memcpy(mb.data(),
793   src.data(), chunk); 793   src.data(), chunk);
794   src += chunk; 794   src += chunk;
795   copied += chunk; 795   copied += chunk;
796   } 796   }
797   sr_->stream_commit(copied); 797   sr_->stream_commit(copied);
798   total += copied; 798   total += copied;
799   799  
800   // Drain output 800   // Drain output
801   while(!sr_->is_done()) 801   while(!sr_->is_done())
802   { 802   {
803   auto cbs = sr_->prepare(); 803   auto cbs = sr_->prepare();
804   if(cbs.has_error()) 804   if(cbs.has_error())
805   { 805   {
806   if(cbs.error() == error::need_data) 806   if(cbs.error() == error::need_data)
807   break; 807   break;
808   co_return {cbs.error(), total}; 808   co_return {cbs.error(), total};
809   } 809   }
810   810  
811   if(capy::buffer_empty(*cbs)) 811   if(capy::buffer_empty(*cbs))
812   { 812   {
813   // advance state machine 813   // advance state machine
814   sr_->consume(0); 814   sr_->consume(0);
815   continue; 815   continue;
816   } 816   }
817   817  
818   auto [ec, written] = 818   auto [ec, written] =
819   co_await stream_->write_some(*cbs); 819   co_await stream_->write_some(*cbs);
820   sr_->consume(written); 820   sr_->consume(written);
821   821  
822   if(ec) 822   if(ec)
823   co_return {ec, total}; 823   co_return {ec, total};
824   } 824   }
825   } 825   }
826   } 826   }
827   827  
828   co_return {{}, total}; 828   co_return {{}, total};
HITCBC 829   144 } 829   144 }
830   830  
831   /** Write final body data and signal end-of-stream. 831   /** Write final body data and signal end-of-stream.
832   832  
833   Lazily starts the serializer in buffer mode if not 833   Lazily starts the serializer in buffer mode if not
834   already started. Writes all data from the provided 834   already started. Writes all data from the provided
835   buffers and then closes the body stream, flushing 835   buffers and then closes the body stream, flushing
836   any remaining output to the underlying stream. 836   any remaining output to the underlying stream.
837   837  
838   @param buffers The buffer sequence containing final body data. 838   @param buffers The buffer sequence containing final body data.
839   839  
840   @return An awaitable yielding `(error_code, std::size_t)`. 840   @return An awaitable yielding `(error_code, std::size_t)`.
841   The size_t is the total number of body bytes written. 841   The size_t is the total number of body bytes written.
842   842  
843   @post The serializer's `is_done()` returns `true` on success. 843   @post The serializer's `is_done()` returns `true` on success.
844   */ 844   */
845   template<class ConstBufferSequence> 845   template<class ConstBufferSequence>
846   auto 846   auto
HITCBC 847   44 write_eof(ConstBufferSequence const& buffers) 847   44 write_eof(ConstBufferSequence const& buffers)
848   -> capy::io_task<std::size_t> 848   -> capy::io_task<std::size_t>
849   { 849   {
850   auto [ec, n] = co_await write(buffers); 850   auto [ec, n] = co_await write(buffers);
851   if(ec) 851   if(ec)
852   co_return {ec, n}; 852   co_return {ec, n};
853   853  
854   sr_->stream_close(); 854   sr_->stream_close();
855   855  
856   while(!sr_->is_done()) 856   while(!sr_->is_done())
857   { 857   {
858   auto cbs = sr_->prepare(); 858   auto cbs = sr_->prepare();
859   if(cbs.has_error()) 859   if(cbs.has_error())
860   { 860   {
861   if(cbs.error() == error::need_data) 861   if(cbs.error() == error::need_data)
862   continue; 862   continue;
863   co_return {cbs.error(), n}; 863   co_return {cbs.error(), n};
864   } 864   }
865   865  
866   if(capy::buffer_empty(*cbs)) 866   if(capy::buffer_empty(*cbs))
867   { 867   {
868   // advance state machine 868   // advance state machine
869   sr_->consume(0); 869   sr_->consume(0);
870   continue; 870   continue;
871   } 871   }
872   872  
873   auto [ec2, written] = co_await stream_->write_some(*cbs); 873   auto [ec2, written] = co_await stream_->write_some(*cbs);
874   sr_->consume(written); 874   sr_->consume(written);
875   875  
876   if(ec2) 876   if(ec2)
877   co_return {ec2, n}; 877   co_return {ec2, n};
878   } 878   }
879   879  
880   co_return {{}, n}; 880   co_return {{}, n};
HITCBC 881   88 } 881   88 }
882   882  
883   /** Signal end-of-stream with no additional data. 883   /** Signal end-of-stream with no additional data.
884   884  
885   Lazily starts the serializer in buffer mode if not 885   Lazily starts the serializer in buffer mode if not
886   already started. Closes the body stream and flushes 886   already started. Closes the body stream and flushes
887   any remaining output to the underlying stream. 887   any remaining output to the underlying stream.
888   888  
889   @return An awaitable yielding `(error_code)`. 889   @return An awaitable yielding `(error_code)`.
890   890  
891   @post The serializer's `is_done()` returns `true` on success. 891   @post The serializer's `is_done()` returns `true` on success.
892   */ 892   */
893   auto 893   auto
HITCBC 894   16 write_eof() 894   16 write_eof()
895   -> capy::io_task<> 895   -> capy::io_task<>
896   { 896   {
897   if(sr_->is_start()) 897   if(sr_->is_start())
898   sr_->start_buffers(); 898   sr_->start_buffers();
899   899  
900   sr_->stream_close(); 900   sr_->stream_close();
901   901  
902   while(!sr_->is_done()) 902   while(!sr_->is_done())
903   { 903   {
904   auto cbs = sr_->prepare(); 904   auto cbs = sr_->prepare();
905   if(cbs.has_error()) 905   if(cbs.has_error())
906   { 906   {
907   if(cbs.error() == error::need_data) 907   if(cbs.error() == error::need_data)
908   continue; 908   continue;
909   co_return {std::error_code(cbs.error())}; 909   co_return {std::error_code(cbs.error())};
910   } 910   }
911   911  
912   if(capy::buffer_empty(*cbs)) 912   if(capy::buffer_empty(*cbs))
913   { 913   {
914   // advance state machine 914   // advance state machine
915   sr_->consume(0); 915   sr_->consume(0);
916   continue; 916   continue;
917   } 917   }
918   918  
919   auto [ec, written] = co_await stream_->write_some(*cbs); 919   auto [ec, written] = co_await stream_->write_some(*cbs);
920   sr_->consume(written); 920   sr_->consume(written);
921   921  
922   if(ec) 922   if(ec)
923   co_return {ec}; 923   co_return {ec};
924   } 924   }
925   925  
926   co_return {}; 926   co_return {};
HITCBC 927   32 } 927   32 }
928   }; 928   };
929   929  
930   //------------------------------------------------ 930   //------------------------------------------------
931   931  
932   template<capy::WriteStream Stream> 932   template<capy::WriteStream Stream>
933   serializer::sink<Stream> 933   serializer::sink<Stream>
HITCBC 934   143 serializer::sink_for(Stream& ws) noexcept 934   143 serializer::sink_for(Stream& ws) noexcept
935   { 935   {
HITCBC 936   143 return sink<Stream>(ws, *this); 936   143 return sink<Stream>(ws, *this);
937   } 937   }
938   938  
939   } // http 939   } // http
940   } // boost 940   } // boost
941   941  
942   #endif 942   #endif