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