]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot 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 | ||
8 | #ifndef BEAST_HTTP_IMPL_BASIC_PARSER_IPP | |
9 | #define BEAST_HTTP_IMPL_BASIC_PARSER_IPP | |
10 | ||
11 | #include <beast/core/buffer_concepts.hpp> | |
12 | #include <beast/core/detail/ci_char_traits.hpp> | |
13 | #include <beast/core/detail/clamp.hpp> | |
14 | #include <beast/core/detail/type_traits.hpp> | |
15 | #include <beast/http/error.hpp> | |
16 | #include <beast/http/rfc7230.hpp> | |
17 | #include <boost/asio/buffer.hpp> | |
18 | #include <algorithm> | |
19 | #include <utility> | |
20 | ||
21 | namespace beast { | |
22 | namespace http { | |
23 | ||
24 | template<bool isRequest, bool isDirect, class Derived> | |
25 | template<bool OtherIsDirect, class OtherDerived> | |
26 | basic_parser<isRequest, isDirect, Derived>:: | |
27 | basic_parser(basic_parser<isRequest, | |
28 | OtherIsDirect, OtherDerived>&& other) | |
29 | : len_(other.len_) | |
30 | , buf_(std::move(other.buf_)) | |
31 | , buf_len_(other.buf_len_) | |
32 | , skip_(other.skip_) | |
33 | , x_(other.x_) | |
34 | , f_(other.f_) | |
35 | , state_(other.state_) | |
36 | { | |
37 | } | |
38 | ||
39 | template<bool isRequest, bool isDirect, class Derived> | |
40 | void | |
41 | basic_parser<isRequest, isDirect, Derived>:: | |
42 | skip_body() | |
43 | { | |
44 | BOOST_ASSERT(! got_some()); | |
45 | f_ |= flagSkipBody; | |
46 | } | |
47 | ||
48 | template<bool isRequest, bool isDirect, class Derived> | |
49 | bool | |
50 | basic_parser<isRequest, isDirect, Derived>:: | |
51 | is_keep_alive() const | |
52 | { | |
53 | BOOST_ASSERT(got_header()); | |
54 | if(f_ & flagHTTP11) | |
55 | { | |
56 | if(f_ & flagConnectionClose) | |
57 | return false; | |
58 | } | |
59 | else | |
60 | { | |
61 | if(! (f_ & flagConnectionKeepAlive)) | |
62 | return false; | |
63 | } | |
64 | return (f_ & flagNeedEOF) == 0; | |
65 | } | |
66 | ||
67 | template<bool isRequest, bool isDirect, class Derived> | |
68 | template<class ConstBufferSequence> | |
69 | std::size_t | |
70 | basic_parser<isRequest, isDirect, Derived>:: | |
71 | write(ConstBufferSequence const& buffers, | |
72 | error_code& ec) | |
73 | { | |
74 | static_assert(is_ConstBufferSequence< | |
75 | ConstBufferSequence>::value, | |
76 | "ConstBufferSequence requirements not met"); | |
77 | auto const buffer = maybe_flatten(buffers); | |
78 | return write(boost::asio::const_buffers_1{ | |
79 | buffer.data(), buffer.size()}, ec); | |
80 | } | |
81 | ||
82 | template<bool isRequest, bool isDirect, class Derived> | |
83 | std::size_t | |
84 | basic_parser<isRequest, isDirect, Derived>:: | |
85 | write(boost::asio::const_buffers_1 const& buffer, | |
86 | error_code& ec) | |
87 | { | |
88 | return do_write(buffer, ec, | |
89 | std::integral_constant<bool, isDirect>{}); | |
90 | } | |
91 | ||
92 | template<bool isRequest, bool isDirect, class Derived> | |
93 | void | |
94 | basic_parser<isRequest, isDirect, Derived>:: | |
95 | write_eof(error_code& ec) | |
96 | { | |
97 | BOOST_ASSERT(got_some()); | |
98 | if(state_ == parse_state::header) | |
99 | { | |
100 | ec = error::partial_message; | |
101 | return; | |
102 | } | |
103 | if(f_ & (flagContentLength | flagChunked)) | |
104 | { | |
105 | if(state_ != parse_state::complete) | |
106 | { | |
107 | ec = error::partial_message; | |
108 | return; | |
109 | } | |
110 | return; | |
111 | } | |
112 | do_complete(ec); | |
113 | if(ec) | |
114 | return; | |
115 | } | |
116 | ||
117 | template<bool isRequest, bool isDirect, class Derived> | |
118 | template<class DynamicBuffer> | |
119 | std::size_t | |
120 | basic_parser<isRequest, isDirect, Derived>:: | |
121 | copy_body(DynamicBuffer& dynabuf) | |
122 | { | |
123 | // This function not available when isDirect==false | |
124 | static_assert(isDirect, ""); | |
125 | ||
126 | using boost::asio::buffer_copy; | |
127 | using boost::asio::buffer_size; | |
128 | BOOST_ASSERT(dynabuf.size() > 0); | |
129 | BOOST_ASSERT( | |
130 | state_ == parse_state::body || | |
131 | state_ == parse_state::body_to_eof || | |
132 | state_ == parse_state::chunk_body); | |
133 | maybe_do_body_direct(); | |
134 | switch(state_) | |
135 | { | |
136 | case parse_state::body_to_eof: | |
137 | { | |
138 | auto const buffers = | |
139 | impl().on_prepare(dynabuf.size()); | |
140 | BOOST_ASSERT( | |
141 | buffer_size(buffers) >= 1 && | |
142 | buffer_size(buffers) <= | |
143 | dynabuf.size()); | |
144 | auto const n = buffer_copy( | |
145 | buffers, dynabuf.data()); | |
146 | dynabuf.consume(n); | |
147 | impl().on_commit(n); | |
148 | return n; | |
149 | } | |
150 | ||
151 | default: | |
152 | { | |
153 | BOOST_ASSERT(len_ > 0); | |
154 | auto const buffers = | |
155 | impl().on_prepare( | |
156 | beast::detail::clamp(len_)); | |
157 | BOOST_ASSERT( | |
158 | buffer_size(buffers) >= 1 && | |
159 | buffer_size(buffers) <= | |
160 | beast::detail::clamp(len_)); | |
161 | auto const n = buffer_copy( | |
162 | buffers, dynabuf.data()); | |
163 | commit_body(n); | |
164 | return n; | |
165 | } | |
166 | } | |
167 | } | |
168 | ||
169 | template<bool isRequest, bool isDirect, class Derived> | |
170 | template<class MutableBufferSequence> | |
171 | void | |
172 | basic_parser<isRequest, isDirect, Derived>:: | |
173 | prepare_body(boost::optional< | |
174 | MutableBufferSequence>& buffers, std::size_t limit) | |
175 | { | |
176 | // This function not available when isDirect==false | |
177 | static_assert(isDirect, ""); | |
178 | ||
179 | BOOST_ASSERT(limit > 0); | |
180 | BOOST_ASSERT( | |
181 | state_ == parse_state::body || | |
182 | state_ == parse_state::body_to_eof || | |
183 | state_ == parse_state::chunk_body); | |
184 | maybe_do_body_direct(); | |
185 | std::size_t n; | |
186 | switch(state_) | |
187 | { | |
188 | case parse_state::body_to_eof: | |
189 | n = limit; | |
190 | break; | |
191 | ||
192 | default: | |
193 | BOOST_ASSERT(len_ > 0); | |
194 | n = beast::detail::clamp(len_, limit); | |
195 | break; | |
196 | } | |
197 | buffers.emplace(impl().on_prepare(n)); | |
198 | } | |
199 | ||
200 | template<bool isRequest, bool isDirect, class Derived> | |
201 | void | |
202 | basic_parser<isRequest, isDirect, Derived>:: | |
203 | commit_body(std::size_t n) | |
204 | { | |
205 | // This function not available when isDirect==false | |
206 | static_assert(isDirect, ""); | |
207 | ||
208 | BOOST_ASSERT(f_ & flagOnBody); | |
209 | impl().on_commit(n); | |
210 | switch(state_) | |
211 | { | |
212 | case parse_state::body: | |
213 | len_ -= n; | |
214 | if(len_ == 0) | |
215 | { | |
216 | // VFALCO This is no good, throwing out ec? | |
217 | error_code ec; | |
218 | do_complete(ec); | |
219 | } | |
220 | break; | |
221 | ||
222 | case parse_state::chunk_body: | |
223 | len_ -= n; | |
224 | if(len_ == 0) | |
225 | state_ = parse_state::chunk_header; | |
226 | break; | |
227 | ||
228 | default: | |
229 | break; | |
230 | } | |
231 | } | |
232 | ||
233 | template<bool isRequest, bool isDirect, class Derived> | |
234 | void | |
235 | basic_parser<isRequest, isDirect, Derived>:: | |
236 | consume_body(error_code& ec) | |
237 | { | |
238 | BOOST_ASSERT( | |
239 | state_ == parse_state::body || | |
240 | state_ == parse_state::body_to_eof || | |
241 | state_ == parse_state::chunk_body); | |
242 | switch(state_) | |
243 | { | |
244 | case parse_state::body: | |
245 | case parse_state::body_to_eof: | |
246 | do_complete(ec); | |
247 | if(ec) | |
248 | return; | |
249 | break; | |
250 | ||
251 | case parse_state::chunk_body: | |
252 | len_ = 0; | |
253 | state_ = parse_state::chunk_header; | |
254 | break; | |
255 | ||
256 | default: | |
257 | break; | |
258 | } | |
259 | } | |
260 | ||
261 | template<bool isRequest, bool isDirect, class Derived> | |
262 | template<class ConstBufferSequence> | |
263 | inline | |
264 | boost::string_ref | |
265 | basic_parser<isRequest, isDirect, Derived>:: | |
266 | maybe_flatten( | |
267 | ConstBufferSequence const& buffers) | |
268 | { | |
269 | using boost::asio::buffer; | |
270 | using boost::asio::buffer_cast; | |
271 | using boost::asio::buffer_copy; | |
272 | using boost::asio::buffer_size; | |
273 | ||
274 | auto const it = buffers.begin(); | |
275 | auto const last = buffers.end(); | |
276 | if(it == last) | |
277 | return {nullptr, 0}; | |
278 | if(std::next(it) == last) | |
279 | { | |
280 | // single buffer | |
281 | auto const b = *it; | |
282 | return {buffer_cast<char const*>(b), | |
283 | buffer_size(b)}; | |
284 | } | |
285 | auto const len = buffer_size(buffers); | |
286 | if(len > buf_len_) | |
287 | { | |
288 | // reallocate | |
289 | buf_.reset(new char[len]); | |
290 | buf_len_ = len; | |
291 | } | |
292 | // flatten | |
293 | buffer_copy( | |
294 | buffer(buf_.get(), buf_len_), buffers); | |
295 | return {buf_.get(), buf_len_}; | |
296 | } | |
297 | ||
298 | template<bool isRequest, bool isDirect, class Derived> | |
299 | inline | |
300 | std::size_t | |
301 | basic_parser<isRequest, isDirect, Derived>:: | |
302 | do_write(boost::asio::const_buffers_1 const& buffer, | |
303 | error_code& ec, std::true_type) | |
304 | { | |
305 | BOOST_ASSERT( | |
306 | state_ == parse_state::header || | |
307 | state_ == parse_state::chunk_header); | |
308 | using boost::asio::buffer_cast; | |
309 | using boost::asio::buffer_size; | |
310 | auto const p = buffer_cast< | |
311 | char const*>(*buffer.begin()); | |
312 | auto const n = | |
313 | buffer_size(*buffer.begin()); | |
314 | if(state_ == parse_state::header) | |
315 | { | |
316 | if(n > 0) | |
317 | f_ |= flagGotSome; | |
318 | return parse_header(p, n, ec); | |
319 | } | |
320 | else | |
321 | { | |
322 | maybe_do_body_direct(); | |
323 | return parse_chunk_header(p, n, ec); | |
324 | } | |
325 | } | |
326 | ||
327 | template<bool isRequest, bool isDirect, class Derived> | |
328 | inline | |
329 | std::size_t | |
330 | basic_parser<isRequest, isDirect, Derived>:: | |
331 | do_write(boost::asio::const_buffers_1 const& buffer, | |
332 | error_code& ec, std::false_type) | |
333 | { | |
334 | BOOST_ASSERT(state_ != parse_state::complete); | |
335 | using boost::asio::buffer_cast; | |
336 | using boost::asio::buffer_size; | |
337 | auto const p = buffer_cast< | |
338 | char const*>(*buffer.begin()); | |
339 | auto const n = | |
340 | buffer_size(*buffer.begin()); | |
341 | switch(state_) | |
342 | { | |
343 | case parse_state::header: | |
344 | if(n > 0) | |
345 | f_ |= flagGotSome; | |
346 | return parse_header(p, n, ec); | |
347 | ||
348 | case parse_state::body: | |
349 | maybe_do_body_indirect(ec); | |
350 | if(ec) | |
351 | return 0; | |
352 | return parse_body(p, n, ec); | |
353 | ||
354 | case parse_state::body_to_eof: | |
355 | maybe_do_body_indirect(ec); | |
356 | if(ec) | |
357 | return 0; | |
358 | return parse_body_to_eof(p, n, ec); | |
359 | ||
360 | case parse_state::chunk_header: | |
361 | maybe_do_body_indirect(ec); | |
362 | if(ec) | |
363 | return 0; | |
364 | return parse_chunk_header(p, n, ec); | |
365 | ||
366 | case parse_state::chunk_body: | |
367 | return parse_chunk_body(p, n, ec); | |
368 | ||
369 | case parse_state::complete: | |
370 | break; | |
371 | } | |
372 | return 0; | |
373 | } | |
374 | ||
375 | ||
376 | template<bool isRequest, bool isDirect, class Derived> | |
377 | void | |
378 | basic_parser<isRequest, isDirect, Derived>:: | |
379 | parse_startline(char const*& it, | |
380 | int& version, int& status, | |
381 | error_code& ec, std::true_type) | |
382 | { | |
383 | /* | |
384 | request-line = method SP request-target SP HTTP-version CRLF | |
385 | method = token | |
386 | */ | |
387 | auto const method = parse_method(it); | |
388 | if(method.empty()) | |
389 | { | |
390 | ec = error::bad_method; | |
391 | return; | |
392 | } | |
393 | if(*it++ != ' ') | |
394 | { | |
395 | ec = error::bad_method; | |
396 | return; | |
397 | } | |
398 | ||
399 | auto const path = parse_path(it); | |
400 | if(path.empty()) | |
401 | { | |
402 | ec = error::bad_path; | |
403 | return; | |
404 | } | |
405 | if(*it++ != ' ') | |
406 | { | |
407 | ec = error::bad_path; | |
408 | return; | |
409 | } | |
410 | ||
411 | version = parse_version(it); | |
412 | if(version < 0 || ! parse_crlf(it)) | |
413 | { | |
414 | ec = error::bad_version; | |
415 | return; | |
416 | } | |
417 | ||
418 | impl().on_request( | |
419 | method, path, version, ec); | |
420 | if(ec) | |
421 | return; | |
422 | } | |
423 | ||
424 | template<bool isRequest, bool isDirect, class Derived> | |
425 | void | |
426 | basic_parser<isRequest, isDirect, Derived>:: | |
427 | parse_startline(char const*& it, | |
428 | int& version, int& status, | |
429 | error_code& ec, std::false_type) | |
430 | { | |
431 | /* | |
432 | status-line = HTTP-version SP status-code SP reason-phrase CRLF | |
433 | status-code = 3*DIGIT | |
434 | reason-phrase = *( HTAB / SP / VCHAR / obs-text ) | |
435 | */ | |
436 | version = parse_version(it); | |
437 | if(version < 0 || *it != ' ') | |
438 | { | |
439 | ec = error::bad_version; | |
440 | return; | |
441 | } | |
442 | ++it; | |
443 | ||
444 | status = parse_status(it); | |
445 | if(status < 0 || *it != ' ') | |
446 | { | |
447 | ec = error::bad_status; | |
448 | return; | |
449 | } | |
450 | ++it; | |
451 | ||
452 | auto const reason = parse_reason(it); | |
453 | if(! parse_crlf(it)) | |
454 | { | |
455 | ec = error::bad_reason; | |
456 | return; | |
457 | } | |
458 | ||
459 | impl().on_response( | |
460 | status, reason, version, ec); | |
461 | if(ec) | |
462 | return; | |
463 | } | |
464 | ||
465 | template<bool isRequest, bool isDirect, class Derived> | |
466 | void | |
467 | basic_parser<isRequest, isDirect, Derived>:: | |
468 | parse_fields(char const*& it, | |
469 | char const* last, error_code& ec) | |
470 | { | |
471 | /* header-field = field-name ":" OWS field-value OWS | |
472 | ||
473 | field-name = token | |
474 | field-value = *( field-content / obs-fold ) | |
475 | field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] | |
476 | field-vchar = VCHAR / obs-text | |
477 | ||
478 | obs-fold = CRLF 1*( SP / HTAB ) | |
479 | ; obsolete line folding | |
480 | ; see Section 3.2.4 | |
481 | */ | |
482 | for(;;) | |
483 | { | |
484 | auto term = find_eol(it, last, ec); | |
485 | if(ec) | |
486 | return; | |
487 | BOOST_ASSERT(term); | |
488 | if(it == term - 2) | |
489 | { | |
490 | it = term; | |
491 | break; | |
492 | } | |
493 | auto const name = parse_name(it); | |
494 | if(name.empty()) | |
495 | { | |
496 | ec = error::bad_field; | |
497 | return; | |
498 | } | |
499 | if(*it++ != ':') | |
500 | { | |
501 | ec = error::bad_field; | |
502 | return; | |
503 | } | |
504 | if(*term != ' ' && | |
505 | *term != '\t') | |
506 | { | |
507 | auto it2 = term - 2; | |
508 | detail::skip_ows(it, it2); | |
509 | detail::skip_ows_rev(it2, it); | |
510 | auto const value = | |
511 | make_string(it, it2); | |
512 | do_field(name, value, ec); | |
513 | if(ec) | |
514 | return; | |
515 | impl().on_field(name, value, ec); | |
516 | if(ec) | |
517 | return; | |
518 | it = term; | |
519 | } | |
520 | else | |
521 | { | |
522 | // obs-fold | |
523 | for(;;) | |
524 | { | |
525 | auto const it2 = term - 2; | |
526 | detail::skip_ows(it, it2); | |
527 | if(it != it2) | |
528 | break; | |
529 | it = term; | |
530 | if(*it != ' ' && *it != '\t') | |
531 | break; | |
532 | term = find_eol(it, last, ec); | |
533 | if(ec) | |
534 | return; | |
535 | } | |
536 | std::string s; | |
537 | if(it != term) | |
538 | { | |
539 | s.append(it, term - 2); | |
540 | it = term; | |
541 | for(;;) | |
542 | { | |
543 | if(*it != ' ' && *it != '\t') | |
544 | break; | |
545 | s.push_back(' '); | |
546 | detail::skip_ows(it, term - 2); | |
547 | term = find_eol(it, last, ec); | |
548 | if(ec) | |
549 | return; | |
550 | if(it != term - 2) | |
551 | s.append(it, term - 2); | |
552 | it = term; | |
553 | } | |
554 | } | |
555 | boost::string_ref value{ | |
556 | s.data(), s.size()}; | |
557 | do_field(name, value, ec); | |
558 | if(ec) | |
559 | return; | |
560 | impl().on_field(name, value, ec); | |
561 | if(ec) | |
562 | return; | |
563 | } | |
564 | } | |
565 | } | |
566 | ||
567 | template<bool isRequest, bool isDirect, class Derived> | |
568 | void | |
569 | basic_parser<isRequest, isDirect, Derived>:: | |
570 | do_field( | |
571 | boost::string_ref const& name, | |
572 | boost::string_ref const& value, | |
573 | error_code& ec) | |
574 | { | |
575 | // Connection | |
576 | if(strieq("connection", name) || | |
577 | strieq("proxy-connection", name)) | |
578 | { | |
579 | auto const list = opt_token_list{value}; | |
580 | if(! validate_list(list)) | |
581 | { | |
582 | // VFALCO Should this be a field specific error? | |
583 | ec = error::bad_value; | |
584 | return; | |
585 | } | |
586 | for(auto const& s : list) | |
587 | { | |
588 | if(strieq("close", s)) | |
589 | { | |
590 | f_ |= flagConnectionClose; | |
591 | continue; | |
592 | } | |
593 | ||
594 | if(strieq("keep-alive", s)) | |
595 | { | |
596 | f_ |= flagConnectionKeepAlive; | |
597 | continue; | |
598 | } | |
599 | ||
600 | if(strieq("upgrade", s)) | |
601 | { | |
602 | f_ |= flagConnectionUpgrade; | |
603 | continue; | |
604 | } | |
605 | } | |
606 | return; | |
607 | } | |
608 | ||
609 | for(auto it = value.begin(); | |
610 | it != value.end(); ++it) | |
611 | { | |
612 | if(! is_text(*it)) | |
613 | { | |
614 | ec = error::bad_value; | |
615 | return; | |
616 | } | |
617 | } | |
618 | ||
619 | // Content-Length | |
620 | if(strieq("content-length", name)) | |
621 | { | |
622 | if(f_ & flagContentLength) | |
623 | { | |
624 | // duplicate | |
625 | ec = error::bad_content_length; | |
626 | return; | |
627 | } | |
628 | ||
629 | if(f_ & flagChunked) | |
630 | { | |
631 | // conflicting field | |
632 | ec = error::bad_content_length; | |
633 | return; | |
634 | } | |
635 | ||
636 | std::uint64_t v; | |
637 | if(! parse_dec( | |
638 | value.begin(), value.end(), v)) | |
639 | { | |
640 | ec = error::bad_content_length; | |
641 | return; | |
642 | } | |
643 | ||
644 | len_ = v; | |
645 | f_ |= flagContentLength; | |
646 | return; | |
647 | } | |
648 | ||
649 | // Transfer-Encoding | |
650 | if(strieq("transfer-encoding", name)) | |
651 | { | |
652 | if(f_ & flagChunked) | |
653 | { | |
654 | // duplicate | |
655 | ec = error::bad_transfer_encoding; | |
656 | return; | |
657 | } | |
658 | ||
659 | if(f_ & flagContentLength) | |
660 | { | |
661 | // conflicting field | |
662 | ec = error::bad_transfer_encoding; | |
663 | return; | |
664 | } | |
665 | ||
666 | auto const v = token_list{value}; | |
667 | auto const it = std::find_if(v.begin(), v.end(), | |
668 | [&](typename token_list::value_type const& s) | |
669 | { | |
670 | return strieq("chunked", s); | |
671 | }); | |
672 | if(it == v.end()) | |
673 | return; | |
674 | if(std::next(it) != v.end()) | |
675 | return; | |
676 | len_ = 0; | |
677 | f_ |= flagChunked; | |
678 | return; | |
679 | } | |
680 | ||
681 | // Upgrade | |
682 | if(strieq("upgrade", name)) | |
683 | { | |
684 | f_ |= flagUpgrade; | |
685 | ec = {}; | |
686 | return; | |
687 | } | |
688 | } | |
689 | ||
690 | template<bool isRequest, bool isDirect, class Derived> | |
691 | inline | |
692 | std::size_t | |
693 | basic_parser<isRequest, isDirect, Derived>:: | |
694 | parse_header(char const* p, | |
695 | std::size_t n, error_code& ec) | |
696 | { | |
697 | if(n < 4) | |
698 | return 0; | |
699 | auto const term = find_eom( | |
700 | p + skip_, p + n, ec); | |
701 | if(ec) | |
702 | return 0; | |
703 | if(! term) | |
704 | { | |
705 | skip_ = n - 3; | |
706 | return 0; | |
707 | } | |
708 | ||
709 | int version; | |
710 | int status; // ignored for requests | |
711 | ||
712 | skip_ = 0; | |
713 | n = term - p; | |
714 | parse_startline(p, version, status, ec, | |
715 | std::integral_constant< | |
716 | bool, isRequest>{}); | |
717 | if(ec) | |
718 | return 0; | |
719 | if(version >= 11) | |
720 | f_ |= flagHTTP11; | |
721 | ||
722 | parse_fields(p, term, ec); | |
723 | if(ec) | |
724 | return 0; | |
725 | BOOST_ASSERT(p == term); | |
726 | ||
727 | do_header(status, | |
728 | std::integral_constant< | |
729 | bool, isRequest>{}); | |
730 | impl().on_header(ec); | |
731 | if(ec) | |
732 | return 0; | |
733 | if(state_ == parse_state::complete) | |
734 | { | |
735 | impl().on_complete(ec); | |
736 | if(ec) | |
737 | return 0; | |
738 | } | |
739 | return n; | |
740 | } | |
741 | ||
742 | template<bool isRequest, bool isDirect, class Derived> | |
743 | void | |
744 | basic_parser<isRequest, isDirect, Derived>:: | |
745 | do_header(int, std::true_type) | |
746 | { | |
747 | // RFC 7230 section 3.3 | |
748 | // https://tools.ietf.org/html/rfc7230#section-3.3 | |
749 | ||
750 | if(f_ & flagSkipBody) | |
751 | { | |
752 | state_ = parse_state::complete; | |
753 | } | |
754 | else if(f_ & flagContentLength) | |
755 | { | |
756 | if(len_ > 0) | |
757 | { | |
758 | f_ |= flagHasBody; | |
759 | state_ = parse_state::body; | |
760 | } | |
761 | else | |
762 | { | |
763 | state_ = parse_state::complete; | |
764 | } | |
765 | } | |
766 | else if(f_ & flagChunked) | |
767 | { | |
768 | f_ |= flagHasBody; | |
769 | state_ = parse_state::chunk_header; | |
770 | } | |
771 | else | |
772 | { | |
773 | len_ = 0; | |
774 | state_ = parse_state::complete; | |
775 | } | |
776 | } | |
777 | ||
778 | template<bool isRequest, bool isDirect, class Derived> | |
779 | void | |
780 | basic_parser<isRequest, isDirect, Derived>:: | |
781 | do_header(int status, std::false_type) | |
782 | { | |
783 | // RFC 7230 section 3.3 | |
784 | // https://tools.ietf.org/html/rfc7230#section-3.3 | |
785 | ||
786 | if( (f_ & flagSkipBody) || // e.g. response to a HEAD request | |
787 | status / 100 == 1 || // 1xx e.g. Continue | |
788 | status == 204 || // No Content | |
789 | status == 304) // Not Modified | |
790 | { | |
791 | state_ = parse_state::complete; | |
792 | return; | |
793 | } | |
794 | ||
795 | if(f_ & flagContentLength) | |
796 | { | |
797 | if(len_ > 0) | |
798 | { | |
799 | f_ |= flagHasBody; | |
800 | state_ = parse_state::body; | |
801 | } | |
802 | else | |
803 | { | |
804 | state_ = parse_state::complete; | |
805 | } | |
806 | } | |
807 | else if(f_ & flagChunked) | |
808 | { | |
809 | f_ |= flagHasBody; | |
810 | state_ = parse_state::chunk_header; | |
811 | } | |
812 | else | |
813 | { | |
814 | f_ |= flagHasBody; | |
815 | f_ |= flagNeedEOF; | |
816 | state_ = parse_state::body_to_eof; | |
817 | } | |
818 | } | |
819 | ||
820 | template<bool isRequest, bool isDirect, class Derived> | |
821 | void | |
822 | basic_parser<isRequest, isDirect, Derived>:: | |
823 | maybe_do_body_direct() | |
824 | { | |
825 | if(f_ & flagOnBody) | |
826 | return; | |
827 | f_ |= flagOnBody; | |
828 | if(got_content_length()) | |
829 | impl().on_body(len_); | |
830 | else | |
831 | impl().on_body(); | |
832 | } | |
833 | ||
834 | template<bool isRequest, bool isDirect, class Derived> | |
835 | void | |
836 | basic_parser<isRequest, isDirect, Derived>:: | |
837 | maybe_do_body_indirect(error_code& ec) | |
838 | { | |
839 | if(f_ & flagOnBody) | |
840 | return; | |
841 | f_ |= flagOnBody; | |
842 | if(got_content_length()) | |
843 | { | |
844 | impl().on_body(len_, ec); | |
845 | if(ec) | |
846 | return; | |
847 | } | |
848 | else | |
849 | { | |
850 | impl().on_body(ec); | |
851 | if(ec) | |
852 | return; | |
853 | } | |
854 | } | |
855 | ||
856 | template<bool isRequest, bool isDirect, class Derived> | |
857 | std::size_t | |
858 | basic_parser<isRequest, isDirect, Derived>:: | |
859 | parse_chunk_header(char const* p, | |
860 | std::size_t n, error_code& ec) | |
861 | { | |
862 | /* | |
863 | chunked-body = *chunk last-chunk trailer-part CRLF | |
864 | ||
865 | chunk = chunk-size [ chunk-ext ] CRLF chunk-data CRLF | |
866 | last-chunk = 1*("0") [ chunk-ext ] CRLF | |
867 | trailer-part = *( header-field CRLF ) | |
868 | ||
869 | chunk-size = 1*HEXDIG | |
870 | chunk-data = 1*OCTET ; a sequence of chunk-size octets | |
871 | chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) | |
872 | chunk-ext-name = token | |
873 | chunk-ext-val = token / quoted-string | |
874 | */ | |
875 | ||
876 | auto const first = p; | |
877 | auto const last = p + n; | |
878 | ||
879 | // Treat the last CRLF in a chunk as | |
880 | // part of the next chunk, so it can | |
881 | // be parsed in one call instead of two. | |
882 | if(f_ & flagExpectCRLF) | |
883 | { | |
884 | if(n < 2) | |
885 | return 0; | |
886 | if(! parse_crlf(p)) | |
887 | { | |
888 | ec = error::bad_chunk; | |
889 | return 0; | |
890 | } | |
891 | n -= 2; | |
892 | } | |
893 | ||
894 | char const* term; | |
895 | ||
896 | if(! (f_ & flagFinalChunk)) | |
897 | { | |
898 | if(n < 2) | |
899 | return 0; | |
900 | term = find_eol(p + skip_, last, ec); | |
901 | if(ec) | |
902 | return 0; | |
903 | if(! term) | |
904 | { | |
905 | skip_ = n - 1; | |
906 | return 0; | |
907 | } | |
908 | std::uint64_t v; | |
909 | if(! parse_hex(p, v)) | |
910 | { | |
911 | ec = error::bad_chunk; | |
912 | return 0; | |
913 | } | |
914 | if(v != 0) | |
915 | { | |
916 | if(*p == ';') | |
917 | { | |
918 | // VFALCO We need to parse the chunk | |
919 | // extension to validate it here. | |
920 | ext_ = make_string(p, term - 2); | |
921 | impl().on_chunk(v, ext_, ec); | |
922 | if(ec) | |
923 | return 0; | |
924 | } | |
925 | else if(p != term - 2) | |
926 | { | |
927 | ec = error::bad_chunk; | |
928 | return 0; | |
929 | } | |
930 | p = term; | |
931 | len_ = v; | |
932 | skip_ = 0; | |
933 | f_ |= flagExpectCRLF; | |
934 | state_ = parse_state::chunk_body; | |
935 | return p - first; | |
936 | } | |
937 | ||
938 | // This is the offset from the buffer | |
939 | // to the beginning of the first '\r\n' | |
940 | x_ = term - 2 - first; | |
941 | skip_ = x_; | |
942 | ||
943 | f_ |= flagFinalChunk; | |
944 | } | |
945 | else | |
946 | { | |
947 | // We are parsing the value again | |
948 | // to advance p to the right place. | |
949 | std::uint64_t v; | |
950 | auto const result = parse_hex(p, v); | |
951 | BOOST_ASSERT(result && v == 0); | |
952 | beast::detail::ignore_unused(result); | |
953 | beast::detail::ignore_unused(v); | |
954 | } | |
955 | ||
956 | term = find_eom( | |
957 | first + skip_, last, ec); | |
958 | if(ec) | |
959 | return 0; | |
960 | if(! term) | |
961 | { | |
962 | if(n > 3) | |
963 | skip_ = (last - first) - 3; | |
964 | return 0; | |
965 | } | |
966 | ||
967 | if(*p == ';') | |
968 | { | |
969 | ext_ = make_string(p, first + x_); | |
970 | impl().on_chunk(0, ext_, ec); | |
971 | if(ec) | |
972 | return 0; | |
973 | p = first + x_; | |
974 | } | |
975 | if(! parse_crlf(p)) | |
976 | { | |
977 | ec = error::bad_chunk; | |
978 | return 0; | |
979 | } | |
980 | parse_fields(p, term, ec); | |
981 | if(ec) | |
982 | return 0; | |
983 | BOOST_ASSERT(p == term); | |
984 | ||
985 | do_complete(ec); | |
986 | if(ec) | |
987 | return 0; | |
988 | return p - first; | |
989 | } | |
990 | ||
991 | template<bool isRequest, bool isDirect, class Derived> | |
992 | inline | |
993 | std::size_t | |
994 | basic_parser<isRequest, isDirect, Derived>:: | |
995 | parse_body(char const* p, | |
996 | std::size_t n, error_code& ec) | |
997 | { | |
998 | n = beast::detail::clamp(len_, n); | |
999 | body_ = boost::string_ref{p, n}; | |
1000 | impl().on_data(body_, ec); | |
1001 | if(ec) | |
1002 | return 0; | |
1003 | len_ -= n; | |
1004 | if(len_ == 0) | |
1005 | { | |
1006 | do_complete(ec); | |
1007 | if(ec) | |
1008 | return 0; | |
1009 | } | |
1010 | return n; | |
1011 | } | |
1012 | ||
1013 | template<bool isRequest, bool isDirect, class Derived> | |
1014 | inline | |
1015 | std::size_t | |
1016 | basic_parser<isRequest, isDirect, Derived>:: | |
1017 | parse_body_to_eof(char const* p, | |
1018 | std::size_t n, error_code& ec) | |
1019 | { | |
1020 | body_ = boost::string_ref{p, n}; | |
1021 | impl().on_data(body_, ec); | |
1022 | if(ec) | |
1023 | return 0; | |
1024 | return n; | |
1025 | } | |
1026 | ||
1027 | template<bool isRequest, bool isDirect, class Derived> | |
1028 | inline | |
1029 | std::size_t | |
1030 | basic_parser<isRequest, isDirect, Derived>:: | |
1031 | parse_chunk_body(char const* p, | |
1032 | std::size_t n, error_code& ec) | |
1033 | { | |
1034 | n = beast::detail::clamp(len_, n); | |
1035 | body_ = boost::string_ref{p, n}; | |
1036 | impl().on_data(body_, ec); | |
1037 | if(ec) | |
1038 | return 0; | |
1039 | len_ -= n; | |
1040 | if(len_ == 0) | |
1041 | { | |
1042 | body_ = {}; | |
1043 | state_ = parse_state::chunk_header; | |
1044 | } | |
1045 | return n; | |
1046 | } | |
1047 | ||
1048 | template<bool isRequest, bool isDirect, class Derived> | |
1049 | void | |
1050 | basic_parser<isRequest, isDirect, Derived>:: | |
1051 | do_complete(error_code& ec) | |
1052 | { | |
1053 | impl().on_complete(ec); | |
1054 | if(ec) | |
1055 | return; | |
1056 | state_ = parse_state::complete; | |
1057 | } | |
1058 | ||
1059 | } // http | |
1060 | } // beast | |
1061 | ||
1062 | #endif |