GCC Code Coverage Report


Directory: libs/http_proto/
File: src/serializer.cpp
Date: 2025-01-06 18:34:49
Exec Total Coverage
Lines: 348 377 92.3%
Functions: 26 28 92.9%
Branches: 188 224 83.9%

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 "detail/filter.hpp"
18
19 #include <boost/buffers/algorithm.hpp>
20 #include <boost/buffers/buffer_copy.hpp>
21 #include <boost/buffers/buffer_size.hpp>
22 #include <boost/core/ignore_unused.hpp>
23
24 #include <stddef.h>
25
26 namespace boost {
27 namespace http_proto {
28
29 namespace {
30 class deflator_filter
31 : public http_proto::detail::filter
32 {
33 zlib::stream& deflator_;
34
35 public:
36 49 deflator_filter(
37 context& ctx,
38 http_proto::detail::workspace& ws,
39 bool use_gzip)
40 196 : deflator_{ ctx.get_service<zlib::service>()
41
2/2
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 24 times.
49 .make_deflator(ws, -1, use_gzip ? 31 : 15, 8) }
42 {
43 49 }
44
45 virtual filter::results
46 23641 on_process(
47 buffers::mutable_buffer out,
48 buffers::const_buffer in,
49 bool more) override
50 {
51 23641 auto flush =
52
2/2
✓ Branch 0 taken 23537 times.
✓ Branch 1 taken 104 times.
23641 more ? zlib::flush::none : zlib::flush::finish;
53 23641 filter::results results;
54
55 for(;;)
56 {
57 45713 auto params = zlib::params{in.data(), in.size(),
58 45713 out.data(), out.size() };
59 45713 auto ec = deflator_.write(params, flush);
60
61 45713 results.in_bytes += in.size() - params.avail_in;
62 45713 results.out_bytes += out.size() - params.avail_out;
63
64
4/4
✓ Branch 1 taken 10969 times.
✓ Branch 2 taken 34744 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 10968 times.
56682 if( ec.failed() &&
65
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 45712 times.
56682 ec != zlib::error::buf_err)
66 {
67 1 results.ec = ec;
68 23641 return results;
69 }
70
71
2/2
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 45616 times.
45712 if( ec == zlib::error::stream_end )
72 {
73 96 results.finished = true;
74 96 return results;
75 }
76
77 45616 in = buffers::suffix(in, params.avail_in);
78 45616 out = buffers::suffix(out, params.avail_out);
79
80
2/2
✓ Branch 1 taken 1416 times.
✓ Branch 2 taken 44200 times.
45616 if( out.size() == 0 )
81 1416 return results;
82
83
1/2
✓ Branch 1 taken 44200 times.
✗ Branch 2 not taken.
44200 if( in.size() == 0 )
84 {
85
4/4
✓ Branch 0 taken 33016 times.
✓ Branch 1 taken 11184 times.
✓ Branch 2 taken 22072 times.
✓ Branch 3 taken 10944 times.
44200 if( results.out_bytes == 0 &&
86 flush == zlib::flush::none )
87 {
88 // TODO: Is flush::block the right choice?
89 // We might need a filter::flush() interface
90 // so that the caller can decide when to flush.
91 22072 flush = zlib::flush::block;
92 22072 continue;
93 }
94 22128 return results;
95 }
96 22072 }
97 }
98 };
99 } // namespace
100
101 void
102 consume_buffers(
103 buffers::const_buffer*& p,
104 std::size_t& n,
105 std::size_t bytes)
106 {
107 while(n > 0)
108 {
109 if(bytes < p->size())
110 {
111 *p += bytes;
112 return;
113 }
114 bytes -= p->size();
115 ++p;
116 --n;
117 }
118
119 // Precondition violation
120 if(bytes > 0)
121 detail::throw_invalid_argument();
122 }
123
124 template<class MutableBuffers>
125 void
126 6240 write_chunk_header(
127 MutableBuffers const& dest0,
128 std::size_t size) noexcept
129 {
130 static constexpr char hexdig[] =
131 "0123456789ABCDEF";
132 char buf[18];
133 6240 auto p = buf + 16;
134
2/2
✓ Branch 0 taken 99840 times.
✓ Branch 1 taken 6240 times.
106080 for(std::size_t i = 16; i--;)
135 {
136 99840 *--p = hexdig[size & 0xf];
137 99840 size >>= 4;
138 }
139 6240 buf[16] = '\r';
140 6240 buf[17] = '\n';
141 6240 auto n = buffers::buffer_copy(
142 dest0,
143 12480 buffers::const_buffer(
144 buf, sizeof(buf)));
145 ignore_unused(n);
146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6240 times.
6240 BOOST_ASSERT(n == 18);
147
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6240 times.
6240 BOOST_ASSERT(
148 buffers::buffer_size(dest0) == n);
149 6240 }
150
151 template<class DynamicBuffer>
152 void
153 write_chunk_close(DynamicBuffer& db)
154 {
155 db.commit(
156 buffers::buffer_copy(
157 db.prepare(2),
158 buffers::const_buffer("\r\n", 2)));
159 }
160
161 template<class DynamicBuffer>
162 void
163 write_last_chunk(DynamicBuffer& db)
164 {
165 db.commit(
166 buffers::buffer_copy(
167 db.prepare(5),
168 buffers::const_buffer("0\r\n\r\n", 5)));
169 }
170
171 //------------------------------------------------
172
173 45 serializer::
174 ~serializer()
175 {
176 45 }
177
178 serializer::
179 serializer(
180 serializer&&) noexcept = default;
181
182 11 serializer::
183 serializer(
184 11 context& ctx)
185 11 : serializer(ctx, 65536)
186 {
187 11 }
188
189 45 serializer::
190 serializer(
191 context& ctx,
192 45 std::size_t buffer_size)
193 45 : ws_(buffer_size)
194 45 , ctx_(ctx)
195 {
196 45 }
197
198 void
199 56 serializer::
200 reset() noexcept
201 {
202 56 chunk_header_ = {};
203 56 chunk_close_ = {};
204 56 last_chunk_ = {};
205 56 filter_ = nullptr;
206 56 more_ = false;
207 56 is_done_ = false;
208 56 is_chunked_ = false;
209 56 is_expect_continue_ = false;
210 56 is_compressed_ = false;
211 56 filter_done_ = false;
212 56 in_ = nullptr;
213 56 out_ = nullptr;
214 56 ws_.clear();
215 56 }
216
217 //------------------------------------------------
218
219 auto
220 12479 serializer::
221 prepare() ->
222 system::result<
223 const_buffers_type>
224 {
225 // Precondition violation
226
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12478 times.
12479 if( is_done_ )
227 1 detail::throw_logic_error();
228
229 // Expect: 100-continue
230
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 12474 times.
12478 if( is_expect_continue_ )
231 {
232
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if( !is_header_done_ )
233 2 return const_buffers_type(hp_, 1);
234 2 is_expect_continue_ = false;
235 2 BOOST_HTTP_PROTO_RETURN_EC(
236 error::expect_100_continue);
237 }
238
239
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 12471 times.
12474 if( st_ == style::empty )
240 9 return const_buffers_type(
241 6 prepped_.data(), prepped_.size());
242
243
4/4
✓ Branch 0 taken 1436 times.
✓ Branch 1 taken 11035 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1433 times.
12471 if( st_ == style::buffers && !filter_ )
244 9 return const_buffers_type(
245 6 prepped_.data(), prepped_.size());
246
247 // TODO: This is a temporary solution until we refactor
248 // the implementation for efficient partial buffer consumption.
249
8/8
✓ Branch 0 taken 6250 times.
✓ Branch 1 taken 6218 times.
✓ Branch 3 taken 37 times.
✓ Branch 4 taken 6213 times.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 30 times.
✓ Branch 7 taken 7 times.
✓ Branch 8 taken 12461 times.
12468 if( is_chunked_ && buffers::buffer_size(prepped_) && is_header_done_ )
250 21 return const_buffers_type(
251 14 prepped_.data(), prepped_.size());
252
253 12461 auto& input = *in_;
254 12461 auto& output = *out_;
255
4/4
✓ Branch 0 taken 5497 times.
✓ Branch 1 taken 6964 times.
✓ Branch 2 taken 5494 times.
✓ Branch 3 taken 3 times.
12461 if( st_ == style::source && more_ )
256 {
257
1/2
✓ Branch 1 taken 5494 times.
✗ Branch 2 not taken.
5494 auto results = src_->read(
258
1/2
✓ Branch 2 taken 5494 times.
✗ Branch 3 not taken.
5494 input.prepare(input.capacity()));
259
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5493 times.
5494 if(results.ec.failed())
260 {
261 1 is_done_ = true;
262 1 return results.ec;
263 }
264 5493 more_ = !results.finished;
265 5493 input.commit(results.bytes);
266 }
267
268 30451 if( st_ == style::stream &&
269
8/8
✓ Branch 0 taken 5531 times.
✓ Branch 1 taken 6929 times.
✓ Branch 2 taken 5510 times.
✓ Branch 3 taken 21 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 5509 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 12459 times.
17970 more_ &&
270 5510 in_->size() == 0 )
271 1 BOOST_HTTP_PROTO_RETURN_EC(error::need_data);
272
273 bool has_avail_out =
274
6/6
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 12409 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 36 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 9 times.
24873 ((!filter_ && (more_ || input.size() > 0)) ||
275
3/4
✓ Branch 0 taken 12409 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 12409 times.
✗ Branch 3 not taken.
12414 (filter_ && !filter_done_));
276
277 25057 auto get_input = [&]() -> buffers::const_buffer
278 {
279
2/2
✓ Branch 0 taken 3081 times.
✓ Branch 1 taken 21976 times.
25057 if( st_ == style::buffers )
280 {
281
2/2
✓ Branch 1 taken 48 times.
✓ Branch 2 taken 3033 times.
3081 if( buffers::buffer_size(buf_) == 0 )
282 48 return {};
283
284 3033 auto buf = *(buf_.data());
285
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3033 times.
3033 BOOST_ASSERT(buf.size() > 0);
286 3033 return buf;
287 }
288 else
289 {
290
2/2
✓ Branch 1 taken 11016 times.
✓ Branch 2 taken 10960 times.
21976 if( input.size() == 0 )
291 11016 return {};
292
293 10960 auto cbs = input.data();
294 10960 auto buf = *cbs.begin();
295
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10960 times.
10960 if( buf.size() == 0 )
296 {
297 auto p = cbs.begin();
298 ++p;
299 buf = *p;
300 }
301
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10960 times.
10960 if( buf.size() == 0 )
302 detail::throw_logic_error();
303 10960 return buf;
304 }
305 12459 };
306
307 25057 auto get_output = [&]() -> buffers::mutable_buffer
308 {
309
1/2
✓ Branch 2 taken 25057 times.
✗ Branch 3 not taken.
25057 auto mbs = output.prepare(output.capacity());
310 25057 auto buf = *mbs.begin();
311
2/2
✓ Branch 1 taken 1416 times.
✓ Branch 2 taken 23641 times.
25057 if( buf.size() == 0 )
312 {
313 1416 auto p = mbs.begin();
314 1416 ++p;
315 1416 buf = *p;
316 }
317 25057 return buf;
318 12459 };
319
320 23640 auto consume = [&](std::size_t n)
321 {
322
2/2
✓ Branch 0 taken 1664 times.
✓ Branch 1 taken 21976 times.
23640 if( st_ == style::buffers )
323 {
324 1664 buf_.consume(n);
325
2/2
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 1608 times.
1664 if( buffers::buffer_size(buf_) == 0 )
326 56 more_ = false;
327 }
328 else
329 21976 input.consume(n);
330 36099 };
331
332 12459 std::size_t num_written = 0;
333
2/2
✓ Branch 0 taken 12409 times.
✓ Branch 1 taken 50 times.
12459 if( !filter_ )
334 50 num_written += input.size();
335 else
336 {
337 for(;;)
338 {
339
1/2
✓ Branch 1 taken 25057 times.
✗ Branch 2 not taken.
25057 auto in = get_input();
340
1/2
✓ Branch 1 taken 25057 times.
✗ Branch 2 not taken.
25057 auto out = get_output();
341
2/2
✓ Branch 1 taken 1416 times.
✓ Branch 2 taken 23641 times.
25057 if( out.size() == 0 )
342 {
343
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1416 times.
1416 if( output.size() == 0 )
344 detail::throw_logic_error();
345 12408 break;
346 }
347
348 23641 auto rs = filter_->process(
349
1/2
✓ Branch 1 taken 23641 times.
✗ Branch 2 not taken.
23641 out, in, more_);
350
351
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 23640 times.
23641 if(rs.ec.failed())
352 {
353 1 is_done_ = true;
354 1 return rs.ec;
355 }
356
357
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 23544 times.
23640 if( rs.finished )
358 96 filter_done_ = true;
359
360
1/2
✓ Branch 1 taken 23640 times.
✗ Branch 2 not taken.
23640 consume(rs.in_bytes);
361
362
2/2
✓ Branch 0 taken 10992 times.
✓ Branch 1 taken 12648 times.
23640 if( rs.out_bytes == 0 )
363 10992 break;
364
365 12648 num_written += rs.out_bytes;
366 12648 output.commit(rs.out_bytes);
367 12648 }
368 }
369
370 // end:
371 12458 std::size_t n = 0;
372
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 12400 times.
12458 if( !is_header_done_ )
373 {
374
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
58 BOOST_ASSERT(hp_ == &prepped_[0]);
375 58 ++n;
376 }
377 else
378 12400 prepped_.reset(prepped_.capacity());
379
380
2/2
✓ Branch 0 taken 6216 times.
✓ Branch 1 taken 6242 times.
12458 if( !is_chunked_ )
381 {
382
2/2
✓ Branch 3 taken 12432 times.
✓ Branch 4 taken 6216 times.
18648 for(buffers::const_buffer const& b : output.data())
383 12432 prepped_[n++] = b;
384 }
385 else
386 {
387
2/2
✓ Branch 0 taken 6239 times.
✓ Branch 1 taken 3 times.
6242 if( has_avail_out )
388 {
389 6239 write_chunk_header(
390 6239 chunk_header_, num_written);
391 6239 prepped_[n++] = chunk_header_;
392
393
2/2
✓ Branch 3 taken 12478 times.
✓ Branch 4 taken 6239 times.
18717 for(buffers::const_buffer const& b : output.data())
394 12478 prepped_[n++] = b;
395
396 6239 prepped_[n++] = chunk_close_;
397 }
398
399
4/4
✓ Branch 0 taken 6220 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 6196 times.
✓ Branch 3 taken 24 times.
6242 if( (filter_ && filter_done_) ||
400
4/4
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 6196 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 17 times.
6218 (!filter_ && !more_) )
401 29 prepped_[n++] = last_chunk_;
402 }
403
404 auto cbs = const_buffers_type(
405 12458 prepped_.data(), prepped_.size());
406
407
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 12458 times.
12458 BOOST_ASSERT(buffers::buffer_size(cbs) > 0);
408 12458 return cbs;
409 }
410
411 void
412 14224 serializer::
413 consume(
414 std::size_t n)
415 {
416 // Precondition violation
417
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 14212 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 11 times.
14224 if( is_done_ && n != 0 )
418 1 detail::throw_logic_error();
419
420
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 14220 times.
14223 if( is_expect_continue_ )
421 {
422 // Cannot consume more than
423 // the header on 100-continue
424
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 if( n > hp_->size() )
425 1 detail::throw_invalid_argument();
426 }
427
428
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 14146 times.
14222 if( !is_header_done_ )
429 {
430 // consume header
431
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 65 times.
76 if( n < hp_->size() )
432 {
433 11 prepped_.consume(n);
434 11 return;
435 }
436 65 n -= hp_->size();
437 65 prepped_.consume(hp_->size());
438 65 is_header_done_ = true;
439 }
440
441 14211 prepped_.consume(n);
442
2/2
✓ Branch 0 taken 14197 times.
✓ Branch 1 taken 14 times.
14211 if( out_ )
443 {
444
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14197 times.
14197 BOOST_ASSERT(st_ != style::empty);
445 14197 out_->consume(n);
446 }
447 14211 auto is_empty = (buffers::buffer_size(prepped_) == 0);
448
449
6/6
✓ Branch 0 taken 1441 times.
✓ Branch 1 taken 12770 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1432 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 5 times.
14211 if( st_ == style::buffers && !filter_ && is_empty )
450 4 more_ = false;
451
452
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 14206 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 1 times.
14211 if( st_ == style::empty &&
453 4 is_empty &&
454
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 !is_expect_continue_ )
455 3 more_ = false;
456
457
2/2
✓ Branch 0 taken 12472 times.
✓ Branch 1 taken 1739 times.
14211 if( is_empty )
458
2/2
✓ Branch 0 taken 12408 times.
✓ Branch 1 taken 64 times.
12472 is_done_ = filter_ ? filter_done_ : !more_;
459 }
460
461 void
462 24 serializer::
463 use_deflate_encoding()
464 {
465 // can only apply one encoding
466
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
24 if(filter_)
467 detail::throw_logic_error();
468
469 24 is_compressed_ = true;
470
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 filter_ = &ws_.emplace<deflator_filter>(ctx_, ws_, false);
471 24 }
472
473 void
474 25 serializer::
475 use_gzip_encoding()
476 {
477 // can only apply one encoding
478
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 if( filter_ )
479 detail::throw_logic_error();
480
481 25 is_compressed_ = true;
482
1/2
✓ Branch 1 taken 25 times.
✗ Branch 2 not taken.
25 filter_ = &ws_.emplace<deflator_filter>(ctx_, ws_, true);
483 25 }
484
485 //------------------------------------------------
486
487 void
488 7 serializer::
489 copy(
490 buffers::const_buffer* dest,
491 buffers::const_buffer const* src,
492 std::size_t n) noexcept
493 {
494
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 while(n--)
495 7 *dest++ = *src++;
496 7 }
497
498 void
499 75 serializer::
500 start_init(
501 message_view_base const& m)
502 {
503 // VFALCO what do we do with
504 // metadata error code failures?
505 // m.ph_->md.maybe_throw();
506
507 75 auto const& md = m.metadata();
508
509 75 is_done_ = false;
510 75 is_header_done_ = false;
511 75 is_expect_continue_ = md.expect.is_100_continue;
512
513 // Transfer-Encoding
514 {
515 75 auto const& te = md.transfer_encoding;
516 75 is_chunked_ = te.is_chunked;
517 }
518
519
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 44 times.
75 if( is_chunked_)
520 {
521 31 auto* p = ws_.reserve_front(chunked_overhead_);
522 31 chunk_header_ =
523 31 buffers::mutable_buffer(p, chunk_header_len_);
524 31 chunk_close_ =
525 62 buffers::mutable_buffer(
526 31 p + chunk_header_len_, crlf_len_);
527 31 last_chunk_ =
528 62 buffers::mutable_buffer(
529 31 p + chunk_header_len_ + crlf_len_,
530 last_chunk_len_);
531
532 31 buffers::buffer_copy(
533 31 chunk_close_, buffers::const_buffer("\r\n", 2));
534 31 buffers::buffer_copy(
535 31 last_chunk_,
536 62 buffers::const_buffer("0\r\n\r\n", 5));
537 }
538 75 }
539
540 void
541 4 serializer::
542 start_empty(
543 message_view_base const& m)
544 {
545 4 start_init(m);
546
547 4 st_ = style::empty;
548 4 more_ = true;
549
550
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if(! is_chunked_)
551 {
552 3 prepped_ = make_array(
553 1); // header
554 }
555 else
556 {
557
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 prepped_ = make_array(
558 1 + // header
559 1); // final chunk
560
561 // Buffer is too small
562
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if(ws_.size() < 5)
563 detail::throw_length_error();
564
565 buffers::mutable_buffer dest(
566 1 ws_.data(), 5);
567 1 buffers::buffer_copy(
568 dest,
569 1 buffers::const_buffer(
570 "0\r\n\r\n", 5));
571 1 prepped_[1] = dest;
572 }
573
574 4 hp_ = &prepped_[0];
575 4 *hp_ = { m.ph_->cbuf, m.ph_->size };
576 4 }
577
578 void
579 24 serializer::
580 start_buffers(
581 message_view_base const& m)
582 {
583 24 st_ = style::buffers;
584 24 tmp1_ = {};
585
586
4/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 1 times.
24 if( !filter_ && !is_chunked_ )
587 {
588 6 prepped_ = make_array(
589 1 + // header
590 6 buf_.size()); // user input
591
592 6 hp_ = &prepped_[0];
593 6 *hp_ = { m.ph_->cbuf, m.ph_->size };
594
595 6 copy(&prepped_[1], buf_.data(), buf_.size());
596
597 6 more_ = (buffers::buffer_size(buf_) > 0);
598 6 return;
599 }
600
601
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
18 if( !filter_ && is_chunked_ )
602 {
603
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if( buffers::buffer_size(buf_) == 0 )
604 {
605 prepped_ = make_array(
606 1 + // header
607 1); // last chunk
608
609 hp_ = &prepped_[0];
610 *hp_ = { m.ph_->cbuf, m.ph_->size };
611 prepped_[1] = last_chunk_;
612 more_ = false;
613 return;
614 }
615
616 2 write_chunk_header(
617 1 chunk_header_, buffers::buffer_size(buf_));
618
619 1 prepped_ = make_array(
620 1 + // header
621 1 + // chunk header
622 1 buf_.size() + // user input
623 1 + // chunk close
624 1); // last chunk
625
626 1 hp_ = &prepped_[0];
627 1 *hp_ = { m.ph_->cbuf, m.ph_->size };
628 1 prepped_[1] = chunk_header_;
629 1 copy(&prepped_[2], buf_.data(), buf_.size());
630
631 1 prepped_[prepped_.size() - 2] = chunk_close_;
632 1 prepped_[prepped_.size() - 1] = last_chunk_;
633 1 more_ = true;
634 1 return;
635 }
636
637
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 if( is_chunked_ )
638 {
639 8 prepped_ = make_array(
640 1 + // header
641 1 + // chunk header
642 2 + // tmp
643 1 + // chunk close
644 1); // last chunk
645 }
646 else
647 9 prepped_ = make_array(
648 1 + // header
649 2); // tmp
650
651 17 hp_ = &prepped_[0];
652 17 *hp_ = { m.ph_->cbuf, m.ph_->size };
653 17 tmp0_ = { ws_.data(), ws_.size() };
654 17 out_ = &tmp0_;
655 17 in_ = out_;
656 17 more_ = true;
657 }
658
659 void
660 25 serializer::
661 start_source(
662 message_view_base const& m,
663 source* src)
664 {
665 25 st_ = style::source;
666 25 src_ = src;
667
668
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 15 times.
25 if( is_chunked_ )
669 {
670 10 prepped_ = make_array(
671 1 + // header
672 1 + // chunk header
673 2 + // tmp
674 1 + // chunk close
675 1); // last chunk
676 }
677 else
678 15 prepped_ = make_array(
679 1 + // header
680 2); // tmp
681
682
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 16 times.
25 if( !filter_ )
683 {
684 9 tmp0_ = { ws_.data(), ws_.size() };
685
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if( tmp0_.capacity() < 1 )
686 detail::throw_length_error();
687
688 9 in_ = &tmp0_;
689 9 out_ = &tmp0_;
690 }
691 else
692 {
693 16 auto n = ws_.size() / 2;
694 16 auto* p = ws_.reserve_front(n);
695 16 tmp1_ = buffers::circular_buffer(p, n);
696
697 16 tmp0_ = { ws_.data(), ws_.size() };
698
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if( tmp0_.capacity() < 1 )
699 detail::throw_length_error();
700
701 16 in_ = &tmp1_;
702 16 out_ = &tmp0_;
703 }
704
705 25 hp_ = &prepped_[0];
706 25 *hp_ = { m.ph_->cbuf, m.ph_->size };
707 25 more_ = true;
708 25 }
709
710 auto
711 22 serializer::
712 start_stream(
713 message_view_base const& m) ->
714 stream
715 {
716 22 start_init(m);
717
718 22 st_ = style::stream;
719
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 11 times.
22 if( is_chunked_ )
720 {
721 11 prepped_ = make_array(
722 1 + // header
723 1 + // chunk header
724 2 + // tmp
725 1 + // chunk close
726 1); // last chunk
727 }
728 else
729 11 prepped_ = make_array(
730 1 + // header
731 2); // tmp
732
733
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 16 times.
22 if( !filter_ )
734 {
735 6 tmp0_ = { ws_.data(), ws_.size() };
736
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if( tmp0_.capacity() < 1 )
737 detail::throw_length_error();
738
739 6 in_ = &tmp0_;
740 6 out_ = &tmp0_;
741 }
742 else
743 {
744 16 auto n = ws_.size() / 2;
745 16 auto* p = ws_.reserve_front(n);
746 16 tmp1_ = buffers::circular_buffer(p, n);
747
748 16 tmp0_ = { ws_.data(), ws_.size() };
749
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16 times.
16 if( tmp0_.capacity() < 1 )
750 detail::throw_length_error();
751
752 16 in_ = &tmp1_;
753 16 out_ = &tmp0_;
754 }
755
756 22 hp_ = &prepped_[0];
757 22 *hp_ = { m.ph_->cbuf, m.ph_->size };
758 22 more_ = true;
759 22 return stream{*this};
760 }
761
762 //------------------------------------------------
763
764 std::size_t
765 139 serializer::
766 stream::
767 capacity() const noexcept
768 {
769 139 return sr_->in_->capacity();
770 }
771
772 std::size_t
773 72 serializer::
774 stream::
775 size() const noexcept
776 {
777 72 return sr_->in_->size();
778 }
779
780 bool
781 63 serializer::
782 stream::
783 is_full() const noexcept
784 {
785 63 return capacity() == 0;
786 }
787
788 auto
789 5512 serializer::
790 stream::
791 prepare() const ->
792 buffers_type
793 {
794 5512 return sr_->in_->prepare(sr_->in_->capacity());
795 }
796
797 void
798 5512 serializer::
799 stream::
800 commit(std::size_t n) const
801 {
802 // the stream must make a non-zero amount of bytes
803 // available to the serializer
804
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5511 times.
5512 if( n == 0 )
805 1 detail::throw_logic_error();
806
807 5511 sr_->in_->commit(n);
808 5511 }
809
810 void
811 25 serializer::
812 stream::
813 close() const
814 {
815 // Precondition violation
816
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 21 times.
25 if(! sr_->more_ )
817 4 detail::throw_logic_error();
818 21 sr_->more_ = false;
819 21 }
820
821 //------------------------------------------------
822
823 } // http_proto
824 } // boost
825