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