src/detail/header.cpp

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