src/parser.cpp

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