Line | Branch | Exec | Source |
---|---|---|---|
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_proto | ||
9 | // | ||
10 | |||
11 | #include <boost/http_proto/context.hpp> | ||
12 | #include <boost/http_proto/detail/except.hpp> | ||
13 | #include <boost/http_proto/error.hpp> | ||
14 | #include <boost/http_proto/parser.hpp> | ||
15 | #include <boost/http_proto/rfc/detail/rules.hpp> | ||
16 | #include <boost/http_proto/service/zlib_service.hpp> | ||
17 | |||
18 | #include <boost/assert.hpp> | ||
19 | #include <boost/buffers/copy.hpp> | ||
20 | #include <boost/buffers/prefix.hpp> | ||
21 | #include <boost/buffers/size.hpp> | ||
22 | #include <boost/buffers/make_buffer.hpp> | ||
23 | #include <boost/url/grammar/ci_string.hpp> | ||
24 | #include <boost/url/grammar/hexdig_chars.hpp> | ||
25 | |||
26 | #include "detail/filter.hpp" | ||
27 | |||
28 | namespace boost { | ||
29 | namespace http_proto { | ||
30 | |||
31 | /* | ||
32 | Principles for fixed-size buffer design | ||
33 | |||
34 | axiom 1: | ||
35 | To read data you must have a buffer. | ||
36 | |||
37 | axiom 2: | ||
38 | The size of the HTTP header is not | ||
39 | known in advance. | ||
40 | |||
41 | conclusion 3: | ||
42 | A single I/O can produce a complete | ||
43 | HTTP header and additional payload | ||
44 | data. | ||
45 | |||
46 | conclusion 4: | ||
47 | A single I/O can produce multiple | ||
48 | complete HTTP headers, complete | ||
49 | payloads, and a partial header or | ||
50 | payload. | ||
51 | |||
52 | axiom 5: | ||
53 | A process is in one of two states: | ||
54 | 1. at or below capacity | ||
55 | 2. above capacity | ||
56 | |||
57 | axiom 6: | ||
58 | A program which can allocate an | ||
59 | unbounded number of resources can | ||
60 | go above capacity. | ||
61 | |||
62 | conclusion 7: | ||
63 | A program can guarantee never going | ||
64 | above capacity if all resources are | ||
65 | provisioned at program startup. | ||
66 | |||
67 | corollary 8: | ||
68 | `parser` and `serializer` should each | ||
69 | allocate a single buffer of calculated | ||
70 | size, and never resize it. | ||
71 | |||
72 | axiom #: | ||
73 | A parser and a serializer are always | ||
74 | used in pairs. | ||
75 | |||
76 | Buffer Usage | ||
77 | |||
78 | | | begin | ||
79 | | H | p | | f | read headers | ||
80 | | H | p | | T | f | set T body | ||
81 | | H | p | | C | T | f | make codec C | ||
82 | | H | p | b | C | T | f | decode p into b | ||
83 | | H | p | b | C | T | f | read/parse loop | ||
84 | | H | | T | f | destroy codec | ||
85 | | H | | T | f | finished | ||
86 | |||
87 | H headers | ||
88 | C codec | ||
89 | T body | ||
90 | f table | ||
91 | p partial payload | ||
92 | b body data | ||
93 | |||
94 | "payload" is the bytes coming in from | ||
95 | the stream. | ||
96 | |||
97 | "body" is the logical body, after transfer | ||
98 | encoding is removed. This can be the | ||
99 | same as the payload. | ||
100 | |||
101 | A "plain payload" is when the payload and | ||
102 | body are identical (no transfer encodings). | ||
103 | |||
104 | A "buffered payload" is any payload which is | ||
105 | not plain. A second buffer is required | ||
106 | for reading. | ||
107 | |||
108 | "overread" is additional data received past | ||
109 | the end of the headers when reading headers, | ||
110 | or additional data received past the end of | ||
111 | the message payload. | ||
112 | */ | ||
113 | |||
114 | namespace { | ||
115 | class inflator_filter | ||
116 | : public http_proto::detail::filter | ||
117 | { | ||
118 | zlib::stream& inflator_; | ||
119 | |||
120 | public: | ||
121 | 73 | inflator_filter( | |
122 | context& ctx, | ||
123 | http_proto::detail::workspace& ws, | ||
124 | bool use_gzip) | ||
125 | 292 | : inflator_{ ctx.get_service<zlib::service>() | |
126 |
2/2✓ Branch 2 taken 36 times.
✓ Branch 3 taken 37 times.
|
73 | .make_inflator(ws, use_gzip ? 31 : 15) } |
127 | { | ||
128 | 73 | } | |
129 | |||
130 | virtual filter::results | ||
131 | 119418 | on_process( | |
132 | buffers::mutable_buffer out, | ||
133 | buffers::const_buffer in, | ||
134 | bool more) override | ||
135 | { | ||
136 | 119418 | auto flush = | |
137 |
2/2✓ Branch 0 taken 119303 times.
✓ Branch 1 taken 115 times.
|
119418 | more ? zlib::flush::none : zlib::flush::finish; |
138 | 119418 | filter::results results; | |
139 | |||
140 | for(;;) | ||
141 | { | ||
142 | 119418 | auto params = zlib::params{in.data(), in.size(), | |
143 | 119418 | out.data(), out.size() }; | |
144 | 119418 | auto ec = inflator_.write(params, flush); | |
145 | |||
146 | 119418 | results.in_bytes += in.size() - params.avail_in; | |
147 | 119418 | results.out_bytes += out.size() - params.avail_out; | |
148 | |||
149 |
4/4✓ Branch 1 taken 58922 times.
✓ Branch 2 taken 60496 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 58921 times.
|
178340 | if( ec.failed() && |
150 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 119417 times.
|
178340 | ec != zlib::error::buf_err ) |
151 | { | ||
152 | 1 | results.ec = ec; | |
153 | 119418 | return results; | |
154 | } | ||
155 | |||
156 |
2/2✓ Branch 2 taken 120 times.
✓ Branch 3 taken 119297 times.
|
119417 | if( ec == zlib::error::stream_end ) |
157 | { | ||
158 | 120 | results.finished = true; | |
159 | 120 | return results; | |
160 | } | ||
161 | |||
162 | 119297 | in = buffers::suffix(in, params.avail_in); | |
163 | 119297 | out = buffers::suffix(out, params.avail_out); | |
164 | |||
165 |
4/6✓ Branch 1 taken 5991 times.
✓ Branch 2 taken 113306 times.
✓ Branch 4 taken 5991 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 119297 times.
✗ Branch 7 not taken.
|
119297 | if( in.size() == 0 || out.size() == 0 ) |
166 | 119297 | return results; | |
167 | ✗ | } | |
168 | } | ||
169 | }; | ||
170 | |||
171 | class chained_sequence | ||
172 | { | ||
173 | char const* pos_; | ||
174 | char const* end_; | ||
175 | char const* begin_b_; | ||
176 | char const* end_b_; | ||
177 | |||
178 | public: | ||
179 | 125668 | chained_sequence(buffers::const_buffer_pair const& cbp) | |
180 | 125668 | : pos_(static_cast<char const*>(cbp[0].data())) | |
181 | 125668 | , end_(pos_ + cbp[0].size()) | |
182 | 125668 | , begin_b_(static_cast<char const*>(cbp[1].data())) | |
183 | 125668 | , end_b_(begin_b_ + cbp[1].size()) | |
184 | { | ||
185 | 125668 | } | |
186 | |||
187 | char const* | ||
188 | 651569 | next() noexcept | |
189 | { | ||
190 | 651569 | ++pos_; | |
191 | // most frequently taken branch | ||
192 |
2/2✓ Branch 0 taken 630246 times.
✓ Branch 1 taken 21323 times.
|
651569 | if(pos_ < end_) |
193 | 630246 | return pos_; | |
194 | |||
195 | // swap with the second range | ||
196 |
2/2✓ Branch 0 taken 39 times.
✓ Branch 1 taken 21284 times.
|
21323 | if(begin_b_ != end_b_) |
197 | { | ||
198 | 39 | pos_ = begin_b_; | |
199 | 39 | end_ = end_b_; | |
200 | 39 | begin_b_ = end_b_; | |
201 | 39 | return pos_; | |
202 | } | ||
203 | |||
204 | // undo the increament | ||
205 | 21284 | pos_ = end_; | |
206 | 21284 | return nullptr; | |
207 | } | ||
208 | |||
209 | bool | ||
210 | 432880 | empty() const noexcept | |
211 | { | ||
212 | 432880 | return pos_ == end_; | |
213 | } | ||
214 | |||
215 | char | ||
216 | 637061 | value() const noexcept | |
217 | { | ||
218 | 637061 | return *pos_; | |
219 | } | ||
220 | |||
221 | std::size_t | ||
222 | 446856 | size() const noexcept | |
223 | { | ||
224 | 446856 | return (end_ - pos_) + (end_b_ - begin_b_); | |
225 | } | ||
226 | }; | ||
227 | |||
228 | std::uint64_t | ||
229 | 121428 | parse_hex( | |
230 | chained_sequence& cs, | ||
231 | system::error_code& ec) noexcept | ||
232 | { | ||
233 | 121428 | std::uint64_t v = 0; | |
234 | 121428 | std::size_t init_size = cs.size(); | |
235 |
2/2✓ Branch 1 taken 299906 times.
✓ Branch 2 taken 19283 times.
|
319189 | while(!cs.empty()) |
236 | { | ||
237 | 299906 | auto n = grammar::hexdig_value(cs.value()); | |
238 |
2/2✓ Branch 0 taken 102144 times.
✓ Branch 1 taken 197762 times.
|
299906 | if(n < 0) |
239 | { | ||
240 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 102143 times.
|
102144 | if(init_size == cs.size()) |
241 | { | ||
242 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
243 | error::bad_payload); | ||
244 | 1 | return 0; | |
245 | } | ||
246 | 102143 | return v; | |
247 | } | ||
248 | |||
249 | // at least 4 significant bits are free | ||
250 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 197761 times.
|
197762 | if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4) |
251 | { | ||
252 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
253 | error::bad_payload); | ||
254 | 1 | return 0; | |
255 | } | ||
256 | |||
257 | 197761 | v = (v << 4) | static_cast<std::uint64_t>(n); | |
258 | 197761 | cs.next(); | |
259 | } | ||
260 | 38566 | ec = BOOST_HTTP_PROTO_ERR( | |
261 | error::need_data); | ||
262 | 19283 | return 0; | |
263 | } | ||
264 | |||
265 | void | ||
266 | 102495 | find_eol( | |
267 | chained_sequence& cs, | ||
268 | system::error_code& ec) noexcept | ||
269 | { | ||
270 |
2/2✓ Branch 1 taken 109096 times.
✓ Branch 2 taken 88 times.
|
109184 | while(!cs.empty()) |
271 | { | ||
272 |
2/2✓ Branch 1 taken 102407 times.
✓ Branch 2 taken 6689 times.
|
109096 | if(cs.value() == '\r') |
273 | { | ||
274 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 102397 times.
|
102407 | if(!cs.next()) |
275 | 10 | break; | |
276 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 102395 times.
|
102397 | if(cs.value() != '\n') |
277 | { | ||
278 | 4 | ec = BOOST_HTTP_PROTO_ERR( | |
279 | error::bad_payload); | ||
280 | 2 | return; | |
281 | } | ||
282 | 102395 | cs.next(); | |
283 | 102395 | return; | |
284 | } | ||
285 | 6689 | cs.next(); | |
286 | } | ||
287 | 196 | ec = BOOST_HTTP_PROTO_ERR( | |
288 | error::need_data); | ||
289 | } | ||
290 | |||
291 | void | ||
292 | 117028 | parse_eol( | |
293 | chained_sequence& cs, | ||
294 | system::error_code& ec) noexcept | ||
295 | { | ||
296 |
2/2✓ Branch 1 taken 117014 times.
✓ Branch 2 taken 14 times.
|
117028 | if(cs.size() >= 2) |
297 | { | ||
298 | // we are sure size is at least 2 | ||
299 |
6/6✓ Branch 1 taken 117012 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 117011 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 117011 times.
✓ Branch 7 taken 3 times.
|
117014 | if(cs.value() == '\r' && *cs.next() == '\n') |
300 | { | ||
301 | 117011 | cs.next(); | |
302 | 117011 | return; | |
303 | } | ||
304 | 6 | ec = BOOST_HTTP_PROTO_ERR( | |
305 | error::bad_payload); | ||
306 | 3 | return; | |
307 | } | ||
308 | 28 | ec = BOOST_HTTP_PROTO_ERR( | |
309 | error::need_data); | ||
310 | } | ||
311 | |||
312 | void | ||
313 | 4223 | skip_trailer_headers( | |
314 | chained_sequence& cs, | ||
315 | system::error_code& ec) noexcept | ||
316 | { | ||
317 |
2/2✓ Branch 1 taken 4501 times.
✓ Branch 2 taken 6 times.
|
4507 | while(!cs.empty()) |
318 | { | ||
319 |
2/2✓ Branch 1 taken 4149 times.
✓ Branch 2 taken 352 times.
|
4501 | if(cs.value() == '\r') |
320 | { | ||
321 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4147 times.
|
4149 | if(!cs.next()) |
322 | 2 | break; | |
323 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4145 times.
|
4147 | if(cs.value() != '\n') |
324 | { | ||
325 | 4 | ec = BOOST_HTTP_PROTO_ERR( | |
326 | error::bad_payload); | ||
327 | 2 | return; | |
328 | } | ||
329 | 4145 | cs.next(); | |
330 | 4145 | return; | |
331 | } | ||
332 | // skip to the end of field | ||
333 | 352 | find_eol(cs, ec); | |
334 |
2/2✓ Branch 1 taken 68 times.
✓ Branch 2 taken 284 times.
|
352 | if(ec) |
335 | 68 | return; | |
336 | } | ||
337 | 16 | ec = BOOST_HTTP_PROTO_ERR( | |
338 | error::need_data); | ||
339 | } | ||
340 | |||
341 | template<class UInt> | ||
342 | std::size_t | ||
343 | 377847 | clamp( | |
344 | UInt x, | ||
345 | std::size_t limit = (std::numeric_limits< | ||
346 | std::size_t>::max)()) noexcept | ||
347 | { | ||
348 |
2/2✓ Branch 0 taken 105520 times.
✓ Branch 1 taken 272327 times.
|
377847 | if(x >= limit) |
349 | 105520 | return limit; | |
350 | 272327 | return static_cast<std::size_t>(x); | |
351 | } | ||
352 | } // namespace | ||
353 | |||
354 | class parser_service | ||
355 | : public service | ||
356 | { | ||
357 | public: | ||
358 | parser::config_base cfg; | ||
359 | std::size_t space_needed = 0; | ||
360 | std::size_t max_codec = 0; | ||
361 | zlib::service const* zlib_svc = nullptr; | ||
362 | |||
363 | parser_service( | ||
364 | context& ctx, | ||
365 | parser::config_base const& cfg_); | ||
366 | |||
367 | std::size_t | ||
368 | 55253 | max_overread() const noexcept | |
369 | { | ||
370 | return | ||
371 | 55253 | cfg.headers.max_size + | |
372 | 55253 | cfg.min_buffer; | |
373 | } | ||
374 | }; | ||
375 | |||
376 | 40 | parser_service:: | |
377 | parser_service( | ||
378 | context& ctx, | ||
379 | 40 | parser::config_base const& cfg_) | |
380 | 40 | : cfg(cfg_) | |
381 | { | ||
382 | /* | ||
383 | | fb | cb0 | cb1 | C | T | f | | ||
384 | |||
385 | fb flat_buffer headers.max_size | ||
386 | cb0 circular_buffer min_buffer | ||
387 | cb1 circular_buffer min_buffer | ||
388 | C codec max_codec | ||
389 | T body max_type_erase | ||
390 | f table max_table_space | ||
391 | |||
392 | */ | ||
393 | // validate | ||
394 | //if(cfg.min_prepare > cfg.max_prepare) | ||
395 | //detail::throw_invalid_argument(); | ||
396 | |||
397 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
|
40 | if(cfg.max_prepare < 1) |
398 | ✗ | detail::throw_invalid_argument(); | |
399 | |||
400 | // VFALCO TODO OVERFLOW CHECING | ||
401 | { | ||
402 | //fb_.size() - h_.size + | ||
403 | //svc_.cfg.min_buffer + | ||
404 | //svc_.cfg.min_buffer + | ||
405 | //svc_.max_codec; | ||
406 | } | ||
407 | |||
408 | // VFALCO OVERFLOW CHECKING ON THIS | ||
409 | 40 | space_needed += | |
410 |
1/2✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
|
40 | cfg.headers.valid_space_needed(); |
411 | |||
412 | // cb0_, cb1_ | ||
413 | // VFALCO OVERFLOW CHECKING ON THIS | ||
414 | 40 | space_needed += | |
415 | 40 | cfg.min_buffer + | |
416 | cfg.min_buffer; | ||
417 | |||
418 | // T | ||
419 | 40 | space_needed += cfg.max_type_erase; | |
420 | |||
421 | // max_codec | ||
422 | { | ||
423 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 37 times.
|
40 | if(cfg.apply_deflate_decoder) |
424 | { | ||
425 | auto const n = ctx.get_service< | ||
426 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | zlib::service>().inflator_space_needed(15); |
427 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | if( max_codec < n) |
428 | 3 | max_codec = n; | |
429 | } | ||
430 | } | ||
431 | 40 | space_needed += max_codec; | |
432 | |||
433 | // round up to alignof(detail::header::entry) | ||
434 | 40 | auto const al = alignof( | |
435 | detail::header::entry); | ||
436 | 40 | space_needed = al * (( | |
437 | 40 | space_needed + al - 1) / al); | |
438 | 40 | } | |
439 | |||
440 | void | ||
441 | 40 | install_parser_service( | |
442 | context& ctx, | ||
443 | parser::config_base const& cfg) | ||
444 | { | ||
445 | ctx.make_service< | ||
446 | 40 | parser_service>(cfg); | |
447 | 40 | } | |
448 | |||
449 | //------------------------------------------------ | ||
450 | // | ||
451 | // Special Members | ||
452 | // | ||
453 | //------------------------------------------------ | ||
454 | |||
455 | 1052 | parser:: | |
456 | 1052 | parser(context& ctx, detail::kind k) | |
457 | 1052 | : ctx_(ctx) | |
458 | 1052 | , svc_(ctx.get_service<parser_service>()) | |
459 | 1052 | , h_(detail::empty{ k }) | |
460 | 1052 | , st_(state::reset) | |
461 | 2104 | , got_header_(false) | |
462 | { | ||
463 | 1052 | auto const n = svc_.space_needed; | |
464 |
1/2✓ Branch 1 taken 1052 times.
✗ Branch 2 not taken.
|
1052 | ws_.allocate(n); |
465 | 1052 | h_.cap = n; | |
466 | 1052 | } | |
467 | |||
468 | 1052 | parser:: | |
469 | ~parser() | ||
470 | { | ||
471 | 1052 | } | |
472 | |||
473 | //-------------------------------------------- | ||
474 | // | ||
475 | // Observers | ||
476 | // | ||
477 | //-------------------------------------------- | ||
478 | |||
479 | bool | ||
480 | 11938 | parser::got_header() const noexcept | |
481 | { | ||
482 | 11938 | return got_header_; | |
483 | } | ||
484 | |||
485 | bool | ||
486 | 51343 | parser::is_complete() const noexcept | |
487 | { | ||
488 | 51343 | return st_ >= state::complete_in_place; | |
489 | } | ||
490 | |||
491 | //------------------------------------------------ | ||
492 | // | ||
493 | // Modifiers | ||
494 | // | ||
495 | //------------------------------------------------ | ||
496 | |||
497 | void | ||
498 | 2480 | parser:: | |
499 | reset() noexcept | ||
500 | { | ||
501 | 2480 | ws_.clear(); | |
502 | 2480 | st_ = state::start; | |
503 | 2480 | got_header_ = false; | |
504 | 2480 | got_eof_ = false; | |
505 | 2480 | } | |
506 | |||
507 | void | ||
508 | 10307 | parser::start() | |
509 | { | ||
510 | 10307 | start_impl(false); | |
511 | 10302 | } | |
512 | |||
513 | void | ||
514 | 10307 | parser:: | |
515 | start_impl( | ||
516 | bool head_response) | ||
517 | { | ||
518 | 10307 | std::size_t leftover = 0; | |
519 |
6/6✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2255 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 8015 times.
✓ Branch 5 taken 32 times.
|
10307 | switch(st_) |
520 | { | ||
521 | 1 | default: | |
522 | case state::reset: | ||
523 | // reset must be called first | ||
524 | 1 | detail::throw_logic_error(); | |
525 | |||
526 | 2255 | case state::start: | |
527 | // reset required on eof | ||
528 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2255 times.
|
2255 | if(got_eof_) |
529 | ✗ | detail::throw_logic_error(); | |
530 | 2255 | break; | |
531 | |||
532 | 3 | case state::header: | |
533 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
|
3 | if(fb_.size() == 0) |
534 | { | ||
535 | // start() called twice | ||
536 | 2 | detail::throw_logic_error(); | |
537 | } | ||
538 | BOOST_FALLTHROUGH; | ||
539 | |||
540 | case state::header_done: | ||
541 | case state::body: | ||
542 | case state::set_body: | ||
543 | // current message is incomplete | ||
544 | 2 | detail::throw_logic_error(); | |
545 | |||
546 | 8015 | case state::complete_in_place: | |
547 | // remove available body. | ||
548 |
2/2✓ Branch 1 taken 4000 times.
✓ Branch 2 taken 4015 times.
|
8015 | if(is_plain()) |
549 | 4000 | cb0_.consume(body_avail_); | |
550 | BOOST_FALLTHROUGH; | ||
551 | |||
552 | case state::complete: | ||
553 | { | ||
554 | // move leftovers to front | ||
555 | |||
556 | 8047 | ws_.clear(); | |
557 | 8047 | leftover = cb0_.size(); | |
558 | |||
559 | 8047 | auto* dest = reinterpret_cast<char*>(ws_.data()); | |
560 | 8047 | auto cbp = cb0_.data(); | |
561 | 8047 | auto* a = static_cast<char const*>(cbp[0].data()); | |
562 | 8047 | auto* b = static_cast<char const*>(cbp[1].data()); | |
563 | 8047 | auto an = cbp[0].size(); | |
564 | 8047 | auto bn = cbp[1].size(); | |
565 | |||
566 |
2/2✓ Branch 0 taken 7609 times.
✓ Branch 1 taken 438 times.
|
8047 | if(bn == 0) |
567 | { | ||
568 | 7609 | std::memmove(dest, a, an); | |
569 | } | ||
570 | else | ||
571 | { | ||
572 | // if `a` can fit between `dest` and `b`, shift `b` to the left | ||
573 | // and copy `a` to its position. if `a` fits perfectly, the | ||
574 | // shift will be of size 0. | ||
575 | // if `a` requires more space, shift `b` to the right and | ||
576 | // copy `a` to its position. this process may require multiple | ||
577 | // iterations and should be done chunk by chunk to prevent `b` | ||
578 | // from overlapping with `a`. | ||
579 | do | ||
580 | { | ||
581 | // clamp right shifts to prevent overlap with `a` | ||
582 | 438 | auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn); | |
583 | 438 | b = static_cast<char const*>(std::memmove(bp, b, bn)); | |
584 | |||
585 | // a chunk or all of `a` based on available space | ||
586 | 438 | auto chunk_a = static_cast<std::size_t>(b - dest); | |
587 | 438 | std::memcpy(dest, a, chunk_a); // never overlap | |
588 | 438 | an -= chunk_a; | |
589 | 438 | dest += chunk_a; | |
590 | 438 | a += chunk_a; | |
591 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 438 times.
|
438 | } while(an); |
592 | } | ||
593 | |||
594 | 8047 | break; | |
595 | } | ||
596 | } | ||
597 | |||
598 | 10302 | ws_.clear(); | |
599 | |||
600 | 20604 | fb_ = { | |
601 | 10302 | ws_.data(), | |
602 |
1/2✓ Branch 1 taken 10302 times.
✗ Branch 2 not taken.
|
10302 | svc_.cfg.headers.max_size + svc_.cfg.min_buffer, |
603 | leftover }; | ||
604 | |||
605 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 10302 times.
|
10302 | BOOST_ASSERT( |
606 | fb_.capacity() == svc_.max_overread() - leftover); | ||
607 | |||
608 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 10302 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
10302 | BOOST_ASSERT( |
609 | head_response == false || | ||
610 | h_.kind == detail::kind::response); | ||
611 | |||
612 | 10302 | h_ = detail::header(detail::empty{h_.kind}); | |
613 | 10302 | h_.buf = reinterpret_cast<char*>(ws_.data()); | |
614 | 10302 | h_.cbuf = h_.buf; | |
615 | 10302 | h_.cap = ws_.size(); | |
616 | |||
617 | 10302 | st_ = state::header; | |
618 | 10302 | how_ = how::in_place; | |
619 | |||
620 | // reset to the configured default | ||
621 | 10302 | body_limit_ = svc_.cfg.body_limit; | |
622 | |||
623 | 10302 | body_total_ = 0; | |
624 | 10302 | payload_remain_ = 0; | |
625 | 10302 | chunk_remain_ = 0; | |
626 | 10302 | body_avail_ = 0; | |
627 | 10302 | nprepare_ = 0; | |
628 | |||
629 | 10302 | filter_ = nullptr; | |
630 | 10302 | eb_ = nullptr; | |
631 | 10302 | sink_ = nullptr; | |
632 | |||
633 | 10302 | got_header_ = false; | |
634 | 10302 | head_response_ = head_response; | |
635 | 10302 | needs_chunk_close_ = false; | |
636 | 10302 | trailer_headers_ = false; | |
637 | 10302 | chunked_body_ended = false; | |
638 | 10302 | } | |
639 | |||
640 | auto | ||
641 | 51100 | parser:: | |
642 | prepare() -> | ||
643 | mutable_buffers_type | ||
644 | { | ||
645 | 51100 | nprepare_ = 0; | |
646 | |||
647 |
5/7✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10374 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40723 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
|
51100 | switch(st_) |
648 | { | ||
649 | 1 | default: | |
650 | case state::reset: | ||
651 | // reset must be called first | ||
652 | 1 | detail::throw_logic_error(); | |
653 | |||
654 | 1 | case state::start: | |
655 | // start must be called first | ||
656 | 1 | detail::throw_logic_error(); | |
657 | |||
658 | 10374 | case state::header: | |
659 | { | ||
660 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10374 times.
|
10374 | BOOST_ASSERT( |
661 | h_.size < svc_.cfg.headers.max_size); | ||
662 | 10374 | std::size_t n = fb_.capacity() - fb_.size(); | |
663 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 10374 times.
|
10374 | BOOST_ASSERT(n <= svc_.max_overread()); |
664 | 10374 | n = clamp(n, svc_.cfg.max_prepare); | |
665 | 10374 | mbp_[0] = fb_.prepare(n); | |
666 | 10374 | nprepare_ = n; | |
667 | 10374 | return mutable_buffers_type(&mbp_[0], 1); | |
668 | } | ||
669 | |||
670 | ✗ | case state::header_done: | |
671 | // forgot to call parse() | ||
672 | ✗ | detail::throw_logic_error(); | |
673 | |||
674 | 40723 | case state::body: | |
675 | { | ||
676 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 40723 times.
|
40723 | if(got_eof_) |
677 | { | ||
678 | // forgot to call parse() | ||
679 | ✗ | detail::throw_logic_error(); | |
680 | } | ||
681 | |||
682 |
2/2✓ Branch 1 taken 21673 times.
✓ Branch 2 taken 19050 times.
|
40723 | if(! is_plain()) |
683 | { | ||
684 | // buffered payload | ||
685 | 21673 | std::size_t n = cb0_.capacity(); | |
686 | 21673 | n = clamp(n, svc_.cfg.max_prepare); | |
687 | 21673 | nprepare_ = n; | |
688 | 21673 | mbp_ = cb0_.prepare(n); | |
689 | 21673 | return mutable_buffers_type(mbp_); | |
690 | } | ||
691 | else | ||
692 | { | ||
693 |
2/2✓ Branch 0 taken 19029 times.
✓ Branch 1 taken 21 times.
|
19050 | switch(how_) |
694 | { | ||
695 | 19029 | default: | |
696 | case how::in_place: | ||
697 | case how::sink: | ||
698 | { | ||
699 | 19029 | std::size_t n = cb0_.capacity(); | |
700 | 19029 | n = clamp(n, svc_.cfg.max_prepare); | |
701 | |||
702 |
2/2✓ Branch 0 taken 19004 times.
✓ Branch 1 taken 25 times.
|
19029 | if(h_.md.payload == payload::size) |
703 | { | ||
704 |
2/2✓ Branch 0 taken 17797 times.
✓ Branch 1 taken 1207 times.
|
19004 | if(n > payload_remain_) |
705 | { | ||
706 | 17797 | std::size_t overread = | |
707 | 17797 | n - static_cast<std::size_t>(payload_remain_); | |
708 |
2/2✓ Branch 1 taken 7877 times.
✓ Branch 2 taken 9920 times.
|
17797 | if(overread > svc_.max_overread()) |
709 | 7877 | n = static_cast<std::size_t>(payload_remain_) + | |
710 | 7877 | svc_.max_overread(); | |
711 | } | ||
712 | } | ||
713 | else | ||
714 | { | ||
715 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
|
25 | BOOST_ASSERT( |
716 | h_.md.payload == payload::to_eof); | ||
717 | 25 | n = clamp(body_limit_remain() + 1, n); | |
718 | } | ||
719 | |||
720 | 19029 | nprepare_ = n; | |
721 | 19029 | mbp_ = cb0_.prepare(n); | |
722 | 19029 | return mutable_buffers_type(mbp_); | |
723 | } | ||
724 | 21 | case how::elastic: | |
725 | { | ||
726 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
|
21 | BOOST_ASSERT(cb0_.size() == 0); |
727 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
|
21 | BOOST_ASSERT(body_avail_ == 0); |
728 | |||
729 | 21 | std::size_t n = svc_.cfg.min_buffer; | |
730 | |||
731 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 15 times.
|
21 | if(h_.md.payload == payload::size) |
732 | { | ||
733 | // Overreads are not allowed, or | ||
734 | // else the caller will see extra | ||
735 | // unrelated data. | ||
736 | 6 | n = clamp(payload_remain_, n); | |
737 | } | ||
738 | else | ||
739 | { | ||
740 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | BOOST_ASSERT( |
741 | h_.md.payload == payload::to_eof); | ||
742 | 15 | n = clamp(body_limit_remain() + 1, n); | |
743 | 15 | n = clamp(n, eb_->max_size() - eb_->size()); | |
744 | // fill capacity first to avoid an allocation | ||
745 | std::size_t avail = | ||
746 | 15 | eb_->capacity() - eb_->size(); | |
747 |
1/2✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
|
15 | if(avail != 0) |
748 | 15 | n = clamp(n, avail); | |
749 | |||
750 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
|
15 | if(n == 0) |
751 | { | ||
752 | // dynamic buffer is full | ||
753 | // attempt a 1 byte read so | ||
754 | // we can detect overflow | ||
755 | 1 | nprepare_ = 1; | |
756 | 1 | mbp_ = cb0_.prepare(1); | |
757 | 1 | return mutable_buffers_type(mbp_); | |
758 | } | ||
759 | } | ||
760 | |||
761 | 20 | n = clamp(n, svc_.cfg.max_prepare); | |
762 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
|
20 | BOOST_ASSERT(n != 0); |
763 | 20 | nprepare_ = n; | |
764 | 20 | return eb_->prepare(n); | |
765 | } | ||
766 | } | ||
767 | } | ||
768 | } | ||
769 | |||
770 | ✗ | case state::set_body: | |
771 | // forgot to call parse() | ||
772 | ✗ | detail::throw_logic_error(); | |
773 | |||
774 | 1 | case state::complete_in_place: | |
775 | case state::complete: | ||
776 | // already complete | ||
777 | 1 | detail::throw_logic_error(); | |
778 | } | ||
779 | } | ||
780 | |||
781 | void | ||
782 | 51097 | parser:: | |
783 | commit( | ||
784 | std::size_t n) | ||
785 | { | ||
786 |
5/7✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10374 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40720 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
|
51097 | switch(st_) |
787 | { | ||
788 | 1 | default: | |
789 | case state::reset: | ||
790 | { | ||
791 | // reset must be called first | ||
792 | 1 | detail::throw_logic_error(); | |
793 | } | ||
794 | |||
795 | 1 | case state::start: | |
796 | { | ||
797 | // forgot to call start() | ||
798 | 1 | detail::throw_logic_error(); | |
799 | } | ||
800 | |||
801 | 10374 | case state::header: | |
802 | { | ||
803 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10373 times.
|
10374 | if(n > nprepare_) |
804 | { | ||
805 | // n can't be greater than size of | ||
806 | // the buffers returned by prepare() | ||
807 | 1 | detail::throw_invalid_argument(); | |
808 | } | ||
809 | |||
810 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10372 times.
|
10373 | if(got_eof_) |
811 | { | ||
812 | // can't commit after EOF | ||
813 | 1 | detail::throw_logic_error(); | |
814 | } | ||
815 | |||
816 | 10372 | nprepare_ = 0; // invalidate | |
817 | 10372 | fb_.commit(n); | |
818 | 10372 | break; | |
819 | } | ||
820 | |||
821 | ✗ | case state::header_done: | |
822 | { | ||
823 | // forgot to call parse() | ||
824 | ✗ | detail::throw_logic_error(); | |
825 | } | ||
826 | |||
827 | 40720 | case state::body: | |
828 | { | ||
829 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 40718 times.
|
40720 | if(n > nprepare_) |
830 | { | ||
831 | // n can't be greater than size of | ||
832 | // the buffers returned by prepare() | ||
833 | 2 | detail::throw_invalid_argument(); | |
834 | } | ||
835 | |||
836 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 40718 times.
|
40718 | if(got_eof_) |
837 | { | ||
838 | // can't commit after EOF | ||
839 | ✗ | detail::throw_logic_error(); | |
840 | } | ||
841 | |||
842 | 40718 | nprepare_ = 0; // invalidate | |
843 |
6/6✓ Branch 1 taken 19045 times.
✓ Branch 2 taken 21673 times.
✓ Branch 3 taken 20 times.
✓ Branch 4 taken 19025 times.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 40698 times.
|
40718 | if(is_plain() && how_ == how::elastic) |
844 | { | ||
845 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19 times.
|
20 | if(eb_->max_size() == eb_->size()) |
846 | { | ||
847 | // borrowed 1 byte from | ||
848 | // cb0_ in prepare() | ||
849 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | BOOST_ASSERT(n <= 1); |
850 | 1 | cb0_.commit(n); | |
851 | } | ||
852 | else | ||
853 | { | ||
854 | 19 | eb_->commit(n); | |
855 | 19 | payload_remain_ -= n; | |
856 | 19 | body_total_ += n; | |
857 | } | ||
858 | } | ||
859 | else | ||
860 | { | ||
861 | 40698 | cb0_.commit(n); | |
862 | } | ||
863 | 40718 | break; | |
864 | } | ||
865 | |||
866 | ✗ | case state::set_body: | |
867 | { | ||
868 | // forgot to call parse() | ||
869 | ✗ | detail::throw_logic_error(); | |
870 | } | ||
871 | |||
872 | 1 | case state::complete_in_place: | |
873 | case state::complete: | ||
874 | { | ||
875 | // already complete | ||
876 | 1 | detail::throw_logic_error(); | |
877 | } | ||
878 | } | ||
879 | 51090 | } | |
880 | |||
881 | void | ||
882 | 402 | parser:: | |
883 | commit_eof() | ||
884 | { | ||
885 | 402 | nprepare_ = 0; // invalidate | |
886 | |||
887 |
5/7✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 368 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
|
402 | switch(st_) |
888 | { | ||
889 | 1 | default: | |
890 | case state::reset: | ||
891 | // reset must be called first | ||
892 | 1 | detail::throw_logic_error(); | |
893 | |||
894 | 1 | case state::start: | |
895 | // forgot to call prepare() | ||
896 | 1 | detail::throw_logic_error(); | |
897 | |||
898 | 31 | case state::header: | |
899 | 31 | got_eof_ = true; | |
900 | 31 | break; | |
901 | |||
902 | ✗ | case state::header_done: | |
903 | // forgot to call parse() | ||
904 | ✗ | detail::throw_logic_error(); | |
905 | |||
906 | 368 | case state::body: | |
907 | 368 | got_eof_ = true; | |
908 | 368 | break; | |
909 | |||
910 | ✗ | case state::set_body: | |
911 | // forgot to call parse() | ||
912 | ✗ | detail::throw_logic_error(); | |
913 | |||
914 | 1 | case state::complete_in_place: | |
915 | case state::complete: | ||
916 | // can't commit eof when complete | ||
917 | 1 | detail::throw_logic_error(); | |
918 | } | ||
919 | 399 | } | |
920 | |||
921 | void | ||
922 | 69899 | parser:: | |
923 | parse( | ||
924 | system::error_code& ec) | ||
925 | { | ||
926 | 69899 | ec = {}; | |
927 |
7/7✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 16649 times.
✓ Branch 3 taken 9083 times.
✓ Branch 4 taken 41372 times.
✓ Branch 5 taken 2333 times.
✓ Branch 6 taken 460 times.
|
69899 | switch(st_) |
928 | { | ||
929 | 1 | default: | |
930 | case state::reset: | ||
931 | // reset must be called first | ||
932 | 1 | detail::throw_logic_error(); | |
933 | |||
934 | 1 | case state::start: | |
935 | // start must be called first | ||
936 | 1 | detail::throw_logic_error(); | |
937 | |||
938 | 16649 | case state::header: | |
939 | { | ||
940 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16649 times.
|
16649 | BOOST_ASSERT(h_.buf == static_cast< |
941 | void const*>(ws_.data())); | ||
942 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 16649 times.
|
16649 | BOOST_ASSERT(h_.cbuf == static_cast< |
943 | void const*>(ws_.data())); | ||
944 | |||
945 | 16649 | h_.parse(fb_.size(), svc_.cfg.headers, ec); | |
946 | |||
947 |
2/2✓ Branch 2 taken 6384 times.
✓ Branch 3 taken 10265 times.
|
16649 | if(ec == condition::need_more_input) |
948 | { | ||
949 |
2/2✓ Branch 0 taken 6357 times.
✓ Branch 1 taken 27 times.
|
6384 | if(! got_eof_) |
950 | { | ||
951 | // headers incomplete | ||
952 | 6357 | return; | |
953 | } | ||
954 | |||
955 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 15 times.
|
27 | if(fb_.size() == 0) |
956 | { | ||
957 | // stream closed cleanly | ||
958 | 12 | st_ = state::reset; | |
959 | 24 | ec = BOOST_HTTP_PROTO_ERR( | |
960 | error::end_of_stream); | ||
961 | 12 | return; | |
962 | } | ||
963 | |||
964 | // stream closed with a | ||
965 | // partial message received | ||
966 | 15 | st_ = state::reset; | |
967 | 30 | ec = BOOST_HTTP_PROTO_ERR( | |
968 | error::incomplete); | ||
969 | 15 | return; | |
970 | } | ||
971 |
2/2✓ Branch 1 taken 259 times.
✓ Branch 2 taken 10006 times.
|
10265 | else if(ec.failed()) |
972 | { | ||
973 | // other error, | ||
974 | // | ||
975 | // VFALCO map this to a bad | ||
976 | // request or bad response error? | ||
977 | // | ||
978 | 259 | st_ = state::reset; // unrecoverable | |
979 | 259 | return; | |
980 | } | ||
981 | |||
982 | 10006 | got_header_ = true; | |
983 | |||
984 | // reserve headers + table | ||
985 | 10006 | ws_.reserve_front(h_.size); | |
986 | 10006 | ws_.reserve_back(h_.table_space()); | |
987 | |||
988 | // no payload | ||
989 |
2/2✓ Branch 0 taken 9084 times.
✓ Branch 1 taken 922 times.
|
10006 | if(h_.md.payload == payload::none || |
990 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9084 times.
|
9084 | head_response_) |
991 | { | ||
992 | // octets of the next message | ||
993 | 922 | auto overread = fb_.size() - h_.size; | |
994 |
1/2✓ Branch 2 taken 922 times.
✗ Branch 3 not taken.
|
922 | cb0_ = { ws_.data(), overread, overread }; |
995 | 922 | ws_.reserve_front(overread); | |
996 | 922 | st_ = state::complete_in_place; | |
997 | 922 | return; | |
998 | } | ||
999 | |||
1000 | 9084 | st_ = state::header_done; | |
1001 | 9084 | break; | |
1002 | } | ||
1003 | |||
1004 | 9083 | case state::header_done: | |
1005 | { | ||
1006 | // metadata error | ||
1007 |
2/2✓ Branch 0 taken 180 times.
✓ Branch 1 taken 8903 times.
|
9083 | if(h_.md.payload == payload::error) |
1008 | { | ||
1009 | // VFALCO This needs looking at | ||
1010 | 360 | ec = BOOST_HTTP_PROTO_ERR( | |
1011 | error::bad_payload); | ||
1012 | 180 | st_ = state::reset; // unrecoverable | |
1013 | 180 | return; | |
1014 | } | ||
1015 | |||
1016 | // overread currently includes any and all octets that | ||
1017 | // extend beyond the current end of the header | ||
1018 | // this can include associated body octets for the | ||
1019 | // current message or octets of the next message in the | ||
1020 | // stream, e.g. pipelining is being used | ||
1021 | 8903 | auto const overread = fb_.size() - h_.size; | |
1022 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 8903 times.
|
8903 | BOOST_ASSERT(overread <= svc_.max_overread()); |
1023 | |||
1024 | 8903 | auto cap = fb_.capacity() + overread + | |
1025 | 8903 | svc_.cfg.min_buffer; | |
1026 | |||
1027 | // reserve body buffers first, as the decoder | ||
1028 | // must be installed after them. | ||
1029 | 8903 | auto const p = ws_.reserve_front(cap); | |
1030 | |||
1031 |
2/2✓ Branch 0 taken 73 times.
✓ Branch 1 taken 8830 times.
|
8903 | if(svc_.cfg.apply_deflate_decoder && |
1032 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 36 times.
|
73 | h_.md.content_encoding.encoding == encoding::deflate) |
1033 | { | ||
1034 |
1/2✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
|
37 | filter_ = &ws_.emplace<inflator_filter>(ctx_, ws_, false); |
1035 | } | ||
1036 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 8830 times.
|
8866 | else if(svc_.cfg.apply_gzip_decoder && |
1037 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | h_.md.content_encoding.encoding == encoding::gzip) |
1038 | { | ||
1039 |
1/2✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
|
36 | filter_ = &ws_.emplace<inflator_filter>(ctx_, ws_, true); |
1040 | } | ||
1041 | else | ||
1042 | { | ||
1043 | 8830 | cap += svc_.max_codec; | |
1044 | 8830 | ws_.reserve_front(svc_.max_codec); | |
1045 | } | ||
1046 | |||
1047 |
6/6✓ Branch 1 taken 4206 times.
✓ Branch 2 taken 4697 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 4182 times.
✓ Branch 5 taken 4721 times.
✓ Branch 6 taken 4182 times.
|
8903 | if(is_plain() || how_ == how::elastic) |
1048 | { | ||
1049 |
1/2✓ Branch 1 taken 4721 times.
✗ Branch 2 not taken.
|
4721 | cb0_ = { p, cap, overread }; |
1050 | 4721 | cb1_ = {}; | |
1051 | } | ||
1052 | else | ||
1053 | { | ||
1054 | // buffered payload | ||
1055 | 8364 | std::size_t n0 = (overread > svc_.cfg.min_buffer) | |
1056 |
2/2✓ Branch 0 taken 4158 times.
✓ Branch 1 taken 24 times.
|
4182 | ? overread |
1057 | 4158 | : svc_.cfg.min_buffer; | |
1058 | 4182 | std::size_t n1 = svc_.cfg.min_buffer; | |
1059 | |||
1060 |
1/2✓ Branch 1 taken 4182 times.
✗ Branch 2 not taken.
|
4182 | cb0_ = { p , n0, overread }; |
1061 | 4182 | cb1_ = { p + n0 , n1 }; | |
1062 | } | ||
1063 | |||
1064 |
2/2✓ Branch 0 taken 4373 times.
✓ Branch 1 taken 4530 times.
|
8903 | if(h_.md.payload == payload::size) |
1065 | { | ||
1066 |
2/2✓ Branch 0 taken 4349 times.
✓ Branch 1 taken 24 times.
|
4373 | if(!filter_ && |
1067 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4348 times.
|
4349 | body_limit_ < h_.md.payload_size) |
1068 | { | ||
1069 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
1070 | error::body_too_large); | ||
1071 | 1 | st_ = state::reset; | |
1072 | 1 | return; | |
1073 | } | ||
1074 | 4372 | payload_remain_ = h_.md.payload_size; | |
1075 | } | ||
1076 | |||
1077 | 8902 | st_ = state::body; | |
1078 | BOOST_FALLTHROUGH; | ||
1079 | } | ||
1080 | |||
1081 | case state::body: | ||
1082 | { | ||
1083 | 50274 | do_body: | |
1084 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50274 times.
|
50274 | BOOST_ASSERT(st_ == state::body); |
1085 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50274 times.
|
50274 | BOOST_ASSERT(h_.md.payload != payload::none); |
1086 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 50274 times.
|
50274 | BOOST_ASSERT(h_.md.payload != payload::error); |
1087 | |||
1088 | 8796 | auto set_state_to_complete = [&]() | |
1089 | { | ||
1090 |
2/2✓ Branch 0 taken 8336 times.
✓ Branch 1 taken 460 times.
|
8796 | if(how_ == how::in_place) |
1091 | { | ||
1092 | 8336 | st_ = state::complete_in_place; | |
1093 | 8336 | return; | |
1094 | } | ||
1095 | 460 | st_ = state::complete; | |
1096 | 50274 | }; | |
1097 | |||
1098 |
2/2✓ Branch 0 taken 24462 times.
✓ Branch 1 taken 25812 times.
|
50274 | if(h_.md.payload == payload::chunked) |
1099 | { | ||
1100 | for(;;) | ||
1101 | { | ||
1102 |
2/2✓ Branch 0 taken 129813 times.
✓ Branch 1 taken 1203 times.
|
131016 | if(chunk_remain_ == 0 |
1103 |
2/2✓ Branch 0 taken 125668 times.
✓ Branch 1 taken 4145 times.
|
129813 | && !chunked_body_ended) |
1104 | { | ||
1105 | 125668 | auto cs = chained_sequence(cb0_.data()); | |
1106 | 19412 | auto check_ec = [&]() | |
1107 | { | ||
1108 |
4/6✓ Branch 2 taken 19403 times.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19403 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 19412 times.
|
19412 | if(ec == condition::need_more_input && got_eof_) |
1109 | { | ||
1110 | ✗ | ec = BOOST_HTTP_PROTO_ERR(error::incomplete); | |
1111 | ✗ | st_ = state::reset; | |
1112 | } | ||
1113 | 145080 | }; | |
1114 | |||
1115 |
2/2✓ Branch 0 taken 117028 times.
✓ Branch 1 taken 8640 times.
|
125668 | if(needs_chunk_close_) |
1116 | { | ||
1117 | 117028 | parse_eol(cs, ec); | |
1118 |
2/2✓ Branch 1 taken 17 times.
✓ Branch 2 taken 117011 times.
|
117028 | if(ec) |
1119 | { | ||
1120 | 17 | check_ec(); | |
1121 | 19412 | return; | |
1122 | } | ||
1123 | } | ||
1124 |
2/2✓ Branch 0 taken 4223 times.
✓ Branch 1 taken 4417 times.
|
8640 | else if(trailer_headers_) |
1125 | { | ||
1126 | 4223 | skip_trailer_headers(cs, ec); | |
1127 |
2/2✓ Branch 1 taken 78 times.
✓ Branch 2 taken 4145 times.
|
4223 | if(ec) |
1128 | { | ||
1129 | 78 | check_ec(); | |
1130 | 78 | return; | |
1131 | } | ||
1132 | 4145 | cb0_.consume(cb0_.size() - cs.size()); | |
1133 | 4145 | chunked_body_ended = true; | |
1134 | 8292 | continue; | |
1135 | } | ||
1136 | |||
1137 | 121428 | auto chunk_size = parse_hex(cs, ec); | |
1138 |
2/2✓ Branch 1 taken 19285 times.
✓ Branch 2 taken 102143 times.
|
121428 | if(ec) |
1139 | { | ||
1140 | 19285 | check_ec(); | |
1141 | 19285 | return; | |
1142 | } | ||
1143 | |||
1144 | // skip chunk extensions | ||
1145 | 102143 | find_eol(cs, ec); | |
1146 |
2/2✓ Branch 1 taken 32 times.
✓ Branch 2 taken 102111 times.
|
102143 | if(ec) |
1147 | { | ||
1148 | 32 | check_ec(); | |
1149 | 32 | return; | |
1150 | } | ||
1151 | |||
1152 | 102111 | cb0_.consume(cb0_.size() - cs.size()); | |
1153 | 102111 | chunk_remain_ = chunk_size; | |
1154 | |||
1155 | 102111 | needs_chunk_close_ = true; | |
1156 |
2/2✓ Branch 0 taken 4147 times.
✓ Branch 1 taken 97964 times.
|
102111 | if(chunk_remain_ == 0) |
1157 | { | ||
1158 | 4147 | needs_chunk_close_ = false; | |
1159 | 4147 | trailer_headers_ = true; | |
1160 | 4147 | continue; | |
1161 | } | ||
1162 | } | ||
1163 | |||
1164 |
6/6✓ Branch 1 taken 2510 times.
✓ Branch 2 taken 100802 times.
✓ Branch 3 taken 365 times.
✓ Branch 4 taken 2145 times.
✓ Branch 5 taken 365 times.
✓ Branch 6 taken 102947 times.
|
103312 | if(cb0_.size() == 0 && !chunked_body_ended) |
1165 | { | ||
1166 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 364 times.
|
365 | if(got_eof_) |
1167 | { | ||
1168 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
1169 | error::incomplete); | ||
1170 | 1 | st_ = state::reset; | |
1171 | 1 | return; | |
1172 | } | ||
1173 | |||
1174 | 728 | ec = BOOST_HTTP_PROTO_ERR( | |
1175 | error::need_data); | ||
1176 | 364 | return; | |
1177 | } | ||
1178 | |||
1179 |
2/2✓ Branch 0 taken 56565 times.
✓ Branch 1 taken 46382 times.
|
102947 | if(filter_) |
1180 | { | ||
1181 |
1/2✓ Branch 2 taken 56565 times.
✗ Branch 3 not taken.
|
56565 | chunk_remain_ -= apply_filter( |
1182 | ec, | ||
1183 | clamp(chunk_remain_, cb0_.size()), | ||
1184 | 56565 | !chunked_body_ended); | |
1185 | |||
1186 |
6/6✓ Branch 1 taken 56025 times.
✓ Branch 2 taken 540 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 56001 times.
✓ Branch 5 taken 564 times.
✓ Branch 6 taken 56001 times.
|
56565 | if(ec || chunked_body_ended) |
1187 | 564 | return; | |
1188 | } | ||
1189 | else | ||
1190 | { | ||
1191 | const std::size_t chunk_avail = | ||
1192 | 46382 | clamp(chunk_remain_, cb0_.size()); | |
1193 | const auto chunk = | ||
1194 | 46382 | buffers::prefix(cb0_.data(), chunk_avail); | |
1195 | |||
1196 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 46382 times.
|
46382 | if(body_limit_remain() < chunk_avail) |
1197 | { | ||
1198 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
1199 | error::body_too_large); | ||
1200 | ✗ | st_ = state::reset; | |
1201 | 4121 | return; | |
1202 | } | ||
1203 | |||
1204 |
1/4✓ Branch 0 taken 46382 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
46382 | switch(how_) |
1205 | { | ||
1206 | 46382 | case how::in_place: | |
1207 | { | ||
1208 | 46382 | auto copied = buffers::copy( | |
1209 |
1/2✓ Branch 2 taken 46382 times.
✗ Branch 3 not taken.
|
46382 | cb1_.prepare(cb1_.capacity()), |
1210 | chunk); | ||
1211 | 46382 | chunk_remain_ -= copied; | |
1212 | 46382 | body_avail_ += copied; | |
1213 | 46382 | body_total_ += copied; | |
1214 | 46382 | cb0_.consume(copied); | |
1215 | 46382 | cb1_.commit(copied); | |
1216 | 46382 | if(cb1_.capacity() == 0 | |
1217 |
2/6✗ Branch 0 not taken.
✓ Branch 1 taken 46382 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 46382 times.
|
46382 | && !chunked_body_ended) |
1218 | { | ||
1219 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
1220 | error::in_place_overflow); | ||
1221 | ✗ | return; | |
1222 | } | ||
1223 | 46382 | break; | |
1224 | } | ||
1225 | ✗ | case how::sink: | |
1226 | { | ||
1227 | ✗ | auto sink_rs = sink_->write( | |
1228 | ✗ | chunk, !chunked_body_ended); | |
1229 | ✗ | chunk_remain_ -= sink_rs.bytes; | |
1230 | ✗ | body_total_ += sink_rs.bytes; | |
1231 | ✗ | cb0_.consume(sink_rs.bytes); | |
1232 | ✗ | if(sink_rs.ec.failed()) | |
1233 | { | ||
1234 | ✗ | body_avail_ += | |
1235 | ✗ | chunk_avail - sink_rs.bytes; | |
1236 | ✗ | ec = sink_rs.ec; | |
1237 | ✗ | st_ = state::reset; | |
1238 | ✗ | return; | |
1239 | } | ||
1240 | ✗ | break; | |
1241 | } | ||
1242 | ✗ | case how::elastic: | |
1243 | { | ||
1244 | ✗ | if(eb_->max_size() - eb_->size() | |
1245 | ✗ | < chunk_avail) | |
1246 | { | ||
1247 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
1248 | error::buffer_overflow); | ||
1249 | ✗ | st_ = state::reset; | |
1250 | ✗ | return; | |
1251 | } | ||
1252 | ✗ | buffers::copy( | |
1253 | ✗ | eb_->prepare(chunk_avail), | |
1254 | chunk); | ||
1255 | ✗ | chunk_remain_ -= chunk_avail; | |
1256 | ✗ | body_total_ += chunk_avail; | |
1257 | ✗ | cb0_.consume(chunk_avail); | |
1258 | ✗ | eb_->commit(chunk_avail); | |
1259 | ✗ | break; | |
1260 | } | ||
1261 | } | ||
1262 | |||
1263 |
2/2✓ Branch 0 taken 4121 times.
✓ Branch 1 taken 42261 times.
|
46382 | if(chunked_body_ended) |
1264 | { | ||
1265 | 4121 | set_state_to_complete(); | |
1266 | 4121 | return; | |
1267 | } | ||
1268 | } | ||
1269 | 106554 | } | |
1270 | } | ||
1271 | else | ||
1272 | { | ||
1273 | // non-chunked payload | ||
1274 | |||
1275 | 77436 | const std::size_t payload_avail = [&]() | |
1276 | { | ||
1277 | 25812 | auto ret = cb0_.size(); | |
1278 |
2/2✓ Branch 0 taken 24119 times.
✓ Branch 1 taken 1693 times.
|
25812 | if(!filter_) |
1279 | 24119 | ret -= body_avail_; | |
1280 |
2/2✓ Branch 0 taken 24181 times.
✓ Branch 1 taken 1631 times.
|
25812 | if(h_.md.payload == payload::size) |
1281 | 24181 | return clamp(payload_remain_, ret); | |
1282 | // payload::eof | ||
1283 | 1631 | return ret; | |
1284 | 25812 | }(); | |
1285 | |||
1286 | 77436 | const bool is_complete = [&]() | |
1287 | { | ||
1288 |
2/2✓ Branch 0 taken 24181 times.
✓ Branch 1 taken 1631 times.
|
25812 | if(h_.md.payload == payload::size) |
1289 | 24181 | return payload_avail == payload_remain_; | |
1290 | // payload::eof | ||
1291 | 1631 | return got_eof_; | |
1292 | 25812 | }(); | |
1293 | |||
1294 |
2/2✓ Branch 0 taken 1693 times.
✓ Branch 1 taken 24119 times.
|
25812 | if(filter_) |
1295 | { | ||
1296 | 3386 | payload_remain_ -= apply_filter( | |
1297 |
1/2✓ Branch 1 taken 1693 times.
✗ Branch 2 not taken.
|
1693 | ec, payload_avail, !is_complete); |
1298 |
6/6✓ Branch 1 taken 612 times.
✓ Branch 2 taken 1081 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 564 times.
✓ Branch 5 taken 1129 times.
✓ Branch 6 taken 564 times.
|
1693 | if(ec || is_complete) |
1299 | 1129 | return; | |
1300 | } | ||
1301 | else | ||
1302 | { | ||
1303 | // plain body | ||
1304 | |||
1305 |
2/2✓ Branch 0 taken 764 times.
✓ Branch 1 taken 23355 times.
|
24119 | if(h_.md.payload == payload::to_eof) |
1306 | { | ||
1307 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 763 times.
|
764 | if(body_limit_remain() < payload_avail) |
1308 | { | ||
1309 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
1310 | error::body_too_large); | ||
1311 | 1 | st_ = state::reset; | |
1312 | 1 | return; | |
1313 | } | ||
1314 | } | ||
1315 | |||
1316 |
3/4✓ Branch 0 taken 23361 times.
✓ Branch 1 taken 371 times.
✓ Branch 2 taken 386 times.
✗ Branch 3 not taken.
|
24118 | switch(how_) |
1317 | { | ||
1318 | 23361 | case how::in_place: | |
1319 | { | ||
1320 | 23361 | payload_remain_ -= payload_avail; | |
1321 | 23361 | body_avail_ += payload_avail; | |
1322 | 23361 | body_total_ += payload_avail; | |
1323 |
5/6✓ Branch 1 taken 1 times.
✓ Branch 2 taken 23360 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 23360 times.
|
23361 | if(cb0_.capacity() == 0 && !is_complete) |
1324 | { | ||
1325 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
1326 | error::in_place_overflow); | ||
1327 | 1 | return; | |
1328 | } | ||
1329 | 23360 | break; | |
1330 | } | ||
1331 | 371 | case how::sink: | |
1332 | { | ||
1333 | 371 | payload_remain_ -= payload_avail; | |
1334 | 371 | body_total_ += payload_avail; | |
1335 |
1/2✓ Branch 1 taken 371 times.
✗ Branch 2 not taken.
|
371 | auto sink_rs = sink_->write( |
1336 | 371 | buffers::prefix( | |
1337 | ✗ | cb0_.data(), | |
1338 | payload_avail), | ||
1339 | 371 | !is_complete); | |
1340 | 371 | cb0_.consume(sink_rs.bytes); | |
1341 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 371 times.
|
371 | if(sink_rs.ec.failed()) |
1342 | { | ||
1343 | ✗ | body_avail_ += | |
1344 | ✗ | payload_avail - sink_rs.bytes; | |
1345 | ✗ | ec = sink_rs.ec; | |
1346 | ✗ | st_ = state::reset; | |
1347 | ✗ | return; | |
1348 | } | ||
1349 | 371 | break; | |
1350 | } | ||
1351 | 386 | case how::elastic: | |
1352 | { | ||
1353 | // payload_remain_ and body_total_ | ||
1354 | // are already updated in commit() | ||
1355 | |||
1356 | // cb0_ contains data | ||
1357 |
2/2✓ Branch 0 taken 193 times.
✓ Branch 1 taken 193 times.
|
386 | if(payload_avail != 0) |
1358 | { | ||
1359 |
2/4✓ Branch 1 taken 193 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 193 times.
✗ Branch 5 not taken.
|
193 | if(eb_->max_size() - eb_->size() |
1360 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 192 times.
|
193 | < payload_avail) |
1361 | { | ||
1362 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
1363 | error::buffer_overflow); | ||
1364 | 1 | st_ = state::reset; | |
1365 | 1 | return; | |
1366 | } | ||
1367 | // only happens when an elastic body | ||
1368 | // is attached in header_done state | ||
1369 | 192 | buffers::copy( | |
1370 |
1/2✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
|
192 | eb_->prepare(payload_avail), |
1371 | 192 | cb0_.data()); | |
1372 | 192 | cb0_.consume(payload_avail); | |
1373 |
1/2✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
|
192 | eb_->commit(payload_avail); |
1374 | 192 | payload_remain_ -= payload_avail; | |
1375 | 192 | body_total_ += payload_avail; | |
1376 | } | ||
1377 | 385 | break; | |
1378 | } | ||
1379 | } | ||
1380 | |||
1381 |
2/2✓ Branch 0 taken 4675 times.
✓ Branch 1 taken 19441 times.
|
24116 | if(is_complete) |
1382 | { | ||
1383 | 4675 | set_state_to_complete(); | |
1384 | 4675 | return; | |
1385 | } | ||
1386 | } | ||
1387 | |||
1388 |
4/4✓ Branch 0 taken 19280 times.
✓ Branch 1 taken 725 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19279 times.
|
20005 | if(h_.md.payload == payload::size && got_eof_) |
1389 | { | ||
1390 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
1391 | error::incomplete); | ||
1392 | 1 | st_ = state::reset; | |
1393 | 1 | return; | |
1394 | } | ||
1395 | |||
1396 | 40008 | ec = BOOST_HTTP_PROTO_ERR( | |
1397 | error::need_data); | ||
1398 | 20004 | return; | |
1399 | } | ||
1400 | |||
1401 | break; | ||
1402 | } | ||
1403 | |||
1404 | 2333 | case state::set_body: | |
1405 | case state::complete_in_place: | ||
1406 | { | ||
1407 |
2/2✓ Branch 1 taken 426 times.
✓ Branch 2 taken 1907 times.
|
2333 | auto& body_buf = is_plain() ? cb0_ : cb1_; |
1408 | |||
1409 |
3/4✓ Branch 0 taken 2216 times.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 59 times.
✗ Branch 3 not taken.
|
2333 | switch(how_) |
1410 | { | ||
1411 | 2216 | case how::in_place: | |
1412 | 2216 | return; // no-op | |
1413 | 58 | case how::sink: | |
1414 | { | ||
1415 |
1/2✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
|
58 | auto rs = sink_->write( |
1416 | 58 | buffers::prefix( | |
1417 | ✗ | body_buf.data(), | |
1418 | body_avail_), | ||
1419 | 58 | st_ == state::set_body); | |
1420 | 58 | body_buf.consume(rs.bytes); | |
1421 | 58 | body_avail_ -= rs.bytes; | |
1422 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
|
58 | if(rs.ec.failed()) |
1423 | { | ||
1424 | ✗ | ec = rs.ec; | |
1425 | ✗ | st_ = state::reset; | |
1426 | ✗ | return; | |
1427 | } | ||
1428 | 58 | break; | |
1429 | } | ||
1430 | 59 | case how::elastic: | |
1431 | { | ||
1432 | 59 | if(eb_->max_size() - eb_->size() | |
1433 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
|
59 | < body_avail_) |
1434 | { | ||
1435 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
1436 | error::buffer_overflow); | ||
1437 | ✗ | return; | |
1438 | } | ||
1439 | 59 | buffers::copy( | |
1440 |
1/2✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
|
59 | eb_->prepare(body_avail_), |
1441 | 59 | body_buf.data()); | |
1442 | 59 | body_buf.consume(body_avail_); | |
1443 | 59 | eb_->commit(body_avail_); | |
1444 | 59 | body_avail_ = 0; | |
1445 | // TODO: expand cb0_ when possible? | ||
1446 | 59 | break; | |
1447 | } | ||
1448 | } | ||
1449 | |||
1450 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
|
117 | if(st_ == state::set_body) |
1451 | { | ||
1452 | ✗ | st_ = state::body; | |
1453 | ✗ | goto do_body; | |
1454 | } | ||
1455 | |||
1456 | 117 | st_ = state::complete; | |
1457 | 117 | break; | |
1458 | } | ||
1459 | |||
1460 | 460 | case state::complete: | |
1461 | 460 | break; | |
1462 | } | ||
1463 | } | ||
1464 | |||
1465 | auto | ||
1466 | 41250 | parser:: | |
1467 | pull_body() -> | ||
1468 | const_buffers_type | ||
1469 | { | ||
1470 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 41250 times.
✗ Branch 2 not taken.
|
41250 | switch(st_) |
1471 | { | ||
1472 | ✗ | case state::header_done: | |
1473 | ✗ | return {}; | |
1474 | 41250 | case state::body: | |
1475 | case state::complete_in_place: | ||
1476 | 41250 | cbp_ = buffers::prefix( | |
1477 |
2/2✓ Branch 1 taken 18981 times.
✓ Branch 2 taken 22269 times.
|
41250 | (is_plain() ? cb0_ : cb1_).data(), |
1478 | body_avail_); | ||
1479 | 41250 | return const_buffers_type(cbp_); | |
1480 | ✗ | default: | |
1481 | ✗ | detail::throw_logic_error(); | |
1482 | } | ||
1483 | } | ||
1484 | |||
1485 | void | ||
1486 | 39606 | parser:: | |
1487 | consume_body(std::size_t n) | ||
1488 | { | ||
1489 |
1/3✗ Branch 0 not taken.
✓ Branch 1 taken 39606 times.
✗ Branch 2 not taken.
|
39606 | switch(st_) |
1490 | { | ||
1491 | ✗ | case state::header_done: | |
1492 | ✗ | return; | |
1493 | 39606 | case state::body: | |
1494 | case state::complete_in_place: | ||
1495 | 39606 | n = clamp(n, body_avail_); | |
1496 |
2/2✓ Branch 1 taken 18981 times.
✓ Branch 2 taken 20625 times.
|
39606 | (is_plain() ? cb0_ : cb1_).consume(n); |
1497 | 39606 | body_avail_ -= n; | |
1498 | 39606 | return; | |
1499 | ✗ | default: | |
1500 | ✗ | detail::throw_logic_error(); | |
1501 | } | ||
1502 | } | ||
1503 | |||
1504 | core::string_view | ||
1505 | 699 | parser:: | |
1506 | body() const noexcept | ||
1507 | { | ||
1508 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 699 times.
|
699 | if(st_ != state::complete_in_place) |
1509 | { | ||
1510 | // Precondition violation | ||
1511 | ✗ | detail::throw_logic_error(); | |
1512 | } | ||
1513 | |||
1514 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 699 times.
|
699 | if(body_avail_ != body_total_) |
1515 | { | ||
1516 | // Precondition violation | ||
1517 | ✗ | detail::throw_logic_error(); | |
1518 | } | ||
1519 | |||
1520 |
2/2✓ Branch 1 taken 578 times.
✓ Branch 2 taken 121 times.
|
699 | auto cbp = (is_plain() ? cb0_ : cb1_).data(); |
1521 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 699 times.
|
699 | BOOST_ASSERT(cbp[1].size() == 0); |
1522 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 699 times.
|
699 | BOOST_ASSERT(cbp[0].size() == body_avail_); |
1523 | 699 | return core::string_view( | |
1524 | 699 | static_cast<char const*>(cbp[0].data()), | |
1525 | 1398 | body_avail_); | |
1526 | } | ||
1527 | |||
1528 | core::string_view | ||
1529 | ✗ | parser:: | |
1530 | release_buffered_data() noexcept | ||
1531 | { | ||
1532 | ✗ | return {}; | |
1533 | } | ||
1534 | |||
1535 | void | ||
1536 | 77 | parser:: | |
1537 | set_body_limit(std::uint64_t n) | ||
1538 | { | ||
1539 |
3/3✓ Branch 0 taken 73 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
|
77 | switch(st_) |
1540 | { | ||
1541 | 73 | case state::header: | |
1542 | case state::header_done: | ||
1543 | 73 | body_limit_ = n; | |
1544 | 73 | break; | |
1545 | 2 | case state::complete_in_place: | |
1546 | // only allowed for empty bodies | ||
1547 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if(body_total_ == 0) |
1548 | 1 | break; | |
1549 | BOOST_FALLTHROUGH; | ||
1550 | default: | ||
1551 | // set body_limit before parsing the body | ||
1552 | 3 | detail::throw_logic_error(); | |
1553 | } | ||
1554 | 74 | } | |
1555 | |||
1556 | //------------------------------------------------ | ||
1557 | // | ||
1558 | // Implementation | ||
1559 | // | ||
1560 | //------------------------------------------------ | ||
1561 | |||
1562 | void | ||
1563 | 755 | parser:: | |
1564 | on_set_body() noexcept | ||
1565 | { | ||
1566 |
4/6✓ Branch 0 taken 118 times.
✓ Branch 1 taken 637 times.
✓ Branch 2 taken 118 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 118 times.
|
755 | BOOST_ASSERT( |
1567 | st_ == state::header_done || | ||
1568 | st_ == state::body || | ||
1569 | st_ == state::complete_in_place); | ||
1570 | |||
1571 | 755 | nprepare_ = 0; // invalidate | |
1572 | |||
1573 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 755 times.
|
755 | if(st_ == state::body) |
1574 | ✗ | st_ = state::set_body; | |
1575 | 755 | } | |
1576 | |||
1577 | std::size_t | ||
1578 | 58258 | parser:: | |
1579 | apply_filter( | ||
1580 | system::error_code& ec, | ||
1581 | std::size_t payload_avail, | ||
1582 | bool more) | ||
1583 | { | ||
1584 | 58258 | std::size_t p0 = payload_avail; | |
1585 | for(;;) | ||
1586 | { | ||
1587 |
4/4✓ Branch 0 taken 56566 times.
✓ Branch 1 taken 60035 times.
✓ Branch 2 taken 56517 times.
✓ Branch 3 taken 49 times.
|
116601 | if(payload_avail == 0 && more) |
1588 | 56638 | break; | |
1589 | |||
1590 | ✗ | auto f_rs = [&](){ | |
1591 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 60084 times.
|
60084 | BOOST_ASSERT(filter_ != nullptr); |
1592 |
2/2✓ Branch 0 taken 19887 times.
✓ Branch 1 taken 40197 times.
|
60084 | if(how_ == how::elastic) |
1593 | { | ||
1594 | 19887 | std::size_t n = clamp(body_limit_remain()); | |
1595 | 19887 | n = clamp(n, svc_.cfg.min_buffer); | |
1596 | 19887 | n = clamp(n, eb_->max_size() - eb_->size()); | |
1597 | |||
1598 | // fill capacity first to avoid | ||
1599 | // an allocation | ||
1600 | std::size_t avail = | ||
1601 | 19887 | eb_->capacity() - eb_->size(); | |
1602 |
2/2✓ Branch 0 taken 19886 times.
✓ Branch 1 taken 1 times.
|
19887 | if(avail != 0) |
1603 | 19886 | n = clamp(n, avail); | |
1604 | |||
1605 |
1/2✓ Branch 1 taken 19887 times.
✗ Branch 2 not taken.
|
19887 | return filter_->process( |
1606 |
1/2✓ Branch 1 taken 19887 times.
✗ Branch 2 not taken.
|
19887 | eb_->prepare(n), |
1607 | 19887 | buffers::prefix( | |
1608 | 19887 | cb0_.data(), | |
1609 | payload_avail), | ||
1610 | 39774 | more); | |
1611 | } | ||
1612 | else // in-place and sink | ||
1613 | { | ||
1614 | 40197 | std::size_t n = clamp(body_limit_remain()); | |
1615 | 40197 | n = clamp(n, cb1_.capacity()); | |
1616 | |||
1617 |
1/2✓ Branch 1 taken 40197 times.
✗ Branch 2 not taken.
|
40197 | return filter_->process( |
1618 |
1/2✓ Branch 1 taken 40197 times.
✗ Branch 2 not taken.
|
40197 | cb1_.prepare(n), |
1619 | 40197 | buffers::prefix( | |
1620 | 40197 | cb0_.data(), | |
1621 | payload_avail), | ||
1622 | 80394 | more); | |
1623 | } | ||
1624 |
1/2✓ Branch 1 taken 60084 times.
✗ Branch 2 not taken.
|
60084 | }(); |
1625 | |||
1626 | 60084 | cb0_.consume(f_rs.in_bytes); | |
1627 | 60084 | payload_avail -= f_rs.in_bytes; | |
1628 | 60084 | body_total_ += f_rs.out_bytes; | |
1629 | |||
1630 |
2/2✓ Branch 0 taken 59964 times.
✓ Branch 1 taken 120 times.
|
120048 | bool needs_more_space = !f_rs.finished && |
1631 |
2/2✓ Branch 0 taken 3462 times.
✓ Branch 1 taken 56502 times.
|
59964 | payload_avail != 0; |
1632 | |||
1633 |
3/4✓ Branch 0 taken 20238 times.
✓ Branch 1 taken 19959 times.
✓ Branch 2 taken 19887 times.
✗ Branch 3 not taken.
|
60084 | switch(how_) |
1634 | { | ||
1635 | 20238 | case how::in_place: | |
1636 | { | ||
1637 | 20238 | cb1_.commit(f_rs.out_bytes); | |
1638 | 20238 | body_avail_ += f_rs.out_bytes; | |
1639 |
6/6✓ Branch 1 taken 1641 times.
✓ Branch 2 taken 18597 times.
✓ Branch 3 taken 1620 times.
✓ Branch 4 taken 21 times.
✓ Branch 5 taken 1620 times.
✓ Branch 6 taken 18618 times.
|
20238 | if(cb1_.capacity() == 0 && |
1640 | needs_more_space) | ||
1641 | { | ||
1642 | 3240 | ec = BOOST_HTTP_PROTO_ERR( | |
1643 | error::in_place_overflow); | ||
1644 | 1620 | goto done; | |
1645 | } | ||
1646 | 18618 | break; | |
1647 | } | ||
1648 | 19959 | case how::sink: | |
1649 | { | ||
1650 | 19959 | cb1_.commit(f_rs.out_bytes); | |
1651 |
1/2✓ Branch 1 taken 19959 times.
✗ Branch 2 not taken.
|
19959 | auto sink_rs = sink_->write( |
1652 | 19959 | cb1_.data(), !f_rs.finished); | |
1653 | 19959 | cb1_.consume(sink_rs.bytes); | |
1654 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 19959 times.
|
19959 | if(sink_rs.ec.failed()) |
1655 | { | ||
1656 | ✗ | ec = sink_rs.ec; | |
1657 | ✗ | st_ = state::reset; | |
1658 | ✗ | goto done; | |
1659 | } | ||
1660 | 19959 | break; | |
1661 | } | ||
1662 | 19887 | case how::elastic: | |
1663 | { | ||
1664 |
1/2✓ Branch 1 taken 19887 times.
✗ Branch 2 not taken.
|
19887 | eb_->commit(f_rs.out_bytes); |
1665 |
4/10✓ Branch 1 taken 19887 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19887 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 19887 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 19887 times.
|
19887 | if(eb_->max_size() - eb_->size() == 0 && |
1666 | needs_more_space) | ||
1667 | { | ||
1668 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
1669 | error::buffer_overflow); | ||
1670 | ✗ | st_ = state::reset; | |
1671 | ✗ | goto done; | |
1672 | } | ||
1673 | 19887 | break; | |
1674 | } | ||
1675 | } | ||
1676 | |||
1677 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 58463 times.
|
58464 | if(f_rs.ec.failed()) |
1678 | { | ||
1679 | 1 | ec = f_rs.ec; | |
1680 | 1 | st_ = state::reset; | |
1681 | 1 | break; | |
1682 | } | ||
1683 | |||
1684 |
4/6✓ Branch 1 taken 120 times.
✓ Branch 2 taken 58343 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 120 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 58463 times.
|
58463 | if(body_limit_remain() == 0 && |
1685 | needs_more_space) | ||
1686 | { | ||
1687 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
1688 | error::body_too_large); | ||
1689 | ✗ | st_ = state::reset; | |
1690 | ✗ | break; | |
1691 | } | ||
1692 | |||
1693 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 58343 times.
|
58463 | if(f_rs.finished) |
1694 | { | ||
1695 |
2/2✓ Branch 0 taken 72 times.
✓ Branch 1 taken 48 times.
|
120 | if(!more) |
1696 | { | ||
1697 | 72 | st_ = (how_ == how::in_place) | |
1698 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 48 times.
|
72 | ? state::complete_in_place |
1699 | : state::complete; | ||
1700 | } | ||
1701 | 120 | break; | |
1702 | } | ||
1703 | 58343 | } | |
1704 | |||
1705 | 58258 | done: | |
1706 | 58258 | return p0 - payload_avail; | |
1707 | } | ||
1708 | |||
1709 | detail::header const* | ||
1710 | 315 | parser:: | |
1711 | safe_get_header() const | ||
1712 | { | ||
1713 | // headers must be received | ||
1714 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 315 times.
|
315 | if(! got_header_) |
1715 | ✗ | detail::throw_logic_error(); | |
1716 | |||
1717 | 315 | return &h_; | |
1718 | } | ||
1719 | |||
1720 | bool | ||
1721 | 182247 | parser:: | |
1722 | is_plain() const noexcept | ||
1723 | { | ||
1724 |
2/2✓ Branch 0 taken 172342 times.
✓ Branch 1 taken 9905 times.
|
354589 | return ! filter_ && |
1725 |
2/2✓ Branch 0 taken 85758 times.
✓ Branch 1 taken 86584 times.
|
354589 | h_.md.payload != payload::chunked; |
1726 | } | ||
1727 | |||
1728 | std::uint64_t | ||
1729 | 165733 | parser:: | |
1730 | body_limit_remain() const noexcept | ||
1731 | { | ||
1732 | 165733 | return body_limit_ - body_total_; | |
1733 | } | ||
1734 | |||
1735 | } // http_proto | ||
1736 | } // boost | ||
1737 |