GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/detail/impl/header.ipp
Date: 2023-02-02 18:17:22
Exec Total Coverage
Lines: 538 577 93.2%
Functions: 45 54 83.3%
Branches: 241 301 80.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_DETAIL_IMPL_HEADER_IPP
11 #define BOOST_HTTP_PROTO_DETAIL_IMPL_HEADER_IPP
12
13 #include <boost/http_proto/detail/header.hpp>
14 #include <boost/http_proto/field.hpp>
15 #include <boost/http_proto/fields_view_base.hpp>
16 #include <boost/http_proto/rfc/list_rule.hpp>
17 #include <boost/http_proto/rfc/token_rule.hpp>
18 #include <boost/http_proto/rfc/transfer_encoding_rule.hpp>
19 #include <boost/http_proto/rfc/upgrade_rule.hpp>
20 #include <boost/http_proto/rfc/detail/rules.hpp>
21 #include <boost/url/grammar/ci_string.hpp>
22 #include <boost/url/grammar/parse.hpp>
23 #include <boost/url/grammar/range_rule.hpp>
24 #include <boost/url/grammar/recycled.hpp>
25 #include <boost/url/grammar/unsigned_rule.hpp>
26 #include <boost/assert.hpp>
27 #include <boost/assert/source_location.hpp>
28 #include <boost/static_assert.hpp>
29 #include <string>
30 #include <utility>
31
32 namespace boost {
33 namespace http_proto {
34 namespace detail {
35
36 //------------------------------------------------
37
38 auto
39 41 header::
40 entry::
41 operator+(
42 std::size_t dv) const noexcept ->
43 entry
44 {
45 return {
46 static_cast<
47 41 off_t>(np + dv),
48 41 nn,
49 static_cast<
50 41 off_t>(vp + dv),
51 41 vn,
52 41 id };
53 }
54
55 auto
56 75 header::
57 entry::
58 operator-(
59 std::size_t dv) const noexcept ->
60 entry
61 {
62 return {
63 static_cast<
64 75 off_t>(np - dv),
65 75 nn,
66 static_cast<
67 75 off_t>(vp - dv),
68 75 vn,
69 75 id };
70 }
71
72 //------------------------------------------------
73
74 constexpr
75 header::
76 header(fields_tag) noexcept
77 : kind(detail::kind::fields)
78 , cbuf("\r\n")
79 , size(2)
80 , fld{}
81 {
82 }
83
84 constexpr
85 header::
86 header(request_tag) noexcept
87 : kind(detail::kind::request)
88 , cbuf("GET / HTTP/1.1\r\n\r\n")
89 , size(18)
90 , prefix(16)
91 , req{ 3, 1,
92 http_proto::method::get }
93 {
94 }
95
96 constexpr
97 header::
98 header(response_tag) noexcept
99 : kind(detail::kind::response)
100 , cbuf("HTTP/1.1 200 OK\r\n\r\n")
101 , size(19)
102 , prefix(17)
103 , res{ 200,
104 http_proto::status::ok }
105 {
106 }
107
108 //------------------------------------------------
109
110 header const*
111 105 header::
112 get_default(detail::kind k) noexcept
113 {
114 static constexpr header h[3] = {
115 fields_tag{},
116 request_tag{},
117 response_tag{}};
118 105 return &h[k];
119 }
120
121 1968 header::
122 1968 header(empty v) noexcept
123 1968 : kind(v.param)
124 {
125 1968 }
126
127 87 header::
128 87 header(detail::kind k) noexcept
129 87 : header(*get_default(k))
130 {
131 87 }
132
133 void
134 62 header::
135 swap(header& h) noexcept
136 {
137 62 std::swap(cbuf, h.cbuf);
138 62 std::swap(buf, h.buf);
139 62 std::swap(cap, h.cap);
140 62 std::swap(size, h.size);
141 62 std::swap(count, h.count);
142 62 std::swap(prefix, h.prefix);
143 62 std::swap(version, h.version);
144 62 std::swap(md, h.md);
145
3/3
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 45 times.
✓ Branch 2 taken 1 times.
62 switch(kind)
146 {
147 16 default:
148 case detail::kind::fields:
149 16 break;
150 45 case detail::kind::request:
151 45 std::swap(
152 45 req.method_len, h.req.method_len);
153 45 std::swap(
154 45 req.target_len, h.req.target_len);
155 45 std::swap(req.method, h.req.method);
156 45 break;
157 1 case detail::kind::response:
158 1 std::swap(
159 1 res.status_int, h.res.status_int);
160 1 std::swap(res.status, h.res.status);
161 1 break;
162 }
163 62 }
164
165 /* References:
166
167 6.3. Persistence
168 https://datatracker.ietf.org/doc/html/rfc7230#section-6.3
169 */
170 bool
171 22 header::
172 keep_alive() const noexcept
173 {
174
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
22 if(md.payload == payload::error)
175 1 return false;
176
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 8 times.
21 if( version ==
177 http_proto::version::http_1_1)
178 {
179
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13 if(md.connection.close)
180 3 return false;
181 }
182 else
183 {
184
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if(! md.connection.keep_alive)
185 4 return false;
186 }
187 // can't use to_eof in requests
188
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
14 BOOST_ASSERT(
189 kind != detail::kind::request ||
190 md.payload != payload::to_eof);
191
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 11 times.
14 if(md.payload == payload::to_eof)
192 3 return false;
193 11 return true;
194 }
195
196 //------------------------------------------------
197
198 // return total bytes needed
199 // to store message of `size`
200 // bytes and `count` fields.
201 std::size_t
202 1310 header::
203 bytes_needed(
204 std::size_t size,
205 std::size_t count) noexcept
206 {
207 // make sure `size` is big enough
208 // to hold the largest default buffer:
209 // "HTTP/1.1 200 OK\r\n\r\n"
210
2/2
✓ Branch 0 taken 130 times.
✓ Branch 1 taken 1180 times.
1310 if( size < 19)
211 130 size = 19;
212 static constexpr auto A =
213 alignof(header::entry);
214 // round up to alignof(A)
215 1310 return A * (
216 1310 (size + A - 1) / A) +
217 1310 (count * sizeof(
218 1310 header::entry));
219 }
220
221 auto
222 2161 header::
223 tab() const noexcept ->
224 table
225 {
226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2161 times.
2161 BOOST_ASSERT(cap > 0);
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2161 times.
2161 BOOST_ASSERT(buf != nullptr);
228 2161 return table(buf + cap);
229 }
230
231 auto
232 348 header::
233 tab_() const noexcept ->
234 entry*
235 {
236 return reinterpret_cast<
237 348 entry*>(buf + cap);
238 }
239
240 // return true if header cbuf is a default
241 bool
242 27 header::
243 is_default() const noexcept
244 {
245 27 return buf == nullptr;
246 }
247
248 std::size_t
249 64 header::
250 find(
251 field id) const noexcept
252 {
253
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 58 times.
64 if(count == 0)
254 6 return 0;
255 58 std::size_t i = 0;
256 58 auto const* p = &tab()[0];
257
1/2
✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
81 while(i < count)
258 {
259
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 23 times.
81 if(p->id == id)
260 58 break;
261 23 ++i;
262 23 --p;
263 }
264 58 return i;
265 }
266
267 std::size_t
268 13 header::
269 find(
270 string_view name) const noexcept
271 {
272
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9 times.
13 if(count == 0)
273 4 return 0;
274 9 std::size_t i = 0;
275 9 auto const* p = &tab()[0];
276
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 while(i < count)
277 {
278 string_view s(
279 12 cbuf + prefix + p->np,
280 12 p->nn);
281
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 3 times.
12 if(grammar::ci_is_equal(s, name))
282 9 break;
283 3 ++i;
284 3 --p;
285 }
286 9 return i;
287 }
288
289 void
290 16 header::
291 copy_table(
292 void* dest,
293 std::size_t n) const noexcept
294 {
295 16 std::memcpy(
296 reinterpret_cast<
297 16 entry*>(dest) - n,
298 reinterpret_cast<
299 entry const*>(
300 16 cbuf + cap) - n,
301 n * sizeof(entry));
302 16 }
303
304 void
305 16 header::
306 copy_table(
307 void* dest) const noexcept
308 {
309 16 copy_table(dest, count);
310 16 }
311
312 // assign all the members but
313 // preserve the allocated memory
314 void
315 17 header::
316 assign_to(
317 header& dest) const noexcept
318 {
319 17 auto const buf_ = dest.buf;
320 17 auto const cbuf_ = dest.cbuf;
321 17 auto const cap_ = dest.cap;
322 17 dest = *this;
323 17 dest.buf = buf_;
324 17 dest.cbuf = cbuf_;
325 17 dest.cap = cap_;
326 17 }
327
328 //------------------------------------------------
329 //
330 // Metadata
331 //
332 //------------------------------------------------
333
334 bool
335 17 header::
336 is_special(
337 field id) const noexcept
338 {
339
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 13 times.
17 if(kind == detail::kind::fields)
340 4 return false;
341
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 6 times.
13 switch(id)
342 {
343 7 case field::connection:
344 case field::content_length:
345 case field::expect:
346 case field::transfer_encoding:
347 case field::upgrade:
348 7 return true;
349 6 default:
350 6 break;
351 }
352 6 return false;
353 }
354
355 std::size_t
356 header::
357 maybe_count(
358 field id) const noexcept
359 {
360 if(kind == detail::kind::fields)
361 return std::size_t(-1);
362 switch(id)
363 {
364 case field::connection:
365 return md.connection.count;
366 case field::content_length:
367 return md.content_length.count;
368 case field::expect:
369 return md.expect.count;
370 case field::transfer_encoding:
371 return md.transfer_encoding.count;
372 case field::upgrade:
373 return md.upgrade.count;
374 default:
375 break;
376 }
377 return std::size_t(-1);
378 }
379
380 //------------------------------------------------
381
382 // called when the start-line changes
383 void
384 1032 header::
385 on_start_line()
386 {
387
2/2
✓ Branch 0 taken 74 times.
✓ Branch 1 taken 958 times.
1032 if(kind ==
388 detail::kind::response)
389 {
390 // maybe status_int
391 74 update_payload();
392 }
393 1032 }
394
395 // called after a field is inserted
396 void
397 1521 header::
398 on_insert(
399 field id,
400 string_view v)
401 {
402
2/2
✓ Branch 0 taken 428 times.
✓ Branch 1 taken 1093 times.
1521 if(kind == detail::kind::fields)
403 428 return;
404
6/6
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 108 times.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 44 times.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 793 times.
1093 switch(id)
405 {
406 91 case field::content_length:
407 91 return on_insert_content_length(v);
408 108 case field::connection:
409 108 return on_insert_connection(v);
410 33 case field::expect:
411 33 return on_insert_expect(v);
412 44 case field::transfer_encoding:
413 44 return on_insert_transfer_encoding();
414 24 case field::upgrade:
415 24 return on_insert_upgrade(v);
416 793 default:
417 793 break;
418 }
419 }
420
421 // called when one field is erased
422 void
423 38 header::
424 on_erase(field id)
425 {
426
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 35 times.
38 if(kind == detail::kind::fields)
427 3 return;
428
6/6
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 5 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 5 times.
35 switch(id)
429 {
430 11 case field::connection:
431 11 return on_erase_connection();
432 4 case field::content_length:
433 4 return on_erase_content_length();
434 6 case field::expect:
435 6 return on_erase_expect();
436 5 case field::transfer_encoding:
437 5 return on_erase_transfer_encoding();
438 4 case field::upgrade:
439 4 return on_erase_upgrade();
440 5 default:
441 5 break;
442 }
443 }
444
445 //------------------------------------------------
446
447 /*
448 https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
449 */
450 void
451 112 header::
452 on_insert_connection(
453 string_view v)
454 {
455 112 ++md.connection.count;
456
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 111 times.
112 if(md.connection.ec.failed())
457 5 return;
458 auto rv = grammar::parse(
459
1/2
✓ Branch 2 taken 111 times.
✗ Branch 3 not taken.
111 v, list_rule(token_rule, 1));
460
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 107 times.
111 if(! rv)
461 {
462 4 md.connection.ec =
463 8 BOOST_HTTP_PROTO_ERR(
464 error::bad_connection);
465 4 return;
466 }
467 107 md.connection.ec = {};
468
2/2
✓ Branch 4 taken 118 times.
✓ Branch 5 taken 107 times.
225 for(auto t : *rv)
469 {
470
2/2
✓ Branch 2 taken 70 times.
✓ Branch 3 taken 48 times.
118 if(grammar::ci_is_equal(
471 t, "close"))
472 70 md.connection.close = true;
473
2/2
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 24 times.
48 else if(grammar::ci_is_equal(
474 t, "keep-alive"))
475 24 md.connection.keep_alive = true;
476
2/2
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 5 times.
24 else if(grammar::ci_is_equal(
477 t, "upgrade"))
478 19 md.connection.upgrade = true;
479 }
480 }
481
482 void
483 92 header::
484 on_insert_content_length(
485 string_view v)
486 {
487 static
488 constexpr
489 grammar::unsigned_rule<
490 std::uint64_t> num_rule{};
491
492 92 ++md.content_length.count;
493
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 90 times.
92 if(md.content_length.ec.failed())
494 89 return;
495 auto rv =
496 90 grammar::parse(v, num_rule);
497
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 85 times.
90 if(! rv)
498 {
499 // parse failure
500 5 md.content_length.ec =
501 10 BOOST_HTTP_PROTO_ERR(
502 error::bad_content_length);
503 5 md.content_length.value = 0;
504 5 update_payload();
505 5 return;
506 }
507
2/2
✓ Branch 0 taken 75 times.
✓ Branch 1 taken 10 times.
85 if(md.content_length.count == 1)
508 {
509 // one value
510 75 md.content_length.ec = {};
511 75 md.content_length.value = *rv;
512 75 update_payload();
513 75 return;
514 }
515
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 3 times.
10 if(*rv == md.content_length.value)
516 {
517 // ok: duplicate value
518 7 return;
519 }
520 // bad: different values
521 3 md.content_length.ec =
522 6 BOOST_HTTP_PROTO_ERR(
523 error::multiple_content_length);
524 3 md.content_length.value = 0;
525 3 update_payload();
526 }
527
528 void
529 36 header::
530 on_insert_expect(
531 string_view v)
532 {
533 36 ++md.expect.count;
534
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 28 times.
36 if(kind != detail::kind::request)
535 8 return;
536
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 27 times.
28 if(md.expect.ec.failed())
537 1 return;
538 // VFALCO Should we allow duplicate
539 // Expect fields that have 100-continue?
540
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
49 if( md.expect.count > 1 ||
541
4/4
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 16 times.
49 ! grammar::ci_is_equal(v,
542 "100-continue"))
543 {
544 11 md.expect.ec =
545 22 BOOST_HTTP_PROTO_ERR(
546 error::bad_expect);
547 11 md.expect.is_100_continue = false;
548 11 return;
549 }
550 16 md.expect.is_100_continue = true;
551 }
552
553 void
554 47 header::
555 on_insert_transfer_encoding()
556 {
557 47 ++md.transfer_encoding.count;
558
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 46 times.
47 if(md.transfer_encoding.ec.failed())
559 1 return;
560 46 auto const n =
561 md.transfer_encoding.count;
562 46 md.transfer_encoding = {};
563 46 md.transfer_encoding.count = n;
564 53 for(auto s :
565 fields_view_base::subrange(
566
2/2
✓ Branch 5 taken 61 times.
✓ Branch 6 taken 38 times.
152 this, find(field::transfer_encoding)))
567 {
568 auto rv = grammar::parse(
569
1/2
✓ Branch 1 taken 61 times.
✗ Branch 2 not taken.
61 s, transfer_encoding_rule);
570
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 57 times.
61 if(! rv)
571 {
572 // parse error
573 4 md.transfer_encoding.ec =
574 8 BOOST_HTTP_PROTO_ERR(
575 error::bad_transfer_encoding);
576 4 md.transfer_encoding.codings = 0;
577 4 md.transfer_encoding.is_chunked = false;
578 4 update_payload();
579 4 return;
580 }
581 57 md.transfer_encoding.codings += rv->size();
582
2/2
✓ Branch 6 taken 66 times.
✓ Branch 7 taken 53 times.
119 for(auto t : *rv)
583 {
584
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 4 times.
66 if(! md.transfer_encoding.is_chunked)
585 {
586
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 36 times.
62 if(t.id == transfer_coding::chunked)
587 26 md.transfer_encoding.is_chunked = true;
588 62 continue;
589 }
590
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(t.id == transfer_coding::chunked)
591 {
592 // chunked appears twice
593 2 md.transfer_encoding.ec =
594 4 BOOST_HTTP_PROTO_ERR(
595 error::bad_transfer_encoding);
596 2 md.transfer_encoding.codings = 0;
597 2 md.transfer_encoding.is_chunked = false;
598 2 update_payload();
599 2 return;
600 }
601 // chunked must be last
602 2 md.transfer_encoding.ec =
603 4 BOOST_HTTP_PROTO_ERR(
604 error::bad_transfer_encoding);
605 2 md.transfer_encoding.codings = 0;
606 2 md.transfer_encoding.is_chunked = false;
607 2 update_payload();
608 2 return;
609 }
610 }
611 38 update_payload();
612 }
613
614 void
615 26 header::
616 on_insert_upgrade(
617 string_view v)
618 {
619 26 ++md.upgrade.count;
620
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 25 times.
26 if(md.upgrade.ec.failed())
621 5 return;
622
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 24 times.
25 if( version !=
623 http_proto::version::http_1_1)
624 {
625 1 md.upgrade.ec =
626 2 BOOST_HTTP_PROTO_ERR(
627 error::bad_upgrade);
628 1 md.upgrade.websocket = false;
629 1 return;
630 }
631 auto rv = grammar::parse(
632
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 v, upgrade_rule);
633
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 21 times.
24 if(! rv)
634 {
635 3 md.upgrade.ec =
636 6 BOOST_HTTP_PROTO_ERR(
637 error::bad_upgrade);
638 3 md.upgrade.websocket = false;
639 3 return;
640 }
641
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 5 times.
21 if(! md.upgrade.websocket)
642 {
643
2/2
✓ Branch 4 taken 16 times.
✓ Branch 5 taken 7 times.
23 for(auto t : *rv)
644 {
645 16 if( grammar::ci_is_equal(
646
6/6
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 7 times.
26 t.name, "websocket") &&
647 10 t.version.empty())
648 {
649 9 md.upgrade.websocket = true;
650 9 break;
651 }
652 }
653 }
654 }
655
656 //------------------------------------------------
657
658 void
659 11 header::
660 on_erase_connection()
661 {
662
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 BOOST_ASSERT(
663 md.connection.count > 0);
664 // reset and re-insert
665 11 auto n = md.connection.count - 1;
666 11 auto const p = cbuf + prefix;
667 11 auto const* e = &tab()[0];
668 11 md.connection = {};
669
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 11 times.
16 while(n > 0)
670 {
671
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if(e->id == field::connection)
672
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 on_insert_connection(string_view(
673 4 p + e->vp, e->vn));
674 5 --n;
675 5 --e;
676 }
677 11 }
678
679 void
680 4 header::
681 on_erase_content_length()
682 {
683
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(
684 md.content_length.count > 0);
685 4 --md.content_length.count;
686
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if(md.content_length.count == 0)
687 {
688 // no Content-Length
689 1 md.content_length = {};
690 1 update_payload();
691 1 return;
692 }
693
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if(! md.content_length.ec.failed())
694 {
695 // removing a duplicate value
696 2 return;
697 }
698 // reset and re-insert
699 1 auto n = md.content_length.count;
700 1 auto const p = cbuf + prefix;
701 1 auto const* e = &tab()[0];
702 1 md.content_length = {};
703
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 while(n > 0)
704 {
705
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(e->id == field::content_length)
706
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 on_insert_content_length(
707 1 string_view(p + e->vp, e->vn));
708 1 --n;
709 1 --e;
710 }
711 1 update_payload();
712 }
713
714 void
715 6 header::
716 on_erase_expect()
717 {
718
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 BOOST_ASSERT(
719 md.expect.count > 0);
720 6 --md.expect.count;
721
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if(kind != detail::kind::request)
722 1 return;
723
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if(md.expect.count == 0)
724 {
725 // no Expect
726 2 md.expect = {};
727 2 return;
728 }
729 // VFALCO This should be uncommented
730 // if we want to allow multiple Expect
731 // fields with the value 100-continue
732 /*
733 if(! md.expect.ec.failed())
734 return;
735 */
736 // reset and re-insert
737 3 auto n = md.expect.count;
738 3 auto const p = cbuf + prefix;
739 3 auto const* e = &tab()[0];
740 3 md.expect = {};
741
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 while(n > 0)
742 {
743
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(e->id == field::expect)
744 3 on_insert_expect(
745 3 string_view(p + e->vp, e->vn));
746 3 --n;
747 3 --e;
748 }
749 }
750
751 void
752 5 header::
753 on_erase_transfer_encoding()
754 {
755
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 BOOST_ASSERT(
756 md.transfer_encoding.count > 0);
757 5 --md.transfer_encoding.count;
758
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if(md.transfer_encoding.count == 0)
759 {
760 // no Transfer-Encoding
761 2 md.transfer_encoding = {};
762 2 update_payload();
763 2 return;
764 }
765 // re-insert everything
766 3 --md.transfer_encoding.count;
767 3 on_insert_transfer_encoding();
768 }
769
770 // called when Upgrade is erased
771 void
772 4 header::
773 on_erase_upgrade()
774 {
775
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(
776 md.upgrade.count > 0);
777 4 --md.upgrade.count;
778
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(md.upgrade.count == 0)
779 {
780 // no Upgrade
781 2 md.upgrade = {};
782 2 return;
783 }
784 // reset and re-insert
785 2 auto n = md.upgrade.count;
786 2 auto const p = cbuf + prefix;
787 2 auto const* e = &tab()[0];
788 2 md.upgrade = {};
789
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 while(n > 0)
790 {
791
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(e->id == field::upgrade)
792
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 on_insert_upgrade(string_view(
793 2 p + e->vp, e->vn));
794 2 --n;
795 2 --e;
796 }
797 }
798
799 //------------------------------------------------
800
801 // called when all fields with id are removed
802 void
803 51 header::
804 on_erase_all(
805 field id)
806 {
807
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 37 times.
51 if(kind == detail::kind::fields)
808 14 return;
809
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 27 times.
37 switch(id)
810 {
811 1 case field::connection:
812 1 md.connection = {};
813 1 return;
814
815 2 case field::content_length:
816 2 md.content_length = {};
817 2 update_payload();
818 2 return;
819
820 5 case field::expect:
821 5 md.expect = {};
822 5 update_payload();
823 5 return;
824
825 1 case field::transfer_encoding:
826 1 md.transfer_encoding = {};
827 1 update_payload();
828 1 return;
829
830 1 case field::upgrade:
831 1 md.upgrade = {};
832 1 return;
833
834 27 default:
835 27 break;
836 }
837 }
838
839 //------------------------------------------------
840
841 /* References:
842
843 3.3. Message Body
844 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3
845
846 3.3.1. Transfer-Encoding
847 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
848
849 3.3.2. Content-Length
850 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
851 */
852 void
853 215 header::
854 update_payload() noexcept
855 {
856
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 215 times.
215 BOOST_ASSERT(kind !=
857 detail::kind::fields);
858
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 215 times.
215 if(md.payload_override)
859 {
860 // e.g. response to
861 // a HEAD request
862 return;
863 }
864
865 /* If there is an error in either Content-Length
866 or Transfer-Encoding, then the payload is
867 undefined. Clients should probably close the
868 connection. Servers can send a Bad Request
869 and avoid reading any payload bytes.
870 */
871
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 207 times.
215 if(md.content_length.ec.failed())
872 {
873 // invalid Content-Length
874 8 md.payload = payload::error;
875 8 md.payload_size = 0;
876 8 return;
877 }
878
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 199 times.
207 if(md.transfer_encoding.ec.failed())
879 {
880 // invalid Transfer-Encoding
881 8 md.payload = payload::error;
882 8 md.payload_size = 0;
883 8 return;
884 }
885
886 /* A sender MUST NOT send a Content-Length
887 header field in any message that contains
888 a Transfer-Encoding header field.
889 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
890 */
891
2/2
✓ Branch 0 taken 79 times.
✓ Branch 1 taken 120 times.
199 if( md.content_length.count > 0 &&
892
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 76 times.
79 md.transfer_encoding.count > 0)
893 {
894 3 md.payload = payload::error;
895 3 md.payload_size = 0;
896 3 return;
897 }
898
899
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 98 times.
196 if(kind == detail::kind::response)
900 98 goto do_response;
901
902 //--------------------------------------------
903
904 /* The presence of a message body in a
905 request is signaled by a Content-Length
906 or Transfer-Encoding header field. Request
907 message framing is independent of method
908 semantics, even if the method does not
909 define any use for a message body.
910 */
911
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 42 times.
98 if(md.content_length.count > 0)
912 {
913
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 6 times.
56 if(md.content_length.value > 0)
914 {
915 // non-zero Content-Length
916 50 md.payload = payload::size;
917 50 md.payload_size = md.content_length.value;
918 50 return;
919 }
920 // Content-Length: 0
921 6 md.payload = payload::none;
922 6 md.payload_size = 0;
923 6 return;
924 }
925
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 27 times.
42 if(md.transfer_encoding.is_chunked)
926 {
927 // chunked
928 15 md.payload = payload::chunked;
929 15 md.payload_size = 0;
930 15 return;
931 }
932 // no payload
933 27 md.payload = payload::none;
934 27 md.payload_size = 0;
935 27 return;
936
937 //--------------------------------------------
938 98 do_response:
939
940
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 2 times.
98 if( res.status_int / 100 == 1 || // 1xx e.g. Continue
941
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 2 times.
96 res.status_int == 204 || // No Content
942
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 92 times.
94 res.status_int == 304) // Not Modified
943 {
944 /* The correctness of any Content-Length
945 here is defined by the particular
946 resource, and cannot be determined
947 here. In any case there is no payload.
948 */
949 6 md.payload = payload::none;
950 6 md.payload_size = 0;
951 6 return;
952 }
953
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 75 times.
92 if(md.content_length.count > 0)
954 {
955
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 11 times.
17 if(md.content_length.value > 0)
956 {
957 // Content-Length > 0
958 6 md.payload = payload::size;
959 6 md.payload_size = md.content_length.value;
960 6 return;
961 }
962 // Content-Length: 0
963 11 md.payload = payload::none;
964 11 md.payload_size = 0;
965 11 return;
966 }
967
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 71 times.
75 if(md.transfer_encoding.is_chunked)
968 {
969 // chunked
970 4 md.payload = payload::chunked;
971 4 md.payload_size = 0;
972 4 return;
973 }
974
975 // eof needed
976 71 md.payload = payload::to_eof;
977 71 md.payload_size = 0;
978 }
979
980 //------------------------------------------------
981
982 std::size_t
983 453 header::
984 count_crlf(
985 string_view s) noexcept
986 {
987 453 auto it = s.data();
988 453 auto len = s.size();
989 453 std::size_t n = 0;
990
2/2
✓ Branch 0 taken 16049 times.
✓ Branch 1 taken 453 times.
16502 while(len >= 2)
991 {
992
2/2
✓ Branch 0 taken 1522 times.
✓ Branch 1 taken 14527 times.
16049 if( it[0] == '\r' &&
993
1/2
✓ Branch 0 taken 1522 times.
✗ Branch 1 not taken.
1522 it[1] != '\r')
994 {
995
1/2
✓ Branch 0 taken 1522 times.
✗ Branch 1 not taken.
1522 if(it[1] == '\n')
996 1522 n++;
997 1522 it += 2;
998 1522 len -= 2;
999 }
1000 else
1001 {
1002 14527 it++;
1003 14527 len--;
1004 }
1005 }
1006 453 return n;
1007 }
1008
1009 static
1010 void
1011 2298 parse_start_line(
1012 header& h,
1013 header::config& cfg,
1014 std::size_t new_size,
1015 error_code& ec) noexcept
1016 {
1017
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2298 times.
2298 BOOST_ASSERT(h.size == 0);
1018
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2298 times.
2298 BOOST_ASSERT(h.prefix == 0);
1019
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2298 times.
2298 BOOST_ASSERT(h.cbuf != nullptr);
1020
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2298 times.
2298 BOOST_ASSERT(
1021 h.kind != detail::kind::fields);
1022
1023 2298 auto const it0 = h.cbuf;
1024 2298 auto const end = it0 + new_size;
1025 2298 char const* it = it0;
1026
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2298 times.
2298 if( new_size > cfg.start_line_limit)
1027 new_size = cfg.start_line_limit;
1028
2/2
✓ Branch 0 taken 2224 times.
✓ Branch 1 taken 74 times.
2298 if(h.kind == detail::kind::request)
1029 {
1030 auto rv = grammar::parse(
1031 2224 it, end, request_line_rule);
1032
2/2
✓ Branch 1 taken 1275 times.
✓ Branch 2 taken 949 times.
2224 if(! rv)
1033 {
1034 1275 ec = rv.error();
1035
2/4
✓ Branch 2 taken 1275 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1275 times.
2550 if( ec == grammar::error::need_more &&
1036
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1275 times.
1275 new_size == cfg.start_line_limit)
1037 ec = BOOST_HTTP_PROTO_ERR(
1038 error::start_line_limit);
1039 1275 return;
1040 }
1041 // method
1042 949 auto sm = std::get<0>(*rv);
1043 949 h.req.method = string_to_method(sm);
1044 949 h.req.method_len =
1045 949 static_cast<off_t>(sm.size());
1046 // target
1047 949 auto st = std::get<1>(*rv);
1048 949 h.req.target_len =
1049 949 static_cast<off_t>(st.size());
1050 // version
1051
2/3
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 929 times.
✗ Branch 4 not taken.
949 switch(std::get<2>(*rv))
1052 {
1053 20 case 10:
1054 20 h.version =
1055 http_proto::version::http_1_0;
1056 20 break;
1057 929 case 11:
1058 929 h.version =
1059 http_proto::version::http_1_1;
1060 929 break;
1061 default:
1062 {
1063 ec = BOOST_HTTP_PROTO_ERR(
1064 error::bad_version);
1065 return;
1066 }
1067 }
1068 }
1069 else
1070 {
1071 auto rv = grammar::parse(
1072 74 it, end, status_line_rule);
1073
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 74 times.
74 if(! rv)
1074 {
1075 ec = rv.error();
1076 if( ec == grammar::error::need_more &&
1077 new_size == cfg.start_line_limit)
1078 ec = BOOST_HTTP_PROTO_ERR(
1079 error::start_line_limit);
1080 return;
1081 }
1082 // version
1083
2/3
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 70 times.
✗ Branch 4 not taken.
74 switch(std::get<0>(*rv))
1084 {
1085 4 case 10:
1086 4 h.version =
1087 http_proto::version::http_1_0;
1088 4 break;
1089 70 case 11:
1090 70 h.version =
1091 http_proto::version::http_1_1;
1092 70 break;
1093 default:
1094 {
1095 ec = BOOST_HTTP_PROTO_ERR(
1096 error::bad_version);
1097 return;
1098 }
1099 }
1100 // status-code
1101 74 h.res.status_int =
1102 static_cast<unsigned short>(
1103 74 std::get<1>(*rv).v);
1104 74 h.res.status = std::get<1>(*rv).st;
1105 }
1106 1023 h.prefix = static_cast<off_t>(it - it0);
1107 1023 h.size = h.prefix;
1108 1023 h.on_start_line();
1109 }
1110
1111 // returns: true if we added a field
1112 static
1113 void
1114 3754 parse_field(
1115 header& h,
1116 header::config& cfg,
1117 std::size_t new_size,
1118 error_code& ec) noexcept
1119 {
1120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3754 times.
3754 if( new_size > cfg.field_size_limit)
1121 new_size = cfg.field_size_limit;
1122 3754 auto const it0 = h.cbuf + h.size;
1123 3754 auto const end = h.cbuf + new_size;
1124 3754 char const* it = it0;
1125 auto rv = grammar::parse(
1126 3754 it, end, field_rule);
1127
2/2
✓ Branch 1 taken 2308 times.
✓ Branch 2 taken 1446 times.
3754 if(rv.has_error())
1128 {
1129 2308 ec = rv.error();
1130
2/2
✓ Branch 2 taken 1037 times.
✓ Branch 3 taken 1271 times.
2308 if(ec == grammar::error::end_of_range)
1131 {
1132 // final CRLF
1133 1037 h.size = static_cast<off_t>(
1134 1037 it - h.cbuf);
1135 2308 return;
1136 }
1137
3/4
✓ Branch 2 taken 1143 times.
✓ Branch 3 taken 128 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1271 times.
2414 if( ec == grammar::error::need_more &&
1138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1143 times.
1143 new_size == cfg.field_size_limit)
1139 {
1140 ec = BOOST_HTTP_PROTO_ERR(
1141 error::field_size_limit);
1142 }
1143 1271 return;
1144 }
1145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1446 times.
1446 if(h.count >= cfg.fields_limit)
1146 {
1147 ec = BOOST_HTTP_PROTO_ERR(
1148 error::fields_limit);
1149 return;
1150 }
1151
2/2
✓ Branch 1 taken 137 times.
✓ Branch 2 taken 1309 times.
1446 if(rv->has_obs_fold)
1152 {
1153 // obs fold not allowed in test views
1154
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
137 BOOST_ASSERT(h.buf != nullptr);
1155 137 remove_obs_fold(h.buf + h.size, it);
1156 }
1157 1446 auto id = string_to_field(rv->name);
1158 1446 h.size = static_cast<off_t>(it - h.cbuf);
1159
1160 // add field table entry
1161
1/2
✓ Branch 0 taken 1446 times.
✗ Branch 1 not taken.
1446 if(h.buf != nullptr)
1162 {
1163 2892 auto& e = header::table(
1164 1446 h.buf + h.cap)[h.count];
1165 1446 auto const base =
1166 1446 h.buf + h.prefix;
1167 1446 e.np = static_cast<off_t>(
1168 1446 rv->name.data() - base);
1169 1446 e.nn = static_cast<off_t>(
1170 1446 rv->name.size());
1171 1446 e.vp = static_cast<off_t>(
1172 1446 rv->value.data() - base);
1173 1446 e.vn = static_cast<off_t>(
1174 1446 rv->value.size());
1175 1446 e.id = id;
1176 }
1177 1446 ++h.count;
1178 1446 h.on_insert(id, rv->value);
1179 }
1180
1181 void
1182 3638 header::
1183 parse(
1184 config& cfg,
1185 std::size_t new_size,
1186 error_code& ec) noexcept
1187 {
1188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3638 times.
3638 if( new_size > cfg.headers_limit)
1189 new_size = cfg.headers_limit;
1190
2/2
✓ Branch 0 taken 2495 times.
✓ Branch 1 taken 1143 times.
3638 if( this->prefix == 0 &&
1191
2/2
✓ Branch 0 taken 2298 times.
✓ Branch 1 taken 197 times.
2495 this->kind !=
1192 detail::kind::fields)
1193 {
1194 2298 parse_start_line(
1195 *this, cfg, new_size, ec);
1196
2/2
✓ Branch 1 taken 1290 times.
✓ Branch 2 taken 1008 times.
2298 if(ec.failed())
1197 {
1198
2/4
✓ Branch 2 taken 1290 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1290 times.
2580 if( ec == grammar::error::need_more &&
1199
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1290 times.
1290 new_size == cfg.headers_limit)
1200 {
1201 ec = BOOST_HTTP_PROTO_ERR(
1202 error::headers_limit);
1203 }
1204 1290 return;
1205 }
1206 }
1207 for(;;)
1208 {
1209 3754 parse_field(
1210 *this, cfg, new_size, ec);
1211
2/2
✓ Branch 1 taken 2348 times.
✓ Branch 2 taken 1406 times.
3754 if(ec.failed())
1212 {
1213
3/4
✓ Branch 2 taken 1183 times.
✓ Branch 3 taken 1165 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2348 times.
3531 if( ec == grammar::error::need_more &&
1214
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1183 times.
1183 new_size == cfg.headers_limit)
1215 {
1216 ec = BOOST_HTTP_PROTO_ERR(
1217 error::headers_limit);
1218 return;
1219 }
1220 2348 break;
1221 }
1222 1406 }
1223
2/2
✓ Branch 2 taken 1037 times.
✓ Branch 3 taken 1311 times.
2348 if(ec == grammar::error::end_of_range)
1224 1037 ec = {};
1225 }
1226
1227 } // detail
1228 } // http_proto
1229 } // boost
1230
1231 #endif
1232