summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/beast/test/bench/parser/nodejs_parser.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/beast/test/bench/parser/nodejs_parser.hpp')
-rw-r--r--src/boost/libs/beast/test/bench/parser/nodejs_parser.hpp640
1 files changed, 640 insertions, 0 deletions
diff --git a/src/boost/libs/beast/test/bench/parser/nodejs_parser.hpp b/src/boost/libs/beast/test/bench/parser/nodejs_parser.hpp
new file mode 100644
index 000000000..68afd6f5d
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/nodejs_parser.hpp
@@ -0,0 +1,640 @@
+//
+// 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