src/detail/header.cpp

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