GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/serializer.cpp
Date: 2024-03-19 15:22:01
Exec Total Coverage
Lines: 182 242 75.2%
Functions: 14 22 63.6%
Branches: 76 127 59.8%

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