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/buffers/buffer_pair.hpp>
19  
#include <boost/capy/buffers/buffer_pair.hpp>
20  
#include <boost/capy/concept/buffer_sink.hpp>
20  
#include <boost/capy/concept/buffer_sink.hpp>
21  
#include <boost/capy/concept/write_stream.hpp>
21  
#include <boost/capy/concept/write_stream.hpp>
22  
#include <boost/capy/io_task.hpp>
22  
#include <boost/capy/io_task.hpp>
23  
#include <boost/core/span.hpp>
23  
#include <boost/core/span.hpp>
24  
#include <boost/system/result.hpp>
24  
#include <boost/system/result.hpp>
25  

25  

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  
        capy::mutable_buffer_pair;
85  
        capy::mutable_buffer_pair;
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  
    */
108  
    serializer() = default;
108  
    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  
    */
602  
    sink(
602  
    sink(
603  
        Stream& stream,
603  
        Stream& stream,
604  
        serializer& sr) noexcept
604  
        serializer& sr) noexcept
605  
        : stream_(&stream)
605  
        : stream_(&stream)
606  
        , sr_(&sr)
606  
        , sr_(&sr)
607  
    {
607  
    {
608  
    }
608  
    }
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>
622  
    prepare(std::span<capy::mutable_buffer> dest)
622  
    prepare(std::span<capy::mutable_buffer> dest)
623  
    {
623  
    {
624  
        if(sr_->is_start())
624  
        if(sr_->is_start())
625  
            sr_->start_writes();
625  
            sr_->start_writes();
626  
        auto bufs = sr_->stream_prepare();
626  
        auto bufs = sr_->stream_prepare();
627  
        std::size_t count = 0;
627  
        std::size_t count = 0;
628  
        for(auto const& b : bufs)
628  
        for(auto const& b : bufs)
629  
        {
629  
        {
630  
            if(count >= dest.size() || b.size() == 0)
630  
            if(count >= dest.size() || b.size() == 0)
631  
                break;
631  
                break;
632  
            dest[count++] = b;
632  
            dest[count++] = b;
633  
        }
633  
        }
634  
        return dest.first(count);
634  
        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
648  
    commit(std::size_t n)
648  
    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 {cbs.error()};
662  
                co_return {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 {};
680  
    }
680  
    }
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
697  
    commit_eof(std::size_t n)
697  
    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 {cbs.error()};
712  
                co_return {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 {};
730  
    }
730  
    }
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
745  
    write(ConstBufferSequence const& buffers)
745  
    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};
829  
    }
829  
    }
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
847  
    write_eof(ConstBufferSequence const& buffers)
847  
    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};
881  
    }
881  
    }
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
894  
    write_eof()
894  
    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 {cbs.error()};
909  
                co_return {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 {};
927  
    }
927  
    }
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>
934  
serializer::sink_for(Stream& ws) noexcept
934  
serializer::sink_for(Stream& ws) noexcept
935  
{
935  
{
936  
    return sink<Stream>(ws, *this);
936  
    return sink<Stream>(ws, *this);
937  
}
937  
}
938  

938  

939  
} // http
939  
} // http
940  
} // boost
940  
} // boost
941  

941  

942  
#endif
942  
#endif