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/url/grammar/ci_string.hpp>
16 : #include <boost/assert.hpp>
17 : #include <boost/none.hpp>
18 : #include <memory>
19 :
20 : namespace boost {
21 : namespace http_proto {
22 :
23 : //------------------------------------------------
24 :
25 707 : parser::
26 : parser(
27 : detail::kind k,
28 : config const& cfg,
29 707 : std::size_t buffer_bytes)
30 : : cfg_(cfg)
31 : , h_(detail::empty{k})
32 : , committed_(0)
33 : , state_(state::empty)
34 : , got_eof_(false)
35 707 : , m_{}
36 : {
37 : // buffer must be large enough to
38 : // hold a complete header.
39 707 : if( buffer_bytes <
40 707 : cfg.max_header_size)
41 707 : buffer_bytes =
42 : cfg.max_header_size;
43 :
44 707 : h_.buf = new char[buffer_bytes];
45 707 : h_.cbuf = h_.buf;
46 707 : h_.cap = buffer_bytes;
47 707 : h_.size = 0;
48 707 : h_.prefix = 0;
49 707 : }
50 :
51 : //------------------------------------------------
52 : //
53 : // Special Members
54 : //
55 : //------------------------------------------------
56 :
57 707 : parser::
58 707 : ~parser()
59 : {
60 707 : delete[] h_.buf;
61 707 : }
62 :
63 : //------------------------------------------------
64 : //
65 : // Observers
66 : //
67 : //------------------------------------------------
68 :
69 : string_view
70 0 : parser::
71 : body() const noexcept
72 : {
73 : // VFALCO How about some
74 : // asserts or exceptions?
75 0 : if(! m_.got_chunked)
76 0 : return string_view(
77 0 : h_.buf + h_.size,
78 0 : m_.n_payload);
79 0 : return string_view(
80 0 : h_.buf +
81 0 : h_.size +
82 0 : m_.n_chunk,
83 0 : m_.n_payload);
84 : }
85 :
86 : //------------------------------------------------
87 : //
88 : // Input
89 : //
90 : //------------------------------------------------
91 :
92 : void
93 0 : parser::
94 : clear() noexcept
95 : {
96 0 : reset();
97 0 : }
98 :
99 : void
100 0 : parser::
101 : reset()
102 : {
103 0 : if(got_eof_)
104 : {
105 : // new connection, throw
106 : // out all previous state.
107 0 : committed_ = 0;
108 0 : got_eof_ = false;
109 : }
110 : else
111 : {
112 : // Throwing out partial data
113 : // will desync the HTTP stream
114 : //BOOST_ASSERT(is_complete());
115 0 : if( committed_ > h_.size &&
116 0 : h_.size > 0)
117 : {
118 : // move unused octets to front
119 0 : std::memcpy(
120 0 : h_.buf,
121 0 : h_.buf + h_.size,
122 0 : committed_ - h_.size);
123 0 : committed_ -= h_.size;
124 0 : h_.size = 0;
125 : }
126 : else
127 : {
128 0 : committed_ = 0;
129 : }
130 : }
131 :
132 : // reset the header but
133 : // preserve the capacity
134 : detail::header h(
135 0 : detail::empty{h_.kind});
136 0 : h.assign_to(h_);
137 :
138 0 : m_ = message{};
139 0 : state_ = state::empty;
140 0 : }
141 :
142 : mutable_buffers
143 2969 : parser::
144 : prepare()
145 : {
146 : // Can't commit bytes after eof
147 2969 : BOOST_ASSERT(! got_eof_);
148 :
149 5938 : mb_ = {
150 2969 : h_.buf + committed_,
151 2969 : h_.cap - committed_ };
152 2969 : return { &mb_, 1 };
153 : }
154 :
155 : void
156 2969 : parser::
157 : commit(
158 : std::size_t n,
159 : error_code& ec)
160 : {
161 2969 : BOOST_ASSERT(! got_eof_);
162 2969 : BOOST_ASSERT(
163 : n <= h_.cap - committed_);
164 2969 : committed_ += n;
165 2969 : parse(ec);
166 2969 : }
167 :
168 : void
169 0 : parser::
170 : commit_eof(
171 : error_code &ec)
172 : {
173 0 : got_eof_ = true;
174 0 : parse(ec);
175 0 : }
176 :
177 : //------------------------------------------------
178 : //
179 : //
180 : //
181 : //------------------------------------------------
182 :
183 : void
184 0 : parser::
185 : discard_header() noexcept
186 : {
187 0 : }
188 :
189 : // discard all of the body,
190 : // but don't discard chunk-ext
191 : void
192 0 : parser::
193 : discard_body() noexcept
194 : {
195 0 : }
196 :
197 : // discard all of the payload
198 : // including any chunk-ext
199 : void
200 0 : parser::
201 : discard_chunk() noexcept
202 : {
203 0 : }
204 :
205 : //------------------------------------------------
206 : //
207 : // Implementation
208 : //
209 : //------------------------------------------------
210 :
211 : void
212 2969 : parser::
213 : parse(
214 : error_code& ec)
215 : {
216 2969 : ec.clear();
217 3548 : while(state_ != state::complete)
218 : {
219 2969 : switch(state_)
220 : {
221 707 : case state::empty:
222 707 : if(got_eof_)
223 : {
224 0 : BOOST_ASSERT(h_.size == 0);
225 0 : ec = error::end_of_stream;
226 0 : return;
227 : }
228 707 : state_ = state::start_line;
229 : BOOST_FALLTHROUGH;
230 :
231 1922 : case state::start_line:
232 1922 : parse_start_line(ec);
233 1922 : if(ec.failed())
234 1215 : return;
235 707 : state_ = state::header_fields;
236 : BOOST_FALLTHROUGH;
237 :
238 1754 : case state::header_fields:
239 1754 : parse_fields(ec);
240 1754 : if(ec.failed())
241 1175 : return;
242 579 : state_ = state::body;
243 : BOOST_FALLTHROUGH;
244 :
245 579 : case state::body:
246 579 : parse_body(ec);
247 579 : if(ec.failed())
248 0 : return;
249 579 : state_ = state::complete;
250 : BOOST_FALLTHROUGH;
251 :
252 579 : default:
253 579 : break;
254 : }
255 : }
256 : }
257 :
258 : void
259 1922 : parser::
260 : parse_start_line(
261 : error_code& ec)
262 : {
263 1922 : BOOST_ASSERT(h_.size == 0);
264 1922 : std::size_t const new_size =
265 1922 : committed_ <= cfg_.max_header_size
266 1922 : ? committed_
267 : : cfg_.max_header_size;
268 : string_view s(
269 1922 : this->h_.buf, new_size);
270 : auto rv =
271 1922 : detail::parse_start_line(h_, s);
272 1922 : if(rv == grammar::error::need_more)
273 : {
274 1215 : if( new_size <
275 1215 : cfg_.max_header_size)
276 : {
277 1215 : ec = rv.error();
278 1215 : return;
279 : }
280 0 : ec = error::header_too_large;
281 0 : return;
282 : }
283 707 : if(! rv)
284 : {
285 0 : ec = rv.error();
286 0 : return;
287 : }
288 707 : ec = {};
289 : }
290 :
291 : void
292 1754 : parser::
293 : parse_fields(
294 : error_code& ec)
295 : {
296 1754 : std::size_t const new_size =
297 1754 : committed_ <= cfg_.max_header_size
298 1754 : ? committed_
299 : : cfg_.max_header_size;
300 : field id;
301 1754 : string_view v;
302 : for(;;)
303 : {
304 2351 : auto const size0 = h_.size;
305 2351 : auto got_one = detail::parse_field(
306 2351 : h_, new_size, id, v, ec);
307 2351 : if(ec == grammar::error::need_more)
308 : {
309 1047 : if(new_size >=
310 1047 : cfg_.max_header_size)
311 0 : ec = error::header_too_large;
312 : }
313 2351 : if(! got_one)
314 1754 : return;
315 597 : if(h_.count > cfg_.max_field_count)
316 : {
317 0 : ec = error::too_many_fields;
318 0 : return;
319 : }
320 597 : if(h_.size >
321 597 : cfg_.max_field_size + size0)
322 : {
323 0 : ec = error::field_too_large;
324 0 : return;
325 : }
326 597 : switch(id)
327 : {
328 32 : case field::connection:
329 : case field::proxy_connection:
330 32 : do_connection(v, ec);
331 32 : if(ec.failed())
332 0 : return;
333 32 : break;
334 31 : case field::content_length:
335 31 : do_content_length(v, ec);
336 31 : if(ec.failed())
337 0 : return;
338 31 : break;
339 1 : case field::transfer_encoding:
340 1 : do_transfer_encoding(v, ec);
341 1 : if(ec.failed())
342 0 : return;
343 1 : break;
344 0 : case field::upgrade:
345 0 : do_upgrade(v, ec);
346 0 : if(ec.failed())
347 0 : return;
348 0 : break;
349 533 : default:
350 533 : break;
351 : }
352 597 : }
353 : }
354 :
355 : void
356 579 : parser::
357 : parse_body(
358 : error_code& ec)
359 : {
360 : (void)ec;
361 579 : return;
362 : // VFALCO TODO
363 : #if 0
364 : BOOST_ASSERT(state_ == state::body);
365 :
366 : if(h_.kind == detail::kind::request)
367 : {
368 : // https://tools.ietf.org/html/rfc7230#section-3.3
369 : if(m_.skip_body)
370 : return;
371 : if(m_.content_len.has_value())
372 : {
373 : if(*m_.content_len > cfg_.body_too_large)
374 : {
375 : ec = error::body_too_large;
376 : return;
377 : }
378 : if(*m_.content_len == 0)
379 : return;
380 : }
381 : else if(m_.got_chunked)
382 : {
383 : // VFALCO TODO
384 : return;
385 : }
386 : else
387 : {
388 : // Content-Length: 0
389 : return;
390 : }
391 : }
392 : else
393 : {
394 : BOOST_ASSERT(h_.kind ==
395 : detail::kind::response);
396 :
397 : // https://tools.ietf.org/html/rfc7230#section-3.3
398 : if((h_.res.status_int / 100 == 1) || // 1xx e.g. Continue
399 : h_.res.status_int == 204 || // No Content
400 : h_.res.status_int == 304) // Not Modified
401 : {
402 : // Content-Length may be present, but we
403 : // treat the message as not having a body.
404 : }
405 : else if(m_.content_len.has_value())
406 : {
407 : if(*m_.content_len > 0)
408 : {
409 : if(*m_.content_len > cfg_.body_too_large)
410 : {
411 : ec = error::body_too_large;
412 : return;
413 : }
414 : }
415 : }
416 : else
417 : {
418 : // No Content-Length
419 : return;
420 : }
421 : }
422 :
423 : auto avail = committed_ - size_;
424 : if(m_.content_len.has_value())
425 : {
426 : // known payload length
427 : BOOST_ASSERT(! m_.got_chunked);
428 : BOOST_ASSERT(m_.n_remain > 0);
429 : BOOST_ASSERT(m_.content_len <
430 : cfg_.body_too_large);
431 : if(avail == 0)
432 : {
433 : if(! got_eof_)
434 : {
435 : ec = grammar::error::need_more;
436 : return;
437 : }
438 : ec = error::need_more;
439 : return;
440 : }
441 : if( avail > m_.n_remain)
442 : avail = static_cast<
443 : std::size_t>(m_.n_remain);
444 : size_ += avail;
445 : m_.payload_seen += avail;
446 : m_.n_payload += avail;
447 : m_.n_remain -= avail;
448 : if(m_.n_remain > 0)
449 : {
450 : ec = {};
451 : return;
452 : }
453 : state_ = state::complete;
454 : ec = error::end_of_message;
455 : return;
456 : }
457 :
458 : if(! m_.got_chunked)
459 : {
460 : // end of body indicated by EOF
461 : if(avail > 0)
462 : {
463 : if(avail > (std::size_t(
464 : -1) - m_.n_payload))
465 : {
466 : // overflow size_t
467 : // VFALCO revisit this
468 : ec = error::numeric_overflow;
469 : return;
470 : }
471 : size_ += avail;
472 : m_.n_payload += avail;
473 : ec = {};
474 : return;
475 : }
476 : if(! got_eof_)
477 : {
478 : ec = grammar::error::need_more;
479 : return;
480 : }
481 : state_ = state::complete;
482 : ec = error::end_of_message;
483 : return;
484 : }
485 : #if 0
486 : if(m_.payload_left == 0)
487 : {
488 : // start of chunk
489 : bnf::chunk_part p;
490 : auto it = p.parse(
491 : h_.buf + size_,
492 : h_.buf + (
493 : committed_ - size_),
494 : ec);
495 : if(ec)
496 : return;
497 : auto const v =
498 : p.value();
499 : m_.chunk.size = v.size;
500 : m_.chunk.ext = v.ext;
501 : m_.chunk.trailer = v.trailer;
502 : m_.chunk.fresh = true;
503 : m_.payload_left =
504 : v.size - v.data.size();
505 : }
506 : else
507 : {
508 : // continuation of chunk
509 :
510 : }
511 : #endif
512 : #endif
513 : }
514 :
515 : void
516 0 : parser::
517 : parse_chunk(
518 : error_code& ec)
519 : {
520 : (void)ec;
521 : #if 0
522 : switch(state_)
523 : {
524 : case state::start_line:
525 : case state::header_fields:
526 : parse_header(ec);
527 : if(ec.failed())
528 : return;
529 : BOOST_ASSERT(state_ >
530 : state::header_fields);
531 : break;
532 : case state::body:
533 : if(! m_.got_chunked)
534 : return parse_body(ec);
535 : break;
536 : case state::complete:
537 : ec = error::end_of_message;
538 : if(! got_eof_)
539 : return;
540 : state_ = state::end_of_stream;
541 : return;
542 : case state::end_of_stream:
543 : ec = error::end_of_stream;
544 : return;
545 : }
546 :
547 : auto const avail = committed_ - size_;
548 : auto const start = h_.buf + size_;
549 : if(m_.payload_left == 0)
550 : {
551 : // start of chunk
552 : // VFALCO What about chunk_part_next?
553 : BOOST_ASSERT(
554 : size_ == m_.header_size);
555 : bnf::chunk_part p;
556 : auto it = p.parse(start,
557 : h_.buf + (
558 : committed_ - size_), ec);
559 : BOOST_ASSERT(it == start);
560 : if(ec)
561 : return;
562 : auto const v = p.value();
563 : m_.chunk.size = v.size;
564 : m_.chunk.ext = v.ext;
565 : m_.chunk.fresh = true;
566 : if(v.size > 0)
567 : {
568 : // chunk
569 : m_.chunk.trailer = {};
570 : m_.payload_left =
571 : v.size - v.data.size();
572 : size_ += it - start; // excludes CRLF
573 : return;
574 : }
575 : // last-chunk
576 : BOOST_ASSERT(
577 : v.data.empty());
578 : m_.chunk.trailer =
579 : v.trailer;
580 : m_.body = {};
581 : size_ += it - start; // excludes CRLF
582 : state_ = state::complete;
583 : }
584 : else
585 : {
586 : // continuation of chunk
587 :
588 : }
589 : #endif
590 0 : }
591 :
592 : //------------------------------------------------
593 :
594 : // https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
595 : void
596 32 : parser::
597 : do_connection(
598 : string_view s, error_code& ec)
599 : {
600 : (void)ec;
601 : (void)s;
602 : #if 0
603 : for(auto v : bnf::range<bnf::connection>(s))
604 : {
605 : if(grammar::ci_is_equal(v, "close"))
606 : {
607 : m_.got_close = true;
608 : }
609 : else if(grammar::ci_is_equal(v, "keep-alive"))
610 : {
611 : m_.got_keep_alive = true;
612 : }
613 : else if(grammar::ci_is_equal(v, "upgrade"))
614 : {
615 : m_.got_upgrade = true;
616 : }
617 : }
618 : #endif
619 32 : }
620 :
621 : void
622 31 : parser::
623 : do_content_length(
624 : string_view s,
625 : error_code& ec)
626 : {
627 : (void)ec;
628 : (void)s;
629 : #if 0
630 : // https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
631 : // Content-Length can be a comma separated
632 : // list, with all the same values, as well
633 : // as having multiple Content-Length fields
634 : // with the same number. We handle these here.
635 : auto list = bnf::range<
636 : bnf::list_of_one_or_more<
637 : bnf::dec_number>>(s);
638 : auto it = list.begin(ec);
639 : if(ec.failed())
640 : {
641 : ec = error::bad_content_length;
642 : return;
643 : }
644 : BOOST_ASSERT(it != list.end());
645 : auto v = *it;
646 : if(! m_.content_len.has_value())
647 : {
648 : if(v > cfg_.max_body_size)
649 : {
650 : ec = error::body_too_large;
651 : return;
652 : }
653 : m_.content_len = v;
654 : }
655 : else if(*m_.content_len != v)
656 : {
657 : // differing values
658 : ec = error::bad_content_length;
659 : return;
660 : }
661 : for(;;)
662 : {
663 : it.increment(ec);
664 : if(ec.failed())
665 : {
666 : ec = error::bad_content_length;
667 : return;
668 : }
669 : if(it == list.end())
670 : break;
671 : v = *it;
672 : if(m_.content_len != v)
673 : {
674 : // differing lengths
675 : ec = error::bad_content_length;
676 : return;
677 : }
678 : }
679 : if(! m_.skip_body)
680 : m_.n_remain = *m_.content_len;
681 : #endif
682 31 : }
683 :
684 : void
685 1 : parser::
686 : do_transfer_encoding(
687 : string_view s, error_code& ec)
688 : {
689 : (void)s;
690 : (void)ec;
691 : #if 0
692 : using namespace detail::string_literals;
693 :
694 : bnf::range<bnf::transfer_encoding> te(s);
695 : auto const end = te.end();
696 : auto it = te.begin(ec);
697 : if(ec.failed())
698 : {
699 : // expected at least one encoding
700 : ec = error::bad_transfer_encoding;
701 : return;
702 : }
703 : BOOST_ASSERT(it != end);
704 : // handle multiple
705 : // Transfer-Encoding header lines
706 : m_.got_chunked = false;
707 : // get last encoding
708 : for(;;)
709 : {
710 : auto prev = it++;
711 : if(it == end)
712 : {
713 : if(grammar::ci_is_equal(
714 : prev->name, "chunked"))
715 : m_.got_chunked = true;
716 : break;
717 : }
718 : }
719 : #endif
720 1 : }
721 :
722 : void
723 0 : parser::
724 : do_upgrade(
725 : string_view s, error_code& ec)
726 : {
727 : (void)s;
728 : (void)ec;
729 0 : }
730 :
731 : } // http_proto
732 : } // boost
733 :
734 : #endif
|