GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/detail/header.cpp
Date: 2024-09-20 16:11:52
Exec Total Coverage
Lines: 561 596 94.1%
Functions: 47 56 83.9%
Branches: 262 317 82.6%

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