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