src/detail/header.cpp

92.7% Lines (569/0/614) 82.5% List of functions (47/0/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 13154x 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 3048x 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 10596x 100.0% 100.0% boost::http::detail::header::on_insert(boost::http::field, boost::core::basic_string_view<char>) :423 11001x 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 7x 100.0% 79.0% boost::http::detail::header::on_insert_content_encoding(boost::core::basic_string_view<char>)::{lambda()#1}::operator()() const :640 1x 90.0% 100.0% boost::http::detail::header::on_insert_upgrade(boost::core::basic_string_view<char>) :686 26x 100.0% 93.0% boost::http::detail::header::on_insert_upgrade(boost::core::basic_string_view<char>)::{lambda()#1}::operator()() const :697 1x 100.0% 100.0% boost::http::detail::header::on_insert_upgrade(boost::core::basic_string_view<char>)::{lambda()#2}::operator()() const :707 3x 100.0% 100.0% boost::http::detail::header::on_erase_connection() :730 9x 100.0% 92.0% boost::http::detail::header::on_erase_content_length() :754 4x 100.0% 95.0% boost::http::detail::header::on_erase_expect() :792 10x 100.0% 94.0% boost::http::detail::header::on_erase_transfer_encoding() :832 4x 100.0% 92.0% boost::http::detail::header::on_erase_content_encoding() :856 0 0.0% 0.0% boost::http::detail::header::on_erase_upgrade() :876 4x 100.0% 93.0% boost::http::detail::header::on_erase_all(boost::http::field) :908 72x 100.0% 100.0% boost::http::detail::header::update_payload() :958 19539x 98.2% 94.0% boost::http::detail::header::count_crlf(boost::core::basic_string_view<char>) :1088 549x 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&) :1116 27541x 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 :1142 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 :1168 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 :1183 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 :1200 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&) :1219 36564x 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 :1245 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 :1252 0 92.0% 0.0% boost::http::detail::header::parse(unsigned long, boost::http::header_limits const&, boost::system::error_code&) :1289 42893x 100.0% 76.0% boost::http::detail::header::parse(unsigned long, boost::http::header_limits const&, boost::system::error_code&)::{lambda()#1}::operator()() const :1308 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 :1323 0 66.7% 0.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2019 Vinnie Falco ([email protected])
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 13154x header::
125 13154x header(empty v) noexcept
126 13154x : kind(v.param)
127 {
128 13154x }
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 3048x 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 3048x if(size < 19)
214 2280x size = 19;
215
216 // align size up to alignof(entry)
217 3048x size = (size + alignof(entry) - 1) & ~(alignof(entry) - 1);
218
219 3048x 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 10596x 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 10596x update_payload();
419 10596x }
420
421 // called after a field is inserted
422 void
423 11001x header::
424 on_insert(
425 field id,
426 core::string_view v)
427 {
428 11001x if(kind == detail::kind::fields)
429 482x return;
430 10519x switch(id)
431 {
432 7x case field::content_encoding:
433 7x 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 7x header::
628 on_insert_content_encoding(
629 core::string_view v)
630 {
631 7x ++md.content_encoding.count;
632 7x if(md.content_encoding.ec)
633 3x return;
634
635 auto rv = grammar::parse(
636 7x v, list_rule(token_rule, 1));
637 7x 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 6x 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 8x if(grammar::ci_is_equal(
655 8x *rv->begin(), "deflate"))
656 {
657 md.content_encoding.coding =
658 content_coding::deflate;
659 }
660 8x else if(grammar::ci_is_equal(
661 8x *rv->begin(), "gzip"))
662 {
663 2x md.content_encoding.coding =
664 content_coding::gzip;
665 }
666 4x else if(grammar::ci_is_equal(
667 4x *rv->begin(), "br"))
668 {
669 1x md.content_encoding.coding =
670 content_coding::br;
671 }
672 2x else if(grammar::ci_is_equal(
673 2x *rv->begin(), "zstd"))
674 {
675 1x md.content_encoding.coding =
676 content_coding::zstd;
677 }
678 else
679 {
680 md.content_encoding.coding =
681 content_coding::unknown;
682 }
683 7x }
684
685 void
686 26x header::
687 on_insert_upgrade(
688 core::string_view v)
689 {
690 26x ++md.upgrade.count;
691 26x if(md.upgrade.ec)
692 5x return;
693 25x if( version !=
694 http::version::http_1_1)
695 {
696 1x md.upgrade.ec =
697 2x BOOST_HTTP_ERR(
698 error::bad_upgrade);
699 1x md.upgrade.websocket = false;
700 1x return;
701 }
702 auto rv = grammar::parse(
703 24x v, upgrade_rule);
704 24x if(! rv)
705 {
706 3x md.upgrade.ec =
707 6x BOOST_HTTP_ERR(
708 error::bad_upgrade);
709 3x md.upgrade.websocket = false;
710 3x return;
711 }
712 21x if(! md.upgrade.websocket)
713 {
714 23x for(auto t : *rv)
715 {
716 16x if( grammar::ci_is_equal(
717 26x t.name, "websocket") &&
718 10x t.version.empty())
719 {
720 9x md.upgrade.websocket = true;
721 9x break;
722 }
723 }
724 }
725 24x }
726
727 //------------------------------------------------
728
729 void
730 9x header::
731 on_erase_connection()
732 {
733 9x BOOST_ASSERT(
734 md.connection.count > 0);
735 // reset and re-insert
736 9x auto n = md.connection.count - 1;
737 9x auto const p = cbuf + prefix;
738 9x auto const* e = &tab()[0];
739 9x md.connection = {};
740 17x while(n > 0)
741 {
742 8x if(e->id == field::connection)
743 {
744 7x on_insert_connection(
745 core::string_view(
746 7x p + e->vp, e->vn));
747 7x --n;
748 }
749 8x --e;
750 }
751 9x }
752
753 void
754 4x header::
755 on_erase_content_length()
756 {
757 4x BOOST_ASSERT(
758 md.content_length.count > 0);
759 4x --md.content_length.count;
760 4x if(md.content_length.count == 0)
761 {
762 // no Content-Length
763 1x md.content_length = {};
764 1x update_payload();
765 1x return;
766 }
767 3x if(! md.content_length.ec)
768 {
769 // removing a duplicate value
770 2x return;
771 }
772 // reset and re-insert
773 1x auto n = md.content_length.count;
774 1x auto const p = cbuf + prefix;
775 1x auto const* e = &tab()[0];
776 1x md.content_length = {};
777 2x while(n > 0)
778 {
779 1x if(e->id == field::content_length)
780 {
781 1x on_insert_content_length(
782 core::string_view(
783 1x p + e->vp, e->vn));
784 1x --n;
785 }
786 1x --e;
787 }
788 1x update_payload();
789 }
790
791 void
792 10x header::
793 on_erase_expect()
794 {
795 10x BOOST_ASSERT(
796 md.expect.count > 0);
797 10x --md.expect.count;
798 10x if(kind != detail::kind::request)
799 1x return;
800 9x if(md.expect.count == 0)
801 {
802 // no Expect
803 3x md.expect = {};
804 3x return;
805 }
806 // VFALCO This should be uncommented
807 // if we want to allow multiple Expect
808 // fields with the value 100-continue
809 /*
810 if(! md.expect.ec)
811 return;
812 */
813 // reset and re-insert
814 6x auto n = md.expect.count;
815 6x auto const p = cbuf + prefix;
816 6x auto const* e = &tab()[0];
817 6x md.expect = {};
818 18x while(n > 0)
819 {
820 12x if(e->id == field::expect)
821 {
822 6x on_insert_expect(
823 core::string_view(
824 6x p + e->vp, e->vn));
825 6x --n;
826 }
827 12x --e;
828 }
829 }
830
831 void
832 4x header::
833 on_erase_transfer_encoding()
834 {
835 4x BOOST_ASSERT(
836 md.transfer_encoding.count > 0);
837 // reset and re-insert
838 4x auto n = md.transfer_encoding.count - 1;
839 4x auto const p = cbuf + prefix;
840 4x auto const* e = &tab()[0];
841 4x md.transfer_encoding = {};
842 7x while(n > 0)
843 {
844 3x if(e->id == field::transfer_encoding)
845 {
846 2x on_insert_transfer_encoding(
847 core::string_view(
848 2x p + e->vp, e->vn));
849 2x --n;
850 }
851 3x --e;
852 }
853 4x }
854
855 void
856 header::
857 on_erase_content_encoding()
858 {
859 BOOST_ASSERT(
860 md.content_encoding.count > 0);
861 --md.content_encoding.count;
862 if(md.content_encoding.count == 0)
863 {
864 // no Content-Encoding
865 md.content_encoding = {};
866 return;
867 }
868 // re-insert everything
869 --md.content_encoding.count;
870 // TODO
871 // on_insert_content_encoding();
872 }
873
874 // called when Upgrade is erased
875 void
876 4x header::
877 on_erase_upgrade()
878 {
879 4x BOOST_ASSERT(
880 md.upgrade.count > 0);
881 4x --md.upgrade.count;
882 4x if(md.upgrade.count == 0)
883 {
884 // no Upgrade
885 2x md.upgrade = {};
886 2x return;
887 }
888 // reset and re-insert
889 2x auto n = md.upgrade.count;
890 2x auto const p = cbuf + prefix;
891 2x auto const* e = &tab()[0];
892 2x md.upgrade = {};
893 4x while(n > 0)
894 {
895 2x if(e->id == field::upgrade)
896 2x on_insert_upgrade(
897 core::string_view(
898 2x p + e->vp, e->vn));
899 2x --n;
900 2x --e;
901 }
902 }
903
904 //------------------------------------------------
905
906 // called when all fields with id are removed
907 void
908 72x header::
909 on_erase_all(
910 field id)
911 {
912 72x if(kind == detail::kind::fields)
913 21x return;
914 51x switch(id)
915 {
916 3x case field::connection:
917 3x md.connection = {};
918 3x return;
919
920 2x case field::content_length:
921 2x md.content_length = {};
922 2x update_payload();
923 2x return;
924
925 5x case field::expect:
926 5x md.expect = {};
927 5x update_payload();
928 5x return;
929
930 1x case field::transfer_encoding:
931 1x md.transfer_encoding = {};
932 1x update_payload();
933 1x return;
934
935 1x case field::upgrade:
936 1x md.upgrade = {};
937 1x return;
938
939 39x default:
940 39x break;
941 }
942 }
943
944 //------------------------------------------------
945
946 /* References:
947
948 3.3. Message Body
949 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3
950
951 3.3.1. Transfer-Encoding
952 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
953
954 3.3.2. Content-Length
955 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
956 */
957 void
958 19539x header::
959 update_payload() noexcept
960 {
961 19539x BOOST_ASSERT(kind !=
962 detail::kind::fields);
963 19539x if(md.payload_override)
964 {
965 // e.g. response to
966 // a HEAD request
967 return;
968 }
969
970 /* If there is an error in either Content-Length
971 or Transfer-Encoding, then the payload is
972 undefined. Clients should probably close the
973 connection. Servers can send a Bad Request
974 and avoid reading any payload bytes.
975 */
976 19539x if(md.content_length.ec)
977 {
978 // invalid Content-Length
979 68x md.payload = payload::error;
980 68x md.payload_size = 0;
981 68x return;
982 }
983 19471x if(md.transfer_encoding.ec)
984 {
985 // invalid Transfer-Encoding
986 8x md.payload = payload::error;
987 8x md.payload_size = 0;
988 8x return;
989 }
990
991 /* A sender MUST NOT send a Content-Length
992 header field in any message that contains
993 a Transfer-Encoding header field.
994 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
995 */
996 19463x if( md.content_length.count > 0 &&
997 4455x md.transfer_encoding.count > 0)
998 {
999 3x md.payload = payload::error;
1000 3x md.payload_size = 0;
1001 3x return;
1002 }
1003
1004 19460x if(kind == detail::kind::response)
1005 1822x goto do_response;
1006
1007 //--------------------------------------------
1008
1009 /* The presence of a message body in a
1010 request is signaled by a Content-Length
1011 or Transfer-Encoding header field. Request
1012 message framing is independent of method
1013 semantics, even if the method does not
1014 define any use for a message body.
1015 */
1016 17638x if(md.content_length.count > 0)
1017 {
1018 4177x if(md.content_length.value > 0)
1019 {
1020 // non-zero Content-Length
1021 4150x md.payload = payload::size;
1022 4150x md.payload_size = md.content_length.value;
1023 4150x return;
1024 }
1025 // Content-Length: 0
1026 27x md.payload = payload::none;
1027 27x md.payload_size = 0;
1028 27x return;
1029 }
1030 13461x if(md.transfer_encoding.is_chunked)
1031 {
1032 // chunked
1033 4012x md.payload = payload::chunked;
1034 4012x md.payload_size = 0;
1035 4012x return;
1036 }
1037 // no payload
1038 9449x md.payload = payload::none;
1039 9449x md.payload_size = 0;
1040 9449x return;
1041
1042 //--------------------------------------------
1043 1822x do_response:
1044
1045 1822x if( res.status_int / 100 == 1 || // 1xx e.g. Continue
1046 1811x res.status_int == 204 || // No Content
1047 1808x res.status_int == 304) // Not Modified
1048 {
1049 /* The correctness of any Content-Length
1050 here is defined by the particular
1051 resource, and cannot be determined
1052 here. In any case there is no payload.
1053 */
1054 16x md.payload = payload::none;
1055 16x md.payload_size = 0;
1056 16x return;
1057 }
1058 1806x if(md.content_length.count > 0)
1059 {
1060 272x if(md.content_length.value > 0)
1061 {
1062 // Content-Length > 0
1063 253x md.payload = payload::size;
1064 253x md.payload_size = md.content_length.value;
1065 253x return;
1066 }
1067 // Content-Length: 0
1068 19x md.payload = payload::none;
1069 19x md.payload_size = 0;
1070 19x return;
1071 }
1072 1534x if(md.transfer_encoding.is_chunked)
1073 {
1074 // chunked
1075 377x md.payload = payload::chunked;
1076 377x md.payload_size = 0;
1077 377x return;
1078 }
1079
1080 // eof needed
1081 1157x md.payload = payload::to_eof;
1082 1157x md.payload_size = 0;
1083 }
1084
1085 //------------------------------------------------
1086
1087 std::size_t
1088 549x header::
1089 count_crlf(
1090 core::string_view s) noexcept
1091 {
1092 549x auto it = s.data();
1093 549x auto len = s.size();
1094 549x std::size_t n = 0;
1095 19120x while(len >= 2)
1096 {
1097 18571x if( it[0] == '\r' &&
1098 1749x it[1] != '\r')
1099 {
1100 1749x if(it[1] == '\n')
1101 1749x n++;
1102 1749x it += 2;
1103 1749x len -= 2;
1104 }
1105 else
1106 {
1107 16822x it++;
1108 16822x len--;
1109 }
1110 }
1111 549x return n;
1112 }
1113
1114 static
1115 void
1116 27541x parse_start_line(
1117 header& h,
1118 header_limits const& lim,
1119 std::size_t new_size,
1120 system::error_code& ec) noexcept
1121 {
1122 27541x BOOST_ASSERT(h.size == 0);
1123 27541x BOOST_ASSERT(h.prefix == 0);
1124 27541x BOOST_ASSERT(h.cbuf != nullptr);
1125 27541x BOOST_ASSERT(
1126 h.kind != detail::kind::fields);
1127
1128 27541x auto const it0 = h.cbuf;
1129 27541x auto const end = it0 + new_size;
1130 27541x char const* it = it0;
1131 27541x if( new_size > lim.max_start_line)
1132 10x new_size = lim.max_start_line;
1133 27541x if(h.kind == detail::kind::request)
1134 {
1135 auto rv = grammar::parse(
1136 11375x it, end, request_line_rule);
1137 11375x if(! rv)
1138 {
1139 1991x ec = rv.error();
1140 3982x if( ec == grammar::error::need_more &&
1141 1991x new_size == lim.max_start_line)
1142 ec = BOOST_HTTP_ERR(
1143 error::start_line_limit);
1144 1991x return;
1145 }
1146 // method
1147 9384x auto sm = std::get<0>(*rv);
1148 9384x h.req.method = string_to_method(sm);
1149 9384x h.req.method_len =
1150 9384x static_cast<header::offset_type>(sm.size());
1151 // target
1152 9384x auto st = std::get<1>(*rv);
1153 9384x h.req.target_len =
1154 9384x static_cast<header::offset_type>(st.size());
1155 // version
1156 9384x switch(std::get<2>(*rv))
1157 {
1158 25x case 10:
1159 25x h.version =
1160 http::version::http_1_0;
1161 25x break;
1162 9359x case 11:
1163 9359x h.version =
1164 http::version::http_1_1;
1165 9359x break;
1166 default:
1167 {
1168 ec = BOOST_HTTP_ERR(
1169 error::bad_version);
1170 return;
1171 }
1172 }
1173 }
1174 else
1175 {
1176 auto rv = grammar::parse(
1177 16166x it, end, status_line_rule);
1178 16166x if(! rv)
1179 {
1180 15022x ec = rv.error();
1181 30044x if( ec == grammar::error::need_more &&
1182 15022x new_size == lim.max_start_line)
1183 ec = BOOST_HTTP_ERR(
1184 error::start_line_limit);
1185 15022x return;
1186 }
1187 // version
1188 1144x switch(std::get<0>(*rv))
1189 {
1190 4x case 10:
1191 4x h.version =
1192 http::version::http_1_0;
1193 4x break;
1194 1140x case 11:
1195 1140x h.version =
1196 http::version::http_1_1;
1197 1140x break;
1198 default:
1199 {
1200 ec = BOOST_HTTP_ERR(
1201 error::bad_version);
1202 return;
1203 }
1204 }
1205 // status-code
1206 1144x h.res.status_int =
1207 static_cast<unsigned short>(
1208 1144x std::get<1>(*rv).v);
1209 1144x h.res.status = std::get<1>(*rv).st;
1210 }
1211 10528x h.prefix = static_cast<header::offset_type>(it - it0);
1212 10528x h.size = h.prefix;
1213 10528x h.on_start_line();
1214 }
1215
1216 // returns: true if we added a field
1217 static
1218 void
1219 36564x parse_field(
1220 header& h,
1221 header_limits const& lim,
1222 std::size_t new_size,
1223 system::error_code& ec) noexcept
1224 {
1225 36564x if( new_size > lim.max_field)
1226 20x new_size = lim.max_field;
1227 36564x auto const it0 = h.cbuf + h.size;
1228 36564x auto const end = h.cbuf + new_size;
1229 36564x char const* it = it0;
1230 36564x auto rv = grammar::parse(
1231 it, end, field_rule);
1232 36564x if(rv.has_error())
1233 {
1234 25880x ec = rv.error();
1235 25880x if(ec == grammar::error::end_of_range)
1236 {
1237 // final CRLF
1238 10103x h.size = static_cast<
1239 10103x header::offset_type>(it - h.cbuf);
1240 25880x return;
1241 }
1242 31295x if( ec == grammar::error::need_more &&
1243 15518x new_size == lim.max_field)
1244 {
1245 ec = BOOST_HTTP_ERR(
1246 error::field_size_limit);
1247 }
1248 15777x return;
1249 }
1250 10684x if(h.count >= lim.max_fields)
1251 {
1252 ec = BOOST_HTTP_ERR(
1253 error::fields_limit);
1254 return;
1255 }
1256 10684x if(rv->has_obs_fold)
1257 {
1258 // obs fold not allowed in test views
1259 210x BOOST_ASSERT(h.buf != nullptr);
1260 210x remove_obs_fold(h.buf + h.size, it);
1261 }
1262 10684x auto id = string_to_field(rv->name)
1263 10684x .value_or(header::unknown_field);
1264 10684x h.size = static_cast<header::offset_type>(it - h.cbuf);
1265
1266 // add field table entry
1267 10684x if(h.buf != nullptr)
1268 {
1269 21368x auto& e = header::table(
1270 10684x h.buf + h.cap)[h.count];
1271 10684x auto const base =
1272 10684x h.buf + h.prefix;
1273 10684x e.np = static_cast<header::offset_type>(
1274 10684x rv->name.data() - base);
1275 10684x e.nn = static_cast<header::offset_type>(
1276 10684x rv->name.size());
1277 10684x e.vp = static_cast<header::offset_type>(
1278 10684x rv->value.data() - base);
1279 10684x e.vn = static_cast<header::offset_type>(
1280 10684x rv->value.size());
1281 10684x e.id = id;
1282 }
1283 10684x ++h.count;
1284 10684x h.on_insert(id, rv->value);
1285 10684x ec = {};
1286 }
1287
1288 void
1289 42893x header::
1290 parse(
1291 std::size_t new_size,
1292 header_limits const& lim,
1293 system::error_code& ec) noexcept
1294 {
1295 42893x if( new_size > lim.max_size)
1296 10x new_size = lim.max_size;
1297 42893x if( this->prefix == 0 &&
1298 27775x this->kind !=
1299 detail::kind::fields)
1300 {
1301 27541x parse_start_line(
1302 *this, lim, new_size, ec);
1303 27541x if(ec)
1304 {
1305 34026x if( ec == grammar::error::need_more &&
1306 17013x new_size == lim.max_fields)
1307 {
1308 ec = BOOST_HTTP_ERR(
1309 error::headers_limit);
1310 }
1311 17013x return;
1312 }
1313 }
1314 for(;;)
1315 {
1316 36564x parse_field(
1317 *this, lim, new_size, ec);
1318 36564x if(ec)
1319 {
1320 41398x if( ec == grammar::error::need_more &&
1321 15518x new_size == lim.max_size)
1322 {
1323 ec = BOOST_HTTP_ERR(
1324 error::headers_limit);
1325 return;
1326 }
1327 25880x break;
1328 }
1329 10684x }
1330 25880x if(ec == grammar::error::end_of_range)
1331 10103x ec = {};
1332 }
1333
1334 } // detail
1335 } // http
1336 } // boost
1337