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 82 : chunk_header_len(
57 : std::size_t max_chunk_size) noexcept
58 : {
59 : return
60 : static_cast<uint8_t>(
61 82 : (core::bit_width(max_chunk_size) + 3) / 4 +
62 82 : 2); // crlf
63 : };
64 :
65 : void
66 35 : 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 35 : auto p = buf + 16;
74 35 : auto const n = capy::buffer_size(mbs);
75 174 : for(std::size_t i = n - 2; i--;)
76 : {
77 139 : *--p = hexdig[size & 0xf];
78 139 : size >>= 4;
79 : }
80 35 : buf[16] = '\r';
81 35 : buf[17] = '\n';
82 35 : auto copied = capy::buffer_copy(
83 : mbs,
84 70 : capy::const_buffer(p, n));
85 : ignore_unused(copied);
86 35 : BOOST_ASSERT(copied == n);
87 35 : }
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 10 : clamp(
216 : UInt x,
217 : std::size_t limit = (std::numeric_limits<
218 : std::size_t>::max)()) noexcept
219 : {
220 10 : if(x >= limit)
221 2 : return limit;
222 8 : 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 : buffers,
243 : stream
244 : };
245 :
246 : std::shared_ptr<serializer_config_impl const> cfg_;
247 : detail::workspace ws_;
248 :
249 : std::unique_ptr<detail::filter> filter_;
250 : cbs_gen* cbs_gen_ = nullptr;
251 :
252 : capy::circular_dynamic_buffer out_;
253 : capy::circular_dynamic_buffer in_;
254 : detail::array_of_const_buffers prepped_;
255 : capy::const_buffer tmp_;
256 :
257 : state state_ = state::start;
258 : style style_ = style::empty;
259 : uint8_t chunk_header_len_ = 0;
260 : bool more_input_ = false;
261 : bool is_chunked_ = false;
262 : bool needs_exp100_continue_ = false;
263 : bool filter_done_ = false;
264 :
265 : public:
266 : message_base const* msg_ = nullptr;
267 :
268 : explicit
269 87 : impl(std::shared_ptr<serializer_config_impl const> cfg)
270 87 : : cfg_(std::move(cfg))
271 87 : , ws_(cfg_->space_needed)
272 : {
273 87 : }
274 :
275 0 : impl(
276 : std::shared_ptr<serializer_config_impl const> cfg,
277 : message_base const& msg)
278 0 : : cfg_(std::move(cfg))
279 0 : , ws_(cfg_->space_needed)
280 0 : , msg_(&msg)
281 : {
282 0 : }
283 :
284 : void
285 43 : reset() noexcept
286 : {
287 43 : filter_.reset();
288 43 : ws_.clear();
289 43 : state_ = state::start;
290 43 : }
291 :
292 : auto
293 170 : prepare() ->
294 : system::result<const_buffers_type>
295 : {
296 : // Precondition violation
297 170 : if(state_ < state::header)
298 1 : detail::throw_logic_error();
299 :
300 : // Expect: 100-continue
301 169 : if(needs_exp100_continue_)
302 : {
303 4 : if(!is_header_done())
304 4 : return const_buffers_type(
305 : prepped_.begin(),
306 2 : 1); // limit to header
307 :
308 2 : needs_exp100_continue_ = false;
309 :
310 2 : BOOST_HTTP_RETURN_EC(
311 : error::expect_100_continue);
312 : }
313 :
314 165 : if(!filter_)
315 : {
316 165 : switch(style_)
317 : {
318 6 : case style::empty:
319 6 : break;
320 :
321 22 : case style::buffers:
322 : {
323 : // add more buffers if prepped_ is half empty.
324 34 : if(more_input_ &&
325 12 : prepped_.capacity() >= prepped_.size())
326 : {
327 6 : prepped_.slide_to_front();
328 54 : while(prepped_.capacity() != 0)
329 : {
330 51 : auto buf = cbs_gen_->next();
331 51 : if(buf.size() == 0)
332 3 : break;
333 48 : prepped_.append(buf);
334 : }
335 6 : if(cbs_gen_->is_empty())
336 : {
337 4 : if(is_chunked_)
338 : {
339 1 : if(prepped_.capacity() != 0)
340 : {
341 1 : prepped_.append(
342 : crlf_and_final_chunk);
343 1 : more_input_ = false;
344 : }
345 : }
346 : else
347 : {
348 3 : more_input_ = false;
349 : }
350 : }
351 : }
352 22 : return detail::make_span(prepped_);
353 : }
354 :
355 137 : case style::stream:
356 137 : if(out_.size() == 0 && is_header_done() && more_input_)
357 31 : BOOST_HTTP_RETURN_EC(
358 : error::need_data);
359 106 : break;
360 : }
361 : }
362 : else // filter
363 : {
364 0 : switch(style_)
365 : {
366 0 : case style::empty:
367 : {
368 0 : if(out_capacity() == 0 || filter_done_)
369 0 : break;
370 :
371 0 : const auto rs = filter_->process(
372 0 : detail::make_span(out_prepare()),
373 : {}, // empty input
374 : false);
375 :
376 0 : if(rs.ec)
377 : {
378 0 : ws_.clear();
379 0 : state_ = state::reset;
380 0 : return rs.ec;
381 : }
382 :
383 0 : out_commit(rs.out_bytes);
384 :
385 0 : if(rs.finished)
386 : {
387 0 : filter_done_ = true;
388 0 : out_finish();
389 : }
390 :
391 0 : break;
392 : }
393 :
394 0 : case style::buffers:
395 : {
396 0 : while(out_capacity() != 0 && !filter_done_)
397 : {
398 0 : if(more_input_ && tmp_.size() == 0)
399 : {
400 0 : tmp_ = cbs_gen_->next();
401 0 : if(tmp_.size() == 0) // cbs_gen_ is empty
402 0 : more_input_ = false;
403 : }
404 :
405 0 : const auto rs = filter_->process(
406 0 : detail::make_span(out_prepare()),
407 : {{ {tmp_}, {} }},
408 0 : more_input_);
409 :
410 0 : if(rs.ec)
411 : {
412 0 : ws_.clear();
413 0 : state_ = state::reset;
414 0 : return rs.ec;
415 : }
416 :
417 0 : capy::remove_prefix(tmp_, rs.in_bytes);
418 0 : out_commit(rs.out_bytes);
419 :
420 0 : if(rs.out_short)
421 0 : break;
422 :
423 0 : if(rs.finished)
424 : {
425 0 : filter_done_ = true;
426 0 : out_finish();
427 : }
428 : }
429 0 : break;
430 : }
431 :
432 0 : case style::stream:
433 : {
434 0 : if(out_capacity() == 0 || filter_done_)
435 0 : break;
436 :
437 0 : const auto rs = filter_->process(
438 0 : detail::make_span(out_prepare()),
439 : in_.data(),
440 0 : more_input_);
441 :
442 0 : if(rs.ec)
443 : {
444 0 : ws_.clear();
445 0 : state_ = state::reset;
446 0 : return rs.ec;
447 : }
448 :
449 0 : in_.consume(rs.in_bytes);
450 0 : out_commit(rs.out_bytes);
451 :
452 0 : if(rs.finished)
453 : {
454 0 : filter_done_ = true;
455 0 : out_finish();
456 : }
457 :
458 0 : if(out_.size() == 0 && is_header_done() && more_input_)
459 0 : BOOST_HTTP_RETURN_EC(
460 : error::need_data);
461 0 : break;
462 : }
463 : }
464 : }
465 :
466 112 : prepped_.reset(!is_header_done());
467 336 : for(auto const& cb : out_.data())
468 : {
469 224 : if(cb.size() != 0)
470 89 : prepped_.append(cb);
471 : }
472 112 : return detail::make_span(prepped_);
473 : }
474 :
475 : void
476 1848 : consume(
477 : std::size_t n)
478 : {
479 : // Precondition violation
480 1848 : if(state_ < state::header)
481 1 : detail::throw_logic_error();
482 :
483 1847 : if(!is_header_done())
484 : {
485 : const auto header_remain =
486 77 : prepped_[0].size();
487 77 : if(n < header_remain)
488 : {
489 32 : prepped_.consume(n);
490 32 : return;
491 : }
492 45 : n -= header_remain;
493 45 : prepped_.consume(header_remain);
494 45 : state_ = state::body;
495 : }
496 :
497 1815 : prepped_.consume(n);
498 :
499 : // no-op when out_ is not in use
500 1815 : out_.consume(n);
501 :
502 1815 : if(!prepped_.empty())
503 1744 : return;
504 :
505 71 : if(more_input_)
506 33 : return;
507 :
508 38 : if(filter_ && !filter_done_)
509 0 : return;
510 :
511 38 : if(needs_exp100_continue_)
512 1 : return;
513 :
514 : // ready for next message
515 37 : reset();
516 : }
517 :
518 : void
519 91 : start_init(
520 : message_base const& m)
521 : {
522 : // Precondition violation
523 91 : if(state_ != state::start)
524 0 : detail::throw_logic_error();
525 :
526 : // TODO: To uphold the strong exception guarantee,
527 : // `state_` must be reset to `state::start` if an
528 : // exception is thrown during the start operation.
529 91 : state_ = state::header;
530 :
531 : // VFALCO what do we do with
532 : // metadata error code failures?
533 : // m.h_.md.maybe_throw();
534 :
535 91 : auto const& md = m.metadata();
536 91 : needs_exp100_continue_ = md.expect.is_100_continue;
537 :
538 : // Transfer-Encoding
539 91 : is_chunked_ = md.transfer_encoding.is_chunked;
540 :
541 : // Content-Encoding
542 91 : switch (md.content_encoding.coding)
543 : {
544 0 : case content_coding::deflate:
545 0 : if(!cfg_->apply_deflate_encoder)
546 0 : goto no_filter;
547 0 : if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
548 : {
549 0 : filter_.reset(new zlib_filter(
550 : *svc,
551 0 : cfg_->zlib_comp_level,
552 0 : cfg_->zlib_window_bits,
553 0 : cfg_->zlib_mem_level));
554 0 : filter_done_ = false;
555 : }
556 0 : break;
557 :
558 0 : case content_coding::gzip:
559 0 : if(!cfg_->apply_gzip_encoder)
560 0 : goto no_filter;
561 0 : if(auto* svc = capy::get_system_context().find_service<http::zlib::deflate_service>())
562 : {
563 0 : filter_.reset(new zlib_filter(
564 : *svc,
565 0 : cfg_->zlib_comp_level,
566 0 : cfg_->zlib_window_bits + 16,
567 0 : cfg_->zlib_mem_level));
568 0 : filter_done_ = false;
569 : }
570 0 : break;
571 :
572 0 : case content_coding::br:
573 0 : if(!cfg_->apply_brotli_encoder)
574 0 : goto no_filter;
575 0 : if(auto* svc = capy::get_system_context().find_service<http::brotli::encode_service>())
576 : {
577 0 : filter_.reset(new brotli_filter(
578 : *svc,
579 0 : cfg_->brotli_comp_quality,
580 0 : cfg_->brotli_comp_window));
581 0 : filter_done_ = false;
582 : }
583 0 : break;
584 :
585 0 : no_filter:
586 91 : default:
587 91 : filter_.reset();
588 91 : break;
589 : }
590 91 : }
591 :
592 : void
593 6 : start_empty(
594 : message_base const& m)
595 : {
596 6 : start_init(m);
597 6 : style_ = style::empty;
598 :
599 6 : prepped_ = make_array(
600 : 1 + // header
601 : 2); // out buffer pairs
602 :
603 6 : out_init();
604 :
605 6 : if(!filter_)
606 6 : out_finish();
607 :
608 6 : prepped_.append({ m.h_.cbuf, m.h_.size });
609 6 : more_input_ = false;
610 6 : }
611 :
612 : void
613 10 : start_buffers(
614 : message_base const& m,
615 : cbs_gen& cbs_gen)
616 : {
617 : // start_init() already called
618 10 : style_ = style::buffers;
619 10 : cbs_gen_ = &cbs_gen;
620 :
621 10 : if(!filter_)
622 : {
623 10 : auto stats = cbs_gen_->stats();
624 10 : auto batch_size = clamp(stats.count, 16);
625 :
626 0 : prepped_ = make_array(
627 : 1 + // header
628 10 : batch_size + // buffers
629 10 : (is_chunked_ ? 2 : 0)); // chunk header + final chunk
630 :
631 10 : prepped_.append({ m.h_.cbuf, m.h_.size });
632 10 : more_input_ = (batch_size != 0);
633 :
634 10 : if(is_chunked_)
635 : {
636 2 : if(!more_input_)
637 : {
638 1 : prepped_.append(final_chunk);
639 : }
640 : else
641 : {
642 1 : auto h_len = chunk_header_len(stats.size);
643 : capy::mutable_buffer mb(
644 1 : ws_.reserve_front(h_len), h_len);
645 1 : write_chunk_header({{ {mb}, {} }}, stats.size);
646 1 : prepped_.append(mb);
647 : }
648 : }
649 10 : return;
650 : }
651 :
652 : // filter
653 :
654 0 : prepped_ = make_array(
655 : 1 + // header
656 : 2); // out buffer pairs
657 :
658 0 : out_init();
659 :
660 0 : prepped_.append({ m.h_.cbuf, m.h_.size });
661 0 : tmp_ = {};
662 0 : more_input_ = true;
663 : }
664 :
665 : void
666 75 : start_stream(message_base const& m)
667 : {
668 75 : start_init(m);
669 75 : style_ = style::stream;
670 :
671 75 : prepped_ = make_array(
672 : 1 + // header
673 : 2); // out buffer pairs
674 :
675 75 : if(filter_)
676 : {
677 : // TODO: smarter buffer distribution
678 0 : auto const n = (ws_.size() - 1) / 2;
679 0 : in_ = { ws_.reserve_front(n), n };
680 : }
681 :
682 75 : out_init();
683 :
684 75 : prepped_.append({ m.h_.cbuf, m.h_.size });
685 75 : more_input_ = true;
686 75 : }
687 :
688 : std::size_t
689 92 : stream_capacity() const
690 : {
691 92 : if(filter_)
692 0 : return in_.capacity();
693 92 : return out_capacity();
694 : }
695 :
696 : capy::mutable_buffer_pair
697 68 : stream_prepare()
698 : {
699 68 : if(state_ == state::start)
700 : {
701 0 : if(!msg_)
702 0 : detail::throw_logic_error();
703 0 : start_stream(*msg_);
704 : }
705 68 : if(filter_)
706 0 : return in_.prepare(in_.capacity());
707 68 : return out_prepare();
708 : }
709 :
710 : void
711 75 : stream_commit(std::size_t n)
712 : {
713 75 : if(n > stream_capacity())
714 1 : detail::throw_invalid_argument();
715 :
716 74 : if(filter_)
717 0 : return in_.commit(n);
718 :
719 74 : out_commit(n);
720 : }
721 :
722 : void
723 40 : stream_close() noexcept
724 : {
725 40 : if(!filter_)
726 40 : out_finish();
727 :
728 40 : more_input_ = false;
729 40 : }
730 :
731 : bool
732 236 : is_done() const noexcept
733 : {
734 236 : return state_ == state::start;
735 : }
736 :
737 : detail::workspace&
738 10 : ws() noexcept
739 : {
740 10 : return ws_;
741 : }
742 :
743 : private:
744 : bool
745 2014 : is_header_done() const noexcept
746 : {
747 2014 : return state_ == state::body;
748 : }
749 :
750 : detail::array_of_const_buffers
751 91 : make_array(std::size_t n)
752 : {
753 91 : BOOST_ASSERT(n <= std::uint16_t(-1));
754 :
755 : return {
756 91 : ws_.push_array(n,
757 0 : capy::const_buffer{}),
758 91 : static_cast<std::uint16_t>(n) };
759 : }
760 :
761 : void
762 81 : out_init()
763 : {
764 : // use all the remaining buffer
765 81 : auto const n = ws_.size() - 1;
766 81 : out_ = { ws_.reserve_front(n), n };
767 81 : chunk_header_len_ =
768 81 : chunk_header_len(out_.capacity());
769 81 : if(out_capacity() == 0)
770 0 : detail::throw_length_error();
771 81 : }
772 :
773 : capy::mutable_buffer_pair
774 68 : out_prepare() noexcept
775 : {
776 68 : auto mbp = out_.prepare(out_.capacity());
777 68 : if(is_chunked_)
778 : {
779 35 : capy::remove_prefix(
780 35 : mbp, chunk_header_len_);
781 35 : capy::remove_suffix(
782 : mbp, crlf_and_final_chunk.size());
783 : }
784 68 : return mbp;
785 : }
786 :
787 : void
788 74 : out_commit(
789 : std::size_t n) noexcept
790 : {
791 74 : if(is_chunked_)
792 : {
793 35 : if(n == 0)
794 1 : return;
795 :
796 34 : write_chunk_header(out_.prepare(chunk_header_len_), n);
797 34 : out_.commit(chunk_header_len_);
798 :
799 34 : out_.prepare(n);
800 34 : out_.commit(n);
801 :
802 34 : capy::buffer_copy(out_.prepare(crlf.size()), crlf);
803 34 : out_.commit(crlf.size());
804 : }
805 : else
806 : {
807 39 : out_.commit(n);
808 : }
809 : }
810 :
811 : std::size_t
812 173 : out_capacity() const noexcept
813 : {
814 173 : if(is_chunked_)
815 : {
816 82 : auto const overhead = chunk_header_len_ +
817 82 : crlf_and_final_chunk.size();
818 82 : if(out_.capacity() < overhead)
819 1 : return 0;
820 81 : return out_.capacity() - overhead;
821 : }
822 91 : return out_.capacity();
823 : }
824 :
825 : void
826 46 : out_finish() noexcept
827 : {
828 46 : if(is_chunked_)
829 : {
830 23 : capy::buffer_copy(
831 23 : out_.prepare(final_chunk.size()), final_chunk);
832 23 : out_.commit(final_chunk.size());
833 : }
834 46 : }
835 : };
836 :
837 : //------------------------------------------------
838 :
839 92 : serializer::
840 : ~serializer()
841 : {
842 92 : delete impl_;
843 92 : }
844 :
845 1 : serializer::
846 1 : serializer(serializer&& other) noexcept
847 1 : : impl_(other.impl_)
848 : {
849 1 : other.impl_ = nullptr;
850 1 : }
851 :
852 : serializer&
853 2 : serializer::
854 : operator=(serializer&& other) noexcept
855 : {
856 2 : if(this != &other)
857 : {
858 2 : delete impl_;
859 2 : impl_ = other.impl_;
860 2 : other.impl_ = nullptr;
861 : }
862 2 : return *this;
863 : }
864 :
865 87 : serializer::
866 : serializer(
867 87 : std::shared_ptr<serializer_config_impl const> cfg)
868 87 : : impl_(new impl(std::move(cfg)))
869 : {
870 87 : }
871 :
872 0 : serializer::
873 : serializer(
874 : std::shared_ptr<serializer_config_impl const> cfg,
875 0 : message_base const& m)
876 0 : : impl_(new impl(std::move(cfg), m))
877 : {
878 0 : }
879 :
880 : void
881 6 : serializer::
882 : reset() noexcept
883 : {
884 6 : BOOST_ASSERT(impl_);
885 6 : impl_->reset();
886 6 : }
887 :
888 : void
889 0 : serializer::
890 : set_message(message_base const& m) noexcept
891 : {
892 0 : BOOST_ASSERT(impl_);
893 0 : impl_->msg_ = &m;
894 0 : }
895 :
896 : void
897 6 : serializer::
898 : start(message_base const& m)
899 : {
900 6 : BOOST_ASSERT(impl_);
901 6 : impl_->start_empty(m);
902 6 : }
903 :
904 : void
905 0 : serializer::
906 : start()
907 : {
908 0 : if(!impl_ || !impl_->msg_)
909 0 : detail::throw_logic_error();
910 0 : impl_->start_empty(*impl_->msg_);
911 0 : }
912 :
913 : void
914 75 : serializer::
915 : start_stream(
916 : message_base const& m)
917 : {
918 75 : BOOST_ASSERT(impl_);
919 75 : impl_->start_stream(m);
920 75 : }
921 :
922 : void
923 0 : serializer::
924 : start_stream()
925 : {
926 0 : if(!impl_ || !impl_->msg_)
927 0 : detail::throw_logic_error();
928 0 : impl_->start_stream(*impl_->msg_);
929 0 : }
930 :
931 : auto
932 170 : serializer::
933 : prepare() ->
934 : system::result<const_buffers_type>
935 : {
936 170 : BOOST_ASSERT(impl_);
937 170 : return impl_->prepare();
938 : }
939 :
940 : void
941 1848 : serializer::
942 : consume(std::size_t n)
943 : {
944 1848 : BOOST_ASSERT(impl_);
945 1848 : impl_->consume(n);
946 1847 : }
947 :
948 : bool
949 236 : serializer::
950 : is_done() const noexcept
951 : {
952 236 : BOOST_ASSERT(impl_);
953 236 : return impl_->is_done();
954 : }
955 :
956 : //------------------------------------------------
957 :
958 : detail::workspace&
959 10 : serializer::
960 : ws()
961 : {
962 10 : BOOST_ASSERT(impl_);
963 10 : return impl_->ws();
964 : }
965 :
966 : void
967 10 : serializer::
968 : start_init(message_base const& m)
969 : {
970 10 : BOOST_ASSERT(impl_);
971 10 : impl_->start_init(m);
972 10 : }
973 :
974 : void
975 10 : serializer::
976 : start_buffers(
977 : message_base const& m,
978 : cbs_gen& cbs_gen)
979 : {
980 10 : BOOST_ASSERT(impl_);
981 10 : impl_->start_buffers(m, cbs_gen);
982 10 : }
983 :
984 : //------------------------------------------------
985 :
986 : std::size_t
987 17 : serializer::
988 : stream_capacity() const
989 : {
990 17 : BOOST_ASSERT(impl_);
991 17 : return impl_->stream_capacity();
992 : }
993 :
994 : auto
995 68 : serializer::
996 : stream_prepare() ->
997 : mutable_buffers_type
998 : {
999 68 : BOOST_ASSERT(impl_);
1000 68 : return impl_->stream_prepare();
1001 : }
1002 :
1003 : void
1004 75 : serializer::
1005 : stream_commit(std::size_t n)
1006 : {
1007 75 : BOOST_ASSERT(impl_);
1008 75 : impl_->stream_commit(n);
1009 74 : }
1010 :
1011 : void
1012 40 : serializer::
1013 : stream_close() noexcept
1014 : {
1015 40 : BOOST_ASSERT(impl_);
1016 40 : impl_->stream_close();
1017 40 : }
1018 :
1019 : } // http
1020 : } // boost
|