Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/CPPAlliance/http_proto
8 : //
9 :
10 : #ifndef BOOST_HTTP_PROTO_IMPL_PARSER_IPP
11 : #define BOOST_HTTP_PROTO_IMPL_PARSER_IPP
12 :
13 : #include <boost/http_proto/error.hpp>
14 : #include <boost/http_proto/parser.hpp>
15 : #include <boost/http_proto/detail/codec.hpp>
16 : #include <boost/http_proto/detail/except.hpp>
17 : #include <boost/buffers/buffer_copy.hpp>
18 : #include <boost/url/grammar/ci_string.hpp>
19 : #include <boost/assert.hpp>
20 : #include <boost/none.hpp>
21 : #include <memory>
22 :
23 : namespace boost {
24 : namespace http_proto {
25 :
26 : /*
27 : Parser design:
28 :
29 : The usage of the parser is thus:
30 :
31 : pr.reset(); // prepare for a new stream
32 :
33 : pr.start(); // prepare for a new message
34 : pr.start_head_response(); // new message with no payload
35 :
36 : read_header( ..., pr );
37 : do
38 : {
39 : read_some(..., pr );
40 : }
41 : while(! got_header());
42 :
43 : pr.set_body( ... );
44 : // invalidates the headers? yes.
45 :
46 : read_body( ..., pr );
47 : while(! (pr.flags() &
48 : parser::is_done_bit ) );
49 : {
50 : read_some(..., pr );
51 : }
52 :
53 : If these are called out of order, an
54 : exception is thrown.
55 :
56 : Every call to `prepare` must be
57 : followed by a call to commit, reset,
58 : or the destructor.
59 : */
60 : //------------------------------------------------
61 :
62 745 : parser::
63 : parser(
64 : detail::kind k,
65 745 : config_base const& cfg)
66 : : cfg_(cfg)
67 745 : , h_(detail::empty{k})
68 : {
69 745 : }
70 :
71 : void
72 745 : parser::
73 : construct(
74 : std::size_t extra_buffer_size)
75 : {
76 : // headers_limit too large
77 745 : if( cfg_.headers_limit >
78 : BOOST_HTTP_PROTO_MAX_HEADER)
79 0 : detail::throw_invalid_argument();
80 :
81 : // start_line_limit too large
82 745 : if( cfg_.start_line_limit >=
83 745 : cfg_.headers_limit)
84 0 : detail::throw_invalid_argument();
85 :
86 : // field_size_limit too large
87 745 : if( cfg_.field_size_limit >=
88 745 : cfg_.headers_limit)
89 0 : detail::throw_invalid_argument();
90 :
91 : // fields_limit too large
92 745 : if( cfg_.fields_limit >
93 745 : cfg_.headers_limit / 4)
94 0 : detail::throw_invalid_argument();
95 :
96 : // largest space needed
97 : auto const bytes_needed =
98 745 : detail::header::bytes_needed(
99 : cfg_.headers_limit,
100 : cfg_.fields_limit);
101 :
102 : // prevent overflow
103 745 : if(extra_buffer_size >
104 745 : std::size_t(-1) - bytes_needed)
105 0 : detail::throw_invalid_argument();
106 :
107 : // allocate max headers plus extra
108 745 : ws_.allocate(
109 : bytes_needed +
110 : extra_buffer_size);
111 :
112 745 : h_.cap = bytes_needed;
113 :
114 745 : reset();
115 745 : }
116 :
117 : //------------------------------------------------
118 : //
119 : // Special Members
120 : //
121 : //------------------------------------------------
122 :
123 745 : parser::
124 2980 : ~parser()
125 : {
126 745 : }
127 :
128 : parser::
129 : parser(
130 : parser&&) noexcept = default;
131 :
132 : //------------------------------------------------
133 : //
134 : // Observers
135 : //
136 : //------------------------------------------------
137 :
138 : string_view
139 0 : parser::
140 : body() const noexcept
141 : {
142 : #if 0
143 : // VFALCO How about some
144 : // asserts or exceptions?
145 : if(! m_.got_chunked)
146 : return string_view(
147 : h_.buf + h_.size,
148 : m_.n_payload);
149 : return string_view(
150 : h_.buf +
151 : h_.size +
152 : m_.n_chunk,
153 : m_.n_payload);
154 : #else
155 0 : return {};
156 : #endif
157 : }
158 :
159 : //------------------------------------------------
160 : //
161 : // Modifiers
162 : //
163 : //------------------------------------------------
164 :
165 : // prepare for a new stream
166 : void
167 745 : parser::
168 : reset() noexcept
169 : {
170 745 : st_ = state::need_start;
171 745 : got_eof_ = false;
172 745 : }
173 :
174 : void
175 770 : parser::
176 : start_impl(
177 : bool head_response)
178 : {
179 770 : std::size_t initial_size = 0;
180 770 : switch(st_)
181 : {
182 737 : default:
183 : case state::need_start:
184 737 : BOOST_ASSERT(h_.size == 0);
185 737 : BOOST_ASSERT(h_buf_.size() == 0);
186 737 : BOOST_ASSERT(! got_eof_);
187 737 : break;
188 :
189 0 : case state::headers:
190 : // Can't call start twice.
191 0 : detail::throw_logic_error();
192 :
193 0 : case state::headers_done:
194 : case state::body:
195 : // Can't call start with
196 : // an incomplete message.
197 0 : detail::throw_logic_error();
198 :
199 33 : case state::complete:
200 33 : if(h_buf_.size() > 0)
201 : {
202 : // headers with no body
203 33 : BOOST_ASSERT(h_.size > 0);
204 33 : h_buf_.consume(h_.size);
205 33 : initial_size = h_buf_.size();
206 : // move unused octets to front
207 33 : buffers::buffer_copy(
208 33 : buffers::mutable_buffer(
209 : ws_.data(),
210 : initial_size),
211 66 : h_buf_.data());
212 : }
213 : else
214 : {
215 : // leftover data after body
216 : }
217 33 : break;
218 : }
219 :
220 770 : ws_.clear();
221 :
222 : // set up header read buffer
223 770 : h_buf_ = {
224 : ws_.data(),
225 : cfg_.headers_limit,
226 : initial_size };
227 :
228 : // reset the header but
229 : // preserve the capacity
230 770 : auto const cap = h_.cap;
231 1540 : h_ = detail::header(
232 770 : detail::empty{h_.kind});
233 770 : h_.buf = reinterpret_cast<
234 770 : char*>(ws_.data());
235 770 : h_.cbuf = h_.buf;
236 770 : h_.cap = cap;
237 :
238 770 : cfg_impl_ = {};
239 770 : cfg_impl_.headers_limit = cfg_.headers_limit;
240 770 : cfg_impl_.start_line_limit = cfg_.start_line_limit;
241 770 : cfg_impl_.field_size_limit = cfg_.field_size_limit;
242 770 : cfg_impl_.fields_limit = cfg_.fields_limit;
243 :
244 770 : st_ = state::headers;
245 :
246 770 : BOOST_ASSERT(! head_response ||
247 : h_.kind == detail::kind::response);
248 770 : head_response_ = head_response;
249 770 : }
250 :
251 : auto
252 3185 : parser::
253 : prepare() ->
254 : mutable_buffers_type
255 : {
256 3185 : switch(st_)
257 : {
258 0 : default:
259 : case state::need_start:
260 : // start must be called once
261 : // before calling prepare.
262 0 : detail::throw_logic_error();
263 :
264 3185 : case state::headers:
265 : // fill up to headers_limit
266 : return {
267 3185 : h_buf_.prepare(
268 3185 : cfg_.headers_limit -
269 3185 : h_buf_.size()),
270 3185 : buffers::mutable_buffer{} };
271 :
272 0 : case state::headers_done:
273 : {
274 : // discard headers and move
275 : // any leftover stream data.
276 0 : std::memmove(
277 : ws_.data(),
278 0 : h_.cbuf + h_.size,
279 0 : h_buf_.size() - h_.size);
280 0 : st_ = state::body;
281 : // VFALCO set up body buffer
282 : BOOST_FALLTHROUGH;
283 : }
284 :
285 0 : case state::body:
286 : {
287 0 : return {};
288 : }
289 :
290 0 : case state::complete:
291 : // Can't call `prepare` again after
292 : // a complete message is parsed,
293 : // call `start` first.
294 0 : detail::throw_logic_error();
295 : }
296 : }
297 :
298 : void
299 3185 : parser::
300 : commit(
301 : std::size_t n)
302 : {
303 : // Can't commit after eof
304 3185 : if(got_eof_)
305 0 : detail::throw_logic_error();
306 :
307 3185 : switch(st_)
308 : {
309 3185 : default:
310 : case state::need_start:
311 : case state::headers:
312 3185 : h_buf_.commit(n);
313 3185 : break;
314 :
315 0 : case state::headers_done:
316 : case state::body:
317 : case state::complete:
318 0 : break;
319 : }
320 3185 : }
321 :
322 : void
323 0 : parser::
324 : commit_eof()
325 : {
326 0 : switch(st_)
327 : {
328 0 : default:
329 : case state::need_start:
330 : // Can't commit eof
331 : // before calling start.
332 0 : detail::throw_logic_error();
333 :
334 0 : case state::headers:
335 : case state::headers_done:
336 : case state::body:
337 0 : got_eof_ = true;
338 0 : break;
339 :
340 0 : case state::complete:
341 : // Can't commit eof when
342 : // message is complete.
343 0 : detail::throw_logic_error();
344 : }
345 0 : }
346 :
347 : // process input data then
348 : // eof if input data runs out.
349 : void
350 3185 : parser::
351 : parse(
352 : error_code& ec)
353 : {
354 3185 : switch(st_)
355 : {
356 0 : default:
357 : case state::need_start:
358 : // You must call start before
359 : // calling parse on a new message.
360 0 : detail::throw_logic_error();
361 :
362 3185 : case state::headers:
363 : {
364 3185 : BOOST_ASSERT(h_.buf == ws_.data());
365 3185 : BOOST_ASSERT(h_.cbuf == ws_.data());
366 3185 : auto const new_size = h_buf_.size();
367 3185 : h_.parse(cfg_impl_, new_size, ec);
368 3185 : if(! ec.failed())
369 : {
370 584 : if( h_.md.payload != payload::none &&
371 3 : ! head_response_)
372 : {
373 : // Deliver headers to caller
374 3 : st_ = state::headers_done;
375 3 : break;
376 : }
377 : // no payload
378 581 : st_ = state::complete;
379 581 : break;
380 : }
381 5074 : if( ec == grammar::error::need_more &&
382 2473 : got_eof_)
383 : {
384 0 : if(h_.size > 0)
385 : {
386 : // Connection closed before
387 : // message is complete.
388 0 : ec = BOOST_HTTP_PROTO_ERR(
389 : error::incomplete);
390 :
391 0 : return;
392 : }
393 :
394 : // Connection closed
395 : // cleanly.
396 0 : ec = BOOST_HTTP_PROTO_ERR(
397 : error::end_of_stream);
398 :
399 0 : return;
400 : }
401 2601 : return;
402 : }
403 :
404 0 : case state::headers_done:
405 : {
406 : // This is a no-op
407 0 : ec = {};
408 0 : break;
409 : }
410 :
411 0 : case state::body:
412 : {
413 0 : parse_body(ec);
414 0 : if(ec.failed())
415 0 : return;
416 0 : st_ = state::complete;
417 0 : break;
418 : }
419 : }
420 : }
421 :
422 : //------------------------------------------------
423 :
424 : string_view
425 0 : parser::
426 : release_buffered_data() noexcept
427 : {
428 0 : return {};
429 : }
430 :
431 : //------------------------------------------------
432 : //
433 : // Implementation
434 : //
435 : //------------------------------------------------
436 :
437 : void
438 2 : parser::
439 : apply_param(
440 : config_base const& cfg) noexcept
441 : {
442 2 : cfg_ = cfg;
443 2 : }
444 :
445 : //------------------------------------------------
446 :
447 : auto
448 37 : parser::
449 : safe_get_header() const ->
450 : detail::header const*
451 : {
452 37 : switch(st_)
453 : {
454 0 : default:
455 : case state::need_start:
456 : case state::headers:
457 : // Headers not received yet
458 0 : detail::throw_logic_error();
459 :
460 3 : case state::headers_done:
461 3 : break;
462 :
463 0 : case state::body:
464 : // Headers received and discarded
465 0 : detail::throw_logic_error();
466 :
467 34 : case state::complete:
468 : // VFALCO Could be OK
469 34 : break;
470 : }
471 37 : return &h_;
472 : }
473 :
474 : void
475 0 : parser::
476 : parse_body(
477 : error_code& ec)
478 : {
479 : (void)ec;
480 0 : return;
481 : // VFALCO TODO
482 : #if 0
483 : BOOST_ASSERT(st_ == state::body);
484 :
485 : if(h_.kind == detail::kind::request)
486 : {
487 : // https://tools.ietf.org/html/rfc7230#section-3.3
488 : if(m_.skip_body)
489 : return;
490 : if(m_.content_len.has_value())
491 : {
492 : if(*m_.content_len > cfg_.body_too_large)
493 : {
494 : ec = error::body_too_large;
495 : return;
496 : }
497 : if(*m_.content_len == 0)
498 : return;
499 : }
500 : else if(m_.got_chunked)
501 : {
502 : // VFALCO TODO
503 : return;
504 : }
505 : else
506 : {
507 : // Content-Length: 0
508 : return;
509 : }
510 : }
511 : else
512 : {
513 : BOOST_ASSERT(h_.kind ==
514 : detail::kind::response);
515 :
516 : // https://tools.ietf.org/html/rfc7230#section-3.3
517 : if((h_.res.status_int / 100 == 1) || // 1xx e.g. Continue
518 : h_.res.status_int == 204 || // No Content
519 : h_.res.status_int == 304) // Not Modified
520 : {
521 : // Content-Length may be present, but we
522 : // treat the message as not having a body.
523 : }
524 : else if(m_.content_len.has_value())
525 : {
526 : if(*m_.content_len > 0)
527 : {
528 : if(*m_.content_len > cfg_.body_too_large)
529 : {
530 : ec = error::body_too_large;
531 : return;
532 : }
533 : }
534 : }
535 : else
536 : {
537 : // No Content-Length
538 : return;
539 : }
540 : }
541 :
542 : auto avail = committed_ - size_;
543 : if(m_.content_len.has_value())
544 : {
545 : // known payload length
546 : BOOST_ASSERT(! m_.got_chunked);
547 : BOOST_ASSERT(m_.n_remain > 0);
548 : BOOST_ASSERT(m_.content_len <
549 : cfg_.body_too_large);
550 : if(avail == 0)
551 : {
552 : if(! got_eof_)
553 : {
554 : ec = grammar::error::need_more;
555 : return;
556 : }
557 : ec = error::need_more;
558 : return;
559 : }
560 : if( avail > m_.n_remain)
561 : avail = static_cast<
562 : std::size_t>(m_.n_remain);
563 : size_ += avail;
564 : m_.payload_seen += avail;
565 : m_.n_payload += avail;
566 : m_.n_remain -= avail;
567 : if(m_.n_remain > 0)
568 : {
569 : ec = {};
570 : return;
571 : }
572 : st_ = state::complete;
573 : ec = error::end_of_message;
574 : return;
575 : }
576 :
577 : if(! m_.got_chunked)
578 : {
579 : // end of body indicated by EOF
580 : if(avail > 0)
581 : {
582 : if(avail > (std::size_t(
583 : -1) - m_.n_payload))
584 : {
585 : // overflow size_t
586 : // VFALCO revisit this
587 : ec = error::numeric_overflow;
588 : return;
589 : }
590 : size_ += avail;
591 : m_.n_payload += avail;
592 : ec = {};
593 : return;
594 : }
595 : if(! got_eof_)
596 : {
597 : ec = grammar::error::need_more;
598 : return;
599 : }
600 : st_ = state::complete;
601 : ec = error::end_of_message;
602 : return;
603 : }
604 : #if 0
605 : if(m_.payload_left == 0)
606 : {
607 : // start of chunk
608 : bnf::chunk_part p;
609 : auto it = p.parse(
610 : h_.buf + size_,
611 : h_.buf + (
612 : committed_ - size_),
613 : ec);
614 : if(ec)
615 : return;
616 : auto const v =
617 : p.value();
618 : m_.chunk.size = v.size;
619 : m_.chunk.ext = v.ext;
620 : m_.chunk.trailer = v.trailer;
621 : m_.chunk.fresh = true;
622 : m_.payload_left =
623 : v.size - v.data.size();
624 : }
625 : else
626 : {
627 : // continuation of chunk
628 :
629 : }
630 : #endif
631 : #endif
632 : }
633 :
634 : void
635 0 : parser::
636 : parse_chunk(
637 : error_code& ec)
638 : {
639 : (void)ec;
640 : #if 0
641 : switch(st_)
642 : {
643 : case state::start_line_line:
644 : case state::header_fields:
645 : parse_header(ec);
646 : if(ec.failed())
647 : return;
648 : BOOST_ASSERT(st_ >
649 : state::header_fields);
650 : break;
651 : case state::body:
652 : if(! m_.got_chunked)
653 : return parse_body(ec);
654 : break;
655 : case state::complete:
656 : ec = error::end_of_message;
657 : if(! got_eof_)
658 : return;
659 : st_ = state::end_of_stream;
660 : return;
661 : case state::end_of_stream:
662 : ec = error::end_of_stream;
663 : return;
664 : }
665 :
666 : auto const avail = committed_ - size_;
667 : auto const start = h_.buf + size_;
668 : if(m_.payload_left == 0)
669 : {
670 : // start of chunk
671 : // VFALCO What about chunk_part_next?
672 : BOOST_ASSERT(
673 : size_ == m_.header_size);
674 : bnf::chunk_part p;
675 : auto it = p.parse(start,
676 : h_.buf + (
677 : committed_ - size_), ec);
678 : BOOST_ASSERT(it == start);
679 : if(ec)
680 : return;
681 : auto const v = p.value();
682 : m_.chunk.size = v.size;
683 : m_.chunk.ext = v.ext;
684 : m_.chunk.fresh = true;
685 : if(v.size > 0)
686 : {
687 : // chunk
688 : m_.chunk.trailer = {};
689 : m_.payload_left =
690 : v.size - v.data.size();
691 : size_ += it - start; // excludes CRLF
692 : return;
693 : }
694 : // last-chunk
695 : BOOST_ASSERT(
696 : v.data.empty());
697 : m_.chunk.trailer =
698 : v.trailer;
699 : m_.body = {};
700 : size_ += it - start; // excludes CRLF
701 : st_ = state::complete;
702 : }
703 : else
704 : {
705 : // continuation of chunk
706 :
707 : }
708 : #endif
709 0 : }
710 :
711 : } // http_proto
712 : } // boost
713 :
714 : #endif
|