GCC Code Coverage Report


Directory: libs/http_proto/
File: src/serializer.cpp
Date: 2025-06-18 09:40:27
Exec Total Coverage
Lines: 399 437 91.3%
Functions: 32 33 97.0%
Branches: 251 342 73.4%

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