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/buffers/buffer_copy.hpp> | ||
18 | #include <boost/url/grammar/ci_string.hpp> | ||
19 | #include <boost/assert.hpp> | ||
20 | #include <boost/none.hpp> | ||
21 | #include <memory> | ||
22 | |||
23 | namespace boost { | ||
24 | namespace http_proto { | ||
25 | |||
26 | /* | ||
27 | Parser design: | ||
28 | |||
29 | The usage of the parser is thus: | ||
30 | |||
31 | pr.reset(); // prepare for a new stream | ||
32 | |||
33 | pr.start(); // prepare for a new message | ||
34 | pr.start_head_response(); // new message with no payload | ||
35 | |||
36 | read_header( ..., pr ); | ||
37 | do | ||
38 | { | ||
39 | read_some(..., pr ); | ||
40 | } | ||
41 | while(! got_header()); | ||
42 | |||
43 | pr.set_body( ... ); | ||
44 | // invalidates the headers? yes. | ||
45 | |||
46 | read_body( ..., pr ); | ||
47 | while(! (pr.flags() & | ||
48 | parser::is_done_bit ) ); | ||
49 | { | ||
50 | read_some(..., pr ); | ||
51 | } | ||
52 | |||
53 | If these are called out of order, an | ||
54 | exception is thrown. | ||
55 | |||
56 | Every call to `prepare` must be | ||
57 | followed by a call to commit, reset, | ||
58 | or the destructor. | ||
59 | */ | ||
60 | //------------------------------------------------ | ||
61 | |||
62 | 745 | parser:: | |
63 | parser( | ||
64 | detail::kind k, | ||
65 | 745 | config_base const& cfg) | |
66 | : cfg_(cfg) | ||
67 | 745 | , h_(detail::empty{k}) | |
68 | { | ||
69 | 745 | } | |
70 | |||
71 | void | ||
72 | 745 | parser:: | |
73 | construct( | ||
74 | std::size_t extra_buffer_size) | ||
75 | { | ||
76 | // headers_limit too large | ||
77 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 745 times.
|
745 | if( cfg_.headers_limit > |
78 | BOOST_HTTP_PROTO_MAX_HEADER) | ||
79 | ✗ | detail::throw_invalid_argument(); | |
80 | |||
81 | // start_line_limit too large | ||
82 | 745 | if( cfg_.start_line_limit >= | |
83 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 745 times.
|
745 | cfg_.headers_limit) |
84 | ✗ | detail::throw_invalid_argument(); | |
85 | |||
86 | // field_size_limit too large | ||
87 | 745 | if( cfg_.field_size_limit >= | |
88 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 745 times.
|
745 | cfg_.headers_limit) |
89 | ✗ | detail::throw_invalid_argument(); | |
90 | |||
91 | // fields_limit too large | ||
92 | 745 | if( cfg_.fields_limit > | |
93 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 745 times.
|
745 | cfg_.headers_limit / 4) |
94 | ✗ | detail::throw_invalid_argument(); | |
95 | |||
96 | // largest space needed | ||
97 | auto const bytes_needed = | ||
98 | 745 | detail::header::bytes_needed( | |
99 | cfg_.headers_limit, | ||
100 | cfg_.fields_limit); | ||
101 | |||
102 | // prevent overflow | ||
103 | 745 | if(extra_buffer_size > | |
104 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 745 times.
|
745 | std::size_t(-1) - bytes_needed) |
105 | ✗ | detail::throw_invalid_argument(); | |
106 | |||
107 | // allocate max headers plus extra | ||
108 | 745 | ws_.allocate( | |
109 | bytes_needed + | ||
110 | extra_buffer_size); | ||
111 | |||
112 | 745 | h_.cap = bytes_needed; | |
113 | |||
114 | 745 | reset(); | |
115 | 745 | } | |
116 | |||
117 | //------------------------------------------------ | ||
118 | // | ||
119 | // Special Members | ||
120 | // | ||
121 | //------------------------------------------------ | ||
122 | |||
123 | 745 | parser:: | |
124 |
3/4✓ Branch 0 taken 745 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2235 times.
✓ Branch 3 taken 745 times.
|
2980 | ~parser() |
125 | { | ||
126 | 745 | } | |
127 | |||
128 | parser:: | ||
129 | parser( | ||
130 | parser&&) noexcept = default; | ||
131 | |||
132 | //------------------------------------------------ | ||
133 | // | ||
134 | // Observers | ||
135 | // | ||
136 | //------------------------------------------------ | ||
137 | |||
138 | string_view | ||
139 | ✗ | parser:: | |
140 | body() const noexcept | ||
141 | { | ||
142 | #if 0 | ||
143 | // VFALCO How about some | ||
144 | // asserts or exceptions? | ||
145 | if(! m_.got_chunked) | ||
146 | return string_view( | ||
147 | h_.buf + h_.size, | ||
148 | m_.n_payload); | ||
149 | return string_view( | ||
150 | h_.buf + | ||
151 | h_.size + | ||
152 | m_.n_chunk, | ||
153 | m_.n_payload); | ||
154 | #else | ||
155 | ✗ | return {}; | |
156 | #endif | ||
157 | } | ||
158 | |||
159 | //------------------------------------------------ | ||
160 | // | ||
161 | // Modifiers | ||
162 | // | ||
163 | //------------------------------------------------ | ||
164 | |||
165 | // prepare for a new stream | ||
166 | void | ||
167 | 745 | parser:: | |
168 | reset() noexcept | ||
169 | { | ||
170 | 745 | st_ = state::need_start; | |
171 | 745 | got_eof_ = false; | |
172 | 745 | } | |
173 | |||
174 | void | ||
175 | 770 | parser:: | |
176 | start_impl( | ||
177 | bool head_response) | ||
178 | { | ||
179 | 770 | std::size_t initial_size = 0; | |
180 |
2/4✓ Branch 0 taken 737 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
|
770 | switch(st_) |
181 | { | ||
182 | 737 | default: | |
183 | case state::need_start: | ||
184 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 737 times.
|
737 | BOOST_ASSERT(h_.size == 0); |
185 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 737 times.
|
737 | BOOST_ASSERT(h_buf_.size() == 0); |
186 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 737 times.
|
737 | BOOST_ASSERT(! got_eof_); |
187 | 737 | break; | |
188 | |||
189 | ✗ | case state::headers: | |
190 | // Can't call start twice. | ||
191 | ✗ | detail::throw_logic_error(); | |
192 | |||
193 | ✗ | case state::headers_done: | |
194 | case state::body: | ||
195 | // Can't call start with | ||
196 | // an incomplete message. | ||
197 | ✗ | detail::throw_logic_error(); | |
198 | |||
199 | 33 | case state::complete: | |
200 |
1/2✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
|
33 | if(h_buf_.size() > 0) |
201 | { | ||
202 | // headers with no body | ||
203 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
|
33 | BOOST_ASSERT(h_.size > 0); |
204 | 33 | h_buf_.consume(h_.size); | |
205 | 33 | initial_size = h_buf_.size(); | |
206 | // move unused octets to front | ||
207 | 33 | buffers::buffer_copy( | |
208 | 33 | buffers::mutable_buffer( | |
209 | ws_.data(), | ||
210 | initial_size), | ||
211 | 66 | h_buf_.data()); | |
212 | } | ||
213 | else | ||
214 | { | ||
215 | // leftover data after body | ||
216 | } | ||
217 | 33 | break; | |
218 | } | ||
219 | |||
220 | 770 | ws_.clear(); | |
221 | |||
222 | // set up header read buffer | ||
223 | 770 | h_buf_ = { | |
224 | ws_.data(), | ||
225 | cfg_.headers_limit, | ||
226 | initial_size }; | ||
227 | |||
228 | // reset the header but | ||
229 | // preserve the capacity | ||
230 | 770 | auto const cap = h_.cap; | |
231 | 1540 | h_ = detail::header( | |
232 | 770 | detail::empty{h_.kind}); | |
233 | 770 | h_.buf = reinterpret_cast< | |
234 | 770 | char*>(ws_.data()); | |
235 | 770 | h_.cbuf = h_.buf; | |
236 | 770 | h_.cap = cap; | |
237 | |||
238 | 770 | cfg_impl_ = {}; | |
239 | 770 | cfg_impl_.headers_limit = cfg_.headers_limit; | |
240 | 770 | cfg_impl_.start_line_limit = cfg_.start_line_limit; | |
241 | 770 | cfg_impl_.field_size_limit = cfg_.field_size_limit; | |
242 | 770 | cfg_impl_.fields_limit = cfg_.fields_limit; | |
243 | |||
244 | 770 | st_ = state::headers; | |
245 | |||
246 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 769 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
770 | BOOST_ASSERT(! head_response || |
247 | h_.kind == detail::kind::response); | ||
248 | 770 | head_response_ = head_response; | |
249 | 770 | } | |
250 | |||
251 | auto | ||
252 | 3185 | parser:: | |
253 | prepare() -> | ||
254 | mutable_buffers_type | ||
255 | { | ||
256 |
1/5✗ Branch 0 not taken.
✓ Branch 1 taken 3185 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
3185 | switch(st_) |
257 | { | ||
258 | ✗ | default: | |
259 | case state::need_start: | ||
260 | // start must be called once | ||
261 | // before calling prepare. | ||
262 | ✗ | detail::throw_logic_error(); | |
263 | |||
264 | 3185 | case state::headers: | |
265 | // fill up to headers_limit | ||
266 | return { | ||
267 | 3185 | h_buf_.prepare( | |
268 | 3185 | cfg_.headers_limit - | |
269 | 3185 | h_buf_.size()), | |
270 | 3185 | buffers::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 | 3185 | 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 3185 times.
|
3185 | if(got_eof_) |
305 | ✗ | detail::throw_logic_error(); | |
306 | |||
307 |
1/2✓ Branch 0 taken 3185 times.
✗ Branch 1 not taken.
|
3185 | switch(st_) |
308 | { | ||
309 | 3185 | default: | |
310 | case state::need_start: | ||
311 | case state::headers: | ||
312 | 3185 | h_buf_.commit(n); | |
313 | 3185 | break; | |
314 | |||
315 | ✗ | case state::headers_done: | |
316 | case state::body: | ||
317 | case state::complete: | ||
318 | ✗ | break; | |
319 | } | ||
320 | 3185 | } | |
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 | 3185 | parser:: | |
351 | parse( | ||
352 | error_code& ec) | ||
353 | { | ||
354 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 3185 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
3185 | 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 | 3185 | case state::headers: | |
363 | { | ||
364 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3185 times.
|
3185 | BOOST_ASSERT(h_.buf == ws_.data()); |
365 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3185 times.
|
3185 | BOOST_ASSERT(h_.cbuf == ws_.data()); |
366 | 3185 | auto const new_size = h_buf_.size(); | |
367 | 3185 | h_.parse(cfg_impl_, new_size, ec); | |
368 |
2/2✓ Branch 1 taken 584 times.
✓ Branch 2 taken 2601 times.
|
3185 | if(! ec.failed()) |
369 | { | ||
370 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 581 times.
|
584 | if( h_.md.payload != payload::none && |
371 |
1/2✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
|
3 | ! head_response_) |
372 | { | ||
373 | // Deliver headers to caller | ||
374 | 3 | st_ = state::headers_done; | |
375 | 3 | break; | |
376 | } | ||
377 | // no payload | ||
378 | 581 | st_ = state::complete; | |
379 | 581 | break; | |
380 | } | ||
381 |
3/4✓ Branch 2 taken 2473 times.
✓ Branch 3 taken 128 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2601 times.
|
5074 | if( ec == grammar::error::need_more && |
382 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2473 times.
|
2473 | 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 | 2601 | 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 | 37 | parser:: | |
449 | safe_get_header() const -> | ||
450 | detail::header const* | ||
451 | { | ||
452 |
2/4✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 34 times.
|
37 | 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 | 34 | case state::complete: | |
468 | // VFALCO Could be OK | ||
469 | 34 | break; | |
470 | } | ||
471 | 37 | 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 |