GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/impl/serializer.ipp
Date: 2023-01-15 07:18:31
Exec Total Coverage
Lines: 208 279 74.6%
Functions: 15 26 57.7%
Branches: 81 156 51.9%

Line Branch Exec Source
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/CPPAlliance/http_proto
8 //
9
10 #ifndef BOOST_HTTP_PROTO_IMPL_SERIALIZER_IPP
11 #define BOOST_HTTP_PROTO_IMPL_SERIALIZER_IPP
12
13 #include <boost/http_proto/serializer.hpp>
14 #include <boost/http_proto/detail/except.hpp>
15 #include <boost/core/ignore_unused.hpp>
16 #include <stddef.h>
17
18 namespace boost {
19 namespace http_proto {
20
21 //------------------------------------------------
22
23 void
24 13 consume_buffers(
25 const_buffer*& p,
26 std::size_t& n,
27 std::size_t bytes)
28 {
29
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 6 times.
13 while(n > 0)
30 {
31
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7 times.
7 if(bytes < p->size())
32 {
33 *p += bytes;
34 return;
35 }
36 7 bytes -= p->size();
37 7 ++p;
38 7 --n;
39 }
40
41 // Precondition violation
42
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(bytes > 0)
43 detail::throw_invalid_argument();
44 }
45
46 template<class MutableBuffers>
47 void
48 3 write_chunk_header(
49 MutableBuffers const& dest0,
50 std::size_t size) noexcept
51 {
52 static constexpr char hexdig[] =
53 "0123456789ABCDEF";
54 char buf[18];
55 3 auto p = buf + 16;
56
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 3 times.
51 for(std::size_t i = 16; i--;)
57 {
58 48 *--p = hexdig[size & 0xf];
59 48 size >>= 4;
60 }
61 3 buf[16] = '\r';
62 3 buf[17] = '\n';
63 3 auto n = buffer_copy(
64 (make_buffers)(dest0),
65 const_buffer(buf, sizeof(buf)));
66 ignore_unused(n);
67
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 BOOST_ASSERT(n == 18);
68
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 BOOST_ASSERT(
69 buffer_size(dest0) == n);
70 3 }
71
72 //------------------------------------------------
73
74 class serializer::
75 reserve
76 : public source::reserve_fn
77 {
78 serializer& sr_;
79 std::size_t limit_;
80
81 public:
82 6 reserve(
83 serializer& sr,
84 std::size_t limit) noexcept
85 6 : sr_(sr)
86 6 , limit_(limit)
87 {
88 6 }
89
90 void*
91 operator()(
92 std::size_t n) const override
93 {
94 // You can only call reserve() once!
95 if(! sr_.is_reserving_ )
96 detail::throw_logic_error();
97
98 // Requested n exceeds the limit
99 if(n > limit_)
100 detail::throw_length_error();
101
102 sr_.is_reserving_ = false;
103 return sr_.ws_.reserve(n);
104 }
105 };
106
107 //------------------------------------------------
108
109 15 serializer::
110 15 ~serializer()
111 {
112 15 }
113
114 10 serializer::
115 10 serializer()
116 10 : serializer(65536)
117 {
118 10 }
119
120 15 serializer::
121 serializer(
122 15 std::size_t buffer_size)
123 15 : ws_(buffer_size)
124 {
125 15 }
126
127 //------------------------------------------------
128
129 auto
130 14 serializer::
131 prepare() ->
132 result<output>
133 {
134 // Precondition violation
135
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if(is_done_)
136 detail::throw_logic_error();
137
138 // Expect: 100-continue
139
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14 if(is_expect_continue_)
140 {
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(hp_ != nullptr);
142
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if(hp_->size() > 0)
143 2 return output(hp_, 1);
144 2 is_expect_continue_ = false;
145 2 hp_ = nullptr;
146 2 BOOST_HTTP_PROTO_RETURN_EC(
147 error::expect_100_continue);
148 }
149
150
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 7 times.
10 if(st_ == style::empty)
151 {
152 3 return output(pp_, pn_);
153 }
154
155
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 if(st_ == style::buffers)
156 {
157 3 return output(pp_, pn_);
158 }
159
160
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if(st_ == style::source)
161 {
162
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if(! is_chunked_)
163 {
164 3 auto rv = src_->read(
165 dat1_.prepare(
166 3 dat1_.capacity() -
167
2/4
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
3 dat1_.size()));
168
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 dat1_.commit(rv.bytes);
169
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
3 if(rv.ec.failed())
170 return rv.ec;
171 3 more_ = rv.more;
172 }
173 else
174 {
175 1 if((dat1_.capacity() -
176
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 dat1_.size()) >
177 chunked_overhead_)
178 {
179
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 auto dest = dat1_.prepare(18);
180 1 write_chunk_header(dest, 0);
181
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 dat1_.commit(18);
182 1 auto rv = src_->read(
183 dat1_.prepare(
184 1 dat1_.capacity() -
185 2 - // CRLF
186 1 5 - // final chunk
187
2/4
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 times.
✗ Branch 7 not taken.
1 dat1_.size()));
188
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 dat1_.commit(rv.bytes);
189
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(rv.bytes == 0)
190 dat1_.uncommit(18); // undo
191
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if(rv.ec.failed())
192 return rv.ec;
193
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(rv.bytes > 0)
194 {
195 // rewrite with correct size
196 1 write_chunk_header(
197 dest, rv.bytes);
198 // terminate chunk
199
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 dat1_.commit(buffer_copy(
200
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 dat1_.prepare(2),
201 2 const_buffer(
202 "\r\n", 2)));
203 }
204
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(! rv.more)
205 {
206
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 dat1_.commit(buffer_copy(
207
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 dat1_.prepare(5),
208 2 const_buffer(
209 "0\r\n\r\n", 5)));
210 }
211 1 more_ = rv.more;
212 }
213 }
214
215 4 std::size_t n = 0;
216
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 if(hp_ != nullptr)
217 3 ++n;
218
2/2
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 4 times.
8 for(const_buffer b : dat1_.data())
219 4 pp_[n++] = b;
220 4 return output(pp_, n);
221 }
222
223 if(st_ == style::stream)
224 {
225 std::size_t n = 0;
226 if(hp_ != nullptr)
227 ++n;
228 if(dat1_.empty() && more_)
229 {
230 BOOST_HTTP_PROTO_RETURN_EC(
231 error::need_data);
232 }
233 for(const_buffer b : dat1_.data())
234 pp_[n++] = b;
235 return output(pp_, n);
236 }
237
238 // should never get here
239 detail::throw_logic_error();
240 }
241
242 void
243 12 serializer::
244 consume(
245 std::size_t n)
246 {
247 // Precondition violation
248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if(is_done_)
249 detail::throw_logic_error();
250
251
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
12 if(is_expect_continue_)
252 {
253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 BOOST_ASSERT(hp_ != nullptr);
254
255 // Precondition violation
256
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(n > hp_->size())
257 detail::throw_invalid_argument();
258
259 // consume header
260
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(n < hp_->size())
261 {
262 *hp_ += n;
263 return;
264 }
265 2 *hp_ = {};
266 2 return;
267 }
268
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 2 times.
10 else if(hp_ != nullptr)
269 {
270 // consume header
271
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if(n < hp_->size())
272 {
273 *hp_ += n;
274 return;
275 }
276 8 n -= hp_->size();
277 8 hp_ = nullptr;
278 8 ++pp_;
279 8 --pn_;
280 }
281
282
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 switch(st_)
283 {
284 6 default:
285 case style::empty:
286 case style::buffers:
287 6 consume_buffers(
288 6 pp_, pn_, n);
289
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(pn_ == 0)
290 6 is_done_ = true;
291 6 return;
292
293 4 case style::source:
294 case style::stream:
295 4 dat1_.consume(n);
296
2/4
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
8 if( dat1_.empty() &&
297
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 ! more_)
298 4 is_done_ = true;
299 4 return;
300 }
301 }
302
303 //------------------------------------------------
304
305 void
306 serializer::
307 apply_param(
308 brotli_decoder_t const&)
309 {
310 }
311
312 void
313 serializer::
314 apply_param(
315 brotli_encoder_t const&)
316 {
317 }
318
319 void
320 serializer::
321 apply_param(
322 deflate_decoder_t const&)
323 {
324 }
325
326 void
327 serializer::
328 apply_param(
329 deflate_encoder_t const&)
330 {
331 }
332
333 void
334 2 serializer::
335 apply_param(
336 gzip_decoder_t const&)
337 {
338 2 }
339
340 void
341 2 serializer::
342 apply_param(
343 gzip_encoder_t const&)
344 {
345 2 }
346
347 //------------------------------------------------
348
349 void
350 6 serializer::
351 do_reserve(
352 source& src,
353 std::size_t limit)
354 {
355 struct cleanup
356 {
357 bool& is_reserving;
358
359 6 ~cleanup()
360 6 {
361 6 is_reserving = false;
362 6 }
363 };
364
365
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 BOOST_ASSERT(! is_reserving_);
366 6 cleanup c{is_reserving_};
367 6 is_reserving_ = true;
368 12 reserve fn(*this, limit);
369
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 src.maybe_reserve(limit, fn);
370 6 }
371
372 void
373 4 serializer::
374 reset_empty_impl(
375 message_view_base const& m)
376 {
377 // Precondition violation
378
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if(ws_.size() < chunked_overhead_)
379 detail::throw_length_error();
380
381 4 ws_.clear();
382
383 4 is_done_ = false;
384 // VFALCO what about the error codes?
385 // m.ph_->md.maybe_throw();
386 4 is_chunked_ =
387 4 m.ph_->md.transfer_encoding.is_chunked;
388 4 is_expect_continue_ =
389 4 m.ph_->md.expect.is_100_continue;
390 //---
391 4 st_ = style::empty;
392
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 pp_ = ws_.push_array(
393 4 2, const_buffer{});
394 4 pn_ = 1;
395 4 hp_ = pp_;
396 8 *hp_ = {
397 4 m.ph_->cbuf,
398 4 m.ph_->size };
399
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if(is_chunked_)
400 {
401 detail::circular_buffer tmp(
402 1 ws_.data(), ws_.size());
403
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 tmp.commit(buffer_copy(
404
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 tmp.prepare(5),
405 1 const_buffer(
406 "0\r\n\r\n", 5)));
407 1 auto data = tmp.data();
408
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 BOOST_ASSERT(
409 data[1].size() == 0);
410 1 pp_[pn_++] = data[0];
411 }
412 4 }
413
414 void
415 11 serializer::
416 reset_buffers_impl(
417 message_view_base const& m,
418 const_buffer* pp,
419 std::size_t pn)
420 {
421 // Precondition violation
422
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
11 if(ws_.size() < chunked_overhead_)
423 detail::throw_length_error();
424
425 11 is_done_ = false;
426 // VFALCO what about the error codes?
427 // m.ph_->md.maybe_throw();
428 11 is_chunked_ =
429 11 m.ph_->md.transfer_encoding.is_chunked;
430 11 is_expect_continue_ =
431 11 m.ph_->md.expect.is_100_continue;
432 //---
433 11 st_ = style::buffers;
434
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
11 if(! is_chunked_)
435 {
436 10 pn_ = pn - 2;
437 10 pp_ = pp + 1;
438 10 hp_ = pp_;
439 10 *hp_ = {
440 10 m.ph_->cbuf,
441 10 m.ph_->size };
442 }
443 else
444 {
445 1 std::size_t n = 0;
446 1 for(std::size_t i = 2;
447
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 i < pn - 1; ++i)
448 1 n += pp[i].size();
449
450 1 pn_ = pn;
451 1 pp_ = pp;
452 1 hp_ = pp_;
453 2 *hp_ = {
454 1 m.ph_->cbuf,
455 1 m.ph_->size };
456
457 detail::circular_buffer tmp{
458 1 ws_.data(), ws_.size() };
459 {
460
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 auto dest = tmp.prepare(18);
461
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 BOOST_ASSERT(dest[0].size() == 18);
462
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 BOOST_ASSERT(dest[1].size() == 0);
463 1 write_chunk_header(dest, n);
464
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 tmp.commit(18);
465 1 pp_[1] = dest[0];
466 const_buffer fc(
467 "\r\n"
468 "0\r\n"
469 1 "\r\n", 7);
470
1/2
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 dest = tmp.prepare(fc.size());
471 1 buffer_copy(dest, fc);
472
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 BOOST_ASSERT(dest[0].size() == 7);
473
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 BOOST_ASSERT(dest[1].size() == 0);
474 1 pp_[pn_ - 1] = dest[0];
475 }
476 }
477 11 }
478
479 void
480 6 serializer::
481 reset_source_impl(
482 message_view_base const& m,
483 source* src)
484 {
485 // Precondition violation
486
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if(ws_.size() <
487 chunked_overhead_ +
488 128) // reasonable lower limit
489 detail::throw_length_error();
490
491 6 is_done_ = false;
492 // VFALCO what about the error codes?
493 // m.ph_->md.maybe_throw();
494 6 is_chunked_ =
495 6 m.ph_->md.transfer_encoding.is_chunked;
496 6 is_expect_continue_ =
497 6 m.ph_->md.expect.is_100_continue;
498 //---
499 6 st_ = style::source;
500 6 src_ = src;
501 6 do_reserve(
502 *src,
503 6 ws_.size() / 2); // VFALCO can this underflow?
504 6 pn_ =
505 1 + // header
506 2; // dat1
507
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 pp_ = ws_.push_array(
508 6 pn_, const_buffer{});
509 6 hp_ = pp_;
510 12 *hp_ = {
511 6 m.ph_->cbuf,
512 6 m.ph_->size };
513 6 dat1_ = {
514 ws_.data(),
515 ws_.size() };
516 6 }
517
518 //------------------------------------------------
519
520 std::size_t
521 serializer::
522 stream::
523 capacity() const
524 {
525 auto const n =
526 chunked_overhead_ +
527 2 + // CRLF
528 5; // final chunk
529 return sr_->dat1_.capacity() -
530 n - 0;
531 }
532
533 std::size_t
534 serializer::
535 stream::
536 size() const
537 {
538 return sr_->dat1_.size();
539 }
540
541 auto
542 serializer::
543 stream::
544 prepare(
545 std::size_t n) const ->
546 buffers_type
547 {
548 return sr_->dat1_.prepare(n);
549 }
550
551 void
552 serializer::
553 stream::
554 commit(std::size_t n) const
555 {
556 sr_->dat1_.commit(n);
557 }
558
559 void
560 serializer::
561 stream::
562 close() const
563 {
564 // Precondition violation
565 if(! sr_->more_)
566 detail::throw_logic_error();
567 sr_->more_ = false;
568 }
569
570 void
571 serializer::
572 reset_stream_impl(
573 message_view_base const& m,
574 source& src)
575 {
576 // Precondition violation
577 if(ws_.size() <
578 chunked_overhead_ +
579 128) // reasonable lower limit
580 detail::throw_length_error();
581
582 is_done_ = false;
583 // VFALCO what about the error codes?
584 // m.ph_->md.maybe_throw();
585 is_chunked_ =
586 m.ph_->md.transfer_encoding.is_chunked;
587 is_expect_continue_ =
588 m.ph_->md.expect.is_100_continue;
589 //---
590 st_ = style::stream;
591 do_reserve(
592 src,
593 ws_.size() / 2); // VFALCO can this underflow?
594 pn_ =
595 1 + // header
596 2; // dat1
597 pp_ = ws_.push_array(
598 pn_, const_buffer{});
599 hp_ = pp_;
600 *hp_ = {
601 m.ph_->cbuf,
602 m.ph_->size };
603 dat1_ = {
604 ws_.data(),
605 ws_.size() };
606 more_ = true;
607 }
608
609 //------------------------------------------------
610
611 } // http_proto
612 } // boost
613
614 #endif
615