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