]>
Commit | Line | Data |
---|---|---|
b32b8144 | 1 | // |
92f5a8d4 | 2 | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com) |
b32b8144 FG |
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/boostorg/beast | |
8 | // | |
9 | ||
10 | #ifndef BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP | |
11 | #define BOOST_BEAST_HTTP_IMPL_BASIC_PARSER_IPP | |
12 | ||
92f5a8d4 | 13 | #include <boost/beast/http/basic_parser.hpp> |
b32b8144 FG |
14 | #include <boost/beast/http/error.hpp> |
15 | #include <boost/beast/http/rfc7230.hpp> | |
92f5a8d4 TL |
16 | #include <boost/beast/core/buffer_traits.hpp> |
17 | #include <boost/beast/core/detail/clamp.hpp> | |
18 | #include <boost/beast/core/detail/config.hpp> | |
19 | #include <boost/beast/core/detail/string.hpp> | |
b32b8144 | 20 | #include <boost/asio/buffer.hpp> |
b32b8144 FG |
21 | #include <algorithm> |
22 | #include <utility> | |
23 | ||
24 | namespace boost { | |
25 | namespace beast { | |
26 | namespace http { | |
27 | ||
92f5a8d4 | 28 | template<bool isRequest> |
b32b8144 | 29 | bool |
92f5a8d4 | 30 | basic_parser<isRequest>:: |
b32b8144 FG |
31 | keep_alive() const |
32 | { | |
33 | BOOST_ASSERT(is_header_done()); | |
34 | if(f_ & flagHTTP11) | |
35 | { | |
36 | if(f_ & flagConnectionClose) | |
37 | return false; | |
38 | } | |
39 | else | |
40 | { | |
41 | if(! (f_ & flagConnectionKeepAlive)) | |
42 | return false; | |
43 | } | |
44 | return (f_ & flagNeedEOF) == 0; | |
45 | } | |
46 | ||
92f5a8d4 | 47 | template<bool isRequest> |
b32b8144 | 48 | boost::optional<std::uint64_t> |
92f5a8d4 | 49 | basic_parser<isRequest>:: |
b32b8144 | 50 | content_length() const |
92f5a8d4 TL |
51 | { |
52 | BOOST_ASSERT(is_header_done()); | |
f67539c2 | 53 | return content_length_unchecked(); |
92f5a8d4 TL |
54 | } |
55 | ||
56 | template<bool isRequest> | |
57 | boost::optional<std::uint64_t> | |
58 | basic_parser<isRequest>:: | |
59 | content_length_remaining() const | |
b32b8144 FG |
60 | { |
61 | BOOST_ASSERT(is_header_done()); | |
62 | if(! (f_ & flagContentLength)) | |
63 | return boost::none; | |
64 | return len_; | |
65 | } | |
66 | ||
92f5a8d4 | 67 | template<bool isRequest> |
b32b8144 | 68 | void |
92f5a8d4 | 69 | basic_parser<isRequest>:: |
b32b8144 FG |
70 | skip(bool v) |
71 | { | |
72 | BOOST_ASSERT(! got_some()); | |
73 | if(v) | |
74 | f_ |= flagSkipBody; | |
75 | else | |
76 | f_ &= ~flagSkipBody; | |
77 | } | |
78 | ||
92f5a8d4 | 79 | template<bool isRequest> |
b32b8144 | 80 | std::size_t |
92f5a8d4 TL |
81 | basic_parser<isRequest>:: |
82 | put(net::const_buffer buffer, | |
b32b8144 FG |
83 | error_code& ec) |
84 | { | |
1e59de90 TL |
85 | // If this goes off you have tried to parse more data after the parser |
86 | // has completed. A common cause of this is re-using a parser, which is | |
87 | // not supported. If you need to re-use a parser, consider storing it | |
88 | // in an optional. Then reset() and emplace() prior to parsing each new | |
89 | // message. | |
90 | BOOST_ASSERT(!is_done()); | |
91 | if (is_done()) | |
92 | { | |
93 | ec = error::stale_parser; | |
94 | return 0; | |
95 | } | |
92f5a8d4 | 96 | auto p = static_cast<char const*>(buffer.data()); |
b32b8144 FG |
97 | auto n = buffer.size(); |
98 | auto const p0 = p; | |
99 | auto const p1 = p0 + n; | |
92f5a8d4 | 100 | ec = {}; |
b32b8144 FG |
101 | loop: |
102 | switch(state_) | |
103 | { | |
104 | case state::nothing_yet: | |
105 | if(n == 0) | |
106 | { | |
107 | ec = error::need_more; | |
108 | return 0; | |
109 | } | |
110 | state_ = state::start_line; | |
11fdf7f2 | 111 | BOOST_FALLTHROUGH; |
b32b8144 FG |
112 | |
113 | case state::start_line: | |
114 | { | |
115 | maybe_need_more(p, n, ec); | |
116 | if(ec) | |
117 | goto done; | |
118 | parse_start_line(p, p + (std::min<std::size_t>)( | |
119 | header_limit_, n), ec, is_request{}); | |
120 | if(ec) | |
121 | { | |
122 | if(ec == error::need_more) | |
123 | { | |
124 | if(n >= header_limit_) | |
125 | { | |
126 | ec = error::header_limit; | |
127 | goto done; | |
128 | } | |
129 | if(p + 3 <= p1) | |
130 | skip_ = static_cast< | |
131 | std::size_t>(p1 - p - 3); | |
132 | } | |
133 | goto done; | |
134 | } | |
135 | BOOST_ASSERT(! is_done()); | |
136 | n = static_cast<std::size_t>(p1 - p); | |
137 | if(p >= p1) | |
138 | { | |
139 | ec = error::need_more; | |
140 | goto done; | |
141 | } | |
11fdf7f2 | 142 | BOOST_FALLTHROUGH; |
b32b8144 FG |
143 | } |
144 | ||
145 | case state::fields: | |
146 | maybe_need_more(p, n, ec); | |
147 | if(ec) | |
148 | goto done; | |
149 | parse_fields(p, p + (std::min<std::size_t>)( | |
150 | header_limit_, n), ec); | |
151 | if(ec) | |
152 | { | |
153 | if(ec == error::need_more) | |
154 | { | |
155 | if(n >= header_limit_) | |
156 | { | |
157 | ec = error::header_limit; | |
158 | goto done; | |
159 | } | |
160 | if(p + 3 <= p1) | |
161 | skip_ = static_cast< | |
162 | std::size_t>(p1 - p - 3); | |
163 | } | |
164 | goto done; | |
165 | } | |
166 | finish_header(ec, is_request{}); | |
1e59de90 TL |
167 | if(ec) |
168 | goto done; | |
b32b8144 FG |
169 | break; |
170 | ||
171 | case state::body0: | |
172 | BOOST_ASSERT(! skip_); | |
92f5a8d4 | 173 | this->on_body_init_impl(content_length(), ec); |
b32b8144 FG |
174 | if(ec) |
175 | goto done; | |
176 | state_ = state::body; | |
11fdf7f2 | 177 | BOOST_FALLTHROUGH; |
b32b8144 FG |
178 | |
179 | case state::body: | |
180 | BOOST_ASSERT(! skip_); | |
181 | parse_body(p, n, ec); | |
182 | if(ec) | |
183 | goto done; | |
184 | break; | |
185 | ||
186 | case state::body_to_eof0: | |
187 | BOOST_ASSERT(! skip_); | |
92f5a8d4 | 188 | this->on_body_init_impl(content_length(), ec); |
b32b8144 FG |
189 | if(ec) |
190 | goto done; | |
191 | state_ = state::body_to_eof; | |
11fdf7f2 | 192 | BOOST_FALLTHROUGH; |
b32b8144 FG |
193 | |
194 | case state::body_to_eof: | |
195 | BOOST_ASSERT(! skip_); | |
196 | parse_body_to_eof(p, n, ec); | |
197 | if(ec) | |
198 | goto done; | |
199 | break; | |
200 | ||
201 | case state::chunk_header0: | |
92f5a8d4 | 202 | this->on_body_init_impl(content_length(), ec); |
b32b8144 FG |
203 | if(ec) |
204 | goto done; | |
205 | state_ = state::chunk_header; | |
11fdf7f2 | 206 | BOOST_FALLTHROUGH; |
b32b8144 FG |
207 | |
208 | case state::chunk_header: | |
209 | parse_chunk_header(p, n, ec); | |
210 | if(ec) | |
211 | goto done; | |
212 | break; | |
213 | ||
214 | case state::chunk_body: | |
215 | parse_chunk_body(p, n, ec); | |
216 | if(ec) | |
217 | goto done; | |
218 | break; | |
219 | ||
220 | case state::complete: | |
92f5a8d4 | 221 | ec = {}; |
b32b8144 FG |
222 | goto done; |
223 | } | |
224 | if(p < p1 && ! is_done() && eager()) | |
225 | { | |
226 | n = static_cast<std::size_t>(p1 - p); | |
227 | goto loop; | |
228 | } | |
229 | done: | |
230 | return static_cast<std::size_t>(p - p0); | |
231 | } | |
232 | ||
92f5a8d4 | 233 | template<bool isRequest> |
b32b8144 | 234 | void |
92f5a8d4 | 235 | basic_parser<isRequest>:: |
b32b8144 FG |
236 | put_eof(error_code& ec) |
237 | { | |
238 | BOOST_ASSERT(got_some()); | |
239 | if( state_ == state::start_line || | |
240 | state_ == state::fields) | |
241 | { | |
242 | ec = error::partial_message; | |
243 | return; | |
244 | } | |
245 | if(f_ & (flagContentLength | flagChunked)) | |
246 | { | |
247 | if(state_ != state::complete) | |
248 | { | |
249 | ec = error::partial_message; | |
250 | return; | |
251 | } | |
92f5a8d4 | 252 | ec = {}; |
b32b8144 FG |
253 | return; |
254 | } | |
92f5a8d4 TL |
255 | ec = {}; |
256 | this->on_finish_impl(ec); | |
b32b8144 FG |
257 | if(ec) |
258 | return; | |
259 | state_ = state::complete; | |
260 | } | |
261 | ||
92f5a8d4 | 262 | template<bool isRequest> |
b32b8144 | 263 | void |
92f5a8d4 | 264 | basic_parser<isRequest>:: |
b32b8144 FG |
265 | maybe_need_more( |
266 | char const* p, std::size_t n, | |
267 | error_code& ec) | |
268 | { | |
269 | if(skip_ == 0) | |
270 | return; | |
271 | if( n > header_limit_) | |
272 | n = header_limit_; | |
273 | if(n < skip_ + 4) | |
274 | { | |
275 | ec = error::need_more; | |
276 | return; | |
277 | } | |
278 | auto const term = | |
279 | find_eom(p + skip_, p + n); | |
280 | if(! term) | |
281 | { | |
282 | skip_ = n - 3; | |
283 | if(skip_ + 4 > header_limit_) | |
284 | { | |
285 | ec = error::header_limit; | |
286 | return; | |
287 | } | |
288 | ec = error::need_more; | |
289 | return; | |
290 | } | |
291 | skip_ = 0; | |
292 | } | |
293 | ||
92f5a8d4 | 294 | template<bool isRequest> |
b32b8144 | 295 | void |
92f5a8d4 | 296 | basic_parser<isRequest>:: |
b32b8144 FG |
297 | parse_start_line( |
298 | char const*& in, char const* last, | |
299 | error_code& ec, std::true_type) | |
300 | { | |
301 | /* | |
302 | request-line = method SP request-target SP HTTP-version CRLF | |
303 | method = token | |
304 | */ | |
305 | auto p = in; | |
306 | ||
307 | string_view method; | |
308 | parse_method(p, last, method, ec); | |
309 | if(ec) | |
310 | return; | |
311 | ||
312 | string_view target; | |
313 | parse_target(p, last, target, ec); | |
314 | if(ec) | |
315 | return; | |
316 | ||
317 | int version = 0; | |
318 | parse_version(p, last, version, ec); | |
319 | if(ec) | |
320 | return; | |
321 | if(version < 10 || version > 11) | |
322 | { | |
323 | ec = error::bad_version; | |
324 | return; | |
325 | } | |
326 | ||
327 | if(p + 2 > last) | |
328 | { | |
329 | ec = error::need_more; | |
330 | return; | |
331 | } | |
332 | if(p[0] != '\r' || p[1] != '\n') | |
333 | { | |
334 | ec = error::bad_version; | |
335 | return; | |
336 | } | |
337 | p += 2; | |
338 | ||
339 | if(version >= 11) | |
340 | f_ |= flagHTTP11; | |
341 | ||
92f5a8d4 | 342 | this->on_request_impl(string_to_verb(method), |
b32b8144 FG |
343 | method, target, version, ec); |
344 | if(ec) | |
345 | return; | |
346 | ||
347 | in = p; | |
348 | state_ = state::fields; | |
349 | } | |
350 | ||
92f5a8d4 | 351 | template<bool isRequest> |
b32b8144 | 352 | void |
92f5a8d4 | 353 | basic_parser<isRequest>:: |
b32b8144 FG |
354 | parse_start_line( |
355 | char const*& in, char const* last, | |
356 | error_code& ec, std::false_type) | |
357 | { | |
358 | /* | |
359 | status-line = HTTP-version SP status-code SP reason-phrase CRLF | |
360 | status-code = 3*DIGIT | |
361 | reason-phrase = *( HTAB / SP / VCHAR / obs-text ) | |
362 | */ | |
363 | auto p = in; | |
364 | ||
365 | int version = 0; | |
366 | parse_version(p, last, version, ec); | |
367 | if(ec) | |
368 | return; | |
369 | if(version < 10 || version > 11) | |
370 | { | |
371 | ec = error::bad_version; | |
372 | return; | |
373 | } | |
374 | ||
375 | // SP | |
376 | if(p + 1 > last) | |
377 | { | |
378 | ec = error::need_more; | |
379 | return; | |
380 | } | |
381 | if(*p++ != ' ') | |
382 | { | |
383 | ec = error::bad_version; | |
384 | return; | |
385 | } | |
386 | ||
387 | parse_status(p, last, status_, ec); | |
388 | if(ec) | |
389 | return; | |
390 | ||
391 | // parse reason CRLF | |
392 | string_view reason; | |
393 | parse_reason(p, last, reason, ec); | |
394 | if(ec) | |
395 | return; | |
396 | ||
397 | if(version >= 11) | |
398 | f_ |= flagHTTP11; | |
399 | ||
92f5a8d4 | 400 | this->on_response_impl( |
b32b8144 FG |
401 | status_, reason, version, ec); |
402 | if(ec) | |
403 | return; | |
404 | ||
405 | in = p; | |
406 | state_ = state::fields; | |
407 | } | |
408 | ||
92f5a8d4 | 409 | template<bool isRequest> |
b32b8144 | 410 | void |
92f5a8d4 | 411 | basic_parser<isRequest>:: |
b32b8144 FG |
412 | parse_fields(char const*& in, |
413 | char const* last, error_code& ec) | |
414 | { | |
415 | string_view name; | |
416 | string_view value; | |
417 | // https://stackoverflow.com/questions/686217/maximum-on-http-header-values | |
92f5a8d4 | 418 | beast::detail::char_buffer<max_obs_fold> buf; |
b32b8144 FG |
419 | auto p = in; |
420 | for(;;) | |
421 | { | |
422 | if(p + 2 > last) | |
423 | { | |
424 | ec = error::need_more; | |
425 | return; | |
426 | } | |
427 | if(p[0] == '\r') | |
428 | { | |
429 | if(p[1] != '\n') | |
430 | ec = error::bad_line_ending; | |
431 | in = p + 2; | |
432 | return; | |
433 | } | |
434 | parse_field(p, last, name, value, buf, ec); | |
435 | if(ec) | |
436 | return; | |
437 | auto const f = string_to_field(name); | |
438 | do_field(f, value, ec); | |
439 | if(ec) | |
440 | return; | |
92f5a8d4 | 441 | this->on_field_impl(f, name, value, ec); |
b32b8144 FG |
442 | if(ec) |
443 | return; | |
444 | in = p; | |
445 | } | |
446 | } | |
447 | ||
92f5a8d4 | 448 | template<bool isRequest> |
b32b8144 | 449 | void |
92f5a8d4 | 450 | basic_parser<isRequest>:: |
b32b8144 FG |
451 | finish_header(error_code& ec, std::true_type) |
452 | { | |
453 | // RFC 7230 section 3.3 | |
454 | // https://tools.ietf.org/html/rfc7230#section-3.3 | |
455 | ||
456 | if(f_ & flagSkipBody) | |
457 | { | |
458 | state_ = state::complete; | |
459 | } | |
460 | else if(f_ & flagContentLength) | |
461 | { | |
20effc67 TL |
462 | if(body_limit_.has_value() && |
463 | len_ > body_limit_) | |
b32b8144 FG |
464 | { |
465 | ec = error::body_limit; | |
466 | return; | |
467 | } | |
468 | if(len_ > 0) | |
469 | { | |
470 | f_ |= flagHasBody; | |
471 | state_ = state::body0; | |
472 | } | |
473 | else | |
474 | { | |
475 | state_ = state::complete; | |
476 | } | |
477 | } | |
478 | else if(f_ & flagChunked) | |
479 | { | |
480 | f_ |= flagHasBody; | |
481 | state_ = state::chunk_header0; | |
482 | } | |
483 | else | |
484 | { | |
485 | len_ = 0; | |
92f5a8d4 | 486 | len0_ = 0; |
b32b8144 FG |
487 | state_ = state::complete; |
488 | } | |
489 | ||
92f5a8d4 TL |
490 | ec = {}; |
491 | this->on_header_impl(ec); | |
b32b8144 FG |
492 | if(ec) |
493 | return; | |
494 | if(state_ == state::complete) | |
495 | { | |
92f5a8d4 | 496 | this->on_finish_impl(ec); |
b32b8144 FG |
497 | if(ec) |
498 | return; | |
499 | } | |
500 | } | |
501 | ||
92f5a8d4 | 502 | template<bool isRequest> |
b32b8144 | 503 | void |
92f5a8d4 | 504 | basic_parser<isRequest>:: |
b32b8144 FG |
505 | finish_header(error_code& ec, std::false_type) |
506 | { | |
507 | // RFC 7230 section 3.3 | |
508 | // https://tools.ietf.org/html/rfc7230#section-3.3 | |
509 | ||
510 | if( (f_ & flagSkipBody) || // e.g. response to a HEAD request | |
511 | status_ / 100 == 1 || // 1xx e.g. Continue | |
512 | status_ == 204 || // No Content | |
513 | status_ == 304) // Not Modified | |
514 | { | |
515 | // VFALCO Content-Length may be present, but we | |
516 | // treat the message as not having a body. | |
517 | // https://github.com/boostorg/beast/issues/692 | |
518 | state_ = state::complete; | |
519 | } | |
520 | else if(f_ & flagContentLength) | |
521 | { | |
b32b8144 FG |
522 | if(len_ > 0) |
523 | { | |
524 | f_ |= flagHasBody; | |
525 | state_ = state::body0; | |
92f5a8d4 | 526 | |
20effc67 TL |
527 | if(body_limit_.has_value() && |
528 | len_ > body_limit_) | |
92f5a8d4 TL |
529 | { |
530 | ec = error::body_limit; | |
531 | return; | |
532 | } | |
b32b8144 FG |
533 | } |
534 | else | |
535 | { | |
536 | state_ = state::complete; | |
537 | } | |
538 | } | |
539 | else if(f_ & flagChunked) | |
540 | { | |
541 | f_ |= flagHasBody; | |
542 | state_ = state::chunk_header0; | |
543 | } | |
544 | else | |
545 | { | |
546 | f_ |= flagHasBody; | |
547 | f_ |= flagNeedEOF; | |
548 | state_ = state::body_to_eof0; | |
549 | } | |
550 | ||
92f5a8d4 TL |
551 | ec = {}; |
552 | this->on_header_impl(ec); | |
b32b8144 FG |
553 | if(ec) |
554 | return; | |
555 | if(state_ == state::complete) | |
556 | { | |
92f5a8d4 | 557 | this->on_finish_impl(ec); |
b32b8144 FG |
558 | if(ec) |
559 | return; | |
560 | } | |
561 | } | |
562 | ||
92f5a8d4 | 563 | template<bool isRequest> |
b32b8144 | 564 | void |
92f5a8d4 | 565 | basic_parser<isRequest>:: |
b32b8144 FG |
566 | parse_body(char const*& p, |
567 | std::size_t n, error_code& ec) | |
568 | { | |
92f5a8d4 TL |
569 | ec = {}; |
570 | n = this->on_body_impl(string_view{p, | |
b32b8144 FG |
571 | beast::detail::clamp(len_, n)}, ec); |
572 | p += n; | |
573 | len_ -= n; | |
574 | if(ec) | |
575 | return; | |
576 | if(len_ > 0) | |
577 | return; | |
92f5a8d4 | 578 | this->on_finish_impl(ec); |
b32b8144 FG |
579 | if(ec) |
580 | return; | |
581 | state_ = state::complete; | |
582 | } | |
583 | ||
92f5a8d4 | 584 | template<bool isRequest> |
b32b8144 | 585 | void |
92f5a8d4 | 586 | basic_parser<isRequest>:: |
b32b8144 FG |
587 | parse_body_to_eof(char const*& p, |
588 | std::size_t n, error_code& ec) | |
589 | { | |
20effc67 | 590 | if(body_limit_.has_value()) |
b32b8144 | 591 | { |
20effc67 TL |
592 | if (n > *body_limit_) |
593 | { | |
594 | ec = error::body_limit; | |
595 | return; | |
596 | } | |
597 | *body_limit_ -= n; | |
b32b8144 | 598 | } |
92f5a8d4 TL |
599 | ec = {}; |
600 | n = this->on_body_impl(string_view{p, n}, ec); | |
b32b8144 FG |
601 | p += n; |
602 | if(ec) | |
603 | return; | |
604 | } | |
605 | ||
92f5a8d4 | 606 | template<bool isRequest> |
b32b8144 | 607 | void |
92f5a8d4 | 608 | basic_parser<isRequest>:: |
b32b8144 FG |
609 | parse_chunk_header(char const*& p0, |
610 | std::size_t n, error_code& ec) | |
611 | { | |
612 | /* | |
613 | chunked-body = *chunk last-chunk trailer-part CRLF | |
614 | ||
615 | chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF | |
616 | last-chunk = 1*("0") [ chunk-ext ] CRLF | |
617 | trailer-part = *( header-field CRLF ) | |
618 | ||
619 | chunk-size = 1*HEXDIG | |
620 | chunk-data = 1*OCTET ; a sequence of chunk-size octets | |
621 | chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) | |
622 | chunk-ext-name = token | |
623 | chunk-ext-val = token / quoted-string | |
624 | */ | |
625 | ||
626 | auto p = p0; | |
627 | auto const pend = p + n; | |
628 | char const* eol; | |
629 | ||
630 | if(! (f_ & flagFinalChunk)) | |
631 | { | |
632 | if(n < skip_ + 2) | |
633 | { | |
634 | ec = error::need_more; | |
635 | return; | |
636 | } | |
637 | if(f_ & flagExpectCRLF) | |
638 | { | |
639 | // Treat the last CRLF in a chunk as | |
640 | // part of the next chunk, so p can | |
641 | // be parsed in one call instead of two. | |
642 | if(! parse_crlf(p)) | |
643 | { | |
644 | ec = error::bad_chunk; | |
645 | return; | |
646 | } | |
647 | } | |
648 | eol = find_eol(p0 + skip_, pend, ec); | |
649 | if(ec) | |
650 | return; | |
651 | if(! eol) | |
652 | { | |
653 | ec = error::need_more; | |
654 | skip_ = n - 1; | |
655 | return; | |
656 | } | |
657 | skip_ = static_cast< | |
658 | std::size_t>(eol - 2 - p0); | |
659 | ||
660 | std::uint64_t size; | |
661 | if(! parse_hex(p, size)) | |
662 | { | |
663 | ec = error::bad_chunk; | |
664 | return; | |
665 | } | |
666 | if(size != 0) | |
667 | { | |
20effc67 | 668 | if (body_limit_.has_value()) |
b32b8144 | 669 | { |
20effc67 TL |
670 | if (size > *body_limit_) |
671 | { | |
672 | ec = error::body_limit; | |
673 | return; | |
674 | } | |
675 | *body_limit_ -= size; | |
b32b8144 | 676 | } |
b32b8144 FG |
677 | auto const start = p; |
678 | parse_chunk_extensions(p, pend, ec); | |
679 | if(ec) | |
680 | return; | |
681 | if(p != eol -2 ) | |
682 | { | |
683 | ec = error::bad_chunk_extension; | |
684 | return; | |
685 | } | |
686 | auto const ext = make_string(start, p); | |
92f5a8d4 | 687 | this->on_chunk_header_impl(size, ext, ec); |
b32b8144 FG |
688 | if(ec) |
689 | return; | |
690 | len_ = size; | |
691 | skip_ = 2; | |
692 | p0 = eol; | |
693 | f_ |= flagExpectCRLF; | |
694 | state_ = state::chunk_body; | |
695 | return; | |
696 | } | |
697 | ||
698 | f_ |= flagFinalChunk; | |
699 | } | |
700 | else | |
701 | { | |
702 | BOOST_ASSERT(n >= 5); | |
703 | if(f_ & flagExpectCRLF) | |
704 | BOOST_VERIFY(parse_crlf(p)); | |
705 | std::uint64_t size; | |
706 | BOOST_VERIFY(parse_hex(p, size)); | |
707 | eol = find_eol(p, pend, ec); | |
708 | BOOST_ASSERT(! ec); | |
709 | } | |
710 | ||
711 | auto eom = find_eom(p0 + skip_, pend); | |
712 | if(! eom) | |
713 | { | |
714 | BOOST_ASSERT(n >= 3); | |
715 | skip_ = n - 3; | |
716 | ec = error::need_more; | |
717 | return; | |
718 | } | |
719 | ||
720 | auto const start = p; | |
721 | parse_chunk_extensions(p, pend, ec); | |
722 | if(ec) | |
723 | return; | |
724 | if(p != eol - 2) | |
725 | { | |
726 | ec = error::bad_chunk_extension; | |
727 | return; | |
728 | } | |
729 | auto const ext = make_string(start, p); | |
92f5a8d4 | 730 | this->on_chunk_header_impl(0, ext, ec); |
b32b8144 FG |
731 | if(ec) |
732 | return; | |
733 | p = eol; | |
734 | parse_fields(p, eom, ec); | |
735 | if(ec) | |
736 | return; | |
737 | BOOST_ASSERT(p == eom); | |
738 | p0 = eom; | |
739 | ||
92f5a8d4 | 740 | this->on_finish_impl(ec); |
b32b8144 FG |
741 | if(ec) |
742 | return; | |
743 | state_ = state::complete; | |
744 | } | |
745 | ||
92f5a8d4 | 746 | template<bool isRequest> |
b32b8144 | 747 | void |
92f5a8d4 | 748 | basic_parser<isRequest>:: |
b32b8144 FG |
749 | parse_chunk_body(char const*& p, |
750 | std::size_t n, error_code& ec) | |
751 | { | |
92f5a8d4 TL |
752 | ec = {}; |
753 | n = this->on_chunk_body_impl( | |
b32b8144 FG |
754 | len_, string_view{p, |
755 | beast::detail::clamp(len_, n)}, ec); | |
756 | p += n; | |
757 | len_ -= n; | |
758 | if(len_ == 0) | |
759 | state_ = state::chunk_header; | |
760 | } | |
761 | ||
92f5a8d4 | 762 | template<bool isRequest> |
b32b8144 | 763 | void |
92f5a8d4 | 764 | basic_parser<isRequest>:: |
b32b8144 FG |
765 | do_field(field f, |
766 | string_view value, error_code& ec) | |
767 | { | |
92f5a8d4 | 768 | using namespace beast::detail::string_literals; |
b32b8144 FG |
769 | // Connection |
770 | if(f == field::connection || | |
771 | f == field::proxy_connection) | |
772 | { | |
773 | auto const list = opt_token_list{value}; | |
774 | if(! validate_list(list)) | |
775 | { | |
776 | // VFALCO Should this be a field specific error? | |
777 | ec = error::bad_value; | |
778 | return; | |
779 | } | |
780 | for(auto const& s : list) | |
781 | { | |
92f5a8d4 | 782 | if(beast::iequals("close"_sv, s)) |
b32b8144 FG |
783 | { |
784 | f_ |= flagConnectionClose; | |
785 | continue; | |
786 | } | |
787 | ||
92f5a8d4 | 788 | if(beast::iequals("keep-alive"_sv, s)) |
b32b8144 FG |
789 | { |
790 | f_ |= flagConnectionKeepAlive; | |
791 | continue; | |
792 | } | |
793 | ||
92f5a8d4 | 794 | if(beast::iequals("upgrade"_sv, s)) |
b32b8144 FG |
795 | { |
796 | f_ |= flagConnectionUpgrade; | |
797 | continue; | |
798 | } | |
799 | } | |
92f5a8d4 | 800 | ec = {}; |
b32b8144 FG |
801 | return; |
802 | } | |
803 | ||
804 | // Content-Length | |
805 | if(f == field::content_length) | |
806 | { | |
f67539c2 | 807 | auto bad_content_length = [&ec] |
b32b8144 | 808 | { |
b32b8144 | 809 | ec = error::bad_content_length; |
f67539c2 | 810 | }; |
b32b8144 | 811 | |
f67539c2 | 812 | // conflicting field |
b32b8144 | 813 | if(f_ & flagChunked) |
f67539c2 TL |
814 | return bad_content_length(); |
815 | ||
816 | // Content-length may be a comma-separated list of integers | |
817 | auto tokens_unprocessed = 1 + | |
818 | std::count(value.begin(), value.end(), ','); | |
819 | auto tokens = opt_token_list(value); | |
820 | if (tokens.begin() == tokens.end() || | |
821 | !validate_list(tokens)) | |
822 | return bad_content_length(); | |
823 | ||
824 | auto existing = this->content_length_unchecked(); | |
825 | for (auto tok : tokens) | |
b32b8144 | 826 | { |
f67539c2 TL |
827 | std::uint64_t v; |
828 | if (!parse_dec(tok, v)) | |
829 | return bad_content_length(); | |
830 | --tokens_unprocessed; | |
831 | if (existing.has_value()) | |
832 | { | |
833 | if (v != *existing) | |
834 | return bad_content_length(); | |
835 | } | |
836 | else | |
837 | { | |
838 | existing = v; | |
839 | } | |
b32b8144 FG |
840 | } |
841 | ||
f67539c2 TL |
842 | if (tokens_unprocessed) |
843 | return bad_content_length(); | |
b32b8144 | 844 | |
f67539c2 | 845 | BOOST_ASSERT(existing.has_value()); |
92f5a8d4 | 846 | ec = {}; |
f67539c2 TL |
847 | len_ = *existing; |
848 | len0_ = *existing; | |
b32b8144 FG |
849 | f_ |= flagContentLength; |
850 | return; | |
851 | } | |
852 | ||
853 | // Transfer-Encoding | |
854 | if(f == field::transfer_encoding) | |
855 | { | |
856 | if(f_ & flagChunked) | |
857 | { | |
858 | // duplicate | |
859 | ec = error::bad_transfer_encoding; | |
860 | return; | |
861 | } | |
862 | ||
863 | if(f_ & flagContentLength) | |
864 | { | |
865 | // conflicting field | |
866 | ec = error::bad_transfer_encoding; | |
867 | return; | |
868 | } | |
869 | ||
92f5a8d4 | 870 | ec = {}; |
b32b8144 FG |
871 | auto const v = token_list{value}; |
872 | auto const p = std::find_if(v.begin(), v.end(), | |
92f5a8d4 | 873 | [&](string_view const& s) |
b32b8144 | 874 | { |
92f5a8d4 | 875 | return beast::iequals("chunked"_sv, s); |
b32b8144 FG |
876 | }); |
877 | if(p == v.end()) | |
878 | return; | |
879 | if(std::next(p) != v.end()) | |
880 | return; | |
881 | len_ = 0; | |
882 | f_ |= flagChunked; | |
883 | return; | |
884 | } | |
885 | ||
886 | // Upgrade | |
887 | if(f == field::upgrade) | |
888 | { | |
92f5a8d4 | 889 | ec = {}; |
b32b8144 FG |
890 | f_ |= flagUpgrade; |
891 | return; | |
892 | } | |
893 | ||
92f5a8d4 | 894 | ec = {}; |
b32b8144 FG |
895 | } |
896 | ||
92f5a8d4 TL |
897 | #ifdef BOOST_BEAST_SOURCE |
898 | template class http::basic_parser<true>; | |
899 | template class http::basic_parser<false>; | |
900 | #endif | |
901 | ||
b32b8144 FG |
902 | } // http |
903 | } // beast | |
904 | } // boost | |
905 | ||
906 | #endif |