Line | Branch | Exec | Source |
---|---|---|---|
1 | // | ||
2 | // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) | ||
3 | // | ||
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
6 | // | ||
7 | // Official repository: https://github.com/CPPAlliance/http_proto | ||
8 | // | ||
9 | |||
10 | #ifndef BOOST_HTTP_PROTO_IMPL_PARSER_IPP | ||
11 | #define BOOST_HTTP_PROTO_IMPL_PARSER_IPP | ||
12 | |||
13 | #include <boost/http_proto/parser.hpp> | ||
14 | #include <boost/http_proto/context.hpp> | ||
15 | #include <boost/http_proto/error.hpp> | ||
16 | #include <boost/http_proto/service/zlib_service.hpp> | ||
17 | #include <boost/http_proto/detail/except.hpp> | ||
18 | #include <boost/buffers/buffer_copy.hpp> | ||
19 | #include <boost/url/grammar/ci_string.hpp> | ||
20 | #include <boost/assert.hpp> | ||
21 | #include <memory> | ||
22 | |||
23 | namespace boost { | ||
24 | namespace http_proto { | ||
25 | |||
26 | /* | ||
27 | Principles for fixed-size buffer design | ||
28 | |||
29 | axiom 1: | ||
30 | To read data you must have a buffer. | ||
31 | |||
32 | axiom 2: | ||
33 | The size of the HTTP header is not | ||
34 | known in advance. | ||
35 | |||
36 | conclusion 3: | ||
37 | A single I/O can produce a complete | ||
38 | HTTP header and additional payload | ||
39 | data. | ||
40 | |||
41 | conclusion 4: | ||
42 | A single I/O can produce multiple | ||
43 | complete HTTP headers, complete | ||
44 | payloads, and a partial header or | ||
45 | payload. | ||
46 | |||
47 | axiom 5: | ||
48 | A process is in one of two states: | ||
49 | 1. at or below capacity | ||
50 | 2. above capacity | ||
51 | |||
52 | axiom 6: | ||
53 | A program which can allocate an | ||
54 | unbounded number of resources can | ||
55 | go above capacity. | ||
56 | |||
57 | conclusion 7: | ||
58 | A program can guarantee never going | ||
59 | above capacity if all resources are | ||
60 | provisioned at program startup. | ||
61 | |||
62 | corollary 8: | ||
63 | `parser` and `serializer` should each | ||
64 | allocate a single buffer of calculated | ||
65 | size, and never resize it. | ||
66 | |||
67 | axiom #: | ||
68 | A parser and a serializer are always | ||
69 | used in pairs. | ||
70 | |||
71 | Buffer Usage | ||
72 | |||
73 | | | begin | ||
74 | | H | p | | f | read headers | ||
75 | | H | p | | T | f | set T body | ||
76 | | H | p | | C | T | f | make codec C | ||
77 | | H | p | b | C | T | f | decode p into b | ||
78 | | H | p | b | C | T | f | read/parse loop | ||
79 | | H | | T | f | destroy codec | ||
80 | | H | | T | f | finished | ||
81 | |||
82 | H headers | ||
83 | C codec | ||
84 | T body | ||
85 | f table | ||
86 | p partial payload | ||
87 | b body data | ||
88 | |||
89 | "payload" is the bytes coming in from | ||
90 | the stream. | ||
91 | |||
92 | "body" is the logical body, after transfer | ||
93 | encoding is removed. This can be the | ||
94 | same as the payload. | ||
95 | |||
96 | A "plain payload" is when the payload and | ||
97 | body are identical (no transfer encodings). | ||
98 | |||
99 | A "buffered payload" is any payload which is | ||
100 | not plain. A second buffer is required | ||
101 | for reading. | ||
102 | |||
103 | "overread" is additional data received past | ||
104 | the end of the headers when reading headers, | ||
105 | or additional data received past the end of | ||
106 | the message payload. | ||
107 | */ | ||
108 | //----------------------------------------------- | ||
109 | |||
110 | class parser_service | ||
111 | : public service | ||
112 | { | ||
113 | public: | ||
114 | parser::config_base cfg; | ||
115 | std::size_t space_needed = 0; | ||
116 | std::size_t max_codec = 0; | ||
117 | zlib::deflate_decoder_service const* | ||
118 | deflate_svc = nullptr; | ||
119 | |||
120 | parser_service( | ||
121 | context& ctx, | ||
122 | parser::config_base const& cfg_); | ||
123 | |||
124 | std::size_t | ||
125 | 7504 | max_overread() const noexcept | |
126 | { | ||
127 | return | ||
128 | 7504 | cfg.headers.max_size + | |
129 | 7504 | cfg.min_buffer; | |
130 | } | ||
131 | }; | ||
132 | |||
133 | 31 | parser_service:: | |
134 | parser_service( | ||
135 | context& ctx, | ||
136 | 31 | parser::config_base const& cfg_) | |
137 | 31 | : cfg(cfg_) | |
138 | { | ||
139 | /* | ||
140 | | fb | cb0 | cb1 | C | T | f | | ||
141 | |||
142 | fb flat_buffer headers.max_size | ||
143 | cb0 circular_buffer min_buffer | ||
144 | cb1 circular_buffer min_buffer | ||
145 | C codec max_codec | ||
146 | T body max_type_erase | ||
147 | f table max_table_space | ||
148 | |||
149 | */ | ||
150 | // validate | ||
151 | //if(cfg.min_prepare > cfg.max_prepare) | ||
152 | //detail::throw_invalid_argument(); | ||
153 | |||
154 |
1/2✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
|
31 | if( cfg.min_buffer < 1 || |
155 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
|
31 | cfg.min_buffer > cfg.body_limit) |
156 | ✗ | detail::throw_invalid_argument(); | |
157 | |||
158 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
|
31 | if(cfg.max_prepare < 1) |
159 | ✗ | detail::throw_invalid_argument(); | |
160 | |||
161 | // VFALCO TODO OVERFLOW CHECING | ||
162 | { | ||
163 | //fb_.size() - h_.size + | ||
164 | //svc_.cfg.min_buffer + | ||
165 | //svc_.cfg.min_buffer + | ||
166 | //svc_.max_codec; | ||
167 | } | ||
168 | |||
169 | // VFALCO OVERFLOW CHECKING ON THIS | ||
170 | 31 | space_needed += | |
171 |
1/2✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
|
31 | cfg.headers.valid_space_needed(); |
172 | |||
173 | // cb0_, cb1_ | ||
174 | // VFALCO OVERFLOW CHECKING ON THIS | ||
175 | 31 | space_needed += | |
176 | 31 | cfg.min_buffer + | |
177 | cfg.min_buffer; | ||
178 | |||
179 | // T | ||
180 | 31 | space_needed += cfg.max_type_erase; | |
181 | |||
182 | // max_codec | ||
183 | { | ||
184 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
|
31 | if(cfg.apply_deflate_decoder) |
185 | { | ||
186 | ✗ | deflate_svc = &ctx.get_service< | |
187 | ✗ | zlib::deflate_decoder_service>(); | |
188 | auto const n = | ||
189 | ✗ | deflate_svc->space_needed(); | |
190 | ✗ | if( max_codec < n) | |
191 | ✗ | max_codec = n; | |
192 | } | ||
193 | } | ||
194 | 31 | space_needed += max_codec; | |
195 | |||
196 | // round up to alignof(detail::header::entry) | ||
197 | 31 | auto const al = alignof( | |
198 | detail::header::entry); | ||
199 | 31 | space_needed = al * (( | |
200 | 31 | space_needed + al - 1) / al); | |
201 | 31 | } | |
202 | |||
203 | void | ||
204 | 31 | install_parser_service( | |
205 | context& ctx, | ||
206 | parser::config_base const& cfg) | ||
207 | { | ||
208 | ctx.make_service< | ||
209 | 31 | parser_service>(cfg); | |
210 | 31 | } | |
211 | |||
212 | //------------------------------------------------ | ||
213 | // | ||
214 | // Special Members | ||
215 | // | ||
216 | //------------------------------------------------ | ||
217 | |||
218 | 791 | parser:: | |
219 | parser( | ||
220 | context& ctx, | ||
221 | 791 | detail::kind k) | |
222 | : ctx_(ctx) | ||
223 | , svc_(ctx.get_service< | ||
224 | 1582 | parser_service>()) | |
225 | , h_(detail::empty{k}) | ||
226 | 791 | , st_(state::reset) | |
227 | { | ||
228 | 791 | auto const n = | |
229 | 791 | svc_.space_needed; | |
230 |
1/2✓ Branch 1 taken 791 times.
✗ Branch 2 not taken.
|
791 | ws_.allocate(n); |
231 | 791 | h_.cap = n; | |
232 | 791 | } | |
233 | |||
234 | //------------------------------------------------ | ||
235 | |||
236 | 791 | parser:: | |
237 | 791 | ~parser() | |
238 | { | ||
239 | 791 | } | |
240 | |||
241 | //------------------------------------------------ | ||
242 | // | ||
243 | // Modifiers | ||
244 | // | ||
245 | //------------------------------------------------ | ||
246 | |||
247 | // prepare for a new stream | ||
248 | void | ||
249 | 1258 | parser:: | |
250 | reset() noexcept | ||
251 | { | ||
252 | 1258 | ws_.clear(); | |
253 | 1258 | st_ = state::start; | |
254 | 1258 | got_eof_ = false; | |
255 | 1258 | } | |
256 | |||
257 | void | ||
258 | 1488 | parser:: | |
259 | start_impl( | ||
260 | bool head_response) | ||
261 | { | ||
262 | 1488 | std::size_t leftover = 0; | |
263 |
5/5✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1243 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 240 times.
|
1488 | switch(st_) |
264 | { | ||
265 | 1 | default: | |
266 | case state::reset: | ||
267 | // reset must be called first | ||
268 | 1 | detail::throw_logic_error(); | |
269 | |||
270 | 1243 | case state::start: | |
271 | // reset required on eof | ||
272 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1243 times.
|
1243 | if(got_eof_) |
273 | ✗ | detail::throw_logic_error(); | |
274 | 1243 | break; | |
275 | |||
276 | 3 | case state::header: | |
277 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
|
3 | if(fb_.size() == 0) |
278 | { | ||
279 | // start() called twice | ||
280 | 2 | detail::throw_logic_error(); | |
281 | } | ||
282 | BOOST_FALLTHROUGH; | ||
283 | |||
284 | case state::body: | ||
285 | case state::set_body: | ||
286 | // current message is incomplete | ||
287 | 2 | detail::throw_logic_error(); | |
288 | |||
289 | 240 | case state::complete: | |
290 | { | ||
291 | // remove partial body. | ||
292 |
1/2✓ Branch 0 taken 240 times.
✗ Branch 1 not taken.
|
240 | if(body_buf_ == &cb0_) |
293 | 240 | cb0_.consume(body_avail_); | |
294 | |||
295 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 240 times.
|
240 | if(cb0_.size() > 0) |
296 | { | ||
297 | // headers with no body | ||
298 | ✗ | BOOST_ASSERT(h_.size > 0); | |
299 | ✗ | fb_.consume(h_.size); | |
300 | ✗ | leftover = fb_.size(); | |
301 | // move unused octets to front | ||
302 | ✗ | buffers::buffer_copy( | |
303 | ✗ | buffers::mutable_buffer( | |
304 | ✗ | ws_.data(), | |
305 | leftover), | ||
306 | ✗ | fb_.data()); | |
307 | } | ||
308 | else | ||
309 | { | ||
310 | // leftover data after body | ||
311 | } | ||
312 | 240 | break; | |
313 | } | ||
314 | } | ||
315 | |||
316 | 1483 | ws_.clear(); | |
317 | |||
318 | 2966 | fb_ = { | |
319 | 1483 | ws_.data(), | |
320 | 1483 | svc_.cfg.headers.max_size + | |
321 | 1483 | svc_.cfg.min_buffer, | |
322 | leftover }; | ||
323 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 1483 times.
|
1483 | BOOST_ASSERT(fb_.capacity() == |
324 | svc_.max_overread()); | ||
325 | |||
326 | 2966 | h_ = detail::header( | |
327 | 1483 | detail::empty{h_.kind}); | |
328 | 1483 | h_.buf = reinterpret_cast< | |
329 | 1483 | char*>(ws_.data()); | |
330 | 1483 | h_.cbuf = h_.buf; | |
331 | 1483 | h_.cap = ws_.size(); | |
332 | |||
333 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1483 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1483 | BOOST_ASSERT(! head_response || |
334 | h_.kind == detail::kind::response); | ||
335 | 1483 | head_response_ = head_response; | |
336 | |||
337 | // begin with in_place mode | ||
338 | 1483 | how_ = how::in_place; | |
339 | 1483 | st_ = state::header; | |
340 | 1483 | nprepare_ = 0; | |
341 | 1483 | } | |
342 | |||
343 | auto | ||
344 | 4494 | parser:: | |
345 | prepare() -> | ||
346 | mutable_buffers_type | ||
347 | { | ||
348 | 4494 | nprepare_ = 0; | |
349 | |||
350 |
6/6✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4431 times.
✓ Branch 3 taken 31 times.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 3 times.
|
4494 | switch(st_) |
351 | { | ||
352 | 1 | default: | |
353 | case state::reset: | ||
354 | // reset must be called first | ||
355 | 1 | detail::throw_logic_error(); | |
356 | |||
357 | 1 | case state::start: | |
358 | // start must be called first | ||
359 | 1 | detail::throw_logic_error(); | |
360 | |||
361 | 4431 | case state::header: | |
362 | { | ||
363 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4431 times.
|
4431 | BOOST_ASSERT(h_.size < |
364 | svc_.cfg.headers.max_size); | ||
365 | 4431 | auto n = fb_.capacity() - fb_.size(); | |
366 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4431 times.
|
4431 | BOOST_ASSERT(n <= svc_.max_overread()); |
367 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 4402 times.
|
4431 | if( n > svc_.cfg.max_prepare) |
368 | 29 | n = svc_.cfg.max_prepare; | |
369 | 4431 | mbp_[0] = fb_.prepare(n); | |
370 | 4431 | nprepare_ = n; | |
371 | 4431 | return mutable_buffers_type( | |
372 | 8862 | &mbp_[0], 1); | |
373 | } | ||
374 | |||
375 | 31 | case state::body: | |
376 | { | ||
377 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
|
31 | if(got_eof_) |
378 | ✗ | return mutable_buffers_type{}; | |
379 | |||
380 | 31 | do_body: | |
381 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
|
55 | if(! is_plain()) |
382 | { | ||
383 | // buffered payload | ||
384 | ✗ | auto n = cb0_.capacity() - | |
385 | ✗ | cb0_.size(); | |
386 | ✗ | if( n > svc_.cfg.max_prepare) | |
387 | ✗ | n = svc_.cfg.max_prepare; | |
388 | ✗ | mbp_ = cb0_.prepare(n); | |
389 | ✗ | nprepare_ = n; | |
390 | ✗ | return mutable_buffers_type(mbp_); | |
391 | } | ||
392 | |||
393 | // plain payload | ||
394 | |||
395 |
2/2✓ Branch 0 taken 29 times.
✓ Branch 1 taken 26 times.
|
55 | if(how_ == how::in_place) |
396 | { | ||
397 | auto n = | ||
398 | 29 | body_buf_->capacity() - | |
399 | 29 | body_buf_->size(); | |
400 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 28 times.
|
29 | if( n > svc_.cfg.max_prepare) |
401 | 1 | n = svc_.cfg.max_prepare; | |
402 | 29 | mbp_ = body_buf_->prepare(n); | |
403 | 29 | nprepare_ = n; | |
404 | 29 | return mutable_buffers_type(mbp_); | |
405 | } | ||
406 | |||
407 |
1/2✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
|
26 | if(how_ == how::dynamic) |
408 | { | ||
409 | // Overreads are not allowed, or | ||
410 | // else the caller will see extra | ||
411 | // unrelated data. | ||
412 | |||
413 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 17 times.
|
26 | if(h_.md.payload == payload::size) |
414 | { | ||
415 | // set_body moves avail to dyn | ||
416 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | BOOST_ASSERT(body_buf_->size() == 0); |
417 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
|
9 | BOOST_ASSERT(body_avail_ == 0); |
418 | 9 | auto n = payload_remain_; | |
419 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
|
9 | if( n > svc_.cfg.max_prepare) |
420 | 1 | n = svc_.cfg.max_prepare; | |
421 | 9 | nprepare_ = n; | |
422 | 9 | return dyn_->prepare(n); | |
423 | } | ||
424 | |||
425 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | BOOST_ASSERT( |
426 | h_.md.payload == payload::to_eof); | ||
427 | 17 | std::size_t n = 0; | |
428 |
1/2✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
|
17 | if(! got_eof_) |
429 | { | ||
430 | // calculate n heuristically | ||
431 | 17 | n = svc_.cfg.min_buffer; | |
432 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
|
17 | if( n > svc_.cfg.max_prepare) |
433 | 1 | n = svc_.cfg.max_prepare; | |
434 | { | ||
435 | // apply max_size() | ||
436 | auto avail = | ||
437 | 17 | dyn_->max_size() - | |
438 | 17 | dyn_->size(); | |
439 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
|
17 | if( n > avail) |
440 | 8 | n = avail; | |
441 | } | ||
442 | // fill capacity() first, | ||
443 | // to avoid an allocation | ||
444 | { | ||
445 | auto avail = | ||
446 | 17 | dyn_->capacity() - | |
447 | 17 | dyn_->size(); | |
448 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
17 | if( n > avail && |
449 | avail != 0) | ||
450 | 1 | n = avail; | |
451 | } | ||
452 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
|
17 | if(n == 0) |
453 | { | ||
454 | // dynamic buffer is full | ||
455 | // attempt a 1 byte read so | ||
456 | // we can detect overflow | ||
457 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | BOOST_ASSERT( |
458 | body_buf_->size() == 0); | ||
459 | // handled in init_dynamic | ||
460 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | BOOST_ASSERT( |
461 | body_avail_ == 0); | ||
462 | 2 | mbp_ = body_buf_->prepare(1); | |
463 | 2 | nprepare_ = 1; | |
464 | return | ||
465 | 2 | mutable_buffers_type(mbp_); | |
466 | } | ||
467 | } | ||
468 | 15 | nprepare_ = n; | |
469 | 15 | return dyn_->prepare(n); | |
470 | } | ||
471 | |||
472 | // VFALCO TODO | ||
473 | ✗ | if(how_ == how::pull) | |
474 | ✗ | detail::throw_logic_error(); | |
475 | |||
476 | // VFALCO TODO | ||
477 | ✗ | detail::throw_logic_error(); | |
478 | } | ||
479 | |||
480 | 27 | case state::set_body: | |
481 | { | ||
482 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
|
27 | BOOST_ASSERT(is_plain()); |
483 | |||
484 |
1/2✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
|
27 | if(how_ == how::dynamic) |
485 | { | ||
486 | // attempt to transfer in-place | ||
487 | // body into the dynamic buffer. | ||
488 | 27 | system::error_code ec; | |
489 |
1/2✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
|
27 | init_dynamic(ec); |
490 |
2/2✓ Branch 1 taken 26 times.
✓ Branch 2 taken 1 times.
|
27 | if(! ec.failed()) |
491 | { | ||
492 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
|
26 | if(st_ == state::body) |
493 | 24 | goto do_body; | |
494 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | BOOST_ASSERT( |
495 | st_ == state::complete); | ||
496 | 2 | return mutable_buffers_type{}; | |
497 | } | ||
498 | |||
499 | // not enough room, so we | ||
500 | // return this error from parse() | ||
501 | return | ||
502 | 1 | mutable_buffers_type{}; | |
503 | } | ||
504 | |||
505 | ✗ | if(how_ == how::sink) | |
506 | { | ||
507 | // this is a no-op, to get the | ||
508 | // caller to call parse next. | ||
509 | ✗ | return mutable_buffers_type{}; | |
510 | } | ||
511 | |||
512 | // VFALCO TODO | ||
513 | ✗ | detail::throw_logic_error(); | |
514 | } | ||
515 | |||
516 | 3 | case state::complete: | |
517 | // intended no-op | ||
518 | 3 | return mutable_buffers_type{}; | |
519 | } | ||
520 | } | ||
521 | |||
522 | void | ||
523 | 4485 | parser:: | |
524 | commit( | ||
525 | std::size_t n) | ||
526 | { | ||
527 |
6/6✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4431 times.
✓ Branch 3 taken 46 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
|
4485 | switch(st_) |
528 | { | ||
529 | 1 | default: | |
530 | case state::reset: | ||
531 | { | ||
532 | // reset must be called first | ||
533 | 1 | detail::throw_logic_error(); | |
534 | } | ||
535 | |||
536 | 1 | case state::start: | |
537 | { | ||
538 | // forgot to call start() | ||
539 | 1 | detail::throw_logic_error(); | |
540 | } | ||
541 | |||
542 | 4431 | case state::header: | |
543 | { | ||
544 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4430 times.
|
4431 | if(n > nprepare_) |
545 | { | ||
546 | // n can't be greater than size of | ||
547 | // the buffers returned by prepare() | ||
548 | 1 | detail::throw_invalid_argument(); | |
549 | } | ||
550 | |||
551 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4429 times.
|
4430 | if(got_eof_) |
552 | { | ||
553 | // can't commit after EOF | ||
554 | 1 | detail::throw_logic_error(); | |
555 | } | ||
556 | |||
557 | 4429 | nprepare_ = 0; // invalidate | |
558 | 4429 | fb_.commit(n); | |
559 | 4429 | break; | |
560 | } | ||
561 | |||
562 | 46 | case state::body: | |
563 | { | ||
564 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 45 times.
|
46 | if(n > nprepare_) |
565 | { | ||
566 | // n can't be greater than size of | ||
567 | // the buffers returned by prepare() | ||
568 | 1 | detail::throw_invalid_argument(); | |
569 | } | ||
570 | |||
571 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
45 | BOOST_ASSERT(! got_eof_ || n == 0); |
572 | |||
573 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 45 times.
|
45 | if(! is_plain()) |
574 | { | ||
575 | // buffered payload | ||
576 | ✗ | cb0_.commit(n); | |
577 | ✗ | break; | |
578 | } | ||
579 | |||
580 | // plain payload | ||
581 | |||
582 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 19 times.
|
45 | if(how_ == how::in_place) |
583 | { | ||
584 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
|
26 | BOOST_ASSERT(body_buf_ == &cb0_); |
585 | 26 | cb0_.commit(n); | |
586 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 14 times.
|
26 | if(h_.md.payload == payload::size) |
587 | { | ||
588 | 12 | if(cb0_.size() < | |
589 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
|
12 | h_.md.payload_size) |
590 | { | ||
591 | 4 | body_avail_ += n; | |
592 | 4 | payload_remain_ -= n; | |
593 | 4 | break; | |
594 | } | ||
595 | 8 | body_avail_ = h_.md.payload_size; | |
596 | 8 | payload_remain_ = 0; | |
597 | 8 | st_ = state::complete; | |
598 | 8 | break; | |
599 | } | ||
600 | |||
601 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | BOOST_ASSERT( |
602 | h_.md.payload == payload::to_eof); | ||
603 | 14 | body_avail_ += n; | |
604 | 14 | break; | |
605 | } | ||
606 | |||
607 |
1/2✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
|
19 | if(how_ == how::dynamic) |
608 | { | ||
609 |
2/2✓ Branch 2 taken 18 times.
✓ Branch 3 taken 1 times.
|
19 | if(dyn_->size() < dyn_->max_size()) |
610 | { | ||
611 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
18 | BOOST_ASSERT(body_avail_ == 0); |
612 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
|
18 | BOOST_ASSERT( |
613 | body_buf_->size() == 0); | ||
614 | 18 | dyn_->commit(n); | |
615 | } | ||
616 | else | ||
617 | { | ||
618 | // If we get here then either | ||
619 | // n==0 as a no-op, or n==1 for | ||
620 | // an intended one byte read. | ||
621 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | BOOST_ASSERT(n <= 1); |
622 | 1 | body_buf_->commit(n); | |
623 | 1 | body_avail_ += n; | |
624 | } | ||
625 | 19 | body_total_ += n; | |
626 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 13 times.
|
19 | if(h_.md.payload == payload::size) |
627 | { | ||
628 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | BOOST_ASSERT( |
629 | n <= payload_remain_); | ||
630 | 6 | payload_remain_ -= n; | |
631 |
1/2✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
|
6 | if(payload_remain_ == 0) |
632 | 6 | st_ = state::complete; | |
633 | } | ||
634 | 19 | break; | |
635 | } | ||
636 | |||
637 | ✗ | if(how_ == how::sink) | |
638 | { | ||
639 | ✗ | cb0_.commit(n); | |
640 | ✗ | break; | |
641 | } | ||
642 | |||
643 | ✗ | if(how_ == how::pull) | |
644 | { | ||
645 | // VFALCO TODO | ||
646 | ✗ | detail::throw_logic_error(); | |
647 | } | ||
648 | ✗ | break; | |
649 | } | ||
650 | |||
651 | 2 | case state::set_body: | |
652 | { | ||
653 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if(n > nprepare_) |
654 | { | ||
655 | // n can't be greater than size of | ||
656 | // the buffers returned by prepare() | ||
657 | 1 | detail::throw_invalid_argument(); | |
658 | } | ||
659 | |||
660 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | BOOST_ASSERT(is_plain()); |
661 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | BOOST_ASSERT(n == 0); |
662 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if( how_ == how::dynamic || |
663 | ✗ | how_ == how::sink) | |
664 | { | ||
665 | // intended no-op | ||
666 | break; | ||
667 | } | ||
668 | |||
669 | // VFALCO TODO | ||
670 | ✗ | detail::throw_logic_error(); | |
671 | } | ||
672 | |||
673 | 4 | case state::complete: | |
674 | { | ||
675 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | BOOST_ASSERT(nprepare_ == 0); |
676 | |||
677 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
|
4 | if(n > 0) |
678 | { | ||
679 | // n can't be greater than size of | ||
680 | // the buffers returned by prepare() | ||
681 | 1 | detail::throw_invalid_argument(); | |
682 | } | ||
683 | |||
684 | // intended no-op | ||
685 | 3 | break; | |
686 | } | ||
687 | } | ||
688 | 4478 | } | |
689 | |||
690 | void | ||
691 | 363 | parser:: | |
692 | commit_eof() | ||
693 | { | ||
694 | 363 | nprepare_ = 0; // invalidate | |
695 | |||
696 |
6/6✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 127 times.
✓ Branch 4 taken 212 times.
✓ Branch 5 taken 1 times.
|
363 | switch(st_) |
697 | { | ||
698 | 1 | default: | |
699 | case state::reset: | ||
700 | // reset must be called first | ||
701 | 1 | detail::throw_logic_error(); | |
702 | |||
703 | 1 | case state::start: | |
704 | // forgot to call prepare() | ||
705 | 1 | detail::throw_logic_error(); | |
706 | |||
707 | 21 | case state::header: | |
708 | 21 | got_eof_ = true; | |
709 | 21 | break; | |
710 | |||
711 | 127 | case state::body: | |
712 | 127 | got_eof_ = true; | |
713 | 127 | break; | |
714 | |||
715 | 212 | case state::set_body: | |
716 | 212 | got_eof_ = true; | |
717 | 212 | break; | |
718 | |||
719 | 1 | case state::complete: | |
720 | // can't commit eof when complete | ||
721 | 1 | detail::throw_logic_error(); | |
722 | } | ||
723 | 360 | } | |
724 | |||
725 | //----------------------------------------------- | ||
726 | |||
727 | // process input data then | ||
728 | // eof if input data runs out. | ||
729 | void | ||
730 | 5407 | parser:: | |
731 | parse( | ||
732 | system::error_code& ec) | ||
733 | { | ||
734 | 5407 | ec = {}; | |
735 |
6/6✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4445 times.
✓ Branch 3 taken 157 times.
✓ Branch 4 taken 211 times.
✓ Branch 5 taken 592 times.
|
5407 | switch(st_) |
736 | { | ||
737 | 1 | default: | |
738 | case state::reset: | ||
739 | // reset must be called first | ||
740 | 1 | detail::throw_logic_error(); | |
741 | |||
742 | 1 | case state::start: | |
743 | // start must be called first | ||
744 | 1 | detail::throw_logic_error(); | |
745 | |||
746 | 4445 | case state::header: | |
747 | { | ||
748 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4445 times.
|
4445 | BOOST_ASSERT(h_.buf == static_cast< |
749 | void const*>(ws_.data())); | ||
750 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4445 times.
|
4445 | BOOST_ASSERT(h_.cbuf == static_cast< |
751 | void const*>(ws_.data())); | ||
752 | 4445 | auto const new_size = fb_.size(); | |
753 | 4445 | h_.parse(new_size, svc_.cfg.headers, ec); | |
754 |
2/2✓ Branch 2 taken 2990 times.
✓ Branch 3 taken 1455 times.
|
4445 | if(ec == condition::need_more_input) |
755 | { | ||
756 |
2/2✓ Branch 0 taken 2972 times.
✓ Branch 1 taken 18 times.
|
2990 | if(! got_eof_) |
757 | { | ||
758 | // headers incomplete | ||
759 | 2972 | return; | |
760 | } | ||
761 | |||
762 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 10 times.
|
18 | if(fb_.size() == 0) |
763 | { | ||
764 | // stream closed cleanly | ||
765 | 8 | st_ = state::complete; | |
766 | 16 | ec = BOOST_HTTP_PROTO_ERR( | |
767 | error::end_of_stream); | ||
768 | 8 | return; | |
769 | } | ||
770 | |||
771 | // stream closed with a | ||
772 | // partial message received | ||
773 | 10 | st_ = state::reset; | |
774 | 20 | ec = BOOST_HTTP_PROTO_ERR( | |
775 | error::incomplete); | ||
776 | 10 | return; | |
777 | } | ||
778 |
2/2✓ Branch 1 taken 128 times.
✓ Branch 2 taken 1327 times.
|
1455 | if(ec.failed()) |
779 | { | ||
780 | // other error, | ||
781 | // | ||
782 | // VFALCO map this to a bad | ||
783 | // request or bad response error? | ||
784 | // | ||
785 | 128 | st_ = state::reset; // unrecoverable | |
786 | 128 | return; | |
787 | } | ||
788 | |||
789 | // headers are complete | ||
790 | 1327 | on_headers(ec); | |
791 |
2/2✓ Branch 1 taken 120 times.
✓ Branch 2 taken 1207 times.
|
1327 | if(ec.failed()) |
792 | 120 | return; | |
793 |
2/2✓ Branch 0 taken 722 times.
✓ Branch 1 taken 485 times.
|
1207 | if(st_ == state::complete) |
794 | 722 | break; | |
795 | BOOST_FALLTHROUGH; | ||
796 | } | ||
797 | |||
798 | case state::body: | ||
799 | { | ||
800 | 485 | do_body: | |
801 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | BOOST_ASSERT(st_ == state::body); |
802 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | BOOST_ASSERT( |
803 | h_.md.payload != payload::none); | ||
804 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | BOOST_ASSERT( |
805 | h_.md.payload != payload::error); | ||
806 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | if(h_.md.payload == payload::chunked) |
807 | { | ||
808 | // VFALCO parse chunked | ||
809 | ✗ | detail::throw_logic_error(); | |
810 | } | ||
811 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | else if(filt_) |
812 | { | ||
813 | // VFALCO TODO apply filter | ||
814 | ✗ | detail::throw_logic_error(); | |
815 | } | ||
816 | |||
817 |
2/2✓ Branch 0 taken 618 times.
✓ Branch 1 taken 126 times.
|
744 | if(how_ == how::in_place) |
818 | { | ||
819 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 618 times.
|
618 | BOOST_ASSERT(body_avail_ == |
820 | body_buf_->size()); | ||
821 |
2/2✓ Branch 0 taken 255 times.
✓ Branch 1 taken 363 times.
|
618 | if(h_.md.payload == payload::size) |
822 | { | ||
823 | 255 | if(body_avail_ < | |
824 |
2/2✓ Branch 0 taken 30 times.
✓ Branch 1 taken 225 times.
|
255 | h_.md.payload_size) |
825 | { | ||
826 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 29 times.
|
30 | if(got_eof_) |
827 | { | ||
828 | // incomplete | ||
829 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
830 | error::incomplete); | ||
831 | 1 | return; | |
832 | } | ||
833 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 28 times.
|
29 | if(body_buf_->capacity() == 0) |
834 | { | ||
835 | // in_place buffer limit | ||
836 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
837 | error::in_place_overflow); | ||
838 | 1 | return; | |
839 | } | ||
840 | 56 | ec = BOOST_HTTP_PROTO_ERR( | |
841 | error::need_data); | ||
842 | 28 | return; | |
843 | } | ||
844 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
|
225 | BOOST_ASSERT(body_avail_ == |
845 | h_.md.payload_size); | ||
846 | 225 | st_ = state::complete; | |
847 | 225 | break; | |
848 | } | ||
849 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 362 times.
|
363 | if(body_avail_ > svc_.cfg.body_limit) |
850 | { | ||
851 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
852 | error::body_too_large); | ||
853 | 1 | st_ = state::reset; // unrecoverable | |
854 | 1 | return; | |
855 | } | ||
856 |
1/2✓ Branch 0 taken 362 times.
✗ Branch 1 not taken.
|
362 | if( h_.md.payload == payload::chunked || |
857 |
2/2✓ Branch 0 taken 248 times.
✓ Branch 1 taken 114 times.
|
362 | ! got_eof_) |
858 | { | ||
859 | 496 | ec = BOOST_HTTP_PROTO_ERR( | |
860 | error::need_data); | ||
861 | 248 | return; | |
862 | } | ||
863 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
|
114 | BOOST_ASSERT(got_eof_); |
864 | 114 | st_ = state::complete; | |
865 | 114 | break; | |
866 | } | ||
867 | |||
868 |
1/2✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
|
126 | if(how_ == how::dynamic) |
869 | { | ||
870 | // state already updated in commit | ||
871 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
|
126 | if(h_.md.payload == payload::size) |
872 | { | ||
873 | ✗ | BOOST_ASSERT(body_total_ < | |
874 | h_.md.payload_size); | ||
875 | ✗ | BOOST_ASSERT(payload_remain_ > 0); | |
876 | ✗ | if(body_avail_ != 0) | |
877 | { | ||
878 | ✗ | BOOST_ASSERT( | |
879 | dyn_->max_size() - | ||
880 | dyn_->size() < | ||
881 | payload_remain_); | ||
882 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
883 | error::buffer_overflow); | ||
884 | ✗ | st_ = state::reset; // unrecoverable | |
885 | ✗ | return; | |
886 | } | ||
887 | ✗ | if(got_eof_) | |
888 | { | ||
889 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
890 | error::incomplete); | ||
891 | ✗ | st_ = state::reset; // unrecoverable | |
892 | ✗ | return; | |
893 | } | ||
894 | ✗ | return; | |
895 | } | ||
896 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
|
126 | BOOST_ASSERT( |
897 | h_.md.payload == payload::to_eof); | ||
898 |
3/4✓ Branch 2 taken 46 times.
✓ Branch 3 taken 80 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 126 times.
|
172 | if( dyn_->size() == dyn_->max_size() && |
899 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
|
46 | body_avail_ > 0) |
900 | { | ||
901 | // got here from the 1-byte read | ||
902 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
903 | error::buffer_overflow); | ||
904 | ✗ | st_ = state::reset; // unrecoverable | |
905 | ✗ | return; | |
906 | } | ||
907 |
2/2✓ Branch 0 taken 113 times.
✓ Branch 1 taken 13 times.
|
126 | if(got_eof_) |
908 | { | ||
909 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
|
113 | BOOST_ASSERT(body_avail_ == 0); |
910 | 113 | st_ = state::complete; | |
911 | 113 | break; | |
912 | } | ||
913 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
|
13 | BOOST_ASSERT(body_avail_ == 0); |
914 | 13 | break; | |
915 | } | ||
916 | |||
917 | // VFALCO TODO | ||
918 | ✗ | detail::throw_logic_error(); | |
919 | } | ||
920 | |||
921 | 211 | case state::set_body: | |
922 | { | ||
923 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 211 times.
|
211 | BOOST_ASSERT(is_plain()); |
924 | |||
925 | // transfer in_place data into set body | ||
926 | |||
927 |
1/2✓ Branch 0 taken 211 times.
✗ Branch 1 not taken.
|
211 | if(how_ == how::dynamic) |
928 | { | ||
929 | 211 | init_dynamic(ec); | |
930 |
1/2✓ Branch 1 taken 211 times.
✗ Branch 2 not taken.
|
211 | if(! ec.failed()) |
931 | { | ||
932 |
2/2✓ Branch 0 taken 102 times.
✓ Branch 1 taken 109 times.
|
211 | if(st_ == state::body) |
933 | 102 | goto do_body; | |
934 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
|
109 | BOOST_ASSERT( |
935 | st_ == state::complete); | ||
936 | 109 | break; | |
937 | } | ||
938 | ✗ | st_ = state::reset; // unrecoverable | |
939 | ✗ | return; | |
940 | } | ||
941 | |||
942 | ✗ | if(how_ == how::sink) | |
943 | { | ||
944 | ✗ | auto n = body_buf_->size(); | |
945 | ✗ | if(h_.md.payload == payload::size) | |
946 | { | ||
947 | // sink_->size_hint(h_.md.payload_size, ec); | ||
948 | |||
949 | ✗ | if(n < h_.md.payload_size) | |
950 | { | ||
951 | ✗ | auto rv = sink_->write( | |
952 | ✗ | body_buf_->data(), false); | |
953 | ✗ | BOOST_ASSERT(rv.ec.failed() || | |
954 | rv.bytes == body_buf_->size()); | ||
955 | ✗ | BOOST_ASSERT( | |
956 | rv.bytes >= body_avail_); | ||
957 | ✗ | BOOST_ASSERT( | |
958 | rv.bytes < payload_remain_); | ||
959 | ✗ | body_buf_->consume(rv.bytes); | |
960 | ✗ | body_avail_ -= rv.bytes; | |
961 | ✗ | body_total_ += rv.bytes; | |
962 | ✗ | payload_remain_ -= rv.bytes; | |
963 | ✗ | if(rv.ec.failed()) | |
964 | { | ||
965 | ✗ | ec = rv.ec; | |
966 | ✗ | st_ = state::reset; // unrecoverable | |
967 | ✗ | return; | |
968 | } | ||
969 | ✗ | st_ = state::body; | |
970 | ✗ | goto do_body; | |
971 | } | ||
972 | |||
973 | ✗ | n = h_.md.payload_size; | |
974 | } | ||
975 | // complete | ||
976 | ✗ | BOOST_ASSERT(body_buf_ == &cb0_); | |
977 | ✗ | auto rv = sink_->write( | |
978 | ✗ | body_buf_->data(), true); | |
979 | ✗ | BOOST_ASSERT(rv.ec.failed() || | |
980 | rv.bytes == body_buf_->size()); | ||
981 | ✗ | body_buf_->consume(rv.bytes); | |
982 | ✗ | if(rv.ec.failed()) | |
983 | { | ||
984 | ✗ | ec = rv.ec; | |
985 | ✗ | st_ = state::reset; // unrecoverable | |
986 | ✗ | return; | |
987 | } | ||
988 | ✗ | st_ = state::complete; | |
989 | ✗ | return; | |
990 | } | ||
991 | |||
992 | // VFALCO TODO | ||
993 | ✗ | detail::throw_logic_error(); | |
994 | } | ||
995 | |||
996 | 592 | case state::complete: | |
997 | { | ||
998 | // This is a no-op except when set_body | ||
999 | // was called and we have in-place data. | ||
1000 |
2/4✓ Branch 0 taken 296 times.
✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
592 | switch(how_) |
1001 | { | ||
1002 | 296 | default: | |
1003 | case how::in_place: | ||
1004 | 296 | break; | |
1005 | |||
1006 | 296 | case how::dynamic: | |
1007 | { | ||
1008 |
1/2✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
|
296 | if(body_buf_->size() == 0) |
1009 | 296 | break; | |
1010 | ✗ | BOOST_ASSERT(dyn_->size() == 0); | |
1011 | ✗ | auto n = buffers::buffer_copy( | |
1012 | ✗ | dyn_->prepare( | |
1013 | ✗ | body_buf_->size()), | |
1014 | ✗ | body_buf_->data()); | |
1015 | ✗ | body_buf_->consume(n); | |
1016 | ✗ | break; | |
1017 | } | ||
1018 | |||
1019 | ✗ | case how::sink: | |
1020 | { | ||
1021 | ✗ | if(body_buf_->size() == 0) | |
1022 | ✗ | break; | |
1023 | ✗ | auto rv = sink_->write( | |
1024 | ✗ | body_buf_->data(), false); | |
1025 | ✗ | body_buf_->consume(rv.bytes); | |
1026 | ✗ | if(rv.ec.failed()) | |
1027 | { | ||
1028 | ✗ | ec = rv.ec; | |
1029 | ✗ | st_ = state::reset; // unrecoverable | |
1030 | ✗ | return; | |
1031 | } | ||
1032 | ✗ | break; | |
1033 | } | ||
1034 | |||
1035 | ✗ | case how::pull: | |
1036 | // VFALCO TODO | ||
1037 | ✗ | detail::throw_logic_error(); | |
1038 | } | ||
1039 | } | ||
1040 | } | ||
1041 | } | ||
1042 | |||
1043 | //------------------------------------------------ | ||
1044 | |||
1045 | auto | ||
1046 | ✗ | parser:: | |
1047 | pull_some() -> | ||
1048 | const_buffers_type | ||
1049 | { | ||
1050 | ✗ | return {}; | |
1051 | } | ||
1052 | |||
1053 | core::string_view | ||
1054 | 1271 | parser:: | |
1055 | body() const noexcept | ||
1056 | { | ||
1057 |
2/2✓ Branch 0 taken 349 times.
✓ Branch 1 taken 922 times.
|
1271 | switch(st_) |
1058 | { | ||
1059 | 349 | default: | |
1060 | case state::reset: | ||
1061 | case state::start: | ||
1062 | case state::header: | ||
1063 | case state::body: | ||
1064 | case state::set_body: | ||
1065 | // not complete | ||
1066 | 349 | return {}; | |
1067 | |||
1068 | 922 | case state::complete: | |
1069 |
2/2✓ Branch 0 taken 346 times.
✓ Branch 1 taken 576 times.
|
922 | if(how_ != how::in_place) |
1070 | { | ||
1071 | // not in_place | ||
1072 | 346 | return {}; | |
1073 | } | ||
1074 | 576 | auto cbp = body_buf_->data(); | |
1075 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 576 times.
|
576 | BOOST_ASSERT(cbp[1].size() == 0); |
1076 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 576 times.
|
576 | BOOST_ASSERT(cbp[0].size() >= body_avail_); |
1077 | 576 | return core::string_view( | |
1078 | static_cast<char const*>( | ||
1079 | 576 | cbp[0].data()), | |
1080 | 1152 | body_avail_); | |
1081 | } | ||
1082 | } | ||
1083 | |||
1084 | core::string_view | ||
1085 | ✗ | parser:: | |
1086 | release_buffered_data() noexcept | ||
1087 | { | ||
1088 | ✗ | return {}; | |
1089 | } | ||
1090 | |||
1091 | //------------------------------------------------ | ||
1092 | // | ||
1093 | // Implementation | ||
1094 | // | ||
1095 | //------------------------------------------------ | ||
1096 | |||
1097 | auto | ||
1098 | 55 | parser:: | |
1099 | safe_get_header() const -> | ||
1100 | detail::header const* | ||
1101 | { | ||
1102 | // headers must be received | ||
1103 |
3/6✓ Branch 1 taken 55 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 55 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 55 times.
|
110 | if( ! got_header() || |
1104 | 55 | fb_.size() == 0) // happens on eof | |
1105 | ✗ | detail::throw_logic_error(); | |
1106 | |||
1107 | 55 | return &h_; | |
1108 | } | ||
1109 | |||
1110 | bool | ||
1111 | 824 | parser:: | |
1112 | is_plain() const noexcept | ||
1113 | { | ||
1114 |
1/2✓ Branch 0 taken 824 times.
✗ Branch 1 not taken.
|
1648 | return ! filt_ && |
1115 |
1/2✓ Branch 0 taken 824 times.
✗ Branch 1 not taken.
|
824 | h_.md.payload != |
1116 | 824 | payload::chunked; | |
1117 | } | ||
1118 | |||
1119 | // Called immediately after complete headers | ||
1120 | // are received. We leave fb_ as-is to indicate | ||
1121 | // whether any data was received before eof. | ||
1122 | // | ||
1123 | void | ||
1124 | 1327 | parser:: | |
1125 | on_headers( | ||
1126 | system::error_code& ec) | ||
1127 | { | ||
1128 | 1327 | auto const overread = fb_.size() - h_.size; | |
1129 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1327 times.
|
1327 | BOOST_ASSERT( |
1130 | overread <= svc_.max_overread()); | ||
1131 | |||
1132 | // metadata error | ||
1133 |
2/2✓ Branch 0 taken 120 times.
✓ Branch 1 taken 1207 times.
|
1327 | if(h_.md.payload == payload::error) |
1134 | { | ||
1135 | // VFALCO This needs looking at | ||
1136 | 240 | ec = BOOST_HTTP_PROTO_ERR( | |
1137 | error::bad_payload); | ||
1138 | 120 | st_ = state::reset; // unrecoverable | |
1139 | 120 | return; | |
1140 | } | ||
1141 | |||
1142 | // reserve headers + table | ||
1143 | 1207 | ws_.reserve_front(h_.size); | |
1144 | 1207 | ws_.reserve_back(h_.table_space()); | |
1145 | |||
1146 | // no payload | ||
1147 |
2/2✓ Branch 0 taken 485 times.
✓ Branch 1 taken 722 times.
|
1207 | if( h_.md.payload == payload::none || |
1148 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 485 times.
|
485 | head_response_) |
1149 | { | ||
1150 | // set cb0_ to overread | ||
1151 | 1444 | cb0_ = { | |
1152 | 722 | ws_.data(), | |
1153 | 722 | fb_.capacity() - h_.size, | |
1154 | overread }; | ||
1155 | 722 | body_avail_ = 0; | |
1156 | 722 | body_total_ = 0; | |
1157 | 722 | body_buf_ = &cb0_; | |
1158 | 722 | st_ = state::complete; | |
1159 | 722 | return; | |
1160 | } | ||
1161 | |||
1162 | // calculate filter | ||
1163 | 485 | filt_ = nullptr; // VFALCO TODO | |
1164 | |||
1165 |
1/2✓ Branch 1 taken 485 times.
✗ Branch 2 not taken.
|
485 | if(is_plain()) |
1166 | { | ||
1167 | // plain payload | ||
1168 | |||
1169 |
2/2✓ Branch 0 taken 250 times.
✓ Branch 1 taken 235 times.
|
485 | if(h_.md.payload == payload::size) |
1170 | { | ||
1171 | 250 | if(h_.md.payload_size > | |
1172 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 250 times.
|
250 | svc_.cfg.body_limit) |
1173 | { | ||
1174 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
1175 | error::body_too_large); | ||
1176 | ✗ | st_ = state::reset; // unrecoverable | |
1177 | ✗ | return; | |
1178 | } | ||
1179 | auto n0 = | ||
1180 | 250 | fb_.capacity() - h_.size + | |
1181 | 250 | svc_.cfg.min_buffer + | |
1182 | 250 | svc_.max_codec; | |
1183 | // limit the capacity of cb0_ so | ||
1184 | // that going over max_overread | ||
1185 | // is impossible. | ||
1186 |
6/6✓ Branch 0 taken 249 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 235 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 236 times.
|
499 | if( n0 > h_.md.payload_size && |
1187 | 249 | n0 - h_.md.payload_size >= | |
1188 | 249 | svc_.max_overread()) | |
1189 | 14 | n0 = h_.md.payload_size + | |
1190 | 14 | svc_.max_overread(); | |
1191 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 250 times.
|
250 | BOOST_ASSERT(n0 <= ws_.size()); |
1192 | 250 | cb0_ = { ws_.data(), n0, overread }; | |
1193 | 250 | body_buf_ = &cb0_; | |
1194 | 250 | body_avail_ = cb0_.size(); | |
1195 |
2/2✓ Branch 0 taken 225 times.
✓ Branch 1 taken 25 times.
|
250 | if( body_avail_ >= h_.md.payload_size) |
1196 | 225 | body_avail_ = h_.md.payload_size; | |
1197 | 250 | body_total_ = body_avail_; | |
1198 | 250 | payload_remain_ = | |
1199 | 250 | h_.md.payload_size - body_total_; | |
1200 | 250 | st_ = state::body; | |
1201 | 250 | return; | |
1202 | } | ||
1203 | |||
1204 | // overread is not applicable | ||
1205 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 235 times.
|
235 | BOOST_ASSERT( |
1206 | h_.md.payload == payload::to_eof); | ||
1207 | auto const n0 = | ||
1208 | 235 | fb_.capacity() - h_.size + | |
1209 | 235 | svc_.cfg.min_buffer + | |
1210 | 235 | svc_.max_codec; | |
1211 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 235 times.
|
235 | BOOST_ASSERT(n0 <= ws_.size()); |
1212 | 235 | cb0_ = { ws_.data(), n0, overread }; | |
1213 | 235 | body_buf_ = &cb0_; | |
1214 | 235 | body_avail_ = cb0_.size(); | |
1215 | 235 | body_total_ = body_avail_; | |
1216 | 235 | st_ = state::body; | |
1217 | 235 | return; | |
1218 | } | ||
1219 | |||
1220 | // buffered payload | ||
1221 | ✗ | auto const n0 = fb_.capacity() - h_.size; | |
1222 | ✗ | BOOST_ASSERT(n0 <= svc_.max_overread()); | |
1223 | ✗ | auto n1 = svc_.cfg.min_buffer; | |
1224 | ✗ | if(! filt_) | |
1225 | ✗ | n1 += svc_.max_codec; | |
1226 | ✗ | BOOST_ASSERT(n0 + n1 <= ws_.size()); | |
1227 | ✗ | cb0_ = { ws_.data(), n0, overread }; | |
1228 | ✗ | cb1_ = { ws_.data() + n0, n1 }; | |
1229 | ✗ | body_buf_ = &cb1_; | |
1230 | ✗ | body_avail_ = 0; | |
1231 | ✗ | body_total_ = 0; | |
1232 | ✗ | st_ = state::body; | |
1233 | } | ||
1234 | |||
1235 | // Called at the end of set_body | ||
1236 | void | ||
1237 | 299 | parser:: | |
1238 | on_set_body() | ||
1239 | { | ||
1240 | // This function is called after all | ||
1241 | // limit checking and calculation of | ||
1242 | // chunked or filter. | ||
1243 | |||
1244 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 299 times.
|
299 | BOOST_ASSERT(got_header()); |
1245 | |||
1246 | 299 | nprepare_ = 0; // invalidate | |
1247 | |||
1248 |
1/2✓ Branch 0 taken 299 times.
✗ Branch 1 not taken.
|
299 | if(how_ == how::dynamic) |
1249 | { | ||
1250 |
2/2✓ Branch 0 taken 58 times.
✓ Branch 1 taken 241 times.
|
299 | if(h_.md.payload == payload::none) |
1251 | { | ||
1252 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
|
58 | BOOST_ASSERT(st_ == state::complete); |
1253 | 58 | return; | |
1254 | } | ||
1255 | |||
1256 | 241 | st_ = state::set_body; | |
1257 | 241 | return; | |
1258 | } | ||
1259 | |||
1260 | ✗ | if(how_ == how::sink) | |
1261 | { | ||
1262 | ✗ | if(h_.md.payload == payload::none) | |
1263 | { | ||
1264 | ✗ | BOOST_ASSERT(st_ == state::complete); | |
1265 | // force a trip through parse so | ||
1266 | // we can calculate any error. | ||
1267 | ✗ | st_ = state::set_body; | |
1268 | ✗ | return; | |
1269 | } | ||
1270 | |||
1271 | ✗ | st_ = state::set_body; | |
1272 | ✗ | return; | |
1273 | } | ||
1274 | |||
1275 | // VFALCO TODO | ||
1276 | ✗ | detail::throw_logic_error(); | |
1277 | } | ||
1278 | |||
1279 | void | ||
1280 | 238 | parser:: | |
1281 | init_dynamic( | ||
1282 | system::error_code& ec) | ||
1283 | { | ||
1284 | // attempt to transfer in-place | ||
1285 | // body into the dynamic buffer. | ||
1286 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 238 times.
|
238 | BOOST_ASSERT( |
1287 | body_avail_ == body_buf_->size()); | ||
1288 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 238 times.
|
238 | BOOST_ASSERT( |
1289 | body_total_ == body_avail_); | ||
1290 | auto const space_left = | ||
1291 | 238 | dyn_->max_size() - dyn_->size(); | |
1292 | |||
1293 |
2/2✓ Branch 0 taken 121 times.
✓ Branch 1 taken 117 times.
|
238 | if(h_.md.payload == payload::size) |
1294 | { | ||
1295 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 120 times.
|
121 | if(space_left < h_.md.payload_size) |
1296 | { | ||
1297 | 2 | ec = BOOST_HTTP_PROTO_ERR( | |
1298 | error::buffer_overflow); | ||
1299 | 1 | return; | |
1300 | } | ||
1301 | // reserve the full size | ||
1302 | 120 | dyn_->prepare(h_.md.payload_size); | |
1303 | // transfer in-place body | ||
1304 | 120 | auto n = body_avail_; | |
1305 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
|
120 | if( n > h_.md.payload_size) |
1306 | ✗ | n = h_.md.payload_size; | |
1307 |
1/2✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
|
120 | dyn_->commit( |
1308 | buffers::buffer_copy( | ||
1309 |
1/2✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
|
120 | dyn_->prepare(n), |
1310 | 120 | body_buf_->data())); | |
1311 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
|
120 | BOOST_ASSERT(body_avail_ == n); |
1312 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
|
120 | BOOST_ASSERT(body_total_ == n); |
1313 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
|
120 | BOOST_ASSERT(payload_remain_ == |
1314 | h_.md.payload_size - n); | ||
1315 | 120 | body_buf_->consume(n); | |
1316 | 120 | body_avail_ = 0; | |
1317 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 111 times.
|
120 | if(n < h_.md.payload_size) |
1318 | { | ||
1319 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
|
9 | BOOST_ASSERT( |
1320 | body_buf_->size() == 0); | ||
1321 | 9 | st_ = state::body; | |
1322 | 9 | return; | |
1323 | } | ||
1324 | // complete | ||
1325 | 111 | st_ = state::complete; | |
1326 | 111 | return; | |
1327 | } | ||
1328 | |||
1329 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
|
117 | BOOST_ASSERT(h_.md.payload == |
1330 | payload::to_eof); | ||
1331 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
|
117 | if(space_left < body_avail_) |
1332 | { | ||
1333 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
1334 | error::buffer_overflow); | ||
1335 | ✗ | return; | |
1336 | } | ||
1337 |
1/2✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
|
117 | dyn_->commit( |
1338 | buffers::buffer_copy( | ||
1339 |
1/2✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
|
117 | dyn_->prepare(body_avail_), |
1340 | 117 | body_buf_->data())); | |
1341 | 117 | body_buf_->consume(body_avail_); | |
1342 | 117 | body_avail_ = 0; | |
1343 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
|
117 | BOOST_ASSERT( |
1344 | body_buf_->size() == 0); | ||
1345 | 117 | st_ = state::body; | |
1346 | } | ||
1347 | |||
1348 | } // http_proto | ||
1349 | } // boost | ||
1350 | |||
1351 | #endif | ||
1352 |