GCC Code Coverage Report


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