GCC Code Coverage Report


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