1  
//
1  
//
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2024 Christian Mazakas
3  
// Copyright (c) 2024 Christian Mazakas
4  
// Copyright (c) 2024 Mohammad Nejati
4  
// Copyright (c) 2024 Mohammad Nejati
5  
//
5  
//
6  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
6  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
7  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8  
//
8  
//
9  
// Official repository: https://github.com/cppalliance/http
9  
// Official repository: https://github.com/cppalliance/http
10  
//
10  
//
11  

11  

12  
#include <boost/http/detail/except.hpp>
12  
#include <boost/http/detail/except.hpp>
13  
#include <boost/http/detail/header.hpp>
13  
#include <boost/http/detail/header.hpp>
14  
#include <boost/http/message_base.hpp>
14  
#include <boost/http/message_base.hpp>
15  
#include <boost/http/serializer.hpp>
15  
#include <boost/http/serializer.hpp>
16  

16  

17  
#include "src/detail/array_of_const_buffers.hpp"
17  
#include "src/detail/array_of_const_buffers.hpp"
18  
#include "src/detail/brotli_filter_base.hpp"
18  
#include "src/detail/brotli_filter_base.hpp"
19  
#include "src/detail/buffer_utils.hpp"
19  
#include "src/detail/buffer_utils.hpp"
20  
#include "src/detail/zlib_filter_base.hpp"
20  
#include "src/detail/zlib_filter_base.hpp"
21  

21  

22  
#include <boost/capy/buffers/circular_dynamic_buffer.hpp>
22  
#include <boost/capy/buffers/circular_dynamic_buffer.hpp>
23  
#include <boost/capy/buffers/buffer_copy.hpp>
23  
#include <boost/capy/buffers/buffer_copy.hpp>
24  
#include <boost/capy/ex/system_context.hpp>
24  
#include <boost/capy/ex/system_context.hpp>
25  
#include <boost/core/bit.hpp>
25  
#include <boost/core/bit.hpp>
26  
#include <boost/core/ignore_unused.hpp>
26  
#include <boost/core/ignore_unused.hpp>
27  
#include <boost/http/brotli/encode.hpp>
27  
#include <boost/http/brotli/encode.hpp>
28  
#include <boost/http/zlib/compression_method.hpp>
28  
#include <boost/http/zlib/compression_method.hpp>
29  
#include <boost/http/zlib/compression_strategy.hpp>
29  
#include <boost/http/zlib/compression_strategy.hpp>
30  
#include <boost/http/zlib/deflate.hpp>
30  
#include <boost/http/zlib/deflate.hpp>
31  
#include <boost/http/zlib/error.hpp>
31  
#include <boost/http/zlib/error.hpp>
32  
#include <boost/http/zlib/flush.hpp>
32  
#include <boost/http/zlib/flush.hpp>
33  

33  

34  
#include <memory>
34  
#include <memory>
35  
#include <stddef.h>
35  
#include <stddef.h>
36  

36  

