GCC Code Coverage Report


Directory: libs/http_proto/
File: src/serializer.cpp
Date: 2025-06-15 05:10:38
Exec Total Coverage
Lines: 391 415 94.2%
Functions: 32 33 97.0%
Branches: 228 305 74.8%

Line Branch Exec Source
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_proto
10 //
11
12 #include <boost/http_proto/detail/except.hpp>
13 #include <boost/http_proto/message_view_base.hpp>
14 #include <boost/http_proto/serializer.hpp>
15 #include <boost/http_proto/service/zlib_service.hpp>
16
17 #include "src/detail/filter.hpp"
18
19 #include <boost/buffers/copy.hpp>
20 #include <boost/buffers/prefix.hpp>
21 #include <boost/buffers/sans_prefix.hpp>
22 #include <boost/buffers/sans_suffix.hpp>
23 #include <boost/buffers/suffix.hpp>
24 #include <boost/buffers/size.hpp>
25 #include <boost/core/ignore_unused.hpp>
26
27 #include <stddef.h>
28
29 namespace boost {
30 namespace http_proto {
31
32 namespace {
33
34 class deflator_filter
35 : public http_proto::detail::filter
36 {
37 zlib::stream& deflator_;
38
39 public:
40 49 deflator_filter(
41 context& ctx,
42 http_proto::detail::workspace& ws,
43 bool use_gzip)
44 196 : deflator_{ ctx.get_service<zlib::service>()
45
2/2
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 24 times.
49 .make_deflator(ws, -1, use_gzip ? 31 : 15, 8) }
46 {
47 49 }
48
49 virtual filter::results
50 21237 on_process(
51 buffers::mutable_buffer out,
52 buffers::const_buffer in,
53 bool more) override
54 {
55 21237 auto flush =
56
2/2
✓ Branch 0 taken 21136 times.
✓ Branch 1 taken 101 times.
21237 more ? zlib::flush::none : zlib::flush::finish;
57 21237 filter::results results;
58
59 for(;;)
60 {
61 38871 auto params = zlib::params{in.data(), in.size(),
62 38871 out.data(), out.size() };
63 38871 auto ec = deflator_.write(params, flush);
64
65 38871 results.in_bytes += in.size() - params.avail_in;
66 38871 results.out_bytes += out.size() - params.avail_out;
67
68
4/4
✓ Branch 1 taken 8925 times.
✓ Branch 2 taken 29946 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 8924 times.
47796 if( ec.failed() &&
69
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 38870 times.
47796 ec != zlib::error::buf_err)
70 {
71 1 results.ec = ec;
72 21237 return results;
73 }
74
75
2/2
✓ Branch 2 taken 48 times.
✓ Branch 3 taken 38822 times.
38870 if( ec == zlib::error::stream_end )
76 {
77 48 results.finished = true;
78 48 return results;
79 }
80
81 38822 in = buffers::suffix(in, params.avail_in);
82 38822 out = buffers::suffix(out, params.avail_out);
83
84
2/2
✓ Branch 1 taken 3524 times.
✓ Branch 2 taken 35298 times.
38822 if( out.size() == 0 )
85 3524 return results;
86
87
1/2
✓ Branch 1 taken 35298 times.
✗ Branch 2 not taken.
35298 if( in.size() == 0 )
88 {
89
4/4
✓ Branch 0 taken 23956 times.
✓ Branch 1 taken 11342 times.
✓ Branch 2 taken 17634 times.
✓ Branch 3 taken 6322 times.
35298 if( results.out_bytes == 0 &&
90 flush == zlib::flush::none )
91 {
92 // TODO: Is flush::block the right choice?
93 // We might need a filter::flush() interface
94 // so that the caller can decide when to flush.
95 17634 flush = zlib::flush::block;
96 17634 continue;
97 }
98 17664 return results;
99 }
100 17634 }
101 }
102 };
103
104 //------------------------------------------------
105
106 constexpr
107 std::size_t
108 crlf_len = 2;
109
110 constexpr
111 std::size_t
112 chunk_header_len = 16 + crlf_len;
113
114 constexpr
115 std::size_t
116 final_chunk_len = 1 + crlf_len + crlf_len;
117
118 constexpr
119 std::size_t
120 chunked_overhead_ =
121 chunk_header_len +
122 crlf_len +
123 final_chunk_len;
124
125 template<class MutableBufferSequence>
126 void
127 9134 write_chunk_header(
128 const MutableBufferSequence& mbs,
129 std::size_t size) noexcept
130 {
131 static constexpr char hexdig[] =
132 "0123456789ABCDEF";
133 char buf[18];
134 9134 auto p = buf + 16;
135
2/2
✓ Branch 0 taken 73072 times.
✓ Branch 1 taken 4567 times.
155278 for(std::size_t i = 16; i--;)
136 {
137 146144 *--p = hexdig[size & 0xf];
138 146144 size >>= 4;
139 }
140 9134 buf[16] = '\r';
141 9134 buf[17] = '\n';
142 9134 auto n = buffers::copy(
143 mbs,
144 18268 buffers::const_buffer(
145 buf, sizeof(buf)));
146 ignore_unused(n);
147
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4567 times.
9134 BOOST_ASSERT(n == 18);
148 9134 }
149
150 template<class MutableBufferSequence>
151 void
152 9134 write_crlf(
153 const MutableBufferSequence& mbs) noexcept
154 {
155 9134 auto n = buffers::copy(
156 mbs,
157 18268 buffers::const_buffer(
158 "\r\n", 2));
159 ignore_unused(n);
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4567 times.
9134 BOOST_ASSERT(n == 2);
161 9134 }
162
163 template<class MutableBufferSequence>
164 void
165 62 write_final_chunk(
166 const MutableBufferSequence& mbs) noexcept
167 {
168 62 auto n = buffers::copy(
169 mbs,
170 124 buffers::const_buffer(
171 "0\r\n\r\n", 5));
172 ignore_unused(n);
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
62 BOOST_ASSERT(n == 5);
174 62 }
175
176 //------------------------------------------------
177
178 class appender
179 {
180 buffers::circular_buffer& cb_;
181 buffers::mutable_buffer_pair mbp_;
182 std::size_t n_ = 0;
183 bool is_chunked_ = false;
184 bool more_input_ = true;
185
186 public:
187 9071 appender(
188 buffers::circular_buffer& cb,
189 bool is_chunked)
190 9071 : cb_(cb)
191 9071 , mbp_(cb.prepare(cb.capacity()))
192 9071 , is_chunked_(is_chunked)
193 {
194 9071 }
195
196 bool
197 21335 is_full() const noexcept
198 {
199 21335 auto remaining = cb_.capacity() - n_;
200
2/2
✓ Branch 0 taken 10688 times.
✓ Branch 1 taken 10647 times.
21335 if(is_chunked_)
201 10688 return remaining <= chunked_overhead_;
202
203 10647 return remaining == 0;
204 }
205
206 buffers::mutable_buffer_pair
207 12331 prepare() noexcept
208 {
209
2/2
✓ Branch 0 taken 6168 times.
✓ Branch 1 taken 6163 times.
12331 if(is_chunked_)
210 {
211 6168 return buffers::sans_suffix(
212 6168 buffers::sans_prefix(
213 6168 mbp_,
214 6168 chunk_header_len + n_)
215 6168 , final_chunk_len + crlf_len);
216 }
217 6163 return buffers::sans_prefix(mbp_, n_);
218 }
219
220 void
221 12329 commit(std::size_t n, bool more) noexcept
222 {
223
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12329 times.
12329 BOOST_ASSERT(more_input_);
224 12329 n_ += n;
225 12329 more_input_ = more;
226 12329 }
227
228 9071 ~appender()
229 {
230
2/2
✓ Branch 0 taken 4552 times.
✓ Branch 1 taken 4519 times.
9071 if(is_chunked_)
231 {
232
2/2
✓ Branch 0 taken 4551 times.
✓ Branch 1 taken 1 times.
4552 if(n_)
233 {
234 4551 write_chunk_header(mbp_, n_);
235 4551 cb_.commit(n_ + chunk_header_len);
236
237 4551 write_crlf(
238 4551 cb_.prepare(crlf_len));
239 4551 cb_.commit(crlf_len);
240 }
241
242
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 4526 times.
4552 if(!more_input_)
243 {
244 26 write_final_chunk(
245 26 cb_.prepare(final_chunk_len));
246 26 cb_.commit(final_chunk_len);
247 }
248 }
249 else // is_chunked_ == false
250 {
251 4519 cb_.commit(n_);
252 }
253 9071 }
254 };
255
256 } // namespace
257
258 //------------------------------------------------
259
260 45 serializer::
261 ~serializer()
262 {
263 45 }
264
265 serializer::
266 serializer(
267 serializer&&) noexcept = default;
268
269 11 serializer::
270 serializer(
271 11 context& ctx)
272 11 : serializer(ctx, 65536)
273 {
274 11 }
275
276 45 serializer::
277 serializer(
278 context& ctx,
279 45 std::size_t buffer_size)
280 45 : ctx_(ctx)
281 45 , ws_(buffer_size)
282 {
283 45 }
284
285 void
286 131 serializer::
287 reset() noexcept
288 {
289 131 filter_ = nullptr;
290
291 131 cb0_ = {};
292 131 tmp_ = {};
293
294 131 more_input_ = false;
295 131 is_done_ = false;
296 131 is_header_done_ = false;
297 131 is_chunked_ = false;
298 131 needs_exp100_continue_ = false;
299 131 filter_done_ = false;
300
301 131 ws_.clear();
302 131 }
303
304 //------------------------------------------------
305
306 auto
307 9138 serializer::
308 prepare() ->
309 system::result<const_buffers_type>
310 {
311 // Precondition violation
312
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9137 times.
9138 if(is_done_)
313 1 detail::throw_logic_error();
314
315 // Expect: 100-continue
316
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9133 times.
9137 if(needs_exp100_continue_)
317 {
318
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(!is_header_done_)
319 4 return const_buffers_type(
320 2 prepped_.begin(),
321 2 1); // limit to header
322
323 2 needs_exp100_continue_ = false;
324
325 2 BOOST_HTTP_PROTO_RETURN_EC(
326 error::expect_100_continue);
327 }
328
329
2/2
✓ Branch 0 taken 80 times.
✓ Branch 1 taken 9053 times.
9133 if(!filter_)
330 {
331
4/5
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 19 times.
✓ Branch 2 taken 23 times.
✓ Branch 3 taken 35 times.
✗ Branch 4 not taken.
80 switch(st_)
332 {
333 3 case style::empty:
334 6 return const_buffers_type(
335 3 prepped_.begin(),
336 6 prepped_.size());
337
338 19 case style::buffers:
339 // add more buffers if prepped_ is half empty.
340
6/6
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 17 times.
29 if(more_input_ &&
341 10 prepped_.capacity() >= prepped_.size())
342 {
343 2 prepped_.slide_to_front();
344
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 while(prepped_.capacity() != 0)
345 {
346
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 auto buf = buf_gen_->operator()();
347
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 2 times.
15 if(buf.size() != 0)
348 {
349 13 prepped_.append(buf);
350 }
351 else // buf_gen_ is empty
352 {
353 // crlf and final chunk
354
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if(tmp_.size() != 0)
355 {
356 1 prepped_.append(tmp_);
357 1 tmp_ = {};
358 }
359 2 break;
360 }
361 }
362
4/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 2 times.
✗ Branch 9 not taken.
2 if(buf_gen_->is_empty() && tmp_.size() == 0)
363 2 more_input_ = false;
364 }
365 38 return const_buffers_type(
366 19 prepped_.begin(),
367 38 prepped_.size());
368
369 23 case style::source:
370 {
371
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 18 times.
23 if(!more_input_)
372 22 break;
373
374 // handles chunked payloads automatically
375
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 appender apndr(cb0_, is_chunked_);
376
377
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
18 if(apndr.is_full())
378 break;
379
380
1/2
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
18 auto rs = source_->read(
381 18 apndr.prepare());
382
383
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 17 times.
18 if(rs.ec.failed())
384 {
385 1 is_done_ = true;
386 1 BOOST_HTTP_PROTO_RETURN_EC(rs.ec);
387 }
388
389 17 more_input_ = !rs.finished;
390 17 apndr.commit(rs.bytes, more_input_);
391 17 break;
392
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 1 times.
18 }
393
394 35 case style::stream:
395
6/6
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 29 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 34 times.
35 if(is_header_done_ && cb0_.size() == 0)
396 1 BOOST_HTTP_PROTO_RETURN_EC(
397 error::need_data);
398 34 break;
399 }
400 }
401 else // filter
402 {
403
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9053 times.
9053 if(st_ == style::empty)
404 return const_buffers_type(
405 prepped_.begin(),
406 prepped_.size());
407
408 17793 auto get_input = [&]()
409 {
410
2/2
✓ Branch 0 taken 1329 times.
✓ Branch 1 taken 16464 times.
17793 if(st_ == style::buffers)
411 {
412 // TODO: for efficiency of deflator, we might
413 // need to return multiple buffers at once
414
2/2
✓ Branch 1 taken 385 times.
✓ Branch 2 taken 944 times.
1329 if(tmp_.size() == 0)
415 {
416 385 tmp_ = buf_gen_->operator()();
417 385 more_input_ = !buf_gen_->is_empty();
418 }
419 return buffers::
420 1329 const_buffer_pair{ tmp_, {} };
421 }
422
423
3/4
✓ Branch 0 taken 10976 times.
✓ Branch 1 taken 5488 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10976 times.
16464 BOOST_ASSERT(
424 st_ == style::source ||
425 st_ == style::stream);
426
427 38416 if(st_ == style::source &&
428
7/8
✓ Branch 0 taken 5488 times.
✓ Branch 1 taken 10976 times.
✓ Branch 2 taken 5480 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 5480 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 5480 times.
✓ Branch 7 taken 10984 times.
21944 more_input_ &&
429 5480 cb1_.capacity() != 0)
430 {
431 // TODO: handle source error
432
1/2
✓ Branch 1 taken 5480 times.
✗ Branch 2 not taken.
5480 auto rs = source_->read(
433
1/2
✓ Branch 2 taken 5480 times.
✗ Branch 3 not taken.
5480 cb1_.prepare(cb1_.capacity()));
434
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 5464 times.
5480 if(rs.finished)
435 16 more_input_ = false;
436 5480 cb1_.commit(rs.bytes);
437 }
438
439 16464 return cb1_.data();
440 9053 };
441
442 12312 auto consume = [&](std::size_t n)
443 {
444
2/2
✓ Branch 0 taken 1328 times.
✓ Branch 1 taken 10984 times.
12312 if(st_ == style::buffers)
445 {
446 1328 tmp_ = buffers::sans_prefix(
447 1328 tmp_, n);
448 1328 return;
449 }
450
3/4
✓ Branch 0 taken 5496 times.
✓ Branch 1 taken 5488 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 5496 times.
10984 BOOST_ASSERT(
451 st_ == style::source ||
452 st_ == style::stream);
453 10984 cb1_.consume(n);
454 9053 };
455
456 // handles chunked payloads automatically
457
1/2
✓ Branch 1 taken 9053 times.
✗ Branch 2 not taken.
9053 appender apndr(cb0_, is_chunked_);
458 for(;;)
459 {
460
2/2
✓ Branch 1 taken 3524 times.
✓ Branch 2 taken 17793 times.
21317 if(apndr.is_full())
461 3524 break;
462
463
1/2
✓ Branch 1 taken 17793 times.
✗ Branch 2 not taken.
17793 auto cbs = get_input();
464
465
6/6
✓ Branch 0 taken 17692 times.
✓ Branch 1 taken 101 times.
✓ Branch 3 taken 5480 times.
✓ Branch 4 taken 12212 times.
✓ Branch 5 taken 5480 times.
✓ Branch 6 taken 12313 times.
17793 if(more_input_ && buffers::size(cbs) == 0)
466 5480 break;
467
468
1/2
✓ Branch 1 taken 12313 times.
✗ Branch 2 not taken.
12313 auto rs = filter_->process(
469 apndr.prepare(),
470 cbs,
471 12313 more_input_);
472
473
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 12312 times.
12313 if(rs.ec.failed())
474 {
475 1 is_done_ = true;
476 1 BOOST_HTTP_PROTO_RETURN_EC(rs.ec);
477 }
478
479
1/2
✓ Branch 1 taken 12312 times.
✗ Branch 2 not taken.
12312 consume(rs.in_bytes);
480 12312 apndr.commit(rs.out_bytes, !rs.finished);
481
482
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 12264 times.
12312 if(rs.finished)
483 {
484 48 filter_done_ = true;
485 48 break;
486 }
487 12264 }
488
2/2
✓ Branch 1 taken 9052 times.
✓ Branch 2 taken 1 times.
9053 }
489
490 9108 prepped_.reset(!is_header_done_);
491 9108 const auto cbp = cb0_.data();
492
2/2
✓ Branch 2 taken 9105 times.
✓ Branch 3 taken 3 times.
9108 if(cbp[0].size() != 0)
493 9105 prepped_.append(cbp[0]);
494
2/2
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 9094 times.
9108 if(cbp[1].size() != 0)
495 14 prepped_.append(cbp[1]);
496
497
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9108 times.
9108 BOOST_ASSERT(
498 buffers::size(prepped_) > 0);
499
500 18216 return const_buffers_type(
501 9108 prepped_.begin(),
502 18216 prepped_.size());
503 }
504
505 void
506 10871 serializer::
507 consume(
508 std::size_t n)
509 {
510 // Precondition violation
511
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10870 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
10871 if(is_done_ && n != 0)
512 1 detail::throw_logic_error();
513
514
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 10794 times.
10870 if(!is_header_done_)
515 {
516 const auto header_remain =
517 76 prepped_[0].size();
518
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 65 times.
76 if(n < header_remain)
519 {
520 11 prepped_.consume(n);
521 11 return;
522 }
523 65 n -= header_remain;
524 65 prepped_.consume(header_remain);
525 65 is_header_done_ = true;
526 }
527
528 10859 prepped_.consume(n);
529
530 // no-op when cb0_ is not in use
531 10859 cb0_.consume(n);
532
533
2/2
✓ Branch 1 taken 1760 times.
✓ Branch 2 taken 9099 times.
10859 if(!prepped_.empty())
534 1760 return;
535
536
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9098 times.
9099 if(needs_exp100_continue_)
537 1 return;
538
539
2/2
✓ Branch 0 taken 8981 times.
✓ Branch 1 taken 117 times.
9098 if(more_input_)
540 8981 return;
541
542
4/4
✓ Branch 0 taken 100 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 48 times.
117 if(filter_ && !filter_done_)
543 52 return;
544
545 65 is_done_ = true;
546 }
547
548 //------------------------------------------------
549
550 detail::array_of_const_buffers
551 75 serializer::
552 make_array(std::size_t n)
553 {
554
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 75 times.
75 if(n > std::numeric_limits<std::uint16_t>::max())
555 detail::throw_length_error();
556
557 return {
558 75 ws_.push_array(n,
559 buffers::const_buffer{}),
560
1/2
✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
75 static_cast<std::uint16_t>(n) };
561 }
562
563 void
564 75 serializer::
565 start_init(
566 message_view_base const& m)
567 {
568 75 reset();
569
570 // VFALCO what do we do with
571 // metadata error code failures?
572 // m.ph_->md.maybe_throw();
573
574 75 auto const& md = m.metadata();
575 75 needs_exp100_continue_ = md.expect.is_100_continue;
576
577 // Transfer-Encoding
578 75 is_chunked_ = md.transfer_encoding.is_chunked;
579
580 // Content-Encoding
581 75 auto const& ce = md.content_encoding;
582
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 51 times.
75 if(ce.encoding == encoding::deflate)
583 {
584 24 filter_ = &ws_.emplace<
585
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 deflator_filter>(ctx_, ws_, false);
586 }
587
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 26 times.
51 else if(ce.encoding == encoding::gzip)
588 {
589 25 filter_ = &ws_.emplace<
590
1/2
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
25 deflator_filter>(ctx_, ws_, true);
591 }
592 75 }
593
594 void
595 4 serializer::
596 start_empty(
597 message_view_base const& m)
598 {
599 using mutable_buffer =
600 buffers::mutable_buffer;
601
602 4 start_init(m);
603 4 st_ = style::empty;
604
605
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if(!is_chunked_)
606 {
607
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 prepped_ = make_array(
608 1); // header
609 }
610 else
611 {
612
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 prepped_ = make_array(
613 1 + // header
614 1); // final chunk
615
616 mutable_buffer final_chunk = {
617
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ws_.reserve_front(
618 final_chunk_len),
619 1 final_chunk_len };
620 1 write_final_chunk(final_chunk);
621
622 1 prepped_[1] = final_chunk;
623 }
624
625 4 prepped_[0] = { m.ph_->cbuf, m.ph_->size };
626 4 }
627
628 void
629 24 serializer::
630 start_buffers(
631 message_view_base const& m)
632 {
633 using mutable_buffer =
634 buffers::mutable_buffer;
635
636 // start_init() already called
637 24 st_ = style::buffers;
638
639 24 const auto buffers_max = (std::min)(
640 48 std::size_t{ 16 },
641
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 buf_gen_->count());
642
643
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 17 times.
24 if(!filter_)
644 {
645
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 if(!is_chunked_)
646 {
647 // no filter and no chunked
648
649
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 prepped_ = make_array(
650 1 + // header
651 buffers_max ); // buffers
652
653 6 prepped_[0] = { m.ph_->cbuf, m.ph_->size };
654
1/2
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
12 std::generate(
655 6 prepped_.begin() + 1,
656 prepped_.end(),
657 6 std::ref(*buf_gen_));
658
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 more_input_ = !buf_gen_->is_empty();
659 6 return;
660 }
661
662 // no filter and chunked
663
664
2/4
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
1 if(buf_gen_->is_empty())
665 {
666 prepped_ = make_array(
667 1 + // header
668 1); // final chunk
669
670 mutable_buffer final_chunk = {
671 ws_.reserve_front(
672 final_chunk_len),
673 final_chunk_len };
674 write_final_chunk(
675 final_chunk);
676
677 prepped_[0] = { m.ph_->cbuf, m.ph_->size };
678 prepped_[1] = final_chunk;
679 return;
680 }
681
682 // Write entire buffers as a single chunk
683 // since total size is known
684
685 mutable_buffer chunk_header = {
686
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ws_.reserve_front(
687 chunk_header_len),
688 1 chunk_header_len };
689
690 1 write_chunk_header(
691 chunk_header,
692
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 buf_gen_->size());
693
694 mutable_buffer crlf_and_final_chunk = {
695
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 ws_.reserve_front(
696 crlf_len + final_chunk_len),
697 1 crlf_len + final_chunk_len };
698
699 1 write_crlf(
700 1 buffers::prefix(
701 crlf_and_final_chunk,
702 crlf_len));
703
704 1 write_final_chunk(
705
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 buffers::sans_prefix(
706 crlf_and_final_chunk,
707 crlf_len));
708
709
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 prepped_ = make_array(
710 1 + // header
711 1 + // chunk header
712 buffers_max + // buffers
713 1); // buffer or (crlf and final chunk)
714
715 1 prepped_[0] = { m.ph_->cbuf, m.ph_->size };
716 1 prepped_[1] = chunk_header;
717
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 std::generate(
718 1 prepped_.begin() + 2,
719 1 prepped_.end() - 1,
720 1 std::ref(*buf_gen_));
721
722
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 more_input_ = !buf_gen_->is_empty();
723 // assigning the last slot
724
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(more_input_)
725 {
726 1 prepped_[prepped_.size() - 1] =
727
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
2 buf_gen_->operator()();
728
729 // deferred until buf_gen_ is drained
730 1 tmp_ = crlf_and_final_chunk;
731 }
732 else
733 {
734 prepped_[prepped_.size() - 1] =
735 crlf_and_final_chunk;
736 }
737 1 return;
738 }
739
740 // filter
741
742
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 prepped_ = make_array(
743 1 + // header
744 2); // circular buffer
745
746 17 const auto n = ws_.size() - 1;
747
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 cb0_ = { ws_.reserve_front(n), n };
748
749
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 if(is_chunked_)
750 {
751
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if(cb0_.capacity() <= chunked_overhead_)
752 detail::throw_length_error();
753 }
754 else
755 {
756
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if(cb0_.capacity() == 0)
757 detail::throw_length_error();
758 }
759
760 17 prepped_[0] = { m.ph_->cbuf, m.ph_->size };
761 17 more_input_ = !buf_gen_->is_empty();
762 }
763
764 void
765 25 serializer::
766 start_source(
767 message_view_base const& m)
768 {
769 // start_init() already called
770 25 st_ = style::source;
771
772
1/2
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
25 prepped_ = make_array(
773 1 + // header
774 2); // circular buffer
775
776
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 9 times.
25 if(filter_)
777 {
778 // TODO: Optimize buffer distribution
779 16 const auto n = (ws_.size() - 1) / 2;
780
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 cb0_ = { ws_.reserve_front(n), n };
781
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 cb1_ = { ws_.reserve_front(n), n };
782 }
783 else
784 {
785 9 const auto n = ws_.size() - 1;
786
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 cb0_ = { ws_.reserve_front(n), n };
787 }
788
789
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 15 times.
25 if(is_chunked_)
790 {
791
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
10 if(cb0_.capacity() <= chunked_overhead_)
792 detail::throw_length_error();
793 }
794 else
795 {
796
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 15 times.
15 if(cb0_.capacity() == 0)
797 detail::throw_length_error();
798 }
799
800 25 prepped_[0] = { m.ph_->cbuf, m.ph_->size };
801 25 more_input_ = true;
802 25 }
803
804 auto
805 22 serializer::
806 start_stream(
807 message_view_base const& m) ->
808 stream
809 {
810 22 start_init(m);
811 22 st_ = style::stream;
812
813
1/2
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
22 prepped_ = make_array(
814 1 + // header
815 2); // circular buffer
816
817
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 6 times.
22 if(filter_)
818 {
819 // TODO: Optimize buffer distribution
820 16 const auto n = (ws_.size() - 1) / 2;
821
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 cb0_ = { ws_.reserve_front(n), n };
822
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 cb1_ = { ws_.reserve_front(n), n };
823 }
824 else
825 {
826 6 const auto n = ws_.size() - 1;
827
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 cb0_ = { ws_.reserve_front(n), n };
828 }
829
830
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 11 times.
22 if(is_chunked_)
831 {
832
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 if(cb0_.capacity() <= chunked_overhead_)
833 detail::throw_length_error();
834 }
835 else
836 {
837
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 if(cb0_.capacity() == 0)
838 detail::throw_length_error();
839 }
840
841 22 prepped_[0] = { m.ph_->cbuf, m.ph_->size };
842 22 more_input_ = true;
843 22 return stream{ *this };
844 }
845
846 //------------------------------------------------
847
848 std::size_t
849 69 serializer::
850 stream::
851 capacity() const noexcept
852 {
853
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
69 if(sr_->filter_)
854 return sr_->cb1_.capacity();
855
856
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 35 times.
69 if(!sr_->is_chunked_)
857 34 return sr_->cb0_.capacity();
858
859 // chunked with no filter
860 35 const auto cap = sr_->cb0_.capacity();
861
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 14 times.
35 if(cap > chunked_overhead_)
862 21 return cap - chunked_overhead_;
863
864 14 return 0;
865 }
866
867 bool
868 61 serializer::
869 stream::
870 is_full() const noexcept
871 {
872 61 return capacity() == 0;
873 }
874
875 auto
876 5511 serializer::
877 stream::
878 prepare() const ->
879 buffers_type
880 {
881
2/2
✓ Branch 0 taken 5480 times.
✓ Branch 1 taken 31 times.
5511 if(sr_->filter_)
882 5480 return sr_->cb1_.prepare(
883 10960 sr_->cb1_.capacity());
884
885
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 16 times.
31 if(!sr_->is_chunked_)
886 15 return sr_->cb0_.prepare(
887 30 sr_->cb0_.capacity());
888
889 // chunked with no filter
890 16 const auto cap = sr_->cb0_.capacity();
891
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if(cap <= chunked_overhead_)
892 detail::throw_length_error();
893
894
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
16 return buffers::sans_prefix(
895
1/2
✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
32 sr_->cb0_.prepare(
896 cap - crlf_len - final_chunk_len),
897 16 chunk_header_len);
898 }
899
900 void
901 5511 serializer::
902 stream::
903 commit(std::size_t n) const
904 {
905
2/2
✓ Branch 0 taken 5480 times.
✓ Branch 1 taken 31 times.
5511 if(sr_->filter_)
906 5480 return sr_->cb1_.commit(n);
907
908
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 16 times.
31 if(!sr_->is_chunked_)
909 15 return sr_->cb0_.commit(n);
910
911 // chunked with no filter
912
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
16 if(n != 0)
913 {
914 15 write_chunk_header(
915
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 sr_->cb0_.prepare(
916 chunk_header_len),
917 n);
918 15 sr_->cb0_.commit(
919 chunk_header_len);
920
921
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 sr_->cb0_.prepare(n);
922 15 sr_->cb0_.commit(n);
923
924 15 write_crlf(
925
1/2
✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
15 sr_->cb0_.prepare(crlf_len));
926 15 sr_->cb0_.commit(crlf_len);
927 }
928 }
929
930 void
931 25 serializer::
932 stream::
933 close() const
934 {
935 // Precondition violation
936
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 21 times.
25 if(!sr_->more_input_)
937 4 detail::throw_logic_error();
938
939 21 sr_->more_input_ = false;
940
941
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 5 times.
21 if(sr_->filter_)
942 16 return;
943
944
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if(!sr_->is_chunked_)
945 2 return;
946
947 // chunked with no filter
948 3 write_final_chunk(
949
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 sr_->cb0_.prepare(
950 final_chunk_len));
951 3 sr_->cb0_.commit(final_chunk_len);
952 }
953
954 //------------------------------------------------
955
956 } // http_proto
957 } // boost
958