GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/detail/impl/header.ipp
Date: 2023-12-11 14:11:20
Exec Total Coverage
Lines: 546 581 94.0%
Functions: 47 56 83.9%
Branches: 243 299 81.3%

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 2727 header::
123 2727 header(empty v) noexcept
124 2727 : kind(v.param)
125 {
126 2727 }
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 1207 header::
224 table_space(
225 std::size_t count) noexcept
226 {
227 return count *
228 1207 sizeof(header::entry);
229 }
230
231 std::size_t
232 1207 header::
233 table_space() const noexcept
234 {
235 1207 return table_space(count);
236 }
237
238 auto
239 2163 header::
240 tab() const noexcept ->
241 table
242 {
243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2163 times.
2163 BOOST_ASSERT(cap > 0);
244
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2163 times.
2163 BOOST_ASSERT(buf != nullptr);
245 2163 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 63 header::
267 find(
268 field id) const noexcept
269 {
270
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 57 times.
63 if(count == 0)
271 6 return 0;
272 57 std::size_t i = 0;
273 57 auto const* p = &tab()[0];
274
1/2
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
78 while(i < count)
275 {
276
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 21 times.
78 if(p->id == id)
277 57 break;
278 21 ++i;
279 21 --p;
280 }
281 57 return i;
282 }
283
284 std::size_t
285 13 header::
286 find(
287 core::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 core::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 1720 header::
402 on_start_line()
403 {
404 // items in both the request-line
405 // and the status-line can affect
406 // the payload, for example whether
407 // or not EOF marks the end of the
408 // payload.
409
410 1720 update_payload();
411 1720 }
412
413 // called after a field is inserted
414 void
415 2557 header::
416 on_insert(
417 field id,
418 core::string_view v)
419 {
420
2/2
✓ Branch 0 taken 428 times.
✓ Branch 1 taken 2129 times.
2557 if(kind == detail::kind::fields)
421 428 return;
422
6/6
✓ Branch 0 taken 559 times.
✓ Branch 1 taken 120 times.
✓ Branch 2 taken 33 times.
✓ Branch 3 taken 43 times.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 1350 times.
2129 switch(id)
423 {
424 559 case field::content_length:
425 559 return on_insert_content_length(v);
426 120 case field::connection:
427 120 return on_insert_connection(v);
428 33 case field::expect:
429 33 return on_insert_expect(v);
430 43 case field::transfer_encoding:
431 43 return on_insert_transfer_encoding();
432 24 case field::upgrade:
433 24 return on_insert_upgrade(v);
434 1350 default:
435 1350 break;
436 }
437 }
438
439 // called when one field is erased
440 void
441 38 header::
442 on_erase(field id)
443 {
444
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 35 times.
38 if(kind == detail::kind::fields)
445 3 return;
446
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)
447 {
448 11 case field::connection:
449 11 return on_erase_connection();
450 4 case field::content_length:
451 4 return on_erase_content_length();
452 6 case field::expect:
453 6 return on_erase_expect();
454 5 case field::transfer_encoding:
455 5 return on_erase_transfer_encoding();
456 4 case field::upgrade:
457 4 return on_erase_upgrade();
458 5 default:
459 5 break;
460 }
461 }
462
463 //------------------------------------------------
464
465 /*
466 https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
467 */
468 void
469 124 header::
470 on_insert_connection(
471 core::string_view v)
472 {
473 124 ++md.connection.count;
474
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 123 times.
124 if(md.connection.ec.failed())
475 5 return;
476 auto rv = grammar::parse(
477
1/2
✓ Branch 2 taken 123 times.
✗ Branch 3 not taken.
123 v, list_rule(token_rule, 1));
478
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 119 times.
123 if(! rv)
479 {
480 4 md.connection.ec =
481 8 BOOST_HTTP_PROTO_ERR(
482 error::bad_connection);
483 4 return;
484 }
485 119 md.connection.ec = {};
486
2/2
✓ Branch 4 taken 130 times.
✓ Branch 5 taken 119 times.
249 for(auto t : *rv)
487 {
488
2/2
✓ Branch 2 taken 82 times.
✓ Branch 3 taken 48 times.
130 if(grammar::ci_is_equal(
489 t, "close"))
490 82 md.connection.close = true;
491
2/2
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 24 times.
48 else if(grammar::ci_is_equal(
492 t, "keep-alive"))
493 24 md.connection.keep_alive = true;
494
2/2
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 5 times.
24 else if(grammar::ci_is_equal(
495 t, "upgrade"))
496 19 md.connection.upgrade = true;
497 }
498 }
499
500 void
501 560 header::
502 on_insert_content_length(
503 core::string_view v)
504 {
505 static
506 constexpr
507 grammar::unsigned_rule<
508 std::uint64_t> num_rule{};
509
510 560 ++md.content_length.count;
511
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 558 times.
560 if(md.content_length.ec.failed())
512 437 return;
513 auto rv =
514 558 grammar::parse(v, num_rule);
515
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 553 times.
558 if(! rv)
516 {
517 // parse failure
518 5 md.content_length.ec =
519 10 BOOST_HTTP_PROTO_ERR(
520 error::bad_content_length);
521 5 md.content_length.value = 0;
522 5 update_payload();
523 5 return;
524 }
525
2/2
✓ Branch 0 taken 423 times.
✓ Branch 1 taken 130 times.
553 if(md.content_length.count == 1)
526 {
527 // one value
528 423 md.content_length.ec = {};
529 423 md.content_length.value = *rv;
530 423 update_payload();
531 423 return;
532 }
533
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 123 times.
130 if(*rv == md.content_length.value)
534 {
535 // ok: duplicate value
536 7 return;
537 }
538 // bad: different values
539 123 md.content_length.ec =
540 246 BOOST_HTTP_PROTO_ERR(
541 error::multiple_content_length);
542 123 md.content_length.value = 0;
543 123 update_payload();
544 }
545
546 void
547 36 header::
548 on_insert_expect(
549 core::string_view v)
550 {
551 36 ++md.expect.count;
552
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 28 times.
36 if(kind != detail::kind::request)
553 8 return;
554
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 27 times.
28 if(md.expect.ec.failed())
555 1 return;
556 // VFALCO Should we allow duplicate
557 // Expect fields that have 100-continue?
558
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 5 times.
49 if( md.expect.count > 1 ||
559
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,
560 "100-continue"))
561 {
562 11 md.expect.ec =
563 22 BOOST_HTTP_PROTO_ERR(
564 error::bad_expect);
565 11 md.expect.is_100_continue = false;
566 11 return;
567 }
568 16 md.expect.is_100_continue = true;
569 }
570
571 void
572 46 header::
573 on_insert_transfer_encoding()
574 {
575 46 ++md.transfer_encoding.count;
576
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 45 times.
46 if(md.transfer_encoding.ec.failed())
577 1 return;
578 45 auto const n =
579 md.transfer_encoding.count;
580 45 md.transfer_encoding = {};
581 45 md.transfer_encoding.count = n;
582 52 for(auto s :
583 fields_view_base::subrange(
584
2/2
✓ Branch 5 taken 60 times.
✓ Branch 6 taken 37 times.
149 this, find(field::transfer_encoding)))
585 {
586 auto rv = grammar::parse(
587
1/2
✓ Branch 1 taken 60 times.
✗ Branch 2 not taken.
60 s, transfer_encoding_rule);
588
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 56 times.
60 if(! rv)
589 {
590 // parse error
591 4 md.transfer_encoding.ec =
592 8 BOOST_HTTP_PROTO_ERR(
593 error::bad_transfer_encoding);
594 4 md.transfer_encoding.codings = 0;
595 4 md.transfer_encoding.is_chunked = false;
596 4 update_payload();
597 4 return;
598 }
599 56 md.transfer_encoding.codings += rv->size();
600
2/2
✓ Branch 4 taken 65 times.
✓ Branch 5 taken 52 times.
117 for(auto t : *rv)
601 {
602
2/2
✓ Branch 0 taken 61 times.
✓ Branch 1 taken 4 times.
65 if(! md.transfer_encoding.is_chunked)
603 {
604
2/2
✓ Branch 0 taken 25 times.
✓ Branch 1 taken 36 times.
61 if(t.id == transfer_coding::chunked)
605 25 md.transfer_encoding.is_chunked = true;
606 61 continue;
607 }
608
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(t.id == transfer_coding::chunked)
609 {
610 // chunked appears twice
611 2 md.transfer_encoding.ec =
612 4 BOOST_HTTP_PROTO_ERR(
613 error::bad_transfer_encoding);
614 2 md.transfer_encoding.codings = 0;
615 2 md.transfer_encoding.is_chunked = false;
616 2 update_payload();
617 2 return;
618 }
619 // chunked must be last
620 2 md.transfer_encoding.ec =
621 4 BOOST_HTTP_PROTO_ERR(
622 error::bad_transfer_encoding);
623 2 md.transfer_encoding.codings = 0;
624 2 md.transfer_encoding.is_chunked = false;
625 2 update_payload();
626 2 return;
627 }
628 }
629 37 update_payload();
630 }
631
632 void
633 26 header::
634 on_insert_upgrade(
635 core::string_view v)
636 {
637 26 ++md.upgrade.count;
638
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 25 times.
26 if(md.upgrade.ec.failed())
639 5 return;
640
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 24 times.
25 if( version !=
641 http_proto::version::http_1_1)
642 {
643 1 md.upgrade.ec =
644 2 BOOST_HTTP_PROTO_ERR(
645 error::bad_upgrade);
646 1 md.upgrade.websocket = false;
647 1 return;
648 }
649 auto rv = grammar::parse(
650
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 v, upgrade_rule);
651
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 21 times.
24 if(! rv)
652 {
653 3 md.upgrade.ec =
654 6 BOOST_HTTP_PROTO_ERR(
655 error::bad_upgrade);
656 3 md.upgrade.websocket = false;
657 3 return;
658 }
659
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 5 times.
21 if(! md.upgrade.websocket)
660 {
661
2/2
✓ Branch 4 taken 16 times.
✓ Branch 5 taken 7 times.
23 for(auto t : *rv)
662 {
663 16 if( grammar::ci_is_equal(
664
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") &&
665 10 t.version.empty())
666 {
667 9 md.upgrade.websocket = true;
668 9 break;
669 }
670 }
671 }
672 }
673
674 //------------------------------------------------
675
676 void
677 11 header::
678 on_erase_connection()
679 {
680
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 BOOST_ASSERT(
681 md.connection.count > 0);
682 // reset and re-insert
683 11 auto n = md.connection.count - 1;
684 11 auto const p = cbuf + prefix;
685 11 auto const* e = &tab()[0];
686 11 md.connection = {};
687
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 11 times.
16 while(n > 0)
688 {
689
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if(e->id == field::connection)
690
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 on_insert_connection(
691 core::string_view(
692 4 p + e->vp, e->vn));
693 5 --n;
694 5 --e;
695 }
696 11 }
697
698 void
699 4 header::
700 on_erase_content_length()
701 {
702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(
703 md.content_length.count > 0);
704 4 --md.content_length.count;
705
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if(md.content_length.count == 0)
706 {
707 // no Content-Length
708 1 md.content_length = {};
709 1 update_payload();
710 1 return;
711 }
712
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if(! md.content_length.ec.failed())
713 {
714 // removing a duplicate value
715 2 return;
716 }
717 // reset and re-insert
718 1 auto n = md.content_length.count;
719 1 auto const p = cbuf + prefix;
720 1 auto const* e = &tab()[0];
721 1 md.content_length = {};
722
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 while(n > 0)
723 {
724
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(e->id == field::content_length)
725
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 on_insert_content_length(
726 core::string_view(
727 1 p + e->vp, e->vn));
728 1 --n;
729 1 --e;
730 }
731 1 update_payload();
732 }
733
734 void
735 6 header::
736 on_erase_expect()
737 {
738
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 BOOST_ASSERT(
739 md.expect.count > 0);
740 6 --md.expect.count;
741
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if(kind != detail::kind::request)
742 1 return;
743
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if(md.expect.count == 0)
744 {
745 // no Expect
746 2 md.expect = {};
747 2 return;
748 }
749 // VFALCO This should be uncommented
750 // if we want to allow multiple Expect
751 // fields with the value 100-continue
752 /*
753 if(! md.expect.ec.failed())
754 return;
755 */
756 // reset and re-insert
757 3 auto n = md.expect.count;
758 3 auto const p = cbuf + prefix;
759 3 auto const* e = &tab()[0];
760 3 md.expect = {};
761
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
6 while(n > 0)
762 {
763
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if(e->id == field::expect)
764 3 on_insert_expect(
765 core::string_view(
766 3 p + e->vp, e->vn));
767 3 --n;
768 3 --e;
769 }
770 }
771
772 void
773 5 header::
774 on_erase_transfer_encoding()
775 {
776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 BOOST_ASSERT(
777 md.transfer_encoding.count > 0);
778 5 --md.transfer_encoding.count;
779
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 if(md.transfer_encoding.count == 0)
780 {
781 // no Transfer-Encoding
782 2 md.transfer_encoding = {};
783 2 update_payload();
784 2 return;
785 }
786 // re-insert everything
787 3 --md.transfer_encoding.count;
788 3 on_insert_transfer_encoding();
789 }
790
791 // called when Upgrade is erased
792 void
793 4 header::
794 on_erase_upgrade()
795 {
796
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(
797 md.upgrade.count > 0);
798 4 --md.upgrade.count;
799
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(md.upgrade.count == 0)
800 {
801 // no Upgrade
802 2 md.upgrade = {};
803 2 return;
804 }
805 // reset and re-insert
806 2 auto n = md.upgrade.count;
807 2 auto const p = cbuf + prefix;
808 2 auto const* e = &tab()[0];
809 2 md.upgrade = {};
810
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 while(n > 0)
811 {
812
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(e->id == field::upgrade)
813
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 on_insert_upgrade(
814 core::string_view(
815 2 p + e->vp, e->vn));
816 2 --n;
817 2 --e;
818 }
819 }
820
821 //------------------------------------------------
822
823 // called when all fields with id are removed
824 void
825 51 header::
826 on_erase_all(
827 field id)
828 {
829
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 37 times.
51 if(kind == detail::kind::fields)
830 14 return;
831
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)
832 {
833 1 case field::connection:
834 1 md.connection = {};
835 1 return;
836
837 2 case field::content_length:
838 2 md.content_length = {};
839 2 update_payload();
840 2 return;
841
842 5 case field::expect:
843 5 md.expect = {};
844 5 update_payload();
845 5 return;
846
847 1 case field::transfer_encoding:
848 1 md.transfer_encoding = {};
849 1 update_payload();
850 1 return;
851
852 1 case field::upgrade:
853 1 md.upgrade = {};
854 1 return;
855
856 27 default:
857 27 break;
858 }
859 }
860
861 //------------------------------------------------
862
863 /* References:
864
865 3.3. Message Body
866 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3
867
868 3.3.1. Transfer-Encoding
869 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
870
871 3.3.2. Content-Length
872 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
873 */
874 void
875 2328 header::
876 update_payload() noexcept
877 {
878
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2328 times.
2328 BOOST_ASSERT(kind !=
879 detail::kind::fields);
880
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2328 times.
2328 if(md.payload_override)
881 {
882 // e.g. response to
883 // a HEAD request
884 return;
885 }
886
887 /* If there is an error in either Content-Length
888 or Transfer-Encoding, then the payload is
889 undefined. Clients should probably close the
890 connection. Servers can send a Bad Request
891 and avoid reading any payload bytes.
892 */
893
2/2
✓ Branch 1 taken 128 times.
✓ Branch 2 taken 2200 times.
2328 if(md.content_length.ec.failed())
894 {
895 // invalid Content-Length
896 128 md.payload = payload::error;
897 128 md.payload_size = 0;
898 128 return;
899 }
900
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 2192 times.
2200 if(md.transfer_encoding.ec.failed())
901 {
902 // invalid Transfer-Encoding
903 8 md.payload = payload::error;
904 8 md.payload_size = 0;
905 8 return;
906 }
907
908 /* A sender MUST NOT send a Content-Length
909 header field in any message that contains
910 a Transfer-Encoding header field.
911 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
912 */
913
2/2
✓ Branch 0 taken 427 times.
✓ Branch 1 taken 1765 times.
2192 if( md.content_length.count > 0 &&
914
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 424 times.
427 md.transfer_encoding.count > 0)
915 {
916 3 md.payload = payload::error;
917 3 md.payload_size = 0;
918 3 return;
919 }
920
921
2/2
✓ Branch 0 taken 579 times.
✓ Branch 1 taken 1610 times.
2189 if(kind == detail::kind::response)
922 579 goto do_response;
923
924 //--------------------------------------------
925
926 /* The presence of a message body in a
927 request is signaled by a Content-Length
928 or Transfer-Encoding header field. Request
929 message framing is independent of method
930 semantics, even if the method does not
931 define any use for a message body.
932 */
933
2/2
✓ Branch 0 taken 281 times.
✓ Branch 1 taken 1329 times.
1610 if(md.content_length.count > 0)
934 {
935
2/2
✓ Branch 0 taken 275 times.
✓ Branch 1 taken 6 times.
281 if(md.content_length.value > 0)
936 {
937 // non-zero Content-Length
938 275 md.payload = payload::size;
939 275 md.payload_size = md.content_length.value;
940 275 return;
941 }
942 // Content-Length: 0
943 6 md.payload = payload::none;
944 6 md.payload_size = 0;
945 6 return;
946 }
947
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 1315 times.
1329 if(md.transfer_encoding.is_chunked)
948 {
949 // chunked
950 14 md.payload = payload::chunked;
951 14 md.payload_size = 0;
952 14 return;
953 }
954 // no payload
955 1315 md.payload = payload::none;
956 1315 md.payload_size = 0;
957 1315 return;
958
959 //--------------------------------------------
960 579 do_response:
961
962
2/2
✓ Branch 0 taken 577 times.
✓ Branch 1 taken 2 times.
579 if( res.status_int / 100 == 1 || // 1xx e.g. Continue
963
2/2
✓ Branch 0 taken 575 times.
✓ Branch 1 taken 2 times.
577 res.status_int == 204 || // No Content
964
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 573 times.
575 res.status_int == 304) // Not Modified
965 {
966 /* The correctness of any Content-Length
967 here is defined by the particular
968 resource, and cannot be determined
969 here. In any case there is no payload.
970 */
971 6 md.payload = payload::none;
972 6 md.payload_size = 0;
973 6 return;
974 }
975
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 433 times.
573 if(md.content_length.count > 0)
976 {
977
2/2
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 11 times.
140 if(md.content_length.value > 0)
978 {
979 // Content-Length > 0
980 129 md.payload = payload::size;
981 129 md.payload_size = md.content_length.value;
982 129 return;
983 }
984 // Content-Length: 0
985 11 md.payload = payload::none;
986 11 md.payload_size = 0;
987 11 return;
988 }
989
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 429 times.
433 if(md.transfer_encoding.is_chunked)
990 {
991 // chunked
992 4 md.payload = payload::chunked;
993 4 md.payload_size = 0;
994 4 return;
995 }
996
997 // eof needed
998 429 md.payload = payload::to_eof;
999 429 md.payload_size = 0;
1000 }
1001
1002 //------------------------------------------------
1003
1004 std::size_t
1005 453 header::
1006 count_crlf(
1007 core::string_view s) noexcept
1008 {
1009 453 auto it = s.data();
1010 453 auto len = s.size();
1011 453 std::size_t n = 0;
1012
2/2
✓ Branch 0 taken 16049 times.
✓ Branch 1 taken 453 times.
16502 while(len >= 2)
1013 {
1014
2/2
✓ Branch 0 taken 1522 times.
✓ Branch 1 taken 14527 times.
16049 if( it[0] == '\r' &&
1015
1/2
✓ Branch 0 taken 1522 times.
✗ Branch 1 not taken.
1522 it[1] != '\r')
1016 {
1017
1/2
✓ Branch 0 taken 1522 times.
✗ Branch 1 not taken.
1522 if(it[1] == '\n')
1018 1522 n++;
1019 1522 it += 2;
1020 1522 len -= 2;
1021 }
1022 else
1023 {
1024 14527 it++;
1025 14527 len--;
1026 }
1027 }
1028 453 return n;
1029 }
1030
1031 static
1032 void
1033 3266 parse_start_line(
1034 header& h,
1035 header_limits const& lim,
1036 std::size_t new_size,
1037 system::error_code& ec) noexcept
1038 {
1039
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3266 times.
3266 BOOST_ASSERT(h.size == 0);
1040
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3266 times.
3266 BOOST_ASSERT(h.prefix == 0);
1041
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3266 times.
3266 BOOST_ASSERT(h.cbuf != nullptr);
1042
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3266 times.
3266 BOOST_ASSERT(
1043 h.kind != detail::kind::fields);
1044
1045 3266 auto const it0 = h.cbuf;
1046 3266 auto const end = it0 + new_size;
1047 3266 char const* it = it0;
1048
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3266 times.
3266 if( new_size > lim.max_start_line)
1049 new_size = lim.max_start_line;
1050
2/2
✓ Branch 0 taken 2683 times.
✓ Branch 1 taken 583 times.
3266 if(h.kind == detail::kind::request)
1051 {
1052 auto rv = grammar::parse(
1053 2683 it, end, request_line_rule);
1054
2/2
✓ Branch 1 taken 1404 times.
✓ Branch 2 taken 1279 times.
2683 if(! rv)
1055 {
1056 1404 ec = rv.error();
1057
2/4
✓ Branch 2 taken 1404 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1404 times.
2808 if( ec == grammar::error::need_more &&
1058
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1404 times.
1404 new_size == lim.max_start_line)
1059 ec = BOOST_HTTP_PROTO_ERR(
1060 error::start_line_limit);
1061 1404 return;
1062 }
1063 // method
1064 1279 auto sm = std::get<0>(*rv);
1065 1279 h.req.method = string_to_method(sm);
1066 1279 h.req.method_len =
1067 1279 static_cast<off_t>(sm.size());
1068 // target
1069 1279 auto st = std::get<1>(*rv);
1070 1279 h.req.target_len =
1071 1279 static_cast<off_t>(st.size());
1072 // version
1073
2/3
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 1259 times.
✗ Branch 4 not taken.
1279 switch(std::get<2>(*rv))
1074 {
1075 20 case 10:
1076 20 h.version =
1077 http_proto::version::http_1_0;
1078 20 break;
1079 1259 case 11:
1080 1259 h.version =
1081 http_proto::version::http_1_1;
1082 1259 break;
1083 default:
1084 {
1085 ec = BOOST_HTTP_PROTO_ERR(
1086 error::bad_version);
1087 return;
1088 }
1089 }
1090 }
1091 else
1092 {
1093 auto rv = grammar::parse(
1094 583 it, end, status_line_rule);
1095
2/2
✓ Branch 1 taken 151 times.
✓ Branch 2 taken 432 times.
583 if(! rv)
1096 {
1097 151 ec = rv.error();
1098
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 &&
1099
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 151 times.
151 new_size == lim.max_start_line)
1100 ec = BOOST_HTTP_PROTO_ERR(
1101 error::start_line_limit);
1102 151 return;
1103 }
1104 // version
1105
2/3
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 428 times.
✗ Branch 4 not taken.
432 switch(std::get<0>(*rv))
1106 {
1107 4 case 10:
1108 4 h.version =
1109 http_proto::version::http_1_0;
1110 4 break;
1111 428 case 11:
1112 428 h.version =
1113 http_proto::version::http_1_1;
1114 428 break;
1115 default:
1116 {
1117 ec = BOOST_HTTP_PROTO_ERR(
1118 error::bad_version);
1119 return;
1120 }
1121 }
1122 // status-code
1123 432 h.res.status_int =
1124 static_cast<unsigned short>(
1125 432 std::get<1>(*rv).v);
1126 432 h.res.status = std::get<1>(*rv).st;
1127 }
1128 1711 h.prefix = static_cast<off_t>(it - it0);
1129 1711 h.size = h.prefix;
1130 1711 h.on_start_line();
1131 }
1132
1133 // returns: true if we added a field
1134 static
1135 void
1136 5825 parse_field(
1137 header& h,
1138 header_limits const& lim,
1139 std::size_t new_size,
1140 system::error_code& ec) noexcept
1141 {
1142
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5825 times.
5825 if( new_size > lim.max_field)
1143 new_size = lim.max_field;
1144 5825 auto const it0 = h.cbuf + h.size;
1145 5825 auto const end = h.cbuf + new_size;
1146 5825 char const* it = it0;
1147 auto rv = grammar::parse(
1148 5825 it, end, field_rule);
1149
2/2
✓ Branch 1 taken 3343 times.
✓ Branch 2 taken 2482 times.
5825 if(rv.has_error())
1150 {
1151 3343 ec = rv.error();
1152
2/2
✓ Branch 2 taken 1780 times.
✓ Branch 3 taken 1563 times.
3343 if(ec == grammar::error::end_of_range)
1153 {
1154 // final CRLF
1155 1780 h.size = static_cast<
1156 1780 off_t>(it - h.cbuf);
1157 3343 return;
1158 }
1159
3/4
✓ Branch 2 taken 1435 times.
✓ Branch 3 taken 128 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1563 times.
2998 if( ec == grammar::error::need_more &&
1160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1435 times.
1435 new_size == lim.max_field)
1161 {
1162 ec = BOOST_HTTP_PROTO_ERR(
1163 error::field_size_limit);
1164 }
1165 1563 return;
1166 }
1167
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2482 times.
2482 if(h.count >= lim.max_fields)
1168 {
1169 ec = BOOST_HTTP_PROTO_ERR(
1170 error::fields_limit);
1171 return;
1172 }
1173
2/2
✓ Branch 1 taken 137 times.
✓ Branch 2 taken 2345 times.
2482 if(rv->has_obs_fold)
1174 {
1175 // obs fold not allowed in test views
1176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 137 times.
137 BOOST_ASSERT(h.buf != nullptr);
1177 137 remove_obs_fold(h.buf + h.size, it);
1178 }
1179 2482 auto id = string_to_field(rv->name);
1180 2482 h.size = static_cast<off_t>(it - h.cbuf);
1181
1182 // add field table entry
1183
1/2
✓ Branch 0 taken 2482 times.
✗ Branch 1 not taken.
2482 if(h.buf != nullptr)
1184 {
1185 4964 auto& e = header::table(
1186 2482 h.buf + h.cap)[h.count];
1187 2482 auto const base =
1188 2482 h.buf + h.prefix;
1189 2482 e.np = static_cast<off_t>(
1190 2482 rv->name.data() - base);
1191 2482 e.nn = static_cast<off_t>(
1192 2482 rv->name.size());
1193 2482 e.vp = static_cast<off_t>(
1194 2482 rv->value.data() - base);
1195 2482 e.vn = static_cast<off_t>(
1196 2482 rv->value.size());
1197 2482 e.id = id;
1198 }
1199 2482 ++h.count;
1200 2482 h.on_insert(id, rv->value);
1201 2482 ec = {};
1202 }
1203
1204 void
1205 4898 header::
1206 parse(
1207 std::size_t new_size,
1208 header_limits const& lim,
1209 system::error_code& ec) noexcept
1210 {
1211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4898 times.
4898 if( new_size > lim.max_size)
1212 new_size = lim.max_size;
1213
2/2
✓ Branch 0 taken 3463 times.
✓ Branch 1 taken 1435 times.
4898 if( this->prefix == 0 &&
1214
2/2
✓ Branch 0 taken 3266 times.
✓ Branch 1 taken 197 times.
3463 this->kind !=
1215 detail::kind::fields)
1216 {
1217 3266 parse_start_line(
1218 *this, lim, new_size, ec);
1219
2/2
✓ Branch 1 taken 1555 times.
✓ Branch 2 taken 1711 times.
3266 if(ec.failed())
1220 {
1221
2/4
✓ Branch 2 taken 1555 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1555 times.
3110 if( ec == grammar::error::need_more &&
1222
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1555 times.
1555 new_size == lim.max_fields)
1223 {
1224 ec = BOOST_HTTP_PROTO_ERR(
1225 error::headers_limit);
1226 }
1227 1555 return;
1228 }
1229 }
1230 for(;;)
1231 {
1232 5825 parse_field(
1233 *this, lim, new_size, ec);
1234
2/2
✓ Branch 1 taken 3343 times.
✓ Branch 2 taken 2482 times.
5825 if(ec.failed())
1235 {
1236
3/4
✓ Branch 2 taken 1435 times.
✓ Branch 3 taken 1908 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3343 times.
4778 if( ec == grammar::error::need_more &&
1237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1435 times.
1435 new_size == lim.max_size)
1238 {
1239 ec = BOOST_HTTP_PROTO_ERR(
1240 error::headers_limit);
1241 return;
1242 }
1243 3343 break;
1244 }
1245 2482 }
1246
2/2
✓ Branch 2 taken 1780 times.
✓ Branch 3 taken 1563 times.
3343 if(ec == grammar::error::end_of_range)
1247 1780 ec = {};
1248 }
1249
1250 } // detail
1251 } // http_proto
1252 } // boost
1253
1254 #endif
1255