1//
2// Copyright (c) 2016-2019 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// 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
13#include <boost/beast/http/basic_parser.hpp>
14#include <boost/beast/http/error.hpp>
15#include <boost/beast/http/rfc7230.hpp>
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>
20#include <boost/asio/buffer.hpp>
21#include <algorithm>
22#include <utility>
23
24namespace boost {
25namespace beast {
26namespace http {
27
28template<bool isRequest>
29bool
30basic_parser<isRequest>::
31keep_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
47template<bool isRequest>
48boost::optional<std::uint64_t>
49basic_parser<isRequest>::
50content_length() const
51{
52    BOOST_ASSERT(is_header_done());
53    if(! (f_ & flagContentLength))
54        return boost::none;
55    return len0_;
56}
57
58template<bool isRequest>
59boost::optional<std::uint64_t>
60basic_parser<isRequest>::
61content_length_remaining() const
62{
63    BOOST_ASSERT(is_header_done());
64    if(! (f_ & flagContentLength))
65        return boost::none;
66    return len_;
67}
68
69template<bool isRequest>
70void
71basic_parser<isRequest>::
72skip(bool v)
73{
74    BOOST_ASSERT(! got_some());
75    if(v)
76        f_ |= flagSkipBody;
77    else
78        f_ &= ~flagSkipBody;
79}
80
81template<bool isRequest>
82std::size_t
83basic_parser<isRequest>::
84put(net::const_buffer buffer,
85    error_code& ec)
86{
87    BOOST_ASSERT(state_ != state::complete);
88    auto p = static_cast<char const*>(buffer.data());
89    auto n = buffer.size();
90    auto const p0 = p;
91    auto const p1 = p0 + n;
92    ec = {};
93loop:
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;
103        BOOST_FALLTHROUGH;
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        }
134        BOOST_FALLTHROUGH;
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_);
163        this->on_body_init_impl(content_length(), ec);
164        if(ec)
165            goto done;
166        state_ = state::body;
167        BOOST_FALLTHROUGH;
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_);
178        this->on_body_init_impl(content_length(), ec);
179        if(ec)
180            goto done;
181        state_ = state::body_to_eof;
182        BOOST_FALLTHROUGH;
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:
192        this->on_body_init_impl(content_length(), ec);
193        if(ec)
194            goto done;
195        state_ = state::chunk_header;
196        BOOST_FALLTHROUGH;
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:
211        ec = {};
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    }
219done:
220    return static_cast<std::size_t>(p - p0);
221}
222
223template<bool isRequest>
224void
225basic_parser<isRequest>::
226put_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        }
242        ec = {};
243        return;
244    }
245    ec = {};
246    this->on_finish_impl(ec);
247    if(ec)
248        return;
249    state_ = state::complete;
250}
251
252template<bool isRequest>
253void
254basic_parser<isRequest>::
255maybe_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
284template<bool isRequest>
285void
286basic_parser<isRequest>::
287parse_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
332    this->on_request_impl(string_to_verb(method),
333        method, target, version, ec);
334    if(ec)
335        return;
336
337    in = p;
338    state_ = state::fields;
339}
340
341template<bool isRequest>
342void
343basic_parser<isRequest>::
344parse_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
390    this->on_response_impl(
391        status_, reason, version, ec);
392    if(ec)
393        return;
394
395    in = p;
396    state_ = state::fields;
397}
398
399template<bool isRequest>
400void
401basic_parser<isRequest>::
402parse_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
408    beast::detail::char_buffer<max_obs_fold> buf;
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;
431        this->on_field_impl(f, name, value, ec);
432        if(ec)
433            return;
434        in = p;
435    }
436}
437
438template<bool isRequest>
439void
440basic_parser<isRequest>::
441finish_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;
475        len0_ = 0;
476        state_ = state::complete;
477    }
478
479    ec = {};
480    this->on_header_impl(ec);
481    if(ec)
482        return;
483    if(state_ == state::complete)
484    {
485        this->on_finish_impl(ec);
486        if(ec)
487            return;
488    }
489}
490
491template<bool isRequest>
492void
493basic_parser<isRequest>::
494finish_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    {
511        if(len_ > 0)
512        {
513            f_ |= flagHasBody;
514            state_ = state::body0;
515
516            if(len_ > body_limit_)
517            {
518                ec = error::body_limit;
519                return;
520            }
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
539    ec = {};
540    this->on_header_impl(ec);
541    if(ec)
542        return;
543    if(state_ == state::complete)
544    {
545        this->on_finish_impl(ec);
546        if(ec)
547            return;
548    }
549}
550
551template<bool isRequest>
552void
553basic_parser<isRequest>::
554parse_body(char const*& p,
555    std::size_t n, error_code& ec)
556{
557    ec = {};
558    n = this->on_body_impl(string_view{p,
559        beast::detail::clamp(len_, n)}, ec);
560    p += n;
561    len_ -= n;
562    if(ec)
563        return;
564    if(len_ > 0)
565        return;
566    this->on_finish_impl(ec);
567    if(ec)
568        return;
569    state_ = state::complete;
570}
571
572template<bool isRequest>
573void
574basic_parser<isRequest>::
575parse_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;
584    ec = {};
585    n = this->on_body_impl(string_view{p, n}, ec);
586    p += n;
587    if(ec)
588        return;
589}
590
591template<bool isRequest>
592void
593basic_parser<isRequest>::
594parse_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);
669            this->on_chunk_header_impl(size, ext, ec);
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);
712    this->on_chunk_header_impl(0, ext, ec);
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
722    this->on_finish_impl(ec);
723    if(ec)
724        return;
725    state_ = state::complete;
726}
727
728template<bool isRequest>
729void
730basic_parser<isRequest>::
731parse_chunk_body(char const*& p,
732    std::size_t n, error_code& ec)
733{
734    ec = {};
735    n = this->on_chunk_body_impl(
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
744template<bool isRequest>
745void
746basic_parser<isRequest>::
747do_field(field f,
748    string_view value, error_code& ec)
749{
750    using namespace beast::detail::string_literals;
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        {
764            if(beast::iequals("close"_sv, s))
765            {
766                f_ |= flagConnectionClose;
767                continue;
768            }
769
770            if(beast::iequals("keep-alive"_sv, s))
771            {
772                f_ |= flagConnectionKeepAlive;
773                continue;
774            }
775
776            if(beast::iequals("upgrade"_sv, s))
777            {
778                f_ |= flagConnectionUpgrade;
779                continue;
780            }
781        }
782        ec = {};
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;
804        if(! parse_dec(value, v))
805        {
806            ec = error::bad_content_length;
807            return;
808        }
809
810        ec = {};
811        len_ = v;
812        len0_ = v;
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
834        ec = {};
835        auto const v = token_list{value};
836        auto const p = std::find_if(v.begin(), v.end(),
837            [&](string_view const& s)
838            {
839                return beast::iequals("chunked"_sv, s);
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    {
853        ec = {};
854        f_ |= flagUpgrade;
855        return;
856    }
857
858    ec = {};
859}
860
861#ifdef BOOST_BEAST_SOURCE
862template class http::basic_parser<true>;
863template class http::basic_parser<false>;
864#endif
865
866} // http
867} // beast
868} // boost
869
870#endif
871