src/parser.cpp

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