GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/impl/serializer.ipp
Date: 2023-01-26 23:44:13
Exec Total Coverage
Lines: 181 264 68.6%
Functions: 15 24 62.5%
Branches: 75 155 48.4%

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