37  
namespace boost {
37  
namespace boost {
38  
namespace http {
38  
namespace http {
39  

39  

40  
namespace {
40  
namespace {
41  

41  

42  
const
42  
const
43  
capy::const_buffer
43  
capy::const_buffer
44  
crlf_and_final_chunk = {"\r\n0\r\n\r\n", 7};
44  
crlf_and_final_chunk = {"\r\n0\r\n\r\n", 7};
45  

45  

46  
const
46  
const
47  
capy::const_buffer
47  
capy::const_buffer
48  
crlf = {"\r\n", 2};
48  
crlf = {"\r\n", 2};
49  

49  

50  
const
50  
const
51  
capy::const_buffer
51  
capy::const_buffer
52  
final_chunk = {"0\r\n\r\n", 5};
52  
final_chunk = {"0\r\n\r\n", 5};
53  

53  

54  
constexpr
54  
constexpr
55  
std::uint8_t
55  
std::uint8_t
56  
chunk_header_len(
56  
chunk_header_len(
57  
    std::size_t max_chunk_size) noexcept
57  
    std::size_t max_chunk_size) noexcept
58  
{
58  
{
59  
    return
59  
    return
60  
        static_cast<uint8_t>(
60  
        static_cast<uint8_t>(
61  
            (core::bit_width(max_chunk_size) + 3) / 4 +
61  
            (core::bit_width(max_chunk_size) + 3) / 4 +
62  
            2); // crlf
62  
            2); // crlf
63  
};
63  
};
64  

64  

65  
void
65  
void
66  
write_chunk_header(
66  
write_chunk_header(
67  
    const capy::mutable_buffer_pair& mbs,
67  
    const capy::mutable_buffer_pair& mbs,
68  
    std::size_t size) noexcept
68  
    std::size_t size) noexcept
69  
{
69  
{
70  
    static constexpr char hexdig[] =
70  
    static constexpr char hexdig[] =
71  
        "0123456789ABCDEF";
71  
        "0123456789ABCDEF";
72  
    char buf[18];
72  
    char buf[18];
73  
    auto p = buf + 16;
73  
    auto p = buf + 16;
74  
    auto const n = capy::buffer_size(mbs);
74  
    auto const n = capy::buffer_size(mbs);
75  
    for(std::size_t i = n - 2; i--;)
75  
    for(std::size_t i = n - 2; i--;)
76  
    {
76  
    {
77  
        *--p = hexdig[size & 0xf];
77  
        *--p = hexdig[size & 0xf];
78  
        size >>= 4;
78  
        size >>= 4;
79  
    }
79  
    }
80  
    buf[16] = '\r';
80  
    buf[16] = '\r';
81  
    buf[17] = '\n';
81  
    buf[17] = '\n';
82  
    auto copied = capy::buffer_copy(
82  
    auto copied = capy::buffer_copy(
83  
        mbs,
83  
        mbs,
84  
        capy::const_buffer(p, n));
84  
        capy::const_buffer(p, n));
85  
    ignore_unused(copied);
85  
    ignore_unused(copied);
86  
    BOOST_ASSERT(copied == n);
86  
    BOOST_ASSERT(copied == n);
87  
}
87  
}
88  

88  

89  
class zlib_filter
89  
class zlib_filter
90  
    : public detail::zlib_filter_base
90  
    : public detail::zlib_filter_base
91  
{
91  
{
92  
    http::zlib::deflate_service& svc_;
92  
    http::zlib::deflate_service& svc_;
93  

93  

94  
public:
94  
public:
95  
    zlib_filter(
95  
    zlib_filter(
96  
        http::zlib::deflate_service& svc,
96  
        http::zlib::deflate_service& svc,
97  
        int comp_level,
97  
        int comp_level,
98  
        int window_bits,
98  
        int window_bits,
99  
        int mem_level)
99  
        int mem_level)
100  
        : svc_(svc)
100  
        : svc_(svc)
101  
    {
101  
    {
102  
        system::error_code ec = static_cast<http::zlib::error>(svc_.init2(
102  
        system::error_code ec = static_cast<http::zlib::error>(svc_.init2(
103  
            strm_,
103  
            strm_,
104  
            comp_level,
104  
            comp_level,
105  
            http::zlib::deflated,
105  
            http::zlib::deflated,
106  
            window_bits,
106  
            window_bits,
107  
            mem_level,
107  
            mem_level,
108  
            http::zlib::default_strategy));
108  
            http::zlib::default_strategy));
109  
        if(ec != http::zlib::error::ok)
109  
        if(ec != http::zlib::error::ok)
110  
            detail::throw_system_error(ec);
110  
            detail::throw_system_error(ec);
111  
    }
111  
    }
112  

112  

113  
private:
113  
private:
114  
    virtual
114  
    virtual
115  
    std::size_t
115  
    std::size_t
116  
    min_out_buffer() const noexcept override
116  
    min_out_buffer() const noexcept override
117  
    {
117  
    {
118  
        return 8;
118  
        return 8;
119  
    }
119  
    }
120  

120  

121  
    virtual
121  
    virtual
122  
    results
122  
    results
123  
    do_process(
123  
    do_process(
124  
        capy::mutable_buffer out,
124  
        capy::mutable_buffer out,
125  
        capy::const_buffer in,
125  
        capy::const_buffer in,
126  
        bool more) noexcept override
126  
        bool more) noexcept override
127  
    {
127  
    {
128  
        strm_.next_out  = static_cast<unsigned char*>(out.data());
128  
        strm_.next_out  = static_cast<unsigned char*>(out.data());
129  
        strm_.avail_out = saturate_cast(out.size());
129  
        strm_.avail_out = saturate_cast(out.size());
130  
        strm_.next_in   = static_cast<unsigned char*>(const_cast<void *>(in.data()));
130  
        strm_.next_in   = static_cast<unsigned char*>(const_cast<void *>(in.data()));
131  
        strm_.avail_in  = saturate_cast(in.size());
131  
        strm_.avail_in  = saturate_cast(in.size());
132  

132  

133  
        auto rs = static_cast<http::zlib::error>(
133  
        auto rs = static_cast<http::zlib::error>(
134  
            svc_.deflate(
134  
            svc_.deflate(
135  
                strm_,
135  
                strm_,
136  
                more ? http::zlib::no_flush : http::zlib::finish));
136  
                more ? http::zlib::no_flush : http::zlib::finish));
137  

137  

138  
        results rv;
138  
        results rv;
139  
        rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
139  
        rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
140  
        rv.in_bytes  = saturate_cast(in.size()) - strm_.avail_in;
140  
        rv.in_bytes  = saturate_cast(in.size()) - strm_.avail_in;
141  
        rv.finished  = (rs == http::zlib::error::stream_end);
141  
        rv.finished  = (rs == http::zlib::error::stream_end);
142  

142  

143  
        if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err)
143  
        if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err)
144  
            rv.ec = rs;
144  
            rv.ec = rs;
145  

145  

146  
        return rv;
146  
        return rv;
147  
    }
147  
    }
148  
};
148  
};
149  

149  

150  
class brotli_filter
150  
class brotli_filter
151  
    : public detail::brotli_filter_base
151  
    : public detail::brotli_filter_base
152  
{
152  
{
153  
    http::brotli::encode_service& svc_;
153  
    http::brotli::encode_service& svc_;
154  
    http::brotli::encoder_state* state_;
154  
    http::brotli::encoder_state* state_;
155  

155  

156  
public:
156  
public:
157  
    brotli_filter(
157  
    brotli_filter(
158  
        http::brotli::encode_service& svc,
158  
        http::brotli::encode_service& svc,
159  
        std::uint32_t comp_quality,
159  
        std::uint32_t comp_quality,
160  
        std::uint32_t comp_window)
160  
        std::uint32_t comp_window)
161  
        : svc_(svc)
161  
        : svc_(svc)
162  
    {
162  
    {
163  
        state_ = svc_.create_instance(nullptr, nullptr, nullptr);
163  
        state_ = svc_.create_instance(nullptr, nullptr, nullptr);
164  
        if(!state_)
164  
        if(!state_)
165  
            detail::throw_bad_alloc();
165  
            detail::throw_bad_alloc();
166  
        using encoder_parameter = http::brotli::encoder_parameter;
166  
        using encoder_parameter = http::brotli::encoder_parameter;
167  
        svc_.set_parameter(state_, encoder_parameter::quality, comp_quality);
167  
        svc_.set_parameter(state_, encoder_parameter::quality, comp_quality);
168  
        svc_.set_parameter(state_, encoder_parameter::lgwin, comp_window);
168  
        svc_.set_parameter(state_, encoder_parameter::lgwin, comp_window);
169  
    }
169  
    }
170  

170  

171  
    ~brotli_filter()
171  
    ~brotli_filter()
172  
    {
172  
    {
173  
        svc_.destroy_instance(state_);
173  
        svc_.destroy_instance(state_);
174  
    }
174  
    }
175  

175  

176  
private:
176  
private:
177  
    virtual
177  
    virtual
178  
    results
178  
    results
179  
    do_process(
179  
    do_process(
180  
        capy::mutable_buffer out,
180  
        capy::mutable_buffer out,
181  
        capy::const_buffer in,
181  
        capy::const_buffer in,
182  
        bool more) noexcept override
182  
        bool more) noexcept override
183  
    {
183  
    {
184  
        auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
184  
        auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
185  
        auto available_in = in.size();
185  
        auto available_in = in.size();
186  
        auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
186  
        auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
187  
        auto available_out = out.size();
187  
        auto available_out = out.size();
188  

188  

189  
        using encoder_operation = 
189  
        using encoder_operation = 
190  
            http::brotli::encoder_operation;
190  
            http::brotli::encoder_operation;
191  

191  

192  
        bool rs = svc_.compress_stream(
192  
        bool rs = svc_.compress_stream(
193  
            state_,
193  
            state_,
194  
            more ? encoder_operation::process : encoder_operation::finish,
194  
            more ? encoder_operation::process : encoder_operation::finish,
195  
            &available_in,
195  
            &available_in,
196  
            &next_in,
196  
            &next_in,
197  
            &available_out,
197  
            &available_out,
198  
            &next_out,
198  
            &next_out,
199  
            nullptr);
199  
            nullptr);
200  

200  

201  
        results rv;
201  
        results rv;
202  
        rv.in_bytes  = in.size()  - available_in;
202  
        rv.in_bytes  = in.size()  - available_in;
203  
        rv.out_bytes = out.size() - available_out;
203  
        rv.out_bytes = out.size() - available_out;
204  
        rv.finished  = svc_.is_finished(state_);
204  
        rv.finished  = svc_.is_finished(state_);
205  

205  

206  
        if(rs == false)
206  
        if(rs == false)
207  
            rv.ec = error::bad_payload;
207  
            rv.ec = error::bad_payload;
208  

208  

209  
        return rv;
209  
        return rv;
210  
    }
210  
    }
211  
};
211  
};
212  

212  

213  
template<class UInt>
213  
template<class UInt>
214  
std::size_t
214  
std::size_t
215  
clamp(
215  
clamp(
216  
    UInt x,
216  
    UInt x,
217  
    std::size_t limit = (std::numeric_limits<
217  
    std::size_t limit = (std::numeric_limits<
218  
        std::size_t>::max)()) noexcept
218  
        std::size_t>::max)()) noexcept
219  
{
219  
{
220  
    if(x >= limit)
220  
    if(x >= limit)
221  
        return limit;
221  
        return limit;
222  
    return static_cast<std::size_t>(x);
222  
    return static_cast<std::size_t>(x);
223  
}
223  
}
224  

224  

225  
} // namespace
225  
} // namespace
226  

226  

227  
//------------------------------------------------
227  
//------------------------------------------------
228  

228  

229  
class serializer::impl
229  
class serializer::impl
230  
{
230  
{
231  
    enum class state
231  
    enum class state
232  
    {
232  
    {
233  
        reset,
233  
        reset,
234  
        start,
234  
        start,
235  
        header,
235  
        header,
236  
        body
236  
        body
237  
    };
237  
    };
238  

238  

239  
    enum class style
239  
    enum class style
240  
    {
240  
    {
241  
        empty,
241  
        empty,
242  
        stream
242  
        stream
243  
    };
243  
    };
244  

244  

245  
    std::shared_ptr<serializer_config_impl const> cfg_;
245  
    std::shared_ptr<serializer_config_impl const> cfg_;
246  
    detail::workspace ws_;
246  
    detail::workspace ws_;
247  

247  

248  
    std::unique_ptr<detail::filter> filter_;
248  
    std::unique_ptr<detail::filter> filter_;
249  

249  

250  
    capy::circular_dynamic_buffer out_;
250  
    capy::circular_dynamic_buffer out_;
251  
    capy::circular_dynamic_buffer in_;
251  
    capy::circular_dynamic_buffer in_;
252  
    detail::array_of_const_buffers prepped_;
252  
    detail::array_of_const_buffers prepped_;
253  
    capy::const_buffer tmp_;
253  
    capy::const_buffer tmp_;
254  

254  

255  
    state state_ = state::start;
255  
    state state_ = state::start;
256  
    style style_ = style::empty;
256  
    style style_ = style::empty;
257  
    uint8_t chunk_header_len_ = 0;
257  
    uint8_t chunk_header_len_ = 0;
258  
    bool more_input_ = false;
258  
    bool more_input_ = false;
259  
    bool is_chunked_ = false;
259  
    bool is_chunked_ = false;
260  
    bool needs_exp100_continue_ = false;
260  
    bool needs_exp100_continue_ = false;
261  
    bool filter_done_ = false;
261  
    bool filter_done_ = false;
262  

262  

263  
public:
263  
public:
264  
    message_base const* msg_ = nullptr;
264  
    message_base const* msg_ = nullptr;
265  

265  

266  
    explicit
266  
    explicit
267  
    impl(std::shared_ptr<serializer_config_impl const> cfg)
267  
    impl(std::shared_ptr<serializer_config_impl const> cfg)
268  
        : cfg_(std::move(cfg))
268  
        : cfg_(std::move(cfg))
269  
        , ws_(cfg_->space_needed)
269  
        , ws_(cfg_->space_needed)
270  
    {
270  
    {
271  
    }
271  
    }
272  

272  

273  
    impl(
273  
    impl(
274  
        std::shared_ptr<serializer_config_impl const> cfg,
274  
        std::shared_ptr<serializer_config_impl const> cfg,
275  
        message_base const& msg)
275  
        message_base const& msg)
276  
        : cfg_(std::move(cfg))
276  
        : cfg_(std::move(cfg))
277  
        , ws_(cfg_->space_needed)
277  
        , ws_(cfg_->space_needed)
278  
        , msg_(&msg)
278  
        , msg_(&msg)
279  
    {
279  
    {
280  
    }
280  
    }
281  

281  

282  
    void
282  
    void
283  
    reset() noexcept
283  
    reset() noexcept
284  
    {
284  
    {
285  
        filter_.reset();
285  
        filter_.reset();
286  
        ws_.clear();
286  
        ws_.clear();
287  
        state_ = state::start;
287  
        state_ = state::start;
288  
    }
288  
    }
289  

289  

290  
    auto
290  
    auto
291  
    prepare() ->
291  
    prepare() ->
292  
        system::result<const_buffers_type>
292  
        system::result<const_buffers_type>
293  
    {
293  
    {
294  
        // Precondition violation
294  
        // Precondition violation
295  
        if(state_ < state::header)
295  
        if(state_ < state::header)
296  
            detail::throw_logic_error();
296  
            detail::throw_logic_error();
297  

297  

298  
        // Expect: 100-continue
298  
        // Expect: 100-continue
299  
        if(needs_exp100_continue_)
299  
        if(needs_exp100_continue_)
300  
        {
300  
        {
301  
            if(!is_header_done())
301  
            if(!is_header_done())
302  
                return const_buffers_type(
302  
                return const_buffers_type(
303  
                    prepped_.begin(),
303  
                    prepped_.begin(),
304  
                    1); // limit to header
304  
                    1); // limit to header
305  

305  

306  
            needs_exp100_continue_ = false;
306  
            needs_exp100_continue_ = false;
307  

307  

308  
            BOOST_HTTP_RETURN_EC(
308  
            BOOST_HTTP_RETURN_EC(
309  
                error::expect_100_continue);
309  
                error::expect_100_continue);
310  
        }
310  
        }
311  

311  

312  
        if(!filter_)
312  
        if(!filter_)
313  
        {
313  
        {
314  
            switch(style_)
314  
            switch(style_)
315  
            {
315  
            {
316  
            case style::empty:
316  
            case style::empty:
317  
                break;
317  
                break;
318  

318  

319  
            case style::stream:
319  
            case style::stream:
320  
                if(out_.size() == 0 && is_header_done() && more_input_)
320  
                if(out_.size() == 0 && is_header_done() && more_input_)
321  
                    BOOST_HTTP_RETURN_EC(
321  
                    BOOST_HTTP_RETURN_EC(
322  
                        error::need_data);
322  
                        error::need_data);
323  
                break;
323  
                break;
324  
            }
324  
            }
325  
        }
325  
        }
326  
        else // filter
326  
        else // filter
327  
        {
327  
        {
328  
            switch(style_)
328  
            switch(style_)
329  
            {
329  
            {
330  
            case style::empty:
330  
            case style::empty:
331  
            {
331  
            {
332  
                if(out_capacity() == 0 || filter_done_)
332  
                if(out_capacity() == 0 || filter_done_)
333  
                    break;
333  
                    break;
334  

334  

335  
                const auto rs = filter_->process(
335  
                const auto rs = filter_->process(
336  
                    detail::make_span(out_prepare()),
336  
                    detail::make_span(out_prepare()),
337  
                    {}, // empty input
337  
                    {}, // empty input
338  
                    false);
338  
                    false);
339  

339  

340  
                if(rs.ec)
340  
                if(rs.ec)
341  
                {
341  
                {
342  
                    ws_.clear();
342  
                    ws_.clear();
343  
                    state_ = state::reset;
343  
                    state_ = state::reset;
344  
                    return rs.ec;
344  
                    return rs.ec;
345  
                }
345  
                }
346  

346  

347  
                out_commit(rs.out_bytes);
347  
                out_commit(rs.out_bytes);
348  

348  

349  
                if(rs.finished)
349  
                if(rs.finished)
350  
                {
350  
                {
351  
                    filter_done_ = true;
351  
                    filter_done_ = true;
352  
                    out_finish();
352  
                    out_finish();
353  
                }
353  
                }
354  

354  

355  
                break;
355  
                break;
356  
            }
356  
            }
357  

357  

358  
            case style::stream:
358  
            case style::stream:
359  
            {
359  
            {
360  
                if(out_capacity() == 0 || filter_done_)
360  
                if(out_capacity() == 0 || filter_done_)
361  
                    break;
361  
                    break;
362  

362  

363  
                const auto rs = filter_->process(
363  
                const auto rs = filter_->process(
364  
                    detail::make_span(out_prepare()),
364  
                    detail::make_span(out_prepare()),
365  
                    in_.data(),
365  
                    in_.data(),
366  
                    more_input_);
366  
                    more_input_);
367  

367  

368  
                if(rs.ec)
368  
                if(rs.ec)
369  
                {
369  
                {
370  
                    ws_.clear();
370  
                    ws_.clear();
371  
                    state_ = state::reset;
371  
                    state_ = state::reset;
372  
                    return rs.ec;
372  
                    return rs.ec;
373  
                }
373  
                }
374  

374  

375  
                in_.consume(rs.in_bytes);
375  
                in_.consume(rs.in_bytes);
376  
                out_commit(rs.out_bytes);
376  
                out_commit(rs.out_bytes);
377  

377  

378  
                if(rs.finished)
378  
                if(rs.finished)
379  
                {
379  
                {
380  
                    filter_done_ = true;
380  
                    filter_done_ = true;
381  
                    out_finish();
381  
                    out_finish();
382  
                }
382  
                }
383  

383  

384  
                if(out_.size() == 0 && is_header_done() && more_input_)
384  
                if(out_.size() == 0 && is_header_done() && more_input_)
385  
                    BOOST_HTTP_RETURN_EC(
385  
                    BOOST_HTTP_RETURN_EC(
386  
                        error::need_data);
386  
                        error::need_data);
387  
                break;
387  
                break;
388  
            }
388  
            }
389  
            }
389  
            }
390  
        }
390  
        }
391  

391  

392  
        prepped_.reset(!is_header_done());
392  
        prepped_.reset(!is_header_done());
393  
        for(auto const& cb : out_.data())
393  
        for(auto const& cb : out_.data())
394  
        {
394  
        {
395  
            if(cb.size() != 0)
395  
            if(cb.size() != 0)
396  
                prepped_.append(cb);
396  
                prepped_.append(cb);
397  
        }
397  
        }
398  
        return detail::make_span(prepped_);
398  
        return detail::make_span(prepped_);
399  
    }
399  
    }
400  

400  

401  
    void
401  
    void
402  
    consume(
402  
    consume(
403  
        std::size_t n)
403  
        std::size_t n)
404  
    {
404  
    {
405  
        // Precondition violation
405  
        // Precondition violation
406  
        if(state_ < state::header)
406  
        if(state_ < state::header)
407  
            detail::throw_logic_error();
407  
            detail::throw_logic_error();
408  

408  

409  
        if(!is_header_done())
409  
        if(!is_header_done())
410  
        {
410  
        {
411  
            const auto header_remain =
411  
            const auto header_remain =
412  
                prepped_[0].size();
412  
                prepped_[0].size();
413  
            if(n < header_remain)
413  
            if(n < header_remain)
414  
            {
414  
            {
415  
                prepped_.consume(n);
415  
                prepped_.consume(n);
416  
                return;
416  
                return;
417  
            }
417  
            }
418  
            n -= header_remain;
418  
            n -= header_remain;
419  
            prepped_.consume(header_remain);
419  
            prepped_.consume(header_remain);
420  
            state_ = state::body;
420  
            state_ = state::body;
421  
        }
421  
        }
422  

422  

423  
        prepped_.consume(n);
423  
        prepped_.consume(n);
424  

424  

425  
        // no-op when out_ is not in use
425  
        // no-op when out_ is not in use
426  
        out_.consume(n);
426  
        out_.consume(n);
427  

427  

428  
        if(!prepped_.empty())
428  
        if(!prepped_.empty())
429  
            return;
429  
            return;
430  

430  

431  
        if(more_input_)
431  
        if(more_input_)
432  
            return;
432  
            return;
433  

433  

434  
        if(filter_ && !filter_done_)
434  
        if(filter_ && !filter_done_)
435  
            return;
435  
            return;
436  

436  

437  
        if(needs_exp100_continue_)
437  
        if(needs_exp100_continue_)
438  
            return;
438  
            return;
439  

439  

440  
        // ready for next message
440  
        // ready for next message
441  
        reset();
441  
        reset();
442  
    }
442  
    }
443  

443  

444  
    void
444  
    void
445  
    start_init(
445  
    start_init(
446  
        message_base const& m)
446  
        message_base const& m)
447  
    {
447  
    {
448  
        // Precondition violation
448  
        // Precondition violation
449  
        if(state_ != state::start)
449  
        if(state_ != state::start)
450  
            detail::throw_logic_error();
450  
            detail::throw_logic_error();
451  

451  

452  
        // TODO: To uphold the strong exception guarantee,
452  
        // TODO: To uphold the strong exception guarantee,
453  
        // `state_` must be reset to `state::start` if an
453  
        // `state_` must be reset to `state::start` if an
454  
        // exception is thrown during the start operation.
454  
        // exception is thrown during the start operation.
455  
        state_ = state::header;
455  
        state_ = state::header;
456  

456  

457  
        // VFALCO what do we do with
457  
        // VFALCO what do we do with
458  
        // metadata error code failures?
458  
        // metadata error code failures?
459  
        // m.h_.md.maybe_throw();
459  
        // m.h_.md.maybe_throw();
460  

460  

461  
        auto const& md = m.metadata();
461  
        auto const& md = m.metadata();
462  
        needs_exp100_continue_ = md.expect.is_100_continue;
462  
        needs_exp100_continue_ = md.expect.is_100_continue;
463  

463  

464  
        // Transfer-Encoding
464  
        // Transfer-Encoding
465  
        is_chunked_ = md.transfer_encoding.is_chunked;
465  
        is_chunked_ = md.transfer_encoding.is_chunked;
466  

466  

467  
        // Content-Encoding
467  
        // Content-Encoding
468  
        switch (md.content_encoding.coding)
468  
        switch (md.content_encoding.coding)
469  
        {
469  
        {
470  
        case content_coding::deflate:
470  
        case content_coding::deflate:
471  
            if(!cfg_->apply_deflate_encoder)
471  
            if(!cfg_->apply_deflate_encoder)
472  
                goto no_filter;
472  
                goto no_filter;
473  
            if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
473  
            if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
474  
            {
474  
            {
475  
                filter_.reset(new zlib_filter(
475  
                filter_.reset(new zlib_filter(
476  
                    *svc,
476  
                    *svc,
477  
                    cfg_->zlib_comp_level,
477  
                    cfg_->zlib_comp_level,
478  
                    cfg_->zlib_window_bits,
478  
                    cfg_->zlib_window_bits,
479  
                    cfg_->zlib_mem_level));
479  
                    cfg_->zlib_mem_level));
480  
                filter_done_ = false;
480  
                filter_done_ = false;
481  
            }
481  
            }
482  
            break;
482  
            break;
483  

483  

484  
        case content_coding::gzip:
484  
        case content_coding::gzip:
485  
            if(!cfg_->apply_gzip_encoder)
485  
            if(!cfg_->apply_gzip_encoder)
486  
                goto no_filter;
486  
                goto no_filter;
487  
            if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
487  
            if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
488  
            {
488  
            {
489  
                filter_.reset(new zlib_filter(
489  
                filter_.reset(new zlib_filter(
490  
                    *svc,
490  
                    *svc,
491  
                    cfg_->zlib_comp_level,
491  
                    cfg_->zlib_comp_level,
492  
                    cfg_->zlib_window_bits + 16,
492  
                    cfg_->zlib_window_bits + 16,
493  
                    cfg_->zlib_mem_level));
493  
                    cfg_->zlib_mem_level));
494  
                filter_done_ = false;
494  
                filter_done_ = false;
495  
            }
495  
            }
496  
            break;
496  
            break;
497  

497  

498  
        case content_coding::br:
498  
        case content_coding::br:
499  
            if(!cfg_->apply_brotli_encoder)
499  
            if(!cfg_->apply_brotli_encoder)
500  
                goto no_filter;
500  
                goto no_filter;
501  
            if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>())
501  
            if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>())
502  
            {
502  
            {
503  
                filter_.reset(new brotli_filter(
503  
                filter_.reset(new brotli_filter(
504  
                    *svc,
504  
                    *svc,
505  
                    cfg_->brotli_comp_quality,
505  
                    cfg_->brotli_comp_quality,
506  
                    cfg_->brotli_comp_window));
506  
                    cfg_->brotli_comp_window));
507  
                filter_done_ = false;
507  
                filter_done_ = false;
508  
            }
508  
            }
509  
            break;
509  
            break;
510  

510  

511  
        no_filter:
511  
        no_filter:
512  
        default:
512  
        default:
513  
            filter_.reset();
513  
            filter_.reset();
514  
            break;
514  
            break;
515  
        }
515  
        }
516  
    }
516  
    }
517  

517  

518  
    void
518  
    void
519  
    start_empty(
519  
    start_empty(
520  
        message_base const& m)
520  
        message_base const& m)
521  
    {
521  
    {
522  
        start_init(m);
522  
        start_init(m);
523  
        style_ = style::empty;
523  
        style_ = style::empty;
524  

524  

525  
        prepped_ = make_array(
525  
        prepped_ = make_array(
526  
            1 + // header
526  
            1 + // header
527  
            2); // out buffer pairs
527  
            2); // out buffer pairs
528  

528  

529  
        out_init();
529  
        out_init();
530  

530  

531  
        if(!filter_)
531  
        if(!filter_)
532  
            out_finish();
532  
            out_finish();
533  

533  

534  
        prepped_.append({ m.h_.cbuf, m.h_.size });
534  
        prepped_.append({ m.h_.cbuf, m.h_.size });
535  
        more_input_ = false;
535  
        more_input_ = false;
536  
    }
536  
    }
537  

537  

538  
    void
538  
    void
539  
    start_stream(message_base const& m)
539  
    start_stream(message_base const& m)
540  
    {
540  
    {
541  
        start_init(m);
541  
        start_init(m);
542  
        style_ = style::stream;
542  
        style_ = style::stream;
543  

543  

544  
        prepped_ = make_array(
544  
        prepped_ = make_array(
545  
            1 + // header
545  
            1 + // header
546  
            2); // out buffer pairs
546  
            2); // out buffer pairs
547  

547  

548  
        if(filter_)
548  
        if(filter_)
549  
        {
549  
        {
550  
            // TODO: smarter buffer distribution
550  
            // TODO: smarter buffer distribution
551  
            auto const n = (ws_.size() - 1) / 2;
551  
            auto const n = (ws_.size() - 1) / 2;
552  
            in_ = { ws_.reserve_front(n), n };
552  
            in_ = { ws_.reserve_front(n), n };
553  
        }
553  
        }
554  

554  

555  
        out_init();
555  
        out_init();
556  

556  

557  
        prepped_.append({ m.h_.cbuf, m.h_.size });
557  
        prepped_.append({ m.h_.cbuf, m.h_.size });
558  
        more_input_ = true;
558  
        more_input_ = true;
559  
    }
559  
    }
560  

560  

561  
    // Like start_stream but without in_ allocation.
561  
    // Like start_stream but without in_ allocation.
562  
    // Entire workspace is used for output buffering.
562  
    // Entire workspace is used for output buffering.
563  
    void
563  
    void
564  
    start_buffers_direct(message_base const& m)
564  
    start_buffers_direct(message_base const& m)
565  
    {
565  
    {
566  
        start_init(m);
566  
        start_init(m);
567  
        style_ = style::stream;
567  
        style_ = style::stream;
568  

568  

569  
        prepped_ = make_array(
569  
        prepped_ = make_array(
570  
            1 + // header
570  
            1 + // header
571  
            2); // out buffer pairs
571  
            2); // out buffer pairs
572  

572  

573  
        out_init();
573  
        out_init();
574  

574  

575  
        prepped_.append({ m.h_.cbuf, m.h_.size });
575  
        prepped_.append({ m.h_.cbuf, m.h_.size });
576  
        more_input_ = true;
576  
        more_input_ = true;
577  
    }
577  
    }
578  

578  

579  
    std::size_t
579  
    std::size_t
580  
    stream_capacity() const
580  
    stream_capacity() const
581  
    {
581  
    {
582  
        if(filter_)
582  
        if(filter_)
583  
            return in_.capacity();
583  
            return in_.capacity();
584  
        return out_capacity();
584  
        return out_capacity();
585  
    }
585  
    }
586  

586  

587  
    capy::mutable_buffer_pair
587  
    capy::mutable_buffer_pair
588  
    stream_prepare()
588  
    stream_prepare()
589  
    {
589  
    {
590  
        if(state_ == state::start)
590  
        if(state_ == state::start)
591  
        {
591  
        {
592  
            if(!msg_)
592  
            if(!msg_)
593  
                detail::throw_logic_error();
593  
                detail::throw_logic_error();
594  
            start_stream(*msg_);
594  
            start_stream(*msg_);
595  
        }
595  
        }
596  
        if(filter_)
596  
        if(filter_)
597  
            return in_.prepare(in_.capacity());
597  
            return in_.prepare(in_.capacity());
598  
        return out_prepare();
598  
        return out_prepare();
599  
    }
599  
    }
600  

600  

601  
    void
601  
    void
602  
    stream_commit(std::size_t n)
602  
    stream_commit(std::size_t n)
603  
    {
603  
    {
604  
        if(n > stream_capacity())
604  
        if(n > stream_capacity())
605  
            detail::throw_invalid_argument();
605  
            detail::throw_invalid_argument();
606  

606  

607  
        if(filter_)
607  
        if(filter_)
608  
            return in_.commit(n);
608  
            return in_.commit(n);
609  

609  

610  
        out_commit(n);
610  
        out_commit(n);
611  
    }
611  
    }
612  

612  

613  
    void
613  
    void
614  
    stream_close() noexcept
614  
    stream_close() noexcept
615  
    {
615  
    {
616  
        if(!filter_)
616  
        if(!filter_)
617  
            out_finish();
617  
            out_finish();
618  

618  

619  
        more_input_ = false;
619  
        more_input_ = false;
620  
    }
620  
    }
621  

621  

622  
    bool
622  
    bool
623  
    is_done() const noexcept
623  
    is_done() const noexcept
624  
    {
624  
    {
625  
        return state_ == state::start;
625  
        return state_ == state::start;
626  
    }
626  
    }
627  

627  

628  
    bool
628  
    bool
629  
    is_start() const noexcept
629  
    is_start() const noexcept
630  
    {
630  
    {
631  
        return state_ == state::start;
631  
        return state_ == state::start;
632  
    }
632  
    }
633  

633  

634  
    detail::workspace&
634  
    detail::workspace&
635  
    ws() noexcept
635  
    ws() noexcept
636  
    {
636  
    {
637  
        return ws_;
637  
        return ws_;
638  
    }
638  
    }
639  

639  

640  
private:
640  
private:
641  
    bool
641  
    bool
642  
    is_header_done() const noexcept
642  
    is_header_done() const noexcept
643  
    {
643  
    {
644  
        return state_ == state::body;
644  
        return state_ == state::body;
645  
    }
645  
    }
646  

646  

647  
    detail::array_of_const_buffers
647  
    detail::array_of_const_buffers
648  
    make_array(std::size_t n)
648  
    make_array(std::size_t n)
649  
    {
649  
    {
650  
        BOOST_ASSERT(n <= std::uint16_t(-1));
650  
        BOOST_ASSERT(n <= std::uint16_t(-1));
651  

651  

652  
        return {
652  
        return {
653  
            ws_.push_array(n,
653  
            ws_.push_array(n,
654  
                capy::const_buffer{}),
654  
                capy::const_buffer{}),
655  
            static_cast<std::uint16_t>(n) };
655  
            static_cast<std::uint16_t>(n) };
656  
    }
656  
    }
657  

657  

658  
    void
658  
    void
659  
    out_init()
659  
    out_init()
660  
    {
660  
    {
661  
        // use all the remaining buffer
661  
        // use all the remaining buffer
662  
        auto const n = ws_.size() - 1;
662  
        auto const n = ws_.size() - 1;
663  
        out_ = { ws_.reserve_front(n), n };
663  
        out_ = { ws_.reserve_front(n), n };
664  
        chunk_header_len_ =
664  
        chunk_header_len_ =
665  
            chunk_header_len(out_.capacity());
665  
            chunk_header_len(out_.capacity());
666  
        if(out_capacity() == 0)
666  
        if(out_capacity() == 0)
667  
            detail::throw_length_error();
667  
            detail::throw_length_error();
668  
    }
668  
    }
669  

669  

670  
    capy::mutable_buffer_pair
670  
    capy::mutable_buffer_pair
671  
    out_prepare() noexcept
671  
    out_prepare() noexcept
672  
    {
672  
    {
673  
        auto mbp = out_.prepare(out_.capacity());
673  
        auto mbp = out_.prepare(out_.capacity());
674  
        if(is_chunked_)
674  
        if(is_chunked_)
675  
        {
675  
        {
676  
            capy::remove_prefix(
676  
            capy::remove_prefix(
677  
                mbp, chunk_header_len_);
677  
                mbp, chunk_header_len_);
678  
            capy::remove_suffix(
678  
            capy::remove_suffix(
679  
                mbp, crlf_and_final_chunk.size());
679  
                mbp, crlf_and_final_chunk.size());
680  
        }
680  
        }
681  
        return mbp;
681  
        return mbp;
682  
    }
682  
    }
683  

683  

684  
    void
684  
    void
685  
    out_commit(
685  
    out_commit(
686  
        std::size_t n) noexcept
686  
        std::size_t n) noexcept
687  
    {
687  
    {
688  
        if(is_chunked_)
688  
        if(is_chunked_)
689  
        {
689  
        {
690  
            if(n == 0)
690  
            if(n == 0)
691  
                return;
691  
                return;
692  

692  

693  
            write_chunk_header(out_.prepare(chunk_header_len_), n);
693  
            write_chunk_header(out_.prepare(chunk_header_len_), n);
694  
            out_.commit(chunk_header_len_);
694  
            out_.commit(chunk_header_len_);
695  

695  

696  
            out_.prepare(n);
696  
            out_.prepare(n);
697  
            out_.commit(n);
697  
            out_.commit(n);
698  

698  

699  
            capy::buffer_copy(out_.prepare(crlf.size()), crlf);
699  
            capy::buffer_copy(out_.prepare(crlf.size()), crlf);
700  
            out_.commit(crlf.size());
700  
            out_.commit(crlf.size());
701  
        }
701  
        }
702  
        else
702  
        else
703  
        {
703  
        {
704  
            out_.commit(n);
704  
            out_.commit(n);
705  
        }
705  
        }
706  
    }
706  
    }
707  

707  

708  
    std::size_t
708  
    std::size_t
709  
    out_capacity() const noexcept
709  
    out_capacity() const noexcept
710  
    {
710  
    {
711  
        if(is_chunked_)
711  
        if(is_chunked_)
712  
        {
712  
        {
713  
            auto const overhead = chunk_header_len_ +
713  
            auto const overhead = chunk_header_len_ +
714  
                crlf_and_final_chunk.size();
714  
                crlf_and_final_chunk.size();
715  
            if(out_.capacity() < overhead)
715  
            if(out_.capacity() < overhead)
716  
                return 0;
716  
                return 0;
717  
            return out_.capacity() - overhead;
717  
            return out_.capacity() - overhead;
718  
        }
718  
        }
719  
        return out_.capacity();
719  
        return out_.capacity();
720  
    }
720  
    }
721  

721  

722  
    void
722  
    void
723  
    out_finish() noexcept
723  
    out_finish() noexcept
724  
    {
724  
    {
725  
        if(is_chunked_)
725  
        if(is_chunked_)
726  
        {
726  
        {
727  
            capy::buffer_copy(
727  
            capy::buffer_copy(
728  
                out_.prepare(final_chunk.size()), final_chunk);
728  
                out_.prepare(final_chunk.size()), final_chunk);
729  
            out_.commit(final_chunk.size());
729  
            out_.commit(final_chunk.size());
730  
        }
730  
        }
731  
    }
731  
    }
732  
};
732  
};
733  

