GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/detail/header.cpp
Date: 2024-04-04 20:17:10
Exec Total Coverage
Lines: 546 581 94.0%
Functions: 47 56 83.9%
Branches: 244 299 81.6%

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