src/serializer.cpp

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