GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/serializer.cpp
Date: 2024-03-08 19:26:15
Exec Total Coverage
Lines: 160 220 72.7%
Functions: 12 20 60.0%
Branches: 65 113 57.5%

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