123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 |
- //
- // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- // Official repository: https://github.com/boostorg/beast
- //
- #ifndef BOOST_BEAST_HTTP_NODEJS_PARSER_HPP
- #define BOOST_BEAST_HTTP_NODEJS_PARSER_HPP
- #include "nodejs-parser/http_parser.h"
- #include <boost/beast/http/message.hpp>
- #include <boost/beast/http/rfc7230.hpp>
- #include <boost/beast/core/buffers_range.hpp>
- #include <boost/beast/core/error.hpp>
- #include <boost/asio/buffer.hpp>
- #include <boost/system/error_code.hpp>
- #include <boost/throw_exception.hpp>
- #include <cstdint>
- #include <string>
- #include <type_traits>
- #include <utility>
- namespace boost {
- namespace beast {
- namespace http {
- namespace detail {
- class nodejs_message_category final
- : public boost::system::error_category
- {
- public:
- const char*
- name() const noexcept override
- {
- return "nodejs-http-error";
- }
- std::string
- message(int ev) const override
- {
- return http_errno_description(
- static_cast<http_errno>(ev));
- }
- boost::system::error_condition
- default_error_condition(int ev) const noexcept override
- {
- return boost::system::error_condition{ev, *this};
- }
- bool
- equivalent(int ev,
- boost::system::error_condition const& condition
- ) const noexcept override
- {
- return condition.value() == ev &&
- &condition.category() == this;
- }
- bool
- equivalent(boost::system::error_code const& error,
- int ev) const noexcept override
- {
- return error.value() == ev &&
- &error.category() == this;
- }
- };
- template<class = void>
- boost::system::error_code
- make_nodejs_error(int http_errno)
- {
- static nodejs_message_category const mc{};
- return boost::system::error_code{http_errno, mc};
- }
- inline
- char const*
- method_to_string(unsigned method)
- {
- using namespace beast;
- switch(static_cast<http_method>(method))
- {
- case HTTP_DELETE: return "DELETE";
- case HTTP_GET: return "GET";
- case HTTP_HEAD: return "HEAD";
- case HTTP_POST: return "POST";
- case HTTP_PUT: return "PUT";
- // pathological
- case HTTP_CONNECT: return "CONNECT";
- case HTTP_OPTIONS: return "OPTIONS";
- case HTTP_TRACE: return "TRACE";
- // webdav
- case HTTP_COPY: return "COPY";
- case HTTP_LOCK: return "LOCK";
- case HTTP_MKCOL: return "MKCOL";
- case HTTP_MOVE: return "MOVE";
- case HTTP_PROPFIND: return "PROPFIND";
- case HTTP_PROPPATCH: return "PROPPATCH";
- case HTTP_SEARCH: return "SEARCH";
- case HTTP_UNLOCK: return "UNLOCK";
- case HTTP_BIND: return "BIND";
- case HTTP_REBIND: return "REBIND";
- case HTTP_UNBIND: return "UNBIND";
- case HTTP_ACL: return "ACL";
- // subversion
- case HTTP_REPORT: return "REPORT";
- case HTTP_MKACTIVITY: return "MKACTIVITY";
- case HTTP_CHECKOUT: return "CHECKOUT";
- case HTTP_MERGE: return "MERGE";
- // upnp
- case HTTP_MSEARCH: return "MSEARCH";
- case HTTP_NOTIFY: return "NOTIFY";
- case HTTP_SUBSCRIBE: return "SUBSCRIBE";
- case HTTP_UNSUBSCRIBE: return "UNSUBSCRIBE";
- // RFC-5789
- case HTTP_PATCH: return "PATCH";
- case HTTP_PURGE: return "PURGE";
- // CalDav
- case HTTP_MKCALENDAR: return "MKCALENDAR";
- // RFC-2068, section 19.6.1.2
- case HTTP_LINK: return "LINK";
- case HTTP_UNLINK: return "UNLINK";
- };
- return "<unknown>";
- }
- } // detail
- template<class Derived>
- class nodejs_basic_parser
- {
- http_parser state_;
- boost::system::error_code* ec_;
- bool complete_ = false;
- std::string url_;
- std::string status_;
- std::string field_;
- std::string value_;
- public:
- using error_code = boost::system::error_code;
- nodejs_basic_parser(nodejs_basic_parser&& other);
- nodejs_basic_parser&
- operator=(nodejs_basic_parser&& other);
- nodejs_basic_parser(nodejs_basic_parser const& other);
- nodejs_basic_parser& operator=(nodejs_basic_parser const& other);
- explicit
- nodejs_basic_parser(bool request) noexcept;
- bool
- complete() const noexcept
- {
- return complete_;
- }
- std::size_t
- write(void const* data, std::size_t size)
- {
- error_code ec;
- auto const used = write(data, size, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
- return used;
- }
- std::size_t
- write(void const* data, std::size_t size,
- error_code& ec);
- template<class ConstBufferSequence>
- std::size_t
- write(ConstBufferSequence const& buffers)
- {
- error_code ec;
- auto const used = write(buffers, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
- return used;
- }
- template<class ConstBufferSequence>
- std::size_t
- write(ConstBufferSequence const& buffers,
- error_code& ec);
- void
- write_eof()
- {
- error_code ec;
- write_eof(ec);
- if(ec)
- BOOST_THROW_EXCEPTION(system_error{ec});
- }
- void
- write_eof(error_code& ec);
- void
- check_header();
- private:
- Derived&
- impl()
- {
- return *static_cast<Derived*>(this);
- }
- static int cb_message_start(http_parser*);
- static int cb_url(http_parser*, char const*, std::size_t);
- static int cb_status(http_parser*, char const*, std::size_t);
- static int cb_header_field(http_parser*, char const*, std::size_t);
- static int cb_header_value(http_parser*, char const*, std::size_t);
- static int cb_headers_complete(http_parser*);
- static int cb_body(http_parser*, char const*, std::size_t);
- static int cb_message_complete(http_parser*);
- static int cb_chunk_header(http_parser*);
- static int cb_chunk_complete(http_parser*);
- struct hooks_t : http_parser_settings
- {
- hooks_t()
- {
- http_parser_settings_init(this);
- on_message_begin = &nodejs_basic_parser::cb_message_start;
- on_url = &nodejs_basic_parser::cb_url;
- on_status = &nodejs_basic_parser::cb_status;
- on_header_field = &nodejs_basic_parser::cb_header_field;
- on_header_value = &nodejs_basic_parser::cb_header_value;
- on_headers_complete = &nodejs_basic_parser::cb_headers_complete;
- on_body = &nodejs_basic_parser::cb_body;
- on_message_complete = &nodejs_basic_parser::cb_message_complete;
- on_chunk_header = &nodejs_basic_parser::cb_chunk_header;
- on_chunk_complete = &nodejs_basic_parser::cb_chunk_complete;
- }
- };
- static
- http_parser_settings const*
- hooks();
- };
- template<class Derived>
- template<class ConstBufferSequence>
- std::size_t
- nodejs_basic_parser<Derived>::write(
- ConstBufferSequence const& buffers, error_code& ec)
- {
- static_assert(net::is_const_buffer_sequence<
- ConstBufferSequence>::value,
- "ConstBufferSequence type requirements not met");
- std::size_t bytes_used = 0;
- for(auto buffer : beast::buffers_range_ref(buffers))
- {
- auto const n = write(
- static_cast<void const*>(buffer.data()),
- buffer.size(), ec);
- if(ec)
- return 0;
- bytes_used += n;
- if(complete())
- break;
- }
- return bytes_used;
- }
- template<class Derived>
- http_parser_settings const*
- nodejs_basic_parser<Derived>::hooks()
- {
- static hooks_t const h;
- return &h;
- }
- template<class Derived>
- nodejs_basic_parser<Derived>::
- nodejs_basic_parser(nodejs_basic_parser&& other)
- {
- state_ = other.state_;
- state_.data = this;
- complete_ = other.complete_;
- url_ = std::move(other.url_);
- status_ = std::move(other.status_);
- field_ = std::move(other.field_);
- value_ = std::move(other.value_);
- }
- template<class Derived>
- auto
- nodejs_basic_parser<Derived>::
- operator=(nodejs_basic_parser&& other) ->
- nodejs_basic_parser&
- {
- state_ = other.state_;
- state_.data = this;
- complete_ = other.complete_;
- url_ = std::move(other.url_);
- status_ = std::move(other.status_);
- field_ = std::move(other.field_);
- value_ = std::move(other.value_);
- return *this;
- }
- template<class Derived>
- nodejs_basic_parser<Derived>::
- nodejs_basic_parser(nodejs_basic_parser const& other)
- {
- state_ = other.state_;
- state_.data = this;
- complete_ = other.complete_;
- url_ = other.url_;
- status_ = other.status_;
- field_ = other.field_;
- value_ = other.value_;
- }
- template<class Derived>
- auto
- nodejs_basic_parser<Derived>::
- operator=(nodejs_basic_parser const& other) ->
- nodejs_basic_parser&
- {
- state_ = other.state_;
- state_.data = this;
- complete_ = other.complete_;
- url_ = other.url_;
- status_ = other.status_;
- field_ = other.field_;
- value_ = other.value_;
- return *this;
- }
- template<class Derived>
- nodejs_basic_parser<Derived>::
- nodejs_basic_parser(bool request) noexcept
- {
- state_.data = this;
- http_parser_init(&state_, request
- ? http_parser_type::HTTP_REQUEST
- : http_parser_type::HTTP_RESPONSE);
- }
- template<class Derived>
- std::size_t
- nodejs_basic_parser<Derived>::
- write(void const* data,
- std::size_t size, error_code& ec)
- {
- ec_ = &ec;
- auto const n = http_parser_execute(
- &state_, hooks(),
- static_cast<const char*>(data), size);
- if(! ec)
- ec = detail::make_nodejs_error(
- static_cast<int>(state_.http_errno));
- if(ec)
- return 0;
- return n;
- }
- template<class Derived>
- void
- nodejs_basic_parser<Derived>::
- write_eof(error_code& ec)
- {
- ec_ = &ec;
- http_parser_execute(&state_, hooks(), nullptr, 0);
- if(! ec)
- ec = detail::make_nodejs_error(
- static_cast<int>(state_.http_errno));
- }
- template<class Derived>
- void
- nodejs_basic_parser<Derived>::
- check_header()
- {
- if(! value_.empty())
- {
- impl().on_field(field_, value_);
- field_.clear();
- value_.clear();
- }
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_message_start(http_parser* p)
- {
- auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
- t.complete_ = false;
- t.url_.clear();
- t.status_.clear();
- t.field_.clear();
- t.value_.clear();
- t.impl().on_start();
- return 0;
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_url(http_parser* p,
- char const* in, std::size_t bytes)
- {
- auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
- t.url_.append(in, bytes);
- return 0;
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_status(http_parser* p,
- char const* in, std::size_t bytes)
- {
- auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
- t.status_.append(in, bytes);
- return 0;
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_header_field(http_parser* p,
- char const* in, std::size_t bytes)
- {
- auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
- t.check_header();
- t.field_.append(in, bytes);
- return 0;
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_header_value(http_parser* p,
- char const* in, std::size_t bytes)
- {
- auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
- t.value_.append(in, bytes);
- return 0;
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_headers_complete(http_parser* p)
- {
- auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
- t.check_header();
- t.impl().on_headers_complete(*t.ec_);
- if(*t.ec_)
- return 1;
- bool const keep_alive =
- http_should_keep_alive(p) != 0;
- if(p->type == http_parser_type::HTTP_REQUEST)
- {
- t.impl().on_request(p->method, t.url_,
- p->http_major, p->http_minor, keep_alive,
- p->upgrade);
- return 0;
- }
- return t.impl().on_response(p->status_code, t.status_,
- p->http_major, p->http_minor, keep_alive,
- p->upgrade) ? 0 : 1;
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_body(http_parser* p,
- char const* in, std::size_t bytes)
- {
- auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
- t.impl().on_body(in, bytes, *t.ec_);
- return *t.ec_ ? 1 : 0;
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_message_complete(http_parser* p)
- {
- auto& t = *reinterpret_cast<nodejs_basic_parser*>(p->data);
- t.complete_ = true;
- t.impl().on_complete();
- return 0;
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_chunk_header(http_parser*)
- {
- return 0;
- }
- template<class Derived>
- int
- nodejs_basic_parser<Derived>::
- cb_chunk_complete(http_parser*)
- {
- return 0;
- }
- //------------------------------------------------------------------------------
- /** A HTTP parser.
- The parser may only be used once.
- */
- template<bool isRequest, class Body, class Fields>
- class nodejs_parser
- : public nodejs_basic_parser<nodejs_parser<isRequest, Body, Fields>>
- {
- bool started_ = false;
- public:
- nodejs_parser(nodejs_parser&&) = default;
- nodejs_parser()
- : http::nodejs_basic_parser<nodejs_parser>(isRequest)
- {
- }
- /// Returns `true` if at least one byte has been processed
- bool
- started()
- {
- return started_;
- }
- private:
- friend class http::nodejs_basic_parser<nodejs_parser>;
- void
- on_start()
- {
- started_ = true;
- }
- void
- on_field(std::string const&, std::string const&)
- {
- }
- void
- on_headers_complete(error_code& ec)
- {
- // vFALCO TODO Decode the Content-Length and
- // Transfer-Encoding, see if we can reserve the buffer.
- //
- // r_.reserve(content_length)
- ec = {};
- }
- bool
- on_request(unsigned, std::string const&,
- int, int, bool, bool, std::true_type)
- {
- return true;
- }
- bool
- on_request(unsigned, std::string const&,
- int, int, bool, bool, std::false_type)
- {
- return true;
- }
- bool
- on_request(unsigned method, std::string const& url,
- int major, int minor, bool keep_alive, bool upgrade)
- {
- return on_request(method, url,
- major, minor, keep_alive, upgrade,
- std::integral_constant<
- bool, isRequest>{});
- }
- bool
- on_response(int, std::string const&,
- int, int, bool, bool, std::true_type)
- {
- return true;
- }
- bool
- on_response(int, std::string const&, int, int, bool, bool,
- std::false_type)
- {
- return true;
- }
- bool
- on_response(int status, std::string const& reason,
- int major, int minor, bool keep_alive, bool upgrade)
- {
- return on_response(
- status, reason, major, minor, keep_alive, upgrade,
- std::integral_constant<bool, ! isRequest>{});
- }
- void
- on_body(void const*, std::size_t, error_code& ec)
- {
- ec = {};
- }
- void
- on_complete()
- {
- }
- };
- } // http
- } // beast
- } // boost
- #endif
|