GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/serializer.cpp
Date: 2024-05-23 18:56:45
Exec Total Coverage
Lines: 237 390 60.8%
Functions: 21 28 75.0%
Branches: 109 241 45.2%

Line Branch Exec Source
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2024 Christian Mazakas
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/http_proto
9 //
10
11 #include <boost/http_proto/serializer.hpp>
12 #include <boost/http_proto/message_view_base.hpp>
13 #include <boost/http_proto/filter.hpp>
14 #include <boost/http_proto/service/zlib_service.hpp>
15 #include <boost/http_proto/detail/except.hpp>
16 #include <boost/buffers/algorithm.hpp>
17 #include <boost/buffers/buffer_copy.hpp>
18 #include <boost/buffers/buffer_size.hpp>
19 #include <boost/core/ignore_unused.hpp>
20 #include <stddef.h>
21 #include <iostream>
22
23 namespace boost {
24 namespace http_proto {
25
26 //------------------------------------------------
27
28 void
29 consume_buffers(
30 buffers::const_buffer*& p,
31 std::size_t& n,
32 std::size_t bytes)
33 {
34 while(n > 0)
35 {
36 if(bytes < p->size())
37 {
38 *p += bytes;
39 return;
40 }
41 bytes -= p->size();
42 ++p;
43 --n;
44 }
45
46 // Precondition violation
47 if(bytes > 0)
48 detail::throw_invalid_argument();
49 }
50
51 template<class MutableBuffers>
52 void
53 54 write_chunk_header(
54 MutableBuffers const& dest0,
55 std::size_t size) noexcept
56 {
57 static constexpr char hexdig[] =
58 "0123456789ABCDEF";
59 char buf[18];
60 54 auto p = buf + 16;
61
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 27 times.
918 for(std::size_t i = 16; i--;)
62 {
63 864 *--p = hexdig[size & 0xf];
64 864 size >>= 4;
65 }
66 54 buf[16] = '\r';
67 54 buf[17] = '\n';
68 54 auto n = buffers::buffer_copy(
69 dest0,
70 108 buffers::const_buffer(
71 buf, sizeof(buf)));
72 ignore_unused(n);
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
54 BOOST_ASSERT(n == 18);
74
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
54 BOOST_ASSERT(
75 buffers::buffer_size(dest0) == n);
76 54 }
77
78 template<class DynamicBuffer>
79 void
80 19 write_chunk_close(DynamicBuffer& db)
81 {
82 19 db.commit(
83 buffers::buffer_copy(
84
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 db.prepare(2),
85 19 buffers::const_buffer("\r\n", 2)));
86 19 }
87
88 template<class DynamicBuffer>
89 void
90 3 write_last_chunk(DynamicBuffer& db)
91 {
92 3 db.commit(
93 buffers::buffer_copy(
94
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 db.prepare(5),
95 3 buffers::const_buffer("0\r\n\r\n", 5)));
96 3 }
97
98 //------------------------------------------------
99
100 20 serializer::
101 ~serializer()
102 {
103 20 }
104
105 9 serializer::
106 9 serializer()
107 9 : serializer(65536)
108 {
109 9 }
110
111 serializer::
112 serializer(
113 serializer&&) noexcept = default;
114
115 20 serializer::
116 serializer(
117 20 std::size_t buffer_size)
118 20 : ws_(buffer_size)
119 {
120 20 }
121
122 serializer::
123 serializer(
124 context& ctx,
125 std::size_t buffer_size)
126 : ws_(buffer_size)
127 , ctx_(&ctx)
128 {
129 if( ctx_->has_service<zlib::deflate_decoder_service>() )
130 {
131 filter_ws_.allocate(1024);
132 tmp1_ = { filter_ws_.data(), filter_ws_.size() };
133 }
134 }
135
136 void
137 serializer::
138 reset() noexcept
139 {
140 }
141
142 //------------------------------------------------
143
144 auto
145 67 serializer::
146 prepare() ->
147 system::result<
148 const_buffers_type>
149 {
150 // Precondition violation
151
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 66 times.
67 if(is_done_)
152 1 detail::throw_logic_error();
153
154 // Expect: 100-continue
155
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 62 times.
66 if(is_expect_continue_)
156 {
157
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if(out_.data() == hp_)
158 2 return const_buffers_type(hp_, 1);
159 2 is_expect_continue_ = false;
160 2 BOOST_HTTP_PROTO_RETURN_EC(
161 error::expect_100_continue);
162 }
163
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if( is_compressed_ )
165 {
166 auto on_end = [&]
167 {
168 std::size_t n = 0;
169 if(out_.data() == hp_)
170 ++n;
171
172 for(buffers::const_buffer const& b : tmp0_.data())
173 out_[n++] = b;
174
175 auto cbs = const_buffers_type(
176 out_.data(), out_.size());
177
178 BOOST_ASSERT(buffers::buffer_size(cbs) > 0);
179
180 return cbs;
181 };
182
183 if(! zlib_filter_ )
184 {
185 auto& svc =
186 ctx_->get_service<
187 zlib::deflate_decoder_service>();
188
189 zlib_filter_ = &svc.make_filter(filter_ws_);
190 tmp1_ =
191 { filter_ws_.data(), filter_ws_.size() };
192 }
193
194 auto& zbuf = tmp1_;
195
196 if( st_ == style::source )
197 {
198 auto results = src_->read(
199 zbuf.prepare(zbuf.capacity()));
200 more_ = !results.finished;
201 zbuf.commit(results.bytes);
202 }
203
204 buffers::mutable_buffer chunk_header;
205 if( is_chunked_ )
206 {
207 auto mbs = tmp0_.prepare(detail::chunk_header_len_);
208 auto buf = *mbs.begin();
209 if( buf.size() == 0 )
210 {
211 auto p = mbs.begin();
212 ++p;
213 buf = *p;
214 }
215
216 if( buf.size() < detail::chunk_header_len_ )
217 detail::throw_length_error();
218
219 std::memset(
220 buf.data(), 0x00, detail::chunk_header_len_);
221 tmp0_.commit(detail::chunk_header_len_);
222
223 chunk_header = buf;
224 }
225
226 auto get_output = [&]() -> buffers::mutable_buffer
227 {
228 auto mbs = tmp0_.prepare(tmp0_.capacity());
229 auto buf = *mbs.begin();
230 if( buf.size() == 0 )
231 {
232 auto p = mbs.begin();
233 ++p;
234 buf = *p;
235 }
236
237 if( is_chunked_ )
238 {
239 if( buf.size() <
240 detail::crlf_len_ + detail::last_chunk_len_ + 1 )
241 return {};
242
243 auto n =
244 buf.size() -
245 detail::crlf_len_ -
246 detail::last_chunk_len_;
247
248 buf = { buf.data(), n };
249 }
250
251 // BOOST_ASSERT(buf.size() > 0);
252 return buf;
253 };
254
255 auto get_input = [&]() -> buffers::const_buffer
256 {
257 if( st_ == style::buffers )
258 {
259 if( buffers::buffer_size(buf_) == 0 )
260 return {};
261
262 auto buf = *(buf_.data());
263 BOOST_ASSERT(buf.size() > 0);
264 return buf;
265 }
266 else
267 {
268 if( zbuf.size() == 0 )
269 return {};
270
271 auto cbs = zbuf.data();
272 auto buf = *cbs.begin();
273 if( buf.size() == 0 )
274 {
275 auto p = cbs.begin();
276 ++p;
277 buf = *p;
278 }
279 BOOST_ASSERT(buf.size() > 0);
280 return buf;
281 }
282 };
283
284 std::size_t num_written = 0;
285 while( true )
286 {
287 auto in = get_input();
288 auto out = get_output();
289 if( out.size() < 6 + 1 )
290 {
291 BOOST_ASSERT(tmp0_.size() > 0);
292 break;
293 }
294
295 auto results = zlib_filter_->on_process(
296 out, in, more_);
297
298 if( results.finished )
299 filter_done_ = true;
300
301 if( st_ == style::buffers )
302 {
303 buf_.consume(results.in_bytes);
304 if( buffers::buffer_size(buf_) == 0 )
305 more_ = false;
306 }
307 else
308 zbuf.consume(results.in_bytes);
309
310 if( results.out_bytes == 0 )
311 break;
312
313 BOOST_ASSERT(results.out_bytes > 0);
314
315 num_written += results.out_bytes;
316 tmp0_.commit(results.out_bytes);
317 }
318
319 BOOST_ASSERT(tmp0_.size() > 0);
320 if( is_chunked_ )
321 {
322 write_chunk_header(
323 chunk_header, num_written);
324
325 buffers::buffer_copy(
326 tmp0_.prepare(2),
327 buffers::const_buffer("\r\n", 2));
328 tmp0_.commit(2);
329
330 if( filter_done_ )
331 {
332 buffers::buffer_copy(
333 tmp0_.prepare(5),
334 buffers::const_buffer(
335 "0\r\n\r\n", 5));
336 tmp0_.commit(5);
337 }
338 }
339 return on_end();
340 }
341
342
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 59 times.
62 if(st_ == style::empty)
343 {
344 9 return const_buffers_type(
345 3 out_.data(),
346 3 out_.size());
347 }
348
349
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 56 times.
59 if(st_ == style::buffers)
350 {
351 9 return const_buffers_type(
352 3 out_.data(),
353 3 out_.size());
354 }
355
356
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 34 times.
56 if(st_ == style::source)
357 {
358
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 5 times.
22 if(more_)
359 {
360
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 8 times.
17 if(! is_chunked_)
361 {
362
1/2
✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
9 auto rv = src_->read(
363
1/2
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
9 tmp0_.prepare(tmp0_.capacity()));
364 9 tmp0_.commit(rv.bytes);
365
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 if(rv.ec.failed())
366 return rv.ec;
367 9 more_ = ! rv.finished;
368 }
369 else
370 {
371
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 if(tmp0_.capacity() > detail::chunked_overhead_)
372 {
373
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto dest = tmp0_.prepare(
374 8 tmp0_.capacity() -
375 2 - // CRLF
376 5); // final chunk
377
378
1/2
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
8 auto rv = src_->read(
379 8 buffers::sans_prefix(dest, 18));
380
381
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if(rv.ec.failed())
382 return rv.ec;
383
384
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 if(rv.bytes != 0)
385 {
386 7 write_chunk_header(
387 7 buffers::prefix(dest, 18), rv.bytes);
388 7 tmp0_.commit(rv.bytes + 18);
389 // terminate chunk
390 7 tmp0_.commit(
391 buffers::buffer_copy(
392
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 tmp0_.prepare(2),
393 14 buffers::const_buffer(
394 "\r\n", 2)));
395 }
396
397
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if(rv.finished)
398 {
399 2 tmp0_.commit(
400 buffers::buffer_copy(
401
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 tmp0_.prepare(5),
402 2 buffers::const_buffer(
403 "0\r\n\r\n", 5)));
404 2 more_ = false;
405 }
406 }
407 }
408 }
409
410 22 std::size_t n = 0;
411
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 17 times.
22 if(out_.data() == hp_)
412 5 ++n;
413
2/2
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 22 times.
66 for(buffers::const_buffer const& b : tmp0_.data())
414 44 out_[n++] = b;
415
416 66 return const_buffers_type(
417 22 out_.data(),
418 22 out_.size());
419 }
420
421
1/2
✓ Branch 0 taken 34 times.
✗ Branch 1 not taken.
34 if(st_ == style::stream)
422 {
423 34 std::size_t n = 0;
424
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 28 times.
34 if(out_.data() == hp_)
425 6 ++n;
426
6/6
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 33 times.
34 if(tmp0_.size() == 0 && more_)
427 {
428 1 BOOST_HTTP_PROTO_RETURN_EC(
429 error::need_data);
430 }
431
2/2
✓ Branch 3 taken 66 times.
✓ Branch 4 taken 33 times.
99 for(buffers::const_buffer const& b : tmp0_.data())
432 66 out_[n++] = b;
433
434 99 return const_buffers_type(
435 33 out_.data(),
436 33 out_.size());
437 }
438
439 // should never get here
440 detail::throw_logic_error();
441 }
442
443 void
444 1745 serializer::
445 consume(
446 std::size_t n)
447 {
448 // Precondition violation
449
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1744 times.
1745 if(is_done_)
450 1 detail::throw_logic_error();
451
452
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1741 times.
1744 if(is_expect_continue_)
453 {
454 // Cannot consume more than
455 // the header on 100-continue
456
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
3 if(n > hp_->size())
457 1 detail::throw_invalid_argument();
458
459 2 out_.consume(n);
460 2 return;
461 }
462
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 1715 times.
1741 else if(out_.data() == hp_)
463 {
464 // consume header
465
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 15 times.
26 if(n < hp_->size())
466 {
467 11 out_.consume(n);
468 11 return;
469 }
470 15 n -= hp_->size();
471 15 out_.consume(hp_->size());
472 }
473
474
3/3
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1724 times.
1730 switch(st_)
475 {
476 3 default:
477 case style::empty:
478 3 out_.consume(n);
479
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if(out_.empty())
480 3 is_done_ = true;
481 3 return;
482
483 3 case style::buffers:
484
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if( is_compressed_ )
485 {
486 tmp0_.consume(n);
487 if( tmp0_.size() == 0 &&
488 filter_done_ )
489 is_done_ = true;
490 return;
491 }
492 3 out_.consume(n);
493
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 if(out_.empty())
494 3 is_done_ = true;
495 3 return;
496
497 1724 case style::source:
498 case style::stream:
499 1724 tmp0_.consume(n);
500
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 1685 times.
1724 if( !is_compressed_ &&
501
3/4
✓ Branch 0 taken 1724 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 11 times.
✓ Branch 4 taken 1713 times.
3448 tmp0_.size() == 0 &&
502
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 28 times.
39 ! more_)
503 11 is_done_ = true;
504
505 if( is_compressed_ &&
506
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1724 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1724 times.
1724 tmp0_.size() == 0 &&
507 filter_done_ )
508 is_done_ = true;
509 1724 return;
510 }
511 }
512
513 //------------------------------------------------
514
515 void
516 7 serializer::
517 copy(
518 buffers::const_buffer* dest,
519 buffers::const_buffer const* src,
520 std::size_t n) noexcept
521 {
522
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 while(n--)
523 7 *dest++ = *src++;
524 7 }
525
526 void
527 26 serializer::
528 start_init(
529 message_view_base const& m)
530 {
531 26 ws_.clear();
532
533 // VFALCO what do we do with
534 // metadata error code failures?
535 // m.ph_->md.maybe_throw();
536
537 26 is_done_ = false;
538
539 26 is_expect_continue_ =
540 26 m.ph_->md.expect.is_100_continue;
541
542 // Transfer-Encoding
543 {
544 26 auto const& te =
545 26 m.ph_->md.transfer_encoding;
546 26 is_chunked_ = te.is_chunked;
547 }
548
549
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
26 if( m.compressed() )
550 {
551 is_compressed_ = true;
552 auto& svc =
553 ctx_->get_service<
554 zlib::deflate_decoder_service>();
555
556 BOOST_ASSERT(!zlib_filter_);
557 zlib_filter_ = &svc.make_filter(filter_ws_);
558 }
559 26 }
560
561 void
562 4 serializer::
563 start_empty(
564 message_view_base const& m)
565 {
566 4 start_init(m);
567
568 4 st_ = style::empty;
569
570
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if(! is_chunked_)
571 {
572 3 out_ = make_array(
573 1); // header
574 }
575 else
576 {
577
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 out_ = make_array(
578 1 + // header
579 1); // final chunk
580
581 // Buffer is too small
582
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if(ws_.size() < 5)
583 detail::throw_length_error();
584
585 buffers::mutable_buffer dest(
586 1 ws_.data(), 5);
587 1 buffers::buffer_copy(
588 dest,
589 1 buffers::const_buffer(
590 "0\r\n\r\n", 5));
591 1 out_[1] = dest;
592 }
593
594 4 hp_ = &out_[0];
595 4 *hp_ = { m.ph_->cbuf, m.ph_->size };
596 4 }
597
598 void
599 7 serializer::
600 start_buffers(
601 message_view_base const& m)
602 {
603 7 st_ = style::buffers;
604
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if( is_compressed_ )
605 {
606 out_ = make_array(
607 1 + // header
608 2); // tmp
609
610 hp_ = &out_[0];
611 *hp_ = { m.ph_->cbuf, m.ph_->size };
612 tmp0_ = { ws_.data(), ws_.size() };
613 more_ = true;
614 return;
615 }
616
617
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 1 times.
7 if(! is_chunked_)
618 {
619 //if(! cod_)
620 {
621 6 out_ = make_array(
622 1 + // header
623 6 buf_.size()); // body
624 12 copy(&out_[1],
625 6 buf_.data(), buf_.size());
626 }
627 #if 0
628 else
629 {
630 out_ = make_array(
631 1 + // header
632 2); // tmp1
633 }
634 #endif
635 }
636 else
637 {
638 //if(! cod_)
639 {
640
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 out_ = make_array(
641 1 + // header
642 1 + // chunk size
643 1 buf_.size() + // body
644 1); // final chunk
645 2 copy(&out_[2],
646 1 buf_.data(), buf_.size());
647
648 // Buffer is too small
649
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if(ws_.size() < 18 + 7)
650 detail::throw_length_error();
651 1 buffers::mutable_buffer s1(ws_.data(), 18);
652 1 buffers::mutable_buffer s2(ws_.data(), 18 + 7);
653 1 s2 += 18; // VFALCO HACK
654 1 write_chunk_header(
655 s1,
656 1 buffers::buffer_size(buf_));
657 1 buffers::buffer_copy(s2, buffers::const_buffer(
658 "\r\n"
659 "0\r\n"
660 "\r\n", 7));
661 1 out_[1] = s1;
662 1 out_[out_.size() - 1] = s2;
663 }
664 #if 0
665 else
666 {
667 out_ = make_array(
668 1 + // header
669 2); // tmp1
670 }
671 #endif
672 }
673
674 7 hp_ = &out_[0];
675 7 *hp_ = { m.ph_->cbuf, m.ph_->size };
676 }
677
678 void
679 8 serializer::
680 start_source(
681 message_view_base const& m,
682 source* src)
683 {
684 8 st_ = style::source;
685 8 src_ = src;
686 8 out_ = make_array(
687 1 + // header
688 2); // tmp
689 //if(! cod_)
690 {
691 8 tmp0_ = { ws_.data(), ws_.size() };
692
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if(tmp0_.capacity() <
693 18 + // chunk size
694 1 + // body (1 byte)
695 2 + // CRLF
696 5) // final chunk
697 detail::throw_length_error();
698 }
699 #if 0
700 else
701 {
702 buffers::buffered_base::allocator a(
703 ws_.data(), ws_.size()/3, false);
704 src->init(a);
705 ws_.reserve(a.size_used());
706
707 auto const n = ws_.size() / 2;
708
709 tmp0_ = { ws_.data(), ws_.size() / 2 };
710 ws_.reserve(n);
711
712 // Buffer is too small
713 if(ws_.size() < 1)
714 detail::throw_length_error();
715
716 tmp1_ = { ws_.data(), ws_.size() };
717 }
718 #endif
719
720 8 hp_ = &out_[0];
721 8 *hp_ = { m.ph_->cbuf, m.ph_->size };
722 8 more_ = true;
723 8 }
724
725 auto
726 7 serializer::
727 start_stream(
728 message_view_base const& m) ->
729 stream
730 {
731 7 start_init(m);
732
733 7 st_ = style::stream;
734 7 out_ = make_array(
735 1 + // header
736 2); // tmp
737 //if(! cod_)
738 {
739 7 tmp0_ = { ws_.data(), ws_.size() };
740
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if(tmp0_.capacity() <
741 18 + // chunk size
742 1 + // body (1 byte)
743 2 + // CRLF
744 5) // final chunk
745 detail::throw_length_error();
746 }
747 #if 0
748 else
749 {
750 auto const n = ws_.size() / 2;
751 tmp0_ = { ws_.data(), n };
752 ws_.reserve(n);
753
754 // Buffer is too small
755 if(ws_.size() < 1)
756 detail::throw_length_error();
757
758 tmp1_ = { ws_.data(), ws_.size() };
759 }
760 #endif
761
762 7 hp_ = &out_[0];
763 7 *hp_ = { m.ph_->cbuf, m.ph_->size };
764
765 7 more_ = true;
766
767 7 return stream{*this};
768 }
769
770 //------------------------------------------------
771
772 std::size_t
773 140 serializer::
774 stream::
775 capacity() const noexcept
776 {
777 140 return sr_->tmp0_.capacity();
778 }
779
780 std::size_t
781 132 serializer::
782 stream::
783 size() const noexcept
784 {
785 132 return sr_->tmp0_.size();
786 }
787
788 bool
789 66 serializer::
790 stream::
791 is_full() const noexcept
792 {
793
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 30 times.
66 if( sr_->is_chunked_ )
794 36 return capacity() < detail::chunked_overhead_ + 1;
795
796 30 return capacity() == 0;
797 }
798
799 auto
800 36 serializer::
801 stream::
802 prepare() const ->
803 buffers_type
804 {
805
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if( sr_->is_compressed_ )
806 return sr_->tmp1_.prepare(
807 sr_->tmp1_.capacity());
808
809 36 auto n = sr_->tmp0_.capacity();
810
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 15 times.
36 if( sr_->is_chunked_ )
811 {
812 // for chunked encoding, we want to unconditionally
813 // reserve space for the complete chunk and the
814 // last-chunk
815 // this enables users to call:
816 //
817 // stream.commit(n); stream.close();
818 //
819 // without needing to worry about draining the
820 // serializer via `consume()` calls
821
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 20 times.
21 if( n < detail::chunked_overhead_ + 1 )
822 1 detail::throw_length_error();
823
824 20 n -= detail::chunked_overhead_;
825 20 return buffers::sans_prefix(
826 40 sr_->tmp0_.prepare(
827 detail::chunk_header_len_ + n),
828 20 detail::chunk_header_len_);
829 }
830
831 15 return sr_->tmp0_.prepare(n);
832 }
833
834 void
835 35 serializer::
836 stream::
837 commit(std::size_t n) const
838 {
839
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 if( sr_->is_compressed_ )
840 {
841 sr_->tmp1_.commit(n);
842 return;
843 }
844
845
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 20 times.
35 if(! sr_->is_chunked_ )
846 {
847 15 sr_->tmp0_.commit(n);
848 }
849 else
850 {
851 // Zero sized chunks are not valid. Call close()
852 // if the intent is to signal the end of the body.
853
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 19 times.
20 if( n == 0 )
854 1 detail::throw_logic_error();
855
856 // TODO: this needs to be relocated
857 //
858 19 auto m = n + detail::chunk_header_len_;
859
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 auto dest = sr_->tmp0_.prepare(m);
860 19 write_chunk_header(
861 19 buffers::prefix(
862 dest, detail::chunk_header_len_),
863 n);
864 19 sr_->tmp0_.commit(m);
865
1/2
✓ Branch 1 taken 19 times.
✗ Branch 2 not taken.
19 write_chunk_close(sr_->tmp0_);
866 }
867 }
868
869 void
870 9 serializer::
871 stream::
872 close() const
873 {
874 // Precondition violation
875
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 if(! sr_->more_ )
876 4 detail::throw_logic_error();
877
878
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
5 if( sr_->is_chunked_ && !sr_->is_compressed_ )
879 3 write_last_chunk(sr_->tmp0_);
880
881 5 sr_->more_ = false;
882 5 }
883
884 //------------------------------------------------
885
886 } // http_proto
887 } // boost
888