src/detail/parser.cpp
96.5% Lines (384/398)
100.0% List of functions (51/51)
86.7% Branches (170/196)
Functions (51)
Function
Calls
Lines
Branches
Blocks
boost::burl::detail::(anonymous namespace)::chained_sequence::chained_sequence(std::array<boost::capy::const_buffer, 2ul> const&)
:56
198x
100.0%
–
100.0%
boost::burl::detail::(anonymous namespace)::chained_sequence::chained_sequence(char const*, unsigned long)
:64
61x
100.0%
–
100.0%
boost::burl::detail::(anonymous namespace)::chained_sequence::next()
:73
1115x
100.0%
100.0%
100.0%
boost::burl::detail::(anonymous namespace)::chained_sequence::advance(unsigned long)
:95
87x
100.0%
100.0%
100.0%
boost::burl::detail::(anonymous namespace)::chained_sequence::empty() const
:111
1030x
100.0%
–
100.0%
boost::burl::detail::(anonymous namespace)::chained_sequence::value() const
:117
1194x
100.0%
–
100.0%
boost::burl::detail::(anonymous namespace)::chained_sequence::pos() const
:123
20x
100.0%
–
100.0%
boost::burl::detail::(anonymous namespace)::chained_sequence::size() const
:129
606x
100.0%
–
100.0%
boost::burl::detail::(anonymous namespace)::chained_sequence::prefix(unsigned long) const
:135
170x
100.0%
100.0%
100.0%
boost::burl::detail::(anonymous namespace)::skip_to_eol(boost::burl::detail::(anonymous namespace)::chained_sequence&)
:146
171x
90.9%
87.5%
86.0%
boost::burl::detail::(anonymous namespace)::parse_chunk_header(boost::burl::detail::(anonymous namespace)::chained_sequence&, unsigned long&)
:165
177x
92.9%
87.5%
87.0%
boost::burl::detail::(anonymous namespace)::skip_trailer(boost::burl::detail::(anonymous namespace)::chained_sequence&)
:190
58x
92.3%
90.0%
89.0%
boost::burl::detail::(anonymous namespace)::skip_crlf(boost::burl::detail::(anonymous namespace)::chained_sequence&)
:212
209x
85.7%
62.5%
80.0%
boost::burl::detail::(anonymous namespace)::collect(std::span<boost::capy::const_buffer, 18446744073709551615ul>, std::array<boost::capy::const_buffer, 2ul> const&, unsigned long)
:223
60x
100.0%
75.0%
100.0%
boost::burl::detail::(anonymous namespace)::move_leftovers(char*, std::array<boost::capy::const_buffer, 2ul> const&)
:241
208x
50.0%
25.0%
73.0%
decltype ({parm#1}) boost::burl::detail::(anonymous namespace)::prefix<boost::capy::mutable_buffer>(boost::capy::mutable_buffer, unsigned long)
:267
25x
100.0%
–
100.0%
boost::burl::detail::parser::chunk_fn::chunk_fn<boost::burl::detail::parser::consume(unsigned long)::{lambda(boost::capy::const_buffer, bool)#1}>(boost::burl::detail::parser::consume(unsigned long)::{lambda(boost::capy::const_buffer, bool)#1}&&)
:283
26x
100.0%
–
100.0%
boost::burl::detail::parser::chunk_fn::chunk_fn<boost::burl::detail::parser::decode_some(std::span<boost::capy::mutable_buffer const, 18446744073709551615ul>)::{lambda(boost::capy::const_buffer, bool)#1}&>(boost::burl::detail::parser::decode_some(std::span<boost::capy::mutable_buffer const, 18446744073709551615ul>)::{lambda(boost::capy::const_buffer, bool)#1}&)
:283
5x
100.0%
–
100.0%
boost::burl::detail::parser::chunk_fn::chunk_fn<boost::burl::detail::parser::do_read_some(std::span<boost::capy::mutable_buffer const, 18446744073709551615ul>)::{lambda(boost::capy::const_buffer, bool)#1}>(boost::burl::detail::parser::do_read_some(std::span<boost::capy::mutable_buffer const, 18446744073709551615ul>)::{lambda(boost::capy::const_buffer, bool)#1}&&)
:283
93x
100.0%
–
100.0%
boost::burl::detail::parser::chunk_fn::chunk_fn<boost::burl::detail::parser::pull(std::span<boost::capy::const_buffer, 18446744073709551615ul>)::{lambda(boost::capy::const_buffer, bool)#1}>(boost::burl::detail::parser::pull(std::span<boost::capy::const_buffer, 18446744073709551615ul>)::{lambda(boost::capy::const_buffer, bool)#1}&&)
:283
74x
100.0%
–
100.0%
boost::burl::detail::parser::chunk_fn::operator()(boost::capy::const_buffer, bool) const
:296
106x
100.0%
–
100.0%
boost::burl::detail::parser::parser(boost::burl::detail::parser::config const&, boost::http::detail::kind, boost::capy::any_read_stream)
:302
192x
100.0%
100.0%
86.0%
boost::burl::detail::parser::got_header() const
:329
142x
100.0%
–
100.0%
boost::burl::detail::parser::got_body() const
:336
228x
100.0%
–
100.0%
boost::burl::detail::parser::has_buffered_data() const
:343
89x
100.0%
100.0%
100.0%
boost::burl::detail::parser::raw_limit_rem() const
:360
416x
100.0%
75.0%
86.0%
boost::burl::detail::parser::dec_limit_rem() const
:370
26x
100.0%
75.0%
86.0%
boost::burl::detail::parser::payload_sized() const
:380
1669x
100.0%
–
100.0%
boost::burl::detail::parser::payload_rem() const
:387
460x
100.0%
50.0%
86.0%
boost::burl::detail::parser::table_reserve() const
:395
658x
100.0%
–
100.0%
boost::burl::detail::parser::start(bool)
:403
208x
100.0%
80.0%
96.0%
boost::burl::detail::parser::reset(boost::capy::any_read_stream)
:439
50x
100.0%
–
100.0%
boost::burl::detail::parser::refill()
:466
987x
100.0%
100.0%
44.0%
boost::burl::detail::parser::walk_chunks(boost::burl::detail::parser::chunk_fn, bool)
:491
198x
100.0%
96.8%
93.0%
boost::burl::detail::parser::flatten_chunks()
:558
61x
85.7%
50.0%
85.0%
boost::burl::detail::parser::flatten_chunks()::{lambda(std::error_code)#1}::operator()(std::error_code) const
:570
61x
96.9%
92.9%
100.0%
boost::burl::detail::parser::read_header()
:618
498x
100.0%
100.0%
44.0%
boost::burl::detail::parser::set_decoder(boost::burl::detail::parser::decoder*)
:672
39x
100.0%
50.0%
67.0%
boost::burl::detail::parser::set_body_limit(unsigned long)
:680
4x
100.0%
–
100.0%
boost::burl::detail::parser::read_body()
:687
55x
100.0%
100.0%
44.0%
boost::burl::detail::parser::get_response() const
:768
299x
100.0%
50.0%
80.0%
boost::burl::detail::parser::get_request() const
:777
2x
100.0%
50.0%
80.0%
boost::burl::detail::parser::decode_some(std::span<boost::capy::mutable_buffer const, 18446744073709551615ul>)
:786
40x
100.0%
100.0%
44.0%
boost::burl::detail::parser::decode_some(std::span<boost::capy::mutable_buffer const, 18446744073709551615ul>)::{lambda(boost::capy::const_buffer, bool)#1}::operator()(boost::capy::const_buffer, bool) const
:796
44x
100.0%
96.6%
90.0%
boost::burl::detail::parser::do_read_some(std::span<boost::capy::mutable_buffer const, 18446744073709551615ul>)
:885
143x
100.0%
100.0%
44.0%
boost::burl::detail::parser::do_read_some(std::span<boost::capy::mutable_buffer const, 18446744073709551615ul>)::{lambda(unsigned long)#1}::operator()(unsigned long) const
:903
22x
100.0%
–
100.0%
boost::burl::detail::parser::do_read_some(std::span<boost::capy::mutable_buffer const, 18446744073709551615ul>)::{lambda(boost::capy::const_buffer, bool)#1}::operator()(boost::capy::const_buffer, bool) const
:927
37x
100.0%
100.0%
94.0%
boost::burl::detail::parser::pull(std::span<boost::capy::const_buffer, 18446744073709551615ul>)
:1002
119x
100.0%
100.0%
44.0%
boost::burl::detail::parser::pull(std::span<boost::capy::const_buffer, 18446744073709551615ul>)::{lambda(boost::capy::const_buffer, bool)#1}::operator()(boost::capy::const_buffer, bool) const
:1035
32x
100.0%
86.7%
93.0%
boost::burl::detail::parser::consume(unsigned long)
:1096
86x
100.0%
100.0%
100.0%
boost::burl::detail::parser::consume(unsigned long)::{lambda(boost::capy::const_buffer, bool)#1}::operator()(boost::capy::const_buffer, bool) const
:1106
30x
100.0%
100.0%
100.0%
| Line | Branch | TLA | Hits | Source Code |
|---|---|---|---|---|
| 1 | // | |||
| 2 | // Copyright (c) 2026 Mohammad Nejati | |||
| 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/burl | |||
| 8 | // | |||
| 9 | ||||
| 10 | #include <boost/burl/detail/parser.hpp> | |||
| 11 | ||||
| 12 | #include "util.hpp" | |||
| 13 | ||||
| 14 | #include <boost/assert.hpp> | |||
| 15 | #include <boost/capy/buffers/buffer_slice.hpp> | |||
| 16 | #include <boost/capy/buffers/consuming_buffers.hpp> | |||
| 17 | #include <boost/capy/buffers/front.hpp> | |||
| 18 | #include <boost/capy/buffers/make_buffer.hpp> | |||
| 19 | #include <boost/capy/error.hpp> | |||
| 20 | #include <boost/url/grammar/error.hpp> | |||
| 21 | #include <boost/url/grammar/hexdig_chars.hpp> | |||
| 22 | ||||
| 23 | #include <cstring> | |||
| 24 | #include <new> | |||
| 25 | #include <type_traits> | |||
| 26 | #include <utility> | |||
| 27 | ||||
| 28 | namespace boost | |||
| 29 | { | |||
| 30 | namespace burl | |||
| 31 | { | |||
| 32 | namespace detail | |||
| 33 | { | |||
| 34 | ||||
| 35 | using http::error::bad_payload; | |||
| 36 | using http::error::body_too_large; | |||
| 37 | using http::error::in_place_overflow; | |||
| 38 | using http::error::incomplete; | |||
| 39 | using http::error::need_data; | |||
| 40 | using http::condition::need_more_input; | |||
| 41 | ||||
| 42 | using header = http::detail::header; | |||
| 43 | using payload = http::payload; | |||
| 44 | ||||
| 45 | namespace | |||
| 46 | { | |||
| 47 | ||||
| 48 | class chained_sequence | |||
| 49 | { | |||
| 50 | char const* pos_; | |||
| 51 | char const* end_; | |||
| 52 | char const* begin_b_; | |||
| 53 | char const* end_b_; | |||
| 54 | ||||
| 55 | public: | |||
| 56 | 198x | chained_sequence(std::array<capy::const_buffer, 2> const& cbp) | ||
| 57 | 198x | : pos_(static_cast<char const*>(cbp[0].data())) | ||
| 58 | 198x | , end_(pos_ + cbp[0].size()) | ||
| 59 | 198x | , begin_b_(static_cast<char const*>(cbp[1].data())) | ||
| 60 | 198x | , end_b_(begin_b_ + cbp[1].size()) | ||
| 61 | { | |||
| 62 | 198x | } | ||
| 63 | ||||
| 64 | 61x | chained_sequence(char const* p, std::size_t n) | ||
| 65 | 61x | : pos_(p) | ||
| 66 | 61x | , end_(p + n) | ||
| 67 | 61x | , begin_b_(end_) | ||
| 68 | 61x | , end_b_(end_) | ||
| 69 | { | |||
| 70 | 61x | } | ||
| 71 | ||||
| 72 | char const* | |||
| 73 | 1115x | next() noexcept | ||
| 74 | { | |||
| 75 | 1115x | ++pos_; | ||
| 76 | // most frequently taken branch | |||
| 77 |
2/2✓ Branch 0 taken 996 times.
✓ Branch 1 taken 119 times.
|
1115x | if(pos_ < end_) | |
| 78 | 996x | return pos_; | ||
| 79 | ||||
| 80 | // bring the second range | |||
| 81 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 117 times.
|
119x | if(begin_b_ != end_b_) | |
| 82 | { | |||
| 83 | 2x | pos_ = begin_b_; | ||
| 84 | 2x | end_ = end_b_; | ||
| 85 | 2x | begin_b_ = end_b_; | ||
| 86 | 2x | return pos_; | ||
| 87 | } | |||
| 88 | ||||
| 89 | // undo the increament | |||
| 90 | 117x | pos_ = end_; | ||
| 91 | 117x | return nullptr; | ||
| 92 | } | |||
| 93 | ||||
| 94 | void | |||
| 95 | 87x | advance(std::size_t n) noexcept | ||
| 96 | { | |||
| 97 | 87x | auto const a = static_cast<std::size_t>(end_ - pos_); | ||
| 98 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 48 times.
|
87x | if(n < a) | |
| 99 | { | |||
| 100 | 39x | pos_ += n; | ||
| 101 | 39x | return; | ||
| 102 | } | |||
| 103 | // exhaust the first range and continue into the second | |||
| 104 | 48x | n -= a; | ||
| 105 | 48x | pos_ = begin_b_ + n; | ||
| 106 | 48x | end_ = end_b_; | ||
| 107 | 48x | begin_b_ = end_b_; | ||
| 108 | } | |||
| 109 | ||||
| 110 | bool | |||
| 111 | 1030x | empty() const noexcept | ||
| 112 | { | |||
| 113 | 1030x | return pos_ == end_; | ||
| 114 | } | |||
| 115 | ||||
| 116 | char | |||
| 117 | 1194x | value() const noexcept | ||
| 118 | { | |||
| 119 | 1194x | return *pos_; | ||
| 120 | } | |||
| 121 | ||||
| 122 | char const * | |||
| 123 | 20x | pos() const noexcept | ||
| 124 | { | |||
| 125 | 20x | return pos_; | ||
| 126 | } | |||
| 127 | ||||
| 128 | std::size_t | |||
| 129 | 606x | size() const noexcept | ||
| 130 | { | |||
| 131 | 606x | return (end_ - pos_) + (end_b_ - begin_b_); | ||
| 132 | } | |||
| 133 | ||||
| 134 | std::array<capy::const_buffer, 2> | |||
| 135 | 170x | prefix(std::size_t n) const noexcept | ||
| 136 | { | |||
| 137 | 170x | auto const a = static_cast<std::size_t>(end_ - pos_); | ||
| 138 | 170x | auto const b = static_cast<std::size_t>(end_b_ - begin_b_); | ||
| 139 |
2/2✓ Branch 0 taken 86 times.
✓ Branch 1 taken 84 times.
|
170x | auto const na = n < a ? n : a; | |
| 140 | 170x | auto const nb = n - na < b ? n - na : b; | ||
| 141 | 170x | return { { { pos_, na }, { begin_b_, nb } } }; | ||
| 142 | } | |||
| 143 | }; | |||
| 144 | ||||
| 145 | std::error_code | |||
| 146 | 171x | skip_to_eol(chained_sequence& cs) noexcept | ||
| 147 | { | |||
| 148 |
2/2✓ Branch 1 taken 597 times.
✓ Branch 2 taken 38 times.
|
635x | while(!cs.empty()) | |
| 149 | { | |||
| 150 |
2/2✓ Branch 1 taken 133 times.
✓ Branch 2 taken 464 times.
|
597x | if(cs.value() == '\r') | |
| 151 | { | |||
| 152 |
2/2✓ Branch 1 taken 17 times.
✓ Branch 2 taken 116 times.
|
133x | if(!cs.next()) | |
| 153 | 17x | break; | ||
| 154 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 116 times.
|
116x | if(cs.value() != '\n') | |
| 155 | ✗ | return bad_payload; | ||
| 156 | 116x | cs.next(); | ||
| 157 | 116x | return {}; | ||
| 158 | } | |||
| 159 | 464x | cs.next(); | ||
| 160 | } | |||
| 161 | 55x | return need_data; | ||
| 162 | } | |||
| 163 | ||||
| 164 | std::error_code | |||
| 165 | 177x | parse_chunk_header( | ||
| 166 | chained_sequence& cs, | |||
| 167 | std::uint64_t& size) noexcept | |||
| 168 | { | |||
| 169 | 177x | for(auto const start = cs.size();;) | ||
| 170 | { | |||
| 171 |
2/2✓ Branch 1 taken 31 times.
✓ Branch 2 taken 298 times.
|
329x | if(cs.empty()) | |
| 172 | 31x | return need_data; | ||
| 173 | 298x | auto const n = urls::grammar::hexdig_value(cs.value()); | ||
| 174 |
2/2✓ Branch 0 taken 146 times.
✓ Branch 1 taken 152 times.
|
298x | if(n < 0) | |
| 175 | { | |||
| 176 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 137 times.
|
146x | if(start == cs.size()) | |
| 177 | 9x | return bad_payload; | ||
| 178 | // skip chunk header's exts | |||
| 179 | 137x | return skip_to_eol(cs); | ||
| 180 | } | |||
| 181 | // at least 4 significant bits are free | |||
| 182 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 152 times.
|
152x | if(size > (std::numeric_limits<std::uint64_t>::max)() >> 4) | |
| 183 | ✗ | return bad_payload; | ||
| 184 | 152x | size = (size << 4) | static_cast<std::uint64_t>(n); | ||
| 185 | 152x | cs.next(); | ||
| 186 | 152x | } | ||
| 187 | } | |||
| 188 | ||||
| 189 | std::error_code | |||
| 190 | 58x | skip_trailer(chained_sequence& cs) noexcept | ||
| 191 | { | |||
| 192 | for(;;) | |||
| 193 | { | |||
| 194 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 60 times.
|
66x | if(cs.empty()) | |
| 195 | 6x | return need_data; | ||
| 196 |
2/2✓ Branch 1 taken 26 times.
✓ Branch 2 taken 34 times.
|
60x | if(cs.value() == '\r') | |
| 197 | { | |||
| 198 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 22 times.
|
26x | if(!cs.next()) | |
| 199 | 4x | return need_data; | ||
| 200 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 22 times.
|
22x | if(cs.value() != '\n') | |
| 201 | ✗ | return bad_payload; | ||
| 202 | 22x | cs.next(); | ||
| 203 | 22x | return {}; | ||
| 204 | } | |||
| 205 | // skip to the end of the field | |||
| 206 |
2/2✓ Branch 2 taken 26 times.
✓ Branch 3 taken 8 times.
|
34x | if(auto ec = skip_to_eol(cs); ec) | |
| 207 | 26x | return ec; | ||
| 208 | 8x | } | ||
| 209 | } | |||
| 210 | ||||
| 211 | std::error_code | |||
| 212 | 209x | skip_crlf(chained_sequence& cs) noexcept | ||
| 213 | { | |||
| 214 |
2/2✓ Branch 1 taken 108 times.
✓ Branch 2 taken 101 times.
|
209x | if(cs.size() < 2) | |
| 215 | 108x | return need_data; | ||
| 216 |
3/6✓ Branch 1 taken 101 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 101 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 101 times.
|
101x | if(cs.value() != '\r' || *cs.next() != '\n') | |
| 217 | ✗ | return bad_payload; | ||
| 218 | 101x | cs.next(); | ||
| 219 | 101x | return {}; | ||
| 220 | } | |||
| 221 | ||||
| 222 | std::span<capy::const_buffer> | |||
| 223 | 60x | collect( | ||
| 224 | std::span<capy::const_buffer> dest, | |||
| 225 | std::array<capy::const_buffer, 2> const& src, | |||
| 226 | std::size_t at_most = std::size_t(-1)) noexcept | |||
| 227 | { | |||
| 228 | 60x | std::size_t n = 0; | ||
| 229 |
1/2✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
|
120x | for(auto b : src) | |
| 230 | { | |||
| 231 | 120x | auto const take = clamp(b.size(), at_most); | ||
| 232 |
5/6✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 60 times.
✓ Branch 5 taken 60 times.
✓ Branch 6 taken 60 times.
|
120x | if(take == 0 || n == dest.size()) | |
| 233 | 60x | break; | ||
| 234 | 60x | at_most -= take; | ||
| 235 | 60x | dest[n++] = { b.data(), take }; | ||
| 236 | } | |||
| 237 | 60x | return dest.first(n); | ||
| 238 | } | |||
| 239 | ||||
| 240 | void | |||
| 241 | 208x | move_leftovers( | ||
| 242 | char* base, | |||
| 243 | std::array<capy::const_buffer, 2> const& bufs) noexcept | |||
| 244 | { | |||
| 245 | 208x | auto const* a = static_cast<char const*>(bufs[0].data()); | ||
| 246 | 208x | auto an = bufs[0].size(); | ||
| 247 | 208x | auto const* b = static_cast<char const*>(bufs[1].data()); | ||
| 248 | 208x | auto const bn = bufs[1].size(); | ||
| 249 |
1/2✓ Branch 0 taken 208 times.
✗ Branch 1 not taken.
|
208x | if(bn == 0) | |
| 250 | { | |||
| 251 | 208x | std::memmove(base, a, an); | ||
| 252 | 208x | return; | ||
| 253 | } | |||
| 254 | do | |||
| 255 | { | |||
| 256 | ✗ | auto* bp = (std::min)(base + an, const_cast<char*>(a) - bn); | ||
| 257 | ✗ | b = static_cast<char const*>(std::memmove(bp, b, bn)); | ||
| 258 | ✗ | auto chunk_a = static_cast<std::size_t>(b - base); | ||
| 259 | ✗ | std::memcpy(base, a, chunk_a); | ||
| 260 | ✗ | an -= chunk_a; | ||
| 261 | ✗ | base += chunk_a; | ||
| 262 | ✗ | a += chunk_a; | ||
| 263 | ✗ | } while(an); | ||
| 264 | } | |||
| 265 | ||||
| 266 | auto | |||
| 267 | 25x | prefix( | ||
| 268 | auto buf, | |||
| 269 | std::size_t n) noexcept -> decltype(buf) | |||
| 270 | { | |||
| 271 | 25x | return { buf.data(), clamp(buf.size(), n) }; | ||
| 272 | }; | |||
| 273 | ||||
| 274 | } // namespace | |||
| 275 | ||||
| 276 | struct parser::chunk_fn | |||
| 277 | { | |||
| 278 | void* obj_; | |||
| 279 | capy::io_result<std::size_t> (*invoke_)( | |||
| 280 | void*, capy::const_buffer, bool); | |||
| 281 | ||||
| 282 | template<class F> | |||
| 283 | 198x | chunk_fn(F&& f) noexcept | ||
| 284 | 198x | : obj_(std::addressof(f)) | ||
| 285 | 198x | , invoke_( | ||
| 286 | 198x | [](void* obj, capy::const_buffer b, bool last) | ||
| 287 | -> capy::io_result<std::size_t> | |||
| 288 | { | |||
| 289 | return (*static_cast< | |||
| 290 | std::remove_reference_t<F>*>(obj))(b, last); | |||
| 291 | }) | |||
| 292 | { | |||
| 293 | 198x | } | ||
| 294 | ||||
| 295 | capy::io_result<std::size_t> | |||
| 296 | 106x | operator()(capy::const_buffer b, bool last) const | ||
| 297 | { | |||
| 298 | 106x | return invoke_(obj_, b, last); | ||
| 299 | } | |||
| 300 | }; | |||
| 301 | ||||
| 302 | 192x | parser:: | ||
| 303 | parser( | |||
| 304 | config const& cfg, | |||
| 305 | http::detail::kind kind, | |||
| 306 | 192x | capy::any_read_stream stream) | ||
| 307 | 192x | : hdr_limits_(cfg.hdr_limits) | ||
| 308 | 192x | , stream_(std::move(stream)) | ||
| 309 | 192x | , body_limit_(cfg.body_limit) | ||
| 310 | { | |||
| 311 | 192x | constexpr auto align = alignof(header::entry); | ||
| 312 |
1/1✓ Branch 1 taken 192 times.
|
192x | auto const h_cap = hdr_limits_.valid_space_needed() + | |
| 313 | 192x | align * ((cfg.in_buffer + align - 1) / align); | ||
| 314 | 384x | auto* const p = static_cast<char*>(::operator new( | ||
| 315 |
1/1✓ Branch 1 taken 192 times.
|
192x | sizeof(http::static_response) + h_cap + cfg.dec_buffer)); | |
| 316 | 192x | in_ = { p + sizeof(http::static_response), h_cap - table_reserve() }; | ||
| 317 | 192x | out_ = { in_.ptr + h_cap, cfg.dec_buffer }; | ||
| 318 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 191 times.
|
192x | if(kind == http::detail::kind::request) | |
| 319 | 1x | h_.reset(reinterpret_cast<header*>( | ||
| 320 | 1x | ::new(static_cast<void*>(p)) | ||
| 321 | 1x | http::static_request(in_.ptr, h_cap))); | ||
| 322 | else | |||
| 323 | 191x | h_.reset(reinterpret_cast<header*>( | ||
| 324 | 191x | ::new(static_cast<void*>(p)) | ||
| 325 | 191x | http::static_response(in_.ptr, h_cap))); | ||
| 326 | 192x | } | ||
| 327 | ||||
| 328 | bool | |||
| 329 | 142x | parser:: | ||
| 330 | got_header() const noexcept | |||
| 331 | { | |||
| 332 | 142x | return got_header_; | ||
| 333 | } | |||
| 334 | ||||
| 335 | bool | |||
| 336 | 228x | parser:: | ||
| 337 | got_body() const noexcept | |||
| 338 | { | |||
| 339 | 228x | return got_body_; | ||
| 340 | } | |||
| 341 | ||||
| 342 | bool | |||
| 343 | 89x | parser:: | ||
| 344 | has_buffered_data() const noexcept | |||
| 345 | { | |||
| 346 |
4/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 46 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 30 times.
|
89x | switch(payload_) | |
| 347 | { | |||
| 348 | 12x | case payload::chunked: | ||
| 349 | 12x | return in_.size() > chunk_rem_; | ||
| 350 | 46x | case payload::size: | ||
| 351 | 46x | return in_.size() > payload_rem(); | ||
| 352 | 1x | case payload::to_eof: | ||
| 353 | 1x | return false; | ||
| 354 | 30x | default: | ||
| 355 | 30x | return !in_.empty(); | ||
| 356 | } | |||
| 357 | } | |||
| 358 | ||||
| 359 | std::size_t | |||
| 360 | 416x | parser:: | ||
| 361 | raw_limit_rem() const noexcept | |||
| 362 | { | |||
| 363 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 416 times.
|
416x | BOOST_ASSERT(!dec_); | |
| 364 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 409 times.
|
416x | if(body_limit_ <= transferred_) | |
| 365 | 7x | return 0; | ||
| 366 | 409x | return clamp(body_limit_ - transferred_); | ||
| 367 | } | |||
| 368 | ||||
| 369 | std::size_t | |||
| 370 | 26x | parser:: | ||
| 371 | dec_limit_rem() const noexcept | |||
| 372 | { | |||
| 373 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26x | BOOST_ASSERT(dec_); | |
| 374 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 25 times.
|
26x | if(body_limit_ <= decoded_) | |
| 375 | 1x | return 0; | ||
| 376 | 25x | return clamp(body_limit_ - decoded_); | ||
| 377 | } | |||
| 378 | ||||
| 379 | bool | |||
| 380 | 1669x | parser:: | ||
| 381 | payload_sized() const noexcept | |||
| 382 | { | |||
| 383 | 1669x | return payload_ == payload::size; | ||
| 384 | } | |||
| 385 | ||||
| 386 | std::size_t | |||
| 387 | 460x | parser:: | ||
| 388 | payload_rem() const noexcept | |||
| 389 | { | |||
| 390 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 460 times.
|
460x | BOOST_ASSERT(payload_sized()); | |
| 391 | 460x | return clamp(h_->md.payload_size - transferred_); | ||
| 392 | } | |||
| 393 | ||||
| 394 | std::size_t | |||
| 395 | 658x | parser:: | ||
| 396 | table_reserve() const noexcept | |||
| 397 | { | |||
| 398 | 658x | return header::table_space( | ||
| 399 | 658x | hdr_limits_.max_fields); | ||
| 400 | } | |||
| 401 | ||||
| 402 | void | |||
| 403 | 208x | parser:: | ||
| 404 | start(bool head) | |||
| 405 | { | |||
| 406 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 205 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 3 times.
|
208x | BOOST_ASSERT(!started_ || got_body_); | |
| 407 | ||||
| 408 |
5/6✓ Branch 1 taken 3 times.
✓ Branch 2 taken 205 times.
✓ Branch 3 taken 3 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 205 times.
|
208x | if(payload_sized() && got_body_) | |
| 409 | 3x | in_.consume(payload_rem()); | ||
| 410 | ||||
| 411 | auto* const base = | |||
| 412 | 208x | reinterpret_cast<char*>(h_.get()) | ||
| 413 | 208x | + sizeof(http::static_response); | ||
| 414 | 208x | move_leftovers(base, in_.data()); | ||
| 415 | 416x | in_ = { base, static_cast<std::size_t>( | ||
| 416 | 208x | out_.ptr - base) - table_reserve(), 0, in_.size() }; | ||
| 417 | 208x | out_ = { out_.ptr, out_.cap }; | ||
| 418 | ||||
| 419 | 208x | *h_ = header{ http::detail::empty{ h_->kind } }; | ||
| 420 | 208x | h_->buf = in_.ptr; | ||
| 421 | 208x | h_->cbuf = in_.ptr; | ||
| 422 | 208x | h_->cap = in_.cap + table_reserve(); | ||
| 423 | ||||
| 424 | 208x | dec_ = nullptr; | ||
| 425 | 208x | chunk_rem_ = 0; | ||
| 426 | 208x | transferred_ = 0; | ||
| 427 | 208x | decoded_ = 0; | ||
| 428 | 208x | dec_err_ = {}; | ||
| 429 | 208x | payload_ = payload::none; | ||
| 430 | 208x | head_ = head; | ||
| 431 | 208x | started_ = true; | ||
| 432 | 208x | got_header_ = false; | ||
| 433 | 208x | got_body_ = false; | ||
| 434 | 208x | mid_chunk_ = false; | ||
| 435 | 208x | fin_chunk_ = false; | ||
| 436 | 208x | } | ||
| 437 | ||||
| 438 | void | |||
| 439 | 50x | parser:: | ||
| 440 | reset(capy::any_read_stream stream) noexcept | |||
| 441 | { | |||
| 442 | auto* const base = | |||
| 443 | 50x | reinterpret_cast<char*>(h_.get()) | ||
| 444 | 50x | + sizeof(http::static_response); | ||
| 445 | 100x | in_ = { base, static_cast<std::size_t>( | ||
| 446 | 50x | out_.ptr - base) - table_reserve() }; | ||
| 447 | 50x | out_ = { out_.ptr, out_.cap }; | ||
| 448 | ||||
| 449 | 50x | stream_ = std::move(stream); | ||
| 450 | 50x | dec_ = nullptr; | ||
| 451 | 50x | chunk_rem_ = 0; | ||
| 452 | 50x | transferred_ = 0; | ||
| 453 | 50x | decoded_ = 0; | ||
| 454 | 50x | dec_err_ = {}; | ||
| 455 | 50x | payload_ = payload::none; | ||
| 456 | 50x | head_ = false; | ||
| 457 | 50x | started_ = false; | ||
| 458 | 50x | got_header_ = false; | ||
| 459 | 50x | got_body_ = false; | ||
| 460 | 50x | mid_chunk_ = false; | ||
| 461 | 50x | fin_chunk_ = false; | ||
| 462 | 50x | eof_ = false; | ||
| 463 | 50x | } | ||
| 464 | ||||
| 465 | capy::io_task<> | |||
| 466 |
1/1✓ Branch 1 taken 987 times.
|
987x | parser::refill() | |
| 467 | { | |||
| 468 | if(eof_) | |||
| 469 | co_return { incomplete }; | |||
| 470 | if(in_.full()) | |||
| 471 | co_return { in_place_overflow }; | |||
| 472 | auto [ec, n] = co_await stream_.read_some(in_.prepare()); | |||
| 473 | in_.commit(n); | |||
| 474 | if(payload_sized() && payload_rem() <= in_.size()) | |||
| 475 | got_body_ = true; | |||
| 476 | if(ec) | |||
| 477 | { | |||
| 478 | if(ec == capy::cond::eof) | |||
| 479 | { | |||
| 480 | eof_ = true; | |||
| 481 | if(payload_ == payload::to_eof) | |||
| 482 | got_body_ = true; | |||
| 483 | co_return {}; | |||
| 484 | } | |||
| 485 | co_return ec; | |||
| 486 | } | |||
| 487 | co_return {}; | |||
| 488 | 1974x | } | ||
| 489 | ||||
| 490 | std::error_code | |||
| 491 | 198x | parser:: | ||
| 492 | walk_chunks(chunk_fn f, bool dry) | |||
| 493 | { | |||
| 494 | 198x | chained_sequence cs = in_.data(); | ||
| 495 | 198x | std::uint64_t size = chunk_rem_; | ||
| 496 | ||||
| 497 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 189 times.
|
198x | if(fin_chunk_) | |
| 498 | { | |||
| 499 | // from flatten_chunks | |||
| 500 | 9x | auto const b = in_.first(clamp(chunk_rem_)); | ||
| 501 |
1/1✓ Branch 1 taken 9 times.
|
9x | auto const [ec, n] = f(b, true); | |
| 502 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 time.
|
9x | if(!dry) | |
| 503 | { | |||
| 504 | 8x | in_.consume(n); | ||
| 505 | 8x | chunk_rem_ -= n; | ||
| 506 | 8x | transferred_ += n; | ||
| 507 | } | |||
| 508 | 9x | return ec; | ||
| 509 | } | |||
| 510 | ||||
| 511 |
2/2✓ Branch 0 taken 128 times.
✓ Branch 1 taken 61 times.
|
189x | if(mid_chunk_) | |
| 512 | 128x | goto invoke; | ||
| 513 | ||||
| 514 | 61x | loop: | ||
| 515 |
2/2✓ Branch 2 taken 51 times.
✓ Branch 3 taken 80 times.
|
131x | if(auto ec = parse_chunk_header(cs, size); ec) | |
| 516 | 51x | return ec; | ||
| 517 | ||||
| 518 | // final chunk | |||
| 519 |
2/2✓ Branch 0 taken 38 times.
✓ Branch 1 taken 42 times.
|
80x | if(size == 0) | |
| 520 | { | |||
| 521 |
2/2✓ Branch 2 taken 20 times.
✓ Branch 3 taken 18 times.
|
38x | if(auto ec = skip_trailer(cs); ec) | |
| 522 | 20x | return ec; | ||
| 523 | 18x | got_body_ = true; | ||
| 524 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
|
18x | if(!dry) | |
| 525 | { | |||
| 526 | 12x | fin_chunk_ = true; | ||
| 527 | 12x | in_.consume(in_.size() - cs.size()); | ||
| 528 | } | |||
| 529 |
1/1✓ Branch 2 taken 18 times.
|
18x | return f({}, true).ec; | |
| 530 | } | |||
| 531 | ||||
| 532 | 42x | invoke: | ||
| 533 |
1/2✓ Branch 3 taken 237 times.
✗ Branch 4 not taken.
|
237x | for(const auto& b : cs.prefix(clamp(size))) | |
| 534 | { | |||
| 535 |
2/2✓ Branch 1 taken 158 times.
✓ Branch 2 taken 79 times.
|
237x | if(b.size() == 0) | |
| 536 | 158x | break; | ||
| 537 |
1/1✓ Branch 1 taken 79 times.
|
79x | auto const [ec, n] = f(b, false); | |
| 538 | 79x | cs.advance(n); | ||
| 539 | 79x | size -= n; | ||
| 540 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 25 times.
|
79x | if(!dry) | |
| 541 | { | |||
| 542 | 54x | in_.consume(in_.size() - cs.size()); | ||
| 543 | 54x | chunk_rem_ = size; | ||
| 544 | 54x | transferred_ += n; | ||
| 545 | 54x | mid_chunk_ = true; | ||
| 546 | } | |||
| 547 |
6/6✓ Branch 1 taken 76 times.
✓ Branch 2 taken 3 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 67 times.
✓ Branch 6 taken 12 times.
✓ Branch 7 taken 67 times.
|
79x | if(ec || n < b.size()) | |
| 548 | 12x | return ec; | ||
| 549 | } | |||
| 550 | ||||
| 551 |
2/2✓ Branch 2 taken 88 times.
✓ Branch 3 taken 70 times.
|
158x | if(auto ec = skip_crlf(cs); ec) | |
| 552 | 88x | return ec; | ||
| 553 | ||||
| 554 | 70x | goto loop; | ||
| 555 | } | |||
| 556 | ||||
| 557 | std::error_code | |||
| 558 | 61x | parser:: | ||
| 559 | flatten_chunks() | |||
| 560 | { | |||
| 561 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
|
61x | if(fin_chunk_) | |
| 562 | ✗ | return {}; | ||
| 563 | ||||
| 564 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 61 times.
|
61x | BOOST_ASSERT(in_.pos == 0); | |
| 565 | ||||
| 566 | 61x | std::size_t flat = clamp(chunk_rem_, in_.len); | ||
| 567 | 61x | char const* keep = in_.ptr + flat; | ||
| 568 | 61x | chained_sequence cs(keep, in_.len - flat); | ||
| 569 | ||||
| 570 | 61x | auto bail = [&](std::error_code ec) | ||
| 571 | { | |||
| 572 | 61x | auto const tail = static_cast<std::size_t>( | ||
| 573 | 61x | in_.ptr + in_.len - keep); | ||
| 574 | 61x | std::memmove(in_.ptr + flat, keep, tail); | ||
| 575 | 61x | in_.len = flat + tail; | ||
| 576 | 61x | return ec; | ||
| 577 | 61x | }; | ||
| 578 | ||||
| 579 | for(;;) | |||
| 580 | { | |||
| 581 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 15 times.
|
66x | if(mid_chunk_) | |
| 582 | { | |||
| 583 |
2/2✓ Branch 2 taken 20 times.
✓ Branch 3 taken 31 times.
|
51x | if(auto ec = skip_crlf(cs); ec) | |
| 584 | 20x | return bail(ec); | ||
| 585 | } | |||
| 586 | ||||
| 587 | 46x | std::uint64_t size = 0; | ||
| 588 |
2/2✓ Branch 2 taken 18 times.
✓ Branch 3 taken 28 times.
|
46x | if(auto ec = parse_chunk_header(cs, size); ec) | |
| 589 | 18x | return bail(ec); | ||
| 590 | ||||
| 591 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 8 times.
|
28x | if(size == 0) | |
| 592 | { | |||
| 593 |
2/2✓ Branch 2 taken 16 times.
✓ Branch 3 taken 4 times.
|
20x | if(auto ec = skip_trailer(cs); ec) | |
| 594 | 16x | return bail(ec); | ||
| 595 | 4x | got_body_ = true; | ||
| 596 | 4x | fin_chunk_ = true; | ||
| 597 | 4x | keep = cs.pos(); | ||
| 598 | 4x | return bail({}); | ||
| 599 | } | |||
| 600 | ||||
| 601 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8x | if(size > in_.cap - flat) | |
| 602 | ✗ | return bail(in_place_overflow); | ||
| 603 | ||||
| 604 | 8x | chunk_rem_ = flat + size; | ||
| 605 | 8x | mid_chunk_ = true; | ||
| 606 | ||||
| 607 | 8x | auto const n = clamp(size, cs.size()); | ||
| 608 | 8x | std::memmove(in_.ptr + flat, cs.pos(), n); | ||
| 609 | 8x | flat += n; | ||
| 610 | 8x | cs.advance(n); | ||
| 611 | 8x | keep = cs.pos(); | ||
| 612 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
|
8x | if(n < size) | |
| 613 | 3x | return bail(need_data); | ||
| 614 | 5x | } | ||
| 615 | } | |||
| 616 | ||||
| 617 | capy::io_task<> | |||
| 618 |
1/1✓ Branch 1 taken 498 times.
|
498x | parser:: | |
| 619 | read_header() | |||
| 620 | { | |||
| 621 | BOOST_ASSERT(started_); | |||
| 622 | ||||
| 623 | if(got_header_) | |||
| 624 | co_return {}; | |||
| 625 | ||||
| 626 | for(;;) | |||
| 627 | { | |||
| 628 | system::error_code ec; | |||
| 629 | h_->parse(in_.size(), hdr_limits_, ec); | |||
| 630 | if(ec) | |||
| 631 | { | |||
| 632 | if(ec != need_more_input) | |||
| 633 | co_return { ec }; | |||
| 634 | if(eof_ && in_.empty()) | |||
| 635 | co_return { http::error::end_of_stream }; | |||
| 636 | if(auto [rec] = co_await refill(); rec) | |||
| 637 | co_return rec; | |||
| 638 | continue; | |||
| 639 | } | |||
| 640 | ||||
| 641 | // TODO: resize out_ based on payload and decoder | |||
| 642 | in_.ptr += h_->size; | |||
| 643 | in_.len -= h_->size; | |||
| 644 | in_.cap -= h_->size; | |||
| 645 | ||||
| 646 | payload_ = head_ ? payload::none : h_->md.payload; | |||
| 647 | got_header_ = true; | |||
| 648 | ||||
| 649 | switch(payload_) | |||
| 650 | { | |||
| 651 | case payload::error: | |||
| 652 | co_return { bad_payload }; | |||
| 653 | case payload::none: | |||
| 654 | got_body_ = true; | |||
| 655 | break; | |||
| 656 | case payload::size: | |||
| 657 | if(payload_rem() <= in_.size()) | |||
| 658 | got_body_ = true; | |||
| 659 | break; | |||
| 660 | case payload::chunked: | |||
| 661 | break; | |||
| 662 | case payload::to_eof: | |||
| 663 | if(eof_) | |||
| 664 | got_body_ = true; | |||
| 665 | break; | |||
| 666 | } | |||
| 667 | co_return {}; | |||
| 668 | } | |||
| 669 | 996x | } | ||
| 670 | ||||
| 671 | void | |||
| 672 | 39x | parser:: | ||
| 673 | set_decoder(decoder* dec) noexcept | |||
| 674 | { | |||
| 675 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 39 times.
|
39x | BOOST_ASSERT(transferred_ == 0); | |
| 676 | 39x | dec_ = dec; | ||
| 677 | 39x | } | ||
| 678 | ||||
| 679 | void | |||
| 680 | 4x | parser:: | ||
| 681 | set_body_limit(std::uint64_t n) noexcept | |||
| 682 | { | |||
| 683 | 4x | body_limit_ = n; | ||
| 684 | 4x | } | ||
| 685 | ||||
| 686 | capy::io_task<std::string_view> | |||
| 687 |
1/1✓ Branch 1 taken 55 times.
|
55x | parser:: | |
| 688 | read_body() | |||
| 689 | { | |||
| 690 | if(auto [ec] = co_await read_header(); ec) | |||
| 691 | co_return { ec, {} }; | |||
| 692 | ||||
| 693 | if(dec_) | |||
| 694 | { | |||
| 695 | if(decoded_ != out_.size()) | |||
| 696 | co_return { incomplete, {} }; | |||
| 697 | for(;;) | |||
| 698 | { | |||
| 699 | if(out_.full()) | |||
| 700 | co_return { in_place_overflow, {} }; | |||
| 701 | auto [ec, n] = co_await decode_some(out_.prepare()); | |||
| 702 | out_.commit(n); | |||
| 703 | if(ec) | |||
| 704 | { | |||
| 705 | if(ec == capy::cond::eof) | |||
| 706 | co_return { {}, { out_.ptr, out_.len } }; | |||
| 707 | co_return { ec, {} }; | |||
| 708 | } | |||
| 709 | } | |||
| 710 | } | |||
| 711 | ||||
| 712 | if(transferred_ != 0) | |||
| 713 | co_return { incomplete, {} }; | |||
| 714 | ||||
| 715 | switch(payload_) | |||
| 716 | { | |||
| 717 | case payload::error: | |||
| 718 | case payload::none: | |||
| 719 | { | |||
| 720 | co_return { {}, {} }; | |||
| 721 | } | |||
| 722 | case payload::chunked: | |||
| 723 | { | |||
| 724 | for(;;) | |||
| 725 | { | |||
| 726 | if(chunk_rem_ > raw_limit_rem()) | |||
| 727 | co_return { body_too_large, {} }; | |||
| 728 | if(fin_chunk_) | |||
| 729 | co_return { {}, { in_.ptr, clamp(chunk_rem_) } }; | |||
| 730 | if(auto ec = flatten_chunks(); ec) | |||
| 731 | { | |||
| 732 | if(ec != need_more_input) | |||
| 733 | co_return { ec, {} }; | |||
| 734 | if(auto [fec] = co_await refill(); fec) | |||
| 735 | co_return { fec, {} }; | |||
| 736 | } | |||
| 737 | } | |||
| 738 | } | |||
| 739 | case payload::size: | |||
| 740 | { | |||
| 741 | auto const rem = payload_rem(); | |||
| 742 | if(rem > raw_limit_rem()) | |||
| 743 | co_return { body_too_large, {} }; | |||
| 744 | for(;;) | |||
| 745 | { | |||
| 746 | if(got_body_) | |||
| 747 | co_return { {}, { in_.ptr, clamp(in_.len, rem) } }; | |||
| 748 | if(auto [fec] = co_await refill(); fec) | |||
| 749 | co_return { fec, {} }; | |||
| 750 | } | |||
| 751 | } | |||
| 752 | case payload::to_eof: | |||
| 753 | { | |||
| 754 | for(;;) | |||
| 755 | { | |||
| 756 | if(in_.size() > raw_limit_rem()) | |||
| 757 | co_return { body_too_large, {} }; | |||
| 758 | if(got_body_) | |||
| 759 | co_return { {}, { in_.ptr, in_.len } }; | |||
| 760 | if(auto [fec] = co_await refill(); fec) | |||
| 761 | co_return { fec, {} }; | |||
| 762 | } | |||
| 763 | } | |||
| 764 | } | |||
| 765 | 110x | } | ||
| 766 | ||||
| 767 | http::static_response const& | |||
| 768 | 299x | parser:: | ||
| 769 | get_response() const | |||
| 770 | { | |||
| 771 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 299 times.
|
299x | BOOST_ASSERT(h_); | |
| 772 | return reinterpret_cast< | |||
| 773 | 299x | http::static_response const&>(*h_); | ||
| 774 | } | |||
| 775 | ||||
| 776 | http::static_request const& | |||
| 777 | 2x | parser:: | ||
| 778 | get_request() const | |||
| 779 | { | |||
| 780 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2x | BOOST_ASSERT(h_); | |
| 781 | return reinterpret_cast< | |||
| 782 | 2x | http::static_request const&>(*h_); | ||
| 783 | } | |||
| 784 | ||||
| 785 | capy::io_task<std::size_t> | |||
| 786 |
1/1✓ Branch 1 taken 40 times.
|
40x | parser:: | |
| 787 | decode_some( | |||
| 788 | std::span<capy::mutable_buffer const> buffers) | |||
| 789 | { | |||
| 790 | if(capy::buffer_empty(buffers)) | |||
| 791 | co_return { {}, 0 }; | |||
| 792 | ||||
| 793 | auto outbufs = capy::consuming_buffers(buffers); | |||
| 794 | std::size_t prod = 0; | |||
| 795 | auto decode = | |||
| 796 | 44x | [&](capy::const_buffer in, bool last) | ||
| 797 | -> capy::io_result<std::size_t> | |||
| 798 | { | |||
| 799 |
2/2✓ Branch 1 taken 19 times.
✓ Branch 2 taken 25 times.
|
44x | if(dec_err_) | |
| 800 | { | |||
| 801 |
2/2✓ Branch 2 taken 15 times.
✓ Branch 3 taken 4 times.
|
19x | if(dec_err_ == capy::cond::eof) | |
| 802 | { | |||
| 803 |
6/6✓ Branch 0 taken 13 times.
✓ Branch 1 taken 2 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 3 times.
✓ Branch 6 taken 12 times.
|
15x | if(!last || in.size() != 0) | |
| 804 |
1/1✓ Branch 2 taken 3 times.
|
3x | return { bad_payload, 0 }; | |
| 805 | } | |||
| 806 | 16x | return { dec_err_, 0 }; | ||
| 807 | } | |||
| 808 | 25x | std::size_t cons = 0; | ||
| 809 | for(;;) | |||
| 810 | { | |||
| 811 | 29x | auto const out = capy::front(outbufs.data()); | ||
| 812 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 26 times.
|
29x | if(out.size() == 0) | |
| 813 |
1/1✓ Branch 2 taken 3 times.
|
3x | return { {}, cons }; | |
| 814 | 26x | auto const lim = dec_limit_rem(); | ||
| 815 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 25 times.
|
26x | if(lim == 0) | |
| 816 |
1/1✓ Branch 2 taken 1 time.
|
1x | return { body_too_large, cons }; | |
| 817 |
1/1✓ Branch 2 taken 25 times.
|
25x | auto const r = dec_->process( | |
| 818 | prefix(out, lim), in, last); | |||
| 819 | 25x | in += r.consumed; | ||
| 820 | 25x | cons += r.consumed; | ||
| 821 | 25x | transferred_ += r.consumed; | ||
| 822 | 25x | prod += r.produced; | ||
| 823 | 25x | decoded_ += r.produced; | ||
| 824 | 25x | outbufs.consume(r.produced); | ||
| 825 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 9 times.
|
25x | if(r.ec) | |
| 826 | { | |||
| 827 | 16x | dec_err_ = r.ec; | ||
| 828 |
1/1✓ Branch 2 taken 16 times.
|
16x | return { {}, cons }; | |
| 829 | } | |||
| 830 |
3/4✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
|
9x | if(r.produced == 0 && r.consumed == 0) | |
| 831 | { | |||
| 832 | 1x | dec_err_ = error::decode_error; | ||
| 833 |
1/1✓ Branch 2 taken 1 time.
|
1x | return { {}, cons }; | |
| 834 | } | |||
| 835 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
|
8x | if(in.size() == 0) | |
| 836 |
1/1✓ Branch 2 taken 4 times.
|
4x | return { {}, cons }; | |
| 837 | 4x | } | ||
| 838 | }; | |||
| 839 | ||||
| 840 | switch(payload_) | |||
| 841 | { | |||
| 842 | case payload::error: | |||
| 843 | case payload::none: | |||
| 844 | { | |||
| 845 | co_return { capy::error::eof, 0 }; | |||
| 846 | } | |||
| 847 | case payload::chunked: | |||
| 848 | { | |||
| 849 | for(;;) | |||
| 850 | { | |||
| 851 | auto ec = walk_chunks(decode); | |||
| 852 | if(prod != 0) | |||
| 853 | co_return { {}, prod }; | |||
| 854 | if(ec != need_more_input) | |||
| 855 | co_return { ec, 0 }; | |||
| 856 | if(auto [fec] = co_await refill(); fec) | |||
| 857 | co_return { fec, 0 }; | |||
| 858 | } | |||
| 859 | } | |||
| 860 | case payload::size: | |||
| 861 | case payload::to_eof: | |||
| 862 | { | |||
| 863 | for(;;) | |||
| 864 | { | |||
| 865 | auto const rem = payload_sized() ? payload_rem() : in_.size(); | |||
| 866 | auto const in = in_.first(rem); | |||
| 867 | if(in.size() == 0 && !got_body_) | |||
| 868 | { | |||
| 869 | if(auto [fec] = co_await refill(); fec) | |||
| 870 | co_return { fec, 0 }; | |||
| 871 | continue; | |||
| 872 | } | |||
| 873 | auto [ec, cons] = decode(in, got_body_ && in.size() == rem); | |||
| 874 | in_.consume(cons); | |||
| 875 | if(prod != 0) | |||
| 876 | co_return { {}, prod }; | |||
| 877 | if(ec) | |||
| 878 | co_return { ec, 0 }; | |||
| 879 | } | |||
| 880 | } | |||
| 881 | } | |||
| 882 | 80x | } | ||
| 883 | ||||
| 884 | capy::io_task<std::size_t> | |||
| 885 |
1/1✓ Branch 1 taken 143 times.
|
143x | parser:: | |
| 886 | do_read_some( | |||
| 887 | std::span<capy::mutable_buffer const> buffers) | |||
| 888 | { | |||
| 889 | if(auto [ec] = co_await read_header(); ec) | |||
| 890 | co_return { ec, 0 }; | |||
| 891 | ||||
| 892 | if(dec_) | |||
| 893 | { | |||
| 894 | if(!out_.empty()) | |||
| 895 | { | |||
| 896 | auto const n = capy::buffer_copy(buffers, out_.data()); | |||
| 897 | out_.consume(n); | |||
| 898 | co_return { {}, n }; | |||
| 899 | } | |||
| 900 | co_return co_await decode_some(buffers); | |||
| 901 | } | |||
| 902 | ||||
| 903 | 22x | auto copy = [&](std::size_t at_most) | ||
| 904 | { | |||
| 905 | 22x | auto const n = capy::buffer_copy( | ||
| 906 | 22x | buffers, in_.data(), at_most); | ||
| 907 | 22x | in_.consume(n); | ||
| 908 | 22x | transferred_ += n; | ||
| 909 | 22x | return n; | ||
| 910 | }; | |||
| 911 | ||||
| 912 | switch(payload_) | |||
| 913 | { | |||
| 914 | case payload::error: | |||
| 915 | case payload::none: | |||
| 916 | { | |||
| 917 | co_return { capy::error::eof, 0 }; | |||
| 918 | } | |||
| 919 | case payload::chunked: | |||
| 920 | { | |||
| 921 | for(;;) | |||
| 922 | { | |||
| 923 | std::size_t read = 0; | |||
| 924 | std::size_t lim = raw_limit_rem(); | |||
| 925 | auto outbufs = capy::consuming_buffers(buffers); | |||
| 926 | auto ec = walk_chunks( | |||
| 927 | 37x | [&](capy::const_buffer b, bool) | ||
| 928 | -> capy::io_result<std::size_t> | |||
| 929 | { | |||
| 930 | 37x | auto const take = clamp(b.size(), lim); | ||
| 931 | 37x | lim -= take; | ||
| 932 | 37x | auto const n = capy::buffer_copy(outbufs.data(), b, take); | ||
| 933 | 37x | read += n; | ||
| 934 | 37x | outbufs.consume(n); | ||
| 935 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 35 times.
|
37x | if(take < b.size()) | |
| 936 |
1/1✓ Branch 2 taken 2 times.
|
2x | return { body_too_large, n }; | |
| 937 |
1/1✓ Branch 2 taken 35 times.
|
35x | return { {}, n }; | |
| 938 | ||||
| 939 | }); | |||
| 940 | if(read != 0) | |||
| 941 | co_return { {}, read }; | |||
| 942 | if(ec == need_more_input) | |||
| 943 | { | |||
| 944 | if(auto [fec] = co_await refill(); fec) | |||
| 945 | co_return { fec, 0 }; | |||
| 946 | continue; | |||
| 947 | } | |||
| 948 | else if(ec) | |||
| 949 | co_return { ec, 0 }; | |||
| 950 | BOOST_ASSERT(got_body_); | |||
| 951 | co_return { capy::error::eof, 0 }; | |||
| 952 | } | |||
| 953 | } | |||
| 954 | case payload::size: | |||
| 955 | { | |||
| 956 | auto const rem = payload_rem(); | |||
| 957 | if(rem == 0) | |||
| 958 | co_return { capy::error::eof, 0 }; | |||
| 959 | auto const lim = raw_limit_rem(); | |||
| 960 | if(lim == 0) | |||
| 961 | co_return { body_too_large, 0 }; | |||
| 962 | if(!in_.empty()) | |||
| 963 | co_return { {}, copy(clamp(rem, lim)) }; | |||
| 964 | if(eof_) | |||
| 965 | co_return { incomplete, 0 }; | |||
| 966 | auto [ec, n] = co_await stream_.read_some( | |||
| 967 | capy::buffer_slice(buffers, 0, clamp(rem, lim))); | |||
| 968 | transferred_ += n; | |||
| 969 | if(n == rem) | |||
| 970 | got_body_ = true; | |||
| 971 | if(ec == capy::cond::eof) | |||
| 972 | { | |||
| 973 | eof_ = true; | |||
| 974 | if(n != rem) | |||
| 975 | co_return { incomplete, n }; | |||
| 976 | } | |||
| 977 | co_return { ec, n }; | |||
| 978 | } | |||
| 979 | case payload::to_eof: | |||
| 980 | { | |||
| 981 | if(eof_) | |||
| 982 | co_return { capy::error::eof, 0 }; | |||
| 983 | auto const lim = raw_limit_rem(); | |||
| 984 | if(lim == 0) | |||
| 985 | co_return { body_too_large, 0 }; | |||
| 986 | if(!in_.empty()) | |||
| 987 | co_return { {}, copy(lim) }; | |||
| 988 | auto [ec, n] = co_await stream_.read_some( | |||
| 989 | capy::buffer_slice(buffers, 0, lim)); | |||
| 990 | transferred_ += n; | |||
| 991 | if(ec == capy::cond::eof) | |||
| 992 | { | |||
| 993 | eof_ = true; | |||
| 994 | got_body_ = true; | |||
| 995 | } | |||
| 996 | co_return { ec, n }; | |||
| 997 | } | |||
| 998 | } | |||
| 999 | 286x | } | ||
| 1000 | ||||
| 1001 | capy::io_task<std::span<capy::const_buffer>> | |||
| 1002 |
1/1✓ Branch 1 taken 119 times.
|
119x | parser:: | |
| 1003 | pull(std::span<capy::const_buffer> dest) | |||
| 1004 | { | |||
| 1005 | if(auto [ec] = co_await read_header(); ec) | |||
| 1006 | co_return { ec, {} }; | |||
| 1007 | ||||
| 1008 | if(dec_) | |||
| 1009 | { | |||
| 1010 | if(!out_.empty()) | |||
| 1011 | co_return { {}, collect(dest, out_.data()) }; | |||
| 1012 | auto [ec, n] = co_await decode_some(out_.prepare()); | |||
| 1013 | out_.commit(n); | |||
| 1014 | if(ec && n == 0) | |||
| 1015 | co_return { ec, {} }; | |||
| 1016 | co_return { {}, collect(dest, out_.data()) }; | |||
| 1017 | } | |||
| 1018 | ||||
| 1019 | switch(payload_) | |||
| 1020 | { | |||
| 1021 | case payload::error: | |||
| 1022 | case payload::none: | |||
| 1023 | { | |||
| 1024 | co_return { capy::error::eof, {} }; | |||
| 1025 | } | |||
| 1026 | case payload::chunked: | |||
| 1027 | { | |||
| 1028 | for(;;) | |||
| 1029 | { | |||
| 1030 | std::size_t n = 0; | |||
| 1031 | std::size_t lim = raw_limit_rem(); | |||
| 1032 | if(lim == 0) | |||
| 1033 | co_return { body_too_large, {} }; | |||
| 1034 | auto ec = walk_chunks( | |||
| 1035 | 32x | [&](capy::const_buffer b, bool last) | ||
| 1036 | -> capy::io_result<std::size_t> | |||
| 1037 | { | |||
| 1038 |
5/6✓ Branch 0 taken 7 times.
✓ Branch 1 taken 25 times.
✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 7 times.
✓ Branch 6 taken 25 times.
|
32x | if(last && b.size() == 0) | |
| 1039 |
1/1✓ Branch 2 taken 7 times.
|
7x | return { capy::error::eof, 0 }; | |
| 1040 | 25x | auto const take = clamp(b.size(), lim); | ||
| 1041 |
5/6✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 24 times.
|
25x | if(take == 0 || n == dest.size()) | |
| 1042 |
1/1✓ Branch 2 taken 1 time.
|
1x | return { {}, 0 }; | |
| 1043 | 24x | lim -= take; | ||
| 1044 | 24x | dest[n++] = { b.data(), take }; | ||
| 1045 |
1/1✓ Branch 2 taken 24 times.
|
24x | return { {}, take }; | |
| 1046 | }, | |||
| 1047 | true); | |||
| 1048 | if(n != 0) | |||
| 1049 | co_return { {}, dest.first(n) }; | |||
| 1050 | if(ec != need_more_input) | |||
| 1051 | { | |||
| 1052 | if(ec == capy::error::eof) | |||
| 1053 | consume(0); // chunk trailer | |||
| 1054 | co_return { ec, {} }; | |||
| 1055 | } | |||
| 1056 | if(auto [fec] = co_await refill(); fec) | |||
| 1057 | co_return { fec, {} }; | |||
| 1058 | } | |||
| 1059 | } | |||
| 1060 | case payload::size: | |||
| 1061 | { | |||
| 1062 | auto const rem = payload_rem(); | |||
| 1063 | auto const lim = raw_limit_rem(); | |||
| 1064 | if(rem == 0) | |||
| 1065 | co_return { capy::error::eof, {} }; | |||
| 1066 | if(lim == 0) | |||
| 1067 | co_return { body_too_large, {} }; | |||
| 1068 | for(;;) | |||
| 1069 | { | |||
| 1070 | if(!in_.empty()) | |||
| 1071 | co_return { {}, collect( | |||
| 1072 | dest, in_.data(), clamp(rem, lim)) }; | |||
| 1073 | if(auto [fec] = co_await refill(); fec) | |||
| 1074 | co_return { fec, {} }; | |||
| 1075 | } | |||
| 1076 | } | |||
| 1077 | case payload::to_eof: | |||
| 1078 | { | |||
| 1079 | auto const lim = raw_limit_rem(); | |||
| 1080 | if(lim == 0) | |||
| 1081 | co_return { body_too_large, {} }; | |||
| 1082 | for(;;) | |||
| 1083 | { | |||
| 1084 | if(!in_.empty()) | |||
| 1085 | co_return { {}, collect(dest, in_.data(), lim) }; | |||
| 1086 | if(eof_) | |||
| 1087 | co_return { capy::error::eof, {} }; | |||
| 1088 | if(auto [fec] = co_await refill(); fec) | |||
| 1089 | co_return { fec, {} }; | |||
| 1090 | } | |||
| 1091 | } | |||
| 1092 | } | |||
| 1093 | 238x | } | ||
| 1094 | ||||
| 1095 | void | |||
| 1096 | 86x | parser:: | ||
| 1097 | consume(std::size_t n) noexcept | |||
| 1098 | { | |||
| 1099 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 85 times.
|
86x | if(dec_) | |
| 1100 | 1x | return out_.consume(n); | ||
| 1101 | ||||
| 1102 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 59 times.
|
85x | switch(payload_) | |
| 1103 | { | |||
| 1104 | 26x | case payload::chunked: | ||
| 1105 | 26x | walk_chunks( | ||
| 1106 | 26x | [&](capy::const_buffer b, bool) | ||
| 1107 | -> capy::io_result<std::size_t> | |||
| 1108 | { | |||
| 1109 | 30x | auto const take = clamp(b.size(), n); | ||
| 1110 | 30x | n -= take; | ||
| 1111 |
1/1✓ Branch 2 taken 30 times.
|
30x | return { {}, take }; | |
| 1112 | }); | |||
| 1113 | 26x | return; | ||
| 1114 | 59x | default: | ||
| 1115 | 59x | in_.consume(n); | ||
| 1116 | 59x | transferred_ += n; | ||
| 1117 | 59x | return; | ||
| 1118 | } | |||
| 1119 | } | |||
| 1120 | ||||
| 1121 | } // namespace detail | |||
| 1122 | } // namespace burl | |||
| 1123 | } // namespace boost | |||
| 1124 |