src/parser.cpp

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