summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/beast/test/bench
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
commit483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch)
treee5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/boost/libs/beast/test/bench
parentInitial commit. (diff)
downloadceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.tar.xz
ceph-483eb2f56657e8e7f419ab1a4fab8dce9ade8609.zip
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/beast/test/bench')
-rw-r--r--src/boost/libs/beast/test/bench/CMakeLists.txt14
-rw-r--r--src/boost/libs/beast/test/bench/Jamfile16
-rw-r--r--src/boost/libs/beast/test/bench/buffers/CMakeLists.txt25
-rw-r--r--src/boost/libs/beast/test/bench/buffers/Jamfile19
-rw-r--r--src/boost/libs/beast/test/bench/buffers/bench_buffers.cpp241
-rw-r--r--src/boost/libs/beast/test/bench/parser/CMakeLists.txt29
-rw-r--r--src/boost/libs/beast/test/bench/parser/Jamfile22
-rw-r--r--src/boost/libs/beast/test/bench/parser/bench_parser.cpp346
-rw-r--r--src/boost/libs/beast/test/bench/parser/nodejs-parser/AUTHORS68
-rw-r--r--src/boost/libs/beast/test/bench/parser/nodejs-parser/LICENSE-MIT23
-rw-r--r--src/boost/libs/beast/test/bench/parser/nodejs-parser/README.md246
-rw-r--r--src/boost/libs/beast/test/bench/parser/nodejs-parser/http_parser.c2468
-rw-r--r--src/boost/libs/beast/test/bench/parser/nodejs-parser/http_parser.h362
-rw-r--r--src/boost/libs/beast/test/bench/parser/nodejs_parser.cpp31
-rw-r--r--src/boost/libs/beast/test/bench/parser/nodejs_parser.hpp640
-rw-r--r--src/boost/libs/beast/test/bench/utf8_checker/CMakeLists.txt25
-rw-r--r--src/boost/libs/beast/test/bench/utf8_checker/Jamfile19
-rw-r--r--src/boost/libs/beast/test/bench/utf8_checker/bench_utf8_checker.cpp157
-rw-r--r--src/boost/libs/beast/test/bench/wsload/CMakeLists.txt24
-rw-r--r--src/boost/libs/beast/test/bench/wsload/Jamfile18
-rw-r--r--src/boost/libs/beast/test/bench/wsload/wsload.cpp338
-rw-r--r--src/boost/libs/beast/test/bench/zlib/CMakeLists.txt31
-rw-r--r--src/boost/libs/beast/test/bench/zlib/Jamfile15
-rw-r--r--src/boost/libs/beast/test/bench/zlib/deflate_stream.cpp199
-rw-r--r--src/boost/libs/beast/test/bench/zlib/inflate_stream.cpp244
25 files changed, 5620 insertions, 0 deletions
diff --git a/src/boost/libs/beast/test/bench/CMakeLists.txt b/src/boost/libs/beast/test/bench/CMakeLists.txt
new file mode 100644
index 00000000..849f032a
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/CMakeLists.txt
@@ -0,0 +1,14 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+add_subdirectory (buffers)
+add_subdirectory (parser)
+add_subdirectory (utf8_checker)
+add_subdirectory (wsload)
+add_subdirectory (zlib)
diff --git a/src/boost/libs/beast/test/bench/Jamfile b/src/boost/libs/beast/test/bench/Jamfile
new file mode 100644
index 00000000..44bae113
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/Jamfile
@@ -0,0 +1,16 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+alias run-tests :
+ buffers//run-tests
+ parser//run-tests
+ wsload//run-tests
+ utf8_checker//run-tests
+ #zlib//run-tests # Not built, too slow
+ ;
diff --git a/src/boost/libs/beast/test/bench/buffers/CMakeLists.txt b/src/boost/libs/beast/test/bench/buffers/CMakeLists.txt
new file mode 100644
index 00000000..d05db11f
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/buffers/CMakeLists.txt
@@ -0,0 +1,25 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+GroupSources (include/boost/beast beast)
+GroupSources (test/bench/buffers "/")
+
+add_executable (bench-buffers
+ ${BOOST_BEAST_FILES}
+ Jamfile
+ bench_buffers.cpp
+)
+
+target_link_libraries(bench-buffers
+ lib-asio
+ lib-beast
+ lib-test
+ )
+
+set_property(TARGET bench-buffers PROPERTY FOLDER "tests-bench")
diff --git a/src/boost/libs/beast/test/bench/buffers/Jamfile b/src/boost/libs/beast/test/bench/buffers/Jamfile
new file mode 100644
index 00000000..c73d8a11
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/buffers/Jamfile
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+exe bench-buffers :
+ bench_buffers.cpp
+ /boost/beast/test//lib-test
+ ;
+
+explicit bench-buffers ;
+
+alias run-tests :
+ [ compile bench_buffers.cpp ]
+ ;
diff --git a/src/boost/libs/beast/test/bench/buffers/bench_buffers.cpp b/src/boost/libs/beast/test/bench/buffers/bench_buffers.cpp
new file mode 100644
index 00000000..fdd00a47
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/buffers/bench_buffers.cpp
@@ -0,0 +1,241 @@
+//
+// 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
+//
+
+#include <boost/beast/core/buffers_range.hpp>
+#include <boost/beast/core/flat_buffer.hpp>
+#include <boost/beast/core/multi_buffer.hpp>
+#include <boost/beast/core/read_size.hpp>
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/_experimental/unit_test/suite.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <algorithm>
+#include <chrono>
+#include <cstdlib>
+#include <iomanip>
+#include <utility>
+#include <vector>
+
+namespace boost {
+namespace beast {
+
+class buffers_test : public beast::unit_test::suite
+{
+public:
+ using size_type = std::uint64_t;
+
+ class timer
+ {
+ using clock_type =
+ std::chrono::system_clock;
+
+ clock_type::time_point when_;
+
+ public:
+ using duration =
+ clock_type::duration;
+
+ timer()
+ : when_(clock_type::now())
+ {
+ }
+
+ duration
+ elapsed() const
+ {
+ return clock_type::now() - when_;
+ }
+ };
+
+ inline
+ size_type
+ throughput(std::chrono::duration<
+ double> const& elapsed, size_type items)
+ {
+ using namespace std::chrono;
+ return static_cast<size_type>(
+ 1 / (elapsed/items).count());
+ }
+
+ template<class MutableBufferSequence>
+ static
+ std::size_t
+ fill(MutableBufferSequence const& buffers)
+ {
+ std::size_t n = 0;
+ for(auto b : beast::buffers_range_ref(buffers))
+ {
+ std::fill(
+ static_cast<char*>(b.data()),
+ static_cast<char*>(b.data()) +
+ b.size(), '\0');
+ n += b.size();
+ }
+ return n;
+ }
+
+ template<class DynamicBuffer>
+ size_type
+ do_prepares(std::size_t repeat,
+ std::size_t count, std::size_t size)
+ {
+ timer t;
+ size_type total = 0;
+ for(auto i = repeat; i--;)
+ {
+ DynamicBuffer b;
+ for(auto j = count; j--;)
+ {
+ auto const n = fill(b.prepare(size));
+ b.commit(n);
+ total += n;
+ }
+ }
+ return throughput(t.elapsed(), total);
+ }
+
+ template<class DynamicBuffer>
+ size_type
+ do_hints(std::size_t repeat,
+ std::size_t count, std::size_t size)
+ {
+ timer t;
+ size_type total = 0;
+ for(auto i = repeat; i--;)
+ {
+ DynamicBuffer b;
+ for(auto j = count; j--;)
+ {
+ for(auto remain = size; remain;)
+ {
+ auto const n = fill(b.prepare(
+ read_size(b, remain)));
+ b.commit(n);
+ remain -= n;
+ total += n;
+ }
+ }
+ }
+ return throughput(t.elapsed(), total);
+ }
+
+ template<class DynamicBuffer>
+ size_type
+ do_random(std::size_t repeat,
+ std::size_t count, std::size_t size)
+ {
+ timer t;
+ size_type total = 0;
+ for(auto i = repeat; i--;)
+ {
+ DynamicBuffer b;
+ for(auto j = count; j--;)
+ {
+ auto const n = fill(b.prepare(
+ 1 + (rand()%(2*size))));
+ b.commit(n);
+ total += n;
+ }
+ }
+ return throughput(t.elapsed(), total);
+ }
+
+ static
+ inline
+ void
+ do_trials_1(bool)
+ {
+ }
+
+ template<class F0, class... FN>
+ void
+ do_trials_1(bool print, F0&& f, FN... fn)
+ {
+ timer t;
+ using namespace std::chrono;
+ static size_type constexpr den = 1024 * 1024;
+ if(print)
+ {
+ log << std::right << std::setw(10) <<
+ ((f() + (den / 2)) / den) << " MB/s";
+ log.flush();
+ }
+ else
+ {
+ f();
+ }
+ do_trials_1(print, fn...);
+ }
+
+ template<class F0, class... FN>
+ void
+ do_trials(string_view name,
+ std::size_t trials, F0&& f0, FN... fn)
+ {
+ using namespace std::chrono;
+ // warm-up
+ do_trials_1(false, f0, fn...);
+ do_trials_1(false, f0, fn...);
+ while(trials--)
+ {
+ timer t;
+ log << std::left << std::setw(24) << name << ":";
+ log.flush();
+ do_trials_1(true, f0, fn...);
+ log << " " <<
+ duration_cast<milliseconds>(t.elapsed()).count() << "ms";
+ log << std::endl;
+ }
+ }
+
+ void
+ run() override
+ {
+ static std::size_t constexpr trials = 1;
+ static std::size_t constexpr repeat = 250;
+ std::vector<std::pair<std::size_t, std::size_t>> params;
+ params.emplace_back(1024, 1024);
+ params.emplace_back(512, 4096);
+ params.emplace_back(256, 32768);
+ log << std::endl;
+ for(auto const& param : params)
+ {
+ auto const count = param.first;
+ auto const size = param.second;
+ auto const s = std::string("count=") + std::to_string(count) +
+ ", size=" + std::to_string(size);
+ log << std::left << std::setw(24) << s << " " <<
+ std::right << std::setw(15) << "prepare" <<
+ std::right << std::setw(15) << "with hint" <<
+ std::right << std::setw(15) << "random" <<
+ std::endl;
+ do_trials("multi_buffer", trials,
+ [&](){ return do_prepares<multi_buffer>(repeat, count, size); }
+ ,[&](){ return do_hints <multi_buffer>(repeat, count, size); }
+ ,[&](){ return do_random <multi_buffer>(repeat, count, size); }
+ );
+ do_trials("flat_buffer", trials,
+ [&](){ return do_prepares<flat_buffer>(repeat, count, size); }
+ ,[&](){ return do_hints <flat_buffer>(repeat, count, size); }
+ ,[&](){ return do_random <flat_buffer>(repeat, count, size); }
+ );
+ do_trials("net::streambuf", trials,
+ [&](){ return do_prepares<net::streambuf>(repeat, count, size); }
+ ,[&](){ return do_hints <net::streambuf>(repeat, count, size); }
+ ,[&](){ return do_random <net::streambuf>(repeat, count, size); }
+ );
+ log << std::endl;
+ }
+ pass();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(beast,benchmarks,buffers);
+
+} // beast
+} // boost
diff --git a/src/boost/libs/beast/test/bench/parser/CMakeLists.txt b/src/boost/libs/beast/test/bench/parser/CMakeLists.txt
new file mode 100644
index 00000000..4a4eb2ce
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/CMakeLists.txt
@@ -0,0 +1,29 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+GroupSources (include/boost/beast beast)
+GroupSources (test/beast/http "/")
+GroupSources (test/bench/parser "/")
+
+add_executable (bench-parser
+ ${BOOST_BEAST_FILES}
+ Jamfile
+ ${PROJECT_SOURCE_DIR}/test/beast/http/message_fuzz.hpp
+ nodejs_parser.hpp
+ nodejs_parser.cpp
+ bench_parser.cpp
+)
+
+target_link_libraries(bench-parser
+ lib-asio
+ lib-beast
+ lib-test
+ )
+
+set_property(TARGET bench-parser PROPERTY FOLDER "tests-bench")
diff --git a/src/boost/libs/beast/test/bench/parser/Jamfile b/src/boost/libs/beast/test/bench/parser/Jamfile
new file mode 100644
index 00000000..43629fc3
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/Jamfile
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+run
+ nodejs_parser.cpp
+ bench_parser.cpp
+ /boost/beast/test//lib-test
+ : : : :
+ bench-parser ;
+
+explicit bench-parser ;
+
+alias run-tests :
+ [ compile nodejs_parser.cpp ]
+ [ compile bench_parser.cpp ]
+ ;
diff --git a/src/boost/libs/beast/test/bench/parser/bench_parser.cpp b/src/boost/libs/beast/test/bench/parser/bench_parser.cpp
new file mode 100644
index 00000000..67cc99a0
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/bench_parser.cpp
@@ -0,0 +1,346 @@
+//
+// 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
+//
+
+#include "nodejs_parser.hpp"
+
+#include "test/beast/http/message_fuzz.hpp"
+
+#include <boost/beast/http.hpp>
+#include <boost/beast/core/buffer_traits.hpp>
+#include <boost/beast/core/buffers_suffix.hpp>
+#include <boost/beast/core/buffers_to_string.hpp>
+#include <boost/beast/core/ostream.hpp>
+#include <boost/beast/core/flat_buffer.hpp>
+#include <boost/beast/core/multi_buffer.hpp>
+#include <boost/beast/_experimental/unit_test/suite.hpp>
+#include <chrono>
+#include <iostream>
+#include <vector>
+
+namespace boost {
+namespace beast {
+namespace http {
+
+class parser_test : public beast::unit_test::suite
+{
+public:
+ static std::size_t constexpr N = 2000;
+
+ //using corpus = std::vector<multi_buffer>;
+ using corpus = std::vector<flat_buffer>;
+
+ corpus creq_;
+ corpus cres_;
+ std::size_t size_ = 0;
+
+ corpus
+ build_corpus(std::size_t n, std::true_type)
+ {
+ corpus v;
+ v.resize(n);
+ message_fuzz mg;
+ for(std::size_t i = 0; i < n; ++i)
+ {
+ mg.request(v[i]);
+ size_ += v[i].size();
+ BEAST_EXPECT(v[i].size() > 0);
+ }
+ return v;
+ }
+
+ corpus
+ build_corpus(std::size_t n, std::false_type)
+ {
+ corpus v;
+ v.resize(n);
+ message_fuzz mg;
+ for(std::size_t i = 0; i < n; ++i)
+ {
+ mg.response(v[i]);
+ size_ += v[i].size();
+ BEAST_EXPECT(v[i].size() > 0);
+ }
+ return v;
+ }
+
+ template<class ConstBufferSequence,
+ bool isRequest>
+ static
+ std::size_t
+ feed(ConstBufferSequence const& buffers,
+ basic_parser<isRequest>& parser,
+ error_code& ec)
+ {
+ beast::buffers_suffix<
+ ConstBufferSequence> cb{buffers};
+ std::size_t used = 0;
+ for(;;)
+ {
+ auto const n =
+ parser.put(cb, ec);
+ if(ec)
+ return 0;
+ if(n == 0)
+ break;
+ cb.consume(n);
+ used += n;
+ if(parser.is_done())
+ break;
+ if(buffer_bytes(cb) == 0)
+ break;
+ }
+ return used;
+ }
+
+ template<class Parser>
+ void
+ testParser1(std::size_t repeat, corpus const& v)
+ {
+ while(repeat--)
+ for(auto const& b : v)
+ {
+ Parser p;
+ error_code ec;
+ p.write(b.data(), ec);
+ if(! BEAST_EXPECTS(! ec, ec.message()))
+ log << buffers_to_string(b.data()) << std::endl;
+ }
+ }
+
+ template<class Parser>
+ void
+ testParser2(std::size_t repeat, corpus const& v)
+ {
+ while(repeat--)
+ for(auto const& b : v)
+ {
+ Parser p;
+ p.header_limit((std::numeric_limits<std::uint32_t>::max)());
+ error_code ec;
+ feed(b.data(), p, ec);
+ if(! BEAST_EXPECTS(! ec, ec.message()))
+ log << buffers_to_string(b.data()) << std::endl;
+ }
+ }
+
+ template<class Function>
+ void
+ timedTest(std::size_t repeat, std::string const& name, Function&& f)
+ {
+ using namespace std::chrono;
+ using clock_type = std::chrono::high_resolution_clock;
+ log << name << std::endl;
+ for(std::size_t trial = 1; trial <= repeat; ++trial)
+ {
+ auto const t0 = clock_type::now();
+ f();
+ auto const elapsed = clock_type::now() - t0;
+ log <<
+ "Trial " << trial << ": " <<
+ duration_cast<milliseconds>(elapsed).count() << " ms" << std::endl;
+ }
+ }
+
+ template<bool isRequest>
+ struct null_parser :
+ basic_parser<isRequest>
+ {
+ void
+ on_request_impl(
+ verb, string_view, string_view,
+ int, error_code&) override
+ {
+ }
+
+ void
+ on_response_impl(
+ int, string_view, int,
+ error_code&) override
+ {
+ }
+
+ void
+ on_field_impl(
+ field, string_view, string_view,
+ error_code&) override
+ {
+ }
+
+ void
+ on_header_impl(error_code&) override
+ {
+ }
+
+ void
+ on_body_init_impl(
+ boost::optional<std::uint64_t> const&,
+ error_code&) override
+ {
+ }
+
+ std::size_t
+ on_body_impl(
+ string_view,
+ error_code&) override
+ {
+ return 0;
+ }
+
+ void
+ on_chunk_header_impl(
+ std::uint64_t,
+ string_view,
+ error_code&) override
+ {
+ }
+
+ std::size_t
+ on_chunk_body_impl(
+ std::uint64_t,
+ string_view,
+ error_code&) override
+ {
+ return 0;
+ }
+
+ void
+ on_finish_impl(error_code&) override
+ {
+ }
+ };
+
+ template<bool isRequest, class Body, class Fields>
+ struct bench_parser : basic_parser<isRequest>
+ {
+ using mutable_buffers_type =
+ net::mutable_buffer;
+
+ void
+ on_request_impl(verb, string_view,
+ string_view, int, error_code&) override
+ {
+ }
+
+ void
+ on_response_impl(int,
+ string_view, int, error_code&) override
+ {
+ }
+
+ void
+ on_field_impl(field,
+ string_view, string_view, error_code&) override
+ {
+ }
+
+ void
+ on_header_impl(error_code&) override
+ {
+ }
+
+ void
+ on_body_init_impl(
+ boost::optional<std::uint64_t> const&,
+ error_code&) override
+ {
+ }
+
+ std::size_t
+ on_body_impl(
+ string_view s, error_code&) override
+ {
+ return s.size();
+ }
+
+ void
+ on_chunk_header_impl(std::uint64_t,
+ string_view, error_code&) override
+ {
+ }
+
+ std::size_t
+ on_chunk_body_impl(std::uint64_t,
+ string_view s, error_code&) override
+ {
+ return s.size();
+ }
+
+ void
+ on_finish_impl(error_code&) override
+ {
+ }
+ };
+
+ void
+ testSpeed()
+ {
+ static std::size_t constexpr Trials = 5;
+ static std::size_t constexpr Repeat = 500;
+
+ creq_ = build_corpus(N/2, std::true_type{});
+ cres_ = build_corpus(N/2, std::false_type{});
+
+ log << "sizeof(request parser) == " <<
+ sizeof(null_parser<true>) << '\n';
+
+ log << "sizeof(response parser) == " <<
+ sizeof(null_parser<false>)<< '\n';
+
+ testcase << "Parser speed test, " <<
+ ((Repeat * size_ + 512) / 1024) << "KB in " <<
+ (Repeat * (creq_.size() + cres_.size())) << " messages";
+
+#if 0
+ timedTest(Trials, "http::parser",
+ [&]
+ {
+ testParser2<request_parser<dynamic_body>>(Repeat, creq_);
+ testParser2<response_parser<dynamic_body>>(Repeat, cres_);
+ });
+#endif
+#if 1
+ timedTest(Trials, "http::basic_parser",
+ [&]
+ {
+ testParser2<bench_parser<
+ true, dynamic_body, fields> >(
+ Repeat, creq_);
+ testParser2<bench_parser<
+ false, dynamic_body, fields>>(
+ Repeat, cres_);
+ });
+#if 1
+ timedTest(Trials, "nodejs_parser",
+ [&]
+ {
+ testParser1<nodejs_parser<
+ true, dynamic_body, fields>>(
+ Repeat, creq_);
+ testParser1<nodejs_parser<
+ false, dynamic_body, fields>>(
+ Repeat, cres_);
+ });
+#endif
+#endif
+ pass();
+ }
+
+ void run() override
+ {
+ pass();
+ testSpeed();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(beast,benchmarks,parser);
+
+} // http
+} // beast
+} // boost
+
diff --git a/src/boost/libs/beast/test/bench/parser/nodejs-parser/AUTHORS b/src/boost/libs/beast/test/bench/parser/nodejs-parser/AUTHORS
new file mode 100644
index 00000000..5323b685
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/nodejs-parser/AUTHORS
@@ -0,0 +1,68 @@
+# Authors ordered by first contribution.
+Ryan Dahl <ry@tinyclouds.org>
+Jeremy Hinegardner <jeremy@hinegardner.org>
+Sergey Shepelev <temotor@gmail.com>
+Joe Damato <ice799@gmail.com>
+tomika <tomika_nospam@freemail.hu>
+Phoenix Sol <phoenix@burninglabs.com>
+Cliff Frey <cliff@meraki.com>
+Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
+Santiago Gala <sgala@apache.org>
+Tim Becker <tim.becker@syngenio.de>
+Jeff Terrace <jterrace@gmail.com>
+Ben Noordhuis <info@bnoordhuis.nl>
+Nathan Rajlich <nathan@tootallnate.net>
+Mark Nottingham <mnot@mnot.net>
+Aman Gupta <aman@tmm1.net>
+Tim Becker <tim.becker@kuriositaet.de>
+Sean Cunningham <sean.cunningham@mandiant.com>
+Peter Griess <pg@std.in>
+Salman Haq <salman.haq@asti-usa.com>
+Cliff Frey <clifffrey@gmail.com>
+Jon Kolb <jon@b0g.us>
+Fouad Mardini <f.mardini@gmail.com>
+Paul Querna <pquerna@apache.org>
+Felix Geisendörfer <felix@debuggable.com>
+koichik <koichik@improvement.jp>
+Andre Caron <andre.l.caron@gmail.com>
+Ivo Raisr <ivosh@ivosh.net>
+James McLaughlin <jamie@lacewing-project.org>
+David Gwynne <loki@animata.net>
+Thomas LE ROUX <thomas@november-eleven.fr>
+Randy Rizun <rrizun@ortivawireless.com>
+Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
+Simon Zimmermann <simonz05@gmail.com>
+Erik Dubbelboer <erik@dubbelboer.com>
+Martell Malone <martellmalone@gmail.com>
+Bertrand Paquet <bpaquet@octo.com>
+BogDan Vatra <bogdan@kde.org>
+Peter Faiman <peter@thepicard.org>
+Corey Richardson <corey@octayn.net>
+Tóth Tamás <tomika_nospam@freemail.hu>
+Cam Swords <cam.swords@gmail.com>
+Chris Dickinson <christopher.s.dickinson@gmail.com>
+Uli Köhler <ukoehler@btronik.de>
+Charlie Somerville <charlie@charliesomerville.com>
+Patrik Stutz <patrik.stutz@gmail.com>
+Fedor Indutny <fedor.indutny@gmail.com>
+runner <runner.mei@gmail.com>
+Alexis Campailla <alexis@janeasystems.com>
+David Wragg <david@wragg.org>
+Vinnie Falco <vinnie.falco@gmail.com>
+Alex Butum <alexbutum@linux.com>
+Rex Feng <rexfeng@gmail.com>
+Alex Kocharin <alex@kocharin.ru>
+Mark Koopman <markmontymark@yahoo.com>
+Helge Heß <me@helgehess.eu>
+Alexis La Goutte <alexis.lagoutte@gmail.com>
+George Miroshnykov <george.miroshnykov@gmail.com>
+Maciej Małecki <me@mmalecki.com>
+Marc O'Morain <github.com@marcomorain.com>
+Jeff Pinner <jpinner@twitter.com>
+Timothy J Fontaine <tjfontaine@gmail.com>
+Akagi201 <akagi201@gmail.com>
+Romain Giraud <giraud.romain@gmail.com>
+Jay Satiro <raysatiro@yahoo.com>
+Arne Steen <Arne.Steen@gmx.de>
+Kjell Schubert <kjell.schubert@gmail.com>
+Olivier Mengué <dolmen@cpan.org>
diff --git a/src/boost/libs/beast/test/bench/parser/nodejs-parser/LICENSE-MIT b/src/boost/libs/beast/test/bench/parser/nodejs-parser/LICENSE-MIT
new file mode 100644
index 00000000..58010b38
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/nodejs-parser/LICENSE-MIT
@@ -0,0 +1,23 @@
+http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright
+Igor Sysoev.
+
+Additional changes are licensed under the same terms as NGINX and
+copyright Joyent, Inc. and other Node contributors. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/src/boost/libs/beast/test/bench/parser/nodejs-parser/README.md b/src/boost/libs/beast/test/bench/parser/nodejs-parser/README.md
new file mode 100644
index 00000000..eedd7f8c
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/nodejs-parser/README.md
@@ -0,0 +1,246 @@
+HTTP Parser
+===========
+
+[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser)
+
+This is a parser for HTTP messages written in C. It parses both requests and
+responses. The parser is designed to be used in performance HTTP
+applications. It does not make any syscalls nor allocations, it does not
+buffer data, it can be interrupted at anytime. Depending on your
+architecture, it only requires about 40 bytes of data per message
+stream (in a web server that is per connection).
+
+Features:
+
+ * No dependencies
+ * Handles persistent streams (keep-alive).
+ * Decodes chunked encoding.
+ * Upgrade support
+ * Defends against buffer overflow attacks.
+
+The parser extracts the following information from HTTP messages:
+
+ * Header fields and values
+ * Content-Length
+ * Request method
+ * Response status code
+ * Transfer-Encoding
+ * HTTP version
+ * Request URL
+ * Message body
+
+
+Usage
+-----
+
+One `http_parser` object is used per TCP connection. Initialize the struct
+using `http_parser_init()` and set the callbacks. That might look something
+like this for a request parser:
+```c
+http_parser_settings settings;
+settings.on_url = my_url_callback;
+settings.on_header_field = my_header_field_callback;
+/* ... */
+
+http_parser *parser = malloc(sizeof(http_parser));
+http_parser_init(parser, HTTP_REQUEST);
+parser->data = my_socket;
+```
+
+When data is received on the socket execute the parser and check for errors.
+
+```c
+size_t len = 80*1024, nparsed;
+char buf[len];
+ssize_t recved;
+
+recved = recv(fd, buf, len, 0);
+
+if (recved < 0) {
+ /* Handle error. */
+}
+
+/* Start up / continue the parser.
+ * Note we pass recved==0 to signal that EOF has been received.
+ */
+nparsed = http_parser_execute(parser, &settings, buf, recved);
+
+if (parser->upgrade) {
+ /* handle new protocol */
+} else if (nparsed != recved) {
+ /* Handle error. Usually just close the connection. */
+}
+```
+
+HTTP needs to know where the end of the stream is. For example, sometimes
+servers send responses without Content-Length and expect the client to
+consume input (for the body) until EOF. To tell http_parser about EOF, give
+`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors
+can still be encountered during an EOF, so one must still be prepared
+to receive them.
+
+Scalar valued message information such as `status_code`, `method`, and the
+HTTP version are stored in the parser structure. This data is only
+temporally stored in `http_parser` and gets reset on each new message. If
+this information is needed later, copy it out of the structure during the
+`headers_complete` callback.
+
+The parser decodes the transfer-encoding for both requests and responses
+transparently. That is, a chunked encoding is decoded before being sent to
+the on_body callback.
+
+
+The Special Problem of Upgrade
+------------------------------
+
+HTTP supports upgrading the connection to a different protocol. An
+increasingly common example of this is the WebSocket protocol which sends
+a request like
+
+ GET /demo HTTP/1.1
+ Upgrade: WebSocket
+ Connection: Upgrade
+ Host: example.com
+ Origin: http://example.com
+ WebSocket-Protocol: sample
+
+followed by non-HTTP data.
+
+(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the
+WebSocket protocol.)
+
+To support this, the parser will treat this as a normal HTTP message without a
+body, issuing both on_headers_complete and on_message_complete callbacks. However
+http_parser_execute() will stop parsing at the end of the headers and return.
+
+The user is expected to check if `parser->upgrade` has been set to 1 after
+`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied
+offset by the return value of `http_parser_execute()`.
+
+
+Callbacks
+---------
+
+During the `http_parser_execute()` call, the callbacks set in
+`http_parser_settings` will be executed. The parser maintains state and
+never looks behind, so buffering the data is not necessary. If you need to
+save certain data for later usage, you can do that from the callbacks.
+
+There are two types of callbacks:
+
+* notification `typedef int (*http_cb) (http_parser*);`
+ Callbacks: on_message_begin, on_headers_complete, on_message_complete.
+* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);`
+ Callbacks: (requests only) on_url,
+ (common) on_header_field, on_header_value, on_body;
+
+Callbacks must return 0 on success. Returning a non-zero value indicates
+error to the parser, making it exit immediately.
+
+For cases where it is necessary to pass local information to/from a callback,
+the `http_parser` object's `data` field can be used.
+An example of such a case is when using threads to handle a socket connection,
+parse a request, and then give a response over that socket. By instantiation
+of a thread-local struct containing relevant data (e.g. accepted socket,
+allocated memory for callbacks to write into, etc), a parser's callbacks are
+able to communicate data between the scope of the thread and the scope of the
+callback in a threadsafe manner. This allows http-parser to be used in
+multi-threaded contexts.
+
+Example:
+```
+ typedef struct {
+ socket_t sock;
+ void* buffer;
+ int buf_len;
+ } custom_data_t;
+
+
+int my_url_callback(http_parser* parser, const char *at, size_t length) {
+ /* access to thread local custom_data_t struct.
+ Use this access save parsed data for later use into thread local
+ buffer, or communicate over socket
+ */
+ parser->data;
+ ...
+ return 0;
+}
+
+...
+
+void http_parser_thread(socket_t sock) {
+ int nparsed = 0;
+ /* allocate memory for user data */
+ custom_data_t *my_data = malloc(sizeof(custom_data_t));
+
+ /* some information for use by callbacks.
+ * achieves thread -> callback information flow */
+ my_data->sock = sock;
+
+ /* instantiate a thread-local parser */
+ http_parser *parser = malloc(sizeof(http_parser));
+ http_parser_init(parser, HTTP_REQUEST); /* initialise parser */
+ /* this custom data reference is accessible through the reference to the
+ parser supplied to callback functions */
+ parser->data = my_data;
+
+ http_parser_settings settings; / * set up callbacks */
+ settings.on_url = my_url_callback;
+
+ /* execute parser */
+ nparsed = http_parser_execute(parser, &settings, buf, recved);
+
+ ...
+ /* parsed information copied from callback.
+ can now perform action on data copied into thread-local memory from callbacks.
+ achieves callback -> thread information flow */
+ my_data->buffer;
+ ...
+}
+
+```
+
+In case you parse HTTP message in chunks (i.e. `read()` request line
+from socket, parse, read half headers, parse, etc) your data callbacks
+may be called more than once. Http-parser guarantees that data pointer is only
+valid for the lifetime of callback. You can also `read()` into a heap allocated
+buffer to avoid copying memory around if this fits your application.
+
+Reading headers may be a tricky task if you read/parse headers partially.
+Basically, you need to remember whether last header callback was field or value
+and apply the following logic:
+
+ (on_header_field and on_header_value shortened to on_h_*)
+ ------------------------ ------------ --------------------------------------------
+ | State (prev. callback) | Callback | Description/action |
+ ------------------------ ------------ --------------------------------------------
+ | nothing (first call) | on_h_field | Allocate new buffer and copy callback data |
+ | | | into it |
+ ------------------------ ------------ --------------------------------------------
+ | value | on_h_field | New header started. |
+ | | | Copy current name,value buffers to headers |
+ | | | list and allocate new buffer for new name |
+ ------------------------ ------------ --------------------------------------------
+ | field | on_h_field | Previous name continues. Reallocate name |
+ | | | buffer and append callback data to it |
+ ------------------------ ------------ --------------------------------------------
+ | field | on_h_value | Value for current header started. Allocate |
+ | | | new buffer and copy callback data to it |
+ ------------------------ ------------ --------------------------------------------
+ | value | on_h_value | Value continues. Reallocate value buffer |
+ | | | and append callback data to it |
+ ------------------------ ------------ --------------------------------------------
+
+
+Parsing URLs
+------------
+
+A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`.
+Users of this library may wish to use it to parse URLs constructed from
+consecutive `on_url` callbacks.
+
+See examples of reading in headers:
+
+* [partial example](http://gist.github.com/155877) in C
+* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C
+* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript
diff --git a/src/boost/libs/beast/test/bench/parser/nodejs-parser/http_parser.c b/src/boost/libs/beast/test/bench/parser/nodejs-parser/http_parser.c
new file mode 100644
index 00000000..71961754
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/nodejs-parser/http_parser.c
@@ -0,0 +1,2468 @@
+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
+ *
+ * Additional changes are licensed under the same terms as NGINX and
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include "http_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef BIT_AT
+# define BIT_AT(a, i) \
+ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
+ (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#ifndef ELEM_AT
+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
+#endif
+
+#define SET_ERRNO(e) \
+do { \
+ parser->http_errno = (e); \
+} while(0)
+
+#define CURRENT_STATE() p_state
+#define UPDATE_STATE(V) p_state = (enum state) (V);
+#define RETURN(V) \
+do { \
+ parser->state = CURRENT_STATE(); \
+ return (V); \
+} while (0);
+#define REEXECUTE() \
+ goto reexecute; \
+
+
+#ifdef __GNUC__
+# define LIKELY(X) __builtin_expect(!!(X), 1)
+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#else
+# define LIKELY(X) (X)
+# define UNLIKELY(X) (X)
+#endif
+
+
+/* Run the notify callback FOR, returning ER if it fails */
+#define CALLBACK_NOTIFY_(FOR, ER) \
+do { \
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
+ \
+ if (LIKELY(settings->on_##FOR)) { \
+ parser->state = CURRENT_STATE(); \
+ if (UNLIKELY(0 != settings->on_##FOR(parser))) { \
+ SET_ERRNO(HPE_CB_##FOR); \
+ } \
+ UPDATE_STATE(parser->state); \
+ \
+ /* We either errored above or got paused; get out */ \
+ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
+ return (ER); \
+ } \
+ } \
+} while (0)
+
+/* Run the notify callback FOR and consume the current byte */
+#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
+
+/* Run the notify callback FOR and don't consume the current byte */
+#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
+
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
+#define CALLBACK_DATA_(FOR, LEN, ER) \
+do { \
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
+ \
+ if (FOR##_mark) { \
+ if (LIKELY(settings->on_##FOR)) { \
+ parser->state = CURRENT_STATE(); \
+ if (UNLIKELY(0 != \
+ settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
+ SET_ERRNO(HPE_CB_##FOR); \
+ } \
+ UPDATE_STATE(parser->state); \
+ \
+ /* We either errored above or got paused; get out */ \
+ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
+ return (ER); \
+ } \
+ } \
+ FOR##_mark = NULL; \
+ } \
+} while (0)
+
+/* Run the data callback FOR and consume the current byte */
+#define CALLBACK_DATA(FOR) \
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
+
+/* Run the data callback FOR and don't consume the current byte */
+#define CALLBACK_DATA_NOADVANCE(FOR) \
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
+
+/* Set the mark FOR; non-destructive if mark is already set */
+#define MARK(FOR) \
+do { \
+ if (!FOR##_mark) { \
+ FOR##_mark = p; \
+ } \
+} while (0)
+
+/* Don't allow the total size of the HTTP headers (including the status
+ * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
+ * embedders against denial-of-service attacks where the attacker feeds
+ * us a never-ending header that the embedder keeps buffering.
+ *
+ * This check is arguably the responsibility of embedders but we're doing
+ * it on the embedder's behalf because most won't bother and this way we
+ * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
+ * than any reasonable request or response so this should never affect
+ * day-to-day operation.
+ */
+#define COUNT_HEADER_SIZE(V) \
+do { \
+ parser->nread += (V); \
+ if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \
+ SET_ERRNO(HPE_HEADER_OVERFLOW); \
+ goto error; \
+ } \
+} while (0)
+
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+
+static const char *method_strings[] =
+ {
+#define XX(num, name, string) #string,
+ HTTP_METHOD_MAP(XX)
+#undef XX
+ };
+
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ 0, '!', 0, '#', '$', '%', '&', '\'',
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 0, 0, '*', '+', 0, '-', '.', 0,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ '0', '1', '2', '3', '4', '5', '6', '7',
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ '8', '9', 0, 0, 0, 0, 0, 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 'x', 'y', 'z', 0, 0, 0, '^', '_',
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 'x', 'y', 'z', 0, '|', 0, '~', 0 };
+
+
+static const int8_t unhex[256] =
+ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ };
+
+
+#if HTTP_PARSER_STRICT
+# define T(v) 0
+#else
+# define T(v) v
+#endif
+
+
+static const uint8_t normal_url_char[32] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
+
+#undef T
+
+enum state
+ { s_dead = 1 /* important that this is > 0 */
+
+ , s_start_req_or_res
+ , s_res_or_resp_H
+ , s_start_res
+ , s_res_H
+ , s_res_HT
+ , s_res_HTT
+ , s_res_HTTP
+ , s_res_first_http_major
+ , s_res_http_major
+ , s_res_first_http_minor
+ , s_res_http_minor
+ , s_res_first_status_code
+ , s_res_status_code
+ , s_res_status_start
+ , s_res_status
+ , s_res_line_almost_done
+
+ , s_start_req
+
+ , s_req_method
+ , s_req_spaces_before_url
+ , s_req_schema
+ , s_req_schema_slash
+ , s_req_schema_slash_slash
+ , s_req_server_start
+ , s_req_server
+ , s_req_server_with_at
+ , s_req_path
+ , s_req_query_string_start
+ , s_req_query_string
+ , s_req_fragment_start
+ , s_req_fragment
+ , s_req_http_start
+ , s_req_http_H
+ , s_req_http_HT
+ , s_req_http_HTT
+ , s_req_http_HTTP
+ , s_req_first_http_major
+ , s_req_http_major
+ , s_req_first_http_minor
+ , s_req_http_minor
+ , s_req_line_almost_done
+
+ , s_header_field_start
+ , s_header_field
+ , s_header_value_discard_ws
+ , s_header_value_discard_ws_almost_done
+ , s_header_value_discard_lws
+ , s_header_value_start
+ , s_header_value
+ , s_header_value_lws
+
+ , s_header_almost_done
+
+ , s_chunk_size_start
+ , s_chunk_size
+ , s_chunk_parameters
+ , s_chunk_size_almost_done
+
+ , s_headers_almost_done
+ , s_headers_done
+
+ /* Important: 's_headers_done' must be the last 'header' state. All
+ * states beyond this must be 'body' states. It is used for overflow
+ * checking. See the PARSING_HEADER() macro.
+ */
+
+ , s_chunk_data
+ , s_chunk_data_almost_done
+ , s_chunk_data_done
+
+ , s_body_identity
+ , s_body_identity_eof
+
+ , s_message_done
+ };
+
+
+#define PARSING_HEADER(state) (state <= s_headers_done)
+
+
+enum header_states
+ { h_general = 0
+ , h_C
+ , h_CO
+ , h_CON
+
+ , h_matching_connection
+ , h_matching_proxy_connection
+ , h_matching_content_length
+ , h_matching_transfer_encoding
+ , h_matching_upgrade
+
+ , h_connection
+ , h_content_length
+ , h_transfer_encoding
+ , h_upgrade
+
+ , h_matching_transfer_encoding_chunked
+ , h_matching_connection_token_start
+ , h_matching_connection_keep_alive
+ , h_matching_connection_close
+ , h_matching_connection_upgrade
+ , h_matching_connection_token
+
+ , h_transfer_encoding_chunked
+ , h_connection_keep_alive
+ , h_connection_close
+ , h_connection_upgrade
+ };
+
+enum http_host_state
+ {
+ s_http_host_dead = 1
+ , s_http_userinfo_start
+ , s_http_userinfo
+ , s_http_host_start
+ , s_http_host_v6_start
+ , s_http_host
+ , s_http_host_v6
+ , s_http_host_v6_end
+ , s_http_host_v6_zone_start
+ , s_http_host_v6_zone
+ , s_http_host_port_start
+ , s_http_host_port
+};
+
+/* Macros for character classes; depends on strict-mode */
+#define CR '\r'
+#define LF '\n'
+#define LOWER(c) (unsigned char)(c | 0x20)
+#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
+ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
+ (c) == ')')
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
+ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+ (c) == '$' || (c) == ',')
+
+#define STRICT_TOKEN(c) (tokens[(unsigned char)c])
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c) (tokens[(unsigned char)c])
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
+#define IS_URL_CHAR(c) \
+ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
+#define IS_HOST_CHAR(c) \
+ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+/**
+ * Verify that a char is a valid visible (printable) US-ASCII
+ * character or %x80-FF
+ **/
+#define IS_HEADER_CHAR(ch) \
+ (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
+
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
+
+
+#if HTTP_PARSER_STRICT
+# define STRICT_CHECK(cond) \
+do { \
+ if (cond) { \
+ SET_ERRNO(HPE_STRICT); \
+ goto error; \
+ } \
+} while (0)
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+# define STRICT_CHECK(cond)
+# define NEW_MESSAGE() start_state
+#endif
+
+
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
+static struct {
+ const char *name;
+ const char *description;
+} http_strerror_tab[] = {
+ HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
+};
+#undef HTTP_STRERROR_GEN
+
+int http_message_needs_eof(const http_parser *parser);
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state
+parse_url_char(enum state s, const char ch)
+{
+ if (ch == ' ' || ch == '\r' || ch == '\n') {
+ return s_dead;
+ }
+
+#if HTTP_PARSER_STRICT
+ if (ch == '\t' || ch == '\f') {
+ return s_dead;
+ }
+#endif
+
+ switch (s) {
+ case s_req_spaces_before_url:
+ /* Proxied requests are followed by scheme of an absolute URI (alpha).
+ * All methods except CONNECT are followed by '/' or '*'.
+ */
+
+ if (ch == '/' || ch == '*') {
+ return s_req_path;
+ }
+
+ if (IS_ALPHA(ch)) {
+ return s_req_schema;
+ }
+
+ break;
+
+ case s_req_schema:
+ if (IS_ALPHA(ch)) {
+ return s;
+ }
+
+ if (ch == ':') {
+ return s_req_schema_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash:
+ if (ch == '/') {
+ return s_req_schema_slash_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash_slash:
+ if (ch == '/') {
+ return s_req_server_start;
+ }
+
+ break;
+
+ case s_req_server_with_at:
+ if (ch == '@') {
+ return s_dead;
+ }
+
+ /* FALLTHROUGH */
+ case s_req_server_start:
+ case s_req_server:
+ if (ch == '/') {
+ return s_req_path;
+ }
+
+ if (ch == '?') {
+ return s_req_query_string_start;
+ }
+
+ if (ch == '@') {
+ return s_req_server_with_at;
+ }
+
+ if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
+ return s_req_server;
+ }
+
+ break;
+
+ case s_req_path:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_query_string_start;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_query_string_start:
+ case s_req_query_string:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_query_string;
+ }
+
+ switch (ch) {
+ case '?':
+ /* allow extra '?' in query string */
+ return s_req_query_string;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_fragment_start:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_fragment;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_fragment;
+
+ case '#':
+ return s;
+ }
+
+ break;
+
+ case s_req_fragment:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ case '#':
+ return s;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* We should never fall out of the switch above unless there's an error */
+ return s_dead;
+}
+
+size_t http_parser_execute (http_parser *parser,
+ const http_parser_settings *settings,
+ const char *data,
+ size_t len)
+{
+ char c, ch;
+ int8_t unhex_val;
+ const char *p = data;
+ const char *header_field_mark = 0;
+ const char *header_value_mark = 0;
+ const char *url_mark = 0;
+ const char *body_mark = 0;
+ const char *status_mark = 0;
+ enum state p_state = (enum state) parser->state;
+ const unsigned int lenient = parser->lenient_http_headers;
+
+ /* We're in an error state. Don't bother doing anything. */
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+ return 0;
+ }
+
+ if (len == 0) {
+ switch (CURRENT_STATE()) {
+ case s_body_identity_eof:
+ /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
+ * we got paused.
+ */
+ CALLBACK_NOTIFY_NOADVANCE(message_complete);
+ return 0;
+
+ case s_dead:
+ case s_start_req_or_res:
+ case s_start_res:
+ case s_start_req:
+ return 0;
+
+ default:
+ SET_ERRNO(HPE_INVALID_EOF_STATE);
+ return 1;
+ }
+ }
+
+
+ if (CURRENT_STATE() == s_header_field)
+ header_field_mark = data;
+ if (CURRENT_STATE() == s_header_value)
+ header_value_mark = data;
+ switch (CURRENT_STATE()) {
+ case s_req_path:
+ case s_req_schema:
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ case s_req_server:
+ case s_req_server_with_at:
+ case s_req_query_string_start:
+ case s_req_query_string:
+ case s_req_fragment_start:
+ case s_req_fragment:
+ url_mark = data;
+ break;
+ case s_res_status:
+ status_mark = data;
+ break;
+ default:
+ break;
+ }
+
+ for (p=data; p != data + len; p++) {
+ ch = *p;
+
+ if (PARSING_HEADER(CURRENT_STATE()))
+ COUNT_HEADER_SIZE(1);
+
+reexecute:
+ switch (CURRENT_STATE()) {
+
+ case s_dead:
+ /* this state is used after a 'Connection: close' message
+ * the parser will error out if it reads another message
+ */
+ if (LIKELY(ch == CR || ch == LF))
+ break;
+
+ SET_ERRNO(HPE_CLOSED_CONNECTION);
+ goto error;
+
+ case s_start_req_or_res:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ if (ch == 'H') {
+ UPDATE_STATE(s_res_or_resp_H);
+
+ CALLBACK_NOTIFY(message_begin);
+ } else {
+ parser->type = HTTP_REQUEST;
+ UPDATE_STATE(s_start_req);
+ REEXECUTE();
+ }
+
+ break;
+ }
+
+ case s_res_or_resp_H:
+ if (ch == 'T') {
+ parser->type = HTTP_RESPONSE;
+ UPDATE_STATE(s_res_HT);
+ } else {
+ if (UNLIKELY(ch != 'E')) {
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+
+ parser->type = HTTP_REQUEST;
+ parser->method = HTTP_HEAD;
+ parser->index = 2;
+ UPDATE_STATE(s_req_method);
+ }
+ break;
+
+ case s_start_res:
+ {
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ switch (ch) {
+ case 'H':
+ UPDATE_STATE(s_res_H);
+ break;
+
+ case CR:
+ case LF:
+ break;
+
+ default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+
+ CALLBACK_NOTIFY(message_begin);
+ break;
+ }
+
+ case s_res_H:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_res_HT);
+ break;
+
+ case s_res_HT:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_res_HTT);
+ break;
+
+ case s_res_HTT:
+ STRICT_CHECK(ch != 'P');
+ UPDATE_STATE(s_res_HTTP);
+ break;
+
+ case s_res_HTTP:
+ STRICT_CHECK(ch != '/');
+ UPDATE_STATE(s_res_first_http_major);
+ break;
+
+ case s_res_first_http_major:
+ if (UNLIKELY(ch < '0' || ch > '9')) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major = ch - '0';
+ UPDATE_STATE(s_res_http_major);
+ break;
+
+ /* major HTTP version or dot */
+ case s_res_http_major:
+ {
+ if (ch == '.') {
+ UPDATE_STATE(s_res_first_http_minor);
+ break;
+ }
+
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major *= 10;
+ parser->http_major += ch - '0';
+
+ if (UNLIKELY(parser->http_major > 999)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ /* first digit of minor HTTP version */
+ case s_res_first_http_minor:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor = ch - '0';
+ UPDATE_STATE(s_res_http_minor);
+ break;
+
+ /* minor HTTP version or end of request line */
+ case s_res_http_minor:
+ {
+ if (ch == ' ') {
+ UPDATE_STATE(s_res_first_status_code);
+ break;
+ }
+
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor *= 10;
+ parser->http_minor += ch - '0';
+
+ if (UNLIKELY(parser->http_minor > 999)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_res_first_status_code:
+ {
+ if (!IS_NUM(ch)) {
+ if (ch == ' ') {
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+ parser->status_code = ch - '0';
+ UPDATE_STATE(s_res_status_code);
+ break;
+ }
+
+ case s_res_status_code:
+ {
+ if (!IS_NUM(ch)) {
+ switch (ch) {
+ case ' ':
+ UPDATE_STATE(s_res_status_start);
+ break;
+ case CR:
+ UPDATE_STATE(s_res_line_almost_done);
+ break;
+ case LF:
+ UPDATE_STATE(s_header_field_start);
+ break;
+ default:
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+ break;
+ }
+
+ parser->status_code *= 10;
+ parser->status_code += ch - '0';
+
+ if (UNLIKELY(parser->status_code > 999)) {
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_res_status_start:
+ {
+ if (ch == CR) {
+ UPDATE_STATE(s_res_line_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_field_start);
+ break;
+ }
+
+ MARK(status);
+ UPDATE_STATE(s_res_status);
+ parser->index = 0;
+ break;
+ }
+
+ case s_res_status:
+ if (ch == CR) {
+ UPDATE_STATE(s_res_line_almost_done);
+ CALLBACK_DATA(status);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_field_start);
+ CALLBACK_DATA(status);
+ break;
+ }
+
+ break;
+
+ case s_res_line_almost_done:
+ STRICT_CHECK(ch != LF);
+ UPDATE_STATE(s_header_field_start);
+ break;
+
+ case s_start_req:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ if (UNLIKELY(!IS_ALPHA(ch))) {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ parser->method = (enum http_method) 0;
+ parser->index = 1;
+ switch (ch) {
+ case 'A': parser->method = HTTP_ACL; break;
+ case 'B': parser->method = HTTP_BIND; break;
+ case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
+ case 'D': parser->method = HTTP_DELETE; break;
+ case 'G': parser->method = HTTP_GET; break;
+ case 'H': parser->method = HTTP_HEAD; break;
+ case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
+ case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
+ case 'N': parser->method = HTTP_NOTIFY; break;
+ case 'O': parser->method = HTTP_OPTIONS; break;
+ case 'P': parser->method = HTTP_POST;
+ /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
+ break;
+ case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
+ case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
+ case 'T': parser->method = HTTP_TRACE; break;
+ case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
+ default:
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+ UPDATE_STATE(s_req_method);
+
+ CALLBACK_NOTIFY(message_begin);
+
+ break;
+ }
+
+ case s_req_method:
+ {
+ const char *matcher;
+ if (UNLIKELY(ch == '\0')) {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ matcher = method_strings[parser->method];
+ if (ch == ' ' && matcher[parser->index] == '\0') {
+ UPDATE_STATE(s_req_spaces_before_url);
+ } else if (ch == matcher[parser->index]) {
+ ; /* nada */
+ } else if (IS_ALPHA(ch)) {
+
+ switch (parser->method << 16 | parser->index << 8 | ch) {
+#define XX(meth, pos, ch, new_meth) \
+ case (HTTP_##meth << 16 | pos << 8 | ch): \
+ parser->method = HTTP_##new_meth; break;
+
+ XX(POST, 1, 'U', PUT)
+ XX(POST, 1, 'A', PATCH)
+ XX(CONNECT, 1, 'H', CHECKOUT)
+ XX(CONNECT, 2, 'P', COPY)
+ XX(MKCOL, 1, 'O', MOVE)
+ XX(MKCOL, 1, 'E', MERGE)
+ XX(MKCOL, 2, 'A', MKACTIVITY)
+ XX(MKCOL, 3, 'A', MKCALENDAR)
+ XX(SUBSCRIBE, 1, 'E', SEARCH)
+ XX(REPORT, 2, 'B', REBIND)
+ XX(POST, 1, 'R', PROPFIND)
+ XX(PROPFIND, 4, 'P', PROPPATCH)
+ XX(PUT, 2, 'R', PURGE)
+ XX(LOCK, 1, 'I', LINK)
+ XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
+ XX(UNLOCK, 2, 'B', UNBIND)
+ XX(UNLOCK, 3, 'I', UNLINK)
+#undef XX
+
+ default:
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+ } else if (ch == '-' &&
+ parser->index == 1 &&
+ parser->method == HTTP_MKCOL) {
+ parser->method = HTTP_MSEARCH;
+ } else {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ ++parser->index;
+ break;
+ }
+
+ case s_req_spaces_before_url:
+ {
+ if (ch == ' ') break;
+
+ MARK(url);
+ if (parser->method == HTTP_CONNECT) {
+ UPDATE_STATE(s_req_server_start);
+ }
+
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_req_schema:
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ {
+ switch (ch) {
+ /* No whitespace allowed here */
+ case ' ':
+ case CR:
+ case LF:
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ default:
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+ }
+
+ break;
+ }
+
+ case s_req_server:
+ case s_req_server_with_at:
+ case s_req_path:
+ case s_req_query_string_start:
+ case s_req_query_string:
+ case s_req_fragment_start:
+ case s_req_fragment:
+ {
+ switch (ch) {
+ case ' ':
+ UPDATE_STATE(s_req_http_start);
+ CALLBACK_DATA(url);
+ break;
+ case CR:
+ case LF:
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ UPDATE_STATE((ch == CR) ?
+ s_req_line_almost_done :
+ s_header_field_start);
+ CALLBACK_DATA(url);
+ break;
+ default:
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+ }
+ break;
+ }
+
+ case s_req_http_start:
+ switch (ch) {
+ case 'H':
+ UPDATE_STATE(s_req_http_H);
+ break;
+ case ' ':
+ break;
+ default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+ break;
+
+ case s_req_http_H:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_req_http_HT);
+ break;
+
+ case s_req_http_HT:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_req_http_HTT);
+ break;
+
+ case s_req_http_HTT:
+ STRICT_CHECK(ch != 'P');
+ UPDATE_STATE(s_req_http_HTTP);
+ break;
+
+ case s_req_http_HTTP:
+ STRICT_CHECK(ch != '/');
+ UPDATE_STATE(s_req_first_http_major);
+ break;
+
+ /* first digit of major HTTP version */
+ case s_req_first_http_major:
+ if (UNLIKELY(ch < '1' || ch > '9')) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major = ch - '0';
+ UPDATE_STATE(s_req_http_major);
+ break;
+
+ /* major HTTP version or dot */
+ case s_req_http_major:
+ {
+ if (ch == '.') {
+ UPDATE_STATE(s_req_first_http_minor);
+ break;
+ }
+
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major *= 10;
+ parser->http_major += ch - '0';
+
+ if (UNLIKELY(parser->http_major > 999)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ /* first digit of minor HTTP version */
+ case s_req_first_http_minor:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor = ch - '0';
+ UPDATE_STATE(s_req_http_minor);
+ break;
+
+ /* minor HTTP version or end of request line */
+ case s_req_http_minor:
+ {
+ if (ch == CR) {
+ UPDATE_STATE(s_req_line_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_field_start);
+ break;
+ }
+
+ /* XXX allow spaces after digit? */
+
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor *= 10;
+ parser->http_minor += ch - '0';
+
+ if (UNLIKELY(parser->http_minor > 999)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ /* end of request line */
+ case s_req_line_almost_done:
+ {
+ if (UNLIKELY(ch != LF)) {
+ SET_ERRNO(HPE_LF_EXPECTED);
+ goto error;
+ }
+
+ UPDATE_STATE(s_header_field_start);
+ break;
+ }
+
+ case s_header_field_start:
+ {
+ if (ch == CR) {
+ UPDATE_STATE(s_headers_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ /* they might be just sending \n instead of \r\n so this would be
+ * the second \n to denote the end of headers*/
+ UPDATE_STATE(s_headers_almost_done);
+ REEXECUTE();
+ }
+
+ c = TOKEN(ch);
+
+ if (UNLIKELY(!c)) {
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ MARK(header_field);
+
+ parser->index = 0;
+ UPDATE_STATE(s_header_field);
+
+ switch (c) {
+ case 'c':
+ parser->header_state = h_C;
+ break;
+
+ case 'p':
+ parser->header_state = h_matching_proxy_connection;
+ break;
+
+ case 't':
+ parser->header_state = h_matching_transfer_encoding;
+ break;
+
+ case 'u':
+ parser->header_state = h_matching_upgrade;
+ break;
+
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_field:
+ {
+ const char* start = p;
+ for (; p != data + len; p++) {
+ ch = *p;
+ c = TOKEN(ch);
+
+ if (!c)
+ break;
+
+ switch (parser->header_state) {
+ case h_general:
+ break;
+
+ case h_C:
+ parser->index++;
+ parser->header_state = (c == 'o' ? h_CO : h_general);
+ break;
+
+ case h_CO:
+ parser->index++;
+ parser->header_state = (c == 'n' ? h_CON : h_general);
+ break;
+
+ case h_CON:
+ parser->index++;
+ switch (c) {
+ case 'n':
+ parser->header_state = h_matching_connection;
+ break;
+ case 't':
+ parser->header_state = h_matching_content_length;
+ break;
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+
+ /* connection */
+
+ case h_matching_connection:
+ parser->index++;
+ if (parser->index > sizeof(CONNECTION)-1
+ || c != CONNECTION[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CONNECTION)-2) {
+ parser->header_state = h_connection;
+ }
+ break;
+
+ /* proxy-connection */
+
+ case h_matching_proxy_connection:
+ parser->index++;
+ if (parser->index > sizeof(PROXY_CONNECTION)-1
+ || c != PROXY_CONNECTION[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
+ parser->header_state = h_connection;
+ }
+ break;
+
+ /* content-length */
+
+ case h_matching_content_length:
+ parser->index++;
+ if (parser->index > sizeof(CONTENT_LENGTH)-1
+ || c != CONTENT_LENGTH[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
+ if (parser->flags & F_CONTENTLENGTH) {
+ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+ goto error;
+ }
+ parser->header_state = h_content_length;
+ parser->flags |= F_CONTENTLENGTH;
+ }
+ break;
+
+ /* transfer-encoding */
+
+ case h_matching_transfer_encoding:
+ parser->index++;
+ if (parser->index > sizeof(TRANSFER_ENCODING)-1
+ || c != TRANSFER_ENCODING[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
+ parser->header_state = h_transfer_encoding;
+ }
+ break;
+
+ /* upgrade */
+
+ case h_matching_upgrade:
+ parser->index++;
+ if (parser->index > sizeof(UPGRADE)-1
+ || c != UPGRADE[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(UPGRADE)-2) {
+ parser->header_state = h_upgrade;
+ }
+ break;
+
+ case h_connection:
+ case h_content_length:
+ case h_transfer_encoding:
+ case h_upgrade:
+ if (ch != ' ') parser->header_state = h_general;
+ break;
+
+ default:
+ assert(0 && "Unknown header_state");
+ break;
+ }
+ }
+
+ COUNT_HEADER_SIZE(p - start);
+
+ if (p == data + len) {
+ --p;
+ break;
+ }
+
+ if (ch == ':') {
+ UPDATE_STATE(s_header_value_discard_ws);
+ CALLBACK_DATA(header_field);
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ case s_header_value_discard_ws:
+ if (ch == ' ' || ch == '\t') break;
+
+ if (ch == CR) {
+ UPDATE_STATE(s_header_value_discard_ws_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_value_discard_lws);
+ break;
+ }
+
+ /* FALLTHROUGH */
+
+ case s_header_value_start:
+ {
+ MARK(header_value);
+
+ UPDATE_STATE(s_header_value);
+ parser->index = 0;
+
+ c = LOWER(ch);
+
+ switch (parser->header_state) {
+ case h_upgrade:
+ parser->flags |= F_UPGRADE;
+ parser->header_state = h_general;
+ break;
+
+ case h_transfer_encoding:
+ /* looking for 'Transfer-Encoding: chunked' */
+ if ('c' == c) {
+ parser->header_state = h_matching_transfer_encoding_chunked;
+ } else {
+ parser->header_state = h_general;
+ }
+ break;
+
+ case h_content_length:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->content_length = ch - '0';
+ break;
+
+ case h_connection:
+ /* looking for 'Connection: keep-alive' */
+ if (c == 'k') {
+ parser->header_state = h_matching_connection_keep_alive;
+ /* looking for 'Connection: close' */
+ } else if (c == 'c') {
+ parser->header_state = h_matching_connection_close;
+ } else if (c == 'u') {
+ parser->header_state = h_matching_connection_upgrade;
+ } else {
+ parser->header_state = h_matching_connection_token;
+ }
+ break;
+
+ /* Multi-value `Connection` header */
+ case h_matching_connection_token_start:
+ break;
+
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_value:
+ {
+ const char* start = p;
+ enum header_states h_state = (enum header_states) parser->header_state;
+ for (; p != data + len; p++) {
+ ch = *p;
+ if (ch == CR) {
+ UPDATE_STATE(s_header_almost_done);
+ parser->header_state = h_state;
+ CALLBACK_DATA(header_value);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_almost_done);
+ COUNT_HEADER_SIZE(p - start);
+ parser->header_state = h_state;
+ CALLBACK_DATA_NOADVANCE(header_value);
+ REEXECUTE();
+ }
+
+ if (!lenient && !IS_HEADER_CHAR(ch)) {
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ c = LOWER(ch);
+
+ switch (h_state) {
+ case h_general:
+ {
+ const char* p_cr;
+ const char* p_lf;
+ size_t limit = data + len - p;
+
+ limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
+
+ p_cr = (const char*) memchr(p, CR, limit);
+ p_lf = (const char*) memchr(p, LF, limit);
+ if (p_cr != NULL) {
+ if (p_lf != NULL && p_cr >= p_lf)
+ p = p_lf;
+ else
+ p = p_cr;
+ } else if (UNLIKELY(p_lf != NULL)) {
+ p = p_lf;
+ } else {
+ p = data + len;
+ }
+ --p;
+
+ break;
+ }
+
+ case h_connection:
+ case h_transfer_encoding:
+ assert(0 && "Shouldn't get here.");
+ break;
+
+ case h_content_length:
+ {
+ uint64_t t;
+
+ if (ch == ' ') break;
+
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ parser->header_state = h_state;
+ goto error;
+ }
+
+ t = parser->content_length;
+ t *= 10;
+ t += ch - '0';
+
+ /* Overflow? Test against a conservative limit for simplicity. */
+ if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ parser->header_state = h_state;
+ goto error;
+ }
+
+ parser->content_length = t;
+ break;
+ }
+
+ /* Transfer-Encoding: chunked */
+ case h_matching_transfer_encoding_chunked:
+ parser->index++;
+ if (parser->index > sizeof(CHUNKED)-1
+ || c != CHUNKED[parser->index]) {
+ h_state = h_general;
+ } else if (parser->index == sizeof(CHUNKED)-2) {
+ h_state = h_transfer_encoding_chunked;
+ }
+ break;
+
+ case h_matching_connection_token_start:
+ /* looking for 'Connection: keep-alive' */
+ if (c == 'k') {
+ h_state = h_matching_connection_keep_alive;
+ /* looking for 'Connection: close' */
+ } else if (c == 'c') {
+ h_state = h_matching_connection_close;
+ } else if (c == 'u') {
+ h_state = h_matching_connection_upgrade;
+ } else if (STRICT_TOKEN(c)) {
+ h_state = h_matching_connection_token;
+ } else if (c == ' ' || c == '\t') {
+ /* Skip lws */
+ } else {
+ h_state = h_general;
+ }
+ break;
+
+ /* looking for 'Connection: keep-alive' */
+ case h_matching_connection_keep_alive:
+ parser->index++;
+ if (parser->index > sizeof(KEEP_ALIVE)-1
+ || c != KEEP_ALIVE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
+ h_state = h_connection_keep_alive;
+ }
+ break;
+
+ /* looking for 'Connection: close' */
+ case h_matching_connection_close:
+ parser->index++;
+ if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(CLOSE)-2) {
+ h_state = h_connection_close;
+ }
+ break;
+
+ /* looking for 'Connection: upgrade' */
+ case h_matching_connection_upgrade:
+ parser->index++;
+ if (parser->index > sizeof(UPGRADE) - 1 ||
+ c != UPGRADE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(UPGRADE)-2) {
+ h_state = h_connection_upgrade;
+ }
+ break;
+
+ case h_matching_connection_token:
+ if (ch == ',') {
+ h_state = h_matching_connection_token_start;
+ parser->index = 0;
+ }
+ break;
+
+ case h_transfer_encoding_chunked:
+ if (ch != ' ') h_state = h_general;
+ break;
+
+ case h_connection_keep_alive:
+ case h_connection_close:
+ case h_connection_upgrade:
+ if (ch == ',') {
+ if (h_state == h_connection_keep_alive) {
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ } else if (h_state == h_connection_close) {
+ parser->flags |= F_CONNECTION_CLOSE;
+ } else if (h_state == h_connection_upgrade) {
+ parser->flags |= F_CONNECTION_UPGRADE;
+ }
+ h_state = h_matching_connection_token_start;
+ parser->index = 0;
+ } else if (ch != ' ') {
+ h_state = h_matching_connection_token;
+ }
+ break;
+
+ default:
+ UPDATE_STATE(s_header_value);
+ h_state = h_general;
+ break;
+ }
+ }
+ parser->header_state = h_state;
+
+ COUNT_HEADER_SIZE(p - start);
+
+ if (p == data + len)
+ --p;
+ break;
+ }
+
+ case s_header_almost_done:
+ {
+ if (UNLIKELY(ch != LF)) {
+ SET_ERRNO(HPE_LF_EXPECTED);
+ goto error;
+ }
+
+ UPDATE_STATE(s_header_value_lws);
+ break;
+ }
+
+ case s_header_value_lws:
+ {
+ if (ch == ' ' || ch == '\t') {
+ UPDATE_STATE(s_header_value_start);
+ REEXECUTE();
+ }
+
+ /* finished the header */
+ switch (parser->header_state) {
+ case h_connection_keep_alive:
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ break;
+ case h_connection_close:
+ parser->flags |= F_CONNECTION_CLOSE;
+ break;
+ case h_transfer_encoding_chunked:
+ parser->flags |= F_CHUNKED;
+ break;
+ case h_connection_upgrade:
+ parser->flags |= F_CONNECTION_UPGRADE;
+ break;
+ default:
+ break;
+ }
+
+ UPDATE_STATE(s_header_field_start);
+ REEXECUTE();
+ }
+
+ case s_header_value_discard_ws_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+ UPDATE_STATE(s_header_value_discard_lws);
+ break;
+ }
+
+ case s_header_value_discard_lws:
+ {
+ if (ch == ' ' || ch == '\t') {
+ UPDATE_STATE(s_header_value_discard_ws);
+ break;
+ } else {
+ switch (parser->header_state) {
+ case h_connection_keep_alive:
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ break;
+ case h_connection_close:
+ parser->flags |= F_CONNECTION_CLOSE;
+ break;
+ case h_connection_upgrade:
+ parser->flags |= F_CONNECTION_UPGRADE;
+ break;
+ case h_transfer_encoding_chunked:
+ parser->flags |= F_CHUNKED;
+ break;
+ default:
+ break;
+ }
+
+ /* header value was empty */
+ MARK(header_value);
+ UPDATE_STATE(s_header_field_start);
+ CALLBACK_DATA_NOADVANCE(header_value);
+ REEXECUTE();
+ }
+ }
+
+ case s_headers_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+
+ if (parser->flags & F_TRAILING) {
+ /* End of a chunked request */
+ UPDATE_STATE(s_message_done);
+ CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
+ REEXECUTE();
+ }
+
+ /* Cannot use chunked encoding and a content-length header together
+ per the HTTP specification. */
+ if ((parser->flags & F_CHUNKED) &&
+ (parser->flags & F_CONTENTLENGTH)) {
+ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+ goto error;
+ }
+
+ UPDATE_STATE(s_headers_done);
+
+ /* Set this here so that on_headers_complete() callbacks can see it */
+ parser->upgrade =
+ ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
+ (F_UPGRADE | F_CONNECTION_UPGRADE) ||
+ parser->method == HTTP_CONNECT);
+
+ /* Here we call the headers_complete callback. This is somewhat
+ * different than other callbacks because if the user returns 1, we
+ * will interpret that as saying that this message has no body. This
+ * is needed for the annoying case of recieving a response to a HEAD
+ * request.
+ *
+ * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
+ * we have to simulate it by handling a change in errno below.
+ */
+ if (settings->on_headers_complete) {
+ switch (settings->on_headers_complete(parser)) {
+ case 0:
+ break;
+
+ case 2:
+ parser->upgrade = 1;
+
+ case 1:
+ parser->flags |= F_SKIPBODY;
+ break;
+
+ default:
+ SET_ERRNO(HPE_CB_headers_complete);
+ RETURN(p - data); /* Error */
+ }
+ }
+
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+ RETURN(p - data);
+ }
+
+ REEXECUTE();
+ }
+
+ case s_headers_done:
+ {
+ int hasBody;
+ STRICT_CHECK(ch != LF);
+
+ parser->nread = 0;
+
+ hasBody = parser->flags & F_CHUNKED ||
+ (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
+ if (parser->upgrade && (parser->method == HTTP_CONNECT ||
+ (parser->flags & F_SKIPBODY) || !hasBody)) {
+ /* Exit, the rest of the message is in a different protocol. */
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ RETURN((p - data) + 1);
+ }
+
+ if (parser->flags & F_SKIPBODY) {
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ } else if (parser->flags & F_CHUNKED) {
+ /* chunked encoding - ignore Content-Length header */
+ UPDATE_STATE(s_chunk_size_start);
+ } else {
+ if (parser->content_length == 0) {
+ /* Content-Length header given but zero: Content-Length: 0\r\n */
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ } else if (parser->content_length != ULLONG_MAX) {
+ /* Content-Length header given and non-zero */
+ UPDATE_STATE(s_body_identity);
+ } else {
+ if (!http_message_needs_eof(parser)) {
+ /* Assume content-length 0 - read the next */
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ } else {
+ /* Read body until EOF */
+ UPDATE_STATE(s_body_identity_eof);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case s_body_identity:
+ {
+ uint64_t to_read = MIN(parser->content_length,
+ (uint64_t) ((data + len) - p));
+
+ assert(parser->content_length != 0
+ && parser->content_length != ULLONG_MAX);
+
+ /* The difference between advancing content_length and p is because
+ * the latter will automaticaly advance on the next loop iteration.
+ * Further, if content_length ends up at 0, we want to see the last
+ * byte again for our message complete callback.
+ */
+ MARK(body);
+ parser->content_length -= to_read;
+ p += to_read - 1;
+
+ if (parser->content_length == 0) {
+ UPDATE_STATE(s_message_done);
+
+ /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
+ *
+ * The alternative to doing this is to wait for the next byte to
+ * trigger the data callback, just as in every other case. The
+ * problem with this is that this makes it difficult for the test
+ * harness to distinguish between complete-on-EOF and
+ * complete-on-length. It's not clear that this distinction is
+ * important for applications, but let's keep it for now.
+ */
+ CALLBACK_DATA_(body, p - body_mark + 1, p - data);
+ REEXECUTE();
+ }
+
+ break;
+ }
+
+ /* read until EOF */
+ case s_body_identity_eof:
+ MARK(body);
+ p = data + len - 1;
+
+ break;
+
+ case s_message_done:
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ if (parser->upgrade) {
+ /* Exit, the rest of the message is in a different protocol. */
+ RETURN((p - data) + 1);
+ }
+ break;
+
+ case s_chunk_size_start:
+ {
+ assert(parser->nread == 1);
+ assert(parser->flags & F_CHUNKED);
+
+ unhex_val = unhex[(unsigned char)ch];
+ if (UNLIKELY(unhex_val == -1)) {
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+ goto error;
+ }
+
+ parser->content_length = unhex_val;
+ UPDATE_STATE(s_chunk_size);
+ break;
+ }
+
+ case s_chunk_size:
+ {
+ uint64_t t;
+
+ assert(parser->flags & F_CHUNKED);
+
+ if (ch == CR) {
+ UPDATE_STATE(s_chunk_size_almost_done);
+ break;
+ }
+
+ unhex_val = unhex[(unsigned char)ch];
+
+ if (unhex_val == -1) {
+ if (ch == ';' || ch == ' ') {
+ UPDATE_STATE(s_chunk_parameters);
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+ goto error;
+ }
+
+ t = parser->content_length;
+ t *= 16;
+ t += unhex_val;
+
+ /* Overflow? Test against a conservative limit for simplicity. */
+ if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->content_length = t;
+ break;
+ }
+
+ case s_chunk_parameters:
+ {
+ assert(parser->flags & F_CHUNKED);
+ /* just ignore this shit. TODO check for overflow */
+ if (ch == CR) {
+ UPDATE_STATE(s_chunk_size_almost_done);
+ break;
+ }
+ break;
+ }
+
+ case s_chunk_size_almost_done:
+ {
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+
+ parser->nread = 0;
+
+ if (parser->content_length == 0) {
+ parser->flags |= F_TRAILING;
+ UPDATE_STATE(s_header_field_start);
+ } else {
+ UPDATE_STATE(s_chunk_data);
+ }
+ CALLBACK_NOTIFY(chunk_header);
+ break;
+ }
+
+ case s_chunk_data:
+ {
+ uint64_t to_read = MIN(parser->content_length,
+ (uint64_t) ((data + len) - p));
+
+ assert(parser->flags & F_CHUNKED);
+ assert(parser->content_length != 0
+ && parser->content_length != ULLONG_MAX);
+
+ /* See the explanation in s_body_identity for why the content
+ * length and data pointers are managed this way.
+ */
+ MARK(body);
+ parser->content_length -= to_read;
+ p += to_read - 1;
+
+ if (parser->content_length == 0) {
+ UPDATE_STATE(s_chunk_data_almost_done);
+ }
+
+ break;
+ }
+
+ case s_chunk_data_almost_done:
+ assert(parser->flags & F_CHUNKED);
+ assert(parser->content_length == 0);
+ STRICT_CHECK(ch != CR);
+ UPDATE_STATE(s_chunk_data_done);
+ CALLBACK_DATA(body);
+ break;
+
+ case s_chunk_data_done:
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+ parser->nread = 0;
+ UPDATE_STATE(s_chunk_size_start);
+ CALLBACK_NOTIFY(chunk_complete);
+ break;
+
+ default:
+ assert(0 && "unhandled state");
+ SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
+ goto error;
+ }
+ }
+
+ /* Run callbacks for any marks that we have leftover after we ran our of
+ * bytes. There should be at most one of these set, so it's OK to invoke
+ * them in series (unset marks will not result in callbacks).
+ *
+ * We use the NOADVANCE() variety of callbacks here because 'p' has already
+ * overflowed 'data' and this allows us to correct for the off-by-one that
+ * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
+ * value that's in-bounds).
+ */
+
+ assert(((header_field_mark ? 1 : 0) +
+ (header_value_mark ? 1 : 0) +
+ (url_mark ? 1 : 0) +
+ (body_mark ? 1 : 0) +
+ (status_mark ? 1 : 0)) <= 1);
+
+ CALLBACK_DATA_NOADVANCE(header_field);
+ CALLBACK_DATA_NOADVANCE(header_value);
+ CALLBACK_DATA_NOADVANCE(url);
+ CALLBACK_DATA_NOADVANCE(body);
+ CALLBACK_DATA_NOADVANCE(status);
+
+ RETURN(len);
+
+error:
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
+ SET_ERRNO(HPE_UNKNOWN);
+ }
+
+ RETURN(p - data);
+}
+
+
+/* Does the parser need to see an EOF to find the end of the message? */
+int
+http_message_needs_eof (const http_parser *parser)
+{
+ if (parser->type == HTTP_REQUEST) {
+ return 0;
+ }
+
+ /* See RFC 2616 section 4.4 */
+ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+ parser->status_code == 204 || /* No Content */
+ parser->status_code == 304 || /* Not Modified */
+ parser->flags & F_SKIPBODY) { /* response to a HEAD request */
+ return 0;
+ }
+
+ if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int
+http_should_keep_alive (const http_parser *parser)
+{
+ if (parser->http_major > 0 && parser->http_minor > 0) {
+ /* HTTP/1.1 */
+ if (parser->flags & F_CONNECTION_CLOSE) {
+ return 0;
+ }
+ } else {
+ /* HTTP/1.0 or earlier */
+ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+ return 0;
+ }
+ }
+
+ return !http_message_needs_eof(parser);
+}
+
+
+const char *
+http_method_str (enum http_method m)
+{
+ return ELEM_AT(method_strings, m, "<unknown>");
+}
+
+
+void
+http_parser_init (http_parser *parser, enum http_parser_type t)
+{
+ void *data = parser->data; /* preserve application data */
+ memset(parser, 0, sizeof(*parser));
+ parser->data = data;
+ parser->type = t;
+ parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+ parser->http_errno = HPE_OK;
+}
+
+void
+http_parser_settings_init(http_parser_settings *settings)
+{
+ memset(settings, 0, sizeof(*settings));
+}
+
+const char *
+http_errno_name(enum http_errno err) {
+ assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+ return http_strerror_tab[err].name;
+}
+
+const char *
+http_errno_description(enum http_errno err) {
+ assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+ return http_strerror_tab[err].description;
+}
+
+static enum http_host_state
+http_parse_host_char(enum http_host_state s, const char ch) {
+ switch(s) {
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ if (ch == '@') {
+ return s_http_host_start;
+ }
+
+ if (IS_USERINFO_CHAR(ch)) {
+ return s_http_userinfo;
+ }
+ break;
+
+ case s_http_host_start:
+ if (ch == '[') {
+ return s_http_host_v6_start;
+ }
+
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ break;
+
+ case s_http_host:
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_end:
+ if (ch == ':') {
+ return s_http_host_port_start;
+ }
+
+ break;
+
+ case s_http_host_v6:
+ if (ch == ']') {
+ return s_http_host_v6_end;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_start:
+ if (IS_HEX(ch) || ch == ':' || ch == '.') {
+ return s_http_host_v6;
+ }
+
+ if (s == s_http_host_v6 && ch == '%') {
+ return s_http_host_v6_zone_start;
+ }
+ break;
+
+ case s_http_host_v6_zone:
+ if (ch == ']') {
+ return s_http_host_v6_end;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_zone_start:
+ /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
+ if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
+ ch == '~') {
+ return s_http_host_v6_zone;
+ }
+ break;
+
+ case s_http_host_port:
+ case s_http_host_port_start:
+ if (IS_NUM(ch)) {
+ return s_http_host_port;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ return s_http_host_dead;
+}
+
+static int
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
+ assert(u->field_set & (1 << UF_HOST));
+ enum http_host_state s;
+
+ const char *p;
+ size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+ u->field_data[UF_HOST].len = 0;
+
+ s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+ for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
+ enum http_host_state new_s = http_parse_host_char(s, *p);
+
+ if (new_s == s_http_host_dead) {
+ return 1;
+ }
+
+ switch(new_s) {
+ case s_http_host:
+ if (s != s_http_host) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_v6:
+ if (s != s_http_host_v6) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_v6_zone_start:
+ case s_http_host_v6_zone:
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_port:
+ if (s != s_http_host_port) {
+ u->field_data[UF_PORT].off = p - buf;
+ u->field_data[UF_PORT].len = 0;
+ u->field_set |= (1 << UF_PORT);
+ }
+ u->field_data[UF_PORT].len++;
+ break;
+
+ case s_http_userinfo:
+ if (s != s_http_userinfo) {
+ u->field_data[UF_USERINFO].off = p - buf ;
+ u->field_data[UF_USERINFO].len = 0;
+ u->field_set |= (1 << UF_USERINFO);
+ }
+ u->field_data[UF_USERINFO].len++;
+ break;
+
+ default:
+ break;
+ }
+ s = new_s;
+ }
+
+ /* Make sure we don't end somewhere unexpected */
+ switch (s) {
+ case s_http_host_start:
+ case s_http_host_v6_start:
+ case s_http_host_v6:
+ case s_http_host_v6_zone_start:
+ case s_http_host_v6_zone:
+ case s_http_host_port_start:
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void
+http_parser_url_init(struct http_parser_url *u) {
+ memset(u, 0, sizeof(*u));
+}
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+ struct http_parser_url *u)
+{
+ enum state s;
+ const char *p;
+ enum http_parser_url_fields uf, old_uf;
+ int found_at = 0;
+
+ u->port = u->field_set = 0;
+ s = is_connect ? s_req_server_start : s_req_spaces_before_url;
+ old_uf = UF_MAX;
+
+ for (p = buf; p < buf + buflen; p++) {
+ s = parse_url_char(s, *p);
+
+ /* Figure out the next field that we're operating on */
+ switch (s) {
+ case s_dead:
+ return 1;
+
+ /* Skip delimeters */
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ case s_req_query_string_start:
+ case s_req_fragment_start:
+ continue;
+
+ case s_req_schema:
+ uf = UF_SCHEMA;
+ break;
+
+ case s_req_server_with_at:
+ found_at = 1;
+
+ /* FALLTROUGH */
+ case s_req_server:
+ uf = UF_HOST;
+ break;
+
+ case s_req_path:
+ uf = UF_PATH;
+ break;
+
+ case s_req_query_string:
+ uf = UF_QUERY;
+ break;
+
+ case s_req_fragment:
+ uf = UF_FRAGMENT;
+ break;
+
+ default:
+ assert(!"Unexpected state");
+ return 1;
+ }
+
+ /* Nothing's changed; soldier on */
+ if (uf == old_uf) {
+ u->field_data[uf].len++;
+ continue;
+ }
+
+ u->field_data[uf].off = p - buf;
+ u->field_data[uf].len = 1;
+
+ u->field_set |= (1 << uf);
+ old_uf = uf;
+ }
+
+ /* host must be present if there is a schema */
+ /* parsing http:///toto will fail */
+ if ((u->field_set & (1 << UF_SCHEMA)) &&
+ (u->field_set & (1 << UF_HOST)) == 0) {
+ return 1;
+ }
+
+ if (u->field_set & (1 << UF_HOST)) {
+ if (http_parse_host(buf, u, found_at) != 0) {
+ return 1;
+ }
+ }
+
+ /* CONNECT requests can only contain "hostname:port" */
+ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
+ return 1;
+ }
+
+ if (u->field_set & (1 << UF_PORT)) {
+ /* Don't bother with endp; we've already validated the string */
+ unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
+
+ /* Ports have a max value of 2^16 */
+ if (v > 0xffff) {
+ return 1;
+ }
+
+ u->port = (uint16_t) v;
+ }
+
+ return 0;
+}
+
+void
+http_parser_pause(http_parser *parser, int paused) {
+ /* Users should only be pausing/unpausing a parser that is not in an error
+ * state. In non-debug builds, there's not much that we can do about this
+ * other than ignore it.
+ */
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
+ HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
+ SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
+ } else {
+ assert(0 && "Attempting to pause parser in error state");
+ }
+}
+
+int
+http_body_is_final(const struct http_parser *parser) {
+ return parser->state == s_message_done;
+}
+
+unsigned long
+http_parser_version(void) {
+ return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
+ HTTP_PARSER_VERSION_MINOR * 0x00100 |
+ HTTP_PARSER_VERSION_PATCH * 0x00001;
+}
diff --git a/src/boost/libs/beast/test/bench/parser/nodejs-parser/http_parser.h b/src/boost/libs/beast/test/bench/parser/nodejs-parser/http_parser.h
new file mode 100644
index 00000000..105ae510
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/nodejs-parser/http_parser.h
@@ -0,0 +1,362 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef http_parser_h
+#define http_parser_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Also update SONAME in the Makefile whenever you change these. */
+#define HTTP_PARSER_VERSION_MAJOR 2
+#define HTTP_PARSER_VERSION_MINOR 7
+#define HTTP_PARSER_VERSION_PATCH 0
+
+#include <sys/types.h>
+#if defined(_WIN32) && !defined(__MINGW32__) && \
+ (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
+#include <BaseTsd.h>
+#include <stddef.h>
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef HTTP_PARSER_STRICT
+# define HTTP_PARSER_STRICT 1
+#endif
+
+/* Maximium header size allowed. If the macro is not defined
+ * before including this header then the default is used. To
+ * change the maximum header size, define the macro in the build
+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
+ * the effective limit on the size of the header, define the macro
+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
+ */
+#ifndef HTTP_MAX_HEADER_SIZE
+# define HTTP_MAX_HEADER_SIZE (80*1024)
+#endif
+
+typedef struct http_parser http_parser;
+typedef struct http_parser_settings http_parser_settings;
+
+
+/* Callbacks should return non-zero to indicate an error. The parser will
+ * then halt execution.
+ *
+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
+ * returning '1' from on_headers_complete will tell the parser that it
+ * should not expect a body. This is used when receiving a response to a
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
+ * chunked' headers that indicate the presence of a body.
+ *
+ * Returning `2` from on_headers_complete will tell parser that it should not
+ * expect neither a body nor any futher responses on this connection. This is
+ * useful for handling responses to a CONNECT request which may not contain
+ * `Upgrade` or `Connection: upgrade` headers.
+ *
+ * http_data_cb does not return data chunks. It will be called arbitrarily
+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
+ * each providing just a few characters more data.
+ */
+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
+typedef int (*http_cb) (http_parser*);
+
+
+/* Request Methods */
+#define HTTP_METHOD_MAP(XX) \
+ XX(0, DELETE, DELETE) \
+ XX(1, GET, GET) \
+ XX(2, HEAD, HEAD) \
+ XX(3, POST, POST) \
+ XX(4, PUT, PUT) \
+ /* pathological */ \
+ XX(5, CONNECT, CONNECT) \
+ XX(6, OPTIONS, OPTIONS) \
+ XX(7, TRACE, TRACE) \
+ /* WebDAV */ \
+ XX(8, COPY, COPY) \
+ XX(9, LOCK, LOCK) \
+ XX(10, MKCOL, MKCOL) \
+ XX(11, MOVE, MOVE) \
+ XX(12, PROPFIND, PROPFIND) \
+ XX(13, PROPPATCH, PROPPATCH) \
+ XX(14, SEARCH, SEARCH) \
+ XX(15, UNLOCK, UNLOCK) \
+ XX(16, BIND, BIND) \
+ XX(17, REBIND, REBIND) \
+ XX(18, UNBIND, UNBIND) \
+ XX(19, ACL, ACL) \
+ /* subversion */ \
+ XX(20, REPORT, REPORT) \
+ XX(21, MKACTIVITY, MKACTIVITY) \
+ XX(22, CHECKOUT, CHECKOUT) \
+ XX(23, MERGE, MERGE) \
+ /* upnp */ \
+ XX(24, MSEARCH, M-SEARCH) \
+ XX(25, NOTIFY, NOTIFY) \
+ XX(26, SUBSCRIBE, SUBSCRIBE) \
+ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+ /* RFC-5789 */ \
+ XX(28, PATCH, PATCH) \
+ XX(29, PURGE, PURGE) \
+ /* CalDAV */ \
+ XX(30, MKCALENDAR, MKCALENDAR) \
+ /* RFC-2068, section 19.6.1.2 */ \
+ XX(31, LINK, LINK) \
+ XX(32, UNLINK, UNLINK) \
+
+enum http_method
+ {
+#define XX(num, name, string) HTTP_##name = num,
+ HTTP_METHOD_MAP(XX)
+#undef XX
+ };
+
+
+enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
+
+
+/* Flag values for http_parser.flags field */
+enum flags
+ { F_CHUNKED = 1 << 0
+ , F_CONNECTION_KEEP_ALIVE = 1 << 1
+ , F_CONNECTION_CLOSE = 1 << 2
+ , F_CONNECTION_UPGRADE = 1 << 3
+ , F_TRAILING = 1 << 4
+ , F_UPGRADE = 1 << 5
+ , F_SKIPBODY = 1 << 6
+ , F_CONTENTLENGTH = 1 << 7
+ };
+
+
+/* Map for errno-related constants
+ *
+ * The provided argument should be a macro that takes 2 arguments.
+ */
+#define HTTP_ERRNO_MAP(XX) \
+ /* No error */ \
+ XX(OK, "success") \
+ \
+ /* Callback-related errors */ \
+ XX(CB_message_begin, "the on_message_begin callback failed") \
+ XX(CB_url, "the on_url callback failed") \
+ XX(CB_header_field, "the on_header_field callback failed") \
+ XX(CB_header_value, "the on_header_value callback failed") \
+ XX(CB_headers_complete, "the on_headers_complete callback failed") \
+ XX(CB_body, "the on_body callback failed") \
+ XX(CB_message_complete, "the on_message_complete callback failed") \
+ XX(CB_status, "the on_status callback failed") \
+ XX(CB_chunk_header, "the on_chunk_header callback failed") \
+ XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
+ \
+ /* Parsing-related errors */ \
+ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
+ XX(HEADER_OVERFLOW, \
+ "too many header bytes seen; overflow detected") \
+ XX(CLOSED_CONNECTION, \
+ "data received after completed connection: close message") \
+ XX(INVALID_VERSION, "invalid HTTP version") \
+ XX(INVALID_STATUS, "invalid HTTP status code") \
+ XX(INVALID_METHOD, "invalid HTTP method") \
+ XX(INVALID_URL, "invalid URL") \
+ XX(INVALID_HOST, "invalid host") \
+ XX(INVALID_PORT, "invalid port") \
+ XX(INVALID_PATH, "invalid path") \
+ XX(INVALID_QUERY_STRING, "invalid query string") \
+ XX(INVALID_FRAGMENT, "invalid fragment") \
+ XX(LF_EXPECTED, "LF character expected") \
+ XX(INVALID_HEADER_TOKEN, "invalid character in header") \
+ XX(INVALID_CONTENT_LENGTH, \
+ "invalid character in content-length header") \
+ XX(UNEXPECTED_CONTENT_LENGTH, \
+ "unexpected content-length header") \
+ XX(INVALID_CHUNK_SIZE, \
+ "invalid character in chunk size header") \
+ XX(INVALID_CONSTANT, "invalid constant string") \
+ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
+ XX(STRICT, "strict mode assertion failed") \
+ XX(PAUSED, "parser is paused") \
+ XX(UNKNOWN, "an unknown error occurred")
+
+
+/* Define HPE_* values for each errno value above */
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
+enum http_errno {
+ HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+};
+#undef HTTP_ERRNO_GEN
+
+
+/* Get an http_errno value from an http_parser */
+#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
+
+
+struct http_parser {
+ /** PRIVATE **/
+ unsigned int type : 2; /* enum http_parser_type */
+ unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
+ unsigned int state : 7; /* enum state from http_parser.c */
+ unsigned int header_state : 7; /* enum header_state from http_parser.c */
+ unsigned int index : 7; /* index into current matcher */
+ unsigned int lenient_http_headers : 1;
+
+ uint32_t nread; /* # bytes read in various scenarios */
+ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
+
+ /** READ-ONLY **/
+ unsigned short http_major;
+ unsigned short http_minor;
+ unsigned int status_code : 16; /* responses only */
+ unsigned int method : 8; /* requests only */
+ unsigned int http_errno : 7;
+
+ /* 1 = Upgrade header was present and the parser has exited because of that.
+ * 0 = No upgrade header present.
+ * Should be checked when http_parser_execute() returns in addition to
+ * error checking.
+ */
+ unsigned int upgrade : 1;
+
+ /** PUBLIC **/
+ void *data; /* A pointer to get hook to the "connection" or "socket" object */
+};
+
+
+struct http_parser_settings {
+ http_cb on_message_begin;
+ http_data_cb on_url;
+ http_data_cb on_status;
+ http_data_cb on_header_field;
+ http_data_cb on_header_value;
+ http_cb on_headers_complete;
+ http_data_cb on_body;
+ http_cb on_message_complete;
+ /* When on_chunk_header is called, the current chunk length is stored
+ * in parser->content_length.
+ */
+ http_cb on_chunk_header;
+ http_cb on_chunk_complete;
+};
+
+
+enum http_parser_url_fields
+ { UF_SCHEMA = 0
+ , UF_HOST = 1
+ , UF_PORT = 2
+ , UF_PATH = 3
+ , UF_QUERY = 4
+ , UF_FRAGMENT = 5
+ , UF_USERINFO = 6
+ , UF_MAX = 7
+ };
+
+
+/* Result structure for http_parser_parse_url().
+ *
+ * Callers should index into field_data[] with UF_* values iff field_set
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
+ * because we probably have padding left over), we convert any port to
+ * a uint16_t.
+ */
+struct http_parser_url {
+ uint16_t field_set; /* Bitmask of (1 << UF_*) values */
+ uint16_t port; /* Converted UF_PORT string */
+
+ struct {
+ uint16_t off; /* Offset into buffer in which field starts */
+ uint16_t len; /* Length of run in buffer */
+ } field_data[UF_MAX];
+};
+
+
+/* Returns the library version. Bits 16-23 contain the major version number,
+ * bits 8-15 the minor version number and bits 0-7 the patch level.
+ * Usage example:
+ *
+ * unsigned long version = http_parser_version();
+ * unsigned major = (version >> 16) & 255;
+ * unsigned minor = (version >> 8) & 255;
+ * unsigned patch = version & 255;
+ * printf("http_parser v%u.%u.%u\n", major, minor, patch);
+ */
+unsigned long http_parser_version(void);
+
+void http_parser_init(http_parser *parser, enum http_parser_type type);
+
+
+/* Initialize http_parser_settings members to 0
+ */
+void http_parser_settings_init(http_parser_settings *settings);
+
+
+/* Executes the parser. Returns number of parsed bytes. Sets
+ * `parser->http_errno` on error. */
+size_t http_parser_execute(http_parser *parser,
+ const http_parser_settings *settings,
+ const char *data,
+ size_t len);
+
+
+/* If http_should_keep_alive() in the on_headers_complete or
+ * on_message_complete callback returns 0, then this should be
+ * the last message on the connection.
+ * If you are the server, respond with the "Connection: close" header.
+ * If you are the client, close the connection.
+ */
+int http_should_keep_alive(const http_parser *parser);
+
+/* Returns a string version of the HTTP method. */
+const char *http_method_str(enum http_method m);
+
+/* Return a string name of the given error */
+const char *http_errno_name(enum http_errno err);
+
+/* Return a string description of the given error */
+const char *http_errno_description(enum http_errno err);
+
+/* Initialize all http_parser_url members to 0 */
+void http_parser_url_init(struct http_parser_url *u);
+
+/* Parse a URL; return nonzero on failure */
+int http_parser_parse_url(const char *buf, size_t buflen,
+ int is_connect,
+ struct http_parser_url *u);
+
+/* Pause or un-pause the parser; a nonzero value pauses */
+void http_parser_pause(http_parser *parser, int paused);
+
+/* Checks if this is the final chunk of the body. */
+int http_body_is_final(const http_parser *parser);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/boost/libs/beast/test/bench/parser/nodejs_parser.cpp b/src/boost/libs/beast/test/bench/parser/nodejs_parser.cpp
new file mode 100644
index 00000000..cc14b981
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/parser/nodejs_parser.cpp
@@ -0,0 +1,31 @@
+//
+// 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
+//
+
+#if defined(__GNUC__) && (__GNUC__ >= 7)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#endif
+
+#include <boost/config.hpp>
+
+#ifdef BOOST_MSVC
+# pragma warning (push)
+# pragma warning (disable: 4127) // conditional expression is constant
+# pragma warning (disable: 4244) // integer conversion, possible loss of data
+#endif
+
+#include "nodejs-parser/http_parser.c"
+
+#ifdef BOOST_MSVC
+# pragma warning (pop)
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ >= 7)
+#pragma GCC diagnostic pop
+#endif
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 00000000..68afd6f5
--- /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
diff --git a/src/boost/libs/beast/test/bench/utf8_checker/CMakeLists.txt b/src/boost/libs/beast/test/bench/utf8_checker/CMakeLists.txt
new file mode 100644
index 00000000..6164127a
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/utf8_checker/CMakeLists.txt
@@ -0,0 +1,25 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+GroupSources (include/boost/beast beast)
+GroupSources (test/bench/utf8_checker "/")
+
+add_executable (bench-utf8-checker
+ ${BOOST_BEAST_FILES}
+ Jamfile
+ bench_utf8_checker.cpp
+)
+
+target_link_libraries(bench-utf8-checker
+ lib-asio
+ lib-beast
+ lib-test
+ )
+
+set_property(TARGET bench-utf8-checker PROPERTY FOLDER "tests-bench")
diff --git a/src/boost/libs/beast/test/bench/utf8_checker/Jamfile b/src/boost/libs/beast/test/bench/utf8_checker/Jamfile
new file mode 100644
index 00000000..1d352f38
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/utf8_checker/Jamfile
@@ -0,0 +1,19 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+exe bench-utf8-checker : bench_utf8_checker.cpp
+ : requirements
+ <library>/boost/beast/test//lib-test
+ ;
+
+explicit bench-utf8-checker ;
+
+alias run-tests :
+ [ compile bench_utf8_checker.cpp ]
+ ;
diff --git a/src/boost/libs/beast/test/bench/utf8_checker/bench_utf8_checker.cpp b/src/boost/libs/beast/test/bench/utf8_checker/bench_utf8_checker.cpp
new file mode 100644
index 00000000..e7f81004
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/utf8_checker/bench_utf8_checker.cpp
@@ -0,0 +1,157 @@
+//
+// 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
+//
+
+#include <boost/beast/websocket/detail/utf8_checker.hpp>
+#include <boost/beast/_experimental/unit_test/suite.hpp>
+#include <chrono>
+#include <random>
+
+#ifndef BEAST_USE_BOOST_LOCALE_BENCHMARK
+#define BEAST_USE_BOOST_LOCALE_BENCHMARK 0
+#endif
+
+#if BEAST_USE_BOOST_LOCALE_BENCHMARK
+#include <boost/locale.hpp>
+#endif
+
+namespace boost {
+namespace beast {
+
+class utf8_checker_test : public beast::unit_test::suite
+{
+ std::mt19937 rng_;
+
+public:
+ using size_type = std::uint64_t;
+
+ class timer
+ {
+ public:
+ using clock_type =
+ std::chrono::system_clock;
+
+ private:
+ clock_type::time_point when_;
+
+ public:
+ using duration =
+ clock_type::duration;
+
+ timer()
+ : when_(clock_type::now())
+ {
+ }
+
+ duration
+ elapsed() const
+ {
+ return clock_type::now() - when_;
+ }
+ };
+
+ template<class UInt = std::size_t>
+ UInt
+ rand(std::size_t n)
+ {
+ return static_cast<UInt>(
+ std::uniform_int_distribution<
+ std::size_t>{0, n-1}(rng_));
+ }
+
+ static
+ inline
+ size_type
+ throughput(std::chrono::duration<
+ double> const& elapsed, size_type items)
+ {
+ using namespace std::chrono;
+ return static_cast<size_type>(
+ 1 / (elapsed/items).count());
+ }
+
+ std::string
+ corpus(std::size_t n)
+ {
+ std::string s;
+ s.reserve(n);
+ while(n--)
+ s.push_back(static_cast<char>(
+ ' ' + rand(95)));
+ return s;
+ }
+
+ void
+ checkBeast(std::string const& s)
+ {
+ beast::websocket::detail::check_utf8(
+ s.data(), s.size());
+ }
+
+#if BEAST_USE_BOOST_LOCALE_BENCHMARK
+ void
+ checkLocale(std::string const& s)
+ {
+ using namespace boost::locale;
+ auto p = s.begin();
+ auto const e = s.end();
+ while(p != e)
+ {
+ auto cp = utf::utf_traits<char>::decode(p, e);
+ if(cp == utf::illegal)
+ break;
+ }
+ }
+#endif
+
+ template<class F>
+ typename timer::clock_type::duration
+ test(F const& f)
+ {
+ timer t;
+ f();
+ return t.elapsed();
+ }
+
+ void
+ run() override
+ {
+ auto const s = corpus(32 * 1024 * 1024);
+ for(int i = 0; i < 5; ++ i)
+ {
+ auto const elapsed = test([&]{
+ checkBeast(s);
+ checkBeast(s);
+ checkBeast(s);
+ checkBeast(s);
+ checkBeast(s);
+ });
+ log << "beast: " << throughput(elapsed, s.size()) << " char/s" << std::endl;
+ }
+ #if BEAST_USE_BOOST_LOCALE_BENCHMARK
+ for(int i = 0; i < 5; ++ i)
+ {
+ auto const elapsed = test([&]{
+ checkLocale(s);
+ checkLocale(s);
+ checkLocale(s);
+ checkLocale(s);
+ checkLocale(s);
+ });
+ log << "locale: " << throughput(elapsed, s.size()) << " char/s" << std::endl;
+ }
+ #endif
+ pass();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(beast,benchmarks,utf8_checker);
+
+} // beast
+} // boost
+
diff --git a/src/boost/libs/beast/test/bench/wsload/CMakeLists.txt b/src/boost/libs/beast/test/bench/wsload/CMakeLists.txt
new file mode 100644
index 00000000..7a6c5876
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/wsload/CMakeLists.txt
@@ -0,0 +1,24 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+GroupSources (include/boost/beast beast)
+GroupSources (test/bench/wsload "/")
+
+add_executable (bench-wsload
+ ${BOOST_BEAST_FILES}
+ Jamfile
+ wsload.cpp
+ )
+
+target_link_libraries(bench-wsload
+ lib-asio
+ lib-beast
+ )
+
+set_property(TARGET bench-wsload PROPERTY FOLDER "tests-bench")
diff --git a/src/boost/libs/beast/test/bench/wsload/Jamfile b/src/boost/libs/beast/test/bench/wsload/Jamfile
new file mode 100644
index 00000000..82706e15
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/wsload/Jamfile
@@ -0,0 +1,18 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+exe wsload :
+ wsload.cpp
+ ;
+
+explicit wsload ;
+
+alias run-tests :
+ [ compile wsload.cpp : : wsload-compile ]
+ ;
diff --git a/src/boost/libs/beast/test/bench/wsload/wsload.cpp b/src/boost/libs/beast/test/bench/wsload/wsload.cpp
new file mode 100644
index 00000000..f697d9b8
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/wsload/wsload.cpp
@@ -0,0 +1,338 @@
+//
+// 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
+//
+
+//------------------------------------------------------------------------------
+//
+// wsload
+//
+// Measure the performance of a WebSocket server
+//
+//------------------------------------------------------------------------------
+
+#include <boost/beast/core.hpp>
+#include <boost/beast/websocket.hpp>
+#include <boost/beast/_experimental/unit_test/dstream.hpp>
+#include <boost/asio.hpp>
+#include <atomic>
+#include <chrono>
+#include <cstdlib>
+#include <functional>
+#include <iostream>
+#include <memory>
+#include <mutex>
+#include <random>
+#include <thread>
+#include <vector>
+
+namespace beast = boost::beast; // from <boost/beast.hpp>
+namespace http = beast::http; // from <boost/beast/http.hpp>
+namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
+namespace net = boost::asio; // from <boost/asio.hpp>
+using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
+
+class test_buffer
+{
+ char data_[4096];
+ net::const_buffer b_;
+
+public:
+ using const_iterator =
+ net::const_buffer const*;
+
+ using value_type = net::const_buffer;
+
+ test_buffer()
+ : b_(data_, sizeof(data_))
+ {
+ std::mt19937_64 rng;
+ std::uniform_int_distribution<unsigned short> dist;
+ for(auto& c : data_)
+ c = static_cast<unsigned char>(dist(rng));
+ }
+
+ const_iterator
+ begin() const
+ {
+ return &b_;
+ }
+
+ const_iterator
+ end() const
+ {
+ return begin() + 1;
+ }
+};
+
+class report
+{
+ std::mutex m_;
+ std::size_t bytes_ = 0;
+ std::size_t messages_ = 0;
+
+public:
+ void
+ insert(std::size_t messages, std::size_t bytes)
+ {
+ std::lock_guard<std::mutex> lock(m_);
+ bytes_ += bytes;
+ messages_ += messages;
+ }
+
+ std::size_t
+ bytes() const
+ {
+ return bytes_;
+ }
+
+ std::size_t
+ messages() const
+ {
+ return messages_;
+ }
+};
+
+void
+fail(beast::error_code ec, char const* what)
+{
+ std::cerr << what << ": " << ec.message() << "\n";
+}
+
+class connection
+ : public std::enable_shared_from_this<connection>
+{
+ websocket::stream<tcp::socket> ws_;
+ tcp::endpoint ep_;
+ std::size_t messages_;
+ report& rep_;
+ test_buffer const& tb_;
+ net::strand<
+ net::io_context::executor_type> strand_;
+ beast::flat_buffer buffer_;
+ std::mt19937_64 rng_;
+ std::size_t count_ = 0;
+ std::size_t bytes_ = 0;
+
+public:
+ connection(
+ net::io_context& ioc,
+ tcp::endpoint const& ep,
+ std::size_t messages,
+ bool deflate,
+ report& rep,
+ test_buffer const& tb)
+ : ws_(ioc)
+ , ep_(ep)
+ , messages_(messages)
+ , rep_(rep)
+ , tb_(tb)
+ , strand_(ioc.get_executor())
+ {
+ websocket::permessage_deflate pmd;
+ pmd.client_enable = deflate;
+ ws_.set_option(pmd);
+ ws_.binary(true);
+ ws_.auto_fragment(false);
+ ws_.write_buffer_bytes(64 * 1024);
+ }
+
+ ~connection()
+ {
+ rep_.insert(count_, bytes_);
+ }
+
+ void
+ run()
+ {
+ ws_.next_layer().async_connect(ep_,
+ beast::bind_front_handler(
+ &connection::on_connect,
+ this->shared_from_this()));
+ }
+
+private:
+ void
+ on_connect(beast::error_code ec)
+ {
+ if(ec)
+ return fail(ec, "on_connect");
+
+ ws_.async_handshake(
+ ep_.address().to_string() + ":" + std::to_string(ep_.port()),
+ "/",
+ beast::bind_front_handler(
+ &connection::on_handshake,
+ this->shared_from_this()));
+ }
+
+ void
+ on_handshake(beast::error_code ec)
+ {
+ if(ec)
+ return fail(ec, "handshake");
+
+ do_write();
+ }
+
+ void
+ do_write()
+ {
+ std::geometric_distribution<std::size_t> dist{
+ double(4) / beast::buffer_bytes(tb_)};
+ ws_.async_write_some(true,
+ beast::buffers_prefix(dist(rng_), tb_),
+ beast::bind_front_handler(
+ &connection::on_write,
+ this->shared_from_this()));
+ }
+
+ void
+ on_write(beast::error_code ec, std::size_t)
+ {
+ if(ec)
+ return fail(ec, "write");
+
+ if(messages_--)
+ return do_read();
+
+ ws_.async_close({},
+ beast::bind_front_handler(
+ &connection::on_close,
+ this->shared_from_this()));
+ }
+
+ void
+ do_read()
+ {
+ ws_.async_read(buffer_,
+ beast::bind_front_handler(
+ &connection::on_read,
+ this->shared_from_this()));
+ }
+
+ void
+ on_read(beast::error_code ec, std::size_t)
+ {
+ if(ec)
+ return fail(ec, "read");
+
+ ++count_;
+ bytes_ += buffer_.size();
+ buffer_.consume(buffer_.size());
+ do_write();
+ }
+
+ void
+ on_close(beast::error_code ec)
+ {
+ if(ec)
+ return fail(ec, "close");
+ }
+};
+
+class timer
+{
+ using clock_type =
+ std::chrono::system_clock;
+
+ clock_type::time_point when_;
+
+public:
+ using duration =
+ clock_type::duration;
+
+ timer()
+ : when_(clock_type::now())
+ {
+ }
+
+ duration
+ elapsed() const
+ {
+ return clock_type::now() - when_;
+ }
+};
+
+inline
+std::uint64_t
+throughput(
+ std::chrono::duration<double> const& elapsed,
+ std::uint64_t items)
+{
+ using namespace std::chrono;
+ return static_cast<std::uint64_t>(
+ 1 / (elapsed/items).count());
+}
+
+int
+main(int argc, char** argv)
+{
+ beast::unit_test::dstream dout(std::cerr);
+
+ try
+ {
+ // Check command line arguments.
+ if(argc != 8)
+ {
+ std::cerr <<
+ "Usage: bench-wsload <address> <port> <trials> <messages> <workers> <threads> <compression:0|1>";
+ return EXIT_FAILURE;
+ }
+
+ auto const address = net::ip::make_address(argv[1]);
+ auto const port = static_cast<unsigned short>(std::atoi(argv[2]));
+ auto const trials = static_cast<std::size_t>(std::atoi(argv[3]));
+ auto const messages= static_cast<std::size_t>(std::atoi(argv[4]));
+ auto const workers = static_cast<std::size_t>(std::atoi(argv[5]));
+ auto const threads = static_cast<std::size_t>(std::atoi(argv[6]));
+ auto const deflate = std::atoi(argv[7]) != 0;
+ auto const work = (messages + workers - 1) / workers;
+ test_buffer tb;
+ for(auto i = trials; i != 0; --i)
+ {
+ report rep;
+ net::io_context ioc{1};
+ for(auto j = workers; j; --j)
+ {
+ auto sp =
+ std::make_shared<connection>(
+ ioc,
+ tcp::endpoint{address, port},
+ work,
+ deflate,
+ rep,
+ tb);
+ sp->run();
+ }
+ timer clock;
+ std::vector<std::thread> tv;
+ if(threads > 1)
+ {
+ tv.reserve(threads);
+ tv.emplace_back([&ioc]{ ioc.run(); });
+ }
+ ioc.run();
+ for(auto& t : tv)
+ t.join();
+ auto const elapsed = clock.elapsed();
+ dout <<
+ throughput(elapsed, rep.bytes()) << " bytes/s in " <<
+ (std::chrono::duration_cast<
+ std::chrono::milliseconds>(
+ elapsed).count() / 1000.) << "ms and " <<
+ rep.bytes() << " bytes" << std::endl;
+ }
+ }
+ catch(std::exception const& e)
+ {
+ std::cerr << "Error: " << e.what() << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/boost/libs/beast/test/bench/zlib/CMakeLists.txt b/src/boost/libs/beast/test/bench/zlib/CMakeLists.txt
new file mode 100644
index 00000000..a030ed93
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/zlib/CMakeLists.txt
@@ -0,0 +1,31 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+GroupSources (include/boost/beast beast)
+GroupSources (test/extern/zlib-1.2.11 zlib)
+GroupSources (test/bench/zlib "/")
+
+if (MSVC)
+ set_source_files_properties (${ZLIB_SOURCES} PROPERTIES COMPILE_FLAGS "/wd4127 /wd4131 /wd4244")
+endif()
+
+add_executable (bench-zlib
+ ${BOOST_BEAST_FILES}
+ ${ZLIB_SOURCES}
+ Jamfile
+ deflate_stream.cpp
+ inflate_stream.cpp
+)
+target_link_libraries(bench-zlib
+ lib-asio
+ lib-beast
+ lib-test
+ )
+
+set_property(TARGET bench-zlib PROPERTY FOLDER "tests-bench")
diff --git a/src/boost/libs/beast/test/bench/zlib/Jamfile b/src/boost/libs/beast/test/bench/zlib/Jamfile
new file mode 100644
index 00000000..6e210c29
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/zlib/Jamfile
@@ -0,0 +1,15 @@
+#
+# Copyright (c) 2016-2017 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
+#
+
+exe bench-zlib :
+ $(ZLIB_SOURCES)
+ deflate_stream.cpp
+ inflate_stream.cpp
+ /boost/beast/test//lib-test
+ ;
diff --git a/src/boost/libs/beast/test/bench/zlib/deflate_stream.cpp b/src/boost/libs/beast/test/bench/zlib/deflate_stream.cpp
new file mode 100644
index 00000000..321fa98c
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/zlib/deflate_stream.cpp
@@ -0,0 +1,199 @@
+//
+// 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
+//
+
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/zlib/deflate_stream.hpp>
+#include <boost/beast/test/throughput.hpp>
+#include <boost/beast/_experimental/unit_test/dstream.hpp>
+#include <boost/beast/_experimental/unit_test/suite.hpp>
+#include <iomanip>
+#include <random>
+#include <string>
+
+#include "zlib-1.2.11/zlib.h"
+
+namespace boost {
+namespace beast {
+namespace zlib {
+
+class deflate_stream_test : public beast::unit_test::suite
+{
+public:
+ // Lots of repeats, limited char range
+ static
+ std::string
+ corpus1(std::size_t n)
+ {
+ static std::string const alphabet{
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ };
+ std::string s;
+ s.reserve(n + 5);
+ std::mt19937 g;
+ std::uniform_int_distribution<std::size_t> d0{
+ 0, alphabet.size() - 1};
+ std::uniform_int_distribution<std::size_t> d1{
+ 1, 5};
+ while(s.size() < n)
+ {
+ auto const rep = d1(g);
+ auto const ch = alphabet[d0(g)];
+ s.insert(s.end(), rep, ch);
+ }
+ s.resize(n);
+ return s;
+ }
+
+ // Random data
+ static
+ std::string
+ corpus2(std::size_t n)
+ {
+ std::string s;
+ s.reserve(n);
+ std::mt19937 g;
+ std::uniform_int_distribution<std::uint32_t> d0{0, 255};
+ while(n--)
+ s.push_back(static_cast<char>(d0(g)));
+ return s;
+ }
+
+ std::string
+ doDeflateBeast(string_view const& in)
+ {
+ z_params zs;
+ deflate_stream ds;
+ ds.reset(
+ Z_DEFAULT_COMPRESSION,
+ 15,
+ 4,
+ Strategy::normal);
+ std::string out;
+ out.resize(deflate_upper_bound(in.size()));
+ zs.next_in = in.data();
+ zs.avail_in = in.size();
+ zs.next_out = &out[0];
+ zs.avail_out = out.size();
+ error_code ec;
+ ds.write(zs, Flush::full, ec);
+ BEAST_EXPECTS(! ec, ec.message());
+ out.resize(zs.total_out);
+ return out;
+ }
+
+ std::string
+ doDeflateZLib(string_view const& in)
+ {
+ int result;
+ z_stream zs;
+ memset(&zs, 0, sizeof(zs));
+ result = deflateInit2(
+ &zs,
+ Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED,
+ -15,
+ 4,
+ Z_DEFAULT_STRATEGY
+ );
+ if(result != Z_OK)
+ throw std::logic_error("deflateInit2 failed");
+ std::string out;
+ out.resize(deflateBound(&zs,
+ static_cast<uLong>(in.size())));
+ zs.next_in = (Bytef*)in.data();
+ zs.avail_in = static_cast<uInt>(in.size());
+ zs.next_out = (Bytef*)&out[0];
+ zs.avail_out = static_cast<uInt>(out.size());
+ result = deflate(&zs, Z_FULL_FLUSH);
+ if(result != Z_OK)
+ throw std::logic_error("deflate failed");
+ out.resize(zs.total_out);
+ deflateEnd(&zs);
+ return out;
+ }
+
+ void
+ doCorpus(
+ std::size_t size,
+ std::size_t repeat)
+ {
+ std::size_t constexpr trials = 3;
+ auto const c1 = corpus1(size);
+ auto const c2 = corpus2(size);
+ log <<
+ std::left << std::setw(10) << (std::to_string(size) + "B") <<
+ std::right << std::setw(12) << "Beast" << " " <<
+ std::right << std::setw(12) << "ZLib" <<
+ std::endl;
+ for(std::size_t i = 0; i < trials; ++i)
+ {
+ test::timer t;
+ log << std::left << std::setw(10) << "corpus1";
+ std::string out1;
+ for(std::size_t j = 0; j < repeat; ++j)
+ out1 = doDeflateBeast(c1);
+ auto const t1 =
+ test::throughput(t.elapsed(), size * repeat);
+ log << std::right << std::setw(12) << t1 << " B/s ";
+ std::string out2;
+ for(std::size_t j = 0; j < repeat; ++j)
+ out2 = doDeflateZLib(c1);
+ BEAST_EXPECT(out1 == out2);
+ auto const t2 =
+ test::throughput(t.elapsed(), size * repeat);
+ log << std::right << std::setw(12) << t2 << " B/s";
+ log << std::right << std::setw(12) <<
+ unsigned(double(t1)*100/t2-100) << "%";
+ log << std::endl;
+ }
+ for(std::size_t i = 0; i < trials; ++i)
+ {
+ test::timer t;
+ log << std::left << std::setw(10) << "corpus2";
+ std::string out1;
+ for(std::size_t j = 0; j < repeat; ++j)
+ out1 = doDeflateBeast(c2);
+ auto const t1 =
+ test::throughput(t.elapsed(), size * repeat);
+ log << std::right << std::setw(12) << t1 << " B/s ";
+ std::string out2;
+ for(std::size_t j = 0; j < repeat; ++j)
+ out2 = doDeflateZLib(c2);
+ BEAST_EXPECT(out1 == out2);
+ auto const t2 =
+ test::throughput(t.elapsed(), size * repeat);
+ log << std::right << std::setw(12) << t2 << " B/s";
+ log << std::right << std::setw(12) <<
+ unsigned(double(t1)*100/t2-100) << "%";
+ log << std::endl;
+ }
+ log << std::endl;
+ }
+
+ void
+ doBench()
+ {
+ doCorpus( 16 * 1024, 512);
+ doCorpus( 1024 * 1024, 8);
+ doCorpus(8 * 1024 * 1024, 1);
+ }
+
+ void
+ run() override
+ {
+ doBench();
+ pass();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(beast,zlib,deflate_stream);
+
+} // zlib
+} // beast
+} // boost
diff --git a/src/boost/libs/beast/test/bench/zlib/inflate_stream.cpp b/src/boost/libs/beast/test/bench/zlib/inflate_stream.cpp
new file mode 100644
index 00000000..ea8d6d98
--- /dev/null
+++ b/src/boost/libs/beast/test/bench/zlib/inflate_stream.cpp
@@ -0,0 +1,244 @@
+//
+// 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
+//
+
+#include <boost/beast/core/string.hpp>
+#include <boost/beast/zlib/inflate_stream.hpp>
+#include <boost/beast/test/throughput.hpp>
+#include <boost/beast/_experimental/unit_test/dstream.hpp>
+#include <boost/beast/_experimental/unit_test/suite.hpp>
+#include <iomanip>
+#include <random>
+#include <string>
+
+#include "zlib-1.2.11/zlib.h"
+
+namespace boost {
+namespace beast {
+namespace zlib {
+
+class inflate_stream_test : public beast::unit_test::suite
+{
+public:
+ // Lots of repeats, limited char range
+ static
+ std::string
+ corpus1(std::size_t n)
+ {
+ static std::string const alphabet{
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ };
+ std::string s;
+ s.reserve(n + 5);
+ std::mt19937 g;
+ std::uniform_int_distribution<std::size_t> d0{
+ 0, alphabet.size() - 1};
+ std::uniform_int_distribution<std::size_t> d1{
+ 1, 5};
+ while(s.size() < n)
+ {
+ auto const rep = d1(g);
+ auto const ch = alphabet[d0(g)];
+ s.insert(s.end(), rep, ch);
+ }
+ s.resize(n);
+ return s;
+ }
+
+ // Random data
+ static
+ std::string
+ corpus2(std::size_t n)
+ {
+ std::string s;
+ s.reserve(n);
+ std::mt19937 g;
+ std::uniform_int_distribution<std::uint32_t> d0{0, 255};
+ while(n--)
+ s.push_back(static_cast<char>(d0(g)));
+ return s;
+ }
+
+ static
+ std::string
+ compress(string_view const& in)
+ {
+ int result;
+ z_stream zs;
+ memset(&zs, 0, sizeof(zs));
+ result = deflateInit2(
+ &zs,
+ Z_DEFAULT_COMPRESSION,
+ Z_DEFLATED,
+ -15,
+ 4,
+ Z_DEFAULT_STRATEGY);
+ if(result != Z_OK)
+ throw std::logic_error("deflateInit2 failed");
+ zs.next_in = (Bytef*)in.data();
+ zs.avail_in = static_cast<uInt>(in.size());
+ std::string out;
+ out.resize(deflateBound(&zs,
+ static_cast<uLong>(in.size())));
+ zs.next_in = (Bytef*)in.data();
+ zs.avail_in = static_cast<uInt>(in.size());
+ zs.next_out = (Bytef*)&out[0];
+ zs.avail_out = static_cast<uInt>(out.size());
+ result = deflate(&zs, Z_FULL_FLUSH);
+ if(result != Z_OK)
+ throw std::logic_error("deflate failed");
+ out.resize(zs.total_out);
+ deflateEnd(&zs);
+ return out;
+ }
+
+ std::string
+ doInflateBeast(string_view const& in)
+ {
+ z_params zs;
+ std::string out;
+ inflate_stream is;
+ zs.next_in = &in[0];
+ zs.avail_in = in.size();
+ out.resize(in.size());
+ zs.next_out = &out[0];
+ zs.avail_out = out.size();
+ for(;;)
+ {
+ error_code ec;
+ is.write(zs, Flush::sync, ec);
+ if(ec)
+ throw std::logic_error("inflate_stream failed");
+ if(zs.avail_out > 0)
+ break;
+ out.resize(2 * zs.total_out);
+ zs.next_out = &out[zs.total_out];
+ zs.avail_out = out.size() - zs.total_out;
+ }
+ out.resize(zs.total_out);
+ return out;
+ }
+
+ std::string
+ doInflateZLib(string_view const& in)
+ {
+ int result;
+ z_stream zs;
+ std::string out;
+ memset(&zs, 0, sizeof(zs));
+ result = inflateInit2(&zs, -15);
+ zs.next_in = (Bytef*)in.data();
+ zs.avail_in = static_cast<uInt>(in.size());
+ out.resize(in.size());
+ zs.next_out = (Bytef*)&out[0];
+ zs.avail_out = static_cast<uInt>(out.size());
+ for(;;)
+ {
+ result = inflate(&zs, Z_SYNC_FLUSH);
+ if( result == Z_NEED_DICT ||
+ result == Z_DATA_ERROR ||
+ result == Z_MEM_ERROR)
+ {
+ throw std::logic_error("inflate failed");
+ }
+ if(zs.avail_out > 0)
+ break;
+ if(result == Z_STREAM_END)
+ break;
+ out.resize(2 * zs.total_out);
+ zs.next_out = (Bytef*)&out[zs.total_out];
+ zs.avail_out = static_cast<uInt>(
+ out.size() - zs.total_out);
+ }
+ out.resize(zs.total_out);
+ inflateEnd(&zs);
+ return out;
+ }
+
+ void
+ doCorpus(
+ std::size_t size,
+ std::size_t repeat)
+ {
+ std::size_t constexpr trials = 3;
+ std::uint64_t constexpr scale = 16;
+ auto const c1 = corpus1(size);
+ auto const c2 = corpus2(size * scale);
+ auto const in1 = compress(c1);
+ auto const in2 = compress(c2);
+ log <<
+ std::left << std::setw(10) << (std::to_string(size) + "B") <<
+ std::right << std::setw(12) << "Beast" << " " <<
+ std::right << std::setw(12) << "ZLib" <<
+ std::endl;
+ for(std::size_t i = 0; i < trials; ++i)
+ {
+ test::timer t;
+ log << std::left << std::setw(10) << "corpus1";
+ std::string out;
+ for(std::size_t j = 0; j < repeat; ++j)
+ out = doInflateBeast(in1);
+ BEAST_EXPECT(out == c1);
+ auto const t1 =
+ test::throughput(t.elapsed(), size * repeat);
+ log << std::right << std::setw(12) << t1 << " B/s ";
+ for(std::size_t j = 0; j < repeat; ++j)
+ out = doInflateZLib(in1);
+ BEAST_EXPECT(out == c1);
+ auto const t2 =
+ test::throughput(t.elapsed(), size * repeat);
+ log << std::right << std::setw(12) << t2 << " B/s";
+ log << std::right << std::setw(12) <<
+ unsigned(double(t1)*100/t2-100) << "%";
+ log << std::endl;
+ }
+ for(std::size_t i = 0; i < trials; ++i)
+ {
+ test::timer t;
+ log << std::left << std::setw(10) << "corpus2";
+ std::string out;
+ for(std::size_t j = 0; j < repeat; ++j)
+ out = doInflateBeast(in2);
+ BEAST_EXPECT(out == c2);
+ auto const t1 =
+ test::throughput(t.elapsed(), size * scale * repeat);
+ log << std::right << std::setw(12) << t1 << " B/s ";
+ for(std::size_t j = 0; j < repeat; ++j)
+ out = doInflateZLib(in2);
+ BEAST_EXPECT(out == c2);
+ auto const t2 =
+ test::throughput(t.elapsed(), size * scale * repeat);
+ log << std::right << std::setw(12) << t2 << " B/s";
+ log << std::right << std::setw(12) <<
+ unsigned(double(t1)*100/t2-100) << "%";
+ log << std::endl;
+ }
+ log << std::endl;
+ }
+
+ void
+ doBench()
+ {
+ doCorpus( 1 * 1024 * 1024, 64);
+ doCorpus( 4 * 1024 * 1024, 16);
+ doCorpus( 16 * 1024 * 1024, 8);
+ }
+
+ void
+ run() override
+ {
+ doBench();
+ pass();
+ }
+};
+
+BEAST_DEFINE_TESTSUITE(beast,zlib,inflate_stream);
+
+} // zlib
+} // beast
+} // boost