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/url/grammar/ci_string.hpp>
18 : #include <boost/assert.hpp>
19 : #include <boost/none.hpp>
20 : #include <memory>
21 :
22 : namespace boost {
23 : namespace http_proto {
24 :
25 : /*
26 : Parser design:
27 :
28 : The usage of the parser is thus:
29 :
30 : pr.reset(); // prepare for a new stream
31 :
32 : pr.start(); // prepare for a new message
33 : pr.start_head_response(); // new message with no payload
34 :
35 : read_header( ..., pr );
36 : do
37 : {
38 : read_some(..., pr );
39 : }
40 : while(! got_header());
41 :
42 : pr.set_body( ... );
43 : // invalidates the headers? yes.
44 :
45 : read_body( ..., pr );
46 : while(! (pr.flags() &
47 : parser::is_done_bit ) );
48 : {
49 : read_some(..., pr );
50 : }
51 :
52 : If these are called out of order, an
53 : exception is thrown.
54 :
55 : Every call to `prepare` must be
56 : followed by a call to commit, reset,
57 : or the destructor.
58 : */
59 : //------------------------------------------------
60 :
61 745 : parser::
62 : parser(
63 : detail::kind k,
64 745 : config_base const& cfg)
65 : : cfg_(cfg)
66 745 : , h_(detail::empty{k})
67 : {
68 745 : }
69 :
70 : void
71 745 : parser::
72 : construct(
73 : std::size_t extra_buffer_size)
74 : {
75 : // headers_limit too large
76 745 : if( cfg_.headers_limit >
77 : BOOST_HTTP_PROTO_MAX_HEADER)
78 0 : detail::throw_invalid_argument();
79 :
80 : // start_line_limit too large
81 745 : if( cfg_.start_line_limit >=
82 745 : cfg_.headers_limit)
83 0 : detail::throw_invalid_argument();
84 :
85 : // field_size_limit too large
86 745 : if( cfg_.field_size_limit >=
87 745 : cfg_.headers_limit)
88 0 : detail::throw_invalid_argument();
89 :
90 : // fields_limit too large
91 745 : if( cfg_.fields_limit >
92 745 : cfg_.headers_limit / 4)
93 0 : detail::throw_invalid_argument();
94 :
95 : // largest space needed
96 : auto const bytes_needed =
97 745 : detail::header::bytes_needed(
98 : cfg_.headers_limit,
99 : cfg_.fields_limit);
100 :
101 : // prevent overflow
102 745 : if(extra_buffer_size >
103 745 : std::size_t(-1) - bytes_needed)
104 0 : detail::throw_invalid_argument();
105 :
106 : // allocate max headers plus extra
107 1490 : ws_ = detail::workspace(
108 : bytes_needed +
109 745 : extra_buffer_size);
110 :
111 745 : h_.cap = bytes_needed;
112 :
113 745 : reset();
114 745 : }
115 :
116 : //------------------------------------------------
117 : //
118 : // Special Members
119 : //
120 : //------------------------------------------------
121 :
122 745 : parser::
123 2980 : ~parser()
124 : {
125 745 : }
126 :
127 : parser::
128 : parser(
129 : parser&&) noexcept = default;
130 :
131 : //------------------------------------------------
132 : //
133 : // Observers
134 : //
135 : //------------------------------------------------
136 :
137 : string_view
138 0 : parser::
139 : body() const noexcept
140 : {
141 : #if 0
142 : // VFALCO How about some
143 : // asserts or exceptions?
144 : if(! m_.got_chunked)
145 : return string_view(
146 : h_.buf + h_.size,
147 : m_.n_payload);
148 : return string_view(
149 : h_.buf +
150 : h_.size +
151 : m_.n_chunk,
152 : m_.n_payload);
153 : #else
154 0 : return {};
155 : #endif
156 : }
157 :
158 : //------------------------------------------------
159 : //
160 : // Modifiers
161 : //
162 : //------------------------------------------------
163 :
164 : // prepare for a new stream
165 : void
166 745 : parser::
167 : reset() noexcept
168 : {
169 745 : st_ = state::need_start;
170 745 : got_eof_ = false;
171 745 : }
172 :
173 : void
174 770 : parser::
175 : start_impl(
176 : bool head_response)
177 : {
178 770 : std::size_t initial_size = 0;
179 770 : switch(st_)
180 : {
181 737 : default:
182 : case state::need_start:
183 737 : BOOST_ASSERT(h_.size == 0);
184 737 : BOOST_ASSERT(h_buf_.size() == 0);
185 737 : BOOST_ASSERT(! got_eof_);
186 737 : break;
187 :
188 0 : case state::headers:
189 : // Can't call start twice.
190 0 : detail::throw_logic_error();
191 :
192 0 : case state::headers_done:
193 : case state::body:
194 : // Can't call start with
195 : // an incomplete message.
196 0 : detail::throw_logic_error();
197 :
198 33 : case state::complete:
199 33 : if(h_buf_.size() > 0)
200 : {
201 : // headers with no body
202 33 : BOOST_ASSERT(h_.size > 0);
203 33 : h_buf_.consume(h_.size);
204 33 : initial_size = h_buf_.size();
205 : // move unused octets to front
206 33 : buffer_copy(
207 33 : mutable_buffer(
208 : ws_.data(),
209 : initial_size),
210 66 : h_buf_.data());
211 : }
212 : else
213 : {
214 : // leftover data after body
215 : }
216 33 : break;
217 : }
218 :
219 770 : ws_.clear();
220 :
221 : // set up header read buffer
222 770 : h_buf_ = {
223 : ws_.data(),
224 : cfg_.headers_limit,
225 : initial_size };
226 :
227 : // reset the header but
228 : // preserve the capacity
229 770 : auto const cap = h_.cap;
230 1540 : h_ = detail::header(
231 770 : detail::empty{h_.kind});
232 770 : h_.buf = reinterpret_cast<
233 770 : char*>(ws_.data());
234 770 : h_.cbuf = h_.buf;
235 770 : h_.cap = cap;
236 :
237 770 : cfg_impl_ = {};
238 770 : cfg_impl_.headers_limit = cfg_.headers_limit;
239 770 : cfg_impl_.start_line_limit = cfg_.start_line_limit;
240 770 : cfg_impl_.field_size_limit = cfg_.field_size_limit;
241 770 : cfg_impl_.fields_limit = cfg_.fields_limit;
242 :
243 770 : st_ = state::headers;
244 :
245 770 : BOOST_ASSERT(! head_response ||
246 : h_.kind == detail::kind::response);
247 770 : head_response_ = head_response;
248 770 : }
249 :
250 : auto
251 3185 : parser::
252 : prepare() ->
253 : mutable_buffers_type
254 : {
255 3185 : switch(st_)
256 : {
257 0 : default:
258 : case state::need_start:
259 : // start must be called once
260 : // before calling prepare.
261 0 : if(st_ == state::need_start)
262 0 : detail::throw_logic_error();
263 :
264 : case state::headers:
265 : // fill up to headers_limit
266 : return {
267 : h_buf_.prepare(
268 3185 : cfg_.headers_limit -
269 3185 : h_buf_.size()),
270 3185 : 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
|