GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/impl/serializer.ipp
Date: 2023-12-22 17:54:30
Exec Total Coverage
Lines: 162 224 72.3%
Functions: 12 20 60.0%
Branches: 60 113 53.1%

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