733  

734  
//------------------------------------------------
734  
//------------------------------------------------
735  

735  

736  
serializer::
736  
serializer::
737  
~serializer()
737  
~serializer()
738  
{
738  
{
739  
    delete impl_;
739  
    delete impl_;
740  
}
740  
}
741  

741  

742  
serializer::
742  
serializer::
743  
serializer(serializer&& other) noexcept
743  
serializer(serializer&& other) noexcept
744  
    : impl_(other.impl_)
744  
    : impl_(other.impl_)
745  
{
745  
{
746  
    other.impl_ = nullptr;
746  
    other.impl_ = nullptr;
747  
}
747  
}
748  

748  

749  
serializer&
749  
serializer&
750  
serializer::
750  
serializer::
751  
operator=(serializer&& other) noexcept
751  
operator=(serializer&& other) noexcept
752  
{
752  
{
753  
    if(this != &other)
753  
    if(this != &other)
754  
    {
754  
    {
755  
        delete impl_;
755  
        delete impl_;
756  
        impl_ = other.impl_;
756  
        impl_ = other.impl_;
757  
        other.impl_ = nullptr;
757  
        other.impl_ = nullptr;
758  
    }
758  
    }
759  
    return *this;
759  
    return *this;
760  
}
760  
}
761  

761  

762  
serializer::
762  
serializer::
763  
serializer(
763  
serializer(
764  
    std::shared_ptr<serializer_config_impl const> cfg)
764  
    std::shared_ptr<serializer_config_impl const> cfg)
765  
    : impl_(new impl(std::move(cfg)))
765  
    : impl_(new impl(std::move(cfg)))
766  
{
766  
{
767  
}
767  
}
768  

768  

769  
void
769  
void
770  
serializer::
770  
serializer::
771  
reset() noexcept
771  
reset() noexcept
772  
{
772  
{
773  
    BOOST_ASSERT(impl_);
773  
    BOOST_ASSERT(impl_);
774  
    impl_->reset();
774  
    impl_->reset();
775  
}
775  
}
776  

776  

777  
void
777  
void
778  
serializer::
778  
serializer::
779  
set_message(message_base const& m) noexcept
779  
set_message(message_base const& m) noexcept
780  
{
780  
{
781  
    BOOST_ASSERT(impl_);
781  
    BOOST_ASSERT(impl_);
782  
    impl_->msg_ = &m;
782  
    impl_->msg_ = &m;
783  
}
783  
}
784  

784  

785  
void
785  
void
786  
serializer::
786  
serializer::
787  
start()
787  
start()
788  
{
788  
{
789  
    if(!impl_ || !impl_->msg_)
789  
    if(!impl_ || !impl_->msg_)
790  
        detail::throw_logic_error();
790  
        detail::throw_logic_error();
791  
    impl_->start_empty(*impl_->msg_);
791  
    impl_->start_empty(*impl_->msg_);
792  
}
792  
}
793  

793  

794  
void
794  
void
795  
serializer::
795  
serializer::
796  
start_stream()
796  
start_stream()
797  
{
797  
{
798  
    if(!impl_ || !impl_->msg_)
798  
    if(!impl_ || !impl_->msg_)
799  
        detail::throw_logic_error();
799  
        detail::throw_logic_error();
800  
    impl_->start_stream(*impl_->msg_);
800  
    impl_->start_stream(*impl_->msg_);
801  
}
801  
}
802  

802  

803  
void
803  
void
804  
serializer::
804  
serializer::
805  
start_writes()
805  
start_writes()
806  
{
806  
{
807  
    if(!impl_ || !impl_->msg_)
807  
    if(!impl_ || !impl_->msg_)
808  
        detail::throw_logic_error();
808  
        detail::throw_logic_error();
809  
    impl_->start_stream(*impl_->msg_);
809  
    impl_->start_stream(*impl_->msg_);
810  
}
810  
}
811  

811  

812  
void
812  
void
813  
serializer::
813  
serializer::
814  
start_buffers()
814  
start_buffers()
815  
{
815  
{
816  
    if(!impl_ || !impl_->msg_)
816  
    if(!impl_ || !impl_->msg_)
817  
        detail::throw_logic_error();
817  
        detail::throw_logic_error();
818  
    impl_->start_buffers_direct(*impl_->msg_);
818  
    impl_->start_buffers_direct(*impl_->msg_);
819  
}
819  
}
820  

820  

821  
auto
821  
auto
822  
serializer::
822  
serializer::
823  
prepare() ->
823  
prepare() ->
824  
    system::result<const_buffers_type>
824  
    system::result<const_buffers_type>
825  
{
825  
{
826  
    BOOST_ASSERT(impl_);
826  
    BOOST_ASSERT(impl_);
827  
    return impl_->prepare();
827  
    return impl_->prepare();
828  
}
828  
}
829  

829  

830  
void
830  
void
831  
serializer::
831  
serializer::
832  
consume(std::size_t n)
832  
consume(std::size_t n)
833  
{
833  
{
834  
    BOOST_ASSERT(impl_);
834  
    BOOST_ASSERT(impl_);
835  
    impl_->consume(n);
835  
    impl_->consume(n);
836  
}
836  
}
837  

837  

838  
bool
838  
bool
839  
serializer::
839  
serializer::
840  
is_done() const noexcept
840  
is_done() const noexcept
841  
{
841  
{
842  
    BOOST_ASSERT(impl_);
842  
    BOOST_ASSERT(impl_);
843  
    return impl_->is_done();
843  
    return impl_->is_done();
844  
}
844  
}
845  

845  

846  
bool
846  
bool
847  
serializer::
847  
serializer::
848  
is_start() const noexcept
848  
is_start() const noexcept
849  
{
849  
{
850  
    BOOST_ASSERT(impl_);
850  
    BOOST_ASSERT(impl_);
851  
    return impl_->is_start();
851  
    return impl_->is_start();
852  
}
852  
}
853  

853  

854  
//------------------------------------------------
854  
//------------------------------------------------
855  

855  

856  
detail::workspace&
856  
detail::workspace&
857  
serializer::
857  
serializer::
858  
ws()
858  
ws()
859  
{
859  
{
860  
    BOOST_ASSERT(impl_);
860  
    BOOST_ASSERT(impl_);
861  
    return impl_->ws();
861  
    return impl_->ws();
862  
}
862  
}
863  

863  

864  
//------------------------------------------------
864  
//------------------------------------------------
865  

865  

866  
std::size_t
866  
std::size_t
867  
serializer::
867  
serializer::
868  
stream_capacity() const
868  
stream_capacity() const
869  
{
869  
{
870  
    BOOST_ASSERT(impl_);
870  
    BOOST_ASSERT(impl_);
871  
    return impl_->stream_capacity();
871  
    return impl_->stream_capacity();
872  
}
872  
}
873  

873  

874  
auto
874  
auto
875  
serializer::
875  
serializer::
876  
stream_prepare() ->
876  
stream_prepare() ->
877  
    mutable_buffers_type
877  
    mutable_buffers_type
878  
{
878  
{
879  
    BOOST_ASSERT(impl_);
879  
    BOOST_ASSERT(impl_);
880  
    return impl_->stream_prepare();
880  
    return impl_->stream_prepare();
881  
}
881  
}
882  

882  

883  
void
883  
void
884  
serializer::
884  
serializer::
885  
stream_commit(std::size_t n)
885  
stream_commit(std::size_t n)
886  
{
886  
{
887  
    BOOST_ASSERT(impl_);
887  
    BOOST_ASSERT(impl_);
888  
    impl_->stream_commit(n);
888  
    impl_->stream_commit(n);
889  
}
889  
}
890  

890  

891  
void
891  
void
892  
serializer::
892  
serializer::
893  
stream_close() noexcept
893  
stream_close() noexcept
894  
{
894  
{
895  
    BOOST_ASSERT(impl_);
895  
    BOOST_ASSERT(impl_);
896  
    impl_->stream_close();
896  
    impl_->stream_close();
897  
}
897  
}
898  

898  

899  
} // http
899  
} // http
900  
} // boost
900  
} // boost