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/error.hpp> | ||
14 | #include <boost/http_proto/parser.hpp> | ||
15 | #include <boost/http_proto/detail/codec.hpp> | ||
16 | #include <boost/http_proto/detail/except.hpp> | ||
17 | #include <boost/url/grammar/ci_string.hpp> | ||
18 | #include <boost/assert.hpp> | ||
19 | #include <boost/none.hpp> | ||
20 | #include <memory> | ||
21 | |||
22 | namespace boost { | ||
23 | namespace http_proto { | ||
24 | |||
25 | /* | ||
26 | Parser design: | ||
27 | |||
28 | The usage of the parser is thus: | ||
29 | |||
30 | pr.reset(); // prepare for a new stream | ||
31 | |||
32 | pr.start(); // prepare for a new message | ||
33 | pr.start_head_response(); // new message with no payload | ||
34 | |||
35 | read_header( ..., pr ); | ||
36 | do | ||
37 | { | ||
38 | read_some(..., pr ); | ||
39 | } | ||
40 | while(! got_header()); | ||
41 | |||
42 | pr.set_body( ... ); | ||
43 | // invalidates the headers? yes. | ||
44 | |||
45 | read_body( ..., pr ); | ||
46 | while(! (pr.flags() & | ||
47 | parser::is_done_bit ) ); | ||
48 | { | ||
49 | read_some(..., pr ); | ||
50 | } | ||
51 | |||
52 | If these are called out of order, an | ||
53 | exception is thrown. | ||
54 | |||
55 | Every call to `prepare` must be | ||
56 | followed by a call to commit, reset, | ||
57 | or the destructor. | ||
58 | */ | ||
59 | //------------------------------------------------ | ||
60 | |||
61 | 744 | parser:: | |
62 | parser( | ||
63 | detail::kind k, | ||
64 | 744 | config_base const& cfg) | |
65 | : cfg_(cfg) | ||
66 | 744 | , h_(detail::empty{k}) | |
67 | { | ||
68 | 744 | } | |
69 | |||
70 | void | ||
71 | 744 | parser:: | |
72 | construct( | ||
73 | std::size_t extra_buffer_size) | ||
74 | { | ||
75 | // headers_limit too large | ||
76 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | if( cfg_.headers_limit > |
77 | BOOST_HTTP_PROTO_MAX_HEADER) | ||
78 | ✗ | detail::throw_invalid_argument(); | |
79 | |||
80 | // start_line_limit too large | ||
81 | 744 | if( cfg_.start_line_limit >= | |
82 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | cfg_.headers_limit) |
83 | ✗ | detail::throw_invalid_argument(); | |
84 | |||
85 | // field_size_limit too large | ||
86 | 744 | if( cfg_.field_size_limit >= | |
87 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | cfg_.headers_limit) |
88 | ✗ | detail::throw_invalid_argument(); | |
89 | |||
90 | // fields_limit too large | ||
91 | 744 | if( cfg_.fields_limit > | |
92 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | cfg_.headers_limit / 4) |
93 | ✗ | detail::throw_invalid_argument(); | |
94 | |||
95 | // largest space needed | ||
96 | auto const bytes_needed = | ||
97 | 744 | detail::header::bytes_needed( | |
98 | cfg_.headers_limit, | ||
99 | cfg_.fields_limit); | ||
100 | |||
101 | // prevent overflow | ||
102 | 744 | if(extra_buffer_size > | |
103 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
|
744 | std::size_t(-1) - bytes_needed) |
104 | ✗ | detail::throw_invalid_argument(); | |
105 | |||
106 | // allocate max headers plus extra | ||
107 | 1488 | ws_ = detail::workspace( | |
108 | bytes_needed + | ||
109 | 744 | extra_buffer_size); | |
110 | |||
111 | 744 | h_.cap = bytes_needed; | |
112 | |||
113 | 744 | reset(); | |
114 | 744 | } | |
115 | |||
116 | //------------------------------------------------ | ||
117 | // | ||
118 | // Special Members | ||
119 | // | ||
120 | //------------------------------------------------ | ||
121 | |||
122 | 744 | parser:: | |
123 |
3/4✓ Branch 0 taken 744 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2232 times.
✓ Branch 3 taken 744 times.
|
2976 | ~parser() |
124 | { | ||
125 | 744 | } | |
126 | |||
127 | parser:: | ||
128 | parser( | ||
129 | parser&&) noexcept = default; | ||
130 | |||
131 | //------------------------------------------------ | ||
132 | // | ||
133 | // Observers | ||
134 | // | ||
135 | //------------------------------------------------ | ||
136 | |||
137 | string_view | ||
138 | ✗ | parser:: | |
139 | body() const noexcept | ||
140 | { | ||
141 | #if 0 | ||
142 | // VFALCO How about some | ||
143 | // asserts or exceptions? | ||
144 | if(! m_.got_chunked) | ||
145 | return string_view( | ||
146 | h_.buf + h_.size, | ||
147 | m_.n_payload); | ||
148 | return string_view( | ||
149 | h_.buf + | ||
150 | h_.size + | ||
151 | m_.n_chunk, | ||
152 | m_.n_payload); | ||
153 | #else | ||
154 | ✗ | return {}; | |
155 | #endif | ||
156 | } | ||
157 | |||
158 | //------------------------------------------------ | ||
159 | // | ||
160 | // Modifiers | ||
161 | // | ||
162 | //------------------------------------------------ | ||
163 | |||
164 | // prepare for a new stream | ||
165 | void | ||
166 | 744 | parser:: | |
167 | reset() noexcept | ||
168 | { | ||
169 | 744 | st_ = state::need_start; | |
170 | 744 | got_eof_ = false; | |
171 | 744 | } | |
172 | |||
173 | void | ||
174 | 736 | parser:: | |
175 | start_impl( | ||
176 | bool head_response) | ||
177 | { | ||
178 | 736 | std::size_t initial_size = 0; | |
179 |
1/4✓ Branch 0 taken 736 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
736 | switch(st_) |
180 | { | ||
181 | 736 | default: | |
182 | case state::need_start: | ||
183 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 736 times.
|
736 | BOOST_ASSERT(h_.size == 0); |
184 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 736 times.
|
736 | BOOST_ASSERT(h_buf_.size() == 0); |
185 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 736 times.
|
736 | BOOST_ASSERT(! got_eof_); |
186 | 736 | break; | |
187 | |||
188 | ✗ | case state::headers: | |
189 | // Can't call start twice. | ||
190 | ✗ | detail::throw_logic_error(); | |
191 | |||
192 | ✗ | case state::headers_done: | |
193 | case state::body: | ||
194 | // Can't call start with | ||
195 | // an incomplete message. | ||
196 | ✗ | detail::throw_logic_error(); | |
197 | |||
198 | ✗ | case state::complete: | |
199 | ✗ | if(h_buf_.size() > 0) | |
200 | { | ||
201 | // headers with no body | ||
202 | ✗ | BOOST_ASSERT(h_.size > 0); | |
203 | ✗ | h_buf_.consume(h_.size); | |
204 | ✗ | initial_size = h_buf_.size(); | |
205 | // move unused octets to front | ||
206 | ✗ | buffer_copy( | |
207 | ✗ | mutable_buffer( | |
208 | ws_.data(), | ||
209 | initial_size), | ||
210 | ✗ | h_buf_.data()); | |
211 | } | ||
212 | else | ||
213 | { | ||
214 | // leftover data after body | ||
215 | } | ||
216 | ✗ | break; | |
217 | } | ||
218 | |||
219 | 736 | ws_.clear(); | |
220 | |||
221 | // set up header read buffer | ||
222 | 736 | h_buf_ = { | |
223 | ws_.data(), | ||
224 | cfg_.headers_limit, | ||
225 | initial_size }; | ||
226 | |||
227 | // reset the header but | ||
228 | // preserve the capacity | ||
229 | 736 | auto const cap = h_.cap; | |
230 | 1472 | h_ = detail::header( | |
231 | 736 | detail::empty{h_.kind}); | |
232 | 736 | h_.buf = reinterpret_cast< | |
233 | 736 | char*>(ws_.data()); | |
234 | 736 | h_.cbuf = h_.buf; | |
235 | 736 | h_.cap = cap; | |
236 | |||
237 | 736 | cfg_impl_ = {}; | |
238 | 736 | cfg_impl_.headers_limit = cfg_.headers_limit; | |
239 | 736 | cfg_impl_.start_line_limit = cfg_.start_line_limit; | |
240 | 736 | cfg_impl_.field_size_limit = cfg_.field_size_limit; | |
241 | 736 | cfg_impl_.fields_limit = cfg_.fields_limit; | |
242 | |||
243 | 736 | st_ = state::headers; | |
244 | |||
245 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 735 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
736 | BOOST_ASSERT(! head_response || |
246 | h_.kind == detail::kind::response); | ||
247 | 736 | head_response_ = head_response; | |
248 | 736 | } | |
249 | |||
250 | auto | ||
251 | 3021 | parser:: | |
252 | prepare() -> | ||
253 | buffers | ||
254 | { | ||
255 |
1/5✗ Branch 0 not taken.
✓ Branch 1 taken 3021 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
3021 | switch(st_) |
256 | { | ||
257 | ✗ | default: | |
258 | case state::need_start: | ||
259 | // start must be called once | ||
260 | // before calling prepare. | ||
261 | ✗ | if(st_ == state::need_start) | |
262 | ✗ | detail::throw_logic_error(); | |
263 | |||
264 | case state::headers: | ||
265 | // fill up to headers_limit | ||
266 | return { | ||
267 | h_buf_.prepare( | ||
268 | 3021 | cfg_.headers_limit - | |
269 | 3021 | h_buf_.size()), | |
270 | 3021 | mutable_buffer{} }; | |
271 | |||
272 | ✗ | case state::headers_done: | |
273 | { | ||
274 | // discard headers and move | ||
275 | // any leftover stream data. | ||
276 | ✗ | std::memmove( | |
277 | ws_.data(), | ||
278 | ✗ | h_.cbuf + h_.size, | |
279 | ✗ | h_buf_.size() - h_.size); | |
280 | ✗ | st_ = state::body; | |
281 | // VFALCO set up body buffer | ||
282 | BOOST_FALLTHROUGH; | ||
283 | } | ||
284 | |||
285 | ✗ | case state::body: | |
286 | { | ||
287 | ✗ | return {}; | |
288 | } | ||
289 | |||
290 | ✗ | case state::complete: | |
291 | // Can't call `prepare` again after | ||
292 | // a complete message is parsed, | ||
293 | // call `start` first. | ||
294 | ✗ | detail::throw_logic_error(); | |
295 | } | ||
296 | } | ||
297 | |||
298 | void | ||
299 | 3021 | parser:: | |
300 | commit( | ||
301 | std::size_t n) | ||
302 | { | ||
303 | // Can't commit after eof | ||
304 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3021 times.
|
3021 | if(got_eof_) |
305 | ✗ | detail::throw_logic_error(); | |
306 | |||
307 |
1/2✓ Branch 0 taken 3021 times.
✗ Branch 1 not taken.
|
3021 | switch(st_) |
308 | { | ||
309 | 3021 | default: | |
310 | case state::need_start: | ||
311 | case state::headers: | ||
312 | 3021 | h_buf_.commit(n); | |
313 | 3021 | break; | |
314 | |||
315 | ✗ | case state::headers_done: | |
316 | case state::body: | ||
317 | case state::complete: | ||
318 | ✗ | break; | |
319 | } | ||
320 | 3021 | } | |
321 | |||
322 | void | ||
323 | ✗ | parser:: | |
324 | commit_eof() | ||
325 | { | ||
326 | ✗ | switch(st_) | |
327 | { | ||
328 | ✗ | default: | |
329 | case state::need_start: | ||
330 | // Can't commit eof | ||
331 | // before calling start. | ||
332 | ✗ | detail::throw_logic_error(); | |
333 | |||
334 | ✗ | case state::headers: | |
335 | case state::headers_done: | ||
336 | case state::body: | ||
337 | ✗ | got_eof_ = true; | |
338 | ✗ | break; | |
339 | |||
340 | ✗ | case state::complete: | |
341 | // Can't commit eof when | ||
342 | // message is complete. | ||
343 | ✗ | detail::throw_logic_error(); | |
344 | } | ||
345 | } | ||
346 | |||
347 | // process input data then | ||
348 | // eof if input data runs out. | ||
349 | void | ||
350 | 3021 | parser:: | |
351 | parse( | ||
352 | error_code& ec) | ||
353 | { | ||
354 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3021 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3021 | switch(st_) |
355 | { | ||
356 | ✗ | default: | |
357 | case state::need_start: | ||
358 | // You must call start before | ||
359 | // calling parse on a new message. | ||
360 | ✗ | detail::throw_logic_error(); | |
361 | |||
362 | 3021 | case state::headers: | |
363 | { | ||
364 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3021 times.
|
3021 | BOOST_ASSERT(h_.buf == ws_.data()); |
365 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3021 times.
|
3021 | BOOST_ASSERT(h_.cbuf == ws_.data()); |
366 | 3021 | auto const new_size = h_buf_.size(); | |
367 | 3021 | h_.parse(cfg_impl_, new_size, ec); | |
368 |
2/2✓ Branch 1 taken 550 times.
✓ Branch 2 taken 2471 times.
|
3021 | if(! ec.failed()) |
369 | { | ||
370 |
2/2✓ Branch 0 taken 547 times.
✓ Branch 1 taken 3 times.
|
550 | if( h_.md.payload != payload::none || |
371 |
1/2✓ Branch 0 taken 547 times.
✗ Branch 1 not taken.
|
547 | ! head_response_) |
372 | { | ||
373 | // Deliver headers to caller | ||
374 | 550 | st_ = state::headers_done; | |
375 | 550 | break; | |
376 | } | ||
377 | // no payload | ||
378 | ✗ | st_ = state::complete; | |
379 | ✗ | break; | |
380 | } | ||
381 |
3/4✓ Branch 2 taken 2343 times.
✓ Branch 3 taken 128 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2471 times.
|
4814 | if( ec == grammar::error::need_more && |
382 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2343 times.
|
2343 | got_eof_) |
383 | { | ||
384 | ✗ | if(h_.size > 0) | |
385 | { | ||
386 | // Connection closed before | ||
387 | // message is complete. | ||
388 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
389 | error::incomplete); | ||
390 | |||
391 | ✗ | return; | |
392 | } | ||
393 | |||
394 | // Connection closed | ||
395 | // cleanly. | ||
396 | ✗ | ec = BOOST_HTTP_PROTO_ERR( | |
397 | error::end_of_stream); | ||
398 | |||
399 | ✗ | return; | |
400 | } | ||
401 | 2471 | return; | |
402 | } | ||
403 | |||
404 | ✗ | case state::headers_done: | |
405 | { | ||
406 | // This is a no-op | ||
407 | ✗ | ec = {}; | |
408 | ✗ | break; | |
409 | } | ||
410 | |||
411 | ✗ | case state::body: | |
412 | { | ||
413 | ✗ | parse_body(ec); | |
414 | ✗ | if(ec.failed()) | |
415 | ✗ | return; | |
416 | ✗ | st_ = state::complete; | |
417 | ✗ | break; | |
418 | } | ||
419 | } | ||
420 | } | ||
421 | |||
422 | //------------------------------------------------ | ||
423 | |||
424 | string_view | ||
425 | ✗ | parser:: | |
426 | release_buffered_data() noexcept | ||
427 | { | ||
428 | ✗ | return {}; | |
429 | } | ||
430 | |||
431 | //------------------------------------------------ | ||
432 | // | ||
433 | // Implementation | ||
434 | // | ||
435 | //------------------------------------------------ | ||
436 | |||
437 | void | ||
438 | 2 | parser:: | |
439 | apply_param( | ||
440 | config_base const& cfg) noexcept | ||
441 | { | ||
442 | 2 | cfg_ = cfg; | |
443 | 2 | } | |
444 | |||
445 | //------------------------------------------------ | ||
446 | |||
447 | auto | ||
448 | 3 | parser:: | |
449 | safe_get_header() const -> | ||
450 | detail::header const* | ||
451 | { | ||
452 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3 | switch(st_) |
453 | { | ||
454 | ✗ | default: | |
455 | case state::need_start: | ||
456 | case state::headers: | ||
457 | // Headers not received yet | ||
458 | ✗ | detail::throw_logic_error(); | |
459 | |||
460 | 3 | case state::headers_done: | |
461 | 3 | break; | |
462 | |||
463 | ✗ | case state::body: | |
464 | // Headers received and discarded | ||
465 | ✗ | detail::throw_logic_error(); | |
466 | |||
467 | ✗ | case state::complete: | |
468 | // VFALCO Could be OK | ||
469 | ✗ | break; | |
470 | } | ||
471 | 3 | return &h_; | |
472 | } | ||
473 | |||
474 | void | ||
475 | ✗ | parser:: | |
476 | parse_body( | ||
477 | error_code& ec) | ||
478 | { | ||
479 | (void)ec; | ||
480 | ✗ | return; | |
481 | // VFALCO TODO | ||
482 | #if 0 | ||
483 | BOOST_ASSERT(st_ == state::body); | ||
484 | |||
485 | if(h_.kind == detail::kind::request) | ||
486 | { | ||
487 | // https://tools.ietf.org/html/rfc7230#section-3.3 | ||
488 | if(m_.skip_body) | ||
489 | return; | ||
490 | if(m_.content_len.has_value()) | ||
491 | { | ||
492 | if(*m_.content_len > cfg_.body_too_large) | ||
493 | { | ||
494 | ec = error::body_too_large; | ||
495 | return; | ||
496 | } | ||
497 | if(*m_.content_len == 0) | ||
498 | return; | ||
499 | } | ||
500 | else if(m_.got_chunked) | ||
501 | { | ||
502 | // VFALCO TODO | ||
503 | return; | ||
504 | } | ||
505 | else | ||
506 | { | ||
507 | // Content-Length: 0 | ||
508 | return; | ||
509 | } | ||
510 | } | ||
511 | else | ||
512 | { | ||
513 | BOOST_ASSERT(h_.kind == | ||
514 | detail::kind::response); | ||
515 | |||
516 | // https://tools.ietf.org/html/rfc7230#section-3.3 | ||
517 | if((h_.res.status_int / 100 == 1) || // 1xx e.g. Continue | ||
518 | h_.res.status_int == 204 || // No Content | ||
519 | h_.res.status_int == 304) // Not Modified | ||
520 | { | ||
521 | // Content-Length may be present, but we | ||
522 | // treat the message as not having a body. | ||
523 | } | ||
524 | else if(m_.content_len.has_value()) | ||
525 | { | ||
526 | if(*m_.content_len > 0) | ||
527 | { | ||
528 | if(*m_.content_len > cfg_.body_too_large) | ||
529 | { | ||
530 | ec = error::body_too_large; | ||
531 | return; | ||
532 | } | ||
533 | } | ||
534 | } | ||
535 | else | ||
536 | { | ||
537 | // No Content-Length | ||
538 | return; | ||
539 | } | ||
540 | } | ||
541 | |||
542 | auto avail = committed_ - size_; | ||
543 | if(m_.content_len.has_value()) | ||
544 | { | ||
545 | // known payload length | ||
546 | BOOST_ASSERT(! m_.got_chunked); | ||
547 | BOOST_ASSERT(m_.n_remain > 0); | ||
548 | BOOST_ASSERT(m_.content_len < | ||
549 | cfg_.body_too_large); | ||
550 | if(avail == 0) | ||
551 | { | ||
552 | if(! got_eof_) | ||
553 | { | ||
554 | ec = grammar::error::need_more; | ||
555 | return; | ||
556 | } | ||
557 | ec = error::need_more; | ||
558 | return; | ||
559 | } | ||
560 | if( avail > m_.n_remain) | ||
561 | avail = static_cast< | ||
562 | std::size_t>(m_.n_remain); | ||
563 | size_ += avail; | ||
564 | m_.payload_seen += avail; | ||
565 | m_.n_payload += avail; | ||
566 | m_.n_remain -= avail; | ||
567 | if(m_.n_remain > 0) | ||
568 | { | ||
569 | ec = {}; | ||
570 | return; | ||
571 | } | ||
572 | st_ = state::complete; | ||
573 | ec = error::end_of_message; | ||
574 | return; | ||
575 | } | ||
576 | |||
577 | if(! m_.got_chunked) | ||
578 | { | ||
579 | // end of body indicated by EOF | ||
580 | if(avail > 0) | ||
581 | { | ||
582 | if(avail > (std::size_t( | ||
583 | -1) - m_.n_payload)) | ||
584 | { | ||
585 | // overflow size_t | ||
586 | // VFALCO revisit this | ||
587 | ec = error::numeric_overflow; | ||
588 | return; | ||
589 | } | ||
590 | size_ += avail; | ||
591 | m_.n_payload += avail; | ||
592 | ec = {}; | ||
593 | return; | ||
594 | } | ||
595 | if(! got_eof_) | ||
596 | { | ||
597 | ec = grammar::error::need_more; | ||
598 | return; | ||
599 | } | ||
600 | st_ = state::complete; | ||
601 | ec = error::end_of_message; | ||
602 | return; | ||
603 | } | ||
604 | #if 0 | ||
605 | if(m_.payload_left == 0) | ||
606 | { | ||
607 | // start of chunk | ||
608 | bnf::chunk_part p; | ||
609 | auto it = p.parse( | ||
610 | h_.buf + size_, | ||
611 | h_.buf + ( | ||
612 | committed_ - size_), | ||
613 | ec); | ||
614 | if(ec) | ||
615 | return; | ||
616 | auto const v = | ||
617 | p.value(); | ||
618 | m_.chunk.size = v.size; | ||
619 | m_.chunk.ext = v.ext; | ||
620 | m_.chunk.trailer = v.trailer; | ||
621 | m_.chunk.fresh = true; | ||
622 | m_.payload_left = | ||
623 | v.size - v.data.size(); | ||
624 | } | ||
625 | else | ||
626 | { | ||
627 | // continuation of chunk | ||
628 | |||
629 | } | ||
630 | #endif | ||
631 | #endif | ||
632 | } | ||
633 | |||
634 | void | ||
635 | ✗ | parser:: | |
636 | parse_chunk( | ||
637 | error_code& ec) | ||
638 | { | ||
639 | (void)ec; | ||
640 | #if 0 | ||
641 | switch(st_) | ||
642 | { | ||
643 | case state::start_line_line: | ||
644 | case state::header_fields: | ||
645 | parse_header(ec); | ||
646 | if(ec.failed()) | ||
647 | return; | ||
648 | BOOST_ASSERT(st_ > | ||
649 | state::header_fields); | ||
650 | break; | ||
651 | case state::body: | ||
652 | if(! m_.got_chunked) | ||
653 | return parse_body(ec); | ||
654 | break; | ||
655 | case state::complete: | ||
656 | ec = error::end_of_message; | ||
657 | if(! got_eof_) | ||
658 | return; | ||
659 | st_ = state::end_of_stream; | ||
660 | return; | ||
661 | case state::end_of_stream: | ||
662 | ec = error::end_of_stream; | ||
663 | return; | ||
664 | } | ||
665 | |||
666 | auto const avail = committed_ - size_; | ||
667 | auto const start = h_.buf + size_; | ||
668 | if(m_.payload_left == 0) | ||
669 | { | ||
670 | // start of chunk | ||
671 | // VFALCO What about chunk_part_next? | ||
672 | BOOST_ASSERT( | ||
673 | size_ == m_.header_size); | ||
674 | bnf::chunk_part p; | ||
675 | auto it = p.parse(start, | ||
676 | h_.buf + ( | ||
677 | committed_ - size_), ec); | ||
678 | BOOST_ASSERT(it == start); | ||
679 | if(ec) | ||
680 | return; | ||
681 | auto const v = p.value(); | ||
682 | m_.chunk.size = v.size; | ||
683 | m_.chunk.ext = v.ext; | ||
684 | m_.chunk.fresh = true; | ||
685 | if(v.size > 0) | ||
686 | { | ||
687 | // chunk | ||
688 | m_.chunk.trailer = {}; | ||
689 | m_.payload_left = | ||
690 | v.size - v.data.size(); | ||
691 | size_ += it - start; // excludes CRLF | ||
692 | return; | ||
693 | } | ||
694 | // last-chunk | ||
695 | BOOST_ASSERT( | ||
696 | v.data.empty()); | ||
697 | m_.chunk.trailer = | ||
698 | v.trailer; | ||
699 | m_.body = {}; | ||
700 | size_ += it - start; // excludes CRLF | ||
701 | st_ = state::complete; | ||
702 | } | ||
703 | else | ||
704 | { | ||
705 | // continuation of chunk | ||
706 | |||
707 | } | ||
708 | #endif | ||
709 | } | ||
710 | |||
711 | } // http_proto | ||
712 | } // boost | ||
713 | |||
714 | #endif | ||
715 |