src/parser.cpp

77.9% Lines (535/687) 81.8% List of functions (72/88)
parser.cpp
f(x) Functions (88)
Function Calls Lines Blocks
boost::http::(anonymous namespace)::prefix_pair(std::array<boost::capy::const_buffer, 2ul> const&, unsigned long) :132 41410x 90.0% 92.0% boost::http::(anonymous namespace)::chained_sequence::chained_sequence(std::array<boost::capy::const_buffer, 2ul> const&) :160 71617x 100.0% 100.0% boost::http::(anonymous namespace)::chained_sequence::next() :169 319930x 63.6% 83.0% boost::http::(anonymous namespace)::chained_sequence::is_empty() const :191 212674x 100.0% 100.0% boost::http::(anonymous namespace)::chained_sequence::value() const :197 305475x 100.0% 100.0% boost::http::(anonymous namespace)::chained_sequence::size() const :203 226936x 100.0% 100.0% boost::http::(anonymous namespace)::parse_hex(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&) :210 66939x 100.0% 100.0% boost::http::(anonymous namespace)::parse_hex(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&)::{lambda()#1}::operator()() const :223 1x 100.0% 100.0% boost::http::(anonymous namespace)::parse_hex(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&)::{lambda()#2}::operator()() const :233 1x 100.0% 100.0% boost::http::(anonymous namespace)::parse_hex(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&)::{lambda()#3}::operator()() const :241 19948x 100.0% 100.0% boost::http::(anonymous namespace)::find_eol(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&) :247 47341x 100.0% 100.0% boost::http::(anonymous namespace)::find_eol(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&)::{lambda()#1}::operator()() const :259 2x 100.0% 100.0% boost::http::(anonymous namespace)::find_eol(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&)::{lambda()#2}::operator()() const :268 418x 100.0% 100.0% boost::http::(anonymous namespace)::parse_eol(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&) :273 62239x 100.0% 100.0% boost::http::(anonymous namespace)::parse_eol(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&)::{lambda()#1}::operator()() const :285 3x 100.0% 100.0% boost::http::(anonymous namespace)::parse_eol(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&)::{lambda()#2}::operator()() const :289 432x 100.0% 100.0% boost::http::(anonymous namespace)::skip_trailer_headers(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&) :294 4243x 100.0% 100.0% boost::http::(anonymous namespace)::skip_trailer_headers(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&)::{lambda()#1}::operator()() const :306 2x 100.0% 100.0% boost::http::(anonymous namespace)::skip_trailer_headers(boost::http::(anonymous namespace)::chained_sequence&, boost::system::error_code&)::{lambda()#2}::operator()() const :318 42x 100.0% 100.0% unsigned long boost::http::(anonymous namespace)::clamp<unsigned long>(unsigned long, unsigned long) :324 193623x 100.0% 100.0% boost::http::(anonymous namespace)::zlib_filter::zlib_filter(boost::http::zlib::inflate_service&, int) :340 0 0.0% 0.0% boost::http::(anonymous namespace)::zlib_filter::do_process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool) :354 0 0.0% 0.0% boost::http::(anonymous namespace)::brotli_filter::brotli_filter(boost::http::brotli::decode_service&) :388 0 0.0% 0.0% boost::http::(anonymous namespace)::brotli_filter::~brotli_filter() :396 0 0.0% 0.0% boost::http::(anonymous namespace)::brotli_filter::do_process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool) :404 0 0.0% 0.0% boost::http::(anonymous namespace)::brotli_filter::do_process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool)::{lambda()#1}::operator()() const :428 0 0.0% 0.0% boost::http::(anonymous namespace)::brotli_filter::do_process(boost::capy::mutable_buffer, boost::capy::const_buffer, bool)::{lambda()#2}::operator()() const :431 0 0.0% 0.0% boost::http::parser::impl::impl(std::shared_ptr<boost::http::parser_config_impl const>, boost::http::detail::kind) :483 2175x 100.0% 94.0% boost::http::parser::impl::got_header() const :494 36129x 100.0% 100.0% boost::http::parser::impl::is_complete() const :500 59142x 100.0% 100.0% boost::http::parser::impl::safe_get_request() const :506 316x 75.0% 67.0% boost::http::parser::impl::safe_get_response() const :516 3x 75.0% 67.0% boost::http::parser::impl::reset() :527 2722x 100.0% 100.0% boost::http::parser::impl::start(bool) :536 10651x 98.3% 88.0% boost::http::parser::impl::prepare() :655 81915x 93.3% 85.0% boost::http::parser::impl::commit(unsigned long) :748 80858x 81.5% 76.0% boost::http::parser::impl::commit_eof() :821 134x 88.9% 81.0% boost::http::parser::impl::parse(boost::system::error_code&) :855 98769x 100.0% 73.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#1}::operator()() const :891 6x 100.0% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#2}::operator()() const :899 5x 100.0% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#3}::operator()() const :942 60x 52.3% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#4}::operator()() const :1024 3x 100.0% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#5}::operator()() const :1042 8364x 100.0% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#6}::operator()() const :1055 20849x 100.0% 55.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#6}::operator()() const::{lambda()#1}::operator()() const :1059 0 93.9% 0.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#7}::operator()() const :1117 1x 100.0% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#8}::operator()() const :1123 1829x 63.6% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#9}::operator()() const :1148 0 83.3% 0.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#10}::operator()() const :1166 0 66.7% 0.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#11}::operator()() const :1183 24652x 100.0% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#12}::operator()() const :1194 24652x 66.7% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#13}::operator()() const :1217 1x 100.0% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#14}::operator()() const :1230 7x 100.0% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#15}::operator()() const :1244 1x 100.0% 100.0% boost::http::parser::impl::parse(boost::system::error_code&)::{lambda()#16}::operator()() const :1250 20410x 100.0% 100.0% boost::http::parser::impl::pull_body() :1264 41440x 91.7% 89.0% boost::http::parser::impl::consume_body(unsigned long) :1287 39606x 53.8% 67.0% boost::http::parser::impl::body() const :1309 712x 80.0% 70.0% boost::http::parser::impl::has_buffered_data() const :1327 9x 100.0% 100.0% boost::http::parser::impl::set_body_limit(unsigned long) :1338 5x 100.0% 100.0% boost::http::parser::impl::is_plain() const :1359 140533x 100.0% 100.0% boost::http::parser::impl::body_limit_remain() const :1366 47967x 100.0% 100.0% boost::http::parser::impl::apply_filter(boost::system::error_code&, unsigned long, bool) :1372 0 0.0% 0.0% boost::http::parser::impl::apply_filter(boost::system::error_code&, unsigned long, bool)::{lambda()#1}::operator()() const :1383 0 0.0% 0.0% boost::http::parser::impl::apply_filter(boost::system::error_code&, unsigned long, bool)::{lambda()#2}::operator()() const :1404 0 0.0% 0.0% boost::http::parser::impl::apply_filter(boost::system::error_code&, unsigned long, bool)::{lambda()#3}::operator()() const :1419 0 0.0% 0.0% boost::http::parser::~parser() :1444 2190x 100.0% 100.0% boost::http::parser::parser() :1450 12x 100.0% 100.0% boost::http::parser::parser(boost::http::parser&&) :1456 3x 100.0% 100.0% boost::http::parser::parser(std::shared_ptr<boost::http::parser_config_impl const>, boost::http::detail::kind) :1463 2175x 100.0% 58.0% boost::http::parser::assign(boost::http::parser&&) :1474 4x 83.3% 57.0% boost::http::parser::got_header() const :1491 36129x 100.0% 75.0% boost::http::parser::is_complete() const :1498 59142x 100.0% 75.0% boost::http::parser::reset() :1511 2722x 100.0% 75.0% boost::http::parser::start() :1519 10651x 100.0% 75.0% boost::http::parser::prepare() :1526 81915x 100.0% 75.0% boost::http::parser::commit(unsigned long) :1535 80858x 100.0% 75.0% boost::http::parser::commit_eof() :1544 134x 100.0% 75.0% boost::http::parser::parse(boost::system::error_code&) :1552 98769x 100.0% 75.0% boost::http::parser::pull_body() :1561 41440x 100.0% 75.0% boost::http::parser::consume_body(unsigned long) :1570 39606x 100.0% 75.0% boost::http::parser::body() const :1578 712x 100.0% 75.0% boost::http::parser::release_buffered_data() :1586 0 0.0% 0.0% boost::http::parser::has_buffered_data() const :1594 9x 100.0% 75.0% boost::http::parser::set_body_limit(unsigned long) :1602 5x 100.0% 75.0% boost::http::parser::start_impl(bool) :1616 0 0.0% 0.0% boost::http::parser::safe_get_request() const :1624 316x 100.0% 80.0% boost::http::parser::safe_get_response() const :1632 3x 100.0% 80.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2024 Mohammad Nejati
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/http
9 //
10
11 #include <boost/http/detail/except.hpp>
12 #include <boost/http/detail/workspace.hpp>
13 #include <boost/http/error.hpp>
14 #include <boost/http/parser.hpp>
15 #include <boost/http/static_request.hpp>
16 #include <boost/http/static_response.hpp>
17
18 #include <boost/http/detail/circular_buffer.hpp>
19 #include <boost/http/detail/flat_buffer.hpp>
20
21 #include <boost/assert.hpp>
22 #include <boost/capy/buffers/buffer_copy.hpp>
23 #include <boost/capy/buffers/front.hpp>
24 #include <boost/capy/buffers/buffer_slice.hpp>
25 #include <boost/capy/ex/system_context.hpp>
26 #include <boost/http/brotli/decode.hpp>
27 #include <boost/http/zlib/error.hpp>
28 #include <boost/http/zlib/inflate.hpp>
29 #include <boost/url/grammar/ci_string.hpp>
30 #include <boost/url/grammar/error.hpp>
31 #include <boost/url/grammar/hexdig_chars.hpp>
32
33 #include "src/detail/brotli_filter_base.hpp"
34 #include "src/detail/buffer_utils.hpp"
35 #include "src/detail/zlib_filter_base.hpp"
36
37 #include <array>
38 #include <memory>
39
40 namespace boost {
41 namespace http {
42
43 /*
44 Principles for fixed-size buffer design
45
46 axiom 1:
47 To read data you must have a buffer.
48
49 axiom 2:
50 The size of the HTTP header is not
51 known in advance.
52
53 conclusion 3:
54 A single I/O can produce a complete
55 HTTP header and additional payload
56 data.
57
58 conclusion 4:
59 A single I/O can produce multiple
60 complete HTTP headers, complete
61 payloads, and a partial header or
62 payload.
63
64 axiom 5:
65 A process is in one of two states:
66 1. at or below capacity
67 2. above capacity
68
69 axiom 6:
70 A program which can allocate an
71 unbounded number of resources can
72 go above capacity.
73
74 conclusion 7:
75 A program can guarantee never going
76 above capacity if all resources are
77 provisioned at program startup.
78
79 corollary 8:
80 `parser` and `serializer` should each
81 allocate a single buffer of calculated
82 size, and never resize it.
83
84 axiom #:
85 A parser and a serializer are always
86 used in pairs.
87
88 Buffer Usage
89
90 | | begin
91 | H | p | | f | read headers
92 | H | p | | T | f | set T body
93 | H | p | | C | T | f | make codec C
94 | H | p | b | C | T | f | decode p into b
95 | H | p | b | C | T | f | read/parse loop
96 | H | | T | f | destroy codec
97 | H | | T | f | finished
98
99 H headers
100 C codec
101 T body
102 f table
103 p partial payload
104 b body data
105
106 "payload" is the bytes coming in from
107 the stream.
108
109 "body" is the logical body, after transfer
110 encoding is removed. This can be the
111 same as the payload.
112
113 A "plain payload" is when the payload and
114 body are identical (no transfer encodings).
115
116 A "buffered payload" is any payload which is
117 not plain. A second buffer is required
118 for reading.
119
120 "overread" is additional data received past
121 the end of the headers when reading headers,
122 or additional data received past the end of
123 the message payload.
124 */
125
126 namespace {
127
128 // Construct a 2-element const_buffer pair representing the first
129 // `n` bytes of `src`. Replaces the pre-#262 `capy::prefix(src, n)`
130 // idiom which yielded a slice convertible to std::array.
131 inline std::array<capy::const_buffer, 2>
132 41410x prefix_pair(
133 std::array<capy::const_buffer, 2> const& src,
134 std::size_t n) noexcept
135 {
136 41410x std::array<capy::const_buffer, 2> result{};
137 41410x if(n <= src[0].size())
138 {
139 40911x result[0] = capy::const_buffer(src[0].data(), n);
140 }
141 else
142 {
143 499x result[0] = src[0];
144 499x std::size_t remaining = n - src[0].size();
145 499x if(remaining > src[1].size())
146 remaining = src[1].size();
147 499x result[1] = capy::const_buffer(src[1].data(), remaining);
148 }
149 41410x return result;
150 }
151
152 class chained_sequence
153 {
154 char const* pos_;
155 char const* end_;
156 char const* begin_b_;
157 char const* end_b_;
158
159 public:
160 71617x chained_sequence(std::array<capy::const_buffer, 2> const& cbp)
161 71617x : pos_(static_cast<char const*>(cbp[0].data()))
162 71617x , end_(pos_ + cbp[0].size())
163 71617x , begin_b_(static_cast<char const*>(cbp[1].data()))
164 71617x , end_b_(begin_b_ + cbp[1].size())
165 {
166 71617x }
167
168 char const*
169 319930x next() noexcept
170 {
171 319930x ++pos_;
172 // most frequently taken branch
173 319930x if(pos_ < end_)
174 297556x return pos_;
175
176 // bring the second range
177 22374x if(begin_b_ != end_b_)
178 {
179 pos_ = begin_b_;
180 end_ = end_b_;
181 begin_b_ = end_b_;
182 return pos_;
183 }
184
185 // undo the increament
186 22374x pos_ = end_;
187 22374x return nullptr;
188 }
189
190 bool
191 212674x is_empty() const noexcept
192 {
193 212674x return pos_ == end_;
194 }
195
196 char
197 305475x value() const noexcept
198 {
199 305475x return *pos_;
200 }
201
202 std::size_t
203 226936x size() const noexcept
204 {
205 226936x return (end_ - pos_) + (end_b_ - begin_b_);
206 }
207 };
208
209 std::uint64_t
210 66939x parse_hex(
211 chained_sequence& cs,
212 system::error_code& ec) noexcept
213 {
214 66939x std::uint64_t v = 0;
215 66939x std::size_t init_size = cs.size();
216 154117x while(!cs.is_empty())
217 {
218 134169x auto n = grammar::hexdig_value(cs.value());
219 134169x if(n < 0)
220 {
221 46990x if(init_size == cs.size())
222 {
223 2x ec = BOOST_HTTP_ERR(
224 error::bad_payload);
225 1x return 0;
226 }
227 46989x return v;
228 }
229
230 // at least 4 significant bits are free
231 87179x if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
232 {
233 2x ec = BOOST_HTTP_ERR(
234 error::bad_payload);
235 1x return 0;
236 }
237
238 87178x v = (v << 4) | static_cast<std::uint64_t>(n);
239 87178x cs.next();
240 }
241 39896x ec = BOOST_HTTP_ERR(
242 error::need_data);
243 19948x return 0;
244 }
245
246 void
247 47341x find_eol(
248 chained_sequence& cs,
249 system::error_code& ec) noexcept
250 {
251 54030x while(!cs.is_empty())
252 {
253 53942x if(cs.value() == '\r')
254 {
255 47253x if(!cs.next())
256 330x break;
257 46923x if(cs.value() != '\n')
258 {
259 4x ec = BOOST_HTTP_ERR(
260 error::bad_payload);
261 2x return;
262 }
263 46921x cs.next();
264 46921x return;
265 }
266 6689x cs.next();
267 }
268 836x ec = BOOST_HTTP_ERR(
269 error::need_data);
270 }
271
272 void
273 62239x parse_eol(
274 chained_sequence& cs,
275 system::error_code& ec) noexcept
276 {
277 62239x if(cs.size() >= 2)
278 {
279 // we are sure size is at least 2
280 61807x if(cs.value() == '\r' && *cs.next() == '\n')
281 {
282 61804x cs.next();
283 61804x return;
284 }
285 6x ec = BOOST_HTTP_ERR(
286 error::bad_payload);
287 3x return;
288 }
289 864x ec = BOOST_HTTP_ERR(
290 error::need_data);
291 }
292
293 void
294 4243x skip_trailer_headers(
295 chained_sequence& cs,
296 system::error_code& ec) noexcept
297 {
298 4527x while(!cs.is_empty())
299 {
300 4501x if(cs.value() == '\r')
301 {
302 4149x if(!cs.next())
303 16x break;
304 4133x if(cs.value() != '\n')
305 {
306 4x ec = BOOST_HTTP_ERR(
307 error::bad_payload);
308 2x return;
309 }
310 4131x cs.next();
311 4131x return;
312 }
313 // skip to the end of field
314 352x find_eol(cs, ec);
315 352x if(ec)
316 68x return;
317 }
318 84x ec = BOOST_HTTP_ERR(
319 error::need_data);
320 }
321
322 template<class UInt>
323 std::size_t
324 193623x clamp(
325 UInt x,
326 std::size_t limit = (std::numeric_limits<
327 std::size_t>::max)()) noexcept
328 {
329 193623x if(x >= limit)
330 46527x return limit;
331 147096x return static_cast<std::size_t>(x);
332 }
333
334 class zlib_filter
335 : public detail::zlib_filter_base
336 {
337 http::zlib::inflate_service& svc_;
338
339 public:
340 zlib_filter(
341 http::zlib::inflate_service& svc,
342 int window_bits)
343 : svc_(svc)
344 {
345 system::error_code ec = static_cast<http::zlib::error>(
346 svc_.init2(strm_, window_bits));
347 if(ec != http::zlib::error::ok)
348 detail::throw_system_error(ec);
349 }
350
351 private:
352 virtual
353 results
354 do_process(
355 capy::mutable_buffer out,
356 capy::const_buffer in,
357 bool more) noexcept override
358 {
359 strm_.next_out = static_cast<unsigned char*>(out.data());
360 strm_.avail_out = saturate_cast(out.size());
361 strm_.next_in = static_cast<unsigned char*>(const_cast<void *>(in.data()));
362 strm_.avail_in = saturate_cast(in.size());
363
364 auto rs = static_cast<http::zlib::error>(
365 svc_.inflate(
366 strm_,
367 more ? http::zlib::no_flush : http::zlib::finish));
368
369 results rv;
370 rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
371 rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in;
372 rv.finished = (rs == http::zlib::error::stream_end);
373
374 if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err)
375 rv.ec = rs;
376
377 return rv;
378 }
379 };
380
381 class brotli_filter
382 : public detail::brotli_filter_base
383 {
384 http::brotli::decode_service& svc_;
385 http::brotli::decoder_state* state_;
386
387 public:
388 brotli_filter(http::brotli::decode_service& svc)
389 : svc_(svc)
390 {
391 state_ = svc_.create_instance(nullptr, nullptr, nullptr);
392 if(!state_)
393 detail::throw_bad_alloc();
394 }
395
396 ~brotli_filter()
397 {
398 svc_.destroy_instance(state_);
399 }
400
401 private:
402 virtual
403 results
404 do_process(
405 capy::mutable_buffer out,
406 capy::const_buffer in,
407 bool more) noexcept override
408 {
409 auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
410 auto available_in = in.size();
411 auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
412 auto available_out = out.size();
413
414 auto rs = svc_.decompress_stream(
415 state_,
416 &available_in,
417 &next_in,
418 &available_out,
419 &next_out,
420 nullptr);
421
422 results rv;
423 rv.in_bytes = in.size() - available_in;
424 rv.out_bytes = out.size() - available_out;
425 rv.finished = svc_.is_finished(state_);
426
427 if(!more && rs == http::brotli::decoder_result::needs_more_input)
428 rv.ec = BOOST_HTTP_ERR(error::bad_payload);
429
430 if(rs == http::brotli::decoder_result::error)
431 rv.ec = BOOST_HTTP_ERR(
432 svc_.get_error_code(state_));
433
434 return rv;
435 }
436 };
437
438 } // namespace
439
440 //------------------------------------------------
441
442 class parser::impl
443 {
444 enum class state
445 {
446 reset,
447 start,
448 header,
449 header_done,
450 body,
451 complete,
452 };
453
454 std::shared_ptr<parser_config_impl const> cfg_;
455
456 detail::workspace ws_;
457 static_request m_;
458 std::uint64_t body_limit_;
459 std::uint64_t body_total_;
460 std::uint64_t payload_remain_;
461 std::uint64_t chunk_remain_;
462 std::size_t body_avail_;
463 std::size_t nprepare_;
464
465 detail::flat_buffer fb_;
466 detail::circular_buffer cb0_;
467 detail::circular_buffer cb1_;
468
469 std::array<capy::mutable_buffer, 2> mbp_;
470 std::array<capy::const_buffer, 2> cbp_;
471
472 std::unique_ptr<detail::filter> filter_;
473
474 state state_;
475 bool got_header_;
476 bool got_eof_;
477 bool head_response_;
478 bool needs_chunk_close_;
479 bool trailer_headers_;
480 bool chunked_body_ended;
481
482 public:
483 2175x impl(std::shared_ptr<parser_config_impl const> cfg, detail::kind k)
484 2175x : cfg_(std::move(cfg))
485 2175x , ws_(cfg_->space_needed)
486 2175x , m_(ws_.data(), ws_.size())
487 2175x , state_(state::reset)
488 2175x , got_header_(false)
489 {
490 2175x m_.h_ = detail::header(detail::empty{ k });
491 2175x }
492
493 bool
494 36129x got_header() const noexcept
495 {
496 36129x return got_header_;
497 }
498
499 bool
500 59142x is_complete() const noexcept
501 {
502 59142x return state_ == state::complete;
503 }
504
505 static_request const&
506 316x safe_get_request() const
507 {
508 // headers must be received
509 316x if(! got_header_)
510 detail::throw_logic_error();
511
512 316x return m_;
513 }
514
515 static_response const&
516 3x safe_get_response() const
517 {
518 // headers must be received
519 3x if(! got_header_)
520 detail::throw_logic_error();
521
522 // TODO: use a union
523 3x return reinterpret_cast<static_response const&>(m_);
524 }
525
526 void
527 2722x reset() noexcept
528 {
529 2722x ws_.clear();
530 2722x state_ = state::start;
531 2722x got_header_ = false;
532 2722x got_eof_ = false;
533 2722x }
534
535 void
536 10651x start(
537 bool head_response)
538 {
539 10651x std::size_t leftover = 0;
540 10651x switch(state_)
541 {
542 1x default:
543 case state::reset:
544 // reset must be called first
545 1x detail::throw_logic_error();
546
547 2647x case state::start:
548 // reset required on eof
549 2647x if(got_eof_)
550 detail::throw_logic_error();
551 2647x break;
552
553 3x case state::header:
554 3x if(fb_.size() == 0)
555 {
556 // start() called twice
557 2x detail::throw_logic_error();
558 }
559 BOOST_FALLTHROUGH;
560
561 case state::header_done:
562 case state::body:
563 // current message is incomplete
564 2x detail::throw_logic_error();
565
566 7999x case state::complete:
567 {
568 // remove available body.
569 7999x if(is_plain())
570 4000x cb0_.consume(body_avail_);
571 // move leftovers to front
572
573 7999x ws_.clear();
574 7999x leftover = cb0_.size();
575
576 7999x auto* dest = reinterpret_cast<char*>(ws_.data());
577 7999x auto cbp = cb0_.data();
578 7999x auto* a = static_cast<char const*>(cbp[0].data());
579 7999x auto* b = static_cast<char const*>(cbp[1].data());
580 7999x auto an = cbp[0].size();
581 7999x auto bn = cbp[1].size();
582
583 7999x if(bn == 0)
584 {
585 7561x std::memmove(dest, a, an);
586 }
587 else
588 {
589 // if `a` can fit between `dest` and `b`, shift `b` to the left
590 // and copy `a` to its position. if `a` fits perfectly, the
591 // shift will be of size 0.
592 // if `a` requires more space, shift `b` to the right and
593 // copy `a` to its position. this process may require multiple
594 // iterations and should be done chunk by chunk to prevent `b`
595 // from overlapping with `a`.
596 do
597 {
598 // clamp right shifts to prevent overlap with `a`
599 438x auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
600 438x b = static_cast<char const*>(std::memmove(bp, b, bn));
601
602 // a chunk or all of `a` based on available space
603 438x auto chunk_a = static_cast<std::size_t>(b - dest);
604 438x std::memcpy(dest, a, chunk_a); // never overlap
605 438x an -= chunk_a;
606 438x dest += chunk_a;
607 438x a += chunk_a;
608 438x } while(an);
609 }
610
611 7999x break;
612 }
613 }
614
615 10646x ws_.clear();
616
617 21292x fb_ = {
618 10646x ws_.data(),
619 10646x cfg_->headers.max_size + cfg_->min_buffer,
620 leftover };
621
622 10646x BOOST_ASSERT(
623 fb_.capacity() == cfg_->max_overread() - leftover);
624
625 10646x BOOST_ASSERT(
626 head_response == false ||
627 m_.h_.kind == detail::kind::response);
628
629 10646x m_.h_ = detail::header(detail::empty{m_.h_.kind});
630 10646x m_.h_.buf = reinterpret_cast<char*>(ws_.data());
631 10646x m_.h_.cbuf = m_.h_.buf;
632 10646x m_.h_.cap = ws_.size();
633
634 10646x state_ = state::header;
635
636 // reset to the configured default
637 10646x body_limit_ = cfg_->body_limit;
638
639 10646x body_total_ = 0;
640 10646x payload_remain_ = 0;
641 10646x chunk_remain_ = 0;
642 10646x body_avail_ = 0;
643 10646x nprepare_ = 0;
644
645 10646x filter_.reset();
646
647 10646x got_header_ = false;
648 10646x head_response_ = head_response;
649 10646x needs_chunk_close_ = false;
650 10646x trailer_headers_ = false;
651 10646x chunked_body_ended = false;
652 10646x }
653
654 auto
655 81915x prepare() ->
656 mutable_buffers_type
657 {
658 81915x nprepare_ = 0;
659
660 81915x switch(state_)
661 {
662 1x default:
663 case state::reset:
664 // reset must be called first
665 1x detail::throw_logic_error();
666
667 1x case state::start:
668 // start must be called first
669 1x detail::throw_logic_error();
670
671 39828x case state::header:
672 {
673 39828x BOOST_ASSERT(
674 m_.h_.size < cfg_->headers.max_size);
675 39828x std::size_t n = fb_.capacity();
676 39828x BOOST_ASSERT(n <= cfg_->max_overread());
677 39828x n = clamp(n, cfg_->max_prepare);
678 39828x mbp_[0] = fb_.prepare(n);
679 39828x nprepare_ = n;
680 39828x return mutable_buffers_type(&mbp_[0], 1);
681 }
682
683 case state::header_done:
684 // forgot to call parse()
685 detail::throw_logic_error();
686
687 42084x case state::body:
688 {
689 42084x if(got_eof_)
690 {
691 // forgot to call parse()
692 detail::throw_logic_error();
693 }
694
695 42084x if(! is_plain())
696 {
697 // buffered payload
698 22017x std::size_t n = cb0_.capacity();
699 22017x n = clamp(n, cfg_->max_prepare);
700 22017x nprepare_ = n;
701 22017x mbp_ = cb0_.prepare(n);
702 22017x return detail::make_span(mbp_);
703 }
704 else
705 {
706 // plain payload
707 20067x std::size_t n = cb0_.capacity();
708 20067x n = clamp(n, cfg_->max_prepare);
709
710 20067x if(m_.payload() == payload::size)
711 {
712 20053x if(n > payload_remain_)
713 {
714 18836x std::size_t overread =
715 18836x n - static_cast<std::size_t>(payload_remain_);
716 18836x if(overread > cfg_->max_overread())
717 8920x n = static_cast<std::size_t>(payload_remain_) +
718 8920x cfg_->max_overread();
719 }
720 }
721 else
722 {
723 14x BOOST_ASSERT(
724 m_.payload() == payload::to_eof);
725 // No more messages can be pipelined, so
726 // limit the output buffer to the remaining
727 // body limit plus one byte to detect
728 // exhaustion.
729 14x std::uint64_t r = body_limit_remain();
730 14x if(r != std::uint64_t(-1))
731 14x r += 1;
732 14x n = clamp(r, n);
733 }
734
735 20067x nprepare_ = n;
736 20067x mbp_ = cb0_.prepare(n);
737 20067x return detail::make_span(mbp_);
738 }
739 }
740
741 1x case state::complete:
742 // already complete
743 1x detail::throw_logic_error();
744 }
745 }
746
747 void
748 80858x commit(
749 std::size_t n)
750 {
751 80858x switch(state_)
752 {
753 1x default:
754 case state::reset:
755 {
756 // reset must be called first
757 1x detail::throw_logic_error();
758 }
759
760 1x case state::start:
761 {
762 // forgot to call start()
763 1x detail::throw_logic_error();
764 }
765
766 39046x case state::header:
767 {
768 39046x if(n > nprepare_)
769 {
770 // n can't be greater than size of
771 // the buffers returned by prepare()
772 1x detail::throw_invalid_argument();
773 }
774
775 39045x if(got_eof_)
776 {
777 // can't commit after EOF
778 1x detail::throw_logic_error();
779 }
780
781 39044x nprepare_ = 0; // invalidate
782 39044x fb_.commit(n);
783 39044x break;
784 }
785
786 case state::header_done:
787 {
788 // forgot to call parse()
789 detail::throw_logic_error();
790 }
791
792 41810x case state::body:
793 {
794 41810x if(n > nprepare_)
795 {
796 // n can't be greater than size of
797 // the buffers returned by prepare()
798 2x detail::throw_invalid_argument();
799 }
800
801 41808x if(got_eof_)
802 {
803 // can't commit after EOF
804 detail::throw_logic_error();
805 }
806
807 41808x nprepare_ = 0; // invalidate
808 41808x cb0_.commit(n);
809 41808x break;
810 }
811
812 case state::complete:
813 {
814 // already complete
815 detail::throw_logic_error();
816 }
817 }
818 80852x }
819
820 void
821 134x commit_eof()
822 {
823 134x nprepare_ = 0; // invalidate
824
825 134x switch(state_)
826 {
827 1x default:
828 case state::reset:
829 // reset must be called first
830 1x detail::throw_logic_error();
831
832 1x case state::start:
833 // forgot to call start()
834 1x detail::throw_logic_error();
835
836 14x case state::header:
837 14x got_eof_ = true;
838 14x break;
839
840 case state::header_done:
841 // forgot to call parse()
842 detail::throw_logic_error();
843
844 117x case state::body:
845 117x got_eof_ = true;
846 117x break;
847
848 1x case state::complete:
849 // can't commit eof when complete
850 1x detail::throw_logic_error();
851 }
852 131x }
853
854 void
855 98769x parse(
856 system::error_code& ec)
857 {
858 98769x ec = {};
859 98769x switch(state_)
860 {
861 1x default:
862 case state::reset:
863 // reset must be called first
864 1x detail::throw_logic_error();
865
866 1x case state::start:
867 // start must be called first
868 1x detail::throw_logic_error();
869
870 45029x case state::header:
871 {
872 45029x BOOST_ASSERT(m_.h_.buf == static_cast<
873 void const*>(ws_.data()));
874 45029x BOOST_ASSERT(m_.h_.cbuf == static_cast<
875 void const*>(ws_.data()));
876
877 45029x m_.h_.parse(fb_.size(), cfg_->headers, ec);
878
879 45029x if(ec == condition::need_more_input)
880 {
881 35185x if(! got_eof_)
882 {
883 // headers incomplete
884 35174x return;
885 }
886
887 11x if(fb_.size() == 0)
888 {
889 // stream closed cleanly
890 6x state_ = state::reset;
891 12x ec = BOOST_HTTP_ERR(
892 error::end_of_stream);
893 6x return;
894 }
895
896 // stream closed with a
897 // partial message received
898 5x state_ = state::reset;
899 10x ec = BOOST_HTTP_ERR(
900 error::incomplete);
901 5x return;
902 }
903 9844x else if(ec)
904 {
905 // other error,
906 //
907 // VFALCO map this to a bad
908 // request or bad response error?
909 //
910 259x state_ = state::reset; // unrecoverable
911 259x return;
912 }
913
914 9585x got_header_ = true;
915
916 // reserve headers + table
917 9585x ws_.reserve_front(m_.h_.size);
918 9585x ws_.reserve_back(m_.h_.table_space());
919
920 // no payload
921 18362x if(m_.payload() == payload::none ||
922 8777x head_response_)
923 {
924 // octets of the next message
925 808x auto overread = fb_.size() - m_.h_.size;
926 808x cb0_ = { ws_.data(), overread, overread };
927 808x ws_.reserve_front(overread);
928 808x state_ = state::complete;
929 808x return;
930 }
931
932 8777x state_ = state::header_done;
933 8777x break;
934 }
935
936 8774x case state::header_done:
937 {
938 // metadata error
939 8774x if(m_.payload() == payload::error)
940 {
941 // VFALCO This needs looking at
942 120x ec = BOOST_HTTP_ERR(
943 error::bad_payload);
944 60x state_ = state::reset; // unrecoverable
945 60x return;
946 }
947
948 // overread currently includes any and all octets that
949 // extend beyond the current end of the header
950 // this can include associated body octets for the
951 // current message or octets of the next message in the
952 // stream, e.g. pipelining is being used
953 8714x auto const overread = fb_.size() - m_.h_.size;
954 8714x BOOST_ASSERT(overread <= cfg_->max_overread());
955
956 8714x auto cap = fb_.capacity() + overread +
957 8714x cfg_->min_buffer;
958
959 // reserve body buffers first, as the decoder
960 // must be installed after them.
961 8714x auto const p = ws_.reserve_front(cap);
962
963 // Content-Encoding
964 8714x switch(m_.metadata().content_encoding.coding)
965 {
966 case content_coding::deflate:
967 if(!cfg_->apply_deflate_decoder)
968 goto no_filter;
969 if(auto* svc = capy::get_system_context().find_service<http::zlib::inflate_service>())
970 {
971 filter_.reset(new zlib_filter(
972 *svc,
973 cfg_->zlib_window_bits));
974 }
975 break;
976
977 case content_coding::gzip:
978 if(!cfg_->apply_gzip_decoder)
979 goto no_filter;
980 if(auto* svc = capy::get_system_context().find_service<http::zlib::inflate_service>())
981 {
982 filter_.reset(new zlib_filter(
983 *svc,
984 cfg_->zlib_window_bits + 16));
985 }
986 break;
987
988 case content_coding::br:
989 if(!cfg_->apply_brotli_decoder)
990 goto no_filter;
991 if(auto* svc = capy::get_system_context().find_service<http::brotli::decode_service>())
992 {
993 filter_.reset(new brotli_filter(*svc));
994 }
995 break;
996
997 no_filter:
998 8714x default:
999 8714x break;
1000 }
1001
1002 8714x if(is_plain())
1003 {
1004 4385x cb0_ = { p, cap, overread };
1005 4385x cb1_ = {};
1006 }
1007 else
1008 {
1009 // buffered payload
1010 4329x std::size_t n0 = (overread > cfg_->min_buffer)
1011 8658x ? overread
1012 4329x : cfg_->min_buffer;
1013 4329x std::size_t n1 = cfg_->min_buffer;
1014
1015 4329x cb0_ = { p , n0, overread };
1016 4329x cb1_ = { p + n0 , n1 };
1017 }
1018
1019 8714x if(m_.payload() == payload::size)
1020 {
1021 8536x if(!filter_ &&
1022 4268x body_limit_ < m_.payload_size())
1023 {
1024 6x ec = BOOST_HTTP_ERR(
1025 error::body_too_large);
1026 3x state_ = state::reset;
1027 3x return;
1028 }
1029 4265x payload_remain_ = m_.payload_size();
1030 }
1031
1032 8711x state_ = state::body;
1033 BOOST_FALLTHROUGH;
1034 }
1035
1036 51462x case state::body:
1037 {
1038 51462x BOOST_ASSERT(state_ == state::body);
1039 51462x BOOST_ASSERT(m_.payload() != payload::none);
1040 51462x BOOST_ASSERT(m_.payload() != payload::error);
1041
1042 8364x auto set_state_to_complete = [&]()
1043 {
1044 8364x state_ = state::complete;
1045 59826x };
1046
1047 51462x if(m_.payload() == payload::chunked)
1048 {
1049 for(;;)
1050 {
1051 78651x if(chunk_remain_ == 0
1052 75748x && !chunked_body_ended)
1053 {
1054 71617x auto cs = chained_sequence(cb0_.data());
1055 20849x auto check_ec = [&]()
1056 {
1057 20849x if(ec == condition::need_more_input && got_eof_)
1058 {
1059 ec = BOOST_HTTP_ERR(error::incomplete);
1060 state_ = state::reset;
1061 }
1062 92466x };
1063
1064 71617x if(needs_chunk_close_)
1065 {
1066 62239x parse_eol(cs, ec);
1067 62239x if(ec)
1068 {
1069 435x check_ec();
1070 20849x return;
1071 }
1072 }
1073 9378x else if(trailer_headers_)
1074 {
1075 4243x skip_trailer_headers(cs, ec);
1076 4243x if(ec)
1077 {
1078 112x check_ec();
1079 112x return;
1080 }
1081 4131x cb0_.consume(cb0_.size() - cs.size());
1082 4131x chunked_body_ended = true;
1083 8276x continue;
1084 }
1085
1086 66939x auto chunk_size = parse_hex(cs, ec);
1087 66939x if(ec)
1088 {
1089 19950x check_ec();
1090 19950x return;
1091 }
1092
1093 // skip chunk extensions
1094 46989x find_eol(cs, ec);
1095 46989x if(ec)
1096 {
1097 352x check_ec();
1098 352x return;
1099 }
1100
1101 46637x cb0_.consume(cb0_.size() - cs.size());
1102 46637x chunk_remain_ = chunk_size;
1103
1104 46637x needs_chunk_close_ = true;
1105 46637x if(chunk_remain_ == 0)
1106 {
1107 4145x needs_chunk_close_ = false;
1108 4145x trailer_headers_ = true;
1109 4145x continue;
1110 }
1111 }
1112
1113 49526x if(cb0_.size() == 0 && !chunked_body_ended)
1114 {
1115 1830x if(got_eof_)
1116 {
1117 2x ec = BOOST_HTTP_ERR(
1118 error::incomplete);
1119 1x state_ = state::reset;
1120 1x return;
1121 }
1122
1123 3658x ec = BOOST_HTTP_ERR(
1124 error::need_data);
1125 1829x return;
1126 }
1127
1128 47696x if(filter_)
1129 {
1130 chunk_remain_ -= apply_filter(
1131 ec,
1132 clamp(chunk_remain_, cb0_.size()),
1133 !chunked_body_ended);
1134
1135 if(ec || chunked_body_ended)
1136 return;
1137 }
1138 else
1139 {
1140 const std::size_t chunk_avail =
1141 47696x clamp(chunk_remain_, cb0_.size());
1142 47696x auto cb0_data = cb0_.data();
1143 47696x auto chunk = capy::buffer_slice(
1144 cb0_data, 0, chunk_avail);
1145
1146 47696x if(body_limit_remain() < chunk_avail)
1147 {
1148 ec = BOOST_HTTP_ERR(
1149 error::body_too_large);
1150 state_ = state::reset;
1151 4131x return;
1152 }
1153
1154 // in_place style
1155 47696x auto copied = capy::buffer_copy(
1156 47696x cb1_.prepare(cb1_.capacity()),
1157 chunk);
1158 47696x chunk_remain_ -= copied;
1159 47696x body_avail_ += copied;
1160 47696x body_total_ += copied;
1161 47696x cb0_.consume(copied);
1162 47696x cb1_.commit(copied);
1163 47696x if(cb1_.capacity() == 0
1164 47696x && !chunked_body_ended)
1165 {
1166 ec = BOOST_HTTP_ERR(
1167 error::in_place_overflow);
1168 return;
1169 }
1170
1171 47696x if(chunked_body_ended)
1172 {
1173 4131x set_state_to_complete();
1174 4131x return;
1175 }
1176 }
1177 51841x }
1178 }
1179 else
1180 {
1181 // non-chunked payload
1182
1183 73956x const std::size_t payload_avail = [&]()
1184 {
1185 24652x auto ret = cb0_.size();
1186 24652x if(!filter_)
1187 24652x ret -= body_avail_;
1188 24652x if(m_.payload() == payload::size)
1189 24395x return clamp(payload_remain_, ret);
1190 // payload::eof
1191 257x return ret;
1192 24652x }();
1193
1194 73956x const bool is_complete = [&]()
1195 {
1196 24652x if(m_.payload() == payload::size)
1197 24395x return payload_avail == payload_remain_;
1198 // payload::eof
1199 257x return got_eof_;
1200 24652x }();
1201
1202 24652x if(filter_)
1203 {
1204 payload_remain_ -= apply_filter(
1205 ec, payload_avail, !is_complete);
1206 if(ec || is_complete)
1207 return;
1208 }
1209 else
1210 {
1211 // plain body
1212
1213 24652x if(m_.payload() == payload::to_eof)
1214 {
1215 257x if(body_limit_remain() < payload_avail)
1216 {
1217 2x ec = BOOST_HTTP_ERR(
1218 error::body_too_large);
1219 1x state_ = state::reset;
1220 1x return;
1221 }
1222 }
1223
1224 // in_place style
1225 24651x payload_remain_ -= payload_avail;
1226 24651x body_avail_ += payload_avail;
1227 24651x body_total_ += payload_avail;
1228 24651x if(cb0_.capacity() == 0 && !is_complete)
1229 {
1230 14x ec = BOOST_HTTP_ERR(
1231 error::in_place_overflow);
1232 7x return;
1233 }
1234
1235 24644x if(is_complete)
1236 {
1237 4233x set_state_to_complete();
1238 4233x return;
1239 }
1240 }
1241
1242 20411x if(m_.payload() == payload::size && got_eof_)
1243 {
1244 2x ec = BOOST_HTTP_ERR(
1245 error::incomplete);
1246 1x state_ = state::reset;
1247 1x return;
1248 }
1249
1250 40820x ec = BOOST_HTTP_ERR(
1251 error::need_data);
1252 20410x return;
1253 }
1254
1255 break;
1256 }
1257
1258 2213x case state::complete:
1259 2213x break;
1260 }
1261 }
1262
1263 auto
1264 41440x pull_body() ->
1265 const_buffers_type
1266 {
1267 41440x switch(state_)
1268 {
1269 28x case state::header_done:
1270 28x return {};
1271 41410x case state::body:
1272 case state::complete:
1273 41410x cbp_ = prefix_pair(
1274 41410x (is_plain() ? cb0_ : cb1_).data(),
1275 body_avail_);
1276 41410x return detail::make_span(cbp_);
1277 2x case state::reset:
1278 2x if(got_header_)
1279 2x return {};
1280 BOOST_FALLTHROUGH;
1281 default:
1282 detail::throw_logic_error();
1283 }
1284 }
1285
1286 void
1287 39606x consume_body(std::size_t n)
1288 {
1289 39606x switch(state_)
1290 {
1291 case state::header_done:
1292 return;
1293 39606x case state::body:
1294 case state::complete:
1295 39606x n = clamp(n, body_avail_);
1296 39606x (is_plain() ? cb0_ : cb1_).consume(n);
1297 39606x body_avail_ -= n;
1298 39606x return;
1299 case state::reset:
1300 if(got_header_)
1301 return;
1302 BOOST_FALLTHROUGH;
1303 default:
1304 detail::throw_logic_error();
1305 }
1306 }
1307
1308 core::string_view
1309 712x body() const
1310 {
1311 // Precondition violation
1312 712x if(state_ != state::complete)
1313 detail::throw_logic_error();
1314
1315 // Precondition violation
1316 712x if(body_avail_ != body_total_)
1317 detail::throw_logic_error();
1318
1319 712x auto cbp = (is_plain() ? cb0_ : cb1_).data();
1320 712x BOOST_ASSERT(body_avail_ <= cbp[0].size());
1321 712x return core::string_view(
1322 712x static_cast<char const*>(cbp[0].data()),
1323 1424x body_avail_);
1324 }
1325
1326 bool
1327 9x has_buffered_data() const noexcept
1328 {
1329 9x if(state_ != state::complete)
1330 1x return false;
1331
1332 8x if(is_plain())
1333 6x return cb0_.size() > body_avail_;
1334 2x return cb0_.size() > 0;
1335 }
1336
1337 void
1338 5x set_body_limit(std::uint64_t n)
1339 {
1340 5x switch(state_)
1341 {
1342 1x case state::header:
1343 case state::header_done:
1344 1x body_limit_ = n;
1345 1x break;
1346 2x case state::complete:
1347 // only allowed for empty bodies
1348 2x if(body_total_ == 0)
1349 1x break;
1350 BOOST_FALLTHROUGH;
1351 default:
1352 // set body_limit before parsing the body
1353 3x detail::throw_logic_error();
1354 }
1355 2x }
1356
1357 private:
1358 bool
1359 140533x is_plain() const noexcept
1360 {
1361 281066x return ! filter_ &&
1362 281066x m_.payload() != payload::chunked;
1363 }
1364
1365 std::uint64_t
1366 47967x body_limit_remain() const noexcept
1367 {
1368 47967x return body_limit_ - body_total_;
1369 }
1370
1371 std::size_t
1372 apply_filter(
1373 system::error_code& ec,
1374 std::size_t payload_avail,
1375 bool more)
1376 {
1377 std::size_t p0 = payload_avail;
1378 for(;;)
1379 {
1380 if(payload_avail == 0 && more)
1381 break;
1382
1383 auto f_rs = [&](){
1384 BOOST_ASSERT(filter_ != nullptr);
1385 std::size_t n = clamp(body_limit_remain());
1386 n = clamp(n, cb1_.capacity());
1387
1388 return filter_->process(
1389 detail::make_span(cb1_.prepare(n)),
1390 prefix_pair(cb0_.data(), payload_avail),
1391 more);
1392 }();
1393
1394 cb0_.consume(f_rs.in_bytes);
1395 payload_avail -= f_rs.in_bytes;
1396 body_total_ += f_rs.out_bytes;
1397
1398 // in_place style
1399 cb1_.commit(f_rs.out_bytes);
1400 body_avail_ += f_rs.out_bytes;
1401 if(cb1_.capacity() == 0 &&
1402 !f_rs.finished && f_rs.in_bytes == 0)
1403 {
1404 ec = BOOST_HTTP_ERR(
1405 error::in_place_overflow);
1406 goto done;
1407 }
1408
1409 if(f_rs.ec)
1410 {
1411 ec = f_rs.ec;
1412 state_ = state::reset;
1413 break;
1414 }
1415
1416 if(body_limit_remain() == 0 &&
1417 !f_rs.finished && f_rs.in_bytes == 0)
1418 {
1419 ec = BOOST_HTTP_ERR(
1420 error::body_too_large);
1421 state_ = state::reset;
1422 break;
1423 }
1424
1425 if(f_rs.finished)
1426 {
1427 if(!more)
1428 state_ = state::complete;
1429 break;
1430 }
1431 }
1432
1433 done:
1434 return p0 - payload_avail;
1435 }
1436 };
1437
1438 //------------------------------------------------
1439 //
1440 // Special Members
1441 //
1442 //------------------------------------------------
1443
1444 2190x parser::
1445 ~parser()
1446 {
1447 2190x delete impl_;
1448 2190x }
1449
1450 12x parser::
1451 12x parser() noexcept
1452 12x : impl_(nullptr)
1453 {
1454 12x }
1455
1456 3x parser::
1457 3x parser(parser&& other) noexcept
1458 3x : impl_(other.impl_)
1459 {
1460 3x other.impl_ = nullptr;
1461 3x }
1462
1463 2175x parser::
1464 parser(
1465 std::shared_ptr<parser_config_impl const> cfg,
1466 2175x detail::kind k)
1467 2175x : impl_(new impl(std::move(cfg), k))
1468 {
1469 // TODO: use a single allocation for
1470 // impl and workspace buffer.
1471 2175x }
1472
1473 void
1474 4x parser::
1475 assign(parser&& other) noexcept
1476 {
1477 4x if(this == &other)
1478 return;
1479 4x delete impl_;
1480 4x impl_ = other.impl_;
1481 4x other.impl_ = nullptr;
1482 }
1483
1484 //--------------------------------------------
1485 //
1486 // Observers
1487 //
1488 //--------------------------------------------
1489
1490 bool
1491 36129x parser::got_header() const noexcept
1492 {
1493 36129x BOOST_ASSERT(impl_);
1494 36129x return impl_->got_header();
1495 }
1496
1497 bool
1498 59142x parser::is_complete() const noexcept
1499 {
1500 59142x BOOST_ASSERT(impl_);
1501 59142x return impl_->is_complete();
1502 }
1503
1504 //------------------------------------------------
1505 //
1506 // Modifiers
1507 //
1508 //------------------------------------------------
1509
1510 void
1511 2722x parser::
1512 reset() noexcept
1513 {
1514 2722x BOOST_ASSERT(impl_);
1515 2722x impl_->reset();
1516 2722x }
1517
1518 void
1519 10651x parser::start()
1520 {
1521 10651x BOOST_ASSERT(impl_);
1522 10651x impl_->start(false);
1523 10646x }
1524
1525 auto
1526 81915x parser::
1527 prepare() ->
1528 mutable_buffers_type
1529 {
1530 81915x BOOST_ASSERT(impl_);
1531 81915x return impl_->prepare();
1532 }
1533
1534 void
1535 80858x parser::
1536 commit(
1537 std::size_t n)
1538 {
1539 80858x BOOST_ASSERT(impl_);
1540 80858x impl_->commit(n);
1541 80852x }
1542
1543 void
1544 134x parser::
1545 commit_eof()
1546 {
1547 134x BOOST_ASSERT(impl_);
1548 134x impl_->commit_eof();
1549 131x }
1550
1551 void
1552 98769x parser::
1553 parse(
1554 system::error_code& ec)
1555 {
1556 98769x BOOST_ASSERT(impl_);
1557 98769x impl_->parse(ec);
1558 98767x }
1559
1560 auto
1561 41440x parser::
1562 pull_body() ->
1563 const_buffers_type
1564 {
1565 41440x BOOST_ASSERT(impl_);
1566 41440x return impl_->pull_body();
1567 }
1568
1569 void
1570 39606x parser::
1571 consume_body(std::size_t n)
1572 {
1573 39606x BOOST_ASSERT(impl_);
1574 39606x impl_->consume_body(n);
1575 39606x }
1576
1577 core::string_view
1578 712x parser::
1579 body() const
1580 {
1581 712x BOOST_ASSERT(impl_);
1582 712x return impl_->body();
1583 }
1584
1585 core::string_view
1586 parser::
1587 release_buffered_data() noexcept
1588 {
1589 // TODO
1590 return {};
1591 }
1592
1593 bool
1594 9x parser::
1595 has_buffered_data() const noexcept
1596 {
1597 9x BOOST_ASSERT(impl_);
1598 9x return impl_->has_buffered_data();
1599 }
1600
1601 void
1602 5x parser::
1603 set_body_limit(std::uint64_t n)
1604 {
1605 5x BOOST_ASSERT(impl_);
1606 5x impl_->set_body_limit(n);
1607 2x }
1608
1609 //------------------------------------------------
1610 //
1611 // Implementation
1612 //
1613 //------------------------------------------------
1614
1615 void
1616 parser::
1617 start_impl(bool head_response)
1618 {
1619 BOOST_ASSERT(impl_);
1620 impl_->start(head_response);
1621 }
1622
1623 static_request const&
1624 316x parser::
1625 safe_get_request() const
1626 {
1627 316x BOOST_ASSERT(impl_);
1628 316x return impl_->safe_get_request();
1629 }
1630
1631 static_response const&
1632 3x parser::
1633 safe_get_response() const
1634 {
1635 3x BOOST_ASSERT(impl_);
1636 3x return impl_->safe_get_response();
1637 }
1638
1639 } // http
1640 } // boost
1641