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