GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/detail/impl/header.ipp
Date: 2023-01-15 07:18:31
Exec Total Coverage
Lines: 517 540 95.7%
Functions: 45 46 97.8%
Branches: 206 243 84.8%

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