diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/boost/libs/beast/example | |
parent | Initial commit. (diff) | |
download | ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.tar.xz ceph-19fcec84d8d7d21e796c7624e521b60d28ee21ed.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/beast/example')
149 files changed, 25944 insertions, 0 deletions
diff --git a/src/boost/libs/beast/example/CMakeLists.txt b/src/boost/libs/beast/example/CMakeLists.txt new file mode 100644 index 000000000..950c29db9 --- /dev/null +++ b/src/boost/libs/beast/example/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Copyright (c) 2013-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 (advanced) +add_subdirectory (http) +add_subdirectory (websocket) + +add_subdirectory (echo-op) diff --git a/src/boost/libs/beast/example/Jamfile b/src/boost/libs/beast/example/Jamfile new file mode 100644 index 000000000..8b49d5fd4 --- /dev/null +++ b/src/boost/libs/beast/example/Jamfile @@ -0,0 +1,32 @@ +# +# Copyright (c) 2013-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 +# + +import testing ; +import ../../config/checks/config : requires ; + +project /boost/beast/example + : requirements + [ requires + cxx11_constexpr + cxx11_decltype + cxx11_hdr_tuple + #cxx11_sfinae_expr # Every MSVC fails this + cxx11_template_aliases + cxx11_variadic_templates + ] + <library>/boost/beast//lib-asio/<link>static + <boost.beast.separate-compilation>on:<library>/boost/beast//lib-beast/<link>static + ; + +build-project advanced ; +build-project http ; +build-project websocket ; + +# legacy +build-project echo-op ; diff --git a/src/boost/libs/beast/example/advanced/CMakeLists.txt b/src/boost/libs/beast/example/advanced/CMakeLists.txt new file mode 100644 index 000000000..af83e043b --- /dev/null +++ b/src/boost/libs/beast/example/advanced/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# Copyright (c) 2013-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 (server) +add_subdirectory (server-flex) diff --git a/src/boost/libs/beast/example/advanced/Jamfile b/src/boost/libs/beast/example/advanced/Jamfile new file mode 100644 index 000000000..d1053a900 --- /dev/null +++ b/src/boost/libs/beast/example/advanced/Jamfile @@ -0,0 +1,13 @@ +# +# Copyright (c) 2013-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 +# + +build-project server ; + +# SSL +build-project server-flex ; diff --git a/src/boost/libs/beast/example/advanced/server-flex/CMakeLists.txt b/src/boost/libs/beast/example/advanced/server-flex/CMakeLists.txt new file mode 100644 index 000000000..d6075b277 --- /dev/null +++ b/src/boost/libs/beast/example/advanced/server-flex/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/advanced/server-flex "/") + + add_executable (advanced-server-flex + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + advanced_server_flex.cpp + ) + + set_property(TARGET advanced-server-flex PROPERTY FOLDER "example-advanced-server") + + target_link_libraries (advanced-server-flex + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/advanced/server-flex/Jamfile b/src/boost/libs/beast/example/advanced/server-flex/Jamfile new file mode 100644 index 000000000..e7bd844aa --- /dev/null +++ b/src/boost/libs/beast/example/advanced/server-flex/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe advanced-server-flex : + advanced_server_flex.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/advanced/server-flex/advanced_server_flex.cpp b/src/boost/libs/beast/example/advanced/server-flex/advanced_server_flex.cpp new file mode 100644 index 000000000..b0c889bf9 --- /dev/null +++ b/src/boost/libs/beast/example/advanced/server-flex/advanced_server_flex.cpp @@ -0,0 +1,1030 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: Advanced server, flex (plain + SSL) +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/dispatch.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/asio/steady_timer.hpp> +#include <boost/asio/strand.hpp> +#include <boost/make_unique.hpp> +#include <boost/optional.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#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> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + // ssl::error::stream_truncated, also known as an SSL "short read", + // indicates the peer closed the connection without performing the + // required closing handshake (for example, Google does this to + // improve performance). Generally this can be a security issue, + // but if your communication protocol is self-terminated (as + // it is with both HTTP and WebSocket) then you may simply + // ignore the lack of close_notify. + // + // https://github.com/boostorg/beast/issues/38 + // + // https://security.stackexchange.com/questions/91435/how-to-handle-a-malicious-ssl-tls-shutdown + // + // When a short read would cut off the end of an HTTP message, + // Beast returns the error beast::http::error::partial_message. + // Therefore, if we see a short read here, it has occurred + // after the message has been completed, so it is safe to ignore it. + + if(ec == net::ssl::error::stream_truncated) + return; + + std::cerr << what << ": " << ec.message() << "\n"; +} + +//------------------------------------------------------------------------------ + +// Echoes back all received WebSocket messages. +// This uses the Curiously Recurring Template Pattern so that +// the same code works with both SSL streams and regular sockets. +template<class Derived> +class websocket_session +{ + // Access the derived class, this is part of + // the Curiously Recurring Template Pattern idiom. + Derived& + derived() + { + return static_cast<Derived&>(*this); + } + + beast::flat_buffer buffer_; + + // Start the asynchronous operation + template<class Body, class Allocator> + void + do_accept(http::request<Body, http::basic_fields<Allocator>> req) + { + // Set suggested timeout settings for the websocket + derived().ws().set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + derived().ws().set_option( + websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " advanced-server-flex"); + })); + + // Accept the websocket handshake + derived().ws().async_accept( + req, + beast::bind_front_handler( + &websocket_session::on_accept, + derived().shared_from_this())); + } + + void + on_accept(beast::error_code ec) + { + if(ec) + return fail(ec, "accept"); + + // Read a message + do_read(); + } + + void + do_read() + { + // Read a message into our buffer + derived().ws().async_read( + buffer_, + beast::bind_front_handler( + &websocket_session::on_read, + derived().shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This indicates that the websocket_session was closed + if(ec == websocket::error::closed) + return; + + if(ec) + fail(ec, "read"); + + // Echo the message + derived().ws().text(derived().ws().got_text()); + derived().ws().async_write( + buffer_.data(), + beast::bind_front_handler( + &websocket_session::on_write, + derived().shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Clear the buffer + buffer_.consume(buffer_.size()); + + // Do another read + do_read(); + } + +public: + // Start the asynchronous operation + template<class Body, class Allocator> + void + run(http::request<Body, http::basic_fields<Allocator>> req) + { + // Accept the WebSocket upgrade request + do_accept(std::move(req)); + } +}; + +//------------------------------------------------------------------------------ + +// Handles a plain WebSocket connection +class plain_websocket_session + : public websocket_session<plain_websocket_session> + , public std::enable_shared_from_this<plain_websocket_session> +{ + websocket::stream<beast::tcp_stream> ws_; + +public: + // Create the session + explicit + plain_websocket_session( + beast::tcp_stream&& stream) + : ws_(std::move(stream)) + { + } + + // Called by the base class + websocket::stream<beast::tcp_stream>& + ws() + { + return ws_; + } +}; + +//------------------------------------------------------------------------------ + +// Handles an SSL WebSocket connection +class ssl_websocket_session + : public websocket_session<ssl_websocket_session> + , public std::enable_shared_from_this<ssl_websocket_session> +{ + websocket::stream< + beast::ssl_stream<beast::tcp_stream>> ws_; + +public: + // Create the ssl_websocket_session + explicit + ssl_websocket_session( + beast::ssl_stream<beast::tcp_stream>&& stream) + : ws_(std::move(stream)) + { + } + + // Called by the base class + websocket::stream< + beast::ssl_stream<beast::tcp_stream>>& + ws() + { + return ws_; + } +}; + +//------------------------------------------------------------------------------ + +template<class Body, class Allocator> +void +make_websocket_session( + beast::tcp_stream stream, + http::request<Body, http::basic_fields<Allocator>> req) +{ + std::make_shared<plain_websocket_session>( + std::move(stream))->run(std::move(req)); +} + +template<class Body, class Allocator> +void +make_websocket_session( + beast::ssl_stream<beast::tcp_stream> stream, + http::request<Body, http::basic_fields<Allocator>> req) +{ + std::make_shared<ssl_websocket_session>( + std::move(stream))->run(std::move(req)); +} + +//------------------------------------------------------------------------------ + +// Handles an HTTP server connection. +// This uses the Curiously Recurring Template Pattern so that +// the same code works with both SSL streams and regular sockets. +template<class Derived> +class http_session +{ + // Access the derived class, this is part of + // the Curiously Recurring Template Pattern idiom. + Derived& + derived() + { + return static_cast<Derived&>(*this); + } + + // This queue is used for HTTP pipelining. + class queue + { + enum + { + // Maximum number of responses we will queue + limit = 8 + }; + + // The type-erased, saved work item + struct work + { + virtual ~work() = default; + virtual void operator()() = 0; + }; + + http_session& self_; + std::vector<std::unique_ptr<work>> items_; + + public: + explicit + queue(http_session& self) + : self_(self) + { + static_assert(limit > 0, "queue limit must be positive"); + items_.reserve(limit); + } + + // Returns `true` if we have reached the queue limit + bool + is_full() const + { + return items_.size() >= limit; + } + + // Called when a message finishes sending + // Returns `true` if the caller should initiate a read + bool + on_write() + { + BOOST_ASSERT(! items_.empty()); + auto const was_full = is_full(); + items_.erase(items_.begin()); + if(! items_.empty()) + (*items_.front())(); + return was_full; + } + + // Called by the HTTP handler to send a response. + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) + { + // This holds a work item + struct work_impl : work + { + http_session& self_; + http::message<isRequest, Body, Fields> msg_; + + work_impl( + http_session& self, + http::message<isRequest, Body, Fields>&& msg) + : self_(self) + , msg_(std::move(msg)) + { + } + + void + operator()() + { + http::async_write( + self_.derived().stream(), + msg_, + beast::bind_front_handler( + &http_session::on_write, + self_.derived().shared_from_this(), + msg_.need_eof())); + } + }; + + // Allocate and store the work + items_.push_back( + boost::make_unique<work_impl>(self_, std::move(msg))); + + // If there was no previous work, start this one + if(items_.size() == 1) + (*items_.front())(); + } + }; + + std::shared_ptr<std::string const> doc_root_; + queue queue_; + + // The parser is stored in an optional container so we can + // construct it from scratch it at the beginning of each new message. + boost::optional<http::request_parser<http::string_body>> parser_; + +protected: + beast::flat_buffer buffer_; + +public: + // Construct the session + http_session( + beast::flat_buffer buffer, + std::shared_ptr<std::string const> const& doc_root) + : doc_root_(doc_root) + , queue_(*this) + , buffer_(std::move(buffer)) + { + } + + void + do_read() + { + // Construct a new parser for each message + parser_.emplace(); + + // Apply a reasonable limit to the allowed size + // of the body in bytes to prevent abuse. + parser_->body_limit(10000); + + // Set the timeout. + beast::get_lowest_layer( + derived().stream()).expires_after(std::chrono::seconds(30)); + + // Read a request using the parser-oriented interface + http::async_read( + derived().stream(), + buffer_, + *parser_, + beast::bind_front_handler( + &http_session::on_read, + derived().shared_from_this())); + } + + void + on_read(beast::error_code ec, std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This means they closed the connection + if(ec == http::error::end_of_stream) + return derived().do_eof(); + + if(ec) + return fail(ec, "read"); + + // See if it is a WebSocket Upgrade + if(websocket::is_upgrade(parser_->get())) + { + // Disable the timeout. + // The websocket::stream uses its own timeout settings. + beast::get_lowest_layer(derived().stream()).expires_never(); + + // Create a websocket session, transferring ownership + // of both the socket and the HTTP request. + return make_websocket_session( + derived().release_stream(), + parser_->release()); + } + + // Send the response + handle_request(*doc_root_, parser_->release(), queue_); + + // If we aren't at the queue limit, try to pipeline another request + if(! queue_.is_full()) + do_read(); + } + + void + on_write(bool close, beast::error_code ec, std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + return derived().do_eof(); + } + + // Inform the queue that a write completed + if(queue_.on_write()) + { + // Read another request + do_read(); + } + } +}; + +//------------------------------------------------------------------------------ + +// Handles a plain HTTP connection +class plain_http_session + : public http_session<plain_http_session> + , public std::enable_shared_from_this<plain_http_session> +{ + beast::tcp_stream stream_; + +public: + // Create the session + plain_http_session( + beast::tcp_stream&& stream, + beast::flat_buffer&& buffer, + std::shared_ptr<std::string const> const& doc_root) + : http_session<plain_http_session>( + std::move(buffer), + doc_root) + , stream_(std::move(stream)) + { + } + + // Start the session + void + run() + { + this->do_read(); + } + + // Called by the base class + beast::tcp_stream& + stream() + { + return stream_; + } + + // Called by the base class + beast::tcp_stream + release_stream() + { + return std::move(stream_); + } + + // Called by the base class + void + do_eof() + { + // Send a TCP shutdown + beast::error_code ec; + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully + } +}; + +//------------------------------------------------------------------------------ + +// Handles an SSL HTTP connection +class ssl_http_session + : public http_session<ssl_http_session> + , public std::enable_shared_from_this<ssl_http_session> +{ + beast::ssl_stream<beast::tcp_stream> stream_; + +public: + // Create the http_session + ssl_http_session( + beast::tcp_stream&& stream, + ssl::context& ctx, + beast::flat_buffer&& buffer, + std::shared_ptr<std::string const> const& doc_root) + : http_session<ssl_http_session>( + std::move(buffer), + doc_root) + , stream_(std::move(stream), ctx) + { + } + + // Start the session + void + run() + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL handshake + // Note, this is the buffered version of the handshake. + stream_.async_handshake( + ssl::stream_base::server, + buffer_.data(), + beast::bind_front_handler( + &ssl_http_session::on_handshake, + shared_from_this())); + } + + // Called by the base class + beast::ssl_stream<beast::tcp_stream>& + stream() + { + return stream_; + } + + // Called by the base class + beast::ssl_stream<beast::tcp_stream> + release_stream() + { + return std::move(stream_); + } + + // Called by the base class + void + do_eof() + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL shutdown + stream_.async_shutdown( + beast::bind_front_handler( + &ssl_http_session::on_shutdown, + shared_from_this())); + } + +private: + void + on_handshake( + beast::error_code ec, + std::size_t bytes_used) + { + if(ec) + return fail(ec, "handshake"); + + // Consume the portion of the buffer used by the handshake + buffer_.consume(bytes_used); + + do_read(); + } + + void + on_shutdown(beast::error_code ec) + { + if(ec) + return fail(ec, "shutdown"); + + // At this point the connection is closed gracefully + } +}; + +//------------------------------------------------------------------------------ + +// Detects SSL handshakes +class detect_session : public std::enable_shared_from_this<detect_session> +{ + beast::tcp_stream stream_; + ssl::context& ctx_; + std::shared_ptr<std::string const> doc_root_; + beast::flat_buffer buffer_; + +public: + explicit + detect_session( + tcp::socket&& socket, + ssl::context& ctx, + std::shared_ptr<std::string const> const& doc_root) + : stream_(std::move(socket)) + , ctx_(ctx) + , doc_root_(doc_root) + { + } + + // Launch the detector + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch( + stream_.get_executor(), + beast::bind_front_handler( + &detect_session::on_run, + this->shared_from_this())); + } + + void + on_run() + { + // Set the timeout. + stream_.expires_after(std::chrono::seconds(30)); + + beast::async_detect_ssl( + stream_, + buffer_, + beast::bind_front_handler( + &detect_session::on_detect, + this->shared_from_this())); + } + + void + on_detect(beast::error_code ec, bool result) + { + if(ec) + return fail(ec, "detect"); + + if(result) + { + // Launch SSL session + std::make_shared<ssl_http_session>( + std::move(stream_), + ctx_, + std::move(buffer_), + doc_root_)->run(); + return; + } + + // Launch plain session + std::make_shared<plain_http_session>( + std::move(stream_), + std::move(buffer_), + doc_root_)->run(); + } +}; + +// Accepts incoming connections and launches the sessions +class listener : public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + ssl::context& ctx_; + tcp::acceptor acceptor_; + std::shared_ptr<std::string const> doc_root_; + +public: + listener( + net::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + std::shared_ptr<std::string const> const& doc_root) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(net::make_strand(ioc)) + , doc_root_(doc_root) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + do_accept(); + } + +private: + void + do_accept() + { + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &listener::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the detector http_session and run it + std::make_shared<detect_session>( + std::move(socket), + ctx_, + doc_root_)->run(); + } + + // Accept another connection + do_accept(); + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: advanced-server-flex <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " advanced-server-flex 0.0.0.0 8080 . 1\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // Create and launch a listening port + std::make_shared<listener>( + ioc, + ctx, + tcp::endpoint{address, port}, + doc_root)->run(); + + // Capture SIGINT and SIGTERM to perform a clean shutdown + net::signal_set signals(ioc, SIGINT, SIGTERM); + signals.async_wait( + [&](beast::error_code const&, int) + { + // Stop the `io_context`. This will cause `run()` + // to return immediately, eventually destroying the + // `io_context` and all of the sockets in it. + ioc.stop(); + }); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + // (If we get here, it means we got a SIGINT or SIGTERM) + + // Block until all the threads exit + for(auto& t : v) + t.join(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/advanced/server/CMakeLists.txt b/src/boost/libs/beast/example/advanced/server/CMakeLists.txt new file mode 100644 index 000000000..08014afee --- /dev/null +++ b/src/boost/libs/beast/example/advanced/server/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/advanced/server "/") + +add_executable (advanced-server + ${BOOST_BEAST_FILES} + Jamfile + advanced_server.cpp +) + +target_link_libraries(advanced-server + lib-asio + lib-beast) + +set_property(TARGET advanced-server PROPERTY FOLDER "example-advanced-server") diff --git a/src/boost/libs/beast/example/advanced/server/Jamfile b/src/boost/libs/beast/example/advanced/server/Jamfile new file mode 100644 index 000000000..5f9ceecd8 --- /dev/null +++ b/src/boost/libs/beast/example/advanced/server/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 advanced-server : + advanced_server.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/advanced/server/advanced_server.cpp b/src/boost/libs/beast/example/advanced/server/advanced_server.cpp new file mode 100644 index 000000000..715f8b8ca --- /dev/null +++ b/src/boost/libs/beast/example/advanced/server/advanced_server.cpp @@ -0,0 +1,700 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: Advanced server +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/dispatch.hpp> +#include <boost/asio/signal_set.hpp> +#include <boost/asio/strand.hpp> +#include <boost/make_unique.hpp> +#include <boost/optional.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#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> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Echoes back all received WebSocket messages +class websocket_session : public std::enable_shared_from_this<websocket_session> +{ + websocket::stream<beast::tcp_stream> ws_; + beast::flat_buffer buffer_; + +public: + // Take ownership of the socket + explicit + websocket_session(tcp::socket&& socket) + : ws_(std::move(socket)) + { + } + + // Start the asynchronous accept operation + template<class Body, class Allocator> + void + do_accept(http::request<Body, http::basic_fields<Allocator>> req) + { + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " advanced-server"); + })); + + // Accept the websocket handshake + ws_.async_accept( + req, + beast::bind_front_handler( + &websocket_session::on_accept, + shared_from_this())); + } + +private: + void + on_accept(beast::error_code ec) + { + if(ec) + return fail(ec, "accept"); + + // Read a message + do_read(); + } + + void + do_read() + { + // Read a message into our buffer + ws_.async_read( + buffer_, + beast::bind_front_handler( + &websocket_session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This indicates that the websocket_session was closed + if(ec == websocket::error::closed) + return; + + if(ec) + fail(ec, "read"); + + // Echo the message + ws_.text(ws_.got_text()); + ws_.async_write( + buffer_.data(), + beast::bind_front_handler( + &websocket_session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Clear the buffer + buffer_.consume(buffer_.size()); + + // Do another read + do_read(); + } +}; + +//------------------------------------------------------------------------------ + +// Handles an HTTP server connection +class http_session : public std::enable_shared_from_this<http_session> +{ + // This queue is used for HTTP pipelining. + class queue + { + enum + { + // Maximum number of responses we will queue + limit = 8 + }; + + // The type-erased, saved work item + struct work + { + virtual ~work() = default; + virtual void operator()() = 0; + }; + + http_session& self_; + std::vector<std::unique_ptr<work>> items_; + + public: + explicit + queue(http_session& self) + : self_(self) + { + static_assert(limit > 0, "queue limit must be positive"); + items_.reserve(limit); + } + + // Returns `true` if we have reached the queue limit + bool + is_full() const + { + return items_.size() >= limit; + } + + // Called when a message finishes sending + // Returns `true` if the caller should initiate a read + bool + on_write() + { + BOOST_ASSERT(! items_.empty()); + auto const was_full = is_full(); + items_.erase(items_.begin()); + if(! items_.empty()) + (*items_.front())(); + return was_full; + } + + // Called by the HTTP handler to send a response. + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) + { + // This holds a work item + struct work_impl : work + { + http_session& self_; + http::message<isRequest, Body, Fields> msg_; + + work_impl( + http_session& self, + http::message<isRequest, Body, Fields>&& msg) + : self_(self) + , msg_(std::move(msg)) + { + } + + void + operator()() + { + http::async_write( + self_.stream_, + msg_, + beast::bind_front_handler( + &http_session::on_write, + self_.shared_from_this(), + msg_.need_eof())); + } + }; + + // Allocate and store the work + items_.push_back( + boost::make_unique<work_impl>(self_, std::move(msg))); + + // If there was no previous work, start this one + if(items_.size() == 1) + (*items_.front())(); + } + }; + + beast::tcp_stream stream_; + beast::flat_buffer buffer_; + std::shared_ptr<std::string const> doc_root_; + queue queue_; + + // The parser is stored in an optional container so we can + // construct it from scratch it at the beginning of each new message. + boost::optional<http::request_parser<http::string_body>> parser_; + +public: + // Take ownership of the socket + http_session( + tcp::socket&& socket, + std::shared_ptr<std::string const> const& doc_root) + : stream_(std::move(socket)) + , doc_root_(doc_root) + , queue_(*this) + { + } + + // Start the session + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch( + stream_.get_executor(), + beast::bind_front_handler( + &http_session::do_read, + this->shared_from_this())); + } + + +private: + void + do_read() + { + // Construct a new parser for each message + parser_.emplace(); + + // Apply a reasonable limit to the allowed size + // of the body in bytes to prevent abuse. + parser_->body_limit(10000); + + // Set the timeout. + stream_.expires_after(std::chrono::seconds(30)); + + // Read a request using the parser-oriented interface + http::async_read( + stream_, + buffer_, + *parser_, + beast::bind_front_handler( + &http_session::on_read, + shared_from_this())); + } + + void + on_read(beast::error_code ec, std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This means they closed the connection + if(ec == http::error::end_of_stream) + return do_close(); + + if(ec) + return fail(ec, "read"); + + // See if it is a WebSocket Upgrade + if(websocket::is_upgrade(parser_->get())) + { + // Create a websocket session, transferring ownership + // of both the socket and the HTTP request. + std::make_shared<websocket_session>( + stream_.release_socket())->do_accept(parser_->release()); + return; + } + + // Send the response + handle_request(*doc_root_, parser_->release(), queue_); + + // If we aren't at the queue limit, try to pipeline another request + if(! queue_.is_full()) + do_read(); + } + + void + on_write(bool close, beast::error_code ec, std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + return do_close(); + } + + // Inform the queue that a write completed + if(queue_.on_write()) + { + // Read another request + do_read(); + } + } + + void + do_close() + { + // Send a TCP shutdown + beast::error_code ec; + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully + } +}; + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +class listener : public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + tcp::acceptor acceptor_; + std::shared_ptr<std::string const> doc_root_; + +public: + listener( + net::io_context& ioc, + tcp::endpoint endpoint, + std::shared_ptr<std::string const> const& doc_root) + : ioc_(ioc) + , acceptor_(net::make_strand(ioc)) + , doc_root_(doc_root) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch( + acceptor_.get_executor(), + beast::bind_front_handler( + &listener::do_accept, + this->shared_from_this())); + } + +private: + void + do_accept() + { + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &listener::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the http session and run it + std::make_shared<http_session>( + std::move(socket), + doc_root_)->run(); + } + + // Accept another connection + do_accept(); + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: advanced-server <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " advanced-server 0.0.0.0 8080 . 1\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // Create and launch a listening port + std::make_shared<listener>( + ioc, + tcp::endpoint{address, port}, + doc_root)->run(); + + // Capture SIGINT and SIGTERM to perform a clean shutdown + net::signal_set signals(ioc, SIGINT, SIGTERM); + signals.async_wait( + [&](beast::error_code const&, int) + { + // Stop the `io_context`. This will cause `run()` + // to return immediately, eventually destroying the + // `io_context` and all of the sockets in it. + ioc.stop(); + }); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + // (If we get here, it means we got a SIGINT or SIGTERM) + + // Block until all the threads exit + for(auto& t : v) + t.join(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/common/root_certificates.hpp b/src/boost/libs/beast/example/common/root_certificates.hpp new file mode 100644 index 000000000..aa65bdeb8 --- /dev/null +++ b/src/boost/libs/beast/example/common/root_certificates.hpp @@ -0,0 +1,151 @@ +// +// 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_EXAMPLE_COMMON_ROOT_CERTIFICATES_HPP +#define BOOST_BEAST_EXAMPLE_COMMON_ROOT_CERTIFICATES_HPP + +#include <boost/asio/ssl.hpp> +#include <string> + +/* + PLEASE READ + + These root certificates here are included just to make the + SSL client examples work. They are NOT intended to be + illustrative of best-practices for performing TLS certificate + verification. + + A REAL program which needs to verify the authenticity of a + server IP address resolved from a given DNS name needs to + consult the operating system specific certificate store + to validate the chain of signatures, compare the domain name + properly against the domain name in the certificate, check + the certificate revocation list, and probably do some other + things. + + ALL of these operations are entirely outside the scope of + both Boost.Beast and Boost.Asio. + + See (work in progress): + https://github.com/djarek/certify + + tl;dr: root_certificates.hpp should not be used in production code +*/ + +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> + +namespace detail { + +inline +void +load_root_certificates(ssl::context& ctx, boost::system::error_code& ec) +{ + std::string const cert = + /* This is the DigiCert Global Root CA + + CN = DigiCert High Assurance EV Root CA + OU = www.digicert.com + O = DigiCert Inc + C = US + + Valid to: 10 November 2031 + + Serial #: + 08:3B:E0:56:90:42:46:B1:A1:75:6A:C9:59:91:C7:4A + + SHA1 Fingerprint: + A8:98:5D:3A:65:E5:E5:C4:B2:D7:D6:6D:40:C6:DD:2F:B1:9C:54:36 + + SHA256 Fingerprint: + 43:48:A0:E9:44:4C:78:CB:26:5E:05:8D:5E:89:44:B4:D8:4F:96:62:BD:26:DB:25:7F:89:34:A4:43:C7:01:61 + */ + "-----BEGIN CERTIFICATE-----\n" + "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" + "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" + "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" + "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" + "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" + "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" + "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" + "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" + "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" + "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" + "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" + "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" + "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" + "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" + "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" + "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" + "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" + "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" + "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" + "-----END CERTIFICATE-----\n" + + /* This is the GeoTrust root certificate. + + CN = GeoTrust Global CA + O = GeoTrust Inc. + C = US + Valid to: Friday, ‎May ‎20, ‎2022 9:00:00 PM + + Thumbprint(sha1): + ‎de 28 f4 a4 ff e5 b9 2f a3 c5 03 d1 a3 49 a7 f9 96 2a 82 12 + */ + "-----BEGIN CERTIFICATE-----\n" + "MIIDaDCCAlCgAwIBAgIJAO8vBu8i8exWMA0GCSqGSIb3DQEBCwUAMEkxCzAJBgNV\n" + "BAYTAlVTMQswCQYDVQQIDAJDQTEtMCsGA1UEBwwkTG9zIEFuZ2VsZXNPPUJlYXN0\n" + "Q049d3d3LmV4YW1wbGUuY29tMB4XDTE3MDUwMzE4MzkxMloXDTQ0MDkxODE4Mzkx\n" + "MlowSTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMS0wKwYDVQQHDCRMb3MgQW5n\n" + "ZWxlc089QmVhc3RDTj13d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA\n" + "A4IBDwAwggEKAoIBAQDJ7BRKFO8fqmsEXw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcF\n" + "xqGitbnLIrOgiJpRAPLy5MNcAXE1strVGfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7b\n" + "Fu8TsCzO6XrxpnVtWk506YZ7ToTa5UjHfBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO\n" + "9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wWKIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBp\n" + "yY8anC8u4LPbmgW0/U31PH0rRVfGcBbZsAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrv\n" + "enu2tOK9Qx6GEzXh3sekZkxcgh+NlIxCNxu//Dk9AgMBAAGjUzBRMB0GA1UdDgQW\n" + "BBTZh0N9Ne1OD7GBGJYz4PNESHuXezAfBgNVHSMEGDAWgBTZh0N9Ne1OD7GBGJYz\n" + "4PNESHuXezAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCmTJVT\n" + "LH5Cru1vXtzb3N9dyolcVH82xFVwPewArchgq+CEkajOU9bnzCqvhM4CryBb4cUs\n" + "gqXWp85hAh55uBOqXb2yyESEleMCJEiVTwm/m26FdONvEGptsiCmF5Gxi0YRtn8N\n" + "V+KhrQaAyLrLdPYI7TrwAOisq2I1cD0mt+xgwuv/654Rl3IhOMx+fKWKJ9qLAiaE\n" + "fQyshjlPP9mYVxWOxqctUdQ8UnsUKKGEUcVrA08i1OAnVKlPFjKBvk+r7jpsTPcr\n" + "9pWXTO9JrYMML7d+XRSZA1n3856OqZDX4403+9FnXCvfcLZLLKTBvwwFgEFGpzjK\n" + "UEVbkhd5qstF6qWK\n" + "-----END CERTIFICATE-----\n"; + ; + + ctx.add_certificate_authority( + boost::asio::buffer(cert.data(), cert.size()), ec); + if(ec) + return; +} + +} // detail + +// Load the root certificates into an ssl::context + +inline +void +load_root_certificates(ssl::context& ctx, boost::system::error_code& ec) +{ + detail::load_root_certificates(ctx, ec); +} + +inline +void +load_root_certificates(ssl::context& ctx) +{ + boost::system::error_code ec; + detail::load_root_certificates(ctx, ec); + if(ec) + throw boost::system::system_error{ec}; +} + +#endif diff --git a/src/boost/libs/beast/example/common/server_certificate.hpp b/src/boost/libs/beast/example/common/server_certificate.hpp new file mode 100644 index 000000000..a20110e40 --- /dev/null +++ b/src/boost/libs/beast/example/common/server_certificate.hpp @@ -0,0 +1,124 @@ +// +// 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_EXAMPLE_COMMON_SERVER_CERTIFICATE_HPP +#define BOOST_BEAST_EXAMPLE_COMMON_SERVER_CERTIFICATE_HPP + +#include <boost/asio/buffer.hpp> +#include <boost/asio/ssl/context.hpp> +#include <cstddef> +#include <memory> + +/* Load a signed certificate into the ssl context, and configure + the context for use with a server. + + For this to work with the browser or operating system, it is + necessary to import the "Beast Test CA" certificate into + the local certificate store, browser, or operating system + depending on your environment Please see the documentation + accompanying the Beast certificate for more details. +*/ +inline +void +load_server_certificate(boost::asio::ssl::context& ctx) +{ + /* + The certificate was generated from CMD.EXE on Windows 10 using: + + winpty openssl dhparam -out dh.pem 2048 + winpty openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 10000 -out cert.pem -subj "//C=US\ST=CA\L=Los Angeles\O=Beast\CN=www.example.com" + */ + + std::string const cert = + "-----BEGIN CERTIFICATE-----\n" + "MIIDaDCCAlCgAwIBAgIJAO8vBu8i8exWMA0GCSqGSIb3DQEBCwUAMEkxCzAJBgNV\n" + "BAYTAlVTMQswCQYDVQQIDAJDQTEtMCsGA1UEBwwkTG9zIEFuZ2VsZXNPPUJlYXN0\n" + "Q049d3d3LmV4YW1wbGUuY29tMB4XDTE3MDUwMzE4MzkxMloXDTQ0MDkxODE4Mzkx\n" + "MlowSTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMS0wKwYDVQQHDCRMb3MgQW5n\n" + "ZWxlc089QmVhc3RDTj13d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA\n" + "A4IBDwAwggEKAoIBAQDJ7BRKFO8fqmsEXw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcF\n" + "xqGitbnLIrOgiJpRAPLy5MNcAXE1strVGfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7b\n" + "Fu8TsCzO6XrxpnVtWk506YZ7ToTa5UjHfBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO\n" + "9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wWKIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBp\n" + "yY8anC8u4LPbmgW0/U31PH0rRVfGcBbZsAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrv\n" + "enu2tOK9Qx6GEzXh3sekZkxcgh+NlIxCNxu//Dk9AgMBAAGjUzBRMB0GA1UdDgQW\n" + "BBTZh0N9Ne1OD7GBGJYz4PNESHuXezAfBgNVHSMEGDAWgBTZh0N9Ne1OD7GBGJYz\n" + "4PNESHuXezAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCmTJVT\n" + "LH5Cru1vXtzb3N9dyolcVH82xFVwPewArchgq+CEkajOU9bnzCqvhM4CryBb4cUs\n" + "gqXWp85hAh55uBOqXb2yyESEleMCJEiVTwm/m26FdONvEGptsiCmF5Gxi0YRtn8N\n" + "V+KhrQaAyLrLdPYI7TrwAOisq2I1cD0mt+xgwuv/654Rl3IhOMx+fKWKJ9qLAiaE\n" + "fQyshjlPP9mYVxWOxqctUdQ8UnsUKKGEUcVrA08i1OAnVKlPFjKBvk+r7jpsTPcr\n" + "9pWXTO9JrYMML7d+XRSZA1n3856OqZDX4403+9FnXCvfcLZLLKTBvwwFgEFGpzjK\n" + "UEVbkhd5qstF6qWK\n" + "-----END CERTIFICATE-----\n"; + + std::string const key = + "-----BEGIN PRIVATE KEY-----\n" + "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ7BRKFO8fqmsE\n" + "Xw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcFxqGitbnLIrOgiJpRAPLy5MNcAXE1strV\n" + "GfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7bFu8TsCzO6XrxpnVtWk506YZ7ToTa5UjH\n" + "fBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wW\n" + "KIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBpyY8anC8u4LPbmgW0/U31PH0rRVfGcBbZ\n" + "sAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrvenu2tOK9Qx6GEzXh3sekZkxcgh+NlIxC\n" + "Nxu//Dk9AgMBAAECggEBAK1gV8uETg4SdfE67f9v/5uyK0DYQH1ro4C7hNiUycTB\n" + "oiYDd6YOA4m4MiQVJuuGtRR5+IR3eI1zFRMFSJs4UqYChNwqQGys7CVsKpplQOW+\n" + "1BCqkH2HN/Ix5662Dv3mHJemLCKUON77IJKoq0/xuZ04mc9csykox6grFWB3pjXY\n" + "OEn9U8pt5KNldWfpfAZ7xu9WfyvthGXlhfwKEetOuHfAQv7FF6s25UIEU6Hmnwp9\n" + "VmYp2twfMGdztz/gfFjKOGxf92RG+FMSkyAPq/vhyB7oQWxa+vdBn6BSdsfn27Qs\n" + "bTvXrGe4FYcbuw4WkAKTljZX7TUegkXiwFoSps0jegECgYEA7o5AcRTZVUmmSs8W\n" + "PUHn89UEuDAMFVk7grG1bg8exLQSpugCykcqXt1WNrqB7x6nB+dbVANWNhSmhgCg\n" + "VrV941vbx8ketqZ9YInSbGPWIU/tss3r8Yx2Ct3mQpvpGC6iGHzEc/NHJP8Efvh/\n" + "CcUWmLjLGJYYeP5oNu5cncC3fXUCgYEA2LANATm0A6sFVGe3sSLO9un1brA4zlZE\n" + "Hjd3KOZnMPt73B426qUOcw5B2wIS8GJsUES0P94pKg83oyzmoUV9vJpJLjHA4qmL\n" + "CDAd6CjAmE5ea4dFdZwDDS8F9FntJMdPQJA9vq+JaeS+k7ds3+7oiNe+RUIHR1Sz\n" + "VEAKh3Xw66kCgYB7KO/2Mchesu5qku2tZJhHF4QfP5cNcos511uO3bmJ3ln+16uR\n" + "GRqz7Vu0V6f7dvzPJM/O2QYqV5D9f9dHzN2YgvU9+QSlUeFK9PyxPv3vJt/WP1//\n" + "zf+nbpaRbwLxnCnNsKSQJFpnrE166/pSZfFbmZQpNlyeIuJU8czZGQTifQKBgHXe\n" + "/pQGEZhVNab+bHwdFTxXdDzr+1qyrodJYLaM7uFES9InVXQ6qSuJO+WosSi2QXlA\n" + "hlSfwwCwGnHXAPYFWSp5Owm34tbpp0mi8wHQ+UNgjhgsE2qwnTBUvgZ3zHpPORtD\n" + "23KZBkTmO40bIEyIJ1IZGdWO32q79nkEBTY+v/lRAoGBAI1rbouFYPBrTYQ9kcjt\n" + "1yfu4JF5MvO9JrHQ9tOwkqDmNCWx9xWXbgydsn/eFtuUMULWsG3lNjfst/Esb8ch\n" + "k5cZd6pdJZa4/vhEwrYYSuEjMCnRb0lUsm7TsHxQrUd6Fi/mUuFU/haC0o0chLq7\n" + "pVOUFq5mW8p0zbtfHbjkgxyF\n" + "-----END PRIVATE KEY-----\n"; + + std::string const dh = + "-----BEGIN DH PARAMETERS-----\n" + "MIIBCAKCAQEArzQc5mpm0Fs8yahDeySj31JZlwEphUdZ9StM2D8+Fo7TMduGtSi+\n" + "/HRWVwHcTFAgrxVdm+dl474mOUqqaz4MpzIb6+6OVfWHbQJmXPepZKyu4LgUPvY/\n" + "4q3/iDMjIS0fLOu/bLuObwU5ccZmDgfhmz1GanRlTQOiYRty3FiOATWZBRh6uv4u\n" + "tff4A9Bm3V9tLx9S6djq31w31Gl7OQhryodW28kc16t9TvO1BzcV3HjRPwpe701X\n" + "oEEZdnZWANkkpR/m/pfgdmGPU66S2sXMHgsliViQWpDCYeehrvFRHEdR9NV+XJfC\n" + "QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n" + "-----END DH PARAMETERS-----\n"; + + ctx.set_password_callback( + [](std::size_t, + boost::asio::ssl::context_base::password_purpose) + { + return "test"; + }); + + ctx.set_options( + boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::single_dh_use); + + ctx.use_certificate_chain( + boost::asio::buffer(cert.data(), cert.size())); + + ctx.use_private_key( + boost::asio::buffer(key.data(), key.size()), + boost::asio::ssl::context::file_format::pem); + + ctx.use_tmp_dh( + boost::asio::buffer(dh.data(), dh.size())); +} + +#endif diff --git a/src/boost/libs/beast/example/echo-op/CMakeLists.txt b/src/boost/libs/beast/example/echo-op/CMakeLists.txt new file mode 100644 index 000000000..d0e086b6e --- /dev/null +++ b/src/boost/libs/beast/example/echo-op/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/echo-op "/") + +add_executable (echo-op + ${BOOST_BEAST_FILES} + Jamfile + echo_op.cpp +) + +target_link_libraries(echo-op + lib-asio + lib-beast) + +set_property(TARGET echo-op PROPERTY FOLDER "example") diff --git a/src/boost/libs/beast/example/echo-op/Jamfile b/src/boost/libs/beast/example/echo-op/Jamfile new file mode 100644 index 000000000..caeb767e2 --- /dev/null +++ b/src/boost/libs/beast/example/echo-op/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 echo-op : + echo_op.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/echo-op/echo_op.cpp b/src/boost/libs/beast/example/echo-op/echo_op.cpp new file mode 100644 index 000000000..475940beb --- /dev/null +++ b/src/boost/libs/beast/example/echo-op/echo_op.cpp @@ -0,0 +1,377 @@ +// +// 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.hpp> +#include <boost/asio.hpp> +#include <cstddef> +#include <iostream> +#include <memory> +#include <utility> + +namespace net = boost::asio; +namespace beast = boost::beast; + +//[example_core_echo_op_1 + +template< + class AsyncStream, + class DynamicBuffer, + class CompletionToken> +auto +async_echo (AsyncStream& stream, DynamicBuffer& buffer, CompletionToken&& token) + +//] + -> + typename net::async_result< + typename std::decay<CompletionToken>::type, + void(beast::error_code)>::return_type; + +//------------------------------------------------------------------------------ + +//[example_core_echo_op_2 + +/** Asynchronously read a line and echo it back. + + This function is used to asynchronously read a line ending + in a newline (`"\n"`) from the stream, and then write + it back. + + This call always returns immediately. The asynchronous operation + will continue until one of the following conditions is true: + + @li A line was read in and written back on the stream + + @li An error occurs. + + The algorithm, known as a <em>composed asynchronous operation</em>, + is implemented in terms of calls to the stream's `async_read_some` + and `async_write_some` function. The program must ensure that no + other reads or writes are performed until this operation completes. + + Since the length of the line is not known ahead of time, the + implementation may read additional characters that lie past the + first line. These characters are stored in the dynamic buffer_. + The same dynamic buffer must be presented again in each call, + to provide the implementation with any leftover bytes. + + @param stream The stream to operate on. The type must meet the + requirements of <em>AsyncReadStream</em> and @AsyncWriteStream + + @param buffer A dynamic buffer to hold implementation-defined + temporary data. Ownership is not transferred; the caller is + responsible for ensuring that the lifetime of this object is + extended least until the completion handler is invoked. + + @param token The handler to be called when the operation completes. + The implementation takes ownership of the handler by performing a decay-copy. + The handler must be invocable with this signature: + @code + void handler( + beast::error_code error // Result of operation. + ); + @endcode + + Regardless of whether the asynchronous operation completes immediately or + not, the handler will not be invoked from within this function. Invocation + of the handler will be performed in a manner equivalent to using + `net::post`. +*/ +template< + class AsyncStream, + class DynamicBuffer, + class CompletionToken> +auto +async_echo ( + AsyncStream& stream, + DynamicBuffer& buffer, /*< Unlike Asio, we pass by non-const reference instead of rvalue-ref >*/ + CompletionToken&& token) -> + typename net::async_result< /*< `async_result` deduces the return type from the completion handler >*/ + typename std::decay<CompletionToken>::type, + void(beast::error_code) /*< The completion handler signature goes here >*/ + >::return_type; +//] + +//[example_core_echo_op_3 + +template<class AsyncStream, class Handler> +class echo_op; + +// This example uses the Asio's stackless "fauxroutines", implemented +// using a macro-based solution. It makes the code easier to write and +// easier to read. This include file defines the necessary macros and types. +#include <boost/asio/yield.hpp> + +// Read a line and echo it back +// +template< + class AsyncStream, + class DynamicBuffer, + class CompletionToken> +auto +async_echo( + AsyncStream& stream, + DynamicBuffer& buffer, + CompletionToken&& token) -> + typename net::async_result< + typename std::decay<CompletionToken>::type, + void(beast::error_code)>::return_type /*< The completion handler signature goes here >*/ +{ + // Perform some type checks using static assert, this helps + // with more friendly error messages when passing the wrong types. + static_assert( + beast::is_async_stream<AsyncStream>::value, + "AsyncStream type requirements not met"); + static_assert( + net::is_dynamic_buffer<DynamicBuffer>::value, + "DynamicBuffer type requirements not met"); + + // This class template deduces the actual handler type from a + // CompletionToken, captures a local reference to the handler, + // and creates the `async_result` object which becomes the + // return value of this initiating function. + + net::async_completion<CompletionToken, void(beast::error_code)> init(token); + + // The helper macro BOOST_ASIO_HANDLER_TYPE converts the completion + // token type into a concrete handler type of the correct signature. + + using handler_type = BOOST_ASIO_HANDLER_TYPE(CompletionToken, void(beast::error_code)); + + // The class template `async_base` holds the caller's completion + // handler for us, and provides all of the boilerplate for forwarding + // the associated allocator and associated executor from the caller's + // handler to our operation. It also maintains a `net::executor_work_guard` + // for the executor associated with the stream. This work guard is + // inexpensive, and prevents the execution context from running out + // of work. It is usually necessary although rarely it can be skipped + // depending on the operation (this echo example needs it because it + // performs more than one asynchronous operation in a row). + // We declare this type alias to make the code easier to read. + + using base_type = beast::async_base< + handler_type, /*< The type of the completion handler obtained from the token >*/ + beast::executor_type<AsyncStream> /*< The type of executor used by the stream to dispatch asynchronous operations >*/ + >; + + // This nested class implements the echo composed operation as a + // stateful completion handler. We derive from `async_base` to + // take care of boilerplate and we derived from asio::coroutine to + // allow the reenter and yield keywords to work. + + struct echo_op : base_type, boost::asio::coroutine + { + AsyncStream& stream_; + DynamicBuffer& buffer_; + + echo_op( + AsyncStream& stream, + DynamicBuffer& buffer, + handler_type&& handler) + : base_type( + std::move(handler), /*< The `async_base` helper takes ownership of the handler, >*/ + stream.get_executor()) /*< and also needs to know which executor to use. >*/ + , stream_(stream) + , buffer_(buffer) + { + // Launch the operation directly from the constructor. We + // pass `false` for `cont` to indicate that the calling + // thread does not represent a continuation of our + // asynchronous control flow. + (*this)({}, 0, false); + } + + // If a newline is present in the buffer sequence, this function returns + // the number of characters from the beginning of the buffer up to the + // newline, including the newline character. Otherwise it returns zero. + + std::size_t + find_newline(typename DynamicBuffer::const_buffers_type const& buffers) + { + // The `buffers_iterator` class template provides random-access + // iterators into a buffer sequence. Use the standard algorithm + // to look for the new line if it exists. + + auto begin = net::buffers_iterator< + typename DynamicBuffer::const_buffers_type>::begin(buffers); + auto end = net::buffers_iterator< + typename DynamicBuffer::const_buffers_type>::end(buffers); + auto result = std::find(begin, end, '\n'); + + if(result == end) + return 0; // not found + + return result + 1 - begin; + } + + // This is the entry point of our completion handler. Every time an + // asynchronous operation completes, this function will be invoked. + + void + operator()( + beast::error_code ec, + std::size_t bytes_transferred = 0, + bool cont = true) /*< Second and subsequent invocations will seee `cont=true`. */ + { + // The `reenter` keyword transfers control to the last + // yield point, or to the beginning of the scope if + // this is the first time. + + reenter(*this) + { + for(;;) + { + std::size_t pos; + + // Search for a newline in the readable bytes of the buffer + pos = find_newline(buffer_.data()); + + // If we don't have the newline, then read more + if(pos == 0) + { + std::size_t bytes_to_read; + + // Determine the number of bytes to read, + // using available capacity in the buffer first. + + bytes_to_read = std::min<std::size_t>( + std::max<std::size_t>(512, // under 512 is too little, + buffer_.capacity() - buffer_.size()), + std::min<std::size_t>(65536, // and over 65536 is too much. + buffer_.max_size() - buffer_.size())); + + // Read some data into our dynamic buffer_. We transfer + // ownership of the composed operation by using the + // `std::move(*this)` idiom. The `yield` keyword causes + // the function to return immediately after the initiating + // function returns. + + yield stream_.async_read_some( + buffer_.prepare(bytes_to_read), std::move(*this)); + + // After the `async_read_some` completes, control is + // transferred to this line by the `reenter` keyword. + + // Move the bytes read from the writable area to the + // readable area. + + buffer_.commit(bytes_transferred); + + // If an error occurs, deliver it to the caller's completion handler. + if(ec) + break; + + // Keep looping until we get the newline + continue; + } + + // We have our newline, so send the first `pos` bytes of the + // buffers. The function `buffers_prefix` returns the front part + // of the buffers we want. + + yield net::async_write(stream_, + beast::buffers_prefix(pos, buffer_.data()), std::move(*this)); + + // After the `async_write` completes, our completion handler will + // be invoked with the error and the number of bytes transferred, + // and the `reenter` statement above will cause control to jump + // to the following line. The variable `pos` is no longer valid + // (remember that we returned from the function using `yield` above) + // but we can use `bytes_transferred` to know how much of the buffer + // to consume. With "real" coroutines this will be easier and more + // natural. + + buffer_.consume(bytes_transferred); + + // The loop terminates here, and we will either deliver a + // successful result or an error to the caller's completion handler. + + break; + } + + // When a composed operation completes immediately, it must not + // directly invoke the completion handler otherwise it could + // lead to unfairness, starvation, or stack overflow. Therefore, + // if cont == false (meaning, that the call stack still includes + // the frame of the initiating function) then we need to use + // `net::post` to cause us to be called again after the initiating + // function. The function `async_base::invoke` takes care of + // calling the final completion handler, using post if the + // first argument is false, otherwise invoking it directly. + + this->complete(cont, ec); + } + } + }; + + // Create the composed operation and launch it. This is a constructor + // call followed by invocation of operator(). We use BOOST_ASIO_HANDLER_TYPE + // to convert the completion token into the correct handler type, + // allowing user-defined specializations of the async_result template + // to be used. + + echo_op(stream, buffer, std::move(init.completion_handler)); + + // This hook lets the caller see a return value when appropriate. + // For example this might return std::future<error_code> if + // CompletionToken is net::use_future, or this might + // return an error code if CompletionToken specifies a coroutine. + + return init.result.get(); +} + +// Including this file undefines the macros used by the stackless fauxroutines. +#include <boost/asio/unyield.hpp> + +//] + +struct move_only_handler +{ + move_only_handler() = default; + move_only_handler(move_only_handler&&) = default; + move_only_handler(move_only_handler const&) = delete; + + void operator()(beast::error_code ec) + { + if(ec) + std::cerr << ": " << ec.message() << std::endl; + } +}; + +int main(int argc, char** argv) +{ + if(argc != 3) + { + std::cerr + << "Usage: echo-op <address> <port>\n" + << "Example:\n" + << " echo-op 0.0.0.0 8080\n"; + return EXIT_FAILURE; + } + + namespace net = boost::asio; + auto const address{net::ip::make_address(argv[1])}; + auto const port{static_cast<unsigned short>(std::atoi(argv[2]))}; + + using endpoint_type = net::ip::tcp::endpoint; + + // Create a listening socket, accept a connection, perform + // the echo, and then shut everything down and exit. + net::io_context ioc; + net::ip::tcp::acceptor acceptor{ioc}; + endpoint_type ep{address, port}; + acceptor.open(ep.protocol()); + acceptor.set_option(net::socket_base::reuse_address(true)); + acceptor.bind(ep); + acceptor.listen(); + auto sock = acceptor.accept(); + beast::flat_buffer buffer; + async_echo(sock, buffer, move_only_handler{}); + ioc.run(); + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/CMakeLists.txt b/src/boost/libs/beast/example/http/CMakeLists.txt new file mode 100644 index 000000000..d38604dd0 --- /dev/null +++ b/src/boost/libs/beast/example/http/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# Copyright (c) 2013-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 (client) +add_subdirectory (server) diff --git a/src/boost/libs/beast/example/http/Jamfile b/src/boost/libs/beast/example/http/Jamfile new file mode 100644 index 000000000..32f2c5144 --- /dev/null +++ b/src/boost/libs/beast/example/http/Jamfile @@ -0,0 +1,11 @@ +# +# Copyright (c) 2013-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 +# + +build-project client ; +build-project server ; diff --git a/src/boost/libs/beast/example/http/client/CMakeLists.txt b/src/boost/libs/beast/example/http/client/CMakeLists.txt new file mode 100644 index 000000000..0c9d18156 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/CMakeLists.txt @@ -0,0 +1,20 @@ +# +# Copyright (c) 2013-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 (async) +add_subdirectory (coro) +add_subdirectory (crawl) +add_subdirectory (sync) + +if (OPENSSL_FOUND) + add_subdirectory (async-ssl) + add_subdirectory (async-ssl-system-executor) + add_subdirectory (coro-ssl) + add_subdirectory (sync-ssl) +endif() diff --git a/src/boost/libs/beast/example/http/client/Jamfile b/src/boost/libs/beast/example/http/client/Jamfile new file mode 100644 index 000000000..11dd77ee5 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/Jamfile @@ -0,0 +1,18 @@ +# +# Copyright (c) 2013-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 +# + +build-project async ; +build-project coro ; +build-project crawl ; +build-project sync ; + +# SSL +build-project async-ssl ; +build-project coro-ssl ; +build-project sync-ssl ; diff --git a/src/boost/libs/beast/example/http/client/async-ssl-system-executor/CMakeLists.txt b/src/boost/libs/beast/example/http/client/async-ssl-system-executor/CMakeLists.txt new file mode 100644 index 000000000..049a2fea9 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/async-ssl-system-executor/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/http/client/async-ssl-system-executor "/") + + add_executable (http-client-async-ssl-system-executor + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/root_certificates.hpp + Jamfile + http_client_async_ssl_system_executor.cpp + ) + + set_property(TARGET http-client-async-ssl-system-executor PROPERTY FOLDER "example-http-client") + + target_link_libraries (http-client-async-ssl-system-executor + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/http/client/async-ssl-system-executor/Jamfile b/src/boost/libs/beast/example/http/client/async-ssl-system-executor/Jamfile new file mode 100644 index 000000000..732da8631 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/async-ssl-system-executor/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe http-client-async-ssl-system-executor : + http_client_async_ssl_system_executor.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/client/async-ssl-system-executor/http_client_async_ssl_system_executor.cpp b/src/boost/libs/beast/example/http/client/async-ssl-system-executor/http_client_async_ssl_system_executor.cpp new file mode 100644 index 000000000..19ca0be69 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/async-ssl-system-executor/http_client_async_ssl_system_executor.cpp @@ -0,0 +1,246 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP SSL client, asynchronous, using system_executor +// +//------------------------------------------------------------------------------ + +#include "example/common/root_certificates.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/strand.hpp> +#include <boost/asio/system_executor.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Performs an HTTP GET and prints the response +class session : public std::enable_shared_from_this<session> +{ + tcp::resolver resolver_; + beast::ssl_stream<beast::tcp_stream> stream_; + beast::flat_buffer buffer_; // (Must persist between reads) + http::request<http::empty_body> req_; + http::response<http::string_body> res_; + + // Objects are constructed with a strand to + // ensure that handlers do not execute concurrently. + session(net::strand<net::system_executor> strand, ssl::context& ctx) + : resolver_(strand) + , stream_(strand, ctx) + { + } + +public: + // Delegate construction to a prive constructor to be able to use + // the same strand for both I/O object. + explicit + session(ssl::context& ctx) + : session(net::make_strand(net::system_executor()), ctx) + { + } + + // Start the asynchronous operation + void + run( + char const* host, + char const* port, + char const* target, + int version) + { + // Set SNI Hostname (many hosts need this to handshake successfully) + if(! SSL_set_tlsext_host_name(stream_.native_handle(), host)) + { + beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()}; + std::cerr << ec.message() << "\n"; + return; + } + + // Set up an HTTP GET request message + req_.version(version); + req_.method(http::verb::get); + req_.target(target); + req_.set(http::field::host, host); + req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + + // Look up the domain name + resolver_.async_resolve( + host, + port, + beast::bind_front_handler( + &session::on_resolve, + shared_from_this())); + } + + void + on_resolve( + beast::error_code ec, + tcp::resolver::results_type results) + { + if(ec) + return fail(ec, "resolve"); + + // Set a timeout on the operation + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + beast::get_lowest_layer(stream_).async_connect( + results, + beast::bind_front_handler( + &session::on_connect, + shared_from_this())); + } + + void + on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type) + { + if(ec) + return fail(ec, "connect"); + + // Perform the SSL handshake + stream_.async_handshake( + ssl::stream_base::client, + beast::bind_front_handler( + &session::on_handshake, + shared_from_this())); + } + + void + on_handshake(beast::error_code ec) + { + if(ec) + return fail(ec, "handshake"); + + // Set a timeout on the operation + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Send the HTTP request to the remote host + http::async_write(stream_, req_, + beast::bind_front_handler( + &session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Receive the HTTP response + http::async_read(stream_, buffer_, res_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "read"); + + // Write the message to standard out + std::cout << res_ << std::endl; + + // Set a timeout on the operation + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Gracefully close the stream + stream_.async_shutdown( + beast::bind_front_handler( + &session::on_shutdown, + shared_from_this())); + } + + void + on_shutdown(beast::error_code ec) + { + if(ec == net::error::eof) + { + // Rationale: + // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error + ec = {}; + } + if(ec) + return fail(ec, "shutdown"); + + // If we get here then the connection is closed gracefully + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4 && argc != 5) + { + std::cerr << + "Usage: http-client-async-ssl-system-executor <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" << + "Example:\n" << + " http-client-async-ssl-system-executor www.example.com 443 /\n" << + " http-client-async-ssl-system-executor www.example.com 443 / 1.0\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const target = argv[3]; + int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12_client}; + + // This holds the root certificate used for verification + load_root_certificates(ctx); + + // Verify the remote server's certificate + ctx.set_verify_mode(ssl::verify_peer); + + // Launch the asynchronous operation + std::make_shared<session>(ctx)->run(host, port, target, version); + + + // The async operations will run on the system_executor. + // Because the main thread has nothing to do in this example, we just wait + // for the system_executor to run out of work. + net::system_executor().context().join(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/client/async-ssl/CMakeLists.txt b/src/boost/libs/beast/example/http/client/async-ssl/CMakeLists.txt new file mode 100644 index 000000000..a64fbf618 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/async-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/http/client/async-ssl "/") + + add_executable (http-client-async-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/root_certificates.hpp + Jamfile + http_client_async_ssl.cpp + ) + + set_property(TARGET http-client-async-ssl PROPERTY FOLDER "example-http-client") + + target_link_libraries (http-client-async-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/http/client/async-ssl/Jamfile b/src/boost/libs/beast/example/http/client/async-ssl/Jamfile new file mode 100644 index 000000000..c11bcb072 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/async-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe http-client-async-ssl : + http_client_async_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/client/async-ssl/http_client_async_ssl.cpp b/src/boost/libs/beast/example/http/client/async-ssl/http_client_async_ssl.cpp new file mode 100644 index 000000000..2a5f95bd9 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/async-ssl/http_client_async_ssl.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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP SSL client, asynchronous +// +//------------------------------------------------------------------------------ + +#include "example/common/root_certificates.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/strand.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Performs an HTTP GET and prints the response +class session : public std::enable_shared_from_this<session> +{ + tcp::resolver resolver_; + beast::ssl_stream<beast::tcp_stream> stream_; + beast::flat_buffer buffer_; // (Must persist between reads) + http::request<http::empty_body> req_; + http::response<http::string_body> res_; + +public: + explicit + session( + net::executor ex, + ssl::context& ctx) + : resolver_(ex) + , stream_(ex, ctx) + { + } + + // Start the asynchronous operation + void + run( + char const* host, + char const* port, + char const* target, + int version) + { + // Set SNI Hostname (many hosts need this to handshake successfully) + if(! SSL_set_tlsext_host_name(stream_.native_handle(), host)) + { + beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()}; + std::cerr << ec.message() << "\n"; + return; + } + + // Set up an HTTP GET request message + req_.version(version); + req_.method(http::verb::get); + req_.target(target); + req_.set(http::field::host, host); + req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + + // Look up the domain name + resolver_.async_resolve( + host, + port, + beast::bind_front_handler( + &session::on_resolve, + shared_from_this())); + } + + void + on_resolve( + beast::error_code ec, + tcp::resolver::results_type results) + { + if(ec) + return fail(ec, "resolve"); + + // Set a timeout on the operation + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + beast::get_lowest_layer(stream_).async_connect( + results, + beast::bind_front_handler( + &session::on_connect, + shared_from_this())); + } + + void + on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type) + { + if(ec) + return fail(ec, "connect"); + + // Perform the SSL handshake + stream_.async_handshake( + ssl::stream_base::client, + beast::bind_front_handler( + &session::on_handshake, + shared_from_this())); + } + + void + on_handshake(beast::error_code ec) + { + if(ec) + return fail(ec, "handshake"); + + // Set a timeout on the operation + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Send the HTTP request to the remote host + http::async_write(stream_, req_, + beast::bind_front_handler( + &session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Receive the HTTP response + http::async_read(stream_, buffer_, res_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "read"); + + // Write the message to standard out + std::cout << res_ << std::endl; + + // Set a timeout on the operation + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Gracefully close the stream + stream_.async_shutdown( + beast::bind_front_handler( + &session::on_shutdown, + shared_from_this())); + } + + void + on_shutdown(beast::error_code ec) + { + if(ec == net::error::eof) + { + // Rationale: + // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error + ec = {}; + } + if(ec) + return fail(ec, "shutdown"); + + // If we get here then the connection is closed gracefully + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4 && argc != 5) + { + std::cerr << + "Usage: http-client-async-ssl <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" << + "Example:\n" << + " http-client-async-ssl www.example.com 443 /\n" << + " http-client-async-ssl www.example.com 443 / 1.0\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const target = argv[3]; + int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11; + + // The io_context is required for all I/O + net::io_context ioc; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12_client}; + + // This holds the root certificate used for verification + load_root_certificates(ctx); + + // Verify the remote server's certificate + ctx.set_verify_mode(ssl::verify_peer); + + // Launch the asynchronous operation + // The session is constructed with a strand to + // ensure that handlers do not execute concurrently. + std::make_shared<session>( + net::make_strand(ioc), + ctx + )->run(host, port, target, version); + + // Run the I/O service. The call will return when + // the get operation is complete. + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/client/async/CMakeLists.txt b/src/boost/libs/beast/example/http/client/async/CMakeLists.txt new file mode 100644 index 000000000..3847ce610 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/async/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/http/client/async "/") + +add_executable (http-client-async + ${BOOST_BEAST_FILES} + Jamfile + http_client_async.cpp +) + +target_link_libraries(http-client-async + lib-asio + lib-beast) + +set_property(TARGET http-client-async PROPERTY FOLDER "example-http-client") diff --git a/src/boost/libs/beast/example/http/client/async/Jamfile b/src/boost/libs/beast/example/http/client/async/Jamfile new file mode 100644 index 000000000..abc9d4d61 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/async/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 http-client-async : + http_client_async.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/client/async/http_client_async.cpp b/src/boost/libs/beast/example/http/client/async/http_client_async.cpp new file mode 100644 index 000000000..f451df51c --- /dev/null +++ b/src/boost/libs/beast/example/http/client/async/http_client_async.cpp @@ -0,0 +1,189 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP client, asynchronous +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/strand.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Performs an HTTP GET and prints the response +class session : public std::enable_shared_from_this<session> +{ + tcp::resolver resolver_; + beast::tcp_stream stream_; + beast::flat_buffer buffer_; // (Must persist between reads) + http::request<http::empty_body> req_; + http::response<http::string_body> res_; + +public: + // Objects are constructed with a strand to + // ensure that handlers do not execute concurrently. + explicit + session(net::io_context& ioc) + : resolver_(net::make_strand(ioc)) + , stream_(net::make_strand(ioc)) + { + } + + // Start the asynchronous operation + void + run( + char const* host, + char const* port, + char const* target, + int version) + { + // Set up an HTTP GET request message + req_.version(version); + req_.method(http::verb::get); + req_.target(target); + req_.set(http::field::host, host); + req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + + // Look up the domain name + resolver_.async_resolve( + host, + port, + beast::bind_front_handler( + &session::on_resolve, + shared_from_this())); + } + + void + on_resolve( + beast::error_code ec, + tcp::resolver::results_type results) + { + if(ec) + return fail(ec, "resolve"); + + // Set a timeout on the operation + stream_.expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + stream_.async_connect( + results, + beast::bind_front_handler( + &session::on_connect, + shared_from_this())); + } + + void + on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type) + { + if(ec) + return fail(ec, "connect"); + + // Set a timeout on the operation + stream_.expires_after(std::chrono::seconds(30)); + + // Send the HTTP request to the remote host + http::async_write(stream_, req_, + beast::bind_front_handler( + &session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Receive the HTTP response + http::async_read(stream_, buffer_, res_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "read"); + + // Write the message to standard out + std::cout << res_ << std::endl; + + // Gracefully close the socket + stream_.socket().shutdown(tcp::socket::shutdown_both, ec); + + // not_connected happens sometimes so don't bother reporting it. + if(ec && ec != beast::errc::not_connected) + return fail(ec, "shutdown"); + + // If we get here then the connection is closed gracefully + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4 && argc != 5) + { + std::cerr << + "Usage: http-client-async <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" << + "Example:\n" << + " http-client-async www.example.com 80 /\n" << + " http-client-async www.example.com 80 / 1.0\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const target = argv[3]; + int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11; + + // The io_context is required for all I/O + net::io_context ioc; + + // Launch the asynchronous operation + std::make_shared<session>(ioc)->run(host, port, target, version); + + // Run the I/O service. The call will return when + // the get operation is complete. + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/client/coro-ssl/CMakeLists.txt b/src/boost/libs/beast/example/http/client/coro-ssl/CMakeLists.txt new file mode 100644 index 000000000..54fe3ae25 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/coro-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/http/client/coro-ssl "/") + + add_executable (http-client-coro-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/root_certificates.hpp + Jamfile + http_client_coro_ssl.cpp + ) + + set_property(TARGET http-client-coro-ssl PROPERTY FOLDER "example-http-client") + + target_link_libraries (http-client-coro-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/http/client/coro-ssl/Jamfile b/src/boost/libs/beast/example/http/client/coro-ssl/Jamfile new file mode 100644 index 000000000..f2c126c59 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/coro-ssl/Jamfile @@ -0,0 +1,23 @@ +# +# 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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe http-client-coro-ssl : + http_client_coro_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + <library>/boost/coroutine//boost_coroutine + ; diff --git a/src/boost/libs/beast/example/http/client/coro-ssl/http_client_coro_ssl.cpp b/src/boost/libs/beast/example/http/client/coro-ssl/http_client_coro_ssl.cpp new file mode 100644 index 000000000..8454db908 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/coro-ssl/http_client_coro_ssl.cpp @@ -0,0 +1,180 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP SSL client, coroutine +// +//------------------------------------------------------------------------------ + +#include "example/common/root_certificates.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/spawn.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <string> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Performs an HTTP GET and prints the response +void +do_session( + std::string const& host, + std::string const& port, + std::string const& target, + int version, + net::io_context& ioc, + ssl::context& ctx, + net::yield_context yield) +{ + beast::error_code ec; + + // These objects perform our I/O + tcp::resolver resolver(ioc); + beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx); + + // Set SNI Hostname (many hosts need this to handshake successfully) + if(! SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) + { + ec.assign(static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()); + std::cerr << ec.message() << "\n"; + return; + } + + // Look up the domain name + auto const results = resolver.async_resolve(host, port, yield[ec]); + if(ec) + return fail(ec, "resolve"); + + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + get_lowest_layer(stream).async_connect(results, yield[ec]); + if(ec) + return fail(ec, "connect"); + + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); + + // Perform the SSL handshake + stream.async_handshake(ssl::stream_base::client, yield[ec]); + if(ec) + return fail(ec, "handshake"); + + // Set up an HTTP GET request message + http::request<http::string_body> req{http::verb::get, target, version}; + req.set(http::field::host, host); + req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); + + // Send the HTTP request to the remote host + http::async_write(stream, req, yield[ec]); + if(ec) + return fail(ec, "write"); + + // This buffer is used for reading and must be persisted + beast::flat_buffer b; + + // Declare a container to hold the response + http::response<http::dynamic_body> res; + + // Receive the HTTP response + http::async_read(stream, b, res, yield[ec]); + if(ec) + return fail(ec, "read"); + + // Write the message to standard out + std::cout << res << std::endl; + + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); + + // Gracefully close the stream + stream.async_shutdown(yield[ec]); + if(ec == net::error::eof) + { + // Rationale: + // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error + ec = {}; + } + if(ec) + return fail(ec, "shutdown"); + + // If we get here then the connection is closed gracefully +} + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4 && argc != 5) + { + std::cerr << + "Usage: http-client-coro-ssl <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" << + "Example:\n" << + " http-client-coro-ssl www.example.com 443 /\n" << + " http-client-coro-ssl www.example.com 443 / 1.0\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const target = argv[3]; + int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11; + + // The io_context is required for all I/O + net::io_context ioc; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12_client}; + + // This holds the root certificate used for verification + load_root_certificates(ctx); + + // Verify the remote server's certificate + ctx.set_verify_mode(ssl::verify_peer); + + // Launch the asynchronous operation + boost::asio::spawn(ioc, std::bind( + &do_session, + std::string(host), + std::string(port), + std::string(target), + version, + std::ref(ioc), + std::ref(ctx), + std::placeholders::_1)); + + // Run the I/O service. The call will return when + // the get operation is complete. + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/client/coro/CMakeLists.txt b/src/boost/libs/beast/example/http/client/coro/CMakeLists.txt new file mode 100644 index 000000000..1a791ab1b --- /dev/null +++ b/src/boost/libs/beast/example/http/client/coro/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/http/client/coro "/") + +add_executable (http-client-coro + ${BOOST_BEAST_FILES} + Jamfile + http_client_coro.cpp + ) + +target_link_libraries(http-client-coro + lib-asio + lib-beast) + +set_property(TARGET http-client-coro PROPERTY FOLDER "example-http-client") diff --git a/src/boost/libs/beast/example/http/client/coro/Jamfile b/src/boost/libs/beast/example/http/client/coro/Jamfile new file mode 100644 index 000000000..25ebe2fda --- /dev/null +++ b/src/boost/libs/beast/example/http/client/coro/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 +# + +exe http-client-coro : + http_client_coro.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + <library>/boost/coroutine//boost_coroutine + ; diff --git a/src/boost/libs/beast/example/http/client/coro/http_client_coro.cpp b/src/boost/libs/beast/example/http/client/coro/http_client_coro.cpp new file mode 100644 index 000000000..274ec05e0 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/coro/http_client_coro.cpp @@ -0,0 +1,144 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP client, coroutine +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/spawn.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <string> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Performs an HTTP GET and prints the response +void +do_session( + std::string const& host, + std::string const& port, + std::string const& target, + int version, + net::io_context& ioc, + net::yield_context yield) +{ + beast::error_code ec; + + // These objects perform our I/O + tcp::resolver resolver(ioc); + beast::tcp_stream stream(ioc); + + // Look up the domain name + auto const results = resolver.async_resolve(host, port, yield[ec]); + if(ec) + return fail(ec, "resolve"); + + // Set the timeout. + stream.expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + stream.async_connect(results, yield[ec]); + if(ec) + return fail(ec, "connect"); + + // Set up an HTTP GET request message + http::request<http::string_body> req{http::verb::get, target, version}; + req.set(http::field::host, host); + req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + + // Set the timeout. + stream.expires_after(std::chrono::seconds(30)); + + // Send the HTTP request to the remote host + http::async_write(stream, req, yield[ec]); + if(ec) + return fail(ec, "write"); + + // This buffer is used for reading and must be persisted + beast::flat_buffer b; + + // Declare a container to hold the response + http::response<http::dynamic_body> res; + + // Receive the HTTP response + http::async_read(stream, b, res, yield[ec]); + if(ec) + return fail(ec, "read"); + + // Write the message to standard out + std::cout << res << std::endl; + + // Gracefully close the socket + stream.socket().shutdown(tcp::socket::shutdown_both, ec); + + // not_connected happens sometimes + // so don't bother reporting it. + // + if(ec && ec != beast::errc::not_connected) + return fail(ec, "shutdown"); + + // If we get here then the connection is closed gracefully +} + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4 && argc != 5) + { + std::cerr << + "Usage: http-client-coro <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" << + "Example:\n" << + " http-client-coro www.example.com 80 /\n" << + " http-client-coro www.example.com 80 / 1.0\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const target = argv[3]; + int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11; + + // The io_context is required for all I/O + net::io_context ioc; + + // Launch the asynchronous operation + boost::asio::spawn(ioc, std::bind( + &do_session, + std::string(host), + std::string(port), + std::string(target), + version, + std::ref(ioc), + std::placeholders::_1)); + + // Run the I/O service. The call will return when + // the get operation is complete. + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/client/crawl/CMakeLists.txt b/src/boost/libs/beast/example/http/client/crawl/CMakeLists.txt new file mode 100644 index 000000000..b502c7d70 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/crawl/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# 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(example/http/client/crawl "/") + +add_executable (http-crawl + ${BOOST_BEAST_FILES} + Jamfile + urls_large_data.hpp + urls_large_data.cpp + http_crawl.cpp +) + +target_link_libraries(http-crawl + lib-asio + lib-beast) + +set_property(TARGET http-crawl PROPERTY FOLDER "example-http-client") + diff --git a/src/boost/libs/beast/example/http/client/crawl/Jamfile b/src/boost/libs/beast/example/http/client/crawl/Jamfile new file mode 100644 index 000000000..8f94c9675 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/crawl/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 +# + +exe http-crawl : + http_crawl.cpp + urls_large_data.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/client/crawl/http_crawl.cpp b/src/boost/libs/beast/example/http/client/crawl/http_crawl.cpp new file mode 100644 index 000000000..c303518d0 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/crawl/http_crawl.cpp @@ -0,0 +1,409 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP crawl (asynchronous) +// +//------------------------------------------------------------------------------ + +#include "urls_large_data.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/bind_executor.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/post.hpp> +#include <boost/asio/strand.hpp> +#include <atomic> +#include <chrono> +#include <cstdlib> +#include <functional> +#include <iomanip> +#include <iostream> +#include <memory> +#include <string> +#include <thread> +#include <vector> +#include <map> + +namespace chrono = std::chrono; // from <chrono> +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// This structure aggregates statistics on all the sites +class crawl_report +{ + net::io_context& ioc_; + net::strand< + net::io_context::executor_type> strand_; + std::atomic<std::size_t> index_; + std::vector<char const*> const& hosts_; + std::size_t count_ = 0; + +public: + crawl_report(net::io_context& ioc) + : ioc_(ioc) + , strand_(ioc_.get_executor()) + , index_(0) + , hosts_(urls_large_data()) + { + } + + // Run an aggregation function on the strand. + // This allows synchronization without a mutex. + template<class F> + void + aggregate(F const& f) + { + net::post( + strand_, + [&, f] + { + f(*this); + if(count_ % 100 == 0) + { + std::cerr << + "Progress: " << count_ << " of " << hosts_.size() << "\n"; + //std::cerr << *this; + } + ++count_; + }); + } + + // Returns the next host to check + char const* + get_host() + { + auto const n = index_++; + if(n >= hosts_.size()) + return nullptr; + return hosts_[n]; + } + + // Counts the number of timer failures + std::size_t timer_failures = 0; + + // Counts the number of name resolution failures + std::size_t resolve_failures = 0; + + // Counts the number of connection failures + std::size_t connect_failures = 0; + + // Counts the number of write failures + std::size_t write_failures = 0; + + // Counts the number of read failures + std::size_t read_failures = 0; + + // Counts the number of success reads + std::size_t success = 0; + + // Counts the number received of each status code + std::map<unsigned, std::size_t> status_codes; +}; + +std::ostream& +operator<<(std::ostream& os, crawl_report const& report) +{ + // Print the report + os << + "Crawl report\n" << + " Failure counts\n" << + " Timer : " << report.timer_failures << "\n" << + " Resolve : " << report.resolve_failures << "\n" << + " Connect : " << report.connect_failures << "\n" << + " Write : " << report.write_failures << "\n" << + " Read : " << report.read_failures << "\n" << + " Success : " << report.success << "\n" << + " Status codes\n" + ; + for(auto const& result : report.status_codes) + os << + " " << std::setw(3) << result.first << ": " << result.second << + " (" << http::obsolete_reason(static_cast<http::status>(result.first)) << ")\n"; + os.flush(); + return os; +} + +//------------------------------------------------------------------------------ + +// Performs HTTP GET requests and aggregates the results into a report +class worker : public std::enable_shared_from_this<worker> +{ + enum + { + // Use a small timeout to keep things lively + timeout = 5 + }; + + crawl_report& report_; + tcp::resolver resolver_; + beast::tcp_stream stream_; + beast::flat_buffer buffer_; // (Must persist between reads) + http::request<http::empty_body> req_; + http::response<http::string_body> res_; + +public: + worker(worker&&) = default; + + // Resolver and socket require an io_context + worker( + crawl_report& report, + net::io_context& ioc) + : report_(report) + , resolver_(net::make_strand(ioc)) + , stream_(net::make_strand(ioc)) + { + // Set up the common fields of the request + req_.version(11); + req_.method(http::verb::get); + req_.target("/"); + req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + } + + // Start the asynchronous operation + void + run() + { + do_get_host(); + } + + void + do_get_host() + { + // Grab another host + auto const host = report_.get_host(); + + // nullptr means no more work + if(! host) + return; + + // The Host HTTP field is required + req_.set(http::field::host, host); + + // Set up an HTTP GET request message + // Look up the domain name + resolver_.async_resolve( + host, + "http", + beast::bind_front_handler( + &worker::on_resolve, + shared_from_this())); + } + + void + on_resolve( + beast::error_code ec, + tcp::resolver::results_type results) + { + if(ec) + { + report_.aggregate( + [](crawl_report& rep) + { + ++rep.resolve_failures; + }); + return do_get_host(); + } + + // Set a timeout on the operation + stream_.expires_after(std::chrono::seconds(10)); + + // Make the connection on the IP address we get from a lookup + stream_.async_connect( + results, + beast::bind_front_handler( + &worker::on_connect, + shared_from_this())); + } + + void + on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type) + { + if(ec) + { + report_.aggregate( + [](crawl_report& rep) + { + ++rep.connect_failures; + }); + return do_get_host(); + } + + // Set a timeout on the operation + stream_.expires_after(std::chrono::seconds(10)); + + // Send the HTTP request to the remote host + http::async_write( + stream_, + req_, + beast::bind_front_handler( + &worker::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + { + report_.aggregate( + [](crawl_report& rep) + { + ++rep.write_failures; + }); + return do_get_host(); + } + + // Receive the HTTP response + res_ = {}; + http::async_read( + stream_, + buffer_, + res_, + beast::bind_front_handler( + &worker::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + { + report_.aggregate( + [](crawl_report& rep) + { + ++rep.read_failures; + }); + return do_get_host(); + } + + auto const code = res_.result_int(); + report_.aggregate( + [code](crawl_report& rep) + { + ++rep.success; + ++rep.status_codes[code]; + }); + + // Gracefully close the socket + stream_.socket().shutdown(tcp::socket::shutdown_both, ec); + stream_.close(); + + // If we get here then the connection is closed gracefully + + do_get_host(); + } +}; + +class timer +{ + using clock_type = 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_; + } +}; + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 2) + { + std::cerr << + "Usage: http-crawl <threads>\n" << + "Example:\n" << + " http-crawl 100 1\n"; + return EXIT_FAILURE; + } + auto const threads = std::max<int>(1, std::atoi(argv[1])); + + // The io_context is required for all I/O + net::io_context ioc; + + // The work keeps io_context::run from returning + auto work = net::make_work_guard(ioc); + + // The report holds the aggregated statistics + crawl_report report{ioc}; + + timer t; + + // Create and launch the worker threads. + std::vector<std::thread> workers; + workers.reserve(threads + 1); + for(int i = 0; i < threads; ++i) + workers.emplace_back( + [&report] + { + // We use a separate io_context for each worker because + // the asio resolver simulates asynchronous operation using + // a dedicated worker thread per io_context, and we want to + // do a lot of name resolutions in parallel. + net::io_context ioc{1}; + std::make_shared<worker>(report, ioc)->run(); + ioc.run(); + }); + + // Add another thread to run the main io_context which + // is used to aggregate the statistics + workers.emplace_back( + [&ioc] + { + ioc.run(); + }); + + // Now block until all threads exit + for(std::size_t i = 0; i < workers.size(); ++i) + { + auto& thread = workers[i]; + + // If this is the last thread, reset the + // work object so that it can return from run. + if(i == workers.size() - 1) + work.reset(); + + // Wait for the thread to exit + thread.join(); + } + + std::cout << + "Elapsed time: " << chrono::duration_cast<chrono::seconds>(t.elapsed()).count() << " seconds\n"; + std::cout << report; + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/client/crawl/urls_large_data.cpp b/src/boost/libs/beast/example/http/client/crawl/urls_large_data.cpp new file mode 100644 index 000000000..cee46ee26 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/crawl/urls_large_data.cpp @@ -0,0 +1,10021 @@ +// +// 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 "urls_large_data.hpp" + +// Data from Alexa top 1 million sites +// http://s3.amazonaws.com/alexa-static/top-1m.csv.zip +// +std::vector<char const*> const& +urls_large_data() +{ + static std::vector <char const*> const urls ({ + "google.com", + "facebook.com", + "youtube.com", + "yahoo.com", + "baidu.com", + "wikipedia.org", + "qq.com", + "linkedin.com", + "taobao.com", + "twitter.com", + "live.com", + "amazon.com", + "sina.com.cn", + "google.co.in", + "hao123.com", + "blogspot.com", + "weibo.com", + "wordpress.com", + "yandex.ru", + "yahoo.co.jp", + "tmall.com", + "bing.com", + "vk.com", + "ebay.com", + "360.cn", + "google.de", + "sohu.com", + "pinterest.com", + "google.co.uk", + "ask.com", + "google.fr", + "msn.com", + "163.com", + "soso.com", + "tumblr.com", + "google.co.jp", + "instagram.com", + "mail.ru", + "google.com.br", + "microsoft.com", + "paypal.com", + "google.ru", + "xvideos.com", + "google.es", + "imdb.com", + "apple.com", + "google.it", + "adcash.com", + "craigslist.org", + "stackoverflow.com", + "amazon.co.jp", + "neobux.com", + "google.com.hk", + "imgur.com", + "ifeng.com", + "cnn.com", + "google.com.mx", + "xhamster.com", + "xinhuanet.com", + "gmw.cn", + "reddit.com", + "bbc.co.uk", + "blogger.com", + "google.ca", + "fc2.com", + "vube.com", + "go.com", + "akamaihd.net", + "alipay.com", + "about.com", + "people.com.cn", + "t.co", + "googleusercontent.com", + "wordpress.org", + "odnoklassniki.ru", + "alibaba.com", + "google.com.tr", + "aliexpress.com", + "youku.com", + "conduit.com", + "huffingtonpost.com", + "godaddy.com", + "flickr.com", + "pornhub.com", + "google.com.au", + "amazon.de", + "blogspot.in", + "kickass.to", + "ebay.de", + "netflix.com", + "google.pl", + "thepiratebay.se", + "bp.blogspot.com", + "adobe.com", + "dailymotion.com", + "china.com", + "vimeo.com", + "dailymail.co.uk", + "cnet.com", + "rakuten.co.jp", + "espn.go.com", + "ku6.com", + "ebay.co.uk", + "themeforest.net", + "xnxx.com", + "livejasmin.com", + "indiatimes.com", + "aol.com", + "redtube.com", + "dropbox.com", + "amazonaws.com", + "uol.com.br", + "weather.com", + "m2newmedia.com", + "amazon.co.uk", + "google.com.ar", + "google.com.sa", + "pixnet.net", + "nytimes.com", + "slideshare.net", + "youporn.com", + "google.com.eg", + "buzzfeed.com", + "wikimedia.org", + "booking.com", + "livejournal.com", + "globo.com", + "fiverr.com", + "adf.ly", + "secureserver.net", + "mozilla.org", + "google.com.pk", + "google.com.tw", + "google.nl", + "yelp.com", + "sogou.com", + "ameblo.jp", + "directrev.com", + "clkmon.com", + "hootsuite.com", + "deviantart.com", + "blogfa.com", + "wikia.com", + "outbrain.com", + "flipkart.com", + "wikihow.com", + "etsy.com", + "google.co.th", + "soundcloud.com", + "google.co.za", + "avg.com", + "w3schools.com", + "theguardian.com", + "stumbleupon.com", + "espncricinfo.com", + "livedoor.com", + "forbes.com", + "archive.org", + "4shared.com", + "foxnews.com", + "files.wordpress.com", + "answers.com", + "bankofamerica.com", + "chase.com", + "google.co.ve", + "mediafire.com", + "torrentz.eu", + "badoo.com", + "salesforce.com", + "aweber.com", + "sourceforge.net", + "bbc.com", + "addthis.com", + "liveinternet.ru", + "china.com.cn", + "indeed.com", + "reference.com", + "github.com", + "skype.com", + "hostgator.com", + "google.gr", + "bet365.com", + "spiegel.de", + "ask.fm", + "so.com", + "statcounter.com", + "gameforge.com", + "onet.pl", + "naver.com", + "google.com.co", + "developunit.info", + "google.com.vn", + "google.co.id", + "nicovideo.jp", + "shutterstock.com", + "walmart.com", + "google.be", + "mailchimp.com", + "softonic.com", + "stackexchange.com", + "google.com.ng", + "google.com.ua", + "popads.net", + "allegro.pl", + "gamer.com.tw", + "wordreference.com", + "wellsfargo.com", + "loading-delivery1.com", + "zillow.com", + "tripadvisor.com", + "quikr.com", + "pandora.com", + "wsj.com", + "goo.ne.jp", + "bild.de", + "tube8.com", + "wix.com", + "google.se", + "coccoc.com", + "google.ro", + "suning.com", + "photobucket.com", + "weebly.com", + "tianya.cn", + "warriorforum.com", + "telegraph.co.uk", + "google.dz", + "naver.jp", + "php.net", + "ups.com", + "rambler.ru", + "zedo.com", + "reuters.com", + "google.at", + "media.tumblr.com", + "taringa.net", + "google.com.ph", + "chinaz.com", + "mashable.com", + "blogspot.com.es", + "hurriyet.com.tr", + "google.com.pe", + "bleacherreport.com", + "gmx.net", + "wp.pl", + "goodreads.com", + "leboncoin.fr", + "rutracker.org", + "lenta.ru", + "babylon.com", + "domaintools.com", + "sharelive.net", + "rediff.com", + "google.ch", + "comcast.net", + "twitch.tv", + "avito.ru", + "kaskus.co.id", + "nbcnews.com", + "onclickads.net", + "businessinsider.com", + "ikea.com", + "codecanyon.net", + "ndtv.com", + "usps.com", + "google.cl", + "doublepimp.com", + "moz.com", + "google.com.sg", + "usatoday.com", + "dmm.co.jp", + "amazon.fr", + "google.pt", + "ucoz.ru", + "rt.com", + "milliyet.com.tr", + "xuite.net", + "samsung.com", + "fedex.com", + "uploaded.net", + "pcpop.com", + "google.com.bd", + "bitly.com", + "goodgamestudios.com", + "ettoday.net", + "baomihua.com", + "39.net", + "web.de", + "rbc.ru", + "9gag.com", + "disqus.com", + "snapdeal.com", + "xcar.com.cn", + "washingtonpost.com", + "bloomberg.com", + "scribd.com", + "hp.com", + "constantcontact.com", + "4dsply.com", + "gsmarena.com", + "intuit.com", + "meetup.com", + "nih.gov", + "americanexpress.com", + "ehow.com", + "infusionsoft.com", + "google.cz", + "mercadolivre.com.br", + "myntra.com", + "varzesh3.com", + "mobile01.com", + "hardsextube.com", + "goal.com", + "thefreecamsecret.com", + "thefreedictionary.com", + "douban.com", + "iqiyi.com", + "it168.com", + "mama.cn", + "tmz.com", + "time.com", + "olx.in", + "microsoftonline.com", + "hulu.com", + "enet.com.cn", + "speedtest.net", + "orange.fr", + "free.fr", + "detik.com", + "bluehost.com", + "libero.it", + "histats.com", + "webmd.com", + "eazel.com", + "hudong.com", + "extratorrent.cc", + "google.co.kr", + "chaturbate.com", + "huanqiu.com", + "cnzz.com", + "daum.net", + "xing.com", + "force.com", + "jrj.com.cn", + "pchome.net", + "cj.com", + "youjizz.com", + "delta-search.com", + "techcrunch.com", + "motherless.com", + "tinyurl.com", + "beeg.com", + "kooora.com", + "google.cn", + "clickbank.com", + "yandex.ua", + "ad6media.fr", + "google.no", + "amazon.cn", + "zippyshare.com", + "acesse.com", + "google.co.hu", + "google.ae", + "hdfcbank.com", + "nba.com", + "zendesk.com", + "blogspot.co.uk", + "bestbuy.com", + "accuweather.com", + "getresponse.com", + "repubblica.it", + "xywy.com", + "ci123.com", + "ebay.in", + "in.com", + "ign.com", + "groupon.com", + "yesky.com", + "elpais.com", + "marca.com", + "kwejk.pl", + "caijing.com.cn", + "google.ie", + "cloudfront.net", + "intoday.in", + "wideinfo.org", + "pof.com", + "kakaku.com", + "bitauto.com", + "life.com.tw", + "xe.com", + "goo.gl", + "google.az", + "feedly.com", + "tagged.com", + "amazon.in", + "list-manage.com", + "w3.org", + "quora.com", + "ixxx.com", + "mysearchdial.com", + "dell.com", + "seznam.cz", + "t-online.de", + "plugrush.com", + "nydailynews.com", + "okcupid.com", + "istockphoto.com", + "snapdo.com", + "abcnews.go.com", + "target.com", + "latimes.com", + "mywebsearch.com", + "joomla.org", + "odesk.com", + "surveymonkey.com", + "elmundo.es", + "siteadvisor.com", + "aili.com", + "uimserv.net", + "ebay.com.au", + "doubleclick.com", + "zeobit.com", + "google.co.il", + "fbcdn.net", + "ero-advertising.com", + "issuu.com", + "naukri.com", + "gazeta.pl", + "ck101.com", + "udn.com", + "google.dk", + "ce.cn", + "capitalone.com", + "att.com", + "drudgereport.com", + "blackhatworld.com", + "adrotator.se", + "retailmenot.com", + "justdial.com", + "icicibank.com", + "likes.com", + "elance.com", + "cntv.cn", + "ameba.jp", + "java.com", + "lifehacker.com", + "jabong.com", + "gizmodo.com", + "ebay.it", + "lenovo.com", + "hypergames.net", + "google.fi", + "doorblog.jp", + "blogspot.de", + "jimdo.com", + "probux.com", + "blogspot.it", + "habrahabr.ru", + "webmoney.ru", + "irctc.co.in", + "sahibinden.com", + "github.io", + "informer.com", + "mysearchresults.com", + "upworthy.com", + "pch.com", + "eyny.com", + "canadaalltax.com", + "b5m.com", + "freelancer.com", + "match.com", + "searchfun.in", + "adnxs.com", + "sberbank.ru", + "ig.com.br", + "trulia.com", + "subscene.com", + "corriere.it", + "livescore.com", + "irs.gov", + "twoo.com", + "clixsense.com", + "4399.com", + "xda-developers.com", + "iminent.com", + "google.com.my", + "onlinesbi.com", + "empowernetwork.com", + "zing.vn", + "youdao.com", + "systweak.com", + "flipora.com", + "ning.com", + "engadget.com", + "abril.com.br", + "exoclick.com", + "semrush.com", + "commentcamarche.net", + "shareasale.com", + "vnexpress.net", + "battle.net", + "youm7.com", + "digg.com", + "rapidgator.net", + "expedia.com", + "twimg.com", + "newegg.com", + "lady8844.com", + "xgo.com.cn", + "pcgames.com.cn", + "fotolia.com", + "rednet.cn", + "inspsearch.com", + "lemonde.fr", + "adultfriendfinder.com", + "typepad.com", + "mercadolibre.com.ar", + "homedepot.com", + "chip.de", + "lefigaro.fr", + "swagbucks.com", + "taleo.net", + "kinopoisk.ru", + "oracle.com", + "who.is", + "2ch.net", + "qtrax.com", + "eonline.com", + "sochi2014.com", + "myfreecams.com", + "pcbaby.com.cn", + "gome.com.cn", + "amazon.it", + "timeanddate.com", + "bestusefuldownloads.com", + "foursquare.com", + "telexfree.com", + "gc.ca", + "58.com", + "hubspot.com", + "backpage.com", + "examiner.com", + "ria.ru", + "google.sk", + "opensiteexplorer.org", + "awesomehp.com", + "sexlog.com", + "gutefrage.net", + "steampowered.com", + "slate.com", + "marketwatch.com", + "beva.com", + "shaadi.com", + "v1.cn", + "teensdigest.com", + "blogspot.ru", + "taboola.com", + "citrixonline.com", + "hatena.ne.jp", + "seesaa.net", + "houzz.com", + "bodybuilding.com", + "webs.com", + "chexun.com", + "ya.ru", + "citibank.com", + "viralnova.com", + "2345.com", + "ashleyrnadison.com", + "verizonwireless.com", + "55bbs.com", + "theblaze.com", + "123rf.com", + "gotomeeting.com", + "kijiji.ca", + "linkbucks.com", + "haber7.com", + "pravda.com.ua", + "altervista.org", + "mihanblog.com", + "terra.com.br", + "soku.com", + "urbandictionary.com", + "mercadolibre.com.mx", + "pixiv.net", + "oneindia.in", + "yellowpages.com", + "hotels.com", + "drtuber.com", + "scoop.it", + "blogspot.jp", + "cbslocal.com", + "movie4k.to", + "cpmterra.com", + "hubpages.com", + "mobile.de", + "yaolan.com", + "etao.com", + "hidemyass.com", + "ca.gov", + "dreamstime.com", + "firedrive.com", + "searchengines.ru", + "m-w.com", + "ebay.fr", + "templatemonster.com", + "evernote.com", + "fastdailyfind.com", + "amazon.es", + "gateable.com", + "hupu.com", + "y8.com", + "blogspot.com.tr", + "tokobagus.com", + "azlyrics.com", + "website-unavailable.com", + "r10.net", + "wiktionary.org", + "bongacams.com", + "focus.de", + "linksynergy.com", + "steamcommunity.com", + "vk.me", + "sakura.ne.jp", + "foxsports.com", + "optmd.com", + "tabelog.com", + "narod.ru", + "glassdoor.com", + "outlook.com", + "europa.eu", + "viadeo.com", + "leadpages.net", + "ouedkniss.com", + "facenama.com", + "lpcloudsvr302.com", + "agoda.com", + "qvo6.com", + "majesticseo.com", + "mirror.co.uk", + "google.com.kw", + "filehippo.com", + "moneycontrol.com", + "duckduckgo.com", + "npr.org", + "coupons.com", + "mynet.com", + "allrecipes.com", + "priceline.com", + "liveleak.com", + "jqw.com", + "slickdeals.net", + "webcrawler.com", + "babytree.com", + "amung.us", + "tomshardware.com", + "openadserving.com", + "independent.co.uk", + "kompas.com", + "turbobit.net", + "google.kz", + "leo.org", + "yandex.com.tr", + "mp3skull.com", + "nordstrom.com", + "news.com.au", + "traidnt.net", + "wunderground.com", + "cnbc.com", + "jquery.com", + "dict.cc", + "persianblog.ir", + "clarin.com", + "all-free-download.com", + "fhserve.com", + "sape.ru", + "asos.com", + "addmefast.com", + "lequipe.fr", + "lapatilla.com", + "ancestry.com", + "stockstar.com", + "monster.com", + "people.com", + "gawker.com", + "howstuffworks.com", + "tradedoubler.com", + "over-blog.com", + "cbc.ca", + "fishcod.com", + "yoka.com", + "macys.com", + "gazeta.ru", + "free-tv-video-online.me", + "google.bg", + "google.lk", + "southwest.com", + "realtor.com", + "custhelp.com", + "bhaskar.com", + "softpedia.com", + "farsnews.com", + "cy-pr.com", + "theverge.com", + "tudou.com", + "youboy.com", + "porn.com", + "wetransfer.com", + "virgilio.it", + "mega.co.nz", + "sfgate.com", + "delta-homes.com", + "squarespace.com", + "watchseries.lt", + "overstock.com", + "nifty.com", + "jvzoo.com", + "lanacion.com.ar", + "prntscr.com", + "vesti.ru", + "zimbio.com", + "adscale.de", + "google.co.nz", + "yac.mx", + "tickld.com", + "kayak.com", + "youth.cn", + "jd.com", + "shopclues.com", + "allocine.fr", + "cracked.com", + "eventbrite.com", + "behance.net", + "youtube-mp3.org", + "bankmellat.ir", + "echo.msk.ru", + "autohome.com.cn", + "yandex.kz", + "sex.com", + "smh.com.au", + "digikala.com", + "mackolik.com", + "rottentomatoes.com", + "renren.com", + "wired.com", + "imageshack.us", + "interia.pl", + "zanox.com", + "eastday.com", + "fool.com", + "chinabyte.com", + "haberturk.com", + "hespress.com", + "wow.com", + "jobrapido.com", + "idnes.cz", + "qinbei.com", + "pr-cy.ru", + "klikbca.com", + "17ok.com", + "gamefaqs.com", + "immobilienscout24.de", + "biblegateway.com", + "espnfc.com", + "ehowenespanol.com", + "tabnak.ir", + "avazutracking.net", + "sapo.pt", + "welt.de", + "google.com.ec", + "sears.com", + "pcmag.com", + "nownews.com", + "kickstarter.com", + "pixlr.com", + "hongkiat.com", + "chinatimes.com", + "zoho.com", + "reverso.net", + "indianrail.gov.in", + "privatehomeclips.com", + "cbssports.com", + "entrepreneur.com", + "sporx.com", + "bhphotovideo.com", + "fatakat.com", + "xunlei.com", + "nikkei.com", + "chron.com", + "24h.com.vn", + "basecamp.com", + "airtel.in", + "dealshark.com", + "shopify.com", + "babycenter.com", + "letitbit.net", + "nuvid.com", + "yts.re", + "aparat.com", + "r2games.com", + "prestashop.com", + "alarabiya.net", + "india.com", + "magentocommerce.com", + "heise.de", + "sulekha.com", + "mpnrs.com", + "ibm.com", + "cbsnews.com", + "searchengineland.com", + "mapquest.com", + "ccb.com", + "zappos.com", + "rightmove.co.uk", + "sahadan.com", + "milanuncios.com", + "wenku.baidu.com/user/reg", + "ovh.net", + "screencast.com", + "ahrefs.com", + "6.cn", + "businessweek.com", + "linternaute.com", + "dafont.com", + "bestblackhatforum.com", + "streamcloud.eu", + "as.com", + "gtmetrix.com", + "wmmail.ru", + "spankwire.com", + "google.com.do", + "jeuxvideo.com", + "digitalpoint.com", + "amazon.ca", + "mlb.com", + "weheartit.com", + "list.ru", + "leagueoflegends.com", + "56.com", + "bloglovin.com", + "payoneer.com", + "nokia.com", + "aizhan.com", + "novinky.cz", + "ustream.tv", + "getbootstrap.com", + "seekingalpha.com", + "codwide.com", + "vice.com", + "usmagazine.com", + "primewire.ag", + "cam4.com", + "thehindu.com", + "deezer.com", + "graphicriver.net", + "dmm.com", + "biglobe.ne.jp", + "google.rs", + "z5x.net", + "tutsplus.com", + "icicibank.co.in", + "indiamart.com", + "gap.com", + "ensonhaber.com", + "sitepoint.com", + "rutor.org", + "chinanews.com", + "brainyquote.com", + "keepvid.com", + "censor.net.ua", + "subito.it", + "tablica.pl", + "onlylady.com", + "zulily.com", + "squidoo.com", + "change.org", + "sh.st", + "webhostingtalk.com", + "9gag.tv", + "nairaland.com", + "gulfup.com", + "staples.com", + "gamespot.com", + "woorank.com", + "box.com", + "infobae.com", + "elegantthemes.com", + "104.com.tw", + "aftonbladet.se", + "google.hr", + "myegy.com", + "noticias24.com", + "eastmoney.com", + "lowes.com", + "xtube.com", + "worldstarhiphop.com", + "glispa.com", + "verizon.com", + "united.com", + "linkwithin.com", + "google.com.ly", + "whitepages.com", + "games.la", + "dmoz.org", + "imagebam.com", + "careerbuilder.com", + "sky.com", + "instructables.com", + "mercadolibre.com.ve", + "makemytrip.com", + "pinimg.com", + "pingdom.com", + "foodnetwork.com", + "orf.at", + "android.com", + "wiocha.pl", + "csdn.net", + "appledaily.com.tw", + "nike.com", + "eluniversal.com.mx", + "delta.com", + "ezinearticles.com", + "sueddeutsche.de", + "woothemes.com", + "comcast.com", + "gismeteo.ru", + "lotour.com", + "nfl.com", + "xbox.com", + "almanar.com.lb", + "disney.go.com", + "ctrip.com", + "forexfactory.com", + "clicksvenue.com", + "nhl.com", + "ted.com", + "gstatic.com", + "forobeta.com", + "ovh.com", + "mystart.com", + "maktoob.com", + "nouvelobs.com", + "hindustantimes.com", + "billdesk.com", + "1und1.de", + "zhaopin.com", + "weather.gov", + "kioskea.net", + "myspace.com", + "firstpost.com", + "picmonkey.com", + "grooveshark.com", + "exblog.jp", + "pantip.com", + "earthlink.net", + "styletv.com.cn", + "icloud.com", + "manta.com", + "google.by", + "mediaset.it", + "searchenginewatch.com", + "japanpost.jp", + "postimg.org", + "mbc.net", + "cheezburger.com", + "workercn.cn", + "directadvert.ru", + "nationzoom.com", + "caixa.gov.br", + "gumtree.com", + "51job.com", + "viooz.co", + "usbank.com", + "wmtransfer.com", + "fidelity.com", + "gogetlinks.net", + "office365.com", + "pchome.com.tw", + "feedburner.com", + "mp3truck.net", + "sfr.fr", + "junglee.com", + "keezmovies.com", + "binarysystem4u.com", + "way2sms.com", + "oyunskor.com", + "peyvandha.ir", + "ticketmaster.com", + "lacaixa.es", + "video-one.com", + "yomiuri.co.jp", + "popcash.net", + "nypost.com", + "dubizzle.com", + "wpmudev.org", + "mgid.com", + "google.lt", + "zazzle.com", + "nbcolympics.com", + "battlefield.com", + "avast.com", + "jsfiddle.net", + "namecheap.com", + "css-tricks.com", + "friv.com", + "6park.com", + "xhamstercams.com", + "mysql.com", + "dx.com", + "thechive.com", + "vsuch.com", + "souq.com", + "anyoption.com", + "dantri.com.vn", + "ebay.es", + "abc.es", + "whatsapp.com", + "bitshare.com", + "savefrom.net", + "beytoote.com", + "xiaomi.com", + "prweb.com", + "prothom-alo.com", + "pagesjaunes.fr", + "bbb.org", + "eenadu.net", + "tinypic.com", + "whois.com", + "sourtimes.org", + "adk2.com", + "criteo.com", + "tistory.com", + "ilyke.net", + "ruten.com.tw", + "skysports.com", + "makeuseof.com", + "p5w.net", + "hh.ru", + "nationalgeographic.com", + "pornmd.com", + "rakuten.ne.jp", + "idealo.de", + "telegraaf.nl", + "barnesandnoble.com", + "mynavi.jp", + "4chan.org", + "media-fire.org", + "crunchbase.com", + "bravotube.net", + "myfitnesspal.com", + "today.com", + "mit.edu", + "cnmo.com", + "nu.nl", + "tripadvisor.co.uk", + "spotify.com", + "homeway.com.cn", + "dianping.com", + "costco.com", + "pastebin.com", + "dhgate.com", + "windowsphone.com", + "stagram.com", + "investopedia.com", + "ninemsn.com.au", + "excite.co.jp", + "22find.com", + "google.iq", + "hi5.com", + "ad4game.com", + "kohls.com", + "online.sh.cn", + "geocities.jp", + "investing.com", + "haberler.com", + "ft.com", + "weblio.jp", + "google.tn", + "list-manage1.com", + "hsbc.co.uk", + "ppstream.com", + "cloob.com", + "korrespondent.net", + "ucoz.com", + "1and1.com", + "mail.com", + "fotostrana.ru", + "accountonline.com", + "forgeofempires.com", + "dribbble.com", + "city-data.com", + "drupal.org", + "tunein.com", + "51fanli.com", + "ibtimes.com", + "intel.com", + "polyvore.com", + "cbs.com", + "bab.la", + "imobile.com.cn", + "bestadbid.com", + "eztv.it", + "smashingmagazine.com", + "skyrock.com", + "copyscape.com", + "cookpad.com", + "smallseotools.com", + "asus.com", + "anysex.com", + "nbcsports.com", + "kinox.to", + "mbank.com.pl", + "500px.com", + "sofanti.com", + "marktplaats.nl", + "cpanel.net", + "hm.com", + "chicagotribune.com", + "ultimate-guitar.com", + "infolinks.com", + "merdeka.com", + "yandex.by", + "tribalfusion.com", + "livedoor.biz", + "prezi.com", + "last.fm", + "sznews.com", + "junbi-tracker.com", + "so-net.ne.jp", + "cocolog-nifty.com", + "ocn.ne.jp", + "perezhilton.com", + "yaplakal.com", + "championat.com", + "ebay.ca", + "mangareader.net", + "donanimhaber.com", + "miniclip.com", + "vcommission.com", + "noaa.gov", + "imagefap.com", + "gumtree.com.au", + "liveperson.net", + "gfycat.com", + "hollywoodreporter.com", + "dropboxusercontent.com", + "akhbarak.net", + "sou300.com", + "kicker.de", + "livestream.com", + "tribunnews.com", + "kp.ru", + "techradar.com", + "315che.com", + "dw.de", + "codeproject.com", + "newsru.com", + "pconline.com.cn", + "aa.com", + "usaa.com", + "asahi.com", + "themetapicture.com", + "quicksprout.com", + "piriform.com", + "888poker.com", + "gittigidiyor.com", + "nhk.or.jp", + "statigr.am", + "ipage.com", + "gazzetta.it", + "letv.com", + "yourlust.com", + "freepik.com", + "trello.com", + "mixi.jp", + "livememe.com", + "ew.com", + "seosprint.net", + "doisongphapluat.com", + "autotrader.com", + "docstoc.com", + "viva.co.id", + "livestrong.com", + "inc.com", + "hunantv.com", + "bahn.de", + "depositphotos.com", + "rackspace.com", + "flippa.com", + "norton.com", + "lynda.com", + "teamviewer.com", + "vip.com", + "ksl.com", + "solarmovie.so", + "klout.com", + "pikabu.ru", + "vporn.com", + "apache.org", + "sportbox.ru", + "correios.com.br", + "toptenreviews.com", + "ea.com", + "thenextweb.com", + "superuser.com", + "delicious.com", + "zoosk.com", + "inbox.com", + "kongregate.com", + "starbaby.cn", + "888casino.com", + "brazzers.com", + "sockshare.com", + "megashare.info", + "vistaprint.com", + "porntube.com", + "seriesyonkis.com", + "20minutos.es", + "jagran.com", + "programme-tv.net", + "elcomercio.pe", + "blogspot.kr", + "t411.me", + "googleapis.com", + "blog.com", + "qingdaonews.com", + "sedo.com", + "itmedia.co.jp", + "mtv.com", + "ynet.co.il", + "playstation.com", + "pornhublive.com", + "thedailybeast.com", + "r7.com", + "sabah.com.tr", + "126.com", + "topix.com", + "liga.net", + "marriott.com", + "tvn24.pl", + "sitesell.com", + "netdna-cdn.com", + "startimes.com", + "hstpnetwork.com", + "blogspot.com.au", + "17u.cn", + "subscribe.ru", + "woot.com", + "rozblog.com", + "wimp.com", + "vg.no", + "t-mobile.com", + "bmi.ir", + "kankan.com", + "tsn.ua", + "gumtree.co.za", + "googleadservices.com", + "pnc.com", + "cars.com", + "soccerway.com", + "o2.pl", + "zeit.de", + "zopim.com", + "cloudflare.com", + "givemesport.com", + "statscrop.com", + "gravatar.com", + "flightradar24.com", + "ubuntu.com", + "salon.com", + "ytimg.com", + "trovigo.com", + "sunporno.com", + "libertagia.com", + "mihanwebads.com", + "shopathome.com", + "cdiscount.com", + "commbank.com.au", + "elwatannews.com", + "discovercard.com", + "infowars.com", + "blogimg.jp", + "portaldosites.com", + "fixya.com", + "drom.ru", + "fastpic.ru", + "pbs.org", + "kandao.com", + "okwave.jp", + "faz.net", + "sbnation.com", + "hilton.com", + "duba.com", + "homeshop18.com", + "ultimasnoticias.com.ve", + "bankrate.com", + "megasesso.com", + "studiopress.com", + "sports.ru", + "asriran.com", + "etoro.com", + "cafemom.com", + "dhl.de", + "indiegogo.com", + "tdcanadatrust.com", + "1tv.ru", + "gogvo.com", + "bt.com", + "blogspot.tw", + "zergnet.com", + "garanti.com.tr", + "privatbank.ua", + "sanook.com", + "travelocity.com", + "networksolutions.com", + "thqafawe3lom.com", + "trklnks.com", + "torchbrowser.com", + "logmein.com", + "williamhill.com", + "zhihu.com", + "auto.ru", + "craigslist.ca", + "olx.com.pk", + "smi2.ru", + "ukr.net", + "dpreview.com", + "boston.com", + "folha.uol.com.br", + "name.com", + "ashemaletube.com", + "slimspots.com", + "envato.com", + "6pm.com", + "fandango.com", + "sozcu.com.tr", + "nikkeibp.co.jp", + "aufeminin.com", + "baiducontent.com", + "buscape.com.br", + "funnyordie.com", + "337.com", + "sakshi.com", + "airasia.com", + "samanyoluhaber.com", + "picofile.com", + "blackberry.com", + "google.com.gt", + "io9.com", + "easyhits4u.com", + "qianyan001.com", + "flashx.tv", + "zap2it.com", + "dnsrsearch.com", + "patch.com", + "staticflickr.com", + "clicksor.com", + "mcafee.com", + "zerohedge.com", + "zara.com", + "freelotto.com", + "tutorialspoint.com", + "qone8.com", + "olx.com.br", + "axisbank.com", + "marketgid.com", + "your-server.de", + "game321.com", + "harvard.edu", + "indianexpress.com", + "fatwallet.com", + "nudevista.com", + "paper.li", + "opera.com", + "serving-sys.com", + "nowvideo.sx", + "theatlantic.com", + "cisco.com", + "bookmyshow.com", + "webmasterworld.com", + "metro.co.uk", + "leparisien.fr", + "orbitz.com", + "h2porn.com", + "4cdn.org", + "itar-tass.com", + "nasa.gov", + "macrumors.com", + "google.si", + "usnews.com", + "premierleague.com", + "largeporntube.com", + "blogsky.com", + "bufferapp.com", + "zeroredirect1.com", + "sendspace.com", + "stanford.edu", + "state.gov", + "yjc.ir", + "abc.net.au", + "pornsharing.com", + "4pda.ru", + "52pk.net", + "meituan.com", + "mmgp.ru", + "sponichi.co.jp", + "masrawy.com", + "whatismyipaddress.com", + "onlinewebfind.com", + "news24.com", + "zdnet.com", + "imagevenue.com", + "gameaholic.com", + "mediaplex.com", + "clip.vn", + "2mdn.net", + "nmisr.com", + "musica.com", + "networkedblogs.com", + "metrolyrics.com", + "chacha.com", + "internetdownloadmanager.com", + "legacy.com", + "wwe.com", + "icbc.com.cn", + "ny.gov", + "chomikuj.pl", + "alexa.com", + "pcworld.com", + "overthumbs.com", + "adp.com", + "ad2games.com", + "adme.ru", + "bidvertiser.com", + "rtl.de", + "sweetim.com", + "gaana.com", + "7k7k.com", + "livingsocial.com", + "netteller.com", + "rakuten.com", + "chess.com", + "blackboard.com", + "thekitchn.com", + "ilfattoquotidiano.it", + "20minutes.fr", + "soufun.com", + "nerdbux.com", + "breitbart.com", + "webtretho.com", + "tvguide.com", + "miralinks.ru", + "weather.com.cn", + "zalando.de", + "dangdang.com", + "bubblews.com", + "thoughtcatalog.com", + "114la.com", + "msn.ca", + "blogmura.com", + "americanas.com.br", + "elitedaily.com", + "lightinthebox.com", + "bizjournals.com", + "yixun.com", + "itau.com.br", + "51.la", + "doodle.com", + "arabyonline.com", + "opencart.com", + "bomnegocio.com", + "lego.com", + "pclady.com.cn", + "metacafe.com", + "kimiss.com", + "journaldunet.com", + "gnavi.co.jp", + "51auto.com", + "clickey.com", + "chekb.com", + "searchnu.com", + "diply.com", + "walgreens.com", + "descargar.es", + "mirrorcreator.com", + "pornerbros.com", + "askmen.com", + "shop.com", + "inmotionhosting.com", + "filmweb.pl", + "kbb.com", + "appround.biz", + "aljazeera.com", + "lumosity.com", + "sweet-page.com", + "pole-emploi.fr", + "fishki.net", + "rollingstone.com", + "kapanlagi.com", + "yandex.com", + "cnblogs.com", + "plaintube.com", + "intentmedia.net", + "about.me", + "torrentz.in", + "01net.com", + "gyazo.com", + "nic.ru", + "juicyads.com", + "trend.az", + "askubuntu.com", + "unam.mx", + "tradus.com", + "tigerdirect.com", + "letras.mus.br", + "imagetwist.com", + "perfectmoney.is", + "wpbeginner.com", + "thepiratetrader.com", + "victoriassecret.com", + "images-amazon.com", + "tusfiles.net", + "chefkoch.de", + "ojooo.com", + "alphaporno.com", + "betfair.com", + "tympanus.net", + "td.com", + "4tube.com", + "phonearena.com", + "bankmandiri.co.id", + "cox.net", + "ozon.ru", + "perfectgirls.net", + "asana.com", + "sprint.com", + "berniaga.com", + "vente-privee.com", + "ing.nl", + "finn.no", + "atwiki.jp", + "toysrus.com", + "petflow.com", + "reverbnation.com", + "freeones.com", + "abc.go.com", + "bradesco.com.br", + "yenisafak.com.tr", + "profit-partner.ru", + "redfin.com", + "mangafox.me", + "appnexus.com", + "sxc.hu", + "speedanalysis.net", + "n-tv.de", + "qidian.com", + "teespring.com", + "break.com", + "nailedhard.com", + "jalan.net", + "authorize.net", + "adultadworld.com", + "sootoo.com", + "roboform.com", + "kotaku.com", + "multitran.ru", + "food.com", + "twitpic.com", + "pogo.com", + "sabq.org", + "priceminister.com", + "lexpress.fr", + "livetv.sx", + "nikkansports.com", + "securepaynet.net", + "vodoumedia.com", + "airbnb.com", + "trafficholder.com", + "rr.com", + "expireddomains.net", + "bandcamp.com", + "wayfair.com", + "tut.by", + "maybank2u.com.my", + "similarweb.com", + "zol.com.cn", + "247realmedia.com", + "asp.net", + "lonelyplanet.com", + "seopult.ru", + "whois.net", + "academia.edu", + "univision.com", + "interfax.ru", + "amarillasinternet.com", + "miercn.com", + "barclays.co.uk", + "societe.com", + "khabaronline.ir", + "unity3d.com", + "nyaa.se", + "euronews.com", + "verizon.net", + "rutube.ru", + "wetter.com", + "imlive.com", + "ggpht.com", + "docin.com", + "yify-torrents.com", + "yhd.com", + "citibank.co.in", + "xdating.com", + "roblox.com", + "voyages-sncf.com", + "189.cn", + "identi.li", + "bayt.com", + "medicinenet.com", + "wmaraci.com", + "stern.de", + "eluniversal.com", + "zoopla.co.uk", + "easyjet.com", + "dhl.com", + "freakshare.com", + "rojadirecta.me", + "sp.gov.br", + "friendfeed.com", + "beamtele.com", + "searchenginejournal.com", + "nocookie.net", + "reliancenetconnect.co.in", + "google.co.ma", + "fanpop.com", + "adk2.co", + "tagesschau.de", + "nate.com", + "ole.com.ar", + "index.hu", + "aruba.it", + "eltiempo.com", + "resellerclub.com", + "qip.ru", + "xossip.com", + "siteground.com", + "royalbank.com", + "advfn.com", + "tebyan.net", + "iciba.com", + "bigcartel.com", + "sciencedirect.com", + "hepsiburada.com", + "cpasbien.me", + "alfabank.ru", + "microsoftstore.com", + "discogs.com", + "onliner.by", + "socialmediaexaminer.com", + "seasonvar.ru", + "tripadvisor.in", + "360doc.com", + "chinabroadcast.cn", + "realestate.com.au", + "avira.com", + "herokuapp.com", + "downloadsetup.net", + "torcache.net", + "uefa.com", + "rg.ru", + "qianlong.com", + "hugedomains.com", + "arstechnica.com", + "fifa.com", + "ryanair.com", + "i.ua", + "axisbank.co.in", + "alimama.com", + "wanggou.com", + "radikal.com.tr", + "echoroukonline.com", + "unian.net", + "slashdot.org", + "dmv.org", + "mayoclinic.org", + "newsmax.com", + "google.com.et", + "icontact.com", + "vanguardngr.com", + "infojobs.net", + "say-move.org", + "ileehoo.com", + "mirtesen.ru", + "yam.com", + "e-hentai.org", + "wikimapia.org", + "shop-pro.jp", + "bukalapak.com", + "jcpenney.com", + "ilmeteo.it", + "iltasanomat.fi", + "telecomitalia.it", + "kariyer.net", + "shutterfly.com", + "santabanta.com", + "softlayer.com", + "complex.com", + "mamba.ru", + "netshoes.com.br", + "theweathernetwork.com", + "politico.com", + "advego.ru", + "eroprofile.com", + "mainichi.jp", + "nta.go.jp", + "sfimg.com", + "ap.org", + "webex.com", + "hinet.net", + "skyscrapercity.com", + "affili.net", + "adplxmd.com", + "iltalehti.fi", + "impress.co.jp", + "jezebel.com", + "extremetube.com", + "digitaltrends.com", + "one.com", + "ranker.com", + "qiwi.com", + "collegehumor.com", + "nbc.com", + "boursorama.com", + "watch32.com", + "meetcheap.com", + "forums.wordpress.com", + "cvs.com", + "kenh14.vn", + "yallakora.com", + "plurk.com", + "xtool.ru", + "airtel.com", + "wp.com", + "sftimes.co", + "economist.com", + "rapidshare.com", + "redbox.com", + "audible.com", + "kissmetrics.com", + "atpanel.com", + "wargaming.net", + "1337x.org", + "shahrekhabar.com", + "bdnews24.com", + "tvrain.ru", + "wykop.pl", + "mts.ru", + "nnm-club.me", + "schwab.com", + "elconfidencial.com", + "modelmayhem.com", + "zhidao.baidu.com/user/admin", + "aastocks.com", + "tiscali.it", + "qadabra.com", + "autoscout24.de", + "vatanim.com.tr", + "gotowebinar.com", + "edmunds.com", + "ebaumsworld.com", + "megafilmeshd.net", + "poste.it", + "mcssl.com", + "aljazeera.net", + "netvibes.com", + "sme.sk", + "tesco.com", + "525j.com.cn", + "tf1.fr", + "ccidnet.com", + "ldblog.jp", + "blockchain.info", + "played.to", + "nthwall.com", + "justanswer.com", + "clickbank.net", + "fnac.com", + "gmarket.co.kr", + "clipconverter.cc", + "nextmedia.com", + "bb.com.br", + "learntotradethemarket.com", + "pornoid.com", + "downloadha.com", + "hellporno.com", + "ooo-sex.com", + "blocket.se", + "tubeplus.me", + "seek.com.au", + "filestube.to", + "ceneo.pl", + "walmart.com.br", + "sonymobile.com", + "dyndns.org", + "ulmart.ru", + "vrbo.com", + "sport.pl", + "ashleymadison.com", + "el-wlid.com", + "boredpanda.com", + "udemy.com", + "mobogenie.com", + "tubegalore.com", + "theglobeandmail.com", + "bancobrasil.com.br", + "888.com", + "adreactor.com", + "zomato.com", + "depositfiles.com", + "moneysavingexpert.com", + "dofus.com", + "oanda.com", + "sport1.de", + "justhost.com", + "longhoo.net", + "otomoto.pl", + "techrepublic.com", + "westpac.com.au", + "dreamhost.com", + "ilsole24ore.com", + "clubic.com", + "gamme.com.tw", + "doctissimo.fr", + "isna.ir", + "s2d6.com", + "tdbank.com", + "zwaar.net", + "slando.ua", + "segodnya.ua", + "appannie.com", + "bartarinha.ir", + "egotastic.com", + "ed.gov", + "lg.com", + "120ask.com", + "hostmonster.com", + "super.cz", + "mercola.com", + "bayproxy.me", + "day.az", + "dummies.com", + "azet.sk", + "huffingtonpost.co.uk", + "okezone.com", + "medu.ir", + "lavanguardia.com", + "espreso.tv", + "nymag.com", + "autotrader.co.uk", + "socialmediatoday.com", + "wn.com", + "rtbpop.com", + "99designs.com", + "sanspo.com", + "proboards.com", + "virginmedia.com", + "99acres.com", + "blic.rs", + "pingomatic.com", + "prnewswire.com", + "sony.com", + "seobuilding.ru", + "www.gov.uk", + "jugem.jp", + "watchtower.com", + "seoprofiler.com", + "centrum.cz", + "techbrowsing.com", + "almasryalyoum.com", + "lolking.net", + "nipic.com", + "fanfiction.net", + "bwin.com", + "anyporn.com", + "sulit.com.ph", + "quickmeme.com", + "carview.co.jp", + "haizhangs.com", + "airtelforum.com", + "flirt4free.com", + "billboard.com", + "trademe.co.nz", + "rapgenius.com", + "pchouse.com.cn", + "beforeitsnews.com", + "peopleperhour.com", + "basecamphq.com", + "onlinedown.net", + "bet365.es", + "fucked-tube.com", + "santander.co.uk", + "speedbit.com", + "gi-akademie.com", + "google.com.pr", + "winupdatevideos.com", + "bizrate.com", + "xxxbunker.com", + "coursera.org", + "zoominfo.com", + "rarbg.com", + "ahram.org.eg", + "howtogeek.com", + "focus.cn", + "duden.de", + "mufg.jp", + "ex.ua", + "titan24.com", + "greatandhra.com", + "qqbaobao.com", + "rtve.es", + "yandex.net", + "yle.fi", + "panoramio.com", + "google.com.af", + "scol.com.cn", + "gamestop.com", + "xiami.com", + "webpagetest.org", + "creativecommons.org", + "archive.is", + "timesjobs.com", + "centurylink.com", + "wowhead.com", + "wordstream.com", + "lloydsbank.co.uk", + "incredibar.com", + "vagalume.com.br", + "installerapplicationusa.com", + "shinobi.jp", + "ruvr.ru", + "natwest.com", + "roulettebotplus.com", + "wildberries.ru", + "huaban.com", + "buenosearch.com", + "reg.ru", + "tempo.co", + "qvc.com", + "mangahere.com", + "tv.com", + "boc.cn", + "fnb.co.za", + "made-in-china.com", + "eleconomista.es", + "ubuntuforums.org", + "derstandard.at", + "xml-sitemaps.com", + "jang.com.pk", + "jiayuan.com", + "credit-agricole.fr", + "androidcentral.com", + "urbanspoon.com", + "pornoxo.com", + "regnum.ru", + "bedbathandbeyond.com", + "skladchik.com", + "daily.co.jp", + "n-mobile.net", + "suntrust.com", + "justjared.com", + "vid2c.com", + "gettyimages.com", + "tubecup.com", + "kinogo.net", + "similarsites.com", + "fling.com", + "vine.co", + "banglanews24.com", + "pudelek.pl", + "support.wordpress.com", + "cox.com", + "xkcd.com", + "adbooth.com", + "anz.com", + "adxcore.com", + "google.com.sv", + "indeed.co.in", + "scottrade.com", + "v9.com", + "isohunt.to", + "srclick.ru", + "gharreh.com", + "zaman.com.tr", + "monsterindia.com", + "delfi.lt", + "chewen.com", + "empowernetwork.com/QUbsgqDwpjjbkpOgwgOeaw==", + "state.tx.us", + "segundamano.es", + "utro.ru", + "sitescout.com", + "justclick.ru", + "wnd.com", + "cosmopolitan.com", + "local.com", + "anitube.se", + "sport.es", + "google.com.qa", + "ads-id.com", + "google.lv", + "capitalone360.com", + "hattrick.org", + "qualtrics.com", + "inagist.com", + "filgoal.com", + "incredibar-search.com", + "adslgate.com", + "directv.com", + "ilivid.com", + "warthunder.ru", + "jasmin.com", + "cyberciti.biz", + "msn.co.jp", + "canalblog.com", + "boerse.bz", + "tnaflix.com", + "apa.az", + "prchecker.info", + "reclameaqui.com.br", + "onlyworldnews.com", + "submarino.com.br", + "kuronekoyamato.co.jp", + "paipai.com", + "realitykings.com", + "deadspin.com", + "islamweb.net", + "no-ip.com", + "lostfilm.tv", + "wattpad.com", + "wav.tv", + "citi.com", + "buysellads.com", + "prezentacya.ru", + "forocoches.com", + "donga.com", + "drive2.ru", + "netsuite.com", + "privalia.com", + "bidorbuy.co.za", + "cmbchina.com", + "searchresultsguide.com", + "odatv.com", + "skrill.com", + "en.wordpress.com", + "dojki.com", + "unibet.com", + "hotpepper.jp", + "simplyhired.com", + "vesti.ua", + "ibtimes.co.uk", + "creativebloq.com", + "filmesonlinegratis.net", + "videohive.net", + "kmart.com", + "metacritic.com", + "rikunabi.com", + "zozo.jp", + "venturebeat.com", + "1and1.es", + "banesconline.com", + "internethaber.com", + "rae.es", + "planetsuzy.org", + "wpengine.com", + "kizi.com", + "armorgames.com", + "argos.co.uk", + "dtiblog.com", + "diigo.com", + "forever21.com", + "flashscore.com", + "vedomosti.ru", + "ccavenue.com", + "att.net", + "esporte.uol.com.br", + "empowernetwork.com/PXQ7Uz1Kyg+LR1APrG7pKQ==", + "to8to.com", + "hotwire.com", + "all.biz", + "info.com", + "xhamster.com/user/video", + "000webhost.com", + "worldoftanks.ru", + "dagbladet.no", + "computerbild.de", + "nasdaq.com", + "chitika.com", + "ipeen.com.tw", + "abola.pt", + "wiley.com", + "lpcloudsvr303.com", + "tinsao.net", + "mehrnews.com", + "hugesex.tv", + "christian-dogma.com", + "search.us.com", + "garmin.com", + "tgbus.com", + "iherb.com", + "torntv-tvv.org", + "chosun.com", + "kinozal.tv", + "berkeley.edu", + "techtarget.com", + "labanquepostale.fr", + "fitbit.com", + "folkd.com", + "theonion.com", + "idealista.com", + "oscar.go.com", + "jiji.com", + "anonym.to", + "allabout.co.jp", + "wufoo.com", + "rantlifestyle.com", + "google.com.bo", + "28.com", + "rocketnews24.com", + "hotspotshield.com", + "opentable.com", + "admngronline.com", + "naturalnews.com", + "zeroredirect2.com", + "ero-video.net", + "dinamalar.com", + "fl.ru", + "mtsindia.in", + "kapook.com", + "theage.com.au", + "allmyvideos.net", + "dinodirect.com", + "videodownloadconverter.com", + "tripleclicks.com", + "brassring.com", + "fapdu.com", + "cafepress.com", + "infospace.com", + "express.com.pk", + "fumu.com", + "infibeam.com", + "etrade.com", + "meneame.net", + "seroundtable.com", + "bigpoint.com", + "chinadaily.com.cn", + "myfonts.com", + "ezdownloadpro.info", + "codeplex.com", + "xl.pt", + "kaspersky.com", + "khabarfarsi.com", + "admitad.com", + "flirchi.com", + "dpstream.net", + "miniinthebox.com", + "samsclub.com", + "charter.net", + "techbang.com", + "tiu.ru", + "fanpage.it", + "spokeo.com", + "imageshack.com", + "ijreview.com", + "bc.vc", + "lazada.co.id", + "gofirstrow.eu", + "uptobox.com", + "newsnow.co.uk", + "symantec.com", + "editor.wix.com", + "avaxhome.cc", + "canalplus.fr", + "copyblogger.com", + "vetogate.com", + "yahoo-mbga.jp", + "manoramaonline.com", + "ah-me.com", + "elheddaf.com", + "bitcointalk.org", + "haqaik.com", + "bbandt.com", + "linguee.de", + "blogspot.nl", + "minus.com", + "amarujala.com", + "openclassrooms.com", + "bigmir.net", + "koramgame.com", + "commonfloor.com", + "gayromeo.com", + "cleartrip.com", + "kompasiana.com", + "tankionline.com", + "google.co.tz", + "springer.com", + "lifenews.ru", + "sify.com", + "tvoyauda4a.ru", + "dfiles.ru", + "technorati.com", + "myorderbox.com", + "fanatik.com.tr", + "activesearchresults.com", + "laposte.net", + "whirlpool.net.au", + "stockcharts.com", + "leadimpact.com", + "cardekho.com", + "ryushare.com", + "blog.jp", + "fastcompany.com", + "eurosport.fr", + "emirates.com", + "lb.ua", + "serverfault.com", + "forbes.ru", + "bfmtv.com", + "google.jo", + "17173.com", + "wikispaces.com", + "tokopedia.com", + "liberation.fr", + "a10.com", + "kdnet.net", + "variety.com", + "blog.me", + "homeaway.com", + "ameritrade.com", + "adult-empire.com", + "national.com.au", + "haraj.com.sa", + "timewarnercable.com", + "casino.com", + "sheknows.com", + "google.com.uy", + "abv.bg", + "liputan6.com", + "etxt.ru", + "bigstockphoto.com", + "strato.de", + "cashtasks.com", + "columbia.edu", + "zdf.de", + "pptv.com", + "cricbuzz.com", + "sharesuper.info", + "radaronline.com", + "pornolab.net", + "google.ba", + "opensubtitles.org", + "abclocal.go.com", + "ansa.it", + "pbskids.org", + "vz.ru", + "iconfinder.com", + "vitaminl.tv", + "ntv.ru", + "peliculasyonkis.com", + "foto-hd.com", + "paytm.com", + "rantsports.com", + "optimum.net", + "postbank.de", + "index.hr", + "smugmug.com", + "haivl.com", + "arbeitsagentur.de", + "dailycaller.com", + "freshbooks.com", + "genieo.com", + "alnaddy.com", + "britishairways.com", + "20min.ch", + "findthebest.com", + "autoblog.com", + "warez-bb.org", + "iza.ne.jp", + "filmaffinity.com", + "megogo.net", + "soccermanager.com", + "dawanda.com", + "parallels.com", + "clickjogos.uol.com.br", + "12306.cn", + "jstv.com", + "cliphunter.com", + "pixabay.com", + "milenio.com", + "keybinary.com", + "suumo.jp", + "mediatakeout.com", + "vodafone.in", + "cheapoair.com", + "seobook.com", + "videoyoum7.com", + "yr.no", + "gofundme.com", + "ixbt.com", + "starbucks.com", + "conferenceplus.com", + "media1first.com", + "sergey-mavrodi.com", + "rafflecopter.com", + "591.com.tw", + "mangapanda.com", + "huffingtonpost.fr", + "tiboo.cn", + "bancomer.com.mx", + "handelsblatt.com", + "hpylgr.com", + "psu.edu", + "bancomercantil.com", + "sport-express.ru", + "codepen.io", + "foundationapi.com", + "ow.ly", + "vatgia.com", + "kommersant.ru", + "srvsinf.com", + "telderi.ru", + "mercadolibre.com.co", + "hsbc.com.hk", + "t3n.de", + "magicbricks.com", + "shopstyle.com", + "mudah.my", + "punchng.com", + "therichest.com", + "fansshare.com", + "lepoint.fr", + "vipzona.info", + "globaltestmarket.com", + "jqueryui.com", + "bcsh.com", + "mundodeportivo.com", + "newyorker.com", + "funnyjunk.com", + "gongchang.com", + "a8.net", + "ecollege.com", + "newhdplugin.net", + "befuck.com", + "elespectador.com", + "drugs.com", + "abplive.in", + "feelcars.com", + "psychologytoday.com", + "instaforex.com", + "strtsrv.com", + "atpworldtour.com", + "incruit.com", + "starwoodhotels.com", + "zone-telechargement.com", + "songspk.name", + "hatenablog.com", + "enfemenino.com", + "msnbc.com", + "freebitco.in", + "akairan.com", + "discovery.com", + "angieslist.com", + "lesechos.fr", + "transfermarkt.de", + "sephora.com", + "come.in", + "dailykos.com", + "deutsche-bank.de", + "warriorplus.com", + "awwwards.com", + "lomadee.com", + "365jia.cn", + "lotterypost.com", + "egrana.com.br", + "rivals.com", + "literotica.com", + "myfreshnet.com", + "ihg.com", + "fazenda.gov.br", + "tomnod.com", + "filesharefanatic.com", + "hotukdeals.com", + "bitsnoop.com", + "tenpay.com", + "putlocker.bz", + "ziddu.com", + "ntvmsnbc.com", + "surveyzrewardcenter.com", + "aliyun.com", + "media.net", + "comdirect.de", + "yoox.com", + "hotelscombined.com", + "vgsgaming-ads.com", + "robokassa.ru", + "webdesignerdepot.com", + "rotoworld.com", + "jmw.com.cn", + "seloger.com", + "dsdomination.com", + "affiliatewindow.com", + "abs-cbnnews.com", + "bmo.com", + "iconarchive.com", + "funshion.com", + "admin5.com", + "playhe.com", + "bigcommerce.com", + "yodobashi.com", + "bigfishgames.com", + "onlinecreditcenter6.com", + "homes.co.jp", + "gameninja.com", + "ebates.com", + "banamex.com.mx", + "google.com.lb", + "2chblog.jp", + "entertainment-factory.com", + "asg.to", + "walla.co.il", + "mktmobi.com", + "opensooq.com", + "adam4adam.com", + "expressen.se", + "elephanttube.com", + "ingdirect.es", + "clicrbs.com.br", + "seriespepito.com", + "skyscanner.net", + "shine.com", + "appround.us", + "washingtontimes.com", + "nrk.no", + "1stwebdesigner.com", + "gucheng.com", + "ghanaweb.com", + "jzip.com", + "rozetka.com.ua", + "wiziwig.tv", + "jobsdb.com", + "kitco.com", + "rpp.com.pe", + "aljaras.com", + "telecinco.es", + "storify.com", + "netease.com", + "business-standard.com", + "babble.com", + "noticias.uol.com.br", + "alice.it", + "bizdec.ru", + "voanews.com", + "freejobalert.com", + "origo.hu", + "list-manage2.com", + "gavick.com", + "national-lottery.co.uk", + "gazzetta.gr", + "freevideodownloadforpc.com", + "xmarks.com", + "wikibooks.org", + "sexad.net", + "alc.co.jp", + "emol.com", + "careesma.in", + "cdc.gov", + "toshiba.com", + "imgchili.net", + "rbcroyalbank.com", + "indowebster.com", + "zynga.com", + "telekom.com", + "jiameng.com", + "adsmarket.com", + "utorrent.com", + "aarp.org", + "cityadspix.com", + "ekstrabladet.dk", + "doc88.com", + "fetlife.com", + "fh21.com.cn", + "cambridge.org", + "tune.pk", + "auction.co.kr", + "patheos.com", + "search-results.com", + "lds.org", + "webrankinfo.com", + "newsit.gr", + "techtudo.com.br", + "moviepilot.com", + "mercador.ro", + "nguoiduatin.vn", + "tripadvisor.it", + "lurkmore.to", + "europapress.es", + "uspto.gov", + "excelsior.com.mx", + "myvideo.de", + "jetblue.com", + "camfrog.com", + "blick.ch", + "batepapo.uol.com.br", + "manager.co.th", + "goibibo.com", + "cornell.edu", + "apetube.com", + "nation.com", + "allmusic.com", + "naughtyamerica.com", + "obozrevatel.com", + "telegrafi.com", + "neimanmarcus.com", + "yabancidiziizle1.com", + "globalewallet.com", + "beeline.ru", + "hinkhoj.com", + "sitemeter.com", + "almesryoon.com", + "localmoxie.com", + "grotal.com", + "knowyourmeme.com", + "qiwi.ru", + "yieldmanager.com", + "yimg.com", + "horoscopedays.com", + "gfy.com", + "mxtoolbox.com", + "searchgol.com", + "inquirer.net", + "hollywoodlife.com", + "ebrun.com", + "shopbop.com", + "mysmartprice.com", + "kijiji.it", + "visual.ly", + "mop.com", + "1fichier.com", + "prlog.org", + "xjtour.com", + "shorouknews.com", + "thegioinet.net", + "hln.be", + "enom.com", + "thaqafnafsak.com", + "wahuu.com", + "ganji.com", + "russianpost.ru", + "myplaycity.com", + "dzone.com", + "dns-shop.ru", + "aif.ru", + "ssisurveys.com", + "uploadboy.com", + "madpandatv.net", + "pcauto.com.cn", + "meinestadt.de", + "tasnimnews.com", + "rockettheme.com", + "imesh.com", + "home.pl", + "lindaikeji.blogspot.com", + "zamunda.net", + "with2.net", + "calameo.com", + "myonlinearcade.com", + "xcams.com", + "unicredit.it", + "clip2net.com", + "mediatemple.net", + "nosub.tv", + "click.in", + "alluc.to", + "thestar.com.my", + "niksalehi.com", + "lastampa.it", + "itv.com", + "hihi2.com", + "digitalocean.com", + "tripadvisor.fr", + "willhaben.at", + "laodong.com.vn", + "591hx.com", + "tineye.com", + "ptt.cc", + "beatport.com", + "infoseek.co.jp", + "avangate.com", + "indiaproperty.com", + "books.com.tw", + "nj.com", + "avaaz.org", + "wikitravel.org", + "direct.gov.uk", + "wangtu.com", + "discover.com", + "slon.ru", + "startpage.com", + "thestar.com", + "meishichina.com", + "finanzen.net", + "estadao.com.br", + "indeed.co.uk", + "expansion.com", + "irpopup.ir", + "arvixe.com", + "secureinternetbank.com", + "csmonitor.com", + "carwale.com", + "mtime.com", + "ceconline.com", + "abchina.com", + "cgbchina.com.cn", + "mediotiempo.com", + "vic.gov.au", + "cnbeta.com", + "alarab.net", + "vietnamnet.vn", + "gay.com", + "watchmygf.net", + "myanimelist.net", + "bannersbroker.com", + "asklaila.com", + "heureka.cz", + "grepolis.com", + "meteofrance.com", + "bol.com", + "hasoffers.com", + "bigrock.in", + "ibanking-services.com", + "nos.nl", + "google.com.gh", + "beppegrillo.it", + "nhaccuatui.com", + "hamariweb.com", + "wikiquote.org", + "evite.com", + "jobdiagnosis.com", + "17track.net", + "baixing.com", + "awempire.com", + "healthgrades.com", + "unifiedlayer.com", + "thisav.com", + "himado.in", + "zcool.com.cn", + "tsn.ca", + "atlassian.net", + "binary500.com", + "alotporn.com", + "dnaindia.com", + "sherdog.com", + "www.nhs.uk", + "gruposantander.es", + "quantcast.com", + "usearchmedia.com", + "merchantcircle.com", + "blog.hu", + "wallstcheatsheet.com", + "iptorrents.com", + "apsense.com", + "redirsvc.com", + "indiarailinfo.com", + "mobile.ir", + "xvideo-jp.com", + "boxofficemojo.com", + "scamadviser.com", + "rotapost.ru", + "someecards.com", + "mydealz.de", + "lvye.cn", + "videoweed.es", + "xlovecam.com", + "pepperjamnetwork.com", + "khabarpu.com", + "mathrubhumi.com", + "online-convert.com", + "b92.net", + "tiptopsoft.org", + "el-balad.com", + "qihoo.com", + "iheart.com", + "campaign-archive1.com", + "photodune.net", + "labnol.org", + "kotobank.jp", + "medium.com", + "google.lu", + "tm1111.com", + "gigazine.net", + "mypcbackup.com", + "instantcheckmate.com", + "seemorgh.com", + "topky.sk", + "puu.sh", + "aeriagames.com", + "whatismyip.com", + "rising.cn", + "scotiabank.com", + "wo.com.cn", + "yootheme.com", + "channel4.com", + "heavy-r.com", + "omgpm.com", + "milli.az", + "bnpparibas.net", + "vmware.com", + "diretta.it", + "skycn.com", + "sap.com", + "zyalt.livejournal.com", + "bookryanair.com", + "kkbox.com", + "subtitleseeker.com", + "abckj123.com", + "mashreghnews.ir", + "websitewelcome.com", + "2ch-c.net", + "health.com", + "uproxx.com", + "uast.ac.ir", + "bol.uol.com.br", + "feedsportal.com", + "ip-adress.com", + "sedty.com", + "ilsistemabinario.com", + "nabble.com", + "hightail.com", + "superjob.ru", + "tokyo.jp", + "hrblock.com", + "china.cn", + "tsetmc.com", + "unfranchise.com.tw", + "bhg.com", + "htmlbook.ru", + "stardoll.com", + "crictime.com", + "hsn.com", + "royalmail.com", + "globallshare.com", + "wikiwiki.jp", + "petardas.com", + "usembassy.gov", + "rakuten-bank.co.jp", + "golem.de", + "stepstone.de", + "aucfan.com", + "stubhub.com", + "rbcdaily.ru", + "ro2.biz", + "huffingtonpost.ca", + "timeout.com", + "digitalspy.co.uk", + "foreningssparbanken.se", + "autodesk.com", + "98ia.com", + "gtbank.com", + "runetki.com", + "freshdesk.com", + "ieee.org", + "getpocket.com", + "jutarnji.hr", + "caisse-epargne.fr", + "profitcentr.com", + "51.com", + "oi.com.br", + "sina.com.tw", + "fontspace.com", + "dynamicdrive.com", + "maduradas.com", + "ligtv.com.tr", + "sport24.gr", + "stargazete.com", + "bola.net", + "template-help.com", + "codecademy.com", + "officedepot.com", + "researchgate.net", + "17u.com", + "gfxtra.com", + "weiphone.com", + "shoutmeloud.com", + "hc360.com", + "lintas.me", + "mk.ru", + "wplocker.com", + "thumbtack.com", + "szn.cz", + "yatra.com", + "societegenerale.fr", + "wyborcza.pl", + "demotywatory.pl", + "thomann.de", + "imperiaonline.org", + "magento.com", + "skillpages.com", + "cam4ads.com", + "spiceworks.com", + "homestead.com", + "locanto.in", + "hotelurbano.com", + "sinaimg.cn", + "chetxia.com", + "menshealth.com", + "ideacellular.com", + "snopes.com", + "newgrounds.com", + "autosottocosto.com", + "admob.com", + "linguee.fr", + "net-a-porter.com", + "text.ru", + "sharebeast.com", + "televisionfanatic.com", + "netlog.com", + "lufthansa.com", + "ngoisao.net", + "3911.net", + "worldssl.net", + "rabobank.nl", + "lvmama.com", + "sharethis.com", + "sambaporno.com", + "mci.ir", + "inosmi.ru", + "joyreactor.cc", + "cartfill.in", + "cooltext.com", + "qz.com", + "onthe.io", + "citilink.ru", + "apartmenttherapy.com", + "lloydsbank.com", + "metroer.com", + "koyotesoft.com", + "subdivx.com", + "alriyadh.com", + "backlinkwatch.com", + "coindesk.com", + "porntube1.xxx", + "zovi.com", + "thestreet.com", + "rtbstream.com", + "etnet.com.hk", + "creditkarma.com", + "lifebuzz.com", + "opodo.co.uk", + "developpez.net", + "tinydeal.com", + "nature.com", + "oxforddictionaries.com", + "anchorfree.net", + "tn.com.ar", + "findicons.com", + "joomlart.com", + "hmrc.gov.uk", + "btc-e.com", + "extra.com.br", + "ad.nl", + "megafon.ru", + "qafqazinfo.az", + "shahvani.com", + "googlecode.com", + "milliyet.tv", + "deadline.com", + "grindtv.com", + "logitech.com", + "nzherald.co.nz", + "ninisite.com", + "webgains.com", + "oschina.net", + "webmasters.ru", + "filmix.net", + "seowhy.com", + "met-art.com", + "discart.ru", + "keywordblocks.com", + "vodly.to", + "redvak.com", + "tomshardware.co.uk", + "2shared.com", + "zoznam.sk", + "icims.com", + "virtapay.com", + "blomaga.jp", + "wasu.cn", + "carsensor.net", + "sportsdirect.com", + "uservoice.com", + "livescience.com", + "gamebase.com.tw", + "persiangig.com", + "livesmi.com", + "eharmony.com", + "banorte.com", + "radiojavan.com", + "startv.com.tr", + "coolmath-games.com", + "downloadquick.net", + "worldoftanks.eu", + "filelist.ro", + "fontsquirrel.com", + "superpages.com", + "yolasite.com", + "funnie.st", + "51cto.com", + "goo-net.com", + "builtwith.com", + "shaparak.ir", + "nuomi.com", + "omegle.com", + "poringa.net", + "lan.com", + "desi-tashan.com", + "shabdkosh.com", + "freedigitalphotos.net", + "betradar.com", + "womenshealthmag.com", + "realsimple.com", + "maxbounty.com", + "bbva.es", + "sweetpacks.com", + "pinoy-ako.info", + "jobstreet.com", + "cafe24.com", + "goldenbirds.biz", + "anandtech.com", + "mapion.co.jp", + "rawstory.com", + "streamate.com", + "celebuzz.com", + "freepornvs.com", + "divyabhaskar.co.in", + "lifehack.org", + "holidaycheck.de", + "quizlet.com", + "zhibo8.cc", + "askmefast.com", + "manageflitter.com", + "campaign-archive2.com", + "cityheaven.net", + "uniqlo.com", + "seoclerks.com", + "tecmundo.com.br", + "the-binary-options-guide.com", + "gigaom.com", + "mywot.com", + "playcast.ru", + "tatadocomo.com", + "csfd.cz", + "ganool.com", + "kaschpo.ru", + "webdunia.com", + "philly.com", + "doctoroz.com", + "jumei.com", + "poponclick.com", + "rueducommerce.fr", + "fararu.com", + "po.st", + "nextag.com", + "manhunt.net", + "sistrix.com", + "woman.ru", + "appbank.net", + "iobit.com", + "joins.com", + "vidtomp3.com", + "un.org", + "volusion.com", + "lipsum.com", + "afkarnews.ir", + "goldenline.pl", + "clickansave.net", + "softgozar.com", + "thefind.com", + "mozzi.com", + "wenxuecity.com", + "up2c.com", + "searchfunmoods.com", + "cabelas.com", + "coffetube.com", + "otto.de", + "webalta.ru", + "tataindicom.com", + "ctvnews.ca", + "inlinkz.com", + "kajabi.com", + "greatergood.com", + "whmcs.com", + "telekom.de", + "christianpost.com", + "fiducia.de", + "bancsabadell.com", + "pirateproxy.net", + "woobox.com", + "google.cm", + "noulinx.com", + "nick.com", + "moviehuts.com", + "mvideo.ru", + "couchtuner.eu", + "wyborcza.biz", + "tradeindia.com", + "girlsgogames.com", + "healthcare.gov", + "videolan.org", + "sky.it", + "tuniu.com", + "ime.nu", + "n4g.com", + "cyberforum.ru", + "justin.tv", + "createspace.com", + "joemonster.org", + "registro.br", + "elkhabar.com", + "open24news.tv", + "irr.ru", + "sanjesh.org", + "standardbank.co.za", + "neogaf.com", + "doyo.cn", + "am15.net", + "ana.co.jp", + "amd.com", + "gidonlinekino.com", + "sitetalk.com", + "nesn.com", + "arabseed.com", + "ingbank.pl", + "webpronews.com", + "saavn.com", + "yourdictionary.com", + "mobile9.com", + "jagranjosh.com", + "www.net.cn", + "computerbase.de", + "3djuegos.com", + "htc.com", + "the-binary-theorem.com", + "google.com.np", + "n11.com", + "vivastreet.com", + "mthai.com", + "seomastering.com", + "mercadolibre.cl", + "searchmetrics.com", + "mentalfloss.com", + "russia.tv", + "ubi.com", + "dwnews.com", + "ecplaza.net", + "trafficfactory.biz", + "loopnet.com", + "refinery29.com", + "minecraftforum.net", + "moviefone.com", + "bom.gov.au", + "google.com.bh", + "eltiempo.es", + "cuantarazon.com", + "2258.com", + "google.ee", + "nwolb.com", + "bezaat.com", + "eelly.com", + "icmwebserv.com", + "techsmith.com", + "timeweb.ru", + "johnlewis.com", + "independent.ie", + "bankia.es", + "dkb.de", + "gilt.com", + "duolingo.com", + "3file.info", + "cam4.de.com", + "pcanalysis.net", + "br.de", + "vodlocker.com", + "adfoc.us", + "ymlp.com", + "dailyfx.com", + "business2community.com", + "banki.ru", + "yell.com", + "smartresponder.ru", + "yaske.to", + "bokra.net", + "mercadolibre.com", + "viewster.com", + "recipesfinder.com", + "rei.com", + "dogpile.com", + "caf.fr", + "sbrf.ru", + "soha.vn", + "voila.fr", + "rp-online.de", + "members.webs.com", + "duvamis.com", + "nazwa.pl", + "google.com.ni", + "ilovemobi.com", + "retrogamer.com", + "blogtalkradio.com", + "utexas.edu", + "sonyentertainmentnetwork.com", + "dr.dk", + "jjwxc.net", + "service-public.fr", + "care2.com", + "musavat.com", + "h12-media.com", + "smartfren.com", + "immowelt.de", + "discuz.net", + "m1905.com", + "cas.sk", + "bitcoinwisdom.com", + "monografias.com", + "mindbodygreen.com", + "getfirebug.com", + "godlikeproductions.com", + "mydrivers.com", + "voc.com.cn", + "truecaller.com", + "livemook.com", + "i8ti.com", + "agame.com", + "kurir-info.rs", + "urbanoutfitters.com", + "shopping.uol.com.br", + "2ip.ru", + "91.com", + "banquepopulaire.fr", + "rappler.com", + "mint.com", + "jz123.cn", + "zurb.com", + "sport.cz", + "justunfollow.com", + "billionuploads.com", + "thaivisa.com", + "seitwert.de", + "ciudad.com.ar", + "archdaily.com", + "jalopnik.com", + "1news.az", + "dailyfinance.com", + "okitspace.com", + "dn.se", + "el-nacional.com", + "larousse.fr", + "mojo-themes.com", + "draugiem.lv", + "teamtreehouse.com", + "xiu.com", + "ennaharonline.com", + "oricon.co.jp", + "openoffice.org", + "tny.cz", + "forumfree.it", + "youthwant.com.tw", + "khamsat.com", + "vitacost.com", + "alrakoba.net", + "marmiton.org", + "yebhi.com", + "the-binary-trader.biz", + "dominos.com", + "dice.com", + "like4like.org", + "tvmuse.com", + "standardchartered.com", + "bitbucket.org", + "driveropti.net", + "google.hn", + "indiblogger.in", + "torrenthound.com", + "wolframalpha.com", + "experienceproject.com", + "nsw.gov.au", + "olx.pt", + "sciencedaily.com", + "viglink.com", + "plala.or.jp", + "sxsw.com", + "mileroticos.com", + "namejet.com", + "1hai.cn", + "skroutz.gr", + "bilibili.tv", + "travelzoo.com", + "saksfifthavenue.com", + "get-a-fuck-tonight.com", + "barclaycardus.com", + "withgoogle.com", + "whitehouse.gov", + "oprah.com", + "fishwrapper.com", + "softbank.jp", + "slutload.com", + "latercera.cl", + "100bestbuy.com", + "nur.kz", + "topit.me", + "computerhope.com", + "snob.ru", + "rackcdn.com", + "3158.cn", + "artlebedev.ru", + "yobt.com", + "rai.it", + "hs.fi", + "tinhte.vn", + "fotocasa.es", + "nyu.edu", + "dawn.com", + "series.ly", + "movshare.net", + "fineartamerica.com", + "westernunion.com", + "leaseweb.com", + "wayn.com", + "ivi.ru", + "bloomingdales.com", + "pons.com", + "rapidtrk.com", + "bama.ir", + "hackforums.net", + "dfiles.eu", + "intercambiosvirtuales.org", + "sdo.com", + "homes.com", + "colissimo.fr", + "discuss.com.hk", + "afreeca.com", + "rbc.ua", + "novamov.com", + "jungle.gr", + "iol.co.za", + "btcclicks.com", + "mapsofindia.com", + "propellerads.com", + "fab.com", + "princeton.edu", + "reliancebroadband.co.in", + "xdf.cn", + "mako.co.il", + "protothema.gr", + "booloo.com", + "livrariasaraiva.com.br", + "efukt.com", + "acrobat.com", + "globovision.com", + "xero.com", + "sevenforums.com", + "semalt.com", + "audiojungle.net", + "e1.ru", + "quoka.de", + "chinaiiss.com", + "gonzoxxxmovies.com", + "wallpaperswide.com", + "fapvid.com", + "trk4.com", + "forosdelweb.com", + "fark.com", + "my.tv.sohu.com/user/video", + "kaixin001.com", + "ntvspor.net", + "mobafire.com", + "zigwheels.com", + "filmifullizle.com", + "8tracks.com", + "readmanga.eu", + "lelong.com.my", + "planetromeo.com", + "192.com", + "republika.co.id", + "in.gr", + "televisao.uol.com.br", + "kalerkantho.com", + "usairways.com", + "malaysiakini.com", + "anonymouse.org", + "xxxhost.me", + "stuff.co.nz", + "dek-d.com", + "bbvanet.com.mx", + "watchseries-online.eu", + "malaysiaairlines.com", + "policymic.com", + "serviporno.com", + "aprod.hu", + "gelocal.it", + "tradetracker.com", + "cibc.com", + "umich.edu", + "smartshe.com", + "sony.jp", + "taobaocdn.com", + "bestgfx.biz", + "irib.ir", + "aliimg.com", + "esuteru.com", + "healthline.com", + "cnnamador.com", + "sat.gob.mx", + "childsafedownloadx.asia", + "playmillion.com", + "strawberrynet.com", + "alnilin.com", + "trustedreviews.com", + "21cn.com", + "persianv.com", + "baomoi.com", + "firestorage.jp", + "google.co.ke", + "admaimai.com", + "sbnlife.com", + "surveyrouter.com", + "megaindex.ru", + "intodns.com", + "yiqifa.com", + "mixcloud.com", + "softonic.fr", + "bakecaincontrii.com", + "payu.in", + "rakuten-card.co.jp", + "almaany.com", + "grantland.com", + "ricardo.ch", + "heavy.com", + "niusnews.com", + "canstockphoto.com", + "acunn.com", + "diythemes.com", + "canalrcnmsn.com", + "ngs.ru", + "rassd.com", + "groupon.co.in", + "depositfiles.org", + "mango.com", + "theregister.co.uk", + "worldfree4u.com", + "f-lite.ru", + "boingboing.net", + "bangbros.com", + "neteller.com", + "tonicmovies.com", + "p2up.ir", + "cartoonnetwork.com", + "lun.com", + "nk.pl", + "webry.info", + "burningcamel.com", + "mplife.com", + "nationalreview.com", + "williamhill.es", + "sssc.cn", + "col3negoriginal.lk", + "yoast.com", + "svyaznoy.ru", + "chengdu.cn", + "listverse.com", + "lastminute.com", + "guardianlv.com", + "39yst.com", + "bahseazad.ir", + "debian.org", + "tokyo-porn-tube.com", + "ilbe.com", + "upsocl.com", + "balatarin.com", + "charter97.org", + "wpexplorer.com", + "immi.gov.au", + "yokamen.cn", + "esmas.com", + "hbr.org", + "livechatinc.com", + "crazyegg.com", + "palcomp3.com", + "calottery.com", + "wistia.com", + "locaweb.com.br", + "techspot.com", + "bit.ly", + "yammer.com", + "uscis.gov", + "bramjnet.com", + "linio.com.mx", + "tharunaya.co.uk", + "desirulez.net", + "madmimi.com", + "everydayhealth.com", + "secondlife.com", + "ceskatelevize.cz", + "enter.ru", + "glopart.ru", + "woso.cn", + "pitlap.info", + "worldoftanks.com", + "videarn.com", + "dafiti.com.br", + "segundamano.mx", + "eamroomsnacks.com", + "vulture.com", + "humblebundle.com", + "rockstargames.com", + "wsodownloads.info", + "yazete.com", + "pcadvisor.co.uk", + "07073.com", + "modcloth.com", + "attracta.com", + "iol.pt", + "travideos.com", + "sparkpeople.com", + "rankingsandreviews.com", + "entekhab.ir", + "goarticles.com", + "wonderhowto.com", + "kankanews.com", + "rzb.ir", + "p30download.com", + "infojobs.it", + "socialspark.com", + "picstopin.com", + "celebritynetworth.com", + "tweakers.net", + "gi-backoffice.com", + "isbank.com.tr", + "qiyou.com", + "templatic.com", + "cyberpresse.ca", + "consumerreports.org", + "focalprice.com", + "linkpad.ru", + "svd.se", + "desitvforum.net", + "bulbagarden.net", + "showup.tv", + "webgozar.com", + "3dnews.ru", + "spyfu.com", + "bikhir.ma", + "trovaprezzi.it", + "autov.com.cn", + "podnapisi.net", + "tocmai.ro", + "realitatea.net", + "geico.com", + "joydownload.com", + "kddi.com", + "unbounce.com", + "meb.gov.tr", + "anchorfree.us", + "gumtree.pl", + "dbs.com", + "ebayimg.com", + "ptcsolution.com", + "clip.dj", + "samplicio.us", + "47news.jp", + "todo1.com", + "anspress.com", + "24sata.hr", + "cineblog01.tv", + "sport-fm.gr", + "unetenet.com", + "tenki.jp", + "jia.com", + "filefactory.com", + "onedio.com", + "gendai.net", + "nickjr.com", + "finviz.com", + "bytes.com", + "inkthemes.com", + "oriflame.com", + "aporrea.org", + "mazika2day.com", + "baimao.com", + "logme.in", + "downloadactivate.com", + "vista.ir", + "duowan.com", + "sinaapp.com", + "proceso.com.mx", + "xxxconnect.com", + "dvdvideosoft.com", + "nomorerack.com", + "almubasher.com.sa", + "afisha.ru", + "clicksure.com", + "11st.co.kr", + "turkishairlines.com", + "freekaamaal.com", + "findwide.com", + "sbisec.co.jp", + "mythemeshop.com", + "webnode.com", + "cumhuriyet.com.tr", + "morningstar.com", + "trafficshop.com", + "gopro.com", + "jeevansathi.com", + "dasoertliche.de", + "markets.com", + "absa.co.za", + "ikman.lk", + "fmworld.net", + "hyatt.com", + "virgin-atlantic.com", + "tfl.gov.uk", + "immonet.de", + "haodf.com", + "thewrap.com", + "appgame.com", + "hdwallpapers.in", + "canon.com", + "newsle.com", + "jorudan.co.jp", + "pcwelt.de", + "coinbase.com", + "yourgirlfriends.com", + "djmaza.info", + "findagrave.com", + "lainformacion.com", + "teacup.com", + "ezilon.com", + "redbubble.com", + "guru.com", + "brothersoft.com", + "coinmarketcap.com", + "mediabistro.com", + "elfagr.org", + "glavcom.ua", + "jino.ru", + "1e100.net", + "g9g.com", + "join.me", + "ynet.com", + "news247.gr", + "rcom.co.in", + "baby.ru", + "argentglobalnetwork.com", + "hsselite.com", + "foxbusiness.com", + "cookmates.com", + "lidl.de", + "timesofindia.com", + "fakt.pl", + "bgr.com", + "ghatreh.com", + "woxikon.de", + "pontofrio.com.br", + "gizmag.com", + "signup.wordpress.com", + "tre.it", + "gardenweb.com", + "alfalfalfa.com", + "prcm.jp", + "freemail.hu", + "iteye.com", + "katproxy.com", + "kickassunblock.info", + "wisegeek.com", + "zaobao.com", + "vpsdomain4.eu", + "naij.com", + "olx.co.za", + "mihanstore.org", + "gezinti.com", + "vistaprint.in", + "dastelefonbuch.de", + "blackhatteam.com", + "yummly.com", + "minecraft.net", + "justcloud.com", + "biblehub.com", + "argentinawarez.com", + "gandul.info", + "trialpay.com", + "paruvendu.fr", + "khanacademy.org", + "topsy.com", + "angelfire.com", + "nseindia.com", + "seositecheckup.com", + "hostelworld.com", + "zalukaj.tv", + "animeflv.net", + "laptopmag.com", + "tripadvisor.de", + "sec.gov", + "tripadvisor.es", + "oreilly.com", + "kanald.com.tr", + "halifax-online.co.uk", + "extremetracking.com", + "reimageplus.com", + "jal.co.jp", + "vuiviet.net", + "yeslibertin.com", + "pichunter.com", + "empowernetwork.com/aI3zkH7s3g6tZH8TDmh8LA==", + "reforma.com", + "singlessalad.com", + "upenn.edu", + "1ting.com", + "hsbc.com.mx", + "sulia.com", + "yepporn.com", + "scrubtheweb.com", + "e-autopay.com", + "rechargeitnow.com", + "ppomppu.co.kr", + "zemtv.com", + "gofeminin.de", + "peoplestylewatch.com", + "france24.com", + "comodo.com", + "openmace.net", + "maultalk.com", + "fancy.com", + "vodafone.it", + "pokerstrategy.com", + "crunchyroll.com", + "aipai.com", + "3dcartstores.com", + "leancy.com", + "myblogguest.com", + "papajohns.com", + "tuolar.com", + "junkmail.co.za", + "nyc.gov", + "rentalcars.com", + "sovsport.ru", + "marketo.com", + "smbc.co.jp", + "yale.edu", + "pitchfork.com", + "thetoptens.com", + "joyme.com", + "linuxquestions.org", + "htmlgoodies.com", + "laredoute.fr", + "0427d7.se", + "journaldesfemmes.com", + "xici.net", + "alwafd.org", + "daniweb.com", + "mtgox.com", + "jetstar.com", + "plotek.pl", + "jobvite.com", + "adage.com", + "aukro.cz", + "ford.com", + "problogger.net", + "518.com.tw", + "blogbigtime.com", + "nieuwsblad.be", + "register.com", + "24tv.ua", + "aftenposten.no", + "weightwatchers.com", + "smartpassiveincome.com", + "radiosvoboda.org", + "web.com", + "caixin.com", + "digitalmarketer.com", + "soup.io", + "soft98.ir", + "muzofon.com", + "gao7.com", + "freefilmshd.com", + "hsbc.com", + "xhamsterhq.com", + "my.tv.sohu.com/user/reg", + "trade65.com", + "chow.com", + "google.com.kh", + "inps.it", + "seesaa.jp", + "hoovers.com", + "peru21.pe", + "sdpnoticias.com", + "schema.org", + "linode.com", + "theknot.com", + "blesk.cz", + "google.ci", + "loltrk.com", + "vod.pl", + "geenstijl.nl", + "moneymakergroup.com", + "tupaki.com", + "huvrtech.com", + "metafilter.com", + "jumia.com.ng", + "newsvine.com", + "mylant.com", + "accenture.com", + "feedjit.com", + "twipple.jp", + "getgoodlinks.ru", + "radiofarda.com", + "informador.com.mx", + "wisc.edu", + "windows.net", + "zamzar.com", + "nationalpost.com", + "epnet.com", + "naaptol.com", + "x-art.com", + "ebookbrowsee.net", + "marketingland.com", + "thedailyshow.com", + "s-oman.net", + "theme-fusion.com", + "immobiliare.it", + "allvoices.com", + "adroll.com", + "francetvinfo.fr", + "callandput.com", + "entireweb.com", + "instructure.com", + "megacinema.fr", + "djelfa.info", + "flightaware.com", + "magazineluiza.com.br", + "ighome.com", + "mature-beauty.com", + "yuku.com", + "peerfly.com", + "hardware.fr", + "dcinside.com", + "telerik.com", + "pengpeng.com", + "gazetaexpress.com", + "publico.es", + "nwsource.com", + "oneandone.co.uk", + "qoo10.sg", + "aboutus.org", + "adsupplyads.com", + "wa.gov", + "google.co.cr", + "benchmarkemail.com", + "valuedealshopper.com", + "hola.com", + "mizuhobank.co.jp", + "zzstream.li", + "kriesi.at", + "exist.ru", + "nvidia.com", + "giallozafferano.it", + "jusbrasil.com.br", + "konga.com", + "avclub.com", + "reed.co.uk", + "advertig.com", + "ripoffreport.com", + "gooddrama.net", + "pornyaz.com", + "budsgunshop.com", + "78.cn", + "livesurf.ru", + "99114.com", + "pornorama.com", + "marksandspencer.com", + "vmall.com", + "fatcow.com", + "mylife.com", + "irishtimes.com", + "medicalnewstoday.com", + "uiuc.edu", + "groupon.it", + "washington.edu", + "addictinggames.com", + "anonymz.com", + "kindgirls.com", + "jne.co.id", + "guiamais.com.br", + "lyricsfreak.com", + "uloz.to", + "tagesanzeiger.ch", + "fashionandyou.com", + "bollywoodhungama.com", + "nationwide.co.uk", + "kboing.com.br", + "python.org", + "theladbible.com", + "eurosport.com", + "webinarjam.com", + "theme.wordpress.com", + "myfxbook.com", + "allwomenstalk.com", + "ucla.edu", + "opendns.com", + "pornbb.org", + "money.pl", + "domain.com.au", + "standaard.be", + "wanelo.com", + "express.co.uk", + "exam8.com", + "zum.com", + "amtrak.com", + "boots.com", + "800notes.com", + "fabfurnish.com", + "amoureux.com", + "ebizmba.com", + "ujian.cc", + "stargames.at", + "panasonic.com", + "ge.tt", + "scout.com", + "tiexue.net", + "nanapi.jp", + "eclipse.org", + "searchquotes.com", + "wetplace.com", + "lpcloudbox300.com", + "hollyscoop.com", + "bdjobs.com", + "larepublica.pe", + "hizliresim.com", + "yepme.com", + "bitcoincharts.com", + "bsnl.co.in", + "ucwan87.net", + "tianji.com", + "hgtv.com", + "cleanfiles.net", + "moonsy.com", + "tomsguide.com", + "datpiff.com", + "net.hr", + "orange.pl", + "bankrate.com.cn", + "mafiashare.net", + "giaoduc.net.vn", + "sucuri.net", + "ringcentral.com", + "gamingwonderland.com", + "freenet.de", + "cian.ru", + "gayboystube.com", + "gog.com", + "gruenderszene.de", + "econsultancy.com", + "aktuality.sk", + "webrankstats.com", + "j-cast.com", + "popsugar.com", + "mr-guangdong.com", + "on.cc", + "jb51.net", + "lamoda.ru", + "sprashivai.ru", + "animoto.com", + "winzip.com", + "vanityfair.com", + "svt.se", + "noyapps.com", + "mnn.com", + "lancenet.com.br", + "e-monsite.com", + "gazeta.ua", + "cari.com.my", + "coub.com", + "veoh.com", + "kienthuc.net.vn", + "eldorado.ru", + "overclock.net", + "meetic.fr", + "tam.com.br", + "alfemminile.com", + "telmex.com", + "newsbomb.gr", + "funda.nl", + "netcoc.com", + "bhinneka.com", + "layalina.com", + "clubpenguin.com", + "talktalk.co.uk", + "dumpert.nl", + "almastba.com", + "clker.com", + "adweek.com", + "gigya.com", + "disponivel.uol.com.br", + "tomoson.com", + "w.org", + "rfi.fr", + "imgbox.com", + "omniture.com", + "n24.de", + "webdesignrazzi.com", + "medscape.com", + "playboy.com", + "usda.gov", + "cuevana.tv", + "trenitalia.com", + "explosm.net", + "mail.uol.com.br", + "delfi.lv", + "elektroda.pl", + "hulkshare.com", + "kotak.com", + "gi-akademie.ning.com", + "yelp.de", + "lrytas.lt", + "couchsurfing.org", + "drugstore.com", + "ssa.gov", + "seriescoco.com", + "iran-tejarat.com", + "hafiz.gov.sa", + "intellicast.com", + "sub.jp", + "paginasamarillas.es", + "anime44.com", + "babyschool.com.cn", + "advertisernets.com", + "vpsdomain2.eu", + "arcor.de", + "video2mp3.net", + "marthastewart.com", + "hawahome.com", + "articlesbase.com", + "spanishdict.com", + "meilishuo.com", + "grammarly.com", + "sportlemon.tv", + "zend.com", + "bigideamastermind.com", + "hawaaworld.com", + "pagseguro.uol.com.br", + "keywordspy.com", + "7search.com", + "cnki.net", + "aamaadmiparty.org", + "javaplayer.info", + "sidereel.com", + "azcentral.com", + "passportindia.gov.in", + "memurlar.net", + "bouyguestelecom.fr", + "buenastareas.com", + "ernmoneynow.com", + "eurosport.ru", + "blog.163.com", + "pizzahut.com", + "bossip.com", + "webdeveloper.com", + "add-anime.net", + "pimptubed.com", + "movistar.es", + "megashare.sh", + "empireavenue.com", + "btemplates.com", + "amkspor.com", + "payza.com", + "farfesh.com", + "mcgraw-hill.com", + "motor-talk.de", + "purepeople.com", + "i-gamer.net", + "sotmarket.ru", + "anthropologie.com", + "google.org", + "rai.tv", + "wikidot.com", + "canoe.ca", + "orange.es", + "umn.edu", + "emag.ro", + "webutations.org", + "ykb.com", + "canadapost.ca", + "zhubajie.com", + "nextcar.cn", + "next.co.uk", + "freemovie-hd.com", + "telegraf.com.ua", + "ajc.com", + "xsrv.jp", + "vanguard.com", + "mybroadband.co.za", + "999120.net", + "panet.co.il", + "bellemaison.jp", + "fonearena.com", + "playvid.com", + "purdue.edu", + "thesuperficial.com", + "114so.cn", + "irna.ir", + "websitetonight.com", + "edublogs.org", + "provincial.com", + "jahaniha.com", + "heroturko.me", + "dynadot.com", + "txtsrving.info", + "adclickxpress.com", + "sammobile.com", + "ebay.at", + "yaplog.jp", + "imtranslator.net", + "fotor.com", + "myvidster.com", + "williamhill.it", + "argaam.com", + "zoomby.ru", + "tcsbank.ru", + "origin.com", + "vov.vn", + "wrapbootstrap.com", + "imvu.com", + "fbsbx.com", + "elcorteingles.es", + "cerdas.com", + "google.com.om", + "dramasonline.com", + "businesswire.com", + "serpbook.com", + "santanderrio.com.ar", + "meta.ua", + "kenhgioitre.com", + "watchcartoononline.com", + "athome.co.jp", + "2checkout.com", + "moheet.com", + "jeu.info", + "ankieta-online.pl", + "triberr.com", + "wordtracker.com", + "blogher.com", + "colourlovers.com", + "maalaimalar.com", + "bakeca.it", + "thepostgame.com", + "beget.ru", + "abebooks.com", + "certified-toolbar.com", + "publico.pt", + "footmercato.net", + "indiabix.com", + "zakzak.co.jp", + "hotfile.com", + "virustotal.com", + "usgs.gov", + "tukif.com", + "banglamail24.com", + "graphicstock.com", + "svpressa.ru", + "sanalpazar.com", + "logsoku.com", + "loteriasyapuestas.es", + "youjizzlive.com", + "all-inkl.com", + "arcot.com", + "blogspot.fi", + "inbox.lv", + "anno-online.com", + "tikona.in", + "newsru.ua", + "centerblog.net", + "jahannews.com", + "joxi.ru", + "proxybay.eu", + "2gis.ru", + "indeed.fr", + "novayagazeta.ru", + "intelius.com", + "hosteurope.de", + "autoscout24.it", + "postjoint.com", + "pulsk.com", + "biobiochile.cl", + "homevv.com", + "zapbux.com", + "gsp.ro", + "priyo.com", + "jamnews.ir", + "addtoany.com", + "lookbook.nu", + "e-travel.com", + "themelock.com", + "bestsocialfeed.com", + "eqla3.com", + "klm.com", + "realtor.ca", + "tv2.no", + "jqueryrain.com", + "seo-fast.ru", + "academic.ru", + "clicksia.com", + "lpcloudbox30.com", + "worldpay.com", + "gogoanime.com", + "photoshelter.com", + "mlive.com", + "wetteronline.de", + "jkforum.net", + "secure.ipage.com", + "antena3.com", + "elsevier.com", + "thaiseoboard.com", + "addic7ed.com", + "rookee.ru", + "jstor.org", + "rajanews.com", + "uline.com", + "o2online.de", + "fbdownloader.com", + "wetpaint.com", + "nnm.me", + "50onred.com", + "tvtropes.org", + "philips.com", + "watchseries.to", + "perfectinter.net", + "progressive.com", + "stcn.com", + "golsearch.com", + "sarayanews.com", + "screenrant.com", + "macworld.com", + "classifiedads.com", + "ip138.com", + "izlesene.com", + "ovguide.com", + "gametop.com", + "urlm.co", + "isitdownrightnow.com", + "redhat.com", + "stafaband.info", + "biglion.ru", + "twitterfeed.com", + "ttnet.com.tr", + "panasonic.jp", + "loc.gov", + "telstra.com.au", + "jiathis.com", + "perfil.com.ar", + "inquisitr.com", + "ratp.fr", + "libertaddigital.com", + "bni.co.id", + "ycombinator.com", + "paginegialle.it", + "momoshop.com.tw", + "theweek.com", + "tuoitre.vn", + "scriptmafia.org", + "megavod.fr", + "indiavisiontv.com", + "history.com", + "flyeralarm.com", + "salespider.com", + "ard.de", + "dslreports.com", + "tokyo-tube.com", + "brazzersnetwork.com", + "bonanza.com", + "storenvy.com", + "37signals.com", + "jxnews.com.cn", + "1sale.com", + "allanalpass.com", + "yiiframework.com", + "kimsufi.com", + "nastygal.com", + "immoral.jp", + "icq.com", + "loxblog.com", + "jcrew.com", + "diamond.jp", + "nissen.co.jp", + "fxstreet.com", + "15min.lt", + "lifo.gr", + "alef.ir", + "iis.net", + "giphy.com", + "mypopup.ir", + "bsnl.in", + "distractify.com", + "foro20.com", + "chachaba.com", + "fx-trend.com", + "imanhua.com", + "playxn.com", + "gocomics.com", + "lcl.fr", + "hsw.cn", + "ellislab.com", + "persiantools.com", + "coupondunia.in", + "fengniao.com", + "coinmill.com", + "freelancer.in", + "blogdetik.com", + "data.com", + "trafficjunky.net", + "neverblue.com", + "flvto.com", + "lijit.com", + "zaycev.net", + "salamnews.org", + "siliconrus.com", + "dv37.com", + "conrad.de", + "ascii.jp", + "yesfreeporn.com", + "globalpost.com", + "d4000.com", + "dobreprogramy.pl", + "hammihan.com", + "wjunction.com", + "liverpoolfc.tv", + "nordea.fi", + "mail2web.com", + "songkick.com", + "comicbookresources.com", + "jwplayer.com", + "moi.gov.sa", + "iranecar.com", + "kayako.com", + "listal.com", + "technet.com", + "infonews.com", + "fun698.com", + "frys.com", + "vectorstock.com", + "vbox7.com", + "leroymerlin.fr", + "gpotato.eu", + "gulfnews.com", + "hot-sex-tube.com", + "buzzle.com", + "uludagsozluk.com", + "onlinefastpaydayloan.com", + "forumcommunity.net", + "potins.net", + "bwin.es", + "lobstertube.com", + "katestube.com", + "endomondo.com", + "navalny.livejournal.com", + "wayport.net", + "dealspl.us", + "pixhost.org", + "webopedia.com", + "flagcounter.com", + "hotnewhiphop.com", + "matomy.com", + "privat24.ua", + "fuq.com", + "readserver.net", + "haiwainet.cn", + "light-dark.net", + "decolar.com", + "tlbb8.com", + "tomtom.com", + "fontanka.ru", + "tchibo.de", + "slutfinder.com", + "rbc.cn", + "pepperfry.com", + "freshersworld.com", + "mob.org", + "brobible.com", + "phim3s.net", + "parsiblog.com", + "link-assistant.com", + "smotri.com", + "flyertalk.com", + "iefimerida.gr", + "pornyeah.com", + "trustpilot.com", + "wowslider.com", + "sekindo.com", + "shape.com", + "pizap.com", + "bluedart.com", + "cmu.edu", + "shueisha.co.jp", + "royal-search.com", + "123greetings.com", + "albawabhnews.com", + "holiday-weather.com", + "4travel.jp", + "lpcloudsvr203.com", + "panorama.com.ve", + "htcmania.com", + "skorer.tv", + "mymovies.it", + "fileice.net", + "1and1.fr", + "legiaodosherois.com.br", + "tomsguide.fr", + "baymirror.com", + "thesun.co.uk", + "huffingtonpost.es", + "61baobao.com", + "juegos.com", + "joyclub.de", + "qatarairways.com", + "ayosdito.ph", + "translate.ru", + "yithemes.com", + "buyvip.com", + "pearltrees.com", + "thinkprogress.org", + "twitcasting.tv", + "lusongsong.com", + "irecommend.ru", + "aukro.ua", + "eldiario.es", + "pop-music.ir", + "libertytimes.com.tw", + "themalaysianinsider.com", + "paddypower.it", + "delfi.ee", + "appthemes.com", + "tdameritrade.com", + "flashback.org", + "tubelib.com", + "claro.com.br", + "jotform.com", + "bigresource.com", + "libsyn.com", + "sendgrid.com", + "silikonvadisi.tv", + "geo.tv", + "pricegrabber.com", + "futhead.com", + "testberichte.de", + "skybet.com", + "wpcentral.com", + "lesnumeriques.com", + "clientoo.com", + "mathsisfun.com", + "expert.ru", + "maxptp.com", + "mediaite.com", + "regions.com", + "fast-torrent.ru", + "enlacespepito.com", + "redtram.com", + "benesse.ne.jp", + "jappy.de", + "wizzair.com", + "adevarul.ro", + "mhlw.go.jp", + "dev-point.com", + "ibtimes.co.in", + "coinwarz.com", + "slidesharecdn.com", + "classmates.com", + "tsite.jp", + "niazerooz.com", + "jma.go.jp", + "motherjones.com", + "easports.com", + "elbotola.com", + "tripadvisor.com.au", + "rozee.pk", + "vivanuncios.com.mx", + "lashou.com", + "theaustralian.com.au", + "superstarmagazine.com", + "zedge.net", + "hardwarezone.com.sg", + "yyets.com", + "nowgamez.com", + "blogspot.ch", + "gq.com", + "adidas.com", + "uyan.cc", + "dailydot.com", + "xenforo.com", + "ilmessaggero.it", + "state.nj.us", + "16888.com", + "searchengines.guru", + "nascar.com", + "dnb.no", + "smosh.com", + "shinyinnovation.com", + "who.int", + "mybet.com", + "yoo7.com", + "xrea.com", + "fotolog.net", + "smartprix.com", + "esquire.com", + "google.com.cy", + "santander.com.br", + "realclearpolitics.com", + "financereports24.com", + "onlinesoccermanager.com", + "noticiaaldia.com", + "kelkoo.com", + "drakulastream.eu", + "vodafone.de", + "bt.dk", + "greenwichmeantime.com", + "ufc.com", + "webstatsdomain.org", + "taoche.com", + "thinkgeek.com", + "caranddriver.com", + "123-reg.co.uk", + "translit.ru", + "w3school.com.cn", + "zwinky.com", + "inboxdollars.com", + "gratka.pl", + "vecernji.hr", + "taikang.com", + "bolshoyvopros.ru", + "urbita.com", + "api.ning.com", + "alltop.com", + "readwrite.com", + "medhelp.org", + "totaljobs.com", + "accorhotels.com", + "prom.ua", + "cuny.edu", + "livemint.com", + "berlin.de", + "blogspot.hk", + "orkut.com.br", + "lzjl.com", + "metric-conversions.org", + "nationalrail.co.uk", + "oxu.az", + "mql5.com", + "chinaluxus.com", + "linguee.es", + "saudiairlines.com", + "standardmedia.co.ke", + "alkislarlayasiyorum.com", + "apk.tw", + "lesoir.be", + "whatculture.com", + "indiewire.com", + "pccomponentes.com", + "paytm.in", + "ebaypartnernetwork.com", + "haivl.tv", + "e-junkie.com", + "tvp.pl", + "ifilez.org", + "aristeguinoticias.com", + "bangkokpost.com", + "whatmobile.com.pk", + "prodavalnik.com", + "arsenal.com", + "buyma.com", + "torrentday.com", + "sammydress.com", + "blogcu.com", + "imore.com", + "shitaraba.com", + "pasadrexam2014.in", + "songspk.at", + "eforosh.com", + "nocoty.pl", + "koreus.com", + "verticalresponse.com", + "arte.tv", + "ocnk.net", + "cryptsy.com", + "haaretz.com", + "straitstimes.com", + "2domains.ru", + "usnetads.com", + "pracuj.pl", + "edreams.it", + "sba.gov", + "instabang.com", + "domainnamesales.com", + "teensnow.com", + "visualwebsiteoptimizer.com", + "blogos.com", + "santander.com.mx", + "solidfiles.com", + "authorstream.com", + "teslamotors.com", + "tema.livejournal.com", + "kolesa.kz", + "startribune.com", + "vevo.com", + "clien.net", + "3asq.com", + "tvnet.lv", + "mouthshut.com", + "binary-machine.com", + "adultwork.com", + "hypebeast.com", + "itrack.it", + "network-tools.com", + "brandsoftheworld.com", + "comedycentral.com", + "hotscripts.com", + "meristation.com", + "rlsbb.com", + "uzai.com", + "volkskrant.nl", + "terra.com.mx", + "howtoforge.com", + "178.com", + "wickedfire.com", + "dilandau.eu", + "seriales.us", + "xataka.com", + "hktdc.com", + "express.pk", + "surfcanyon.com", + "buildhr.com", + "ouest-france.fr", + "stltoday.com", + "alternet.org", + "site5.com", + "payserve.com", + "society6.com", + "douguo.com", + "prosieben.de", + "verywed.com", + "enikos.gr", + "tubewolf.com", + "openrice.com", + "blankrefer.com", + "gouv.qc.ca", + "torrentfreak.com", + "craveonline.com", + "unfollowers.com", + "deser.pl", + "quanjing.com", + "runetki.tv", + "yepi.com", + "socialbakers.com", + "webmotors.com.br", + "mmo-champion.com", + "globososo.com", + "whois.sc", + "sportskeeda.com", + "socialblade.com", + "jin115.com", + "avjavjav.com", + "thenewstribe.com", + "pinkrod.com", + "blox.pl", + "mediamarkt.de", + "vivo.com.br", + "facilisimo.com", + "sinarharian.com.my", + "leguide.com", + "voegol.com.br", + "j.gs", + "okgj.com", + "revolveclothing.com", + "lolinez.com", + "go2cloud.org", + "vmoptions.com", + "myip.ms", + "ay.gy", + "liquidweb.com", + "sbi.co.in", + "alamaula.com", + "reshareable.tv", + "stirileprotv.ro", + "siriusxm.com", + "banvenez.com", + "sergey-mavrodi-mmm.net", + "ocj.com.cn", + "elderscrollsonline.com", + "webaslan.com", + "dereferer.org", + "newsbeast.gr", + "778669.com", + "yeucahat.com", + "guitarcenter.com", + "mercurynews.com", + "channeladvisor.com", + "go2000.com", + "google.ge", + "pandora.tv", + "haqqin.az", + "google.tt", + "32d1d3b9c.se", + "sexdatenow.net", + "befunky.com", + "micromaxinfo.com", + "jquerymobile.com", + "radikal.ru", + "wigetmedia.com", + "girlsplay.com", + "pureleverage.com", + "mathxl.com", + "themefuse.com", + "giga.de", + "freevideo.cz", + "stylebistro.com", + "iwriter.com", + "winporn.com", + "tripadvisor.ca", + "atresplayer.com", + "igma.tv", + "ravelry.com", + "semana.com", + "verycd.com", + "sblo.jp", + "fansided.com", + "goalunited.org", + "petfinder.com", + "worthofweb.com", + "memuruz.net", + "njuskalo.hr", + "yellowpages.ca", + "91jm.com", + "webconfs.com", + "interpals.net", + "erepublik.com", + "akismet.com", + "gaadi.com", + "casasbahia.com.br", + "techsupportalert.com", + "bostonglobe.com", + "uploadable.ch", + "smartprofitsystem.com", + "cimbclicks.com.my", + "uwants.com", + "gamestar.de", + "neowin.net", + "inilah.com", + "toster.ru", + "gosuslugi.ru", + "windguru.cz", + "3366.com", + "beinsports.net", + "vector.co.jp", + "memecenter.com", + "hotnews.ro", + "webgame.web.id", + "llnw.com", + "jobstreet.co.id", + "congratulations-you-won.com", + "libertatea.ro", + "app111.com", + "scientificamerican.com", + "strava.com", + "iranjib.ir", + "coneco.net", + "fdrmx.com", + "nps.gov", + "businessinsider.in", + "uniblue.com", + "proporn.com", + "dm5.com", + "qld.gov.au", + "nexusmods.com", + "mandatory.com", + "moskva.fm", + "prosperityteam.com", + "lent.az", + "appbrain.com", + "24smi.org", + "avaxsearch.com", + "gorillavid.in", + "realgm.com", + "favicon.cc", + "astro.com", + "joomlaforum.ru", + "joystiq.com", + "computerworld.com", + "tvspielfilm.de", + "centos.org", + "bugun.com.tr", + "amctv.com", + "relianceada.com", + "papystreaming.com", + "clickindia.com", + "sayyac.com", + "q.gs", + "bravoerotica.com", + "bibsonomy.org", + "netload.in", + "shopatron.com", + "iautos.cn", + "banggood.com", + "commerzbanking.de", + "legacyclix.com", + "intesasanpaolo.com", + "awesomescreenshot.com", + "desidime.com", + "eporner.com", + "alwatanvoice.com", + "ok.ru", + "europe1.fr", + "karnaval.com", + "tornn-tv.com", + "frmtr.com", + "pulscen.ru", + "groupon.co.uk", + "tradera.com", + "comingsoon.net", + "speckyboy.com", + "cargurus.com", + "citysearch.com", + "brother.com", + "acfun.tv", + "jamejamonline.ir", + "enterprise.com", + "elotrolado.net", + "userscripts.org", + "confirmit.com", + "compete.com", + "auctiva.com", + "potterybarn.com", + "ivoox.com", + "multiupload.nl", + "glavnoe.ua", + "p30world.com", + "snapnames.com", + "filmon.com", + "flipkey.com", + "rhhbschool.com", + "worldbank.org", + "proz.com", + "docusign.net", + "akakce.com", + "empflix.com", + "androidforums.com", + "mos.ru", + "contenko.com", + "malwarebytes.org", + "voyeurhit.com", + "filmvz.com", + "windowsazure.com", + "rlslog.net", + "zulagames.com", + "onlinefinder.net", + "blekko.com", + "tvb.com", + "mojomarketplace.com", + "warnerbros.com", + "rogers.com", + "sparkasse.at", + "bigcinema.tv", + "mattcutts.com", + "vemale.com", + "pgatour.com", + "bakusai.com", + "lavozdegalicia.es", + "style.com", + "torrentino.com", + "activeden.net", + "sweetpacks-search.com", + "pcstore.com.tw", + "starmedia.com", + "lycos.com", + "uncomo.com", + "caribbeancom.com", + "googleping.com", + "sondakika.com", + "premiumwp.com", + "tribune.com.pk", + "sc.com", + "91mobiles.com", + "elle.com", + "motortrend.com", + "starsports.com", + "otlan.com", + "planetminecraft.com", + "oregonlive.com", + "publichd.se", + "financialexpress.com", + "huxiu.com", + "hotwords.com", + "asda.com", + "impiego24.it", + "tunisia-sat.com", + "xojane.com", + "shiftdelete.net", + "hamshahrionline.ir", + "mp3.es", + "minutebuzz.com", + "footlocker.com", + "rusnovosti.ru", + "defencenet.gr", + "ndr.de", + "blogosfera.uol.com.br", + "quibids.com", + "soft32.com", + "davidwalsh.name", + "blogun.ru", + "webbirga.net", + "dict.cn", + "groupon.jp", + "abovetopsecret.com", + "contra.gr", + "zn.ua", + "jetbrains.com", + "myus.com", + "redbus.in", + "creativemarket.com", + "uainfo.org", + "rtbf.be", + "forexpf.ru", + "mainlink.ru", + "fantasti.cc", + "usagc.org", + "narutoget.com", + "trendyol.com", + "carsales.com.au", + "fujitv.co.jp", + "dardarkom.com", + "corrieredellosport.it", + "mcanime.net", + "nolo.com", + "beareyes.com.cn", + "familysearch.org", + "eprice.com.tw", + "indonetwork.co.id", + "geizhals.at", + "watchfreemovies.ch", + "siweiw.com", + "elnuevoherald.com", + "codecupdaters.com", + "p1.com", + "imhonet.ru", + "spox.com", + "spreadshirt.com", + "bittorrent.com", + "airberlin.com", + "epicurious.com", + "adverstitial.com", + "researchnow.com", + "lolnexus.com", + "xbabe.com", + "designmodo.com", + "hamusoku.com", + "radioshack.com", + "teamliquid.net", + "helpster.de", + "globalsources.com", + "fasttech.com", + "dickssportinggoods.com", + "j-sen.jp", + "smashwords.com", + "adversal.com", + "musiciansfriend.com", + "whoishostingthis.com", + "ads8.com", + "trend4pay.com", + "tribune.com", + "xsrving.com", + "youngleafs.com", + "gamersky.com", + "podio.com", + "state.pa.us", + "iphoneogram.com", + "datehookup.com", + "hbs.edu", + "ally.com", + "mitbbs.com", + "siasat.pk", + "miratuserie.tv", + "active.com", + "ricardoeletro.com.br", + "html.it", + "minnano-av.com", + "meetly.in", + "denverpost.com", + "supersport.com", + "grupobancolombia.com", + "freemake.com", + "bankier.pl", + "instapaper.com", + "derwesten.de", + "blogspot.hu", + "tamindir.com", + "play.pl", + "warthunder.com", + "team-bhp.com", + "freepeople.com", + "laposte.fr", + "cnews.ru", + "fayerwayer.com", + "hitta.se", + "hao123.com.eg", + "it-ebooks.info", + "freep.com", + "newser.com", + "lexilogos.com", + "polldaddy.com", + "auspost.com.au", + "aib.ie", + "cs.com.cn", + "providesupport.com", + "unblocksit.es", + "twittercounter.com", + "luisaviaroma.com", + "imgbabes.com", + "tharunee.lk", + "nexon.com", + "airliners.net", + "srf.ch", + "angel.co", + "tokyo-sports.co.jp", + "mt5.com", + "down1oads.com", + "vodonet.net", + "axar.az", + "taknaz.ir", + "google.com.py", + "ss.lv", + "stuffgate.com", + "giveawayoftheday.com", + "climatempo.com.br", + "xnxxmovies.com", + "heartinternet.co.uk", + "digikey.com", + "zimbra.free.fr", + "line.me", + "talkingpointsmemo.com", + "dyn.com", + "exacttarget.com", + "yourtango.com", + "shopping.com", + "king.com", + "crackberry.com", + "sm3na.com", + "matchesfashion.com", + "rk.com", + "ems.com.cn", + "xiaomi.cn", + "hillnews.com", + "geforce.com", + "internetslang.com", + "theoldreader.com", + "digitaljournal.com", + "aol.co.uk", + "straightdope.com", + "farfetch.com", + "lupoporno.com", + "ilgiornale.it", + "binzhi.com", + "loveplanet.ru", + "sogi.com.tw", + "googlevideo.com", + "nintendo.com", + "jreast.co.jp", + "coinad.com", + "keek.com", + "chegg.com", + "sparknotes.com", + "mywapblog.com", + "idlebrain.com", + "boardgamegeek.com", + "daserste.de", + "nicozon.net", + "ampnetzwerk.de", + "agenziaentrate.gov.it", + "apec.fr", + "redbull.com", + "listentoyoutube.com", + "diepresse.com", + "gohappy.com.tw", + "morguefile.com", + "dallasnews.com", + "adxpansion.com", + "almos3a.com", + "updatetube.com", + "lowyat.net", + "lolipop.jp", + "terafile.co", + "eversave.com", + "sportingnews.com", + "517dv.com", + "milfmovs.com", + "myflorida.com", + "brooonzyah.net", + "cdn-cachefront.net", + "ig.com", + "indiapost.gov.in", + "hangame.co.jp", + "ithemes.com", + "monova.org", + "udmserve.net", + "impactradius.com", + "teasernet.com", + "gmx.at", + "mmotraffic.com", + "citehr.com", + "viator.com", + "rt.ru", + "japannetbank.co.jp", + "zonealarm.com", + "ebayclassifieds.com", + "safecart.com", + "proptp.net", + "51hejia.com", + "telkomsel.com", + "deutschepost.de", + "startpagina.nl", + "alexaboostup.com", + "istgah.com", + "aufreeads.com", + "thinkstockphotos.com", + "similarsitesearch.com", + "gossiplankanews.com", + "venere.com", + "javascriptkit.com", + "square-enix.com", + "tvtoday.de", + "netaffiliation.com", + "computrabajo.com.mx", + "arioo.com", + "fromdoctopdf.com", + "lookany.com", + "bleepingcomputer.com", + "perfectmoney.com", + "dorkly.com", + "macmillandictionary.com", + "fotka.pl", + "porntubevidz.com", + "rj.gov.br", + "1.1.1.1", + "ebay.be", + "divxplanet.com", + "cyworld.com", + "biography.com", + "p1.cn", + "bazos.sk", + "55.la", + "lockerdome.com", + "yola.com", + "ccbill.com", + "groupon.de", + "okazii.ro", + "izismile.com", + "velvet.hu", + "news-postseven.com", + "ardmediathek.de", + "sagawa-exp.co.jp", + "sarzamindownload.com", + "pdfonline.com", + "gaaks.com", + "azhibo.com", + "pensador.uol.com.br", + "surveygizmo.com", + "familydoctor.com.cn", + "lazada.vn", + "guokr.com", + "enjoydressup.com", + "sky.de", + "afp.com", + "imujer.com", + "livescore.net", + "xinnet.com", + "mypageresults.com", + "safaribooksonline.com", + "extremetech.com", + "ecnavi.jp", + "babyblog.ru", + "practicalecommerce.com", + "vidspot.net", + "studymode.com", + "nation.co.ke", + "cpmrocket.com", + "es.wix.com", + "google.com.cu", + "pravda.ru", + "cda.pl", + "appszoom.com", + "selfhtml.org", + "zmovie.tw", + "dba.dk", + "domain.com", + "brightcove.com", + "lol55.com", + "textsale.ru", + "casualclub.com", + "gudvin.tv", + "torrentreactor.net", + "downloadcamp.com", + "eset.com", + "softonic.it", + "internations.org", + "retre.org", + "3quan.com", + "companieshouse.gov.uk", + "hautelook.com", + "edx.org", + "ono.es", + "jango.com", + "7sur7.be", + "spinrewriter.com", + "hqxnxx.com", + "elperiodico.com", + "arabiaweather.com", + "liansuo.com", + "pcgamer.com", + "offervault.com", + "mytheresa.com", + "memegenerator.net", + "gsm.ir", + "britannica.com", + "mensfitness.com", + "flaticon.com", + "newrelic.com", + "greatmmos.com", + "ifixit.com", + "aiyellow.com", + "my.tv.sohu.com/user/card", + "ce4arab.com", + "news-us.jp", + "slando.kz", + "redflagdeals.com", + "gsmhosting.com", + "raiffeisen.at", + "tagesspiegel.de", + "epravda.com.ua", + "bell.ca", + "sleazyneasy.com", + "dhnet.be", + "webcars.com.cn", + "freepik.es", + "androidpolice.com", + "cbr.ru", + "thanhnien.com.vn", + "shoptime.com.br", + "dailytelegraph.com.au", + "fastcodesign.com", + "full-stream.net", + "google.al", + "creativecow.net", + "centrum24.pl", + "openstat.ru", + "unfollowed.me", + "brb.to", + "babal.net", + "ibotoolbox.com", + "torrentdownloads.me", + "miami.com", + "mubasher.info", + "patient.co.uk", + "ayudawordpress.com", + "makepolo.com", + "exactseek.com", + "echosign.com", + "epson.com", + "somo.vn", + "tripadvisor.jp", + "bikroy.com", + "postimees.ee", + "band.uol.com.br", + "adopteunmec.com", + "imgserve.net", + "toofab.com", + "neeu.com", + "mailjet.com", + "kinja.com", + "qwertypay.com", + "cosme.net", + "dreammovies.com", + "avvo.com", + "wenyard.com", + "dir.bg", + "digital-photography-school.com", + "peru.com", + "thomsonreuters.com", + "grazia.com.cn", + "vivastreet.co.in", + "officemax.com", + "creditmutuel.fr", + "themify.me", + "ait-themes.com", + "share-links.biz", + "graphixshare.com", + "theoutnet.com", + "revolvermaps.com", + "careers360.com", + "tyroodr.com", + "escapistmagazine.com", + "ofreegames.com", + "surveryewadcentrez.eu", + "notebookcheck.net", + "k2s.cc", + "barstoolsports.com", + "townhall.com", + "mk.co.kr", + "epochtimes.com", + "arduino.cc", + "blogcms.jp", + "udacity.com", + "habervaktim.com", + "funweek.it", + "techtunes.com.bd", + "shopzilla.com", + "sun-sentinel.com", + "tfile.me", + "novafile.com", + "eurogamer.net", + "thenewporn.com", + "tuttosport.com", + "sverve.com", + "simplemachines.org", + "nginx.org", + "kepu.com.cn", + "jxedt.com", + "southparkstudios.com", + "gusuwang.com", + "utrace.de", + "peperonity.de", + "hvg.hu", + "cairodar.com", + "mappy.com", + "kouclo.com", + "my.tv.sohu.com/user/lucky", + "lushstories.com", + "stocktwits.com", + "atlantico.fr", + "baihe.com", + "wowkeren.com", + "v7n.com", + "mozilla.com", + "centurylink.net", + "1001freefonts.com", + "pubdirecte.com", + "mercadolibre.com.pe", + "vandelaydesign.com", + "cpaelites.com", + "wanyh.com", + "propertyguru.com.sg", + "esy.es", + "torrentbutler.eu", + "final.ir", + "53.com", + "clips4sale.com", + "freerepublic.com", + "huffingtonpost.de", + "boards.ie", + "fluege.de", + "parents.com", + "gimp.org", + "adorocinema.com", + "yelp.ca", + "twicsy.com", + "kuaibo.com", + "gnu.org", + "parsine.com", + "jpc.com", + "cloudapp.net", + "expatriates.com", + "sproutsocial.com", + "natemat.pl", + "livingrichwithcoupons.com", + "x-ho.com", + "adv-adserver.com", + "downloadchop.com", + "sagepub.com", + "eleconomista.com.mx", + "formula1.com", + "quackit.com", + "dlsite.com", + "powned.tv", + "stream-tv.me", + "mindbodyonline.com", + "mindmeister.com", + "twitlonger.com", + "ebay.ie", + "deccanchronicle.com", + "domaintuno.com", + "thethirdmedia.com", + "laiguana.tv", + "flurry.com", + "eloqua.com", + "createsend.com", + "hrloo.com", + "interfax.com.ua", + "admin.ch", + "zgzcw.com", + "bancoestado.cl", + "fda.gov", + "chinayes.com", + "beslist.nl", + "crossfit.com", + "skai.gr", + "covers.com", + "antpoker.com", + "perfectworld.com", + "momdot.com", + "1c-bitrix.ru", + "feiren.com", + "navitime.co.jp", + "siliconindia.com", + "warning.or.kr", + "qatarliving.com", + "uber.com", + "ing-diba.de", + "boldsky.com", + "animeid.tv", + "tom.com", + "whatsmyserp.com", + "ftbpro.com", + "yobt.tv", + "toluna.com", + "burrp.com", + "viber.com", + "df.eu", + "vatera.hu", + "songmeanings.com", + "smbc-card.com", + "hip2save.com", + "wmzona.com", + "banamex.com", + "chinaacc.com", + "t-online.hu", + "indianpornvideos.com", + "runescape.com", + "wapka.mobi", + "115.com", + "blogspot.no", + "aksam.com.tr", + "bankcomm.com", + "giantbomb.com", + "maxcdn.com", + "topshop.com", + "livescores.com", + "rosbalt.ru", + "gandi.net", + "ioffer.com", + "mihandownload.com", + "eweb4.com", + "britishcouncil.org", + "cheetahmail.com", + "vbulletin.com", + "bradsdeals.com", + "empowernetwork.com/commissionloophole", + "vueling.com", + "raventools.com", + "yext.com", + "everychina.com", + "reduxmediia.com", + "openstreetmap.org", + "wed114.cn", + "dzwww.com", + "miibeian.gov.cn", + "ufl.edu", + "deseretnews.com", + "presstv.ir", + "copy.com", + "fourhourworkweek.com", + "buzztheme.net", + "bollywoodlife.com", + "redtubelive.com", + "pengyou.com", + "antaranews.com", + "selectornews.com", + "lavoz.com.ar", + "avm.de", + "flalottery.com", + "jmp9.com", + "baltimoresun.com", + "mozillazine.org", + "dnevnik.hr", + "aebn.net", + "mightydeals.com", + "livescore.eu", + "publipt.com", + "xbmc.org", + "scmp.com", + "sbobet.com", + "alpari.ru", + "harborfreight.com", + "linio.com.pe", + "2500sz.com", + "rdio.com", + "bearshare.com", + "thetrainline.com", + "888poker.es", + "filecrop.com", + "twiends.com", + "elnuevodia.com", + "rasekhoon.net", + "36.cn", + "axeso5.com", + "mzamin.com", + "webmastersitesi.com", + "thinkdigit.com", + "lifescript.com", + "experian.com", + "adorama.com", + "d1net.com", + "torrentz.pro", + "fullhdfilmizle.org", + "space.com", + "collider.com", + "zalando.it", + "slate.fr", + "carmax.com", + "recode.net", + "gfan.com", + "fonts.com", + "honda.com", + "ecwid.com", + "debate.com.mx", + "bmail.uol.com.br", + "santandernet.com.br", + "coderanch.com", + "lyricsmode.com", + "gametrailers.com", + "3600.com", + "designboom.com", + "131.com", + "finam.ru", + "df.gob.mx", + "85dcf732d593.se", + "coches.net", + "dhs.gov", + "neurs.com", + "poppen.de", + "public-api.wordpress.com", + "magnovideo.com", + "squareup.com", + "balagana.net", + "goodsearch.com", + "blog.ir", + "ne10.uol.com.br", + "di.se", + "sinembargo.mx", + "k618.cn", + "eventim.de", + "mysavings.com", + "futureshop.ca", + "spinding.com", + "landsend.com", + "oqenadserving.com", + "here.com", + "upi.com", + "picresize.com", + "abnamro.nl", + "filenuke.com", + "yxku.com", + "mypearson.com", + "dinakaran.com", + "correos.es", + "hotchatdirect.com", + "markafoni.com", + "gofuckbiz.com", + "webfindpage.com", + "cancan.ro", + "hostgator.in", + "latribune.fr", + "adxite.com", + "mofos.com", + "bluewin.ch", + "24ur.com", + "dion.ne.jp", + "vpsdomain3.eu", + "newsweek.com", + "oasgames.com", + "mtv3.fi", + "internetworld.de", + "yatedo.com", + "globaltimes.cn", + "paddypower.com", + "berlin1.de", + "norwegian.com", + "opteck.com", + "zenfolio.com", + "kakprosto.ru", + "ecosia.org", + "check24.de", + "idbibank.co.in", + "zoomit.ir", + "notebooksbilliger.de", + "torontosun.com", + "egopay.com", + "cnnexpansion.com", + "emoneyspace.com", + "rsport.ru", + "fok.nl", + "mql4.com", + "inhabitat.com", + "tqn.com", + "noticierodigital.com", + "wrike.com", + "bazos.cz", + "phonegap.com", + "microlancer.com", + "anipo.jp", + "fotocommunity.de", + "kuwo.cn", + "chinacaipu.com", + "terra.cl", + "500px.org", + "qantas.com.au", + "advertising.com", + "push2check.net", + "livefyre.com", + "philstar.com", + "erail.in", + "mywebgrocer.com", + "der-postillon.com", + "ru-board.com", + "saramin.co.kr", + "yjbys.com", + "aaa.com", + "flavorwire.com", + "fcbarcelona.com", + "lacentrale.fr", + "googlegroups.com", + "luck4.me", + "catho.com.br", + "centauro.com.br", + "sia.az", + "forumactif.com", + "paltalk.com", + "beyond.com", + "occ.com.mx", + "stamps.com", + "getoffmyinternets.net", + "mbok.jp", + "cleveland.com", + "ucdavis.edu", + "tielabs.com", + "hkgolden.com", + "vandal.net", + "lyst.com", + "mrskin.com", + "bunshun.jp", + "netcarshow.com", + "b1.org", + "hromadske.tv", + "sf-express.com", + "pirateproxy.ca", + "getcashforsurveys.com", + "clikz4freakz.com", + "adsupply.com", + "truste.org", + "boxuu.com", + "webartex.ru", + "usc.edu", + "moo.com", + "winkal.com", + "deloitte.com", + "xvideos-field5.com", + "suomi24.fi", + "flexmls.com", + "weeklystandard.com", + "debenhams.com", + "mejortorrent.com", + "vconnect.com", + "ligatus.com", + "skynewsarabia.com", + "herschina.com", + "92lux.cn", + "care.com", + "chip.com.tr", + "tangdou.com", + "vatanbilgisayar.com", + "uppit.com", + "metronews.fr", + "parenting.com.tw", + "meteo.gr", + "meetgee.com", + "ldlc.com", + "ubs.com", + "elcolombiano.com", + "mafengwo.cn", + "moodle.org", + "asiaone.com", + "huffingtonpost.it", + "ipas2free.com", + "casa.it", + "kaiserpermanente.org", + "listindiario.com", + "treehugger.com", + "payeer.com", + "mypoints.com", + "pjmedia.com", + "foreca.com", + "luxup.ru", + "halifax.co.uk", + "webinarjam.net", + "webmeup.com", + "webdesignledger.com", + "rte.ie", + "18andabused.com", + "vbulletin.org", + "apne.tv", + "immoweb.be", + "colonelcassad.livejournal.com", + "atlassian.com", + "investors.com", + "globalbux.net", + "wechat.com", + "kanoon.ir", + "mawaly.com", + "self.com", + "drive.ru", + "autozone.com", + "creditonebank.com", + "fdj.fr", + "hsbc.com.br", + "desk.com", + "dlvr.it", + "wpml.org", + "newmobilelife.com", + "crosswalk.com", + "thehun.com", + "hobbyking.com", + "tiantian.com", + "hichina.com", + "cyanogenmod.org", + "horoscope.com", + "bizsugar.com", + "popularmechanics.com", + "egaliteetreconciliation.fr", + "eol.cn", + "rzd.ru", + "escalatenetwork.net", + "yad2.co.il", + "cna.com.tw", + "zi-m.com", + "leonardo.it", + "ap.gov.in", + "rakuten.de", + "wallsave.com", + "netzwelt.de", + "bitrix24.ru", + "techinasia.com", + "bitstamp.net", + "hostelbookers.com", + "shoplocal.com", + "200please.com", + "tune-up.com", + "mapy.cz", + "dealnews.com", + "livescore.co.uk", + "seocentro.com", + "rstyle.me", + "neoseeker.com", + "kaban.tv", + "visualstudio.com", + "cinetux.org", + "groupon.com.br", + "lastpass.com", + "rincondelvago.com", + "popsci.com", + "2144.cn", + "clickfair.com", + "dsebd.org", + "banesco.com", + "spi0n.com", + "blogspot.cz", + "tnews.ir", + "nme.com", + "paperblog.com", + "firstcry.com", + "tophotels.ru", + "liepin.com", + "korabia.com", + "ifttt.com", + "aemet.es", + "alot.com", + "manutd.com", + "edreams.es", + "turkcell.com.tr", + "eva.vn", + "techpowerup.com", + "vkrugudruzei.ru", + "feed2all.eu", + "thehindubusinessline.com", + "express.de", + "ixquick.com", + "thrillist.com", + "iqilu.com", + "seriouseats.com", + "edgesuite.net", + "telexads.com", + "alza.cz", + "en-japan.com", + "al.com", + "dota2lounge.com", + "totalping.com", + "seocheki.net", + "iceporn.com", + "col3negoriginel.lk", + "porsyar.com", + "property24.com", + "madamenoire.com", + "markethealth.com", + "alphacoders.com", + "webempresa.com", + "empireonline.com", + "downforeveryoneorjustme.com", + "lankadeepa.lk", + "darty.com", + "census.gov", + "wapka.me", + "kdslife.com", + "jpost.com", + "cricfree.tv", + "7xxxtube.com", + "businesscatalyst.com", + "dilbert.com", + "gaymaletube.com", + "bellanaija.com", + "renfe.es", + "equifax.com", + "pornmaki.com", + "lolesports.com", + "yunfan.com", + "everybitcity.com", + "9yao.com", + "avantlink.com", + "paginebianche.it", + "xrosview.com", + "newsday.com", + "usportnews.com", + "petapixel.com", + "pistonheads.com", + "lidovky.cz", + "cvent.com", + "adlandpro.com", + "wikisource.org", + "ename.com", + "prosport.ro", + "sharebuilder.com", + "prisjakt.nu", + "cw.com.tw", + "datafilehost.com", + "aircanada.com", + "osu.edu", + "linuxmint.com", + "toyota.com", + "google.am", + "adobeconnect.com", + "h33t.to", + "companycheck.co.uk", + "juno.com", + "beitaichufang.com", + "alaskaair.com", + "tipsandtricks-hq.com", + "yourstory.com", + "wow-impulse.ru", + "wishpond.com", + "promosite.ru", + "weddingwire.com", + "icicidirect.com", + "mouser.com", + "shopstyle.co.uk", + "sinemalar.com", + "addurl.nu", + "seccionamarilla.com.mx", + "yllix.com", + "motorola.com", + "huawei.com", + "mp3juices.com", + "fitday.com", + "pakwheels.com", + "uploading.com", + "snapwidget.com", + "rsb.ru", + "mturk.com", + "libreoffice.org", + "acer.com", + "rd.com", + "aa.com.tr", + "glamour.com", + "docnhat.net", + "cnbb.com.cn", + "piwik.org", + "mycompanyadmin.com", + "ohozaa.com", + "fox.com", + "mastercard.com.au", + "microsofttranslator.com", + "myhabit.com", + "acesso.uol.com.br", + "cqnews.net", + "default-search.net", + "juntadeandalucia.es", + "carigold.com", + "tweepi.com", + "gamedog.cn", + "videomega.tv", + "softarchive.net", + "akamai.com", + "adorika.net", + "zorpia.com", + "bitdefender.com", + "formstack.com", + "al3abbarq.com", + "ruelala.com", + "andhrajyothy.com", + "siteheart.com", + "spankbang.com", + "buxp.org", + "subtlepatterns.com", + "cucirca.eu", + "apontador.com.br", + "sbs.co.kr", + "rong360.com", + "billmelater.com", + "nowdownload.ch", + "pingmyurl.com", + "main.jp", + "allabolag.se", + "fastspring.com", + "20d625b48e.se", + "wpzoom.com", + "dl-protect.com", + "onvista.de", + "iberia.com", + "learni.st", + "minijuegos.com", + "ae.com", + "india-forums.com", + "port.hu", + "truelocal.com.au", + "ziprecruiter.com", + "toyokeizai.net", + "24timezones.com", + "tzetze.it", + "the-village.ru", + "ptcapusa.com", + "indiaresults.com", + "desjardins.com", + "nchsoftware.com", + "yasdl.com", + "delo.ua", + "baskino.com", + "salary.com", + "freecontact.com", + "wumii.com", + "ssc.nic.in", + "synology.com", + "nttdocomo.co.jp", + "racing-games.com", + "freeonlinegames.com", + "aftabnews.ir", + "zumi.pl", + "efshop.com.tw", + "hawamer.com", + "eastbay.com", + "izvestia.ru", + "mosaiquefm.net", + "twitchy.com", + "slaati.com", + "bookingbuddy.com", + "networksolutionsemail.com", + "etsystatic.com", + "sina.com", + "asiandatingbeauties.com", + "blogs.com", + "france3.fr", + "healthcentral.com", + "downloadatoz.com", + "matthewwoodward.co.uk", + "legalzoom.com", + "phun.org", + "campaignmonitor.com", + "cifraclub.com.br", + "hir24.hu", + "twoplustwo.com", + "hotshame.com", + "arvixededicated.com", + "designcrowd.com", + "mrexcel.com", + "blu-ray.com", + "bundesliga.de", + "comments.ua", + "filmstarts.de", + "3sk.tv", + "donedeal.ie", + "natunbarta.com", + "forex-mmcis.com", + "lifehacker.ru", + "sitepronews.com", + "tudogostoso.com.br", + "titanfall.com", + "brainpickings.org", + "saharareporters.com", + "vidto.me", + "mlit.go.jp", + "terapeak.com", + "techweb.com.cn", + "premiere.fr", + "kanui.com.br", + "asianetnews.tv", + "cric.lk", + "exxxtrasmall.com", + "hiphopdx.com", + "5617.com", + "namepros.com", + "humanmetrics.com", + "allin1convert.com", + "ox.ac.uk", + "msecnd.net", + "cathaypacific.com", + "bet365affiliates.com", + "tcs.com", + "trafficestimate.com", + "lmgtfy.com", + "finance.ua", + "find404.com", + "pagesperso-orange.fr", + "curbed.com", + "tasteofhome.com", + "ar15.com", + "mydigitallife.info", + "promiflash.de", + "technobuffalo.com", + "gamestorrents.com", + "labirint.ru", + "pclab.pl", + "indiatvnews.com", + "finya.de", + "redmondpie.com", + "rabota.ru", + "share-online.biz", + "packtpub.com", + "techgig.com", + "shaw.ca", + "apkmania.co", + "bet-at-home.com", + "barclaycard.co.uk", + "onefloorserve.com", + "raiffeisen.ru", + "t-nation.com", + "mbalib.com", + "staseraintv.com", + "uswitch.com", + "smartadserver.com", + "bbcgoodfood.com", + "elitetorrent.net", + "backlinko.com", + "naszemiasto.pl", + "submitexpress.com", + "savings.com", + "gem.pl", + "ahlamontada.com", + "fotomac.com.tr", + "gougou.com", + "payamsara.com", + "klix.ba", + "doostiha.ir", + "bibliocommons.com", + "wealthyaffiliate.com", + "emgn.com", + "sifyitest.com", + "washingtonexaminer.com", + "rakuten-sec.co.jp", + "mysurvey.com", + "uuu9.com", + "uscourts.gov", + "showroomprive.com", + "cargocollective.com", + "hsoub.com", + "afterbuy.de", + "marunadanmalayali.com", + "jumponhottie.com", + "avon.com", + "lazada.com.my", + "51netu.com.cn", + "ria.com", + "sankakucomplex.com", + "tvline.com", + "devshed.com", + "superherohype.com", + "tv-series.me", + "stylecraze.com", + "wizards.com", + "linkcollider.com", + "xinjunshi.com", + "bestchange.ru", + "1111.com.tw", + "fakenamegenerator.com", + "newsmth.net", + "noupe.com", + "marketingprofs.com", + "bluestacks.com", + "jemtube.com", + "bestbuy.ca", + "backcountry.com", + "designshack.net", + "pond5.com", + "highsnobiety.com", + "rtl.fr", + "pearsoncmg.com", + "hellowork.go.jp", + "koimoi.com", + "soali.ir", + "incentria.com", + "vagas.com.br", + "huaxi100.com", + "cheaa.com", + "lohaco.jp", + "gov.on.ca", + "fabthemes.com", + "pipl.com", + "comscore.com", + "pcfinancial.ca", + "craigslist.co.in", + "michaels.com", + "jibjab.com", + "sankeibiz.jp", + "dominos.co.in", + "supermoney.eu", + "itusozluk.com", + "sdchina.com", + "allkpop.com", + "cam4.com.br", + "stayfriends.de", + "futbol24.com", + "searchina.net", + "sgcpanel.com", + "informe21.com", + "alsacreations.com", + "crateandbarrel.com", + "alliancewarfare.com", + "filerio.in", + "emailsrvr.com", + "reventmedia.com", + "vietcombank.com.vn", + "rxlist.com", + "acidcow.com", + "orange.co.uk", + "hellocoton.fr", + "boldchat.com", + "psychcentral.com", + "surfline.com", + "xxxkinky.com", + "es.wordpress.com", + "publika.az", + "shufoo.net", + "hertz.com", + "businessweekly.com.tw", + "virgula.uol.com.br", + "linkis.com", + "ixwebhosting.com", + "usa.gov", + "linguee.com", + "yengo.com", + "huffingtonpost.jp", + "1x3x.com", + "mediametrics.ru", + "ec21.com", + "80018.cn", + "get-tune.net", + "yupoo.com", + "moviepilot.de", + "ihned.cz", + "fio.cz", + "pubted.com", + "cinemablend.com", + "stooorage.com", + "thevault.bz", + "journaldugeek.com", + "sommer-sommer.com", + "travelchannel.com", + "softonic.de", + "petsmart.com", + "ufxmarkets.com", + "e24.no", + "spirit.com", + "dabi.ir", + "admin5.net", + "serialu.net", + "vancouversun.com", + "submanga.com", + "sbicard.com", + "affaritaliani.it", + "idownloadblog.com", + "castorama.fr", + "sudouest.fr", + "payscale.com", + "serebii.net", + "storypick.com", + "jobkorea.co.kr", + "gowildcasino.com", + "hmetro.com.my", + "parimatch.com", + "51pinwei.com", + "globalnews.ca", + "mercadopago.com", + "sixrevisions.com", + "saisoncard.co.jp", + "gov.kz", + "gossip-tv.gr", + "zbozi.cz", + "aliorbank.pl", + "huanqiuauto.com", + "okaz.com.sa", + "cplusplus.com", + "mochimedia.com", + "google.sn", + "jquery-plugins.net", + "peoplesmart.com", + "mmafighting.com", + "ponpare.jp", + "porsche.com", + "charter.com", + "scrabblefinder.com", + "unpcn.com", + "loveaholics.com", + "geocities.co.jp", + "ladbrokes.com", + "52che.com", + "swansonvitamins.com", + "sweetwater.com", + "ali213.net", + "savenkeep.com", + "hotair.com", + "orgasmatrix.com", + "runrun.es", + "filezilla-project.org", + "looti.net", + "pagesix.com", + "twentytwowords.com", + "gingersoftware.com", + "youxiduo.com", + "isimtescil.net", + "rui.jp", + "d1g.com", + "2dbook.com", + "zigzig.ir", + "123telugu.com", + "wdr.de", + "hankyung.com", + "pr.com", + "search.ch", + "iranproud.com", + "raaga.com", + "alt1040.com", + "okpay.com", + "recordchina.co.jp", + "dreamincode.net", + "bendibao.com", + "creativelive.com", + "socialoomph.com", + "cssdeck.com", + "taxactonline.com", + "pb.com", + "fnac.es", + "ucsd.edu", + "tmart.com", + "whorush.com", + "spicejet.com", + "socialadr.com", + "tutorialzine.com", + "christianmingle.com", + "torrentleech.org", + "statefarm.com", + "orkut.com", + "whoscored.com", + "truthaboutonlinesluts.com", + "32red.com", + "wp-themes.com", + "adpost.com", + "cnr.cn", + "180upload.com", + "snagajob.com", + "textbroker.com", + "freshome.com", + "talkgold.com", + "dudamobile.com", + "weblancer.net", + "newone.org", + "nacion.com", + "takepart.com", + "voici.fr", + "gizmodo.jp", + "viki.com", + "symbaloo.com", + "topface.com", + "businessdictionary.com", + "adtech.info", + "elandroidelibre.com", + "fux.com", + "21-sun.com", + "cpmfx.com", + "siemens.com", + "edmodo.com", + "notitarde.com", + "babypips.com", + "silkroad.com", + "onextrapixel.com", + "notdoppler.com", + "walmart.ca", + "marvel.com", + "groupon.fr", + "us1.com", + "bunte.de", + "findlaw.com", + "topito.com", + "carrefour.fr", + "aawsat.com", + "super.ae", + "kicktipp.de", + "simplyrecipes.com", + "i-mobile.co.jp", + "podrobnosti.ua", + "snn.ir", + "successfactors.com", + "paixie.net", + "wiwo.de", + "glowgaze.com", + "mapsofworld.com", + "limetorrents.com", + "plarium.com", + "tvi.ua", + "corel.com", + "adhitprofits.com", + "findthecompany.com", + "noticiasaldiayalahora.co", + "160by2.com", + "jobberman.com", + "rus.ec", + "fujitsu.com", + "ladepeche.fr", + "pravda.sk", + "wwtdd.com", + "b9dm.com", + "marathonbet.com", + "sexlunch.com", + "komputronik.pl", + "gutenberg.org", + "dizo.com.cn", + "comunio.de", + "naked.com", + "gamesradar.com", + "moneysupermarket.com", + "cheaptickets.com", + "assembla.com", + "timesonline.co.uk", + "mckinsey.com", + "ebook3000.com", + "videoblocks.com", + "ruseller.com", + "pideo.net", + "joomlacode.org", + "pciconcursos.com.br", + "channelnewsasia.com", + "cnnturk.com", + "consultant.ru", + "michigan.gov", + "manoto1.com", + "10010.com", + "wholefoodsmarket.com", + "globalgrind.com", + "pichak.net", + "morpakampus.com", + "crazydomains.com.au", + "uptodown.com", + "polygon.com", + "raspberrypi.org", + "atmarkit.co.jp", + "heftig.co", + "informationweek.com", + "trendhunter.com", + "state.ny.us", + "pornpros.com", + "tnt.com", + "sportal.bg", + "spritzinc.com", + "theladders.com", + "lulu.com", + "applesfera.com", + "qoo10.jp", + "notebookreview.com", + "startertv.fr", + "healthkart.com", + "imgsin.com", + "mp3olimp.net", + "readms.com", + "casadellibro.com", + "nalog.ru", + "4gamer.net", + "jquery4u.com", + "betfred.com", + "3dwwwgame.com", + "appleinsider.com", + "dailystar.co.uk", + "swalif.com", + "hushmail.com", + "worldtimebuddy.com", + "sportschau.de", + "mediatraffic.com", + "gismeteo.ua", + "record.com.mx", + "putnik1.livejournal.com", + "nextbigwhat.com", + "vinescope.com", + "jarchi.ir", + "yidio.com", + "dunyanews.tv", + "hangseng.com", + "interpark.com", + "scholastic.com", + "penny-arcade.com", + "geilundlive.com", + "budbi.com", + "stop55.com", + "jaidefinichon.com", + "smartsheet.com", + "vector.me", + "farecompare.com", + "vodafone.com.eg", + "craftsy.com", + "theresumator.com", + "downloadab.com", + "abendzeitung-muenchen.de", + "dicio.com.br", + "fobshanghai.com", + "2dehands.be", + "thethao247.vn", + "cams.com", + "gothamist.com", + "moneynews.com", + "zocdoc.com", + "rol.ro", + "nic.ar", + "avenues.info", + "cleverbridge.com", + "usajobs.gov", + "divxstage.eu", + "jsticket.net", + "himasoku.com", + "thrivethemes.com", + "orientaltrading.com", + "premiumpress.com", + "house365.com", + "drugoi.livejournal.com", + "monster.co.uk", + "quickanddirtytips.com", + "virtuemart.net", + "huffpost.com", + "real.com", + "teacherspayteachers.com", + "t24.com.tr", + "zakon.kz", + "shoppinglifestyle.com", + "trendmicro.com", + "surfingbird.ru", + "cam.ac.uk", + "navyfederal.org", + "rateyourmusic.com", + "bravenet.com", + "polo.com", + "cl.ly", + "nanacast.com", + "cnfol.com", + "24option.com", + "myscienceacademy.org", + "getsoftfree.com", + "cba.pl", + "templateism.com", + "actudesfinances.org", + "1.254.254.254", + "jobtalk.jp", + "barbie.com", + "rahnama.com", + "gigporno.com", + "buddypress.org", + "corporationwiki.com", + "enorth.com.cn", + "thepioneerwoman.com", + "cam4ultimate.com", + "phpclasses.org", + "uplus.metroer.com/~content", + "komando.com", + "iha.com.tr", + "televisa.com", + "tmtpost.com", + "classifiedsgiant.com", + "sportinglife.com", + "askul.co.jp", + "travelagency.travel", + "jqueryscript.net", + "rusfolder.com", + "netcq.net", + "irangrand.ir", + "interaztv.com", + "downvids.net", + "mongodb.org", + "nasgo.net", + "thesitewizard.com", + "songspk.cc", + "travelandleisure.com", + "d-h.st", + "wasanga.com", + "cgg.gov.in", + "silverclix.com", + "almogaz.com", + "sexmob.es", + "mistreci.com", + "nukistream.com", + "postaffiliatepro.com", + "dish.com", + "ulta.com", + "worldcat.org", + "crackle.com", + "cineblog01.net", + "parispornmovies.com", + "avito.ma", + "nethouse.ru", + "elfinanciero.com.mx", + "ab-in-den-urlaub.de", + "pixeden.com", + "thetimenow.com", + "divar.ir", + "coolrom.com", + "akbank.com", + "saturn.de", + "dstv.com", + "mttbsystem.com", + "hujiang.com", + "bash.im", + "sextgem.com", + "subaonet.com", + "cnsnews.com", + "informationng.com", + "utoronto.ca", + "hetzner.de", + "vend-o.com", + "cybozu.com", + "autotimes.com.cn", + "5giay.vn", + "theepochtimes.com", + "penesalud.com", + "livenation.com", + "qoinpro.com", + "myway.com", + "safe-swaps.com", + "elephantjournal.com", + "curse.com", + "17zwd.com", + "hitleap.com", + "mec.gov.br", + "freewebcams.com", + "westelm.com", + "imagegals.com", + "streeteasy.com", + "ekitan.com", + "git-scm.com", + "p30day.com", + "kapaza.be", + "svoboda.org", + "google.mg", + "timesofisrael.com", + "roodo.com", + "malwaretips.com", + "dumpaday.com", + "dha.com.tr", + "diary.ru", + "epa.gov", + "jonloomer.com", + "holidaylettings.co.uk", + "oem.com.mx", + "oxfordjournals.org", + "3bmeteo.com", + "iwebtool.com", + "yinyuetai.com", + "ixl.com", + "rezultati.com", + "51sole.com", + "moudamepo.com", + "macromill.com", + "nzz.ch", + "buzznet.com", + "roku.com", + "posta.com.tr", + "letsbonus.com", + "theoatmeal.com", + "artofmanliness.com", + "tubepornkiss.com", + "aviny.com", + "sabresonicweb.com", + "tech-wd.com", + "qbank.ru", + "getsatisfaction.com", + "prokerala.com", + "ebc.com.br", + "typiol.com", + "photo.net", + "rockpapershotgun.com", + "netgear.com", + "mg.co.za", + "impressrd.jp", + "jkbk.cn", + "bakufu.jp", + "lgmi.com", + "tnt-online.ru", + "frequency.com", + "zip-codes.com", + "sport.ro", + "wolfram.com", + "pgmediaserve.com", + "icann.org", + "twistedsifter.com", + "mopo.de", + "vogue.co.uk", + "chatrandom.com", + "checkpagerank.net", + "colorzilla.com", + "autobip.com", + "laban.vn", + "pricecheck.co.za", + "notimex.com.mx", + "pik.ba", + "monster.de", + "google.cd", + "economia.uol.com.br", + "vsnl.net.in", + "futura-sciences.com", + "appstorm.net", + "icefilms.info", + "dicelacancion.com", + "hypovereinsbank.de", + "piratestreaming.tv", + "101domain.com", + "google.mn", + "rutgers.edu", + "behindwoods.com", + "downloadmaster.ru", + "smartinsights.com", + "wiziq.com", + "ftc.gov", + "animenewsnetwork.com", + "tandfonline.com", + "seagate.com", + "famitsu.com", + "onekingslane.com", + "zalando.fr", + "convio.com", + "seochat.com", + "qq163.com", + "capital.gr", + "tamin.ir", + "phpfreaks.com", + "cricketcountry.com", + "ubersuggest.org", + "ntv.co.jp", + "olark.com", + "1001fonts.com", + "appsgeyser.com", + "elsiglodetorreon.com.mx", + "lyricsmint.com", + "tovima.gr", + "petco.com", + "campograndenews.com.br", + "amadeus.com", + "tinychat.com", + "astrology.com", + "idg.se", + "nordea.se", + "gelbeseiten.de", + "nrc.nl", + "houseoffraser.co.uk", + "hepsibahis3.com", + "empowernetwork.com/almostasecret", + "phpnuke.org", + "xfwed.com", + "gulte.com", + "msi.com", + "netfirms.com", + "ovcanada.com", + "smzdm.com", + "wwwhatsnew.com", + "mydomainadvisor.com", + "newindianexpress.com", + "luxtarget.com", + "evz.ro", + "otodom.pl", + "linkin.net", + "moneysavingmom.com", + "uploadbaz.com", + "egyup.com", + "three.co.uk", + "co-operativebank.co.uk", + "redad.ru", + "ipsosinteractive.com", + "all-union.com", + "trustlink.ru", + "softango.com", + "toucharcade.com", + "tv2.dk", + "thisissouthwales.co.uk", + "jra.go.jp", + "alfavita.gr", + "ibigdan.livejournal.com", + "gtarcade.com", + "jc001.cn", + "microcenter.com", + "optimizepress.com", + "beyebe.com", + "muyzorras.com", + "expedia.co.uk", + "volvocars.com", + "lyd.com.cn", + "korben.info", + "disput.az", + "aeroflot.ru", + "betanews.com", + "avangard.ru", + "wrzko.eu", + "cakephp.org", + "majorgeeks.com", + "dreamteammoney.com", + "foroactivo.com", + "ut.ac.ir", + "eonet.jp", + "vn-zoom.com", + "footytube.com", + "expedia.de", + "barneys.com", + "lieyunwang.com", + "gartner.com", + "lazada.com.ph", + "ourtime.com", + "ohnotheydidnt.livejournal.com", + "e-rewards.com", + "updatestar.com", + "cafef.vn", + "cookinglight.com", + "expressdownload.net", + "shockmansion.com", + "adserverpub.com", + "ipko.pl", + "gsa-online.de", + "car.gr", + "cqcounter.com", + "thenews.com.pk", + "gnc.com", + "teambeachbody.com", + "kobobooks.com", + "cwan.com", + "ca.com", + "list.ly", + "baijob.com", + "gunbroker.com", + "rtl.be", + "myadvertisingpays.com", + "mediapost.com", + "groupalia.com", + "anquan.org", + "mixshowblast.com", + "myscore.ru", + "bancochile.cl", + "a2hosting.com", + "hayneedle.com", + "myfc.ir", + "aktifhaber.com", + "eater.com", + "anymeeting.com", + "cylex.de", + "sayidaty.net", + "synonym.com", + "comprendrechoisir.com", + "prlog.ru", + "solidtrustpay.com", + "cinejosh.com", + "yelp.co.uk", + "olx.com.ar", + "dihitt.com", + "donkeymails.com", + "chainreactioncycles.com", + "wearehairy.com", + "ebuyer.com", + "bplans.com", + "ponparemall.com", + "readthedocs.org", + "tehparadox.com", + "source-wave.com", + "tbs.co.jp", + "redcoon.de", + "francetv.fr", + "freelogoservices.com", + "ogone.com", + "jeanmarcmorandini.com", + "lasexta.com", + "sponsoredreviews.com", + "govome.com", + "vecteezy.com", + "alhayat.com", + "cnhubei.com", + "mumayi.com", + "webfail.com", + "eluniverso.com", + "moddb.com", + "gaiaonline.com", + "gordonua.com", + "liveadexchanger.com", + "zhe800.com", + "softonic.com.br", + "cnyes.com", + "cumlouder.com", + "krakow.pl", + "nullrefer.com", + "zaluu.com", + "diretube.com", + "seattlepi.com", + "eanswers.com", + "gostorego.com", + "play.com", + "lenskart.com", + "unblog.fr", + "chatwork.com", + "bronto.com", + "jobisjob.co.in", + "mercadolibre.com.uy", + "overdrive.com", + "runkeeper.com", + "smartshopping.com", + "getrichradio.com", + "grader.com", + "101greatgoals.com", + "elbilad.net", + "kopp-verlag.de", + "ranksignals.com", + "trome.pe", + "jobstreet.com.my", + "farnell.com", + "fastmail.fm", + "faithit.com", + "blogdohotelurbano.com", + "sencha.com", + "daisycon.com", + "random.org", + "tportal.hr", + "rkanr.com", + "fc2-erodouga.com", + "youngpornvideos.com", + "bravoteens.com", + "mobile.free.fr", + "mitele.es", + "edis.at", + "questionablecontent.net", + "indiangilma.com", + "comunio.es", + "bitnami.com", + "sliptalk.com", + "sourceforge.jp", + "lyoness.net", + "nespresso.com", + "ultrafarma.com.br", + "lik.cl", + "jsoftj.com", + "metoffice.gov.uk", + "androidauthority.com", + "360safe.com", + "wikimart.ru", + "tutu.ru", + "truetwit.com", + "xinruijunshi.com", + "online-sweepstakes.com", + "singaporeair.com", + "cwtv.com", + "politiken.dk", + "alaan.tv", + "tuttomercatoweb.com", + "imagehost123.com", + "drakensang.com", + "frenchweb.fr", + "syosetu.com", + "zhifang.com", + "viptube.com", + "theme123.net", + "statista.com", + "51bi.com", + "clickz.com", + "kingworldnews.com", + "qunar.com", + "pimpandhost.com", + "pagina12.com.ar", + "xinmin.cn", + "kidshealth.org", + "pics.livejournal.com", + "kabum.com.br", + "anandabazar.com", + "pnu.ac.ir", + "scene7.com", + "paperpkads.com", + "mylikes.com", + "longtailvideo.com", + "firmy.cz", + "bitcoinity.org", + "inopressa.ru", + "expat-blog.com", + "governmentjobs.com", + "airdroid.com", + "mid-day.com", + "frandroid.com", + "webceo.com", + "cadenaser.com", + "minutouno.com", + "onamae.com", + "onlinekhabar.com", + "williams-sonoma.com", + "freewebsubmission.com", + "vozforums.com", + "bigpicture.ru", + "mymodernmet.com", + "presseportal.de", + "licindia.in", + "ismedia.jp", + "depor.pe", + "sing365.com", + "v2cigs.com", + "postimage.org", + "gencat.cat", + "west263.com", + "alohatube.com", + "eenadupratibha.net", + "terra.com", + "bri.co.id", + "silktide.com", + "anyang.gov.cn", + "speedanalysis.com", + "bitcoin.org", + "toodledo.com", + "topgear.com", + "rtvslo.si", + "hotpads.com", + "nedsecure.co.za", + "expedia.ca", + "thairath.co.th", + "gizbot.com", + "naukrigulf.com", + "guffins.com", + "twilio.com", + "avsforum.com", + "sharpnews.ru", + "inoreader.com", + "stgeorge.com.au", + "shoebuy.com", + "sbwire.com", + "bfm.ru", + "dongtaiwang.com", + "mandrillapp.com", + "176.com", + "olx.com", + "trustpilot.co.uk", + "tgju.org", + "pap.fr", + "iphonehacks.com", + "gry-online.pl", + "mp3xd.com", + "viabcp.com", + "bizcommunity.com", + "chillingeffects.org", + "thomasnet.com", + "performersoft.com", + "paymaster.ru", + "comdotgame.com", + "bseindia.com", + "foodgawker.com", + "clicktale.com", + "dot.tk", + "smbc-comics.com", + "vvmembers.co", + "c-sharpcorner.com", + "acdcads.com", + "goldporntube.com", + "abakus-internet-marketing.de", + "babbel.com", + "fzg360.com", + "99inf.com", + "cduniverse.com", + "autobild.de", + "tvrage.com", + "phoenix.edu", + "hurriyetemlak.com", + "va.gov", + "maxmind.com", + "lifehacker.jp", + "nola.com", + "niroensani.ir", + "ihotelier.com", + "mianbao.com", + "powerball.com", + "express.com", + "freshdesignweb.com", + "o2.co.uk", + "utusan.com.my", + "overclockers.ru", + "fsiblog.com", + "launchpad.net", + "prizee.com", + "alalam.ir", + "imtalk.org", + "vavel.com", + "nyc.ny.us", + "youmob.com", + "dailythanthi.com", + "ksu.edu.sa", + "nlayer.net", + "kurier.at", + "expert-offers.com", + "marry52.com", + "cityads.ru", + "darkorbit.com", + "cio.com", + "a9.com", + "symfony.com", + "crobo.com", + "shatel.ir", + "opposingviews.com", + "myvoffice.com", + "vkmag.com", + "starpulse.com", + "bls.gov", + "lifebettering.com", + "fanhuan.com", + "upload7.ir", + "infoplease.com", + "suruga-ya.jp", + "deredactie.be", + "shangdu.com", + "portail.free.fr", + "smartaddons.com", + "w3layouts.com", + "wallstreet-online.de", + "yapo.cl", + "standard.co.uk", + "atlas.sk", + "seowizard.ru", + "currys.co.uk", + "nova.cz", + "mygully.com", + "favstar.fm", + "themuse.com", + "yudu.com", + "urlopener.com", + "ellentv.com", + "imgsrc.ru", + "km.ru", + "guildwars2.com", + "google.mu", + "androidpit.de", + "mrporter.com", + "ttt4.com", + "cbox.ws", + "hf365.com", + "1mobile.com", + "punjabkesari.in", + "msu.edu", + "grasscity.com", + "abduzeedo.com", + "sublimetext.com", + "jtb.co.jp", + "mensxp.com", + "myheritage.com", + "onvasortir.com", + "safeway.com", + "comicvine.com", + "cpubenchmark.net", + "nn.ru", + "animefreak.tv", + "angularjs.org", + "managewp.com", + "ameli.fr", + "merrjep.com", + "christianbook.com", + "nubiles.net", + "klmty.net", + "247sports.com", + "aramex.com", + "tubent.com", + "cognitiveseo.com", + "bankpasargad.com", + "pingler.com", + "compareja.com.br", + "surveyzrewardcenter.eu", + "allyes.com", + "graaam.com", + "poriborton.com", + "seslisozluk.net", + "shopyourway.com", + "awd.ru", + "wa.gov.au", + "slrclub.com", + "apollo.lv", + "girls-ly.com", + "totalfilm.com", + "cam4.it", + "gogecapital.com", + "sketchup.com", + "muscleandfitness.com", + "m24.ru", + "blinklist.com", + "artisteer.com", + "nettavisen.no", + "enjin.com", + "pc6.com", + "citrix.com", + "indgovtjobs.in", + "phpbb.com", + "laughingsquid.com", + "fblife.com", + "stansberryresearch.com", + "hugefiles.net", + "mp3monkey.net", + "mcdonalds.com", + "vodafone.co.uk", + "t-mobile.de", + "tp-link.com.cn", + "iflscience.com", + "nst.com.my", + "zoom.com.br", + "dsw.com", + "tigerair.com", + "doityourself.com", + "bongdainfo.com", + "imacros.net", + "paperlesspost.com", + "sprinthost.ru", + "lovoo.net", + "kadinlarkulubu.com", + "championselect.net", + "jcb.co.jp", + "miui.com", + "impots.gouv.fr", + "linkshop.com.cn", + "pando.com", + "ckeditor.com", + "arabylife.com", + "ml.com", + "sheldonsfans.com", + "freecharge.in", + "magicmovies.com", + "laweekly.com", + "highrisehq.com", + "tr.gg", + "vidbull.com", + "diy.com", + "yieldtraffic.com", + "cima4u.com", + "gendama.jp", + "netpnb.com", + "kannaway.com", + "soft112.com", + "die-boersenformel.com", + "movie25.so", + "footyroom.com", + "dostor.org", + "aol.de", + "desimartini.com", + "themeko.org", + "danarimedia.com", + "toroporno.com", + "stylemepretty.com", + "urlm.de", + "demotivation.me", + "gentside.com", + "carfax.com", + "huzhou.gov.cn", + "hotline.ua", + "fullmoneysystem.com", + "pornomovies.com", + "fsymbols.com", + "blog.de", + "clickpoint.com", + "tv-asahi.co.jp", + "flightstats.com", + "sonico.com", + "afamily.vn", + "anadolu.edu.tr", + "rs-online.com", + "androidpit.com", + "a-telecharger.com", + "wikifeet.com", + "nix.ru", + "tvmao.com", + "groupon.es", + "hitfix.com", + "weathernews.jp", + "dig.do", + "googledrive.com", + "fodors.com", + "melon.com", + "testflightapp.com", + "markt.de", + "mahua.com", + "sedo.co.uk", + "tuenti.com", + "comm100.com", + "olx.co.id", + "lefrecce.it", + "gov.cl", + "wplift.com", + "unn.com.ua", + "arizona.edu", + "msn.com.cn", + "k7x.com", + "techz.vn", + "jobomas.com", + "extranetinvestment.com", + "tengrinews.kz", + "comicbookmovie.com", + "4porn.com", + "rubiconproject.com", + "gametracker.com", + "otcmarkets.com", + "mismarcadores.com", + "trojmiasto.pl", + "cleverreach.com", + "contactform7.com", + "weloveshopping.com", + "runnersworld.com", + "falabella.com", + "airtelbroadband.in", + "twistys.com", + "ebesucher.de", + "belastingdienst.nl", + "orlandosentinel.com", + "th3professional.com", + "duke.edu", + "opensubtitles.us", + "clickme.net", + "infor.pl", + "unesco.org", + "socialfabric.us", + "ename.net", + "alternativeto.net", + "rotahaber.com", + "johnchow.com", + "pervclips.com", + "linkfeed.ru", + "dtvideo.com", + "kinoman.tv", + "siteprice.org", + "lacuerda.net", + "amulyam.in", + "mandghomebusiness.com", + "infojobs.com.br", + "hirufm.lk", + "mybloggertricks.com", + "athemes.com", + "moikrug.ru", + "fybersearch.com", + "wine-searcher.com", + "davidicke.com", + "dotabuff.com", + "sainsburys.co.uk", + "ovaustralia.com", + "updatesoftnow.com", + "desarrolloweb.com", + "elaph.com", + "icc-cricket.com", + "gaytube.com", + "di.fm", + "99wed.com", + "c-and-a.com", + "mlxchange.com", + "realgfporn.com", + "synxis.com", + "optymalizacja.com", + "ibtesama.com", + "hankooki.com", + "shiksha.com", + "anoox.com", + "seamless.com", + "stripe.com", + "ets.org", + "fresherslive.com", + "webcoinpay.net", + "oscommerce.com", + "gratisprogramas.org", + "football365.com", + "mkyong.com", + "antichat.ru", + "spamarrest.com", + "ubc.ca", + "36kr.com", + "startv.in", + "shoutcast.com", + "heureka.sk", + "entheosweb.com", + "51wan.com", + "madadsmedia.com", + "parse.com", + "ngacn.cc", + "techbargains.com", + "tvmovie.de", + "cpmstar.com", + "torproject.org", + "residentadvisor.net", + "genbeta.com", + "hulu.jp", + "pluralsight.com", + "postini.com", + "zombie.jp", + "skimlinks.com", + "liverail.com", + "phys.org", + "slashfilm.com", + "livemaster.ru", + "idwebgame.com", + "pho.to", + "dealfish.co.th", + "s1979.com", + "designfloat.com", + "live365.com", + "pbase.com", + "eniro.se", + "irannaz.com", + "socialmonkee.com", + "uidai.gov.in", + "joomlashine.com", + "allhyipmon.ru", + "upbulk.com", + "diapers.com", + "flypgs.com", + "hotmart.com.br", + "globus-inter.com", + "airfrance.fr", + "t3.com", + "unetepubli.com", + "yahoo-help.jp", + "iphones.ru", + "95559.com.cn", + "glamsham.com", + "gophoto.it", + "statmyweb.com", + "aftermarket.pl", + "forumotion.com", + "iafd.com", + "hotlog.ru", + "drclix.com", + "wawa-mania.ec", + "nichepursuits.com", + "imp.free.fr", + "baofeng.com", + "binsearch.info", + "neswangy.net", + "parperfeito.com.br", + "theeroticreview.com", + "sabayacafe.com", + "maplestage.com", + "icook.tw", + "customerhub.net", + "uid.me", + "viperchill.com", + "askfrank.net", + "juegosjuegos.com", + "debonairblog.com", + "coingeneration.com", + "onetravel.com", + "slutroulette.com", + "jetpack.me", + "embedupload.com", + "bancodevenezuela.com", + "mysavingsmedia.net", + "smallbiztrends.com", + "emirates247.com", + "bu.edu", + "fineco.it", + "netbarg.com", + "gatech.edu", + "zik.ua", + "peliculascoco.com", + "hkjc.com", + "turkiyegazetesi.com.tr", + "cooperativa.cl", + "challenges.fr", + "usingenglish.com", + "vouchercodes.co.uk", + "bonprix.de", + "disneylatino.com", + "townwork.net", + "rentanything.com", + "krypt.com", + "wral.com", + "globes.co.il", + "designtaxi.com", + "mp-success.com", + "invisionpower.com", + "liontravel.com", + "classicrummy.com", + "mt.co.kr", + "inccel.com", + "santander.cl", + "afip.gov.ar", + "yellowpages.com.au", + "osclass.org", + "couriermail.com.au", + "stream.cz", + "wizaz.pl", + "linkresearchtools.com", + "cam4.co.uk", + "allafrica.com", + "gezginler.net", + "free-ebooks.net", + "hopesandfears.com", + "ticketmaster.com.mx", + "ohio.gov", + "liberoquotidiano.it", + "neurs.net", + "forumophilia.com", + "odnako.org", + "gamekult.com", + "vh1.com", + "zxart.cn", + "mass.gov", + "tv-tokyo.co.jp", + "mundopromocion.com", + "theme-junkie.com", + "asu.edu", + "lalibre.be", + "pornokopilka.info", + "fx678.com", + "netbeans.org", + "chinabidding.com.cn", + "lunapic.com", + "dramafever.com", + "hbogo.com", + "torrents.net", + "woopra.com", + "justfab.com", + "fitnessmagazine.com", + "tripadvisor.com.mx", + "183.com.cn", + "adscendmedia.com", + "geek.com", + "thepetitionsite.com", + "skyscanner.ru", + "morningpost.com.cn", + "concursolutions.com", + "camplace.com", + "smarturl.it", + "browsershots.org", + "piktochart.com", + "elcorreo.com", + "collegeboard.org", + "kasikornbank.com", + "adsoftheworld.com", + "fakku.net", + "jpn.org", + "sahafah.net", + "rubias19.com", + "fc2web.com", + "redsurf.ru", + "iproperty.com.my", + "browsersafeguard.com", + "mmajunkie.com", + "bangkokbank.com", + "ovi.com", + "fap.to", + "filmygyan.in", + "edaily.vn", + "affilorama.com", + "valuecommerce.ne.jp", + "softaculous.com", + "exchanger.ru", + "html-color-codes.info", + "hd-xvideos.com", + "realself.com", + "geocaching.com", + "mom.me", + "stc.com.sa", + "video-i365.com", + "umd.edu", + "jeffbullas.com", + "kora.com", + "cyberchimps.com", + "popupads.ir", + "chatango.com", + "gearslutz.com", + "fout.jp", + "contactmusic.com", + "linkcrypt.ws", + "sophos.com", + "forum-auto.com", + "svtplay.se", + "newsbusters.org", + "fileswap.com", + "phpfox.com", + "denic.de", + "riverisland.com", + "encuentra24.com", + "foxnewsinsider.com", + "dtdc.com", + "tayyar.org", + "thepushbuttonmillionaire.com", + "brokenlinkcheck.com", + "ju51.com", + "stihi.ru", + "psdgraphics.com", + "mommyfucktube.com", + "mangocity.com", + "routard.com", + "whicdn.com", + "socialtriggers.com", + "e-bookspdf.org", + "songspk3.in", + "paginasamarillas.com", + "webgame.in.th", + "xxxdessert.com", + "materiel.net", + "skillshare.com", + "tubepleasure.com", + "paypal.de", + "yeahmobi.com", + "torrentz-proxy.com", + "aircel.com", + "openask.com", + "trafficg.com", + "anthem.com", + "aleseriale.pl", + "f-page.ru", + "hrsmart.com", + "cbengine.com", + "startlap.com", + "spamhaus.org", + "trovit.com", + "sporcle.com", + "xs8.cn", + "romwe.com", + "powtoon.com", + "cybozulive.com", + "zbiornik.com", + "cebit.de", + "flipboard.com", + "news.am", + "wm-panel.com", + "topnews.ru", + "usp.br", + "realmadrid.com", + "fastdist.net", + "unian.ua", + "youtube.com/user/PewDiePie", + "unc.edu", + "tamu.edu", + "ip2location.com", + "americanapparel.net", + "virtualedge.com", + "vikatan.com", + "goodlayers.com", + "feedblitz.com", + "dateinasia.com", + "freepdfconvert.com", + "iweb.com", + "lachainemeteo.com", + "aetna.com", + "ti.com", + "gratisjuegos.org", + "webhostinghub.com", + "preev.com", + "holidayiq.com", + "elpais.com.uy", + "uchicago.edu", + "telcel.com", + "feebbo.com", + "webhallen.com", + "dmir.ru", + "emailmg.ipage.com", + "hrs.de", + "propakistani.pk", + "theme.co", + "bigpara.com", + "finishline.com", + "fardanews.com", + "nat.gov.tw", + "stoloto.ru", + "sermepa.es", + "radaris.com", + "softportal.com", + "turkiye.gov.tr", + "nnn.ru", + "e-familynet.com", + "wenkang.cn", + "teamworkpm.net", + "videohelp.com", + "pixmania.com", + "games.com", + "guns.ru", + "plus28.com", + "pf.pl", + "pbworks.com", + "sedo.de", + "freedownloadscenter.com", + "backpackers.com.tw", + "mycokerewards.com", + "chinaunix.net", + "invisionapp.com", + "wordhippo.com", + "unitezz.com", + "yan.vn", + "sonhoo.com", + "spielaffe.de", + "d.pr", + "technologyreview.com", + "democraticunderground.com", + "molotok.ru", + "watchever.de", + "wdc.com", + "torg.com", + "ivillage.com", + "portableapps.com", + "ovh.es", + "vse.kz", + "alrajhibank.com.sa", + "dhl-usa.com", + "pingan.com", + "excite.com", + "cincodias.com", + "marksdailyapple.com", + "bter.com", + "nationaljournal.com", + "fao.org", + "oddee.com", + "digitalriver.com", + "livescore.in", + "siilu.com", + "gofirstrowus.eu", + "alquds.co.uk", + "autoevolution.com", + "zerx.ru", + "trovit.it", + "hvylya.org", + "carters.com", + "shejis.com", + "radio-canada.ca", + "vodafone.es", + "sky.fm", + "maturetube.com", + "virtualtourist.com", + "line25.com", + "rzeczpospolita.pl", + "megacurioso.com.br", + "beliefnet.com", + "eklablog.com", + "hbo.com", + "bsi.ir", + "fastweb.it", + "webrazzi.com", + "telam.com.ar", + "downloadming.me", + "bluefly.com", + "thewire.com", + "erotube.org", + "vt.edu", + "jsbin.com", + "ibosocial.com", + "brides.com.cn", + "jn.pt", + "togetter.com", + "olhardigital.uol.com.br", + "ebookee.org", + "socialgamenet.com", + "romaniatv.net", + "epweike.com", + "pronto.com", + "blog.pl", + "top81.cn", + "flagfox.wordpress.com", + "unionbank.com", + "visitkorea.or.kr", + "blogspot.dk", + "fermasosedi.ru", + "inmobi.com", + "abc.com.py", + "iqoo.me", + "17k.com", + "onlineapplicationsdownloads.com", + "komli.com", + "tala.ir", + "videopremium.me", + "job.ru", + "sunbiz.org", + "tw116.com", + "browserstack.com", + "youtu.be", + "yardbarker.com", + "flvmplayer.com", + "kulichki.net", + "gravity.com", + "levelclix.com", + "kissanime.com", + "insightexpressai.com", + "colorhexa.com", + "citizensbankonline.com", + "hjenglish.com", + "voeazul.com.br", + "paraskhnio.gr", + "es.tl", + "busuu.com", + "appsumo.com", + "metacrawler.com", + "ciku5.com", + "android-hilfe.de", + "laterooms.com", + "google.com.bn", + "gamefront.com", + "rev2pub.com", + "bharatiyamobile.com", + "publicidees.com", + "jossandmain.com", + "babycentre.co.uk", + "zetabux.com", + "movieweb.com", + "timescity.com", + "dnevnik.ru", + "nlcafe.hu", + "magazine3k.com", + "irancell.ir", + "kalaydo.de", + "e5.ru", + "lecai.com", + "sbs.com.au", + "sierratradingpost.com", + "girlfriendvideos.com", + "ink361.com", + "corporate-ir.net", + "allgameshome.com", + "povarenok.ru", + "typo3.org", + "turbosquid.com", + "miniurls.co", + "thinkwithgoogle.com", + "haber3.com", + "newsmeback.com", + "sipse.com", + "osxdaily.com", + "9lessons.info", + "cyclopaedia.net", + "reallifecam.com", + "gameblog.fr", + "colourbox.com", + "mixedmartialarts.com", + "prostoporno.net", + "chanel.com", + "maxim.com", + "cam4.es", + "olx.com.mx", + "newscientist.com", + "netcombo.com.br", + "serienjunkies.org", + "shablol.com", + "bnpparibasfortis.be", + "canada.com", + "timeslive.co.za", + "cinematoday.jp", + "21cineplex.com", + "bodisparking.com", + "cruisecritic.com", + "vertoz.com", + "glosbe.com", + "nstarikov.ru", + "epinions.com", + "colbertnation.com", + "marketglory.com", + "emailmeform.com", + "angloinfo.com", + "filfan.com", + "yougetsignal.com", + "broker.to", + "quickbux.net", + "gottabemobile.com", + "beyondtherack.com", + "tripadvisor.ru", + "jschina.com.cn", + "registraduria.gov.co", + "thisoldhouse.com", + "appcelerator.com", + "noticias24carabobo.com", + "skelbiu.lt", + "ru.wix.com", + "iransong.com", + "i-sux.com", + "telerama.fr", + "mydala.com", + "pia.jp", + "fusebux.com", + "biquge.com", + "microworkers.com", + "onlywire.com", + "fanbox.com", + "ambitoweb.com", + "tripadvisor.com.br", + "postgresql.org", + "telenet.be", + "thegeekstuff.com", + "primelocation.com", + "ee.co.uk", + "phoenixads.co.in", + "brigitte.de", + "360kad.com", + "kathimerini.gr", + "batoto.net", + "babebuns.com", + "webimpresion.com", + "olx.com.ng", + "which.co.uk", + "f5haber.com", + "cic.fr", + "talkfusion.com", + "ctrlq.org", + "caracol.com.co", + "wer-weiss-was.de", + "invisionzone.com", + "head-fi.org", + "thingiverse.com", + "manager-magazin.de", + "bzwbk.pl", + "f1news.ru", + "tweetreach.com", + "sj998.com", + "streetdirectory.com", + "asos.fr", + "kalahari.com", + "classiccars.com", + "bitacoras.com", + "bravotv.com", + "beanfun.com", + "bonappetit.com", + "fashionara.com", + "techdirt.com", + "newlook.com", + "iyinet.com", + "mogujie.com", + "carros.com.br", + "starfall.com", + "rp5.ru", + "pocket-lint.com", + "homeadvisor.com", + "webhostbox.net", + "cameraboys.com", + "pricerunner.com", + "worldcarfans.com", + "meganovosti.net", + "taxheaven.gr", + "freewebsitetemplates.com", + "mr90.ir", + "cheapflights.co.uk", + "bankinter.com", + "euroresidentes.com", + "bettycrocker.com", + "filmey.com", + "sheinside.com", + "2ememain.be", + "tu.tv", + "kugou.com", + "destructoid.com", + "todoist.com", + "karmaloop.com", + "appadvice.com", + "html5rocks.com", + "osdir.com", + "webisgreat.info", + "dailyblogtips.com", + "mca.gov.in", + "e-estekhdam.com", + "hlntv.com", + "proprofs.com", + "his-j.com", + "im286.com", + "mmorpg.com", + "shooshtime.com", + "terra.es", + "lpcloudbox301.com", + "tubepornstars.com", + "tableausoftware.com", + "soccersuck.com", + "websurf.ru", + "indiafreestuff.in", + "pimei.com", + "colatour.com.tw", + "fssnet.co.in", + "maxpark.com", + "foodandwine.com", + "apachefriends.org", + "standardchartered.co.in", + "waveapps.com", + "gosong.net", + "spring.me", + "weatherbug.com", + "iw33.com", + "apserver.net", + "thenationonlineng.net", + "tjournal.ru", + "greatschools.net", + "3movs.com", + "61.com", + "directpaybiz.com", + "w3snoop.com", + "jsonline.com", + "grainger.com", + "prevention.com", + "blizzard.com", + "baseball-reference.com", + "cjn.cn", + "fasthosts.co.uk", + "kym-cdn.com", + "cuponation.in", + "cam4.fr", + "lalawan67.net", + "videovantage.co", + "elimparcial.com", + "edlen24.com", + "donya-e-eqtesad.com", + "englishforums.com", + "dom2.ru", + "cinepolis.com", + "healthtap.com", + "astroawani.com", + "autosport.com", + "finalfantasyxiv.com", + "valueclickmedia.com", + "vitaminshoppe.com", + "apartmentguide.com", + "notjustok.com", + "gratispeliculas.org", + "intporn.com", + "eroticbeauties.net", + "egloos.com", + "nujij.nl", + "godvine.com", + "punishtube.com", + "wisegeek.org", + "somuch.com", + "taz.de", + "zetaboards.com", + "gravityforms.com", + "telegraf.rs", + "odn.ne.jp", + "topeleven.com", + "peekyou.com", + "razerzone.com", + "furaffinity.net", + "mobikwik.com", + "eventful.com", + "sextube.fm", + "treccani.it", + "phatograph.com", + "feedthebot.com", + "netdoctor.co.uk", + "collarme.com", + "reformal.ru", + "hemnet.se", + "jacquieetmicheltv.net", + "9ku.com", + "kraloyun.com", + "directadmin.com", + "bongda.com.vn", + "ddizi.org", + "tedata.net", + "fb.org", + "pagemodo.com", + "satu.kz", + "nodejs.org", + "tiberiumalliances.com", + "wallst.com", + "dezeen.com", + "rense.com", + "hawkhost.com", + "vonage.com", + "zjol.com.cn", + "housefun.com.tw", + "ad1.ru", + "myredbook.com", + "rarlab.com", + "audiopoisk.com", + "pbebank.com", + "whocallsme.com", + "playground.ru", + "tnr.com", + "firstdirect.com", + "xinhua.jp", + "gmane.org", + "verseriesynovelas.com", + "broadwayworld.com", + "thefrisky.com", + "rapmls.com", + "skapiec.pl", + "wholesale-dress.net", + "alison.com", + "n4hr.com", + "prefiles.com", + "vtc.vn", + "coral.co.uk", + "azyya.com", + "smithsonianmag.com", + "ticksy.com", + "theync.com", + "pcinpact.com", + "eventbrite.co.uk", + "buenosaires.gob.ar", + "a5cee7.se", + "bazingamob.com", + "pp.cc", + "shortnews.de", + "responsinator.com", + "filepost.com", + "securefreedom.com", + "notepad-plus-plus.org", + "thekrazycouponlady.com", + "apktops.ir", + "nr2.ru", + "timinternet.it", + "man.lv", + "forexpeacearmy.com", + "plius.lt", + "vcp.ir", + "mysitecost.ru", + "yourfiledownloader.com", + "dealguardian.com", + "sql.ru", + "laravel.com", + "shape5.com", + "visa.com", + "vocus.com", + "stackthatmoney.com", + "pornicom.com", + "my.tv.sohu.com/user/cc128", + "urban-rivals.com", + "robotstxt.org", + "colorlabsproject.com", + "mg.gov.br", + "local.ch", + "adbooth.net", + "se.pl", + "al-akhbar.com", + "he.net", + "covoiturage.fr", + "sakshieducation.com", + "thisdaylive.com", + "thestudentroom.co.uk", + "pzy.be", + "designyoutrust.com", + "forbes.ua", + "fandongxi.com", + "expertreviews.co.uk", + "dream-demo.com", + "barchart.com", + "ht.ly", + "burbuja.info", + "edgecastcdn.net", + "amazinglytimedphotos.com", + "value-domain.com", + "shareaholic.com", + "promptfile.com", + "voetbalzone.nl", + "ziare.com", + "trafficbroker.com", + "coolsport.tv", + "vanguardia.com.mx", + "makeshop.jp", + "mobilism.org", + "rebelmouse.com", + "brisbanetimes.com.au", + "publimetro.com.mx", + "coolkora.com", + "realsparrow.com", + "honda.co.jp", + "dealcatcher.com", + "sinoptik.ua", + "lefeng.com", + "mamaclub.com", + "bezuzyteczna.pl", + "smn.gov.ar", + "manhub.com", + "pcgameshardware.de", + "shipstation.com", + "zive.cz", + "disneystore.com", + "despegar.com.mx", + "addictivetips.com", + "path.com", + "pathofexile.com", + "seitenreport.de", + "pardot.com", + "decathlon.fr", + "flvrunner.com", + "xat.com", + "matchendirect.fr", + "roem.ru", + "wat.tv", + "edline.net", + "znds.com", + "northwestern.edu", + "friendscout24.de", + "movoto.com", + "dengiprosto.info", + "dlisted.com", + "sports.fr", + "maxifoot.fr", + "hubspot.net", + "natalie.mu", + "tabloidpulsa.co.id", + "polki.pl", + "gamasutra.com", + "matomeantena.com", + "adwords-community.com", + "gallup.com", + "redtubenacional.com", + "mangastream.com", + "zulutrade.com", + "wunderlist.com", + "taste.com.au", + "myhosting.com", + "totalbeauty.com", + "8pic.ir", + "sltrib.com", + "88db.com", + "minikoyuncu.com", + "net4.in", + "nuevoloquo.com", + "cago365.com", + "afip.gob.ar", + "keep2share.cc", + "kw.com", + "terra.com.pe", + "profi-forex.org", + "filecloud.io", + "peliculas4.com", + "mysitemyway.com", + "ilmattino.it", + "geizhals.de", + "sheershanews.com", + "whatwpthemeisthat.com", + "teknosa.com", + "myshopping.com.au", + "teamskeet.com", + "gandalfporn.com", + "2670.com", + "peoplefinders.com", + "elo7.com.br", + "sofoot.com", + "uhaul.com", + "sagepay.com", + "unb.br", + "jhu.edu", + "barrons.com", + "hasznaltauto.hu", + "winfxstrategy.com", + "spamcop.net", + "az.pl", + "op.fi", + "istruzione.it", + "eveonline.com", + "imgnip.com", + "jjoobb.cn", + "itesm.mx", + "wptavern.com", + "kproxy.com", + "pulptastic.com", + "quidco.com", + "ekantipur.com", + "joann.com", + "whattoexpect.com", + "tomshw.it", + "internetbookshop.it", + "followerwonk.com", + "cisionpoint.com", + "cint.com", + "quondos.com", + "suntimes.com", + "saveinter.net", + "eiga.com", + "jdzj.com", + "goldprice.org", + "websitemagazine.com", + "051jk.com", + "inweb24.com", + "gamgos.ae", + "elog-ch.com", + "trovit.com.mx", + "webnode.cz", + "ethnos.gr", + "oodle.com", + "subtitulos.es", + "gameinformer.com", + "9to5mac.com", + "youwatch.org", + "ibps.in", + "biggerpockets.com", + "sport24.co.za", + "my.tv.sohu.com/user/cc133", + "capsulecrm.com", + "fuett.mx", + "poznan.pl", + "melateiran.com", + "divxtotal.com", + "aftabir.com", + "cyberlink.com", + "ulozto.cz", + "jetswap.com", + "premium.wix.com", + "eprize.com", + "huntington.com", + "careerlink.vn", + "allhyipmonitors.com", + "autorambler.ru", + "jamieoliver.com", + "minube.com", + "credomatic.com", + "oddschecker.com", + "listia.com", + "mrrebates.com", + "8684.cn", + "kabam.com", + "tripit.com", + "gry.pl", + "ecvv.com", + "utanbaby.com", + "lavoixdunord.fr", + "just-eat.co.uk", + "go-for-files.com", + "afpbb.com", + "mybb.com", + "madmoizelle.com", + "sensacine.com", + "jtl-software.de", + "worldtimeserver.com", + "sunmaker.com", + "marieforleo.com", + "123sdfsdfsdfsd.ru", + "duga.jp", + "avery.com", + "tvguide.co.uk", + "salliemae.com", + "zap.com.br", + "my.tv.sohu.com/user/cc112", + "bookmark4you.com", + "twodollarclick.com", + "encontreinarede.com", + "joomshaper.com", + "marinetraffic.com", + "feedermatrix.com", + "nowvideo.at", + "millenium.org", + "newalbumreleases.net", + "frankkern.com", + "onlineconversion.com", + "ahlalhdeeth.com", + "mediawiki.org", + "xseo.in", + "generation-nt.com", + "naftemporiki.gr", + "hardforum.com", + "hornbunny.com", + "my.tv.sohu.com/user/cc124", + "housing.com", + "vogue.es", + "safemls.net", + "cuisineaz.com", + "estekhtam.com", + "coqnu.com", + "despegar.com.ar", + "fedoraproject.org", + "france2.fr", + "autovit.ro", + "tradetang.com", + "levelupgames.uol.com.br", + "dnforum.com", + "homebank.kz", + "carnival.com", + "hzycsj.com", + "army.mil", + "bootsnipp.com", + "istockimg.com", + "po-kaki-to.com", + "qiushibaike.com", + "tsb.co.uk", + "gzmama.com", + "aitnews.com", + "tripwiremagazine.com", + "torrenty.org", + "djangoproject.com", + "chelseafc.com", + "gamesgames.com", + "herold.at", + "baur.de", + "kauppalehti.fi", + "nrelate.com", + "clickwebinar.com", + "entropay.com", + "gridserver.com", + "virginia.gov", + "tuan800.com", + "trueactivist.com", + "caras.uol.com.br", + "fontawesome.io", + "haodou.com", + "reshareworthy.com", + "royalcaribbean.com", + "crakrevenue.com", + "motika.com.mk", + "xovi.de", + "deutsche-startups.de", + "inven.co.kr", + "atrapalo.com", + "ictv.ua", + "muji.net", + "kingplayer.com", + "adworkmedia.com", + "xjtu.edu.cn", + "thenounproject.com", + "professionali.ru", + "xizhi.com", + "boo-box.com", + "customink.com", + "groupon.my", + "spartoo.com", + "bannersdontwork.com", + "ncore.cc", + "reklama5.mk", + "macrojuegos.com", + "zenhabits.net", + "te3p.com", + "utah.gov", + "telesurtv.net", + "101.ru", + "5yi.com", + "lide.cz", + "ancestry.co.uk", + "sana.sy", + "siteslike.com", + "vg247.com", + "tyzhden.ua", + "ncsu.edu", + "apherald.com", + "taaza.com", + "traffic-delivery.com", + "uuyoyo.com", + "hwupgrade.it", + "game-debate.com", + "jetpack.wordpress.com", + "anycodes.com", + "femina.hu", + "zattoo.com", + "acm.org", + "comenity.net", + "tarot.com", + "azadliq.org", + "advertiserdigital.com", + "osym.gov.tr", + "empornium.me", + "netzero.net", + "myfico.com", + "nolix.ru", + "cnwnews.com", + "springpad.com", + "ahangestan.in", + "localbitcoins.com", + "nikkei.co.jp", + "youlikehits.com", + "boyfriendtv.com", + "wampserver.com", + "elnorte.com", + "codeschool.com", + "si.edu", + "nosis.com", + "downloadhelper.net", + "global-free-classified-ads.com", + "flagmantube.com", + "elnuevodiario.com.ni", + "handelsbanken.se", + "sverigesradio.se", + "femjoy.com", + "444.hu", + "calcalist.co.il", + "asiatech.ir", + "tv.nu", + "installmac.com", + "programmableweb.com", + "fotki.com", + "vidivodo.com", + "websquash.com", + "selfgrowth.com", + "wehkamp.nl", + "amawebs.com", + "3dlat.com", + "comunidades.net", + "streamin.to", + "idbi.com", + "cshtracker.com", + "cdbaby.com", + "mycommerce.com", + "united-domains.de", + "tradingview.com", + "niniweblog.com", + "avn.info.ve", + "apa.tv", + "pinnaclesports.com", + "adafruit.com", + "ipower.com", + "lebuteur.com", + "utah.edu", + "mobeoffice.com", + "lingualeo.com", + "fuub.net", + "tvmongol.com", + "namasthetelangaana.com", + "secretsearchenginelabs.com", + "whowhatwear.com", + "hiapk.com", + "torrentz.sx", + "php.su", + "sodahead.com", + "sbb.ch", + "k12.ca.us", + "jecontacte.com", + "thehollywoodgossip.com", + "pekao24.pl", + "yourtest-india.com", + "screwfix.com", + "humoron.com", + "yiwugou.com", + "mulher.uol.com.br", + "thehackernews.com", + "shopmania.ro", + "dlink.com", + "despegar.com", + "archlinux.org", + "nielsen.com", + "rosnet.ru", + "appsapk.com", + "funfarsi.ir", + "4over.com", + "subscribe.wordpress.com", + "lewrockwell.com", + "portalnet.cl", + "antenam.info", + "letribunaldunet.fr", + "see-tube.com", + "tureng.com", + "topcashback.co.uk", + "contactcars.com", + "jeux.fr", + "khan.co.kr", + "ffffound.com", + "gmail.com", + "laprensa.com.ni", + "jizzhut.com", + "liilas.com", + "muare.vn", + "kting.cn", + "heteml.jp", + "genericsteps.com", + "searchcompletion.com", + "dwayir.com", + "dicionarioinformal.com.br", + "vcita.com", + "fitsugar.com", + "kleiderkreisel.de", + "collective-evolution.com", + "ethz.ch", + "sendreach.com", + "abidjan.net", + "islamicfinder.org", + "elong.com", + "yonhapnews.co.kr", + "seratnews.ir", + "stream2watch.me", + "content-watch.ru", + "seotechnocrat.com", + "todaoferta.uol.com.br", + "toyota.jp", + "purevid.com", + "incredimail.com", + "kia.com", + "intensedebate.com", + "kaufmich.com", + "servis24.cz", + "brusheezy.com", + "rtbtraf.com", + "tejiawang.com", + "expedia.co.in", + "food52.com", + "top-channel.tv", + "jpnn.com", + "desitorrents.com", + "faxingw.cn", + "crutchfield.com", + "seneweb.com", + "usenet.nl", + "aviasales.ru", + "nesine.com", + "appshopper.com", + "dressupgamesite.com", + "lowendtalk.com", + "muslima.com", + "v-webs.com", + "linkomanija.net", + "geniuzz.com", + "caringbridge.org", + "gooya.com", + "dospy.com", + "siol.net", + "games.co.id", + "archiveofourown.org", + "rocketlawyer.com", + "free-downloadz.net", + "pastemagazine.com", + "invest-system.net", + "el-ahly.com", + "todayhumor.co.kr", + "racingpost.com", + "stereogum.com", + "soompi.com", + "pof.com.br", + "brainabundance.com", + "fontpalace.com", + "canon.jp", + "eldeforma.com", + "sacbee.com", + "jobscore.com", + "fashionista.com", + "mywork.vn", + "thisis50.com", + "cubadebate.cu", + "healthguru.com", + "instatheme.com", + "apotheken-umschau.de", + "blizko.ru", + "tinymce.com", + "virtualbox.org", + "tehran.ir", + "pr.gov.br", + "mlmleadsystempro.com", + "resbux.com", + "komonews.com", + "childrensplace.com", + "yoyopress.com", + "cloudify.cc", + "24livenewspaper.com", + "w4.com", + "islamway.net", + "fantagazzetta.com", + "imgspice.com", + "bovada.lv", + "payback.de", + "peb.pl", + "hotnigerianjobs.com", + "dev7studios.com", + "tejaratbank.net", + "takuhai.jp", + "travelmath.com", + "cultofmac.com", + "wakeupnow.com", + "insidefacebook.com", + "mediamarkt.es", + "500wan.com", + "rumah123.com", + "contentmarketinginstitute.com", + "ed.ac.uk", + "phimvang.com", + "womansday.com", + "lookatme.ru", + "citizensbank.com", + "esam.ir", + "phandroid.com", + "canadiantire.ca", + "serienjunkies.de", + "bbspink.com", + "yaranehkala.ir", + "vesti.bg", + "el-carabobeno.com", + "smarty.net", + "6tie.com", + "antpedia.com", + "p-world.co.jp", + "camelcamelcamel.com", + "sudu.cn", + "priberam.pt", + "infoworld.com", + "myrecipes.com", + "metrodeal.com", + "widgetbox.com", + "krone.at", + "outfox.tv", + "mymp4.in", + "685wo.com", + "sadanduseless.com", + "cgd.pt", + "mrmlsmatrix.com", + "parenting.com", + "tradepub.com", + "linkvehicle.com", + "mobiledia.com", + "rghost.ru", + "xxsy.net", + "goanimate.com", + "pikacn.com", + "metropcs.com", + "byu.edu", + "bitelia.com", + "designscrazed.com", + "wpde.org", + "paheal.net", + "nulled.cc", + "hmv.co.jp", + "openx.com", + "hyundai.com", + "vvvdj.com", + "reason.com", + "livesports.pl", + "polskieradio.pl", + "oszone.net", + "chinhphu.vn", + "performancehorizon.com", + "internetretailer.com", + "skoob.com.br", + "mediaworld.it", + "imgrind.com", + "bancopopular.es", + "seogadget.ru", + "m6.fr", + "rongbay.com", + "niazpardaz.com", + "rewity.com", + "ssense.com", + "sky-fire.com", + "flibusta.net", + "eat24hours.com", + "lotto.pl", + "cnw.com.cn", + "takvim.com.tr", + "englishtown.com", + "letudiant.fr", + "peliculasmas.com", + "askme.com", + "linksmanagement.com", + "webseoanalytics.com", + "presse-citron.net", + "eldiariodeamerica.com", + "instantshift.com", + "ampxchange.com", + "xe.gr", + "experts-exchange.com", + "sportcategory.com", + "sharecash.org", + "linksys.com", + "yninfo.com", + "onlinelic.in", + "aflamneek.com", + "22.cn", + "ubuntu-fr.org", + "cjs.com.cn", + "mp3clan.com", + "grani.ru", + "zen-cart.com", + "shippingchina.com", + "jsfor.net", + "wanadoo.es", + "zurker.com", + "chotot.vn", + "masaladesi.com", + "insight.ly", + "trivago.de", + "neue-sexpartner.com", + "boe.es", + "military.com", + "moonbasa.com", + "langlaoda.com", + "tnooz.com", + "zacks.com", + "liverpool.com.mx", + "internic.net", + "ecuavisa.com", + "autonews.ru", + "mcafeesecure.com", + "peopleclick.com", + "madthumbs.com", + "hot-live-chat.com", + "sugarcrm.com", + "notonthehighstreet.com", + "linio.com.co", + "smartbrief.com", + "auto-motor-und-sport.de", + "kyivpost.com", + "instantteleseminar.com", + "dillards.com", + "5i5j.com", + "runtastic.com", + "danawa.com", + "ladunliadi.blogspot.com", + "getnews.jp", + "romedic.ro", + "rodfile.com", + "instyle.com", + "swoodoo.com", + "bannersnack.com", + "evolife.cn", + "ktonanovenkogo.ru", + "office.com", + "zamalekfans.com", + "magiran.com", + "ithome.com", + "ktrmr.com", + "spoonful.com", + "olleh.com", + "bgr.in", + "industrialthemes.com", + "adbkm.com", + "tsa-algerie.com", + "bloggang.com", + "mojang.com", + "geoiptool.com", + "autocarindia.com", + "ajansspor.com", + "onmeda.de", + "midwayusa.com", + "alicdn.com", + "sportdog.gr", + "ideeli.com", + "haspa.de", + "gladbux.com", + "porn-star.com", + "experiandirect.com", + "yicheshi.com", + "acervoamador.com", + "designwall.com", + "balkanweb.com", + "response.jp", + "nettoos.com", + "beruby.com", + "manageyourloans.com", + "kidstaff.com.ua", + "indeed.co.za", + "monoprice.com", + "chsi.com.cn", + "boorsekala.com", + "mstaml.com", + "trafficvance.com", + "tatatele.in", + "startbootstrap.com", + "graphicburger.com", + "ad-vid-webs.com", + "jp.dk", + "one.co.il", + "athensvoice.gr", + "payu2blog.com", + "epidemz.net", + "dondominio.com", + "doviz.com", + "disquscdn.com", + "work.ua", + "vsemayki.ru", + "ikyu.com", + "songlyrics.com", + "onlinebank.com", + "thebump.com", + "travian.com.tr", + "java2s.com", + "twittmate.com", + "ignou.ac.in", + "relink.us", + "4738.com", + "printvenue.com", + "tekstowo.pl", + "natro.com", + "dengi-tut.info", + "freeservers.com", + "inbound.org", + "free-press-release.com", + "mydirtyhobby.com", + "gamefly.com", + "overclockers.co.uk", + "eci.nic.in", + "196.1.211.6", + "versus.com", + "batdongsan.com.vn", + "vrisko.gr", + "albayan.ae", + "trendcounter.com", + "500.com", + "pagomiscuentas.com", + "wankoz.com", + "duote.com", + "javascript.ru", + "chevrolet.com", + "coach.com", + "decidatriunfar.net", + "rememberthemilk.com", + "virginmobileusa.com", + "google.com.jm", + "ucm.es", + "ninja.co.jp", + "rqzao.com", + "periodistadigital.com", + "heritage.org", + "momtubesex.xxx", + "vocabulary.com", + "kupujemprodajem.com", + "e-cigarette-forum.com", + "paxum.com", + "ilpost.it", + "abercrombie.com", + "pearsonmylabandmastering.com", + "echoecho.com", + "expedia.co.jp", + "ticketfly.com", + "colostate.edu", + "sjtu.edu.cn", + "seobudget.ru", + "gigabyte.com", + "gsmspain.com", + "misr5.com", + "enterfactory.com", + "ciao.de", + "wuv.de", + "dagospia.com", + "slashgear.com", + "urdupoint.com", + "themepunch.com", + "travian.com", + "discuz.com", + "pixroute.com", + "otzovik.com", + "ubergizmo.com", + "edreams.fr", + "internet-positif.org", + "asmcentral.com", + "copadomundo.uol.com.br", + "adjal.com", + "gotomypc.com", + "partycity.com", + "leggo.it", + "blogosfere.it", + "teamcoco.com", + "pricedealsindia.com", + "glennbeck.com", + "allyou.com", + "china.org.cn", + "xitek.com", + "photoshopessentials.com", + "infopraca.pl", + "boardingarea.com", + "geni.com", + "rotaban.ru", + "foxtab.com", + "sadistic.pl", + "socialbro.com", + "tradesparq.com", + "itp.ne.jp", + "swefilmer.com", + "jobsite.co.uk", + "topwar.ru", + "yemeksepeti.com", + "tokfm.pl", + "businessinsider.com.au", + "moe.gov.eg", + "swedbank.lt", + "jxgdw.com", + "bayern.de", + "alistapart.com", + "bebinin.com", + "annunci69.it", + "plimus.com", + "cuevana2.tv", + "viamichelin.fr", + "whatsmydns.net", + "tarad.com", + "hostgator.com.br", + "hasbro.com", + "hi-chic.com", + "driverscollection.com", + "onetad.com", + "game2.com.cn", + "elitepvpers.com", + "tvuol.uol.com.br", + "caclubindia.com", + "diariocorreo.pe", + "c-rewards.com", + "business.gov.au", + "chooseauto.com.cn", + "timetrade.com", + "alternate.de", + "syracuse.com", + "advanceautoparts.com", + "shopware.de", + "newpct.com", + "proranktracker.com", + "cex.io", + "thebot.net", + "tradekorea.com", + "legifrance.gouv.fr", + "cpan.org", + "rightel.ir", + "247wallst.com", + "goldentowns.com", + "bankifsccode.com", + "color-hex.com", + "consumerist.com", + "juegosdiarios.com", + "on24.com", + "stockfreeimages.com", + "m4.cn", + "sponsoredtweets.com", + "open.ac.uk", + "bilyoner.com", + "lexisnexis.com", + "gsu.edu", + "popscreen.com", + "stiftung-warentest.de", + "education.com", + "leckerficken.de", + "hostinger.ru", + "staples.ca", + "greenpeace.org", + "mes-meilleurs-films.fr", + "delish.com", + "register.it", + "idiva.com", + "static.squarespace.com", + "ololo.fm", + "basketball-reference.com", + "tro-ma-ktiko.blogspot.gr", + "arabianbusiness.com", + "avianca.com", + "watchuseek.com", + "6play.fr", + "kora-online.tv", + "armagedomfilmes.biz", + "531314.com", + "craigslist.co.uk", + "inchallah.com", + "bongdaso.com", + "toprankblog.com", + "k12.com", + "itimes.com", + "html.net", + "utsandiego.com", + "gyakorikerdesek.hu", + "viddyhddownload.com", + "wannonce.com", + "qudong.com", + "ibicn.com", + "w3resource.com", + "socialsecurity.gov", + "tvn.pl", + "momsexclipz.com", + "fstoppers.com", + "kafeteria.pl", + "boulanger.fr", + "guj.nic.in", + "divaina.com", + "desktopnexus.com", + "topseda.ir", + "payumoney.com", + "klicktel.de", + "foxtv.es", + "cuatro.com", + "publix.com", + "astrosage.com", + "kompass.com", + "web-hosting.com", + "nus.edu.sg", + "jamiiforums.com", + "lancers.jp", + "peixeurbano.com.br", + "buysub.com", + "yp.com", + "enstage.com", + "6eat.com", + "1ent.com.cn", + "unionpaysecure.com", + "pcgames.de", + "ccidcom.com", + "lavenir.net", + "novaposhta.ua", + "hotpornshow.com", + "telebank.ru", + "avaz.ba", + "doit.com.cn", + "setlinks.ru", + "zagat.com", + "sportmaster.ru", + "utarget.ru", + "lingualeo.ru", + "compucalitv.com", + "anastasiadate.com", + "webmaster-rank.info", + "fastcoexist.com", + "lavasoft.com", + "ppvguru.com", + "greatist.com", + "knack.be", + "wowwiki.com", + "ilcorsaronero.info", + "jword.jp", + "750g.com", + "pantipmarket.com", + "top.de", + "photofunia.com", + "dryicons.com", + "alweeam.com.sa", + "dnfight.com", + "wpolityce.pl", + "vimeocdn.com", + "visit-x.net", + "takungpao.com", + "perfectworld.eu", + "kat.ph", + "hefei.cc", + "cmse.ru", + "virginia.edu", + "fudan.edu.cn", + "sethgodin.typepad.com", + "caracaschronicles.com", + "18qt.com", + "be2.com.br", + "rakuten.tw", + "bukkit.org", + "miss-no1.com", + "nts.org.pk", + "ulozto.net", + "mixpanel.com", + "glavred.info", + "vui.vn", + "mindspark.com", + "worldmarket.com", + "walmart.com.mx", + "deperu.com", + "taxact.com", + "m5zn.com", + "navy.mil", + "hibapress.com", + "cpasbien.com", + "interjet.com.mx", + "llbean.com", + "kenrockwell.com", + "japan-guide.com", + "financialpost.com", + "rankingsinstitute.com", + "master-x.com", + "pandawill.com", + "marketingweek.co.uk", + "freeforums.org", + "ysear.ch", + "pokerstars.com", + "wired.co.uk", + "amino.dk", + "rapidcrush.com", + "vivanuncios.com", + "blogsvertise.com", + "cwb.gov.tw", + "piter.tv", + "gistmania.com", + "topdocumentaryfilms.com", + "shoghlanty.com", + "bankmillennium.pl", + "burberry.com", + "youporngay.com", + "webresourcesdepot.com", + "beachbody.com", + "asstr.org", + "pwc.com", + "competitor.com", + "rss2search.com", + "vocuspr.com", + "behindthename.com", + "moniker.com", + "jayde.com", + "opensuse.org", + "poemhunter.com", + "toranoana.jp", + "interaksyon.com", + "aeon.co.jp", + "gungho.jp", + "fgov.be", + "xpg.uol.com.br", + "logic-immo.com", + "worldsex.com", + "watchonlineseries.eu", + "google.co.mz", + "discas.net", + "chmail.ir", + "consumercomplaints.in", + "allstate.com", + "twelveskip.com", + "ibibo.com", + "noelshack.com", + "bonprix.ru", + "directupload.net", + "equipobimlatino.com", + "weather2umbrella.com", + "aibang.com", + "blog.wordpress.com", + "turner.com", + "mediapart.fr", + "bloog.pl", + "monotaro.com", + "online.net", + "bodmillenium.com", + "distancesfrom.com", + "naosalvo.com.br", + "ap7am.com", + "focus.ua", + "cokeandpopcorn.ch", + "qwant.com", + "megaplan.ru", + "sinhvienit.net", + "dream-marriage.com", + "voicefive.com", + "forex.com.cn", + "xabbs.com", + "netmums.com", + "tuxboard.com", + "cs-cart.com", + "bitcoin.it", + "boblil.com", + "nirsoft.net", + "neopets.com", + "spin.com", + "costco.ca", + "mirraw.com", + "ara.cat", + "klamm.de", + "glam.com", + "guenstiger.de", + "citruspay.com", + "dnslink.com", + "guyism.com", + "shopotam.ru", + "tim.it", + "3suisses.fr", + "gooroops.com", + "kau.edu.sa", + "ukbusinessforums.co.uk", + "gree.jp", + "komikid.com", + "redakcja.pl", + "qpic.cn", + "voyeurweb.com", + "mathworks.com", + "monster.fr", + "seniat.gob.ve", + "computrabajo.com.co", + "xilu.com", + "madrid.org", + "ostraining.com", + "bharian.com.my", + "telegram.org", + "sumofus.org", + "whosdatedwho.com", + "moreofit.com", + "meetic.it", + "femina.mk", + "itworld.com", + "indiaglitz.com", + "cheatcc.com", + "filmfanatic.com", + "the-bux.net", + "very.co.uk", + "fetishshrine.com", + "speakingtree.in", + "iu.edu", + "modern.az", + "tak.ru", + "internetmarketingninjas.com", + "example.com", + "sportgeza.hu", + "wrestlezone.com", + "jameda.de", + "caikuu.com", + "gioco.it", + "yourtv.com.au", + "euro.com.pl", + "anz.co.nz", + "daft.ie", + "tirerack.com", + "vivareal.com.br", + "definebabe.com", + "ittefaq.com.bd", + "lastfm.es", + "zocalo.com.mx", + "dpd.de", + "somethingawful.com", + "virgin.com", + "shtyle.fm", + "viralforest.com", + "dntrck.com", + "its-mo.com", + "q.cc", + "kinghost.net", + "elpais.com.co", + "joomlaspanish.org", + "sopitas.com", + "wallbase.cc", + "cpalead.com", + "torrentfunk.com", + "bizcom.com.ru", + "quizony.com", + "rcgroups.com", + "spring.io", + "kuaidi100.com", + "xatakandroid.com", + "mlstatic.com", + "borsaat.com", + "jagobd.com", + "cosmiq.de", + "crucial.com", + "cdstm.cn", + "betclic.fr", + "telewebion.com", + "exhentai.org", + "gnezdo.ru", + "cebupacificair.com", + "fusionmls.com", + "shafaf.ir", + "ticketmaster.co.uk", + "adslzone.net", + "k8.cn", + "recipdonor.com", + "in.gov", + "mforos.com", + "asscj.com", + "linkconnector.com", + "ohfreesex.com", + "cntraveler.com", + "scambioetico.org", + "ladenzeile.de", + "ntn24.com", + "beyazperde.com", + "motherearthnews.com", + "yupptv.com", + "220tube.com", + "fizzle.co", + "lakii.com", + "deutsche-wirtschafts-nachrichten.de", + "saaid.net", + "bimlatino.com", + "verisign.com", + "funpatogh.com", + "matadornetwork.com", + "seatguru.com", + "lululemon.com", + "uzise.com", + "networkworld.com", + "hottube.me", + "jobsdb.com.hk", + "life.hu", + "bonusvid.com", + "incometaxindiaefiling.gov.in", + "ey.com", + "alraimedia.com", + "allposters.com", + "authorityroi.com", + "bmwusa.com", + "ebaystatic.com", + "entertainmentcrave.com", + "ehow.com.br", + "prisonplanet.com", + "elnashra.com", + "email.cz", + "yes123.com.tw", + "uitzendinggemist.nl", + "mindtools.com", + "atv.com.tr", + "shoes.net.cn", + "joomlaportal.de", + "themoneyconverter.com", + "meteomedia.com", + "bankaustria.at", + "advertise.com", + "trafficswarm.com", + "aztecaporno.com", + "fourseasons.com", + "movie-blog.org", + "govdelivery.com", + "didigames.com", + "invideo.biz", + "linux.org.ru", + "omelete.uol.com.br", + "babybus.cn", + "oursogo.com", + "ero-an.com", + "mediafax.ro", + "football.ua", + "sii.cl", + "dreamamateurs.com", + "cheaperthandirt.com", + "iplocation.net", + "insideview.com", + "hihe.vn", + "stop-sex.com", + "fubiz.net", + "url.org", + "greenmangaming.com", + "pornup.me", + "indofeed.com", + "forumhouse.ru", + "restaurant.com", + "key.com", + "windstream.net", + "brightedge.com", + "foreignpolicy.com", + "noticiasdatv.uol.com.br", + "logopond.com", + "datemule.com", + "webrootanywhere.com", + "stomp.com.sg", + "trthaber.com", + "bitterstrawberry.com", + "turbo.az", + "kbs.co.kr", + "joblo.com", + "seatimes.com.vn", + "kuwait.tt", + "258.com", + "kp.ua", + "moviesmobile.net", + "flamingtext.com", + "youngt.com", + "etradenow.cn", + "copart.com", + "tuanweihui.com", + "zinkwaphd.com", + "adsl.free.fr", + "infomaniak.ch", + "re-direcciona.me", + "mejorenvo.com", + "instantservice.com", + "harpersbazaar.com", + "colorado.edu", + "belboon.com", + "telelistas.net", + "photofans.cn", + "51seer.com", + "danjur.com", + "wunderweib.de", + "extratorrentlive.com", + "serpfox.com", + "androidkade.com", + "raja.ir", + "tampabay.com", + "ser-mujer.org", + "imasters.com.br", + "coachfactory.com", + "mojopages.com", + "makezine.com", + "forexschoolonline.com", + "bochk.com", + "siamsport.co.th", + "mdpr.jp", + "serialssolutions.com", + "videosexarchive.com", + "enlnks.com", + "cision.com", + "scratchinginfo.com", + "skidrowgames.net", + "optimizely.com", + "video2brain.com", + "ariva.de", + "audiomack.com", + "pimproll.com", + "house.gov", + "acs.org", + "pijamasurf.com", + "autoc-one.jp", + "southcn.com", + "vesti.az", + "300mbfilms.com", + "speedmystream.com", + "wooservers.com", + "siteliner.com", + "tinmoi.vn", + "extremepornvideos.com", + "cqsq.com", + "hello-today.com", + "turkcealtyazi.org", + "tvyo.com", + "dealmoon.com", + "openenglish.com", + "golfchannel.com", + "irancloob.com", + "ebs.in", + "portalcorreio.uol.com.br", + "beemp3s.org", + "condenast.com", + "mtwebcenters.com.tw", + "hatelabo.jp", + "pathci.net", + "mamsy.ru", + "noticiasdelmundo.org", + "guru3d.com", + "logomaker.com", + "adhitz.com", + "swiki.jp", + "turnitin.com", + "patoghu.com", + "carnaval.uol.com.br", + "cancer.org", + "clickprime8.com", + "fahrinfo-berlin.de", + "unisender.com", + "flashkhor.com", + "goodfon.ru", + "ethiotube.net", + "cunbang.com", + "ti-da.net", + "1phads.com", + "bien.hu", + "home77.com", + "mbabycare.com", + "vudu.com", + "fantasy8.com", + "landrover.com", + "opencartforum.ru", + "antena3.ro", + "getapp.com", + "torlock.com", + "wind.it", + "expatads.com", + "bloglines.com", + "sexytube.me", + "frontier.com", + "asandownload.com", + "groupon.pl", + "storesonlinepro.com", + "brands4friends.de", + "infogr.am", + "webeffector.ru", + "google.com.mm", + "ppchero.com", + "privacystar.com", + "ocregister.com", + "liex.ru", + "kopatheme.com", + "toggl.com", + "raywenderlich.com", + "hecha.cn", + "vpls.net", + "manpianyi.com", + "1881.no", + "scbeasy.com", + "plex.tv", + "selfridges.com", + "anumex.com", + "postplanner.com", + "thisiscolossal.com", + "you-will-date.com", + "idealist.org", + "emarketer.com", + "anibis.ch", + "dansmovies.com", + "footballitarin.com", + "fragrantica.com", + "10minutemail.com", + "ntu.edu.tw", + "clasicooo.com", + "onsugar.com", + "vr-zone.com", + "walkerplus.com", + "easymobilerecharge.com", + "cz001.com.cn", + "sportsseoul.com", + "inazumanews2.com", + "marketingdirecto.com", + "chartboost.com", + "unblocked.co", + "rhymezone.com", + "portalinmobiliario.com", + "afrointroductions.com", + "transunion.com", + "panorama.it", + "powershow.com", + "avval.ir", + "onlinemeetingnow.com", + "xoom.com", + "wnbc.com", + "tlen.pl", + "jetairways.com", + "vtb24.ru", + "offcn.com", + "crx7601.com", + "adverts.ie", + "kasserver.com", + "minhavida.com.br", + "jkpj.com", + "123contactform.com", + "imgtiger.com", + "folha.com.br", + "npcgo.com", + "semprot.com", + "moonfruit.com", + "jizzbell.com", + "online-stopwatch.com", + "allhiphop.com", + "hackaday.com", + "digitalplayground.com", + "careers24.com", + "cwq.com", + "wondershare.com", + "filesend.to", + "puromarketing.com", + "workopolis.com", + "mudainodocument.com", + "home.ne.jp", + "shorte.st", + "tanea.gr", + "sporza.be", + "vgorode.ua", + "google.co.zm", + "argumentua.com", + "3m.com", + "komputerswiat.pl", + "bongdaplus.vn", + "supersonicads.com", + "soundclick.com", + "lollipop-network.com", + "lpcloudsvr304.com", + "313.cn", + "rtl2.de", + "ts.cn", + "mglradio.com", + "diynetwork.com", + "komikcaps.net", + "all-that-is-interesting.com", + "ultipro.com", + "frombar.com", + "sitedeals.nl", + "codingforums.com", + "kansascity.com", + "twtrland.com", + "cengage.com", + "lolspotsarticles.com", + "jackthreads.com", + "jobstreet.com.sg", + "diffen.com", + "eme2000.com", + "superbru.com", + "droid-life.com", + "itb-berlin.de", + "estibot.com", + "yourepeat.com", + "112.ua", + "70e.com", + "grid.mk", + "cromaretail.com", + "jobstreet.com.ph", + "gloadmarket.com", + "gemius.com", + "huuto.net", + "vogue.it", + "divxonline.info", + "mytoys.de", + "cashnhits.com", + "minkchan.com", + "talabat.com", + "helpareporter.com", + "grandascent.com", + "catalunyacaixa.com", + "sparwelt.de", + "tvboxnow.com", + "imagecurl.org", + "coocan.jp", + "eatingwell.com", + "truelife.com", + "binaryoptionsbrands.com", + "foxsportsla.com", + "jjshouse.com", + "makaan.com", + "yamaha.com", + "activerain.com", + "excelforum.com", + "mobinnet.ir", + "kasikornbankgroup.com", + "googleplex.com", + "medianama.com", + "fullsail.edu", + "makeupandbeauty.com", + "marketwired.com", + "beachbodycoach.com", + "taloussanomat.fi", + "youngliving.us", + "dana.ir", + "adsgostar.com", + "esquire.com.cn", + "meyerweb.com", + "positivemed.com", + "harvestapp.com", + "tim.com.br", + "sumorobo.net", + "zbigz.com", + "schulferien.org", + "quzhao.com", + "tsinghua.edu.cn", + "vogue.com", + "ecrater.com", + "internetbs.net", + "oper.ru", + "kingstone.com.tw", + "olympic.org", + "code.org", + "wp.tv", + "adchakra.net", + "passarela.com.br", + "damn.com", + "spn.com.cn", + "sacitaslan.com", + "uplod.ir", + "kununu.com", + "englishclub.com", + "ibood.com", + "buzzfed.com", + "peopledaily.com.cn", + "vivastreet.it", + "adshostnet.com", + "npmjs.org", + "lovethatsex.com", + "garena.tw", + "hq-sex-tube.com", + "tvsubtitles.net", + "trademarkia.com", + "96down.com", + "morhipo.com", + "chuguoqu.com", + "free-css.com", + "getsecuredfiles.com", + "justia.com", + "paypal-community.com", + "wezoner.com", + "google-analytics.com", + "outsideonline.com", + "simplymeasured.com", + "wattsupwiththat.com", + "vancl.com", + "leadership.ng", + "plaisio.gr", + "fastcocreate.com", + "fewo-direkt.de", + "dedecms.com", + "bostonherald.com", + "elmundo.com.ve", + "idhostinger.com", + "animaljam.com", + "infoq.com", + "exler.ru", + "leprosorium.ru", + "avis.com", + "ulusalpost.com", + "namnak.com", + "goindigo.in", + "activeboard.com", + "fatsecret.com", + "4umf.com", + "porn-w.org", + "sarkariexam.co.in", + "patagonia.com", + "3u5.net", + "roshd.ir", + "netechangisme.com", + "ojogo.pt", + "ujipin.com", + "topuniversities.com", + "consequenceofsound.net", + "no-ip.biz", + "webdesignerwall.com", + "fin24.com", + "openculture.com", + "pbh2.com", + "dota2.com", + "secureupload.eu", + "alfajertv.com", + "canlidizihdtv.com", + "muffingroup.com", + "imageforum.com", + "paris.fr", + "kaufda.de", + "pelis24.com", + "ixian.cn", + "ebenpagan.com", + "sportsauthority.com", + "wish.com", + "ganeshaspeaks.com", + "meritnation.com", + "ijie.com", + "vodafone.com.tr", + "trovit.es", + "farsisubtitle.com", + "musawvir.com", + "resellerratings.com", + "soyentrepreneur.com", + "samakal.net", + "dietnavi.com", + "55tuan.com", + "elcomercio.com", + "hereisthecity.com", + "buienradar.nl", + "arenabg.com", + "indiansexstories.net", + "slideboom.com", + "findsection.net", + "rankrecon.com", + "e-radio.gr", + "cnn.co.jp", + "gucci.com", + "natura.net", + "googlesyndication.com", + "advancedwebranking.com", + "lib.ru", + "sfora.pl", + "uci.edu", + "haaretz.co.il", + "ing.be", + "yokboylebirsey.com.tr", + "myfact.tv", + "yenibiris.com", + "stuff.tv", + "dn.pt", + "whydontyoutrythis.com", + "nediyor.com", + "worldoftanks.asia", + "megamillions.com", + "elemanonline.net", + "computrabajo.com.pe", + "arxiv.org", + "livescore.tv", + "aintitcool.com", + "girlschannel.net", + "madbid.com", + "tadawul.com.sa", + "liqpay.com", + "softonic.jp", + "alhilal.com", + "bizpowa.com", + "thatguywiththeglasses.com", + "yemeneconomist.com", + "pazar3.mk", + "repai.com", + "1jux.net", + "solvusoft.com", + "yotpo.com", + "cuantocabron.com", + "ba.gov.br", + "arin.net", + "loginza.ru", + "homebank.ro", + "0755car.com", + "carscoops.com", + "antarvasna.com", + "tizag.com", + "offerpop.com", + "techmeme.com", + "ipress.ua", + "emuparadise.me", + "sexvidx.com", + "whatseek.com", + "become.co.jp", + "snapfish.com", + "gsis.gr", + "speedyshare.com", + "uncrate.com", + "doda.jp", + "straighttalk.com", + "rsjoomla.com", + "vozpopuli.com", + "gotprint.net", + "finansbank.com.tr", + "chinatrust.com.tw", + "stratoserver.net", + "icnetwork.co.uk", + "getpopcornti.me", + "pornper.com", + "3dmgame.com", + "squirt.org", + "mbusa.com", + "katespade.com", + "skat.dk", + "radiko.jp", + "dexonline.ro", + "bicaps.net", + "bitsoma.com", + "brasil247.com", + "alittlemarket.com", + "bd-pratidin.com", + "amorincontro.com", + "bigxvideos.com", + "nissan.co.jp", + "incomediary.com", + "theranking.com", + "el7l.co", + "webcamtoy.com", + "steamgifts.com", + "miles-and-more.com", + "szonline.net", + "blocked-website.com", + "kaztorka.org", + "avforums.com", + "indulgy.com", + "extendcp.co.uk", + "yonkis.com", + "ad1111.com", + "unionbankph.com", + "job.com", + "samanepay.com", + "aceona.com", + "kar.nic.in", + "tipico.com", + "sa.ae", + "froma.com", + "songza.com", + "calciomercato.com", + "besaba.com", + "x-rates.com", + "arsys.es", + "sisal.it", + "amateur.tv", + "designyourway.net", + "alpha.gr", + "nimble.com", + "livefreefun.com", + "wipo.int", + "webssearches.com", + "vodjk.com", + "91tiger.com", + "swisscom.com", + "tuvaro.com", + "parsonline.net", + "uolhost.com.br", + "cnwest.com", + "roksa.pl", + "socialmention.com", + "unibytes.com", + "screamingfrog.co.uk", + "owncloud.org", + "jacquielawson.com", + "openclipart.org", + "zimbra.com", + "poczta-polska.pl", + "hayah.cc", + "ringtonematcher.com", + "emai.com", + "wealink.com", + "celebzen.com", + "pichost.me", + "rediffmail.com", + "dinos.co.jp", + "netvasco.com.br", + "meilele.com", + "career.ru", + "signupgenius.com", + "screencast-o-matic.com", + "nav.no", + "reactiongifs.com", + "italki.com", + "mo.gov", + "bandsintown.com", + "elwatan.com", + "jdate.com", + "quiente.net", + "skynet.be", + "googlekeywordtool.com", + "nubilefilms.com", + "memedeportes.com", + "rewardingzonez.com", + "litmus.com", + "iyaxin.com", + "collegeconfidential.com", + "semperplugins.com", + "sofort.com", + "ineedhits.com", + "live24.gr", + "acronymfinder.com", + "jobui.com", + "9show.com", + "thedirty.com", + "tsutaya.co.jp", + "9787.com", + "brighthouse.com", + "boxingscene.com", + "indeed.com.br", + "morazzia.com", + "privatejobshub.blogspot.in", + "searchenginepeople.com", + "pozdravok.ru", + "watchstore.com.cn", + "rts.ch", + "pluska.sk", + "villagevoice.com", + "sprintrade.com", + "conversionxl.com", + "evsuite.com", + "bci.cl", + "lampsplus.com", + "gossipcop.com", + "valuecommerce.com", + "zalora.co.id", + "fastshop.com.br", + "hockeysfuture.com", + "bootply.com", + "chinavasion.com", + "menards.com", + "kinobar.net", + "tokyodisneyresort.jp", + "knownhost.com", + "ozbargain.com.au", + "olx.com.ve", + "sgk.gov.tr", + "berliner-sparkasse.de", + "bancogalicia.com.ar", + "joomla-master.org", + "augsburger-allgemeine.de", + "kroger.com", + "pulse.ng", + "putlocker.com", + "jrtj.com", + "tapuz.co.il", + "madhyamam.com", + "donews.com", + "showhaber.com", + "allxnxx.com", + "uned.es", + "adsharebux.com", + "d-addicts.com", + "banki.ir", + "levi.com", + "e97527f0.se", + "entertainmentwise.com", + "xvideos-field.com", + "naver.net", + "urbia.de", + "fashionsfriend.com", + "emuch.net", + "yelp.fr", + "metatube.com", + "sina.com.hk", + "omb100.com", + "grameenphone.com", + "stylebook.de", + "funny-games.biz", + "googlewebmastercentral.blogspot.com", + "vogella.com", + "surveymonkey.net", + "forex4you.org", + "mex.tl", + "online-fish-games.com", + "millenniumbcp.pt", + "fileom.com", + "friendorfollow.com", + "downloads.nl", + "typekit.com", + "gongkong.com", + "iqoption.com", + "tori.fi", + "smartsource.com", + "1616.net", + "1shoppingcart.com", + "novosti.rs", + "yofond.com", + "idokep.hu", + "motor.ru", + "xbiao.com", + "colorcombos.com", + "pway.cn", + "egynews.net", + "elshaab.org", + "telus.com", + "olx.com.co", + "cctvcj.com", + "kpopstarz.com", + "razorsocial.com", + "ncrypt.in", + "zonetheme.com", + "gumroad.com", + "e-shop.gr", + "kugli.com", + "levif.be", + "bookfi.org", + "globalresearch.ca", + "sas.com", + "cloudaccess.net", + "uplooder.net", + "exposedwebcams.com", + "1prime.ru", + "webappers.com", + "crocko.com", + "remax.com", + "ikco.com", + "dinside.no", + "winbank.gr", + "mijnwoordenboek.nl", + "weatherzone.com.au", + "allday2.com", + "sunat.gob.pe", + "getit.in", + "entrance-exam.net", + "i2ya.com", + "tlife.gr", + "qyer.com", + "calibre-ebook.com", + "internetcorkboard.com", + "unitedhyipleague.com", + "spyrestudios.com", + "metal-archives.com", + "aeromexico.com", + "cv-library.co.uk", + "bdr1.net", + "aremo.com.br", + "blip.tv", + "afilio.com.br", + "renewalcoupons.com", + "proxfree.com", + "magix.com", + "jobs.net", + "souism.com", + "adtcstrk.com", + "goodhousekeeping.com", + "lastfm.ru", + "sharecare.com", + "porn.xxx", + "deathandtaxesmag.com", + "unocero.com", + "wer-kennt-wen.de", + "hotfrog.com", + "intraship.de", + "press24.mk", + "yournewscorner.com", + "rbc.com", + "eventure.com", + "minglebox.com", + "legendas.tv", + "drp.su", + "voicefrom.me", + "venezuelaaldia.com", + "cooks.com", + "numbeo.com", + "chilehardware.cl", + "cryptocointalk.com", + "venusfactor.com", + "recruitmentcareer.in", + "larazon.es", + "sptechs.com", + "asx.com.au", + "ahlife.com", + "st001.com", + "actudesfinances.info", + "talkarcades.com", + "nitori-net.jp", + "chinahr.com", + "websitetestlink.com", + "hypem.com", + "demorgen.be", + "sharefile.com", + "prensalibre.com", + "eldia.com.ar", + "billiger.de", + "3docean.net", + "streamsend.com", + "tuaw.com", + "portafolio.co", + "trivago.com", + "olxpix.com", + "geotrust.com", + "torrentgun.net", + "enha.kr", + "pcastuces.com", + "t.cn", + "dimsemenov.com", + "cam4.nl", + "basspro.com", + "macupdate.com", + "strawpoll.me", + "elintransigente.com", + "homeaway.co.uk", + "my.ecwid.com", + "graphpaperpress.com", + "architonic.com", + "flowplayer.org", + "syshl.com", + "18schoolgirlz.com" + }); + + return urls; +} diff --git a/src/boost/libs/beast/example/http/client/crawl/urls_large_data.hpp b/src/boost/libs/beast/example/http/client/crawl/urls_large_data.hpp new file mode 100644 index 000000000..06be4acd9 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/crawl/urls_large_data.hpp @@ -0,0 +1,18 @@ +// +// 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_EXAMPLE_HTTP_CLIENT_CRAWL_URLS_LARGE_DATA_HPP +#define BOOST_BEAST_EXAMPLE_HTTP_CLIENT_CRAWL_URLS_LARGE_DATA_HPP + +#include <vector> + +std::vector<char const*> const& +urls_large_data(); + +#endif diff --git a/src/boost/libs/beast/example/http/client/sync-ssl/CMakeLists.txt b/src/boost/libs/beast/example/http/client/sync-ssl/CMakeLists.txt new file mode 100644 index 000000000..0e0fbea07 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/sync-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/http/client/sync-ssl "/") + + add_executable (http-client-sync-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/root_certificates.hpp + Jamfile + http_client_sync_ssl.cpp + ) + + set_property(TARGET http-client-sync-ssl PROPERTY FOLDER "example-http-client") + + target_link_libraries (http-client-sync-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/http/client/sync-ssl/Jamfile b/src/boost/libs/beast/example/http/client/sync-ssl/Jamfile new file mode 100644 index 000000000..c1fabf479 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/sync-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe http-client-sync-ssl : + http_client_sync_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/client/sync-ssl/http_client_sync_ssl.cpp b/src/boost/libs/beast/example/http/client/sync-ssl/http_client_sync_ssl.cpp new file mode 100644 index 000000000..726c84204 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/sync-ssl/http_client_sync_ssl.cpp @@ -0,0 +1,128 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP SSL client, synchronous +// +//------------------------------------------------------------------------------ + +#include "example/common/root_certificates.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/ssl/error.hpp> +#include <boost/asio/ssl/stream.hpp> +#include <cstdlib> +#include <iostream> +#include <string> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +namespace ssl = net::ssl; // from <boost/asio/ssl.hpp> +using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Performs an HTTP GET and prints the response +int main(int argc, char** argv) +{ + try + { + // Check command line arguments. + if(argc != 4 && argc != 5) + { + std::cerr << + "Usage: http-client-sync-ssl <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" << + "Example:\n" << + " http-client-sync-ssl www.example.com 443 /\n" << + " http-client-sync-ssl www.example.com 443 / 1.0\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const target = argv[3]; + int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11; + + // The io_context is required for all I/O + net::io_context ioc; + + // The SSL context is required, and holds certificates + ssl::context ctx(ssl::context::tlsv12_client); + + // This holds the root certificate used for verification + load_root_certificates(ctx); + + // Verify the remote server's certificate + ctx.set_verify_mode(ssl::verify_peer); + + // These objects perform our I/O + tcp::resolver resolver(ioc); + beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx); + + // Set SNI Hostname (many hosts need this to handshake successfully) + if(! SSL_set_tlsext_host_name(stream.native_handle(), host)) + { + beast::error_code ec{static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()}; + throw beast::system_error{ec}; + } + + // Look up the domain name + auto const results = resolver.resolve(host, port); + + // Make the connection on the IP address we get from a lookup + beast::get_lowest_layer(stream).connect(results); + + // Perform the SSL handshake + stream.handshake(ssl::stream_base::client); + + // Set up an HTTP GET request message + http::request<http::string_body> req{http::verb::get, target, version}; + req.set(http::field::host, host); + req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + + // Send the HTTP request to the remote host + http::write(stream, req); + + // This buffer is used for reading and must be persisted + beast::flat_buffer buffer; + + // Declare a container to hold the response + http::response<http::dynamic_body> res; + + // Receive the HTTP response + http::read(stream, buffer, res); + + // Write the message to standard out + std::cout << res << std::endl; + + // Gracefully close the stream + beast::error_code ec; + stream.shutdown(ec); + if(ec == net::error::eof) + { + // Rationale: + // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error + ec = {}; + } + if(ec) + throw beast::system_error{ec}; + + // If we get here then the connection is closed gracefully + } + 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/example/http/client/sync/CMakeLists.txt b/src/boost/libs/beast/example/http/client/sync/CMakeLists.txt new file mode 100644 index 000000000..ea877ec4f --- /dev/null +++ b/src/boost/libs/beast/example/http/client/sync/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/http/client/sync "/") + +add_executable (http-client-sync + ${BOOST_BEAST_FILES} + Jamfile + http_client_sync.cpp +) + +target_link_libraries(http-client-sync + lib-asio + lib-beast) + +set_property(TARGET http-client-sync PROPERTY FOLDER "example-http-client") diff --git a/src/boost/libs/beast/example/http/client/sync/Jamfile b/src/boost/libs/beast/example/http/client/sync/Jamfile new file mode 100644 index 000000000..969246f65 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/sync/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 http-client-sync : + http_client_sync.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/client/sync/http_client_sync.cpp b/src/boost/libs/beast/example/http/client/sync/http_client_sync.cpp new file mode 100644 index 000000000..795618161 --- /dev/null +++ b/src/boost/libs/beast/example/http/client/sync/http_client_sync.cpp @@ -0,0 +1,105 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP client, synchronous +// +//------------------------------------------------------------------------------ + +//[example_http_client + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <cstdlib> +#include <iostream> +#include <string> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Performs an HTTP GET and prints the response +int main(int argc, char** argv) +{ + try + { + // Check command line arguments. + if(argc != 4 && argc != 5) + { + std::cerr << + "Usage: http-client-sync <host> <port> <target> [<HTTP version: 1.0 or 1.1(default)>]\n" << + "Example:\n" << + " http-client-sync www.example.com 80 /\n" << + " http-client-sync www.example.com 80 / 1.0\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const target = argv[3]; + int version = argc == 5 && !std::strcmp("1.0", argv[4]) ? 10 : 11; + + // The io_context is required for all I/O + net::io_context ioc; + + // These objects perform our I/O + tcp::resolver resolver(ioc); + beast::tcp_stream stream(ioc); + + // Look up the domain name + auto const results = resolver.resolve(host, port); + + // Make the connection on the IP address we get from a lookup + stream.connect(results); + + // Set up an HTTP GET request message + http::request<http::string_body> req{http::verb::get, target, version}; + req.set(http::field::host, host); + req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + + // Send the HTTP request to the remote host + http::write(stream, req); + + // This buffer is used for reading and must be persisted + beast::flat_buffer buffer; + + // Declare a container to hold the response + http::response<http::dynamic_body> res; + + // Receive the HTTP response + http::read(stream, buffer, res); + + // Write the message to standard out + std::cout << res << std::endl; + + // Gracefully close the socket + beast::error_code ec; + stream.socket().shutdown(tcp::socket::shutdown_both, ec); + + // not_connected happens sometimes + // so don't bother reporting it. + // + if(ec && ec != beast::errc::not_connected) + throw beast::system_error{ec}; + + // If we get here then the connection is closed gracefully + } + 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/example/http/server/CMakeLists.txt b/src/boost/libs/beast/example/http/server/CMakeLists.txt new file mode 100644 index 000000000..350b465a7 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright (c) 2013-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 (async) +add_subdirectory (coro) +add_subdirectory (fast) +add_subdirectory (small) +add_subdirectory (stackless) +add_subdirectory (sync) + +if (OPENSSL_FOUND) + add_subdirectory (async-ssl) + add_subdirectory (coro-ssl) + add_subdirectory (flex) + add_subdirectory (stackless-ssl) + add_subdirectory (sync-ssl) +endif() diff --git a/src/boost/libs/beast/example/http/server/Jamfile b/src/boost/libs/beast/example/http/server/Jamfile new file mode 100644 index 000000000..cb8ab104f --- /dev/null +++ b/src/boost/libs/beast/example/http/server/Jamfile @@ -0,0 +1,22 @@ +# +# Copyright (c) 2013-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 +# + +build-project async ; +build-project coro ; +build-project fast ; +build-project small ; +build-project stackless ; +build-project sync ; + +# SSL +build-project async-ssl ; +build-project coro-ssl ; +build-project flex ; +build-project stackless-ssl ; +build-project sync-ssl ; diff --git a/src/boost/libs/beast/example/http/server/async-ssl/CMakeLists.txt b/src/boost/libs/beast/example/http/server/async-ssl/CMakeLists.txt new file mode 100644 index 000000000..4c1e2cd02 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/async-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/http/server/async-ssl "/") + + add_executable (http-server-async-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + http_server_async_ssl.cpp + ) + + set_property(TARGET http-server-async-ssl PROPERTY FOLDER "example-http-server") + + target_link_libraries (http-server-async-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/http/server/async-ssl/Jamfile b/src/boost/libs/beast/example/http/server/async-ssl/Jamfile new file mode 100644 index 000000000..f4cf0204e --- /dev/null +++ b/src/boost/libs/beast/example/http/server/async-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe http-server-async-ssl : + http_server_async_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/server/async-ssl/http_server_async_ssl.cpp b/src/boost/libs/beast/example/http/server/async-ssl/http_server_async_ssl.cpp new file mode 100644 index 000000000..f6c66c90f --- /dev/null +++ b/src/boost/libs/beast/example/http/server/async-ssl/http_server_async_ssl.cpp @@ -0,0 +1,564 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP SSL server, asynchronous +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/dispatch.hpp> +#include <boost/asio/strand.hpp> +#include <boost/config.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#include <thread> +#include <vector> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + // ssl::error::stream_truncated, also known as an SSL "short read", + // indicates the peer closed the connection without performing the + // required closing handshake (for example, Google does this to + // improve performance). Generally this can be a security issue, + // but if your communication protocol is self-terminated (as + // it is with both HTTP and WebSocket) then you may simply + // ignore the lack of close_notify. + // + // https://github.com/boostorg/beast/issues/38 + // + // https://security.stackexchange.com/questions/91435/how-to-handle-a-malicious-ssl-tls-shutdown + // + // When a short read would cut off the end of an HTTP message, + // Beast returns the error beast::http::error::partial_message. + // Therefore, if we see a short read here, it has occurred + // after the message has been completed, so it is safe to ignore it. + + if(ec == net::ssl::error::stream_truncated) + return; + + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Handles an HTTP server connection +class session : public std::enable_shared_from_this<session> +{ + // This is the C++11 equivalent of a generic lambda. + // The function object is used to send an HTTP message. + struct send_lambda + { + session& self_; + + explicit + send_lambda(session& self) + : self_(self) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // The lifetime of the message has to extend + // for the duration of the async operation so + // we use a shared_ptr to manage it. + auto sp = std::make_shared< + http::message<isRequest, Body, Fields>>(std::move(msg)); + + // Store a type-erased version of the shared + // pointer in the class to keep it alive. + self_.res_ = sp; + + // Write the response + http::async_write( + self_.stream_, + *sp, + beast::bind_front_handler( + &session::on_write, + self_.shared_from_this(), + sp->need_eof())); + } + }; + + beast::ssl_stream<beast::tcp_stream> stream_; + beast::flat_buffer buffer_; + std::shared_ptr<std::string const> doc_root_; + http::request<http::string_body> req_; + std::shared_ptr<void> res_; + send_lambda lambda_; + +public: + // Take ownership of the socket + explicit + session( + tcp::socket&& socket, + ssl::context& ctx, + std::shared_ptr<std::string const> const& doc_root) + : stream_(std::move(socket), ctx) + , doc_root_(doc_root) + , lambda_(*this) + { + } + + // Start the asynchronous operation + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch( + stream_.get_executor(), + beast::bind_front_handler( + &session::on_run, + shared_from_this())); + } + + void + on_run() + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after( + std::chrono::seconds(30)); + + // Perform the SSL handshake + stream_.async_handshake( + ssl::stream_base::server, + beast::bind_front_handler( + &session::on_handshake, + shared_from_this())); + } + + void + on_handshake(beast::error_code ec) + { + if(ec) + return fail(ec, "handshake"); + + do_read(); + } + + void + do_read() + { + // Make the request empty before reading, + // otherwise the operation behavior is undefined. + req_ = {}; + + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Read a request + http::async_read(stream_, buffer_, req_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This means they closed the connection + if(ec == http::error::end_of_stream) + return do_close(); + + if(ec) + return fail(ec, "read"); + + // Send the response + handle_request(*doc_root_, std::move(req_), lambda_); + } + + void + on_write( + bool close, + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + return do_close(); + } + + // We're done with the response so delete it + res_ = nullptr; + + // Read another request + do_read(); + } + + void + do_close() + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL shutdown + stream_.async_shutdown( + beast::bind_front_handler( + &session::on_shutdown, + shared_from_this())); + } + + void + on_shutdown(beast::error_code ec) + { + if(ec) + return fail(ec, "shutdown"); + + // At this point the connection is closed gracefully + } +}; + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +class listener : public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + ssl::context& ctx_; + tcp::acceptor acceptor_; + std::shared_ptr<std::string const> doc_root_; + +public: + listener( + net::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + std::shared_ptr<std::string const> const& doc_root) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(ioc) + , doc_root_(doc_root) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + do_accept(); + } + +private: + void + do_accept() + { + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &listener::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the session and run it + std::make_shared<session>( + std::move(socket), + ctx_, + doc_root_)->run(); + } + + // Accept another connection + do_accept(); + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: http-server-async-ssl <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " http-server-async-ssl 0.0.0.0 8080 . 1\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // Create and launch a listening port + std::make_shared<listener>( + ioc, + ctx, + tcp::endpoint{address, port}, + doc_root)->run(); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/server/async/CMakeLists.txt b/src/boost/libs/beast/example/http/server/async/CMakeLists.txt new file mode 100644 index 000000000..b94c0521f --- /dev/null +++ b/src/boost/libs/beast/example/http/server/async/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(example/http/server/async "/") + +add_executable (http-server-async + ${BOOST_BEAST_FILES} + Jamfile + http_server_async.cpp +) + +target_link_libraries(http-server-async + lib-asio + lib-asio-ssl + lib-beast) + +set_property(TARGET http-server-async PROPERTY FOLDER "example-http-server") diff --git a/src/boost/libs/beast/example/http/server/async/Jamfile b/src/boost/libs/beast/example/http/server/async/Jamfile new file mode 100644 index 000000000..f36d1ad47 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/async/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 http-server-async : + http_server_async.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/server/async/http_server_async.cpp b/src/boost/libs/beast/example/http/server/async/http_server_async.cpp new file mode 100644 index 000000000..55fd234bb --- /dev/null +++ b/src/boost/libs/beast/example/http/server/async/http_server_async.cpp @@ -0,0 +1,490 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP server, asynchronous +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/dispatch.hpp> +#include <boost/asio/strand.hpp> +#include <boost/config.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#include <thread> +#include <vector> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Handles an HTTP server connection +class session : public std::enable_shared_from_this<session> +{ + // This is the C++11 equivalent of a generic lambda. + // The function object is used to send an HTTP message. + struct send_lambda + { + session& self_; + + explicit + send_lambda(session& self) + : self_(self) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // The lifetime of the message has to extend + // for the duration of the async operation so + // we use a shared_ptr to manage it. + auto sp = std::make_shared< + http::message<isRequest, Body, Fields>>(std::move(msg)); + + // Store a type-erased version of the shared + // pointer in the class to keep it alive. + self_.res_ = sp; + + // Write the response + http::async_write( + self_.stream_, + *sp, + beast::bind_front_handler( + &session::on_write, + self_.shared_from_this(), + sp->need_eof())); + } + }; + + beast::tcp_stream stream_; + beast::flat_buffer buffer_; + std::shared_ptr<std::string const> doc_root_; + http::request<http::string_body> req_; + std::shared_ptr<void> res_; + send_lambda lambda_; + +public: + // Take ownership of the stream + session( + tcp::socket&& socket, + std::shared_ptr<std::string const> const& doc_root) + : stream_(std::move(socket)) + , doc_root_(doc_root) + , lambda_(*this) + { + } + + // Start the asynchronous operation + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch(stream_.get_executor(), + beast::bind_front_handler( + &session::do_read, + shared_from_this())); + } + + void + do_read() + { + // Make the request empty before reading, + // otherwise the operation behavior is undefined. + req_ = {}; + + // Set the timeout. + stream_.expires_after(std::chrono::seconds(30)); + + // Read a request + http::async_read(stream_, buffer_, req_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This means they closed the connection + if(ec == http::error::end_of_stream) + return do_close(); + + if(ec) + return fail(ec, "read"); + + // Send the response + handle_request(*doc_root_, std::move(req_), lambda_); + } + + void + on_write( + bool close, + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + return do_close(); + } + + // We're done with the response so delete it + res_ = nullptr; + + // Read another request + do_read(); + } + + void + do_close() + { + // Send a TCP shutdown + beast::error_code ec; + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully + } +}; + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +class listener : public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + tcp::acceptor acceptor_; + std::shared_ptr<std::string const> doc_root_; + +public: + listener( + net::io_context& ioc, + tcp::endpoint endpoint, + std::shared_ptr<std::string const> const& doc_root) + : ioc_(ioc) + , acceptor_(net::make_strand(ioc)) + , doc_root_(doc_root) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + do_accept(); + } + +private: + void + do_accept() + { + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &listener::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the session and run it + std::make_shared<session>( + std::move(socket), + doc_root_)->run(); + } + + // Accept another connection + do_accept(); + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: http-server-async <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " http-server-async 0.0.0.0 8080 . 1\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // Create and launch a listening port + std::make_shared<listener>( + ioc, + tcp::endpoint{address, port}, + doc_root)->run(); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/server/coro-ssl/CMakeLists.txt b/src/boost/libs/beast/example/http/server/coro-ssl/CMakeLists.txt new file mode 100644 index 000000000..9bbf294cf --- /dev/null +++ b/src/boost/libs/beast/example/http/server/coro-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/http/server/coro-ssl "/") + + add_executable (http-server-coro-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + http_server_coro_ssl.cpp + ) + + set_property(TARGET http-server-coro-ssl PROPERTY FOLDER "example-http-server") + + target_link_libraries (http-server-coro-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/http/server/coro-ssl/Jamfile b/src/boost/libs/beast/example/http/server/coro-ssl/Jamfile new file mode 100644 index 000000000..8870f65d2 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/coro-ssl/Jamfile @@ -0,0 +1,23 @@ +# +# 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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe http-server-coro-ssl : + http_server_coro_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + <library>/boost/coroutine//boost_coroutine + ; diff --git a/src/boost/libs/beast/example/http/server/coro-ssl/http_server_coro_ssl.cpp b/src/boost/libs/beast/example/http/server/coro-ssl/http_server_coro_ssl.cpp new file mode 100644 index 000000000..a733c6244 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/coro-ssl/http_server_coro_ssl.cpp @@ -0,0 +1,432 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP SSL server, coroutine +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/spawn.hpp> +#include <boost/config.hpp> +#include <algorithm> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <string> +#include <thread> +#include <vector> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + // ssl::error::stream_truncated, also known as an SSL "short read", + // indicates the peer closed the connection without performing the + // required closing handshake (for example, Google does this to + // improve performance). Generally this can be a security issue, + // but if your communication protocol is self-terminated (as + // it is with both HTTP and WebSocket) then you may simply + // ignore the lack of close_notify. + // + // https://github.com/boostorg/beast/issues/38 + // + // https://security.stackexchange.com/questions/91435/how-to-handle-a-malicious-ssl-tls-shutdown + // + // When a short read would cut off the end of an HTTP message, + // Beast returns the error beast::http::error::partial_message. + // Therefore, if we see a short read here, it has occurred + // after the message has been completed, so it is safe to ignore it. + + if(ec == net::ssl::error::stream_truncated) + return; + + std::cerr << what << ": " << ec.message() << "\n"; +} + +// This is the C++11 equivalent of a generic lambda. +// The function object is used to send an HTTP message. +struct send_lambda +{ + beast::ssl_stream<beast::tcp_stream>& stream_; + bool& close_; + beast::error_code& ec_; + net::yield_context yield_; + + send_lambda( + beast::ssl_stream<beast::tcp_stream>& stream, + bool& close, + beast::error_code& ec, + net::yield_context yield) + : stream_(stream) + , close_(close) + , ec_(ec) + , yield_(yield) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // Determine if we should close the connection after + close_ = msg.need_eof(); + + // We need the serializer here because the serializer requires + // a non-const file_body, and the message oriented version of + // http::write only works with const messages. + http::serializer<isRequest, Body, Fields> sr{msg}; + http::async_write(stream_, sr, yield_[ec_]); + } +}; + +// Handles an HTTP server connection +void +do_session( + beast::ssl_stream<beast::tcp_stream>& stream, + std::shared_ptr<std::string const> const& doc_root, + net::yield_context yield) +{ + bool close = false; + beast::error_code ec; + + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); + + // Perform the SSL handshake + stream.async_handshake(ssl::stream_base::server, yield[ec]); + if(ec) + return fail(ec, "handshake"); + + // This buffer is required to persist across reads + beast::flat_buffer buffer; + + // This lambda is used to send messages + send_lambda lambda{stream, close, ec, yield}; + + for(;;) + { + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); + + // Read a request + http::request<http::string_body> req; + http::async_read(stream, buffer, req, yield[ec]); + if(ec == http::error::end_of_stream) + break; + if(ec) + return fail(ec, "read"); + + // Send the response + handle_request(*doc_root, std::move(req), lambda); + if(ec) + return fail(ec, "write"); + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + break; + } + } + + // Set the timeout. + beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(30)); + + // Perform the SSL shutdown + stream.async_shutdown(yield[ec]); + if(ec) + return fail(ec, "shutdown"); + + // At this point the connection is closed gracefully +} + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +void +do_listen( + net::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + std::shared_ptr<std::string const> const& doc_root, + net::yield_context yield) +{ + beast::error_code ec; + + // Open the acceptor + tcp::acceptor acceptor(ioc); + acceptor.open(endpoint.protocol(), ec); + if(ec) + return fail(ec, "open"); + + // Allow address reuse + acceptor.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + return fail(ec, "set_option"); + + // Bind to the server address + acceptor.bind(endpoint, ec); + if(ec) + return fail(ec, "bind"); + + // Start listening for connections + acceptor.listen(net::socket_base::max_listen_connections, ec); + if(ec) + return fail(ec, "listen"); + + for(;;) + { + tcp::socket socket(ioc); + acceptor.async_accept(socket, yield[ec]); + if(ec) + fail(ec, "accept"); + else + boost::asio::spawn( + acceptor.get_executor(), + std::bind( + &do_session, + beast::ssl_stream<beast::tcp_stream>( + std::move(socket), ctx), + doc_root, + std::placeholders::_1)); + } +} + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: http-server-coro-ssl <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " http-server-coro-ssl 0.0.0.0 8080 . 1\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // Spawn a listening port + boost::asio::spawn(ioc, + std::bind( + &do_listen, + std::ref(ioc), + std::ref(ctx), + tcp::endpoint{address, port}, + doc_root, + std::placeholders::_1)); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/server/coro/CMakeLists.txt b/src/boost/libs/beast/example/http/server/coro/CMakeLists.txt new file mode 100644 index 000000000..6cc8244a8 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/coro/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/http/server/coro "/") + +add_executable (http-server-coro + ${BOOST_BEAST_FILES} + Jamfile + http_server_coro.cpp +) + +target_link_libraries(http-server-coro + lib-asio + lib-beast) + +set_property(TARGET http-server-coro PROPERTY FOLDER "example-http-server") diff --git a/src/boost/libs/beast/example/http/server/coro/Jamfile b/src/boost/libs/beast/example/http/server/coro/Jamfile new file mode 100644 index 000000000..600b0fa09 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/coro/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 +# + +exe http-server-coro : + http_server_coro.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + <library>/boost/coroutine//boost_coroutine + ; diff --git a/src/boost/libs/beast/example/http/server/coro/http_server_coro.cpp b/src/boost/libs/beast/example/http/server/coro/http_server_coro.cpp new file mode 100644 index 000000000..a8baae640 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/coro/http_server_coro.cpp @@ -0,0 +1,387 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP server, coroutine +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/spawn.hpp> +#include <boost/config.hpp> +#include <algorithm> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <string> +#include <thread> +#include <vector> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// This is the C++11 equivalent of a generic lambda. +// The function object is used to send an HTTP message. +struct send_lambda +{ + beast::tcp_stream& stream_; + bool& close_; + beast::error_code& ec_; + net::yield_context yield_; + + send_lambda( + beast::tcp_stream& stream, + bool& close, + beast::error_code& ec, + net::yield_context yield) + : stream_(stream) + , close_(close) + , ec_(ec) + , yield_(yield) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // Determine if we should close the connection after + close_ = msg.need_eof(); + + // We need the serializer here because the serializer requires + // a non-const file_body, and the message oriented version of + // http::write only works with const messages. + http::serializer<isRequest, Body, Fields> sr{msg}; + http::async_write(stream_, sr, yield_[ec_]); + } +}; + +// Handles an HTTP server connection +void +do_session( + beast::tcp_stream& stream, + std::shared_ptr<std::string const> const& doc_root, + net::yield_context yield) +{ + bool close = false; + beast::error_code ec; + + // This buffer is required to persist across reads + beast::flat_buffer buffer; + + // This lambda is used to send messages + send_lambda lambda{stream, close, ec, yield}; + + for(;;) + { + // Set the timeout. + stream.expires_after(std::chrono::seconds(30)); + + // Read a request + http::request<http::string_body> req; + http::async_read(stream, buffer, req, yield[ec]); + if(ec == http::error::end_of_stream) + break; + if(ec) + return fail(ec, "read"); + + // Send the response + handle_request(*doc_root, std::move(req), lambda); + if(ec) + return fail(ec, "write"); + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + break; + } + } + + // Send a TCP shutdown + stream.socket().shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully +} + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +void +do_listen( + net::io_context& ioc, + tcp::endpoint endpoint, + std::shared_ptr<std::string const> const& doc_root, + net::yield_context yield) +{ + beast::error_code ec; + + // Open the acceptor + tcp::acceptor acceptor(ioc); + acceptor.open(endpoint.protocol(), ec); + if(ec) + return fail(ec, "open"); + + // Allow address reuse + acceptor.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + return fail(ec, "set_option"); + + // Bind to the server address + acceptor.bind(endpoint, ec); + if(ec) + return fail(ec, "bind"); + + // Start listening for connections + acceptor.listen(net::socket_base::max_listen_connections, ec); + if(ec) + return fail(ec, "listen"); + + for(;;) + { + tcp::socket socket(ioc); + acceptor.async_accept(socket, yield[ec]); + if(ec) + fail(ec, "accept"); + else + boost::asio::spawn( + acceptor.get_executor(), + std::bind( + &do_session, + beast::tcp_stream(std::move(socket)), + doc_root, + std::placeholders::_1)); + } +} + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: http-server-coro <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " http-server-coro 0.0.0.0 8080 . 1\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // Spawn a listening port + boost::asio::spawn(ioc, + std::bind( + &do_listen, + std::ref(ioc), + tcp::endpoint{address, port}, + doc_root, + std::placeholders::_1)); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/server/fast/CMakeLists.txt b/src/boost/libs/beast/example/http/server/fast/CMakeLists.txt new file mode 100644 index 000000000..ecc8f7bdc --- /dev/null +++ b/src/boost/libs/beast/example/http/server/fast/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# 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(example/common common) +GroupSources(example/http/server/fast "/") + +add_executable (http-server-fast + ${BOOST_BEAST_FILES} + ${COMMON_FILES} + Jamfile + fields_alloc.hpp + http_server_fast.cpp +) + +target_link_libraries(http-server-fast + lib-asio + lib-beast) + +set_property(TARGET http-server-fast PROPERTY FOLDER "example-http-server") diff --git a/src/boost/libs/beast/example/http/server/fast/Jamfile b/src/boost/libs/beast/example/http/server/fast/Jamfile new file mode 100644 index 000000000..8a9dd44d6 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/fast/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 http-server-fast : + http_server_fast.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/server/fast/fields_alloc.hpp b/src/boost/libs/beast/example/http/server/fast/fields_alloc.hpp new file mode 100644 index 000000000..d60c1c499 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/fast/fields_alloc.hpp @@ -0,0 +1,205 @@ +// +// 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_EXAMPLE_FIELDS_ALLOC_HPP +#define BOOST_BEAST_EXAMPLE_FIELDS_ALLOC_HPP + +#include <boost/throw_exception.hpp> +#include <cstdlib> +#include <memory> +#include <stdexcept> + +namespace detail { + +struct static_pool +{ + std::size_t size_; + std::size_t refs_ = 1; + std::size_t count_ = 0; + char* p_; + + char* + end() + { + return reinterpret_cast<char*>(this + 1) + size_; + } + + explicit + static_pool(std::size_t size) + : size_(size) + , p_(reinterpret_cast<char*>(this + 1)) + { + } + +public: + static + static_pool& + construct(std::size_t size) + { + auto p = new char[sizeof(static_pool) + size]; + return *(::new(p) static_pool{size}); + } + + static_pool& + share() + { + ++refs_; + return *this; + } + + void + destroy() + { + if(refs_--) + return; + this->~static_pool(); + delete[] reinterpret_cast<char*>(this); + } + + void* + alloc(std::size_t n) + { + auto last = p_ + n; + if(last >= end()) + BOOST_THROW_EXCEPTION(std::bad_alloc{}); + ++count_; + auto p = p_; + p_ = last; + return p; + } + + void + dealloc() + { + if(--count_) + return; + p_ = reinterpret_cast<char*>(this + 1); + } +}; + +} // detail + +/** A non-thread-safe allocator optimized for @ref basic_fields. + + This allocator obtains memory from a pre-allocated memory block + of a given size. It does nothing in deallocate until all + previously allocated blocks are deallocated, upon which it + resets the internal memory block for re-use. + + To use this allocator declare an instance persistent to the + connection or session, and construct with the block size. + A good rule of thumb is 20% more than the maximum allowed + header size. For example if the application only allows up + to an 8,000 byte header, the block size could be 9,600. + + Then, for every instance of `message` construct the header + with a copy of the previously declared allocator instance. +*/ +template<class T> +struct fields_alloc +{ + detail::static_pool* pool_; + +public: + using value_type = T; + using is_always_equal = std::false_type; + using pointer = T*; + using reference = T&; + using const_pointer = T const*; + using const_reference = T const&; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + template<class U> + struct rebind + { + using other = fields_alloc<U>; + }; + +#if defined(_GLIBCXX_USE_CXX11_ABI) && (_GLIBCXX_USE_CXX11_ABI == 0) + // Workaround for g++ + // basic_string assumes that allocators are default-constructible + // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 + fields_alloc() = default; +#endif + + explicit + fields_alloc(std::size_t size) + : pool_(&detail::static_pool::construct(size)) + { + } + + fields_alloc(fields_alloc const& other) + : pool_(&other.pool_->share()) + { + } + + template<class U> + fields_alloc(fields_alloc<U> const& other) + : pool_(&other.pool_->share()) + { + } + + ~fields_alloc() + { + pool_->destroy(); + } + + value_type* + allocate(size_type n) + { + return static_cast<value_type*>( + pool_->alloc(n * sizeof(T))); + } + + void + deallocate(value_type*, size_type) + { + pool_->dealloc(); + } + +#if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000 + template<class U, class... Args> + void + construct(U* ptr, Args&&... args) + { + ::new(static_cast<void*>(ptr)) U( + std::forward<Args>(args)...); + } + + template<class U> + void + destroy(U* ptr) + { + ptr->~U(); + } +#endif + + template<class U> + friend + bool + operator==( + fields_alloc const& lhs, + fields_alloc<U> const& rhs) + { + return &lhs.pool_ == &rhs.pool_; + } + + template<class U> + friend + bool + operator!=( + fields_alloc const& lhs, + fields_alloc<U> const& rhs) + { + return ! (lhs == rhs); + } +}; + +#endif diff --git a/src/boost/libs/beast/example/http/server/fast/http_server_fast.cpp b/src/boost/libs/beast/example/http/server/fast/http_server_fast.cpp new file mode 100644 index 000000000..66c27cade --- /dev/null +++ b/src/boost/libs/beast/example/http/server/fast/http_server_fast.cpp @@ -0,0 +1,352 @@ +// +// Copyright (c) 2017 Christopher M. Kohlhoff (chris at kohlhoff 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP server, fast +// +//------------------------------------------------------------------------------ + +#include "fields_alloc.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio.hpp> +#include <chrono> +#include <cstdlib> +#include <cstring> +#include <iostream> +#include <list> +#include <memory> +#include <string> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +class http_worker +{ +public: + http_worker(http_worker const&) = delete; + http_worker& operator=(http_worker const&) = delete; + + http_worker(tcp::acceptor& acceptor, const std::string& doc_root) : + acceptor_(acceptor), + doc_root_(doc_root) + { + } + + void start() + { + accept(); + check_deadline(); + } + +private: + using alloc_t = fields_alloc<char>; + //using request_body_t = http::basic_dynamic_body<beast::flat_static_buffer<1024 * 1024>>; + using request_body_t = http::string_body; + + // The acceptor used to listen for incoming connections. + tcp::acceptor& acceptor_; + + // The path to the root of the document directory. + std::string doc_root_; + + // The socket for the currently connected client. + tcp::socket socket_{acceptor_.get_executor()}; + + // The buffer for performing reads + beast::flat_static_buffer<8192> buffer_; + + // The allocator used for the fields in the request and reply. + alloc_t alloc_{8192}; + + // The parser for reading the requests + boost::optional<http::request_parser<request_body_t, alloc_t>> parser_; + + // The timer putting a time limit on requests. + net::steady_timer request_deadline_{ + acceptor_.get_executor(), (std::chrono::steady_clock::time_point::max)()}; + + // The string-based response message. + boost::optional<http::response<http::string_body, http::basic_fields<alloc_t>>> string_response_; + + // The string-based response serializer. + boost::optional<http::response_serializer<http::string_body, http::basic_fields<alloc_t>>> string_serializer_; + + // The file-based response message. + boost::optional<http::response<http::file_body, http::basic_fields<alloc_t>>> file_response_; + + // The file-based response serializer. + boost::optional<http::response_serializer<http::file_body, http::basic_fields<alloc_t>>> file_serializer_; + + void accept() + { + // Clean up any previous connection. + beast::error_code ec; + socket_.close(ec); + buffer_.consume(buffer_.size()); + + acceptor_.async_accept( + socket_, + [this](beast::error_code ec) + { + if (ec) + { + accept(); + } + else + { + // Request must be fully processed within 60 seconds. + request_deadline_.expires_after( + std::chrono::seconds(60)); + + read_request(); + } + }); + } + + void read_request() + { + // On each read the parser needs to be destroyed and + // recreated. We store it in a boost::optional to + // achieve that. + // + // Arguments passed to the parser constructor are + // forwarded to the message object. A single argument + // is forwarded to the body constructor. + // + // We construct the dynamic body with a 1MB limit + // to prevent vulnerability to buffer attacks. + // + parser_.emplace( + std::piecewise_construct, + std::make_tuple(), + std::make_tuple(alloc_)); + + http::async_read( + socket_, + buffer_, + *parser_, + [this](beast::error_code ec, std::size_t) + { + if (ec) + accept(); + else + process_request(parser_->get()); + }); + } + + void process_request(http::request<request_body_t, http::basic_fields<alloc_t>> const& req) + { + switch (req.method()) + { + case http::verb::get: + send_file(req.target()); + break; + + default: + // We return responses indicating an error if + // we do not recognize the request method. + send_bad_response( + http::status::bad_request, + "Invalid request-method '" + std::string(req.method_string()) + "'\r\n"); + break; + } + } + + void send_bad_response( + http::status status, + std::string const& error) + { + string_response_.emplace( + std::piecewise_construct, + std::make_tuple(), + std::make_tuple(alloc_)); + + string_response_->result(status); + string_response_->keep_alive(false); + string_response_->set(http::field::server, "Beast"); + string_response_->set(http::field::content_type, "text/plain"); + string_response_->body() = error; + string_response_->prepare_payload(); + + string_serializer_.emplace(*string_response_); + + http::async_write( + socket_, + *string_serializer_, + [this](beast::error_code ec, std::size_t) + { + socket_.shutdown(tcp::socket::shutdown_send, ec); + string_serializer_.reset(); + string_response_.reset(); + accept(); + }); + } + + void send_file(beast::string_view target) + { + // Request path must be absolute and not contain "..". + if (target.empty() || target[0] != '/' || target.find("..") != std::string::npos) + { + send_bad_response( + http::status::not_found, + "File not found\r\n"); + return; + } + + std::string full_path = doc_root_; + full_path.append( + target.data(), + target.size()); + + http::file_body::value_type file; + beast::error_code ec; + file.open( + full_path.c_str(), + beast::file_mode::read, + ec); + if(ec) + { + send_bad_response( + http::status::not_found, + "File not found\r\n"); + return; + } + + file_response_.emplace( + std::piecewise_construct, + std::make_tuple(), + std::make_tuple(alloc_)); + + file_response_->result(http::status::ok); + file_response_->keep_alive(false); + file_response_->set(http::field::server, "Beast"); + file_response_->set(http::field::content_type, mime_type(std::string(target))); + file_response_->body() = std::move(file); + file_response_->prepare_payload(); + + file_serializer_.emplace(*file_response_); + + http::async_write( + socket_, + *file_serializer_, + [this](beast::error_code ec, std::size_t) + { + socket_.shutdown(tcp::socket::shutdown_send, ec); + file_serializer_.reset(); + file_response_.reset(); + accept(); + }); + } + + void check_deadline() + { + // The deadline may have moved, so check it has really passed. + if (request_deadline_.expiry() <= std::chrono::steady_clock::now()) + { + // Close socket to cancel any outstanding operation. + beast::error_code ec; + socket_.close(); + + // Sleep indefinitely until we're given a new deadline. + request_deadline_.expires_at( + std::chrono::steady_clock::time_point::max()); + } + + request_deadline_.async_wait( + [this](beast::error_code) + { + check_deadline(); + }); + } +}; + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 6) + { + std::cerr << "Usage: http_server_fast <address> <port> <doc_root> <num_workers> {spin|block}\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " http_server_fast 0.0.0.0 80 . 100 block\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " http_server_fast 0::0 80 . 100 block\n"; + return EXIT_FAILURE; + } + + auto const address = net::ip::make_address(argv[1]); + unsigned short port = static_cast<unsigned short>(std::atoi(argv[2])); + std::string doc_root = argv[3]; + int num_workers = std::atoi(argv[4]); + bool spin = (std::strcmp(argv[5], "spin") == 0); + + net::io_context ioc{1}; + tcp::acceptor acceptor{ioc, {address, port}}; + + std::list<http_worker> workers; + for (int i = 0; i < num_workers; ++i) + { + workers.emplace_back(acceptor, doc_root); + workers.back().start(); + } + + if (spin) + for (;;) ioc.poll(); + else + ioc.run(); + } + catch (const std::exception& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} diff --git a/src/boost/libs/beast/example/http/server/flex/CMakeLists.txt b/src/boost/libs/beast/example/http/server/flex/CMakeLists.txt new file mode 100644 index 000000000..0920953b3 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/flex/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/http/server/flex "/") + + add_executable (http-server-flex + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + http_server_flex.cpp + ) + + set_property(TARGET http-server-flex PROPERTY FOLDER "example-http-server") + + target_link_libraries (http-server-flex + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/http/server/flex/Jamfile b/src/boost/libs/beast/example/http/server/flex/Jamfile new file mode 100644 index 000000000..f094caa27 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/flex/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe http-server-flex : + http_server_flex.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/server/flex/http_server_flex.cpp b/src/boost/libs/beast/example/http/server/flex/http_server_flex.cpp new file mode 100644 index 000000000..dd4b4ccd0 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/flex/http_server_flex.cpp @@ -0,0 +1,712 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP flex server (plain and SSL), asynchronous +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/dispatch.hpp> +#include <boost/asio/strand.hpp> +#include <boost/config.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#include <thread> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + // ssl::error::stream_truncated, also known as an SSL "short read", + // indicates the peer closed the connection without performing the + // required closing handshake (for example, Google does this to + // improve performance). Generally this can be a security issue, + // but if your communication protocol is self-terminated (as + // it is with both HTTP and WebSocket) then you may simply + // ignore the lack of close_notify. + // + // https://github.com/boostorg/beast/issues/38 + // + // https://security.stackexchange.com/questions/91435/how-to-handle-a-malicious-ssl-tls-shutdown + // + // When a short read would cut off the end of an HTTP message, + // Beast returns the error beast::http::error::partial_message. + // Therefore, if we see a short read here, it has occurred + // after the message has been completed, so it is safe to ignore it. + + if(ec == net::ssl::error::stream_truncated) + return; + + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Handles an HTTP server connection. +// This uses the Curiously Recurring Template Pattern so that +// the same code works with both SSL streams and regular sockets. +template<class Derived> +class session +{ + // Access the derived class, this is part of + // the Curiously Recurring Template Pattern idiom. + Derived& + derived() + { + return static_cast<Derived&>(*this); + } + + // This is the C++11 equivalent of a generic lambda. + // The function object is used to send an HTTP message. + struct send_lambda + { + session& self_; + + explicit + send_lambda(session& self) + : self_(self) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // The lifetime of the message has to extend + // for the duration of the async operation so + // we use a shared_ptr to manage it. + auto sp = std::make_shared< + http::message<isRequest, Body, Fields>>(std::move(msg)); + + // Store a type-erased version of the shared + // pointer in the class to keep it alive. + self_.res_ = sp; + + // Write the response + http::async_write( + self_.derived().stream(), + *sp, + beast::bind_front_handler( + &session::on_write, + self_.derived().shared_from_this(), + sp->need_eof())); + } + }; + + std::shared_ptr<std::string const> doc_root_; + http::request<http::string_body> req_; + std::shared_ptr<void> res_; + send_lambda lambda_; + +protected: + beast::flat_buffer buffer_; + +public: + // Take ownership of the buffer + session( + beast::flat_buffer buffer, + std::shared_ptr<std::string const> const& doc_root) + : doc_root_(doc_root) + , lambda_(*this) + , buffer_(std::move(buffer)) + { + } + + void + do_read() + { + // Set the timeout. + beast::get_lowest_layer( + derived().stream()).expires_after(std::chrono::seconds(30)); + + // Read a request + http::async_read( + derived().stream(), + buffer_, + req_, + beast::bind_front_handler( + &session::on_read, + derived().shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This means they closed the connection + if(ec == http::error::end_of_stream) + return derived().do_eof(); + + if(ec) + return fail(ec, "read"); + + // Send the response + handle_request(*doc_root_, std::move(req_), lambda_); + } + + void + on_write( + bool close, + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + return derived().do_eof(); + } + + // We're done with the response so delete it + res_ = nullptr; + + // Read another request + do_read(); + } +}; + +// Handles a plain HTTP connection +class plain_session + : public session<plain_session> + , public std::enable_shared_from_this<plain_session> +{ + beast::tcp_stream stream_; + +public: + // Create the session + plain_session( + tcp::socket&& socket, + beast::flat_buffer buffer, + std::shared_ptr<std::string const> const& doc_root) + : session<plain_session>( + std::move(buffer), + doc_root) + , stream_(std::move(socket)) + { + } + + // Called by the base class + beast::tcp_stream& + stream() + { + return stream_; + } + + // Start the asynchronous operation + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch(stream_.get_executor(), + beast::bind_front_handler( + &session::do_read, + shared_from_this())); + } + + void + do_eof() + { + // Send a TCP shutdown + beast::error_code ec; + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully + } +}; + +// Handles an SSL HTTP connection +class ssl_session + : public session<ssl_session> + , public std::enable_shared_from_this<ssl_session> +{ + beast::ssl_stream<beast::tcp_stream> stream_; + +public: + // Create the session + ssl_session( + tcp::socket&& socket, + ssl::context& ctx, + beast::flat_buffer buffer, + std::shared_ptr<std::string const> const& doc_root) + : session<ssl_session>( + std::move(buffer), + doc_root) + , stream_(std::move(socket), ctx) + { + } + + // Called by the base class + beast::ssl_stream<beast::tcp_stream>& + stream() + { + return stream_; + } + + // Start the asynchronous operation + void + run() + { + auto self = shared_from_this(); + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. + net::dispatch(stream_.get_executor(), [self]() { + // Set the timeout. + beast::get_lowest_layer(self->stream_).expires_after( + std::chrono::seconds(30)); + + // Perform the SSL handshake + // Note, this is the buffered version of the handshake. + self->stream_.async_handshake( + ssl::stream_base::server, + self->buffer_.data(), + beast::bind_front_handler( + &ssl_session::on_handshake, + self)); + }); + } + + void + on_handshake( + beast::error_code ec, + std::size_t bytes_used) + { + if(ec) + return fail(ec, "handshake"); + + // Consume the portion of the buffer used by the handshake + buffer_.consume(bytes_used); + + do_read(); + } + + void + do_eof() + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL shutdown + stream_.async_shutdown( + beast::bind_front_handler( + &ssl_session::on_shutdown, + shared_from_this())); + } + + void + on_shutdown(beast::error_code ec) + { + if(ec) + return fail(ec, "shutdown"); + + // At this point the connection is closed gracefully + } +}; + +//------------------------------------------------------------------------------ + +// Detects SSL handshakes +class detect_session : public std::enable_shared_from_this<detect_session> +{ + beast::tcp_stream stream_; + ssl::context& ctx_; + std::shared_ptr<std::string const> doc_root_; + beast::flat_buffer buffer_; + +public: + detect_session( + tcp::socket&& socket, + ssl::context& ctx, + std::shared_ptr<std::string const> const& doc_root) + : stream_(std::move(socket)) + , ctx_(ctx) + , doc_root_(doc_root) + { + } + + // Launch the detector + void + run() + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Detect a TLS handshake + async_detect_ssl( + stream_, + buffer_, + beast::bind_front_handler( + &detect_session::on_detect, + shared_from_this())); + } + + void + on_detect(beast::error_code ec, bool result) + { + if(ec) + return fail(ec, "detect"); + + if(result) + { + // Launch SSL session + std::make_shared<ssl_session>( + stream_.release_socket(), + ctx_, + std::move(buffer_), + doc_root_)->run(); + return; + } + + // Launch plain session + std::make_shared<plain_session>( + stream_.release_socket(), + std::move(buffer_), + doc_root_)->run(); + } +}; + +// Accepts incoming connections and launches the sessions +class listener : public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + ssl::context& ctx_; + tcp::acceptor acceptor_; + std::shared_ptr<std::string const> doc_root_; + +public: + listener( + net::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + std::shared_ptr<std::string const> const& doc_root) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(net::make_strand(ioc)) + , doc_root_(doc_root) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + do_accept(); + } + +private: + void + do_accept() + { + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &listener::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the detector session and run it + std::make_shared<detect_session>( + std::move(socket), + ctx_, + doc_root_)->run(); + } + + // Accept another connection + do_accept(); + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: http-server-flex <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " http-server-flex 0.0.0.0 8080 .\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // Create and launch a listening port + std::make_shared<listener>( + ioc, + ctx, + tcp::endpoint{address, port}, + doc_root)->run(); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/server/small/CMakeLists.txt b/src/boost/libs/beast/example/http/server/small/CMakeLists.txt new file mode 100644 index 000000000..c443dec5c --- /dev/null +++ b/src/boost/libs/beast/example/http/server/small/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/http/server/small "/") + +add_executable (http-server-small + ${BOOST_BEAST_FILES} + Jamfile + http_server_small.cpp +) + +target_link_libraries(http-server-small + lib-asio + lib-beast) + +set_property(TARGET http-server-small PROPERTY FOLDER "example-http-server") diff --git a/src/boost/libs/beast/example/http/server/small/Jamfile b/src/boost/libs/beast/example/http/server/small/Jamfile new file mode 100644 index 000000000..48e9fd138 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/small/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 http-server-small : + http_server_small.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/server/small/http_server_small.cpp b/src/boost/libs/beast/example/http/server/small/http_server_small.cpp new file mode 100644 index 000000000..53b6a2b3e --- /dev/null +++ b/src/boost/libs/beast/example/http/server/small/http_server_small.cpp @@ -0,0 +1,251 @@ +// +// Copyright (c) 2017 Christopher M. Kohlhoff (chris at kohlhoff 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP server, small +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio.hpp> +#include <chrono> +#include <cstdlib> +#include <ctime> +#include <iostream> +#include <memory> +#include <string> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +namespace my_program_state +{ + std::size_t + request_count() + { + static std::size_t count = 0; + return ++count; + } + + std::time_t + now() + { + return std::time(0); + } +} + +class http_connection : public std::enable_shared_from_this<http_connection> +{ +public: + http_connection(tcp::socket socket) + : socket_(std::move(socket)) + { + } + + // Initiate the asynchronous operations associated with the connection. + void + start() + { + read_request(); + check_deadline(); + } + +private: + // The socket for the currently connected client. + tcp::socket socket_; + + // The buffer for performing reads. + beast::flat_buffer buffer_{8192}; + + // The request message. + http::request<http::dynamic_body> request_; + + // The response message. + http::response<http::dynamic_body> response_; + + // The timer for putting a deadline on connection processing. + net::steady_timer deadline_{ + socket_.get_executor(), std::chrono::seconds(60)}; + + // Asynchronously receive a complete request message. + void + read_request() + { + auto self = shared_from_this(); + + http::async_read( + socket_, + buffer_, + request_, + [self](beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + if(!ec) + self->process_request(); + }); + } + + // Determine what needs to be done with the request message. + void + process_request() + { + response_.version(request_.version()); + response_.keep_alive(false); + + switch(request_.method()) + { + case http::verb::get: + response_.result(http::status::ok); + response_.set(http::field::server, "Beast"); + create_response(); + break; + + default: + // We return responses indicating an error if + // we do not recognize the request method. + response_.result(http::status::bad_request); + response_.set(http::field::content_type, "text/plain"); + beast::ostream(response_.body()) + << "Invalid request-method '" + << std::string(request_.method_string()) + << "'"; + break; + } + + write_response(); + } + + // Construct a response message based on the program state. + void + create_response() + { + if(request_.target() == "/count") + { + response_.set(http::field::content_type, "text/html"); + beast::ostream(response_.body()) + << "<html>\n" + << "<head><title>Request count</title></head>\n" + << "<body>\n" + << "<h1>Request count</h1>\n" + << "<p>There have been " + << my_program_state::request_count() + << " requests so far.</p>\n" + << "</body>\n" + << "</html>\n"; + } + else if(request_.target() == "/time") + { + response_.set(http::field::content_type, "text/html"); + beast::ostream(response_.body()) + << "<html>\n" + << "<head><title>Current time</title></head>\n" + << "<body>\n" + << "<h1>Current time</h1>\n" + << "<p>The current time is " + << my_program_state::now() + << " seconds since the epoch.</p>\n" + << "</body>\n" + << "</html>\n"; + } + else + { + response_.result(http::status::not_found); + response_.set(http::field::content_type, "text/plain"); + beast::ostream(response_.body()) << "File not found\r\n"; + } + } + + // Asynchronously transmit the response message. + void + write_response() + { + auto self = shared_from_this(); + + response_.set(http::field::content_length, response_.body().size()); + + http::async_write( + socket_, + response_, + [self](beast::error_code ec, std::size_t) + { + self->socket_.shutdown(tcp::socket::shutdown_send, ec); + self->deadline_.cancel(); + }); + } + + // Check whether we have spent enough time on this connection. + void + check_deadline() + { + auto self = shared_from_this(); + + deadline_.async_wait( + [self](beast::error_code ec) + { + if(!ec) + { + // Close socket to cancel any outstanding operation. + self->socket_.close(ec); + } + }); + } +}; + +// "Loop" forever accepting new connections. +void +http_server(tcp::acceptor& acceptor, tcp::socket& socket) +{ + acceptor.async_accept(socket, + [&](beast::error_code ec) + { + if(!ec) + std::make_shared<http_connection>(std::move(socket))->start(); + http_server(acceptor, socket); + }); +} + +int +main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if(argc != 3) + { + std::cerr << "Usage: " << argv[0] << " <address> <port>\n"; + std::cerr << " For IPv4, try:\n"; + std::cerr << " receiver 0.0.0.0 80\n"; + std::cerr << " For IPv6, try:\n"; + std::cerr << " receiver 0::0 80\n"; + return EXIT_FAILURE; + } + + auto const address = net::ip::make_address(argv[1]); + unsigned short port = static_cast<unsigned short>(std::atoi(argv[2])); + + net::io_context ioc{1}; + + tcp::acceptor acceptor{ioc, {address, port}}; + tcp::socket socket{ioc}; + http_server(acceptor, socket); + + ioc.run(); + } + catch(std::exception const& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} diff --git a/src/boost/libs/beast/example/http/server/stackless-ssl/CMakeLists.txt b/src/boost/libs/beast/example/http/server/stackless-ssl/CMakeLists.txt new file mode 100644 index 000000000..f25b9b3d6 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/stackless-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/http/server/stackless-ssl "/") + + add_executable (http-server-stackless-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + http_server_stackless_ssl.cpp + ) + + set_property(TARGET http-server-stackless-ssl PROPERTY FOLDER "example-http-server") + + target_link_libraries (http-server-stackless-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/http/server/stackless-ssl/Jamfile b/src/boost/libs/beast/example/http/server/stackless-ssl/Jamfile new file mode 100644 index 000000000..b47524646 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/stackless-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe http-server-stackless-ssl : + http_server_stackless_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp b/src/boost/libs/beast/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp new file mode 100644 index 000000000..7786cbb42 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/stackless-ssl/http_server_stackless_ssl.cpp @@ -0,0 +1,560 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP SSL server, stackless coroutine +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/coroutine.hpp> +#include <boost/asio/dispatch.hpp> +#include <boost/asio/strand.hpp> +#include <boost/config.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#include <thread> +#include <vector> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + // ssl::error::stream_truncated, also known as an SSL "short read", + // indicates the peer closed the connection without performing the + // required closing handshake (for example, Google does this to + // improve performance). Generally this can be a security issue, + // but if your communication protocol is self-terminated (as + // it is with both HTTP and WebSocket) then you may simply + // ignore the lack of close_notify. + // + // https://github.com/boostorg/beast/issues/38 + // + // https://security.stackexchange.com/questions/91435/how-to-handle-a-malicious-ssl-tls-shutdown + // + // When a short read would cut off the end of an HTTP message, + // Beast returns the error beast::http::error::partial_message. + // Therefore, if we see a short read here, it has occurred + // after the message has been completed, so it is safe to ignore it. + + if(ec == net::ssl::error::stream_truncated) + return; + + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Handles an HTTP server connection +class session + : public boost::asio::coroutine + , public std::enable_shared_from_this<session> +{ + // This is the C++11 equivalent of a generic lambda. + // The function object is used to send an HTTP message. + struct send_lambda + { + session& self_; + + explicit + send_lambda(session& self) + : self_(self) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // The lifetime of the message has to extend + // for the duration of the async operation so + // we use a shared_ptr to manage it. + auto sp = std::make_shared< + http::message<isRequest, Body, Fields>>(std::move(msg)); + + // Store a type-erased version of the shared + // pointer in the class to keep it alive. + self_.res_ = sp; + + // Write the response + http::async_write( + self_.stream_, + *sp, + std::bind( + &session::loop, + self_.shared_from_this(), + std::placeholders::_1, + std::placeholders::_2, + sp->need_eof())); + } + }; + + beast::ssl_stream<beast::tcp_stream> stream_; + beast::flat_buffer buffer_; + std::shared_ptr<std::string const> doc_root_; + http::request<http::string_body> req_; + std::shared_ptr<void> res_; + send_lambda lambda_; + +public: + // Take ownership of the socket + explicit + session( + tcp::socket&& socket, + ssl::context& ctx, + std::shared_ptr<std::string const> const& doc_root) + : stream_(std::move(socket), ctx) + , doc_root_(doc_root) + , lambda_(*this) + { + } + + // Start the asynchronous operation + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session.Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch(stream_.get_executor(), + beast::bind_front_handler(&session::loop, + shared_from_this(), + beast::error_code{}, + 0, + false)); + } + + #include <boost/asio/yield.hpp> + + void + loop( + beast::error_code ec, + std::size_t bytes_transferred, + bool close) + { + boost::ignore_unused(bytes_transferred); + reenter(*this) + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL handshake + yield stream_.async_handshake( + ssl::stream_base::server, + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + 0, + false)); + if(ec) + return fail(ec, "handshake"); + + for(;;) + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Make the request empty before reading, + // otherwise the operation behavior is undefined. + req_ = {}; + + // Read a request + yield http::async_read(stream_, buffer_, req_, + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2, + false)); + if(ec == http::error::end_of_stream) + { + // The remote host closed the connection + break; + } + if(ec) + return fail(ec, "read"); + + // Send the response + yield handle_request(*doc_root_, std::move(req_), lambda_); + if(ec) + return fail(ec, "write"); + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + break; + } + + // We're done with the response so delete it + res_ = nullptr; + } + + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL shutdown + yield stream_.async_shutdown( + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + 0, + false)); + if(ec) + return fail(ec, "shutdown"); + + // At this point the connection is closed gracefully + } + } + + #include <boost/asio/unyield.hpp> +}; + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +class listener + : public boost::asio::coroutine + , public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + ssl::context& ctx_; + tcp::acceptor acceptor_; + tcp::socket socket_; + std::shared_ptr<std::string const> doc_root_; + +public: + listener( + net::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + std::shared_ptr<std::string const> const& doc_root) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(net::make_strand(ioc)) + , socket_(net::make_strand(ioc)) + , doc_root_(doc_root) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + loop(); + } + +private: + + #include <boost/asio/yield.hpp> + + void + loop(beast::error_code ec = {}) + { + reenter(*this) + { + for(;;) + { + yield acceptor_.async_accept( + socket_, + std::bind( + &listener::loop, + shared_from_this(), + std::placeholders::_1)); + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the session and run it + std::make_shared<session>( + std::move(socket_), + ctx_, + doc_root_)->run(); + } + + // Make sure each session gets its own strand + socket_ = tcp::socket(net::make_strand(ioc_)); + } + } + } + + #include <boost/asio/unyield.hpp> +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: http-server-stackless-ssl <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " http-server-stackless-ssl 0.0.0.0 8080 . 1\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // Create and launch a listening port + std::make_shared<listener>( + ioc, + ctx, + tcp::endpoint{address, port}, + doc_root)->run(); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/server/stackless/CMakeLists.txt b/src/boost/libs/beast/example/http/server/stackless/CMakeLists.txt new file mode 100644 index 000000000..1f82d391f --- /dev/null +++ b/src/boost/libs/beast/example/http/server/stackless/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/http/server/stackless "/") + +add_executable (http-server-stackless + ${BOOST_BEAST_FILES} + Jamfile + http_server_stackless.cpp +) + +target_link_libraries(http-server-stackless + lib-asio + lib-beast) + +set_property(TARGET http-server-stackless PROPERTY FOLDER "example-http-server") diff --git a/src/boost/libs/beast/example/http/server/stackless/Jamfile b/src/boost/libs/beast/example/http/server/stackless/Jamfile new file mode 100644 index 000000000..f82934ff8 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/stackless/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 http-server-stackless : + http_server_stackless.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/server/stackless/http_server_stackless.cpp b/src/boost/libs/beast/example/http/server/stackless/http_server_stackless.cpp new file mode 100644 index 000000000..2439c6894 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/stackless/http_server_stackless.cpp @@ -0,0 +1,493 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP server, stackless coroutine +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/coroutine.hpp> +#include <boost/asio/dispatch.hpp> +#include <boost/asio/strand.hpp> +#include <boost/config.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#include <thread> +#include <vector> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Handles an HTTP server connection +class session + : public boost::asio::coroutine + , public std::enable_shared_from_this<session> +{ + // This is the C++11 equivalent of a generic lambda. + // The function object is used to send an HTTP message. + struct send_lambda + { + session& self_; + std::shared_ptr<void> res_; + + explicit + send_lambda(session& self) + : self_(self) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // The lifetime of the message has to extend + // for the duration of the async operation so + // we use a shared_ptr to manage it. + auto sp = std::make_shared< + http::message<isRequest, Body, Fields>>(std::move(msg)); + + // Store a type-erased version of the shared + // pointer in the class to keep it alive. + self_.res_ = sp; + + // Write the response + http::async_write( + self_.stream_, + *sp, + beast::bind_front_handler( + &session::loop, + self_.shared_from_this(), + sp->need_eof())); + } + }; + + beast::tcp_stream stream_; + beast::flat_buffer buffer_; + std::shared_ptr<std::string const> doc_root_; + http::request<http::string_body> req_; + std::shared_ptr<void> res_; + send_lambda lambda_; + +public: + // Take ownership of the socket + explicit + session( + tcp::socket&& socket, + std::shared_ptr<std::string const> const& doc_root) + : stream_(std::move(socket)) + , doc_root_(doc_root) + , lambda_(*this) + { + } + + // Start the asynchronous operation + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch(stream_.get_executor(), + beast::bind_front_handler(&session::loop, + shared_from_this(), + false, + beast::error_code{}, + 0)); + } + + #include <boost/asio/yield.hpp> + + void + loop( + bool close, + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + reenter(*this) + { + for(;;) + { + // Make the request empty before reading, + // otherwise the operation behavior is undefined. + req_ = {}; + + // Set the timeout. + stream_.expires_after(std::chrono::seconds(30)); + + // Read a request + yield http::async_read(stream_, buffer_, req_, + beast::bind_front_handler( + &session::loop, + shared_from_this(), + false)); + if(ec == http::error::end_of_stream) + { + // The remote host closed the connection + break; + } + if(ec) + return fail(ec, "read"); + + // Send the response + yield handle_request(*doc_root_, std::move(req_), lambda_); + if(ec) + return fail(ec, "write"); + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + break; + } + + // We're done with the response so delete it + res_ = nullptr; + } + + // Send a TCP shutdown + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully + } + } + + #include <boost/asio/unyield.hpp> +}; + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +class listener + : public boost::asio::coroutine + , public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + tcp::acceptor acceptor_; + tcp::socket socket_; + std::shared_ptr<std::string const> doc_root_; + +public: + listener( + net::io_context& ioc, + tcp::endpoint endpoint, + std::shared_ptr<std::string const> const& doc_root) + : ioc_(ioc) + , acceptor_(net::make_strand(ioc)) + , socket_(net::make_strand(ioc)) + , doc_root_(doc_root) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen(net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + loop(); + } + +private: + + #include <boost/asio/yield.hpp> + + void + loop(beast::error_code ec = {}) + { + reenter(*this) + { + for(;;) + { + yield acceptor_.async_accept( + socket_, + beast::bind_front_handler( + &listener::loop, + shared_from_this())); + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the session and run it + std::make_shared<session>( + std::move(socket_), + doc_root_)->run(); + } + + // Make sure each session gets its own strand + socket_ = tcp::socket(net::make_strand(ioc_)); + } + } + } + + #include <boost/asio/unyield.hpp> +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: http-server-stackless <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " http-server-stackless 0.0.0.0 8080 . 1\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // Create and launch a listening port + std::make_shared<listener>( + ioc, + tcp::endpoint{address, port}, + doc_root)->run(); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/http/server/sync-ssl/CMakeLists.txt b/src/boost/libs/beast/example/http/server/sync-ssl/CMakeLists.txt new file mode 100644 index 000000000..4142e2c95 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/sync-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/http/server/sync-ssl "/") + + add_executable (http-server-sync-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + http_server_sync_ssl.cpp + ) + + set_property(TARGET http-server-sync-ssl PROPERTY FOLDER "example-http-server") + + target_link_libraries (http-server-sync-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/http/server/sync-ssl/Jamfile b/src/boost/libs/beast/example/http/server/sync-ssl/Jamfile new file mode 100644 index 000000000..0ad5e57f5 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/sync-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe http-server-sync-ssl : + http_server_sync_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/server/sync-ssl/http_server_sync_ssl.cpp b/src/boost/libs/beast/example/http/server/sync-ssl/http_server_sync_ssl.cpp new file mode 100644 index 000000000..34b277530 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/sync-ssl/http_server_sync_ssl.cpp @@ -0,0 +1,356 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP SSL server, synchronous +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/ssl/stream.hpp> +#include <boost/config.hpp> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <string> +#include <thread> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// This is the C++11 equivalent of a generic lambda. +// The function object is used to send an HTTP message. +template<class Stream> +struct send_lambda +{ + Stream& stream_; + bool& close_; + beast::error_code& ec_; + + explicit + send_lambda( + Stream& stream, + bool& close, + beast::error_code& ec) + : stream_(stream) + , close_(close) + , ec_(ec) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // Determine if we should close the connection after + close_ = msg.need_eof(); + + // We need the serializer here because the serializer requires + // a non-const file_body, and the message oriented version of + // http::write only works with const messages. + http::serializer<isRequest, Body, Fields> sr{msg}; + http::write(stream_, sr, ec_); + } +}; + +// Handles an HTTP server connection +void +do_session( + tcp::socket& socket, + ssl::context& ctx, + std::shared_ptr<std::string const> const& doc_root) +{ + bool close = false; + beast::error_code ec; + + // Construct the stream around the socket + beast::ssl_stream<tcp::socket&> stream{socket, ctx}; + + // Perform the SSL handshake + stream.handshake(ssl::stream_base::server, ec); + if(ec) + return fail(ec, "handshake"); + + // This buffer is required to persist across reads + beast::flat_buffer buffer; + + // This lambda is used to send messages + send_lambda<beast::ssl_stream<tcp::socket&>> lambda{stream, close, ec}; + + for(;;) + { + // Read a request + http::request<http::string_body> req; + http::read(stream, buffer, req, ec); + if(ec == http::error::end_of_stream) + break; + if(ec) + return fail(ec, "read"); + + // Send the response + handle_request(*doc_root, std::move(req), lambda); + if(ec) + return fail(ec, "write"); + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + break; + } + } + + // Perform the SSL shutdown + stream.shutdown(ec); + if(ec) + return fail(ec, "shutdown"); + + // At this point the connection is closed gracefully +} + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: http-server-sync-ssl <address> <port> <doc_root>\n" << + "Example:\n" << + " http-server-sync-ssl 0.0.0.0 8080 .\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + + // The io_context is required for all I/O + net::io_context ioc{1}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // The acceptor receives incoming connections + tcp::acceptor acceptor{ioc, {address, port}}; + for(;;) + { + // This will receive the new connection + tcp::socket socket{ioc}; + + // Block until we get a connection + acceptor.accept(socket); + + // Launch the session, transferring ownership of the socket + std::thread{std::bind( + &do_session, + std::move(socket), + std::ref(ctx), + doc_root)}.detach(); + } + } + catch (const std::exception& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} diff --git a/src/boost/libs/beast/example/http/server/sync/CMakeLists.txt b/src/boost/libs/beast/example/http/server/sync/CMakeLists.txt new file mode 100644 index 000000000..250add773 --- /dev/null +++ b/src/boost/libs/beast/example/http/server/sync/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(example/http/server/sync "/") + +add_executable (http-server-sync + ${BOOST_BEAST_FILES} + Jamfile + http_server_sync.cpp +) + +target_link_libraries(http-server-sync + lib-asio + lib-asio-ssl + lib-beast) + +set_property(TARGET http-server-sync PROPERTY FOLDER "example-http-server") diff --git a/src/boost/libs/beast/example/http/server/sync/Jamfile b/src/boost/libs/beast/example/http/server/sync/Jamfile new file mode 100644 index 000000000..30e4f026b --- /dev/null +++ b/src/boost/libs/beast/example/http/server/sync/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 http-server-sync : + http_server_sync.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/http/server/sync/http_server_sync.cpp b/src/boost/libs/beast/example/http/server/sync/http_server_sync.cpp new file mode 100644 index 000000000..2c5ca60fb --- /dev/null +++ b/src/boost/libs/beast/example/http/server/sync/http_server_sync.cpp @@ -0,0 +1,335 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: HTTP server, synchronous +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/config.hpp> +#include <cstdlib> +#include <iostream> +#include <memory> +#include <string> +#include <thread> + +namespace beast = boost::beast; // from <boost/beast.hpp> +namespace http = beast::http; // from <boost/beast/http.hpp> +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if (base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// This is the C++11 equivalent of a generic lambda. +// The function object is used to send an HTTP message. +template<class Stream> +struct send_lambda +{ + Stream& stream_; + bool& close_; + beast::error_code& ec_; + + explicit + send_lambda( + Stream& stream, + bool& close, + beast::error_code& ec) + : stream_(stream) + , close_(close) + , ec_(ec) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // Determine if we should close the connection after + close_ = msg.need_eof(); + + // We need the serializer here because the serializer requires + // a non-const file_body, and the message oriented version of + // http::write only works with const messages. + http::serializer<isRequest, Body, Fields> sr{msg}; + http::write(stream_, sr, ec_); + } +}; + +// Handles an HTTP server connection +void +do_session( + tcp::socket& socket, + std::shared_ptr<std::string const> const& doc_root) +{ + bool close = false; + beast::error_code ec; + + // This buffer is required to persist across reads + beast::flat_buffer buffer; + + // This lambda is used to send messages + send_lambda<tcp::socket> lambda{socket, close, ec}; + + for(;;) + { + // Read a request + http::request<http::string_body> req; + http::read(socket, buffer, req, ec); + if(ec == http::error::end_of_stream) + break; + if(ec) + return fail(ec, "read"); + + // Send the response + handle_request(*doc_root, std::move(req), lambda); + if(ec) + return fail(ec, "write"); + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + break; + } + } + + // Send a TCP shutdown + socket.shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully +} + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: http-server-sync <address> <port> <doc_root>\n" << + "Example:\n" << + " http-server-sync 0.0.0.0 8080 .\n"; + 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 doc_root = std::make_shared<std::string>(argv[3]); + + // The io_context is required for all I/O + net::io_context ioc{1}; + + // The acceptor receives incoming connections + tcp::acceptor acceptor{ioc, {address, port}}; + for(;;) + { + // This will receive the new connection + tcp::socket socket{ioc}; + + // Block until we get a connection + acceptor.accept(socket); + + // Launch the session, transferring ownership of the socket + std::thread{std::bind( + &do_session, + std::move(socket), + doc_root)}.detach(); + } + } + catch (const std::exception& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} diff --git a/src/boost/libs/beast/example/websocket/CMakeLists.txt b/src/boost/libs/beast/example/websocket/CMakeLists.txt new file mode 100644 index 000000000..d38604dd0 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/CMakeLists.txt @@ -0,0 +1,11 @@ +# +# Copyright (c) 2013-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 (client) +add_subdirectory (server) diff --git a/src/boost/libs/beast/example/websocket/Jamfile b/src/boost/libs/beast/example/websocket/Jamfile new file mode 100644 index 000000000..32f2c5144 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/Jamfile @@ -0,0 +1,11 @@ +# +# Copyright (c) 2013-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 +# + +build-project client ; +build-project server ; diff --git a/src/boost/libs/beast/example/websocket/client/CMakeLists.txt b/src/boost/libs/beast/example/websocket/client/CMakeLists.txt new file mode 100644 index 000000000..5d1300e2a --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/CMakeLists.txt @@ -0,0 +1,19 @@ +# +# Copyright (c) 2013-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 (async) +add_subdirectory (coro) +add_subdirectory (sync) + +if (OPENSSL_FOUND) + add_subdirectory (async-ssl) + add_subdirectory (async-ssl-system-executor) + add_subdirectory (coro-ssl) + add_subdirectory (sync-ssl) +endif() diff --git a/src/boost/libs/beast/example/websocket/client/Jamfile b/src/boost/libs/beast/example/websocket/client/Jamfile new file mode 100644 index 000000000..94a458110 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/Jamfile @@ -0,0 +1,18 @@ +# +# Copyright (c) 2013-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 +# + +build-project async ; +build-project coro ; +build-project sync ; + +# SSL +build-project async-ssl ; +build-project async-ssl-system-executor ; +build-project coro-ssl ; +build-project sync-ssl ; diff --git a/src/boost/libs/beast/example/websocket/client/async-ssl-system-executor/CMakeLists.txt b/src/boost/libs/beast/example/websocket/client/async-ssl-system-executor/CMakeLists.txt new file mode 100644 index 000000000..3a22aade8 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/async-ssl-system-executor/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/websocket/client/async-ssl-system-executor "/") + + add_executable (websocket-client-async-ssl-system-executor + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/root_certificates.hpp + Jamfile + websocket_client_async_ssl_system_executor.cpp + ) + + set_property(TARGET websocket-client-async-ssl-system-executor PROPERTY FOLDER "example-websocket-client") + + target_link_libraries (websocket-client-async-ssl-system-executor + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/websocket/client/async-ssl-system-executor/Jamfile b/src/boost/libs/beast/example/websocket/client/async-ssl-system-executor/Jamfile new file mode 100644 index 000000000..770a93c7d --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/async-ssl-system-executor/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe websocket-client-async-ssl-system-executor : + websocket_client_async_ssl_system_executor.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/client/async-ssl-system-executor/websocket_client_async_ssl_system_executor.cpp b/src/boost/libs/beast/example/websocket/client/async-ssl-system-executor/websocket_client_async_ssl_system_executor.cpp new file mode 100644 index 000000000..40d19fd00 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/async-ssl-system-executor/websocket_client_async_ssl_system_executor.cpp @@ -0,0 +1,259 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket SSL client, asynchronous, using system_executor +// +//------------------------------------------------------------------------------ + +#include "example/common/root_certificates.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/websocket/ssl.hpp> +#include <boost/asio/strand.hpp> +#include <boost/asio/system_executor.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> + +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> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Sends a WebSocket message and prints the response +class session : public std::enable_shared_from_this<session> +{ + tcp::resolver resolver_; + websocket::stream< + beast::ssl_stream<beast::tcp_stream>> ws_; + beast::flat_buffer buffer_; + std::string host_; + std::string text_; + + // Objects are constructed with a strand to + // ensure that handlers do not execute concurrently. + session(net::strand<net::system_executor> ex, ssl::context& ctx) + : resolver_(ex) + , ws_(ex, ctx) + { + } +public: + // Delegate construction to a prive constructor to be able to use + // the same strand for both I/O objects. + explicit + session(ssl::context& ctx) + : session(net::make_strand(net::system_executor{}), ctx) + { + } + + // Start the asynchronous operation + void + run( + char const* host, + char const* port, + char const* text) + { + // Save these for later + host_ = host; + text_ = text; + + // Look up the domain name + resolver_.async_resolve( + host, + port, + beast::bind_front_handler( + &session::on_resolve, + shared_from_this())); + } + + void + on_resolve( + beast::error_code ec, + tcp::resolver::results_type results) + { + if(ec) + return fail(ec, "resolve"); + + // Set a timeout on the operation + beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + beast::get_lowest_layer(ws_).async_connect( + results, + beast::bind_front_handler( + &session::on_connect, + shared_from_this())); + } + + void + on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type ep) + { + if(ec) + return fail(ec, "connect"); + + // Update the host_ string. This will provide the value of the + // Host HTTP header during the WebSocket handshake. + // See https://tools.ietf.org/html/rfc7230#section-5.4 + host_ += ':' + std::to_string(ep.port()); + + // Set a timeout on the operation + beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL handshake + ws_.next_layer().async_handshake( + ssl::stream_base::client, + beast::bind_front_handler( + &session::on_ssl_handshake, + shared_from_this())); + } + + void + on_ssl_handshake(beast::error_code ec) + { + if(ec) + return fail(ec, "ssl_handshake"); + + // Turn off the timeout on the tcp_stream, because + // the websocket stream has its own timeout system. + beast::get_lowest_layer(ws_).expires_never(); + + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::client)); + + // Set a decorator to change the User-Agent of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-async-ssl"); + })); + + // Perform the websocket handshake + ws_.async_handshake(host_, "/", + beast::bind_front_handler( + &session::on_handshake, + shared_from_this())); + } + + void + on_handshake(beast::error_code ec) + { + if(ec) + return fail(ec, "handshake"); + + // Send the message + ws_.async_write( + net::buffer(text_), + beast::bind_front_handler( + &session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Read a message into our buffer + ws_.async_read( + buffer_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "read"); + + // Close the WebSocket connection + ws_.async_close(websocket::close_code::normal, + beast::bind_front_handler( + &session::on_close, + shared_from_this())); + } + + void + on_close(beast::error_code ec) + { + if(ec) + return fail(ec, "close"); + + // If we get here then the connection is closed gracefully + + // The make_printable() function helps print a ConstBufferSequence + std::cout << beast::make_printable(buffer_.data()) << std::endl; + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4) + { + std::cerr << + "Usage: websocket-client-async-ssl <host> <port> <text>\n" << + "Example:\n" << + " websocket-client-async-ssl echo.websocket.org 443 \"Hello, world!\"\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const text = argv[3]; + + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12_client}; + + // This holds the root certificate used for verification + load_root_certificates(ctx); + + // Launch the asynchronous operation + std::make_shared<session>(ctx)->run(host, port, text); + + // The async operations will run on the system_executor. + // Because the main thread has nothing to do in this example, we just wait + // for the system_executor to run out of work. + net::system_executor().context().join(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/client/async-ssl/CMakeLists.txt b/src/boost/libs/beast/example/websocket/client/async-ssl/CMakeLists.txt new file mode 100644 index 000000000..a21982806 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/async-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/websocket/client/async-ssl "/") + + add_executable (websocket-client-async-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/root_certificates.hpp + Jamfile + websocket_client_async_ssl.cpp + ) + + set_property(TARGET websocket-client-async-ssl PROPERTY FOLDER "example-websocket-client") + + target_link_libraries (websocket-client-async-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/websocket/client/async-ssl/Jamfile b/src/boost/libs/beast/example/websocket/client/async-ssl/Jamfile new file mode 100644 index 000000000..1f46a94d9 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/async-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe websocket-client-async-ssl : + websocket_client_async_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp b/src/boost/libs/beast/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp new file mode 100644 index 000000000..4d413aa78 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp @@ -0,0 +1,252 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket SSL client, asynchronous +// +//------------------------------------------------------------------------------ + +#include "example/common/root_certificates.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/websocket/ssl.hpp> +#include <boost/asio/strand.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> + +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> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Sends a WebSocket message and prints the response +class session : public std::enable_shared_from_this<session> +{ + tcp::resolver resolver_; + websocket::stream< + beast::ssl_stream<beast::tcp_stream>> ws_; + beast::flat_buffer buffer_; + std::string host_; + std::string text_; + +public: + // Resolver and socket require an io_context + explicit + session(net::io_context& ioc, ssl::context& ctx) + : resolver_(net::make_strand(ioc)) + , ws_(net::make_strand(ioc), ctx) + { + } + + // Start the asynchronous operation + void + run( + char const* host, + char const* port, + char const* text) + { + // Save these for later + host_ = host; + text_ = text; + + // Look up the domain name + resolver_.async_resolve( + host, + port, + beast::bind_front_handler( + &session::on_resolve, + shared_from_this())); + } + + void + on_resolve( + beast::error_code ec, + tcp::resolver::results_type results) + { + if(ec) + return fail(ec, "resolve"); + + // Set a timeout on the operation + beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + beast::get_lowest_layer(ws_).async_connect( + results, + beast::bind_front_handler( + &session::on_connect, + shared_from_this())); + } + + void + on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type ep) + { + if(ec) + return fail(ec, "connect"); + + // Update the host_ string. This will provide the value of the + // Host HTTP header during the WebSocket handshake. + // See https://tools.ietf.org/html/rfc7230#section-5.4 + host_ += ':' + std::to_string(ep.port()); + + // Set a timeout on the operation + beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL handshake + ws_.next_layer().async_handshake( + ssl::stream_base::client, + beast::bind_front_handler( + &session::on_ssl_handshake, + shared_from_this())); + } + + void + on_ssl_handshake(beast::error_code ec) + { + if(ec) + return fail(ec, "ssl_handshake"); + + // Turn off the timeout on the tcp_stream, because + // the websocket stream has its own timeout system. + beast::get_lowest_layer(ws_).expires_never(); + + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::client)); + + // Set a decorator to change the User-Agent of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-async-ssl"); + })); + + // Perform the websocket handshake + ws_.async_handshake(host_, "/", + beast::bind_front_handler( + &session::on_handshake, + shared_from_this())); + } + + void + on_handshake(beast::error_code ec) + { + if(ec) + return fail(ec, "handshake"); + + // Send the message + ws_.async_write( + net::buffer(text_), + beast::bind_front_handler( + &session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Read a message into our buffer + ws_.async_read( + buffer_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "read"); + + // Close the WebSocket connection + ws_.async_close(websocket::close_code::normal, + beast::bind_front_handler( + &session::on_close, + shared_from_this())); + } + + void + on_close(beast::error_code ec) + { + if(ec) + return fail(ec, "close"); + + // If we get here then the connection is closed gracefully + + // The make_printable() function helps print a ConstBufferSequence + std::cout << beast::make_printable(buffer_.data()) << std::endl; + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4) + { + std::cerr << + "Usage: websocket-client-async-ssl <host> <port> <text>\n" << + "Example:\n" << + " websocket-client-async-ssl echo.websocket.org 443 \"Hello, world!\"\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const text = argv[3]; + + // The io_context is required for all I/O + net::io_context ioc; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12_client}; + + // This holds the root certificate used for verification + load_root_certificates(ctx); + + // Launch the asynchronous operation + std::make_shared<session>(ioc, ctx)->run(host, port, text); + + // Run the I/O service. The call will return when + // the socket is closed. + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/client/async/CMakeLists.txt b/src/boost/libs/beast/example/websocket/client/async/CMakeLists.txt new file mode 100644 index 000000000..cc6434071 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/async/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/websocket/client/async "/") + +add_executable (websocket-client-async + ${BOOST_BEAST_FILES} + Jamfile + websocket_client_async.cpp +) + +target_link_libraries(websocket-client-async + lib-asio + lib-beast) + +set_property(TARGET websocket-client-async PROPERTY FOLDER "example-websocket-client") diff --git a/src/boost/libs/beast/example/websocket/client/async/Jamfile b/src/boost/libs/beast/example/websocket/client/async/Jamfile new file mode 100644 index 000000000..e5359db3b --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/async/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 websocket-client-async : + websocket_client_async.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/client/async/websocket_client_async.cpp b/src/boost/libs/beast/example/websocket/client/async/websocket_client_async.cpp new file mode 100644 index 000000000..5fcf721b8 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/async/websocket_client_async.cpp @@ -0,0 +1,223 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket client, asynchronous +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/asio/strand.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> + +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> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Sends a WebSocket message and prints the response +class session : public std::enable_shared_from_this<session> +{ + tcp::resolver resolver_; + websocket::stream<beast::tcp_stream> ws_; + beast::flat_buffer buffer_; + std::string host_; + std::string text_; + +public: + // Resolver and socket require an io_context + explicit + session(net::io_context& ioc) + : resolver_(net::make_strand(ioc)) + , ws_(net::make_strand(ioc)) + { + } + + // Start the asynchronous operation + void + run( + char const* host, + char const* port, + char const* text) + { + // Save these for later + host_ = host; + text_ = text; + + // Look up the domain name + resolver_.async_resolve( + host, + port, + beast::bind_front_handler( + &session::on_resolve, + shared_from_this())); + } + + void + on_resolve( + beast::error_code ec, + tcp::resolver::results_type results) + { + if(ec) + return fail(ec, "resolve"); + + // Set the timeout for the operation + beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + beast::get_lowest_layer(ws_).async_connect( + results, + beast::bind_front_handler( + &session::on_connect, + shared_from_this())); + } + + void + on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type ep) + { + if(ec) + return fail(ec, "connect"); + + // Turn off the timeout on the tcp_stream, because + // the websocket stream has its own timeout system. + beast::get_lowest_layer(ws_).expires_never(); + + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::client)); + + // Set a decorator to change the User-Agent of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-async"); + })); + + // Update the host_ string. This will provide the value of the + // Host HTTP header during the WebSocket handshake. + // See https://tools.ietf.org/html/rfc7230#section-5.4 + host_ += ':' + std::to_string(ep.port()); + + // Perform the websocket handshake + ws_.async_handshake(host_, "/", + beast::bind_front_handler( + &session::on_handshake, + shared_from_this())); + } + + void + on_handshake(beast::error_code ec) + { + if(ec) + return fail(ec, "handshake"); + + // Send the message + ws_.async_write( + net::buffer(text_), + beast::bind_front_handler( + &session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Read a message into our buffer + ws_.async_read( + buffer_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "read"); + + // Close the WebSocket connection + ws_.async_close(websocket::close_code::normal, + beast::bind_front_handler( + &session::on_close, + shared_from_this())); + } + + void + on_close(beast::error_code ec) + { + if(ec) + return fail(ec, "close"); + + // If we get here then the connection is closed gracefully + + // The make_printable() function helps print a ConstBufferSequence + std::cout << beast::make_printable(buffer_.data()) << std::endl; + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4) + { + std::cerr << + "Usage: websocket-client-async <host> <port> <text>\n" << + "Example:\n" << + " websocket-client-async echo.websocket.org 80 \"Hello, world!\"\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const text = argv[3]; + + // The io_context is required for all I/O + net::io_context ioc; + + // Launch the asynchronous operation + std::make_shared<session>(ioc)->run(host, port, text); + + // Run the I/O service. The call will return when + // the socket is closed. + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/client/coro-ssl/CMakeLists.txt b/src/boost/libs/beast/example/websocket/client/coro-ssl/CMakeLists.txt new file mode 100644 index 000000000..fe9db75c4 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/coro-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/websocket/client/coro-ssl "/") + + add_executable (websocket-client-coro-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/root_certificates.hpp + Jamfile + websocket_client_coro_ssl.cpp + ) + + set_property(TARGET websocket-client-coro-ssl PROPERTY FOLDER "example-websocket-client") + + target_link_libraries (websocket-client-coro-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/websocket/client/coro-ssl/Jamfile b/src/boost/libs/beast/example/websocket/client/coro-ssl/Jamfile new file mode 100644 index 000000000..f6f1bb922 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/coro-ssl/Jamfile @@ -0,0 +1,23 @@ +# +# 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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe websocket-client-coro-ssl : + websocket_client_coro_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + <library>/boost/coroutine//boost_coroutine + ; diff --git a/src/boost/libs/beast/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp b/src/boost/libs/beast/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp new file mode 100644 index 000000000..5b4d9e9b4 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/coro-ssl/websocket_client_coro_ssl.cpp @@ -0,0 +1,175 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket SSL client, coroutine +// +//------------------------------------------------------------------------------ + +#include "example/common/root_certificates.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/websocket/ssl.hpp> +#include <boost/asio/spawn.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <string> + +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> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Sends a WebSocket message and prints the response +void +do_session( + std::string host, + std::string const& port, + std::string const& text, + net::io_context& ioc, + ssl::context& ctx, + net::yield_context yield) +{ + beast::error_code ec; + + // These objects perform our I/O + tcp::resolver resolver(ioc); + websocket::stream< + beast::ssl_stream<beast::tcp_stream>> ws(ioc, ctx); + + // Look up the domain name + auto const results = resolver.async_resolve(host, port, yield[ec]); + if(ec) + return fail(ec, "resolve"); + + // Set a timeout on the operation + beast::get_lowest_layer(ws).expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + auto ep = beast::get_lowest_layer(ws).async_connect(results, yield[ec]); + if(ec) + return fail(ec, "connect"); + + // Update the host_ string. This will provide the value of the + // Host HTTP header during the WebSocket handshake. + // See https://tools.ietf.org/html/rfc7230#section-5.4 + host += ':' + std::to_string(ep.port()); + + // Set a timeout on the operation + beast::get_lowest_layer(ws).expires_after(std::chrono::seconds(30)); + + // Set a decorator to change the User-Agent of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-coro"); + })); + + // Perform the SSL handshake + ws.next_layer().async_handshake(ssl::stream_base::client, yield[ec]); + if(ec) + return fail(ec, "ssl_handshake"); + + // Turn off the timeout on the tcp_stream, because + // the websocket stream has its own timeout system. + beast::get_lowest_layer(ws).expires_never(); + + // Set suggested timeout settings for the websocket + ws.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::client)); + + // Perform the websocket handshake + ws.async_handshake(host, "/", yield[ec]); + if(ec) + return fail(ec, "handshake"); + + // Send the message + ws.async_write(net::buffer(std::string(text)), yield[ec]); + if(ec) + return fail(ec, "write"); + + // This buffer will hold the incoming message + beast::flat_buffer buffer; + + // Read a message into our buffer + ws.async_read(buffer, yield[ec]); + if(ec) + return fail(ec, "read"); + + // Close the WebSocket connection + ws.async_close(websocket::close_code::normal, yield[ec]); + if(ec) + return fail(ec, "close"); + + // If we get here then the connection is closed gracefully + + // The make_printable() function helps print a ConstBufferSequence + std::cout << beast::make_printable(buffer.data()) << std::endl; +} + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4) + { + std::cerr << + "Usage: websocket-client-coro-ssl <host> <port> <text>\n" << + "Example:\n" << + " websocket-client-coro-ssl echo.websocket.org 443 \"Hello, world!\"\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const text = argv[3]; + + // The io_context is required for all I/O + net::io_context ioc; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12_client}; + + // This holds the root certificate used for verification + load_root_certificates(ctx); + + // Launch the asynchronous operation + boost::asio::spawn(ioc, std::bind( + &do_session, + std::string(host), + std::string(port), + std::string(text), + std::ref(ioc), + std::ref(ctx), + std::placeholders::_1)); + + // Run the I/O service. The call will return when + // the socket is closed. + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/client/coro/CMakeLists.txt b/src/boost/libs/beast/example/websocket/client/coro/CMakeLists.txt new file mode 100644 index 000000000..2bfeef282 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/coro/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/websocket/client/coro "/") + +add_executable (websocket-client-coro + ${BOOST_BEAST_FILES} + Jamfile + websocket_client_coro.cpp +) + +target_link_libraries(websocket-client-coro + lib-asio + lib-beast) + +set_property(TARGET websocket-client-coro PROPERTY FOLDER "example-websocket-client") diff --git a/src/boost/libs/beast/example/websocket/client/coro/Jamfile b/src/boost/libs/beast/example/websocket/client/coro/Jamfile new file mode 100644 index 000000000..761dfe8ff --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/coro/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 +# + +exe websocket-client-coro : + websocket_client_coro.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + <library>/boost/coroutine//boost_coroutine + ; diff --git a/src/boost/libs/beast/example/websocket/client/coro/websocket_client_coro.cpp b/src/boost/libs/beast/example/websocket/client/coro/websocket_client_coro.cpp new file mode 100644 index 000000000..7a4b0616d --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/coro/websocket_client_coro.cpp @@ -0,0 +1,153 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket client, coroutine +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/asio/spawn.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <string> + +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> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Sends a WebSocket message and prints the response +void +do_session( + std::string host, + std::string const& port, + std::string const& text, + net::io_context& ioc, + net::yield_context yield) +{ + beast::error_code ec; + + // These objects perform our I/O + tcp::resolver resolver(ioc); + websocket::stream<beast::tcp_stream> ws(ioc); + + // Look up the domain name + auto const results = resolver.async_resolve(host, port, yield[ec]); + if(ec) + return fail(ec, "resolve"); + + // Set a timeout on the operation + beast::get_lowest_layer(ws).expires_after(std::chrono::seconds(30)); + + // Make the connection on the IP address we get from a lookup + auto ep = beast::get_lowest_layer(ws).async_connect(results, yield[ec]); + if(ec) + return fail(ec, "connect"); + + // Update the host_ string. This will provide the value of the + // Host HTTP header during the WebSocket handshake. + // See https://tools.ietf.org/html/rfc7230#section-5.4 + host += ':' + std::to_string(ep.port()); + + // Turn off the timeout on the tcp_stream, because + // the websocket stream has its own timeout system. + beast::get_lowest_layer(ws).expires_never(); + + // Set suggested timeout settings for the websocket + ws.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::client)); + + // Set a decorator to change the User-Agent of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-coro"); + })); + + // Perform the websocket handshake + ws.async_handshake(host, "/", yield[ec]); + if(ec) + return fail(ec, "handshake"); + + // Send the message + ws.async_write(net::buffer(std::string(text)), yield[ec]); + if(ec) + return fail(ec, "write"); + + // This buffer will hold the incoming message + beast::flat_buffer buffer; + + // Read a message into our buffer + ws.async_read(buffer, yield[ec]); + if(ec) + return fail(ec, "read"); + + // Close the WebSocket connection + ws.async_close(websocket::close_code::normal, yield[ec]); + if(ec) + return fail(ec, "close"); + + // If we get here then the connection is closed gracefully + + // The make_printable() function helps print a ConstBufferSequence + std::cout << beast::make_printable(buffer.data()) << std::endl; +} + +//------------------------------------------------------------------------------ + +int main(int argc, char** argv) +{ + // Check command line arguments. + if(argc != 4) + { + std::cerr << + "Usage: websocket-client-coro <host> <port> <text>\n" << + "Example:\n" << + " websocket-client-coro echo.websocket.org 80 \"Hello, world!\"\n"; + return EXIT_FAILURE; + } + auto const host = argv[1]; + auto const port = argv[2]; + auto const text = argv[3]; + + // The io_context is required for all I/O + net::io_context ioc; + + // Launch the asynchronous operation + boost::asio::spawn(ioc, std::bind( + &do_session, + std::string(host), + std::string(port), + std::string(text), + std::ref(ioc), + std::placeholders::_1)); + + // Run the I/O service. The call will return when + // the socket is closed. + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/client/sync-ssl/CMakeLists.txt b/src/boost/libs/beast/example/websocket/client/sync-ssl/CMakeLists.txt new file mode 100644 index 000000000..b84495702 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/sync-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/websocket/client/sync-ssl "/") + + add_executable (websocket-client-sync-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/root_certificates.hpp + Jamfile + websocket_client_sync_ssl.cpp + ) + + set_property(TARGET websocket-client-sync-ssl PROPERTY FOLDER "example-websocket-client") + + target_link_libraries (websocket-client-sync-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/websocket/client/sync-ssl/Jamfile b/src/boost/libs/beast/example/websocket/client/sync-ssl/Jamfile new file mode 100644 index 000000000..0cfec0f68 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/sync-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe websocket-client-sync-ssl : + websocket_client_sync_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp b/src/boost/libs/beast/example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp new file mode 100644 index 000000000..1a51f6b6b --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/sync-ssl/websocket_client_sync_ssl.cpp @@ -0,0 +1,116 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket SSL client, synchronous +// +//------------------------------------------------------------------------------ + +#include "example/common/root_certificates.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/websocket/ssl.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/ssl/stream.hpp> +#include <cstdlib> +#include <iostream> +#include <string> + +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> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +// Sends a WebSocket message and prints the response +int main(int argc, char** argv) +{ + try + { + // Check command line arguments. + if(argc != 4) + { + std::cerr << + "Usage: websocket-client-sync-ssl <host> <port> <text>\n" << + "Example:\n" << + " websocket-client-sync-ssl echo.websocket.org 443 \"Hello, world!\"\n"; + return EXIT_FAILURE; + } + std::string host = argv[1]; + auto const port = argv[2]; + auto const text = argv[3]; + + // The io_context is required for all I/O + net::io_context ioc; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12_client}; + + // This holds the root certificate used for verification + load_root_certificates(ctx); + + // These objects perform our I/O + tcp::resolver resolver{ioc}; + websocket::stream<beast::ssl_stream<tcp::socket>> ws{ioc, ctx}; + + // Look up the domain name + auto const results = resolver.resolve(host, port); + + // Make the connection on the IP address we get from a lookup + auto ep = net::connect(get_lowest_layer(ws), results); + + // Update the host_ string. This will provide the value of the + // Host HTTP header during the WebSocket handshake. + // See https://tools.ietf.org/html/rfc7230#section-5.4 + host += ':' + std::to_string(ep.port()); + + // Perform the SSL handshake + ws.next_layer().handshake(ssl::stream_base::client); + + // Set a decorator to change the User-Agent of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-coro"); + })); + + // Perform the websocket handshake + ws.handshake(host, "/"); + + // Send the message + ws.write(net::buffer(std::string(text))); + + // This buffer will hold the incoming message + beast::flat_buffer buffer; + + // Read a message into our buffer + ws.read(buffer); + + // Close the WebSocket connection + ws.close(websocket::close_code::normal); + + // If we get here then the connection is closed gracefully + + // The make_printable() function helps print a ConstBufferSequence + std::cout << beast::make_printable(buffer.data()) << 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/example/websocket/client/sync/CMakeLists.txt b/src/boost/libs/beast/example/websocket/client/sync/CMakeLists.txt new file mode 100644 index 000000000..d8606ab55 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/sync/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/websocket/client/sync "/") + +add_executable (websocket-client-sync + ${BOOST_BEAST_FILES} + Jamfile + websocket_client_sync.cpp +) + +target_link_libraries(websocket-client-sync + lib-asio + lib-beast) + +set_property(TARGET websocket-client-sync PROPERTY FOLDER "example-websocket-client") diff --git a/src/boost/libs/beast/example/websocket/client/sync/Jamfile b/src/boost/libs/beast/example/websocket/client/sync/Jamfile new file mode 100644 index 000000000..ea71a0ce6 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/sync/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 websocket-client-sync : + websocket_client_sync.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/client/sync/websocket_client_sync.cpp b/src/boost/libs/beast/example/websocket/client/sync/websocket_client_sync.cpp new file mode 100644 index 000000000..07409b70e --- /dev/null +++ b/src/boost/libs/beast/example/websocket/client/sync/websocket_client_sync.cpp @@ -0,0 +1,105 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket client, synchronous +// +//------------------------------------------------------------------------------ + +//[example_websocket_client + +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/asio/connect.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <cstdlib> +#include <iostream> +#include <string> + +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> + +// Sends a WebSocket message and prints the response +int main(int argc, char** argv) +{ + try + { + // Check command line arguments. + if(argc != 4) + { + std::cerr << + "Usage: websocket-client-sync <host> <port> <text>\n" << + "Example:\n" << + " websocket-client-sync echo.websocket.org 80 \"Hello, world!\"\n"; + return EXIT_FAILURE; + } + std::string host = argv[1]; + auto const port = argv[2]; + auto const text = argv[3]; + + // The io_context is required for all I/O + net::io_context ioc; + + // These objects perform our I/O + tcp::resolver resolver{ioc}; + websocket::stream<tcp::socket> ws{ioc}; + + // Look up the domain name + auto const results = resolver.resolve(host, port); + + // Make the connection on the IP address we get from a lookup + auto ep = net::connect(ws.next_layer(), results); + + // Update the host_ string. This will provide the value of the + // Host HTTP header during the WebSocket handshake. + // See https://tools.ietf.org/html/rfc7230#section-5.4 + host += ':' + std::to_string(ep.port()); + + // Set a decorator to change the User-Agent of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::request_type& req) + { + req.set(http::field::user_agent, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-client-coro"); + })); + + // Perform the websocket handshake + ws.handshake(host, "/"); + + // Send the message + ws.write(net::buffer(std::string(text))); + + // This buffer will hold the incoming message + beast::flat_buffer buffer; + + // Read a message into our buffer + ws.read(buffer); + + // Close the WebSocket connection + ws.close(websocket::close_code::normal); + + // If we get here then the connection is closed gracefully + + // The make_printable() function helps print a ConstBufferSequence + std::cout << beast::make_printable(buffer.data()) << 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/example/websocket/server/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/CMakeLists.txt new file mode 100644 index 000000000..c5b8da5d6 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright (c) 2013-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 (async) +add_subdirectory (chat-multi) +add_subdirectory (coro) +add_subdirectory (fast) +add_subdirectory (stackless) +add_subdirectory (sync) + +if (OPENSSL_FOUND) + add_subdirectory (async-ssl) + add_subdirectory (coro-ssl) + add_subdirectory (stackless-ssl) + add_subdirectory (sync-ssl) +endif() diff --git a/src/boost/libs/beast/example/websocket/server/Jamfile b/src/boost/libs/beast/example/websocket/server/Jamfile new file mode 100644 index 000000000..ccfd63fa4 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/Jamfile @@ -0,0 +1,21 @@ +# +# Copyright (c) 2013-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 +# + +build-project async ; +build-project chat-multi ; +build-project coro ; +build-project fast ; +build-project stackless ; +build-project sync ; + +# SSL +build-project async-ssl ; +build-project coro-ssl ; +build-project stackless-ssl ; +build-project sync-ssl ; diff --git a/src/boost/libs/beast/example/websocket/server/async-ssl/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/async-ssl/CMakeLists.txt new file mode 100644 index 000000000..8f1055d2f --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/async-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/websocket/server/async-ssl "/") + + add_executable (websocket-server-async-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + websocket_server_async_ssl.cpp + ) + + set_property(TARGET websocket-server-async-ssl PROPERTY FOLDER "example-websocket-server") + + target_link_libraries (websocket-server-async-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/websocket/server/async-ssl/Jamfile b/src/boost/libs/beast/example/websocket/server/async-ssl/Jamfile new file mode 100644 index 000000000..f9b253a0c --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/async-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe websocket-server-async-ssl : + websocket_server_async_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp b/src/boost/libs/beast/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp new file mode 100644 index 000000000..b79abbbfb --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/async-ssl/websocket_server_async_ssl.cpp @@ -0,0 +1,317 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket SSL server, asynchronous +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/websocket/ssl.hpp> +#include <boost/asio/strand.hpp> +#include <boost/asio/dispatch.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#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> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Echoes back all received WebSocket messages +class session : public std::enable_shared_from_this<session> +{ + websocket::stream< + beast::ssl_stream<beast::tcp_stream>> ws_; + beast::flat_buffer buffer_; + +public: + // Take ownership of the socket + session(tcp::socket&& socket, ssl::context& ctx) + : ws_(std::move(socket), ctx) + { + } + + // Get on the correct executor + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch(ws_.get_executor(), + beast::bind_front_handler( + &session::on_run, + shared_from_this())); + } + + // Start the asynchronous operation + void + on_run() + { + // Set the timeout. + beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL handshake + ws_.next_layer().async_handshake( + ssl::stream_base::server, + beast::bind_front_handler( + &session::on_handshake, + shared_from_this())); + } + + void + on_handshake(beast::error_code ec) + { + if(ec) + return fail(ec, "handshake"); + + // Turn off the timeout on the tcp_stream, because + // the websocket stream has its own timeout system. + beast::get_lowest_layer(ws_).expires_never(); + + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-async-ssl"); + })); + + // Accept the websocket handshake + ws_.async_accept( + beast::bind_front_handler( + &session::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec) + { + if(ec) + return fail(ec, "accept"); + + // Read a message + do_read(); + } + + void + do_read() + { + // Read a message into our buffer + ws_.async_read( + buffer_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This indicates that the session was closed + if(ec == websocket::error::closed) + return; + + if(ec) + fail(ec, "read"); + + // Echo the message + ws_.text(ws_.got_text()); + ws_.async_write( + buffer_.data(), + beast::bind_front_handler( + &session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Clear the buffer + buffer_.consume(buffer_.size()); + + // Do another read + do_read(); + } +}; + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +class listener : public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + ssl::context& ctx_; + tcp::acceptor acceptor_; + +public: + listener( + net::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(net::make_strand(ioc)) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + do_accept(); + } + +private: + void + do_accept() + { + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &listener::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the session and run it + std::make_shared<session>(std::move(socket), ctx_)->run(); + } + + // Accept another connection + do_accept(); + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: websocket-server-async-ssl <address> <port> <threads>\n" << + "Example:\n" << + " websocket-server-async-ssl 0.0.0.0 8080 1\n"; + 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 threads = std::max<int>(1, std::atoi(argv[3])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // Create and launch a listening port + std::make_shared<listener>(ioc, ctx, tcp::endpoint{address, port})->run(); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/server/async/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/async/CMakeLists.txt new file mode 100644 index 000000000..0e1ceb8b6 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/async/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/websocket/server/async "/") + +add_executable (websocket-server-async + ${BOOST_BEAST_FILES} + Jamfile + websocket_server_async.cpp +) + +target_link_libraries(websocket-server-async + lib-asio + lib-beast) + +set_property(TARGET websocket-server-async PROPERTY FOLDER "example-websocket-server") diff --git a/src/boost/libs/beast/example/websocket/server/async/Jamfile b/src/boost/libs/beast/example/websocket/server/async/Jamfile new file mode 100644 index 000000000..e1e63e14d --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/async/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 websocket-server-async : + websocket_server_async.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/server/async/websocket_server_async.cpp b/src/boost/libs/beast/example/websocket/server/async/websocket_server_async.cpp new file mode 100644 index 000000000..4b9570868 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/async/websocket_server_async.cpp @@ -0,0 +1,281 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket server, asynchronous +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/asio/dispatch.hpp> +#include <boost/asio/strand.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#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> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Echoes back all received WebSocket messages +class session : public std::enable_shared_from_this<session> +{ + websocket::stream<beast::tcp_stream> ws_; + beast::flat_buffer buffer_; + +public: + // Take ownership of the socket + explicit + session(tcp::socket&& socket) + : ws_(std::move(socket)) + { + } + + // Get on the correct executor + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch(ws_.get_executor(), + beast::bind_front_handler( + &session::on_run, + shared_from_this())); + } + + // Start the asynchronous operation + void + on_run() + { + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-async"); + })); + // Accept the websocket handshake + ws_.async_accept( + beast::bind_front_handler( + &session::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec) + { + if(ec) + return fail(ec, "accept"); + + // Read a message + do_read(); + } + + void + do_read() + { + // Read a message into our buffer + ws_.async_read( + buffer_, + beast::bind_front_handler( + &session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This indicates that the session was closed + if(ec == websocket::error::closed) + return; + + if(ec) + fail(ec, "read"); + + // Echo the message + ws_.text(ws_.got_text()); + ws_.async_write( + buffer_.data(), + beast::bind_front_handler( + &session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Clear the buffer + buffer_.consume(buffer_.size()); + + // Do another read + do_read(); + } +}; + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +class listener : public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + tcp::acceptor acceptor_; + +public: + listener( + net::io_context& ioc, + tcp::endpoint endpoint) + : ioc_(ioc) + , acceptor_(ioc) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + do_accept(); + } + +private: + void + do_accept() + { + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &listener::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the session and run it + std::make_shared<session>(std::move(socket))->run(); + } + + // Accept another connection + do_accept(); + } +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: websocket-server-async <address> <port> <threads>\n" << + "Example:\n" << + " websocket-server-async 0.0.0.0 8080 1\n"; + 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 threads = std::max<int>(1, std::atoi(argv[3])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // Create and launch a listening port + std::make_shared<listener>(ioc, tcp::endpoint{address, port})->run(); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/chat-multi/CMakeLists.txt new file mode 100644 index 000000000..a256b000f --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/CMakeLists.txt @@ -0,0 +1,41 @@ +# +# 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(example/websocket/server/chat-multi "/") + +file (GLOB APP_FILES + beast.hpp + http_session.cpp + http_session.hpp + Jamfile + listener.cpp + listener.hpp + main.cpp + net.hpp + shared_state.cpp + shared_state.hpp + websocket_session.cpp + websocket_session.hpp + chat_client.html + README.md +) + +source_group ("" FILES ${APP_FILES}) + +add_executable (websocket-chat-multi + ${APP_FILES} + ${BOOST_BEAST_FILES} +) + +target_link_libraries(websocket-chat-multi + lib-asio + lib-beast) + +set_property(TARGET websocket-chat-multi PROPERTY FOLDER "example-websocket-server") diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/Jamfile b/src/boost/libs/beast/example/websocket/server/chat-multi/Jamfile new file mode 100644 index 000000000..a8d49bfdd --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/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 websocket-chat-multi : + http_session.cpp + listener.cpp + main.cpp + shared_state.cpp + websocket_session.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/beast.hpp b/src/boost/libs/beast/example/websocket/server/chat-multi/beast.hpp new file mode 100644 index 000000000..0b7a9c61a --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/beast.hpp @@ -0,0 +1,19 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#ifndef BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_BEAST_HPP +#define BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_BEAST_HPP + +#include <boost/beast.hpp> + +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> + +#endif diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/chat_client.html b/src/boost/libs/beast/example/websocket/server/chat-multi/chat_client.html new file mode 100644 index 000000000..2be69cdf0 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/chat_client.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8" /> + <title>Boost.Beast WebSocket Chat Client</title> +</head> +<body> + <h1>Boost.Beast WebSocket Chat Client</h1> + <p> + <a href="http://www.boost.org/libs/beast">Boost.Beast</a> + <a href="http://www.boost.org/libs/beast/example/websocket/server/chat-multi">Source Code</a> + </p> + Server URI: <input class="draw-border" id="uri" size="47" value="ws://localhost:8080" style="margin-bottom: 5px;"> + <button class="echo-button" id="connect">Connect</button> + <button class="echo-button" id="disconnect">Disconnect</button><br> + Your Name: <input class="draw-border" id="userName" size=47 style="margin-bottom: 5px;"><br> + <pre id="messages" style="width: 600px; height: 400px; white-space: normal; overflow: auto; border: solid 1px #cccccc; margin-bottom: 5px;"></pre> + <div style="margin-bottom: 5px;"> + Message<br> + <input class="draw-border" id="sendMessage" size="74" value=""> + <button class="echo-button" id="send">Send</button> + </div> + <script> + var ws = null; + function showMessage(msg) { + messages.innerText += msg + "\n"; + messages.scrollTop = messages.scrollHeight - messages.clientHeight; + }; + connect.onclick = function() { + ws = new WebSocket(uri.value); + ws.onopen = function(ev) { + showMessage("[connection opened]"); + }; + ws.onclose = function(ev) { + showMessage("[connection closed]"); + }; + ws.onmessage = function(ev) { + showMessage(ev.data); + }; + ws.onerror = function(ev) { + showMessage("[error]"); + console.log(ev); + }; + }; + disconnect.onclick = function() { + ws.close(); + }; + send.onclick = function() { + ws.send(userName.value + ": " + sendMessage.value); + sendMessage.value = ""; + }; + sendMessage.onkeyup = function(ev) { + ev.preventDefault(); + if (ev.keyCode === 13) { + send.click(); + } + } + </script> +</body> +</html> diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/http_session.cpp b/src/boost/libs/beast/example/websocket/server/chat-multi/http_session.cpp new file mode 100644 index 000000000..9d4dee871 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/http_session.cpp @@ -0,0 +1,371 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#include "http_session.hpp" +#include "websocket_session.hpp" +#include <boost/config.hpp> +#include <iostream> + +#define BOOST_NO_CXX14_GENERIC_LAMBDAS + +//------------------------------------------------------------------------------ + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if(base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request<Body, http::basic_fields<Allocator>>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response<http::string_body> res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response<http::string_body> res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response<http::string_body> res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == boost::system::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response<http::empty_body> res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response<http::file_body> res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +struct http_session::send_lambda +{ + http_session& self_; + + explicit + send_lambda(http_session& self) + : self_(self) + { + } + + template<bool isRequest, class Body, class Fields> + void + operator()(http::message<isRequest, Body, Fields>&& msg) const + { + // The lifetime of the message has to extend + // for the duration of the async operation so + // we use a shared_ptr to manage it. + auto sp = boost::make_shared< + http::message<isRequest, Body, Fields>>(std::move(msg)); + + // Write the response + auto self = self_.shared_from_this(); + http::async_write( + self_.stream_, + *sp, + [self, sp](beast::error_code ec, std::size_t bytes) + { + self->on_write(ec, bytes, sp->need_eof()); + }); + } +}; + +//------------------------------------------------------------------------------ + +http_session:: +http_session( + tcp::socket&& socket, + boost::shared_ptr<shared_state> const& state) + : stream_(std::move(socket)) + , state_(state) +{ +} + +void +http_session:: +run() +{ + do_read(); +} + +// Report a failure +void +http_session:: +fail(beast::error_code ec, char const* what) +{ + // Don't report on canceled operations + if(ec == net::error::operation_aborted) + return; + + std::cerr << what << ": " << ec.message() << "\n"; +} + +void +http_session:: +do_read() +{ + // Construct a new parser for each message + parser_.emplace(); + + // Apply a reasonable limit to the allowed size + // of the body in bytes to prevent abuse. + parser_->body_limit(10000); + + // Set the timeout. + stream_.expires_after(std::chrono::seconds(30)); + + // Read a request + http::async_read( + stream_, + buffer_, + parser_->get(), + beast::bind_front_handler( + &http_session::on_read, + shared_from_this())); +} + +void +http_session:: +on_read(beast::error_code ec, std::size_t) +{ + // This means they closed the connection + if(ec == http::error::end_of_stream) + { + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); + return; + } + + // Handle the error, if any + if(ec) + return fail(ec, "read"); + + // See if it is a WebSocket Upgrade + if(websocket::is_upgrade(parser_->get())) + { + // Create a websocket session, transferring ownership + // of both the socket and the HTTP request. + boost::make_shared<websocket_session>( + stream_.release_socket(), + state_)->run(parser_->release()); + return; + } + + // Send the response +#ifndef BOOST_NO_CXX14_GENERIC_LAMBDAS + // + // The following code requires generic + // lambdas, available in C++14 and later. + // + handle_request( + state_->doc_root(), + std::move(req_), + [this](auto&& response) + { + // The lifetime of the message has to extend + // for the duration of the async operation so + // we use a shared_ptr to manage it. + using response_type = typename std::decay<decltype(response)>::type; + auto sp = boost::make_shared<response_type>(std::forward<decltype(response)>(response)); + + #if 0 + // NOTE This causes an ICE in gcc 7.3 + // Write the response + http::async_write(this->stream_, *sp, + [self = shared_from_this(), sp]( + beast::error_code ec, std::size_t bytes) + { + self->on_write(ec, bytes, sp->need_eof()); + }); + #else + // Write the response + auto self = shared_from_this(); + http::async_write(stream_, *sp, + [self, sp]( + beast::error_code ec, std::size_t bytes) + { + self->on_write(ec, bytes, sp->need_eof()); + }); + #endif + }); +#else + // + // This code uses the function object type send_lambda in + // place of a generic lambda which is not available in C++11 + // + handle_request( + state_->doc_root(), + parser_->release(), + send_lambda(*this)); + +#endif +} + +void +http_session:: +on_write(beast::error_code ec, std::size_t, bool close) +{ + // Handle the error, if any + if(ec) + return fail(ec, "write"); + + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); + return; + } + + // Read another request + do_read(); +} diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/http_session.hpp b/src/boost/libs/beast/example/websocket/server/chat-multi/http_session.hpp new file mode 100644 index 000000000..637a5d898 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/http_session.hpp @@ -0,0 +1,48 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#ifndef BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_HTTP_SESSION_HPP +#define BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_HTTP_SESSION_HPP + +#include "net.hpp" +#include "beast.hpp" +#include "shared_state.hpp" +#include <boost/optional.hpp> +#include <boost/smart_ptr.hpp> +#include <cstdlib> +#include <memory> + +/** Represents an established HTTP connection +*/ +class http_session : public boost::enable_shared_from_this<http_session> +{ + beast::tcp_stream stream_; + beast::flat_buffer buffer_; + boost::shared_ptr<shared_state> state_; + + // The parser is stored in an optional container so we can + // construct it from scratch it at the beginning of each new message. + boost::optional<http::request_parser<http::string_body>> parser_; + + struct send_lambda; + + void fail(beast::error_code ec, char const* what); + void do_read(); + void on_read(beast::error_code ec, std::size_t); + void on_write(beast::error_code ec, std::size_t, bool close); + +public: + http_session( + tcp::socket&& socket, + boost::shared_ptr<shared_state> const& state); + + void run(); +}; + +#endif diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/listener.cpp b/src/boost/libs/beast/example/websocket/server/chat-multi/listener.cpp new file mode 100644 index 000000000..8bbecb49b --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/listener.cpp @@ -0,0 +1,101 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#include "listener.hpp" +#include "http_session.hpp" +#include <iostream> + +listener:: +listener( + net::io_context& ioc, + tcp::endpoint endpoint, + boost::shared_ptr<shared_state> const& state) + : ioc_(ioc) + , acceptor_(ioc) + , state_(state) +{ + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } +} + +void +listener:: +run() +{ + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &listener::on_accept, + shared_from_this())); +} + +// Report a failure +void +listener:: +fail(beast::error_code ec, char const* what) +{ + // Don't report on canceled operations + if(ec == net::error::operation_aborted) + return; + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Handle a connection +void +listener:: +on_accept(beast::error_code ec, tcp::socket socket) +{ + if(ec) + return fail(ec, "accept"); + else + // Launch a new session for this connection + boost::make_shared<http_session>( + std::move(socket), + state_)->run(); + + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &listener::on_accept, + shared_from_this())); +} diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/listener.hpp b/src/boost/libs/beast/example/websocket/server/chat-multi/listener.hpp new file mode 100644 index 000000000..a0c536e2e --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/listener.hpp @@ -0,0 +1,42 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#ifndef BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_LISTENER_HPP +#define BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_LISTENER_HPP + +#include "beast.hpp" +#include "net.hpp" +#include <boost/smart_ptr.hpp> +#include <memory> +#include <string> + +// Forward declaration +class shared_state; + +// Accepts incoming connections and launches the sessions +class listener : public boost::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + tcp::acceptor acceptor_; + boost::shared_ptr<shared_state> state_; + + void fail(beast::error_code ec, char const* what); + void on_accept(beast::error_code ec, tcp::socket socket); + +public: + listener( + net::io_context& ioc, + tcp::endpoint endpoint, + boost::shared_ptr<shared_state> const& state); + + // Start accepting incoming connections + void run(); +}; + +#endif diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/main.cpp b/src/boost/libs/beast/example/websocket/server/chat-multi/main.cpp new file mode 100644 index 000000000..8645f1ded --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/main.cpp @@ -0,0 +1,84 @@ +// +// 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/vinniefalco/CppCon2018 +// + +//------------------------------------------------------------------------------ +/* + WebSocket chat server, multi-threaded + + This implements a multi-user chat room using WebSocket. The + `io_context` runs on any number of threads, specified at + the command line. + +*/ +//------------------------------------------------------------------------------ + +#include "listener.hpp" +#include "shared_state.hpp" + +#include <boost/asio/signal_set.hpp> +#include <boost/smart_ptr.hpp> +#include <iostream> +#include <vector> + +int +main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 5) + { + std::cerr << + "Usage: websocket-chat-multi <address> <port> <doc_root> <threads>\n" << + "Example:\n" << + " websocket-chat-server 0.0.0.0 8080 . 5\n"; + return EXIT_FAILURE; + } + auto address = net::ip::make_address(argv[1]); + auto port = static_cast<unsigned short>(std::atoi(argv[2])); + auto doc_root = argv[3]; + auto const threads = std::max<int>(1, std::atoi(argv[4])); + + // The io_context is required for all I/O + net::io_context ioc; + + // Create and launch a listening port + boost::make_shared<listener>( + ioc, + tcp::endpoint{address, port}, + boost::make_shared<shared_state>(doc_root))->run(); + + // Capture SIGINT and SIGTERM to perform a clean shutdown + net::signal_set signals(ioc, SIGINT, SIGTERM); + signals.async_wait( + [&ioc](boost::system::error_code const&, int) + { + // Stop the io_context. This will cause run() + // to return immediately, eventually destroying the + // io_context and any remaining handlers in it. + ioc.stop(); + }); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + // (If we get here, it means we got a SIGINT or SIGTERM) + + // Block until all the threads exit + for(auto& t : v) + t.join(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/net.hpp b/src/boost/libs/beast/example/websocket/server/chat-multi/net.hpp new file mode 100644 index 000000000..48a215355 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/net.hpp @@ -0,0 +1,18 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#ifndef BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_NET_HPP +#define BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_NET_HPP + +#include <boost/asio.hpp> + +namespace net = boost::asio; // from <boost/asio.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +#endif diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/shared_state.cpp b/src/boost/libs/beast/example/websocket/server/chat-multi/shared_state.cpp new file mode 100644 index 000000000..9fceecc5d --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/shared_state.cpp @@ -0,0 +1,59 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#include "shared_state.hpp" +#include "websocket_session.hpp" + +shared_state:: +shared_state(std::string doc_root) + : doc_root_(std::move(doc_root)) +{ +} + +void +shared_state:: +join(websocket_session* session) +{ + std::lock_guard<std::mutex> lock(mutex_); + sessions_.insert(session); +} + +void +shared_state:: +leave(websocket_session* session) +{ + std::lock_guard<std::mutex> lock(mutex_); + sessions_.erase(session); +} + +// Broadcast a message to all websocket client sessions +void +shared_state:: +send(std::string message) +{ + // Put the message in a shared pointer so we can re-use it for each client + auto const ss = boost::make_shared<std::string const>(std::move(message)); + + // Make a local list of all the weak pointers representing + // the sessions, so we can do the actual sending without + // holding the mutex: + std::vector<boost::weak_ptr<websocket_session>> v; + { + std::lock_guard<std::mutex> lock(mutex_); + v.reserve(sessions_.size()); + for(auto p : sessions_) + v.emplace_back(p->weak_from_this()); + } + + // For each session in our local list, try to acquire a strong + // pointer. If successful, then send the message on that session. + for(auto const& wp : v) + if(auto sp = wp.lock()) + sp->send(ss); +} diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/shared_state.hpp b/src/boost/libs/beast/example/websocket/server/chat-multi/shared_state.hpp new file mode 100644 index 000000000..08f4b2d34 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/shared_state.hpp @@ -0,0 +1,48 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#ifndef BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_SHARED_STATE_HPP +#define BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_SHARED_STATE_HPP + +#include <boost/smart_ptr.hpp> +#include <memory> +#include <mutex> +#include <string> +#include <unordered_set> + +// Forward declaration +class websocket_session; + +// Represents the shared server state +class shared_state +{ + std::string const doc_root_; + + // This mutex synchronizes all access to sessions_ + std::mutex mutex_; + + // Keep a list of all the connected clients + std::unordered_set<websocket_session*> sessions_; + +public: + explicit + shared_state(std::string doc_root); + + std::string const& + doc_root() const noexcept + { + return doc_root_; + } + + void join (websocket_session* session); + void leave (websocket_session* session); + void send (std::string message); +}; + +#endif diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/websocket_session.cpp b/src/boost/libs/beast/example/websocket/server/chat-multi/websocket_session.cpp new file mode 100644 index 000000000..4807a5f1e --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/websocket_session.cpp @@ -0,0 +1,135 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#include "websocket_session.hpp" +#include <iostream> + +websocket_session:: +websocket_session( + tcp::socket&& socket, + boost::shared_ptr<shared_state> const& state) + : ws_(std::move(socket)) + , state_(state) +{ +} + +websocket_session:: +~websocket_session() +{ + // Remove this session from the list of active sessions + state_->leave(this); +} + +void +websocket_session:: +fail(beast::error_code ec, char const* what) +{ + // Don't report these + if( ec == net::error::operation_aborted || + ec == websocket::error::closed) + return; + + std::cerr << what << ": " << ec.message() << "\n"; +} + +void +websocket_session:: +on_accept(beast::error_code ec) +{ + // Handle the error, if any + if(ec) + return fail(ec, "accept"); + + // Add this session to the list of active sessions + state_->join(this); + + // Read a message + ws_.async_read( + buffer_, + beast::bind_front_handler( + &websocket_session::on_read, + shared_from_this())); +} + +void +websocket_session:: +on_read(beast::error_code ec, std::size_t) +{ + // Handle the error, if any + if(ec) + return fail(ec, "read"); + + // Send to all connections + state_->send(beast::buffers_to_string(buffer_.data())); + + // Clear the buffer + buffer_.consume(buffer_.size()); + + // Read another message + ws_.async_read( + buffer_, + beast::bind_front_handler( + &websocket_session::on_read, + shared_from_this())); +} + +void +websocket_session:: +send(boost::shared_ptr<std::string const> const& ss) +{ + // Post our work to the strand, this ensures + // that the members of `this` will not be + // accessed concurrently. + + net::post( + ws_.get_executor(), + beast::bind_front_handler( + &websocket_session::on_send, + shared_from_this(), + ss)); +} + +void +websocket_session:: +on_send(boost::shared_ptr<std::string const> const& ss) +{ + // Always add to queue + queue_.push_back(ss); + + // Are we already writing? + if(queue_.size() > 1) + return; + + // We are not currently writing, so send this immediately + ws_.async_write( + net::buffer(*queue_.front()), + beast::bind_front_handler( + &websocket_session::on_write, + shared_from_this())); +} + +void +websocket_session:: +on_write(beast::error_code ec, std::size_t) +{ + // Handle the error, if any + if(ec) + return fail(ec, "write"); + + // Remove the string from the queue + queue_.erase(queue_.begin()); + + // Send the next message if any + if(! queue_.empty()) + ws_.async_write( + net::buffer(*queue_.front()), + beast::bind_front_handler( + &websocket_session::on_write, + shared_from_this())); +} diff --git a/src/boost/libs/beast/example/websocket/server/chat-multi/websocket_session.hpp b/src/boost/libs/beast/example/websocket/server/chat-multi/websocket_session.hpp new file mode 100644 index 000000000..c47d48ac9 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/chat-multi/websocket_session.hpp @@ -0,0 +1,86 @@ +// +// 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/vinniefalco/CppCon2018 +// + +#ifndef BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_WEBSOCKET_SESSION_HPP +#define BOOST_BEAST_EXAMPLE_WEBSOCKET_CHAT_MULTI_WEBSOCKET_SESSION_HPP + +#include "net.hpp" +#include "beast.hpp" +#include "shared_state.hpp" + +#include <cstdlib> +#include <memory> +#include <string> +#include <vector> + +// Forward declaration +class shared_state; + +/** Represents an active WebSocket connection to the server +*/ +class websocket_session : public boost::enable_shared_from_this<websocket_session> +{ + beast::flat_buffer buffer_; + websocket::stream<beast::tcp_stream> ws_; + boost::shared_ptr<shared_state> state_; + std::vector<boost::shared_ptr<std::string const>> queue_; + + void fail(beast::error_code ec, char const* what); + void on_accept(beast::error_code ec); + void on_read(beast::error_code ec, std::size_t bytes_transferred); + void on_write(beast::error_code ec, std::size_t bytes_transferred); + +public: + websocket_session( + tcp::socket&& socket, + boost::shared_ptr<shared_state> const& state); + + ~websocket_session(); + + template<class Body, class Allocator> + void + run(http::request<Body, http::basic_fields<Allocator>> req); + + // Send a message + void + send(boost::shared_ptr<std::string const> const& ss); + +private: + void + on_send(boost::shared_ptr<std::string const> const& ss); +}; + +template<class Body, class Allocator> +void +websocket_session:: +run(http::request<Body, http::basic_fields<Allocator>> req) +{ + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-chat-multi"); + })); + + // Accept the websocket handshake + ws_.async_accept( + req, + beast::bind_front_handler( + &websocket_session::on_accept, + shared_from_this())); +} + +#endif diff --git a/src/boost/libs/beast/example/websocket/server/coro-ssl/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/coro-ssl/CMakeLists.txt new file mode 100644 index 000000000..a302a2f56 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/coro-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/websocket/server/coro-ssl "/") + + add_executable (websocket-server-coro-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + websocket_server_coro_ssl.cpp + ) + + set_property(TARGET websocket-server-coro-ssl PROPERTY FOLDER "example-websocket-server") + + target_link_libraries (websocket-server-coro-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/websocket/server/coro-ssl/Jamfile b/src/boost/libs/beast/example/websocket/server/coro-ssl/Jamfile new file mode 100644 index 000000000..621b0ce58 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/coro-ssl/Jamfile @@ -0,0 +1,23 @@ +# +# 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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe websocket-server-coro-ssl : + websocket_server_coro_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + <library>/boost/coroutine//boost_coroutine + ; diff --git a/src/boost/libs/beast/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp b/src/boost/libs/beast/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp new file mode 100644 index 000000000..3e1785f00 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/coro-ssl/websocket_server_coro_ssl.cpp @@ -0,0 +1,206 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket SSL server, coroutine +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/websocket/ssl.hpp> +#include <boost/asio/spawn.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#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> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Echoes back all received WebSocket messages +void +do_session( + websocket::stream< + beast::ssl_stream<beast::tcp_stream>>& ws, + net::yield_context yield) +{ + beast::error_code ec; + + // Set the timeout. + beast::get_lowest_layer(ws).expires_after(std::chrono::seconds(30)); + + // Perform the SSL handshake + ws.next_layer().async_handshake(ssl::stream_base::server, yield[ec]); + if(ec) + return fail(ec, "handshake"); + + // Turn off the timeout on the tcp_stream, because + // the websocket stream has its own timeout system. + beast::get_lowest_layer(ws).expires_never(); + + // Set suggested timeout settings for the websocket + ws.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-coro-ssl"); + })); + + // Accept the websocket handshake + ws.async_accept(yield[ec]); + if(ec) + return fail(ec, "accept"); + + for(;;) + { + // This buffer will hold the incoming message + beast::flat_buffer buffer; + + // Read a message + ws.async_read(buffer, yield[ec]); + + // This indicates that the session was closed + if(ec == websocket::error::closed) + break; + + if(ec) + return fail(ec, "read"); + + // Echo the message back + ws.text(ws.got_text()); + ws.async_write(buffer.data(), yield[ec]); + if(ec) + return fail(ec, "write"); + } +} + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +void +do_listen( + net::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + net::yield_context yield) +{ + beast::error_code ec; + + // Open the acceptor + tcp::acceptor acceptor(ioc); + acceptor.open(endpoint.protocol(), ec); + if(ec) + return fail(ec, "open"); + + // Allow address reuse + acceptor.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + return fail(ec, "set_option"); + + // Bind to the server address + acceptor.bind(endpoint, ec); + if(ec) + return fail(ec, "bind"); + + // Start listening for connections + acceptor.listen(net::socket_base::max_listen_connections, ec); + if(ec) + return fail(ec, "listen"); + + for(;;) + { + tcp::socket socket(ioc); + acceptor.async_accept(socket, yield[ec]); + if(ec) + fail(ec, "accept"); + else + boost::asio::spawn( + acceptor.get_executor(), + std::bind( + &do_session, + websocket::stream<beast::ssl_stream< + beast::tcp_stream>>(std::move(socket), ctx), + std::placeholders::_1)); + } +} + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: websocket-server-coro-ssl <address> <port> <threads>\n" << + "Example:\n" << + " websocket-server-coro-ssl 0.0.0.0 8080 1\n"; + 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 threads = std::max<int>(1, std::atoi(argv[3])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // Spawn a listening port + boost::asio::spawn(ioc, + std::bind( + &do_listen, + std::ref(ioc), + std::ref(ctx), + tcp::endpoint{address, port}, + std::placeholders::_1)); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/server/coro/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/coro/CMakeLists.txt new file mode 100644 index 000000000..c8e408d1c --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/coro/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/websocket/server/coro "/") + +add_executable (websocket-server-coro + ${BOOST_BEAST_FILES} + Jamfile + websocket_server_coro.cpp +) + +target_link_libraries(websocket-server-coro + lib-asio + lib-beast) + +set_property(TARGET websocket-server-coro PROPERTY FOLDER "example-websocket-server") diff --git a/src/boost/libs/beast/example/websocket/server/coro/Jamfile b/src/boost/libs/beast/example/websocket/server/coro/Jamfile new file mode 100644 index 000000000..5e8cbf948 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/coro/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 +# + +exe websocket-server-coro : + websocket_server_coro.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + <library>/boost/coroutine//boost_coroutine + ; diff --git a/src/boost/libs/beast/example/websocket/server/coro/websocket_server_coro.cpp b/src/boost/libs/beast/example/websocket/server/coro/websocket_server_coro.cpp new file mode 100644 index 000000000..e15b01079 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/coro/websocket_server_coro.cpp @@ -0,0 +1,180 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket server, coroutine +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/asio/spawn.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#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> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Echoes back all received WebSocket messages +void +do_session( + websocket::stream<beast::tcp_stream>& ws, + net::yield_context yield) +{ + beast::error_code ec; + + // Set suggested timeout settings for the websocket + ws.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-coro"); + })); + + // Accept the websocket handshake + ws.async_accept(yield[ec]); + if(ec) + return fail(ec, "accept"); + + for(;;) + { + // This buffer will hold the incoming message + beast::flat_buffer buffer; + + // Read a message + ws.async_read(buffer, yield[ec]); + + // This indicates that the session was closed + if(ec == websocket::error::closed) + break; + + if(ec) + return fail(ec, "read"); + + // Echo the message back + ws.text(ws.got_text()); + ws.async_write(buffer.data(), yield[ec]); + if(ec) + return fail(ec, "write"); + } +} + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +void +do_listen( + net::io_context& ioc, + tcp::endpoint endpoint, + net::yield_context yield) +{ + beast::error_code ec; + + // Open the acceptor + tcp::acceptor acceptor(ioc); + acceptor.open(endpoint.protocol(), ec); + if(ec) + return fail(ec, "open"); + + // Allow address reuse + acceptor.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + return fail(ec, "set_option"); + + // Bind to the server address + acceptor.bind(endpoint, ec); + if(ec) + return fail(ec, "bind"); + + // Start listening for connections + acceptor.listen(net::socket_base::max_listen_connections, ec); + if(ec) + return fail(ec, "listen"); + + for(;;) + { + tcp::socket socket(ioc); + acceptor.async_accept(socket, yield[ec]); + if(ec) + fail(ec, "accept"); + else + boost::asio::spawn( + acceptor.get_executor(), + std::bind( + &do_session, + websocket::stream< + beast::tcp_stream>(std::move(socket)), + std::placeholders::_1)); + } +} + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: websocket-server-coro <address> <port> <threads>\n" << + "Example:\n" << + " websocket-server-coro 0.0.0.0 8080 1\n"; + 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 threads = std::max<int>(1, std::atoi(argv[3])); + + // The io_context is required for all I/O + net::io_context ioc(threads); + + // Spawn a listening port + boost::asio::spawn(ioc, + std::bind( + &do_listen, + std::ref(ioc), + tcp::endpoint{address, port}, + std::placeholders::_1)); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/server/fast/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/fast/CMakeLists.txt new file mode 100644 index 000000000..93d469e91 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/fast/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/websocket/server/fast "/") + +add_executable (websocket-server-fast + ${BOOST_BEAST_FILES} + Jamfile + websocket_server_fast.cpp +) + +target_link_libraries(websocket-server-fast + lib-asio + lib-beast) + +set_property(TARGET websocket-server-fast PROPERTY FOLDER "example-websocket-server") diff --git a/src/boost/libs/beast/example/websocket/server/fast/Jamfile b/src/boost/libs/beast/example/websocket/server/fast/Jamfile new file mode 100644 index 000000000..3e6cbf5b0 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/fast/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 +# + +exe websocket-server-fast : + websocket_server_fast.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + <library>/boost/coroutine//boost_coroutine + ; diff --git a/src/boost/libs/beast/example/websocket/server/fast/websocket_server_fast.cpp b/src/boost/libs/beast/example/websocket/server/fast/websocket_server_fast.cpp new file mode 100644 index 000000000..7f2a5e3cb --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/fast/websocket_server_fast.cpp @@ -0,0 +1,480 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket server, fast +// +//------------------------------------------------------------------------------ + +/* This server contains the following ports: + + Synchronous <base port + 0> + Asynchronous <base port + 1> + Coroutine <base port + 2> + + This program is optimized for the Autobahn|Testsuite + benchmarking and WebSocket compliants testing program. + + See: + https://github.com/crossbario/autobahn-testsuite +*/ + +#include <boost/beast/core.hpp> +#include <boost/beast/http.hpp> +#include <boost/beast/version.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/asio/spawn.hpp> +#include <boost/asio/strand.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#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> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << (std::string(what) + ": " + ec.message() + "\n"); +} + +// Adjust settings on the stream +template<class NextLayer> +void +setup_stream(websocket::stream<NextLayer>& ws) +{ + // These values are tuned for Autobahn|Testsuite, and + // should also be generally helpful for increased performance. + + websocket::permessage_deflate pmd; + pmd.client_enable = true; + pmd.server_enable = true; + pmd.compLevel = 3; + ws.set_option(pmd); + + ws.auto_fragment(false); + + // Autobahn|Testsuite needs this + ws.read_message_max(64 * 1024 * 1024); +} + +//------------------------------------------------------------------------------ + +void +do_sync_session(websocket::stream<beast::tcp_stream>& ws) +{ + beast::error_code ec; + + setup_stream(ws); + + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, std::string( + BOOST_BEAST_VERSION_STRING) + "-Sync"); + })); + + ws.accept(ec); + if(ec) + return fail(ec, "accept"); + + for(;;) + { + beast::flat_buffer buffer; + + ws.read(buffer, ec); + if(ec == websocket::error::closed) + break; + if(ec) + return fail(ec, "read"); + ws.text(ws.got_text()); + ws.write(buffer.data(), ec); + if(ec) + return fail(ec, "write"); + } +} + +void +do_sync_listen( + net::io_context& ioc, + tcp::endpoint endpoint) +{ + beast::error_code ec; + tcp::acceptor acceptor{ioc, endpoint}; + for(;;) + { + tcp::socket socket{ioc}; + + acceptor.accept(socket, ec); + if(ec) + return fail(ec, "accept"); + + std::thread(std::bind( + &do_sync_session, + websocket::stream<beast::tcp_stream>( + std::move(socket)))).detach(); + } +} + +//------------------------------------------------------------------------------ + +// Echoes back all received WebSocket messages +class async_session : public std::enable_shared_from_this<async_session> +{ + websocket::stream<beast::tcp_stream> ws_; + beast::flat_buffer buffer_; + +public: + // Take ownership of the socket + explicit + async_session(tcp::socket&& socket) + : ws_(std::move(socket)) + { + setup_stream(ws_); + } + + // Start the asynchronous operation + void + run() + { + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, std::string( + BOOST_BEAST_VERSION_STRING) + "-Async"); + })); + + // Accept the websocket handshake + ws_.async_accept( + beast::bind_front_handler( + &async_session::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec) + { + if(ec) + return fail(ec, "accept"); + + // Read a message + do_read(); + } + + void + do_read() + { + // Read a message into our buffer + ws_.async_read( + buffer_, + beast::bind_front_handler( + &async_session::on_read, + shared_from_this())); + } + + void + on_read( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + // This indicates that the async_session was closed + if(ec == websocket::error::closed) + return; + + if(ec) + fail(ec, "read"); + + // Echo the message + ws_.text(ws_.got_text()); + ws_.async_write( + buffer_.data(), + beast::bind_front_handler( + &async_session::on_write, + shared_from_this())); + } + + void + on_write( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + if(ec) + return fail(ec, "write"); + + // Clear the buffer + buffer_.consume(buffer_.size()); + + // Do another read + do_read(); + } +}; + +// Accepts incoming connections and launches the sessions +class async_listener : public std::enable_shared_from_this<async_listener> +{ + net::io_context& ioc_; + tcp::acceptor acceptor_; + +public: + async_listener( + net::io_context& ioc, + tcp::endpoint endpoint) + : ioc_(ioc) + , acceptor_(net::make_strand(ioc)) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + do_accept(); + } + +private: + void + do_accept() + { + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &async_listener::on_accept, + shared_from_this())); + } + + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the async_session and run it + std::make_shared<async_session>(std::move(socket))->run(); + } + + // Accept another connection + do_accept(); + } +}; + +//------------------------------------------------------------------------------ + +void +do_coro_session( + websocket::stream<beast::tcp_stream>& ws, + net::yield_context yield) +{ + beast::error_code ec; + + setup_stream(ws); + + // Set suggested timeout settings for the websocket + ws.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, std::string( + BOOST_BEAST_VERSION_STRING) + "-Fiber"); + })); + + ws.async_accept(yield[ec]); + if(ec) + return fail(ec, "accept"); + + for(;;) + { + beast::flat_buffer buffer; + + ws.async_read(buffer, yield[ec]); + if(ec == websocket::error::closed) + break; + if(ec) + return fail(ec, "read"); + + ws.text(ws.got_text()); + ws.async_write(buffer.data(), yield[ec]); + if(ec) + return fail(ec, "write"); + } +} + +void +do_coro_listen( + net::io_context& ioc, + tcp::endpoint endpoint, + net::yield_context yield) +{ + beast::error_code ec; + + tcp::acceptor acceptor(ioc); + acceptor.open(endpoint.protocol(), ec); + if(ec) + return fail(ec, "open"); + + acceptor.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + return fail(ec, "set_option"); + + acceptor.bind(endpoint, ec); + if(ec) + return fail(ec, "bind"); + + acceptor.listen(net::socket_base::max_listen_connections, ec); + if(ec) + return fail(ec, "listen"); + + for(;;) + { + tcp::socket socket(ioc); + + acceptor.async_accept(socket, yield[ec]); + if(ec) + { + fail(ec, "accept"); + continue; + } + + boost::asio::spawn( + acceptor.get_executor(), + std::bind( + &do_coro_session, + websocket::stream< + beast::tcp_stream>(std::move(socket)), + std::placeholders::_1)); + } +} + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: websocket-server-fast <address> <starting-port> <threads>\n" << + "Example:\n" + " websocket-server-fast 0.0.0.0 8080 1\n" + " Connect to:\n" + " starting-port+0 for synchronous,\n" + " starting-port+1 for asynchronous,\n" + " starting-port+2 for coroutine.\n"; + 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 threads = std::max<int>(1, std::atoi(argv[3])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // Create sync port + std::thread(beast::bind_front_handler( + &do_sync_listen, + std::ref(ioc), + tcp::endpoint{ + address, + static_cast<unsigned short>(port + 0u)} + )).detach(); + + // Create async port + std::make_shared<async_listener>( + ioc, + tcp::endpoint{ + address, + static_cast<unsigned short>(port + 1u)})->run(); + + // Create coro port + boost::asio::spawn(ioc, + std::bind( + &do_coro_listen, + std::ref(ioc), + tcp::endpoint{ + address, + static_cast<unsigned short>(port + 2u)}, + std::placeholders::_1)); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/server/stackless-ssl/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/stackless-ssl/CMakeLists.txt new file mode 100644 index 000000000..7ae66a07b --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/stackless-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/websocket/server/stackless-ssl "/") + + add_executable (websocket-server-stackless-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + websocket_server_stackless_ssl.cpp + ) + + set_property(TARGET websocket-server-stackless-ssl PROPERTY FOLDER "example-websocket-server") + + target_link_libraries (websocket-server-stackless-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/websocket/server/stackless-ssl/Jamfile b/src/boost/libs/beast/example/websocket/server/stackless-ssl/Jamfile new file mode 100644 index 000000000..b0eaa4aa2 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/stackless-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe websocket-server-stackless-ssl : + websocket_server_stackless_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp b/src/boost/libs/beast/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp new file mode 100644 index 000000000..c6eaec60c --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/stackless-ssl/websocket_server_stackless_ssl.cpp @@ -0,0 +1,314 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket SSL server, stackless coroutine +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/websocket/ssl.hpp> +#include <boost/asio/coroutine.hpp> +#include <boost/asio/strand.hpp> +#include <boost/asio/dispatch.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#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> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Echoes back all received WebSocket messages +class session + : public boost::asio::coroutine + , public std::enable_shared_from_this<session> +{ + websocket::stream<beast::ssl_stream<beast::tcp_stream>> ws_; + beast::flat_buffer buffer_; + +public: + // Take ownership of the socket + session(tcp::socket&& socket, ssl::context& ctx) + : ws_(std::move(socket), ctx) + { + } + + // Start the asynchronous operation + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch(ws_.get_executor(), + beast::bind_front_handler(&session::loop, + shared_from_this(), + beast::error_code{}, + 0)); + } + + #include <boost/asio/yield.hpp> + + void + loop( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + + reenter(*this) + { + // Set the timeout. + beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30)); + + // Perform the SSL handshake + yield ws_.next_layer().async_handshake( + ssl::stream_base::server, + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + 0)); + if(ec) + return fail(ec, "handshake"); + + // Turn off the timeout on the tcp_stream, because + // the websocket stream has its own timeout system. + beast::get_lowest_layer(ws_).expires_never(); + + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-stackless-ssl"); + })); + + // Accept the websocket handshake + yield ws_.async_accept( + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + 0)); + if(ec) + return fail(ec, "accept"); + + for(;;) + { + // Read a message into our buffer + yield ws_.async_read( + buffer_, + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); + if(ec == websocket::error::closed) + { + // This indicates that the session was closed + return; + } + if(ec) + fail(ec, "read"); + + // Echo the message + ws_.text(ws_.got_text()); + yield ws_.async_write( + buffer_.data(), + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); + if(ec) + return fail(ec, "write"); + + // Clear the buffer + buffer_.consume(buffer_.size()); + } + } + } + + #include <boost/asio/unyield.hpp> +}; + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +class listener + : public boost::asio::coroutine + , public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + ssl::context& ctx_; + tcp::acceptor acceptor_; + tcp::socket socket_; + +public: + listener( + net::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint) + : ioc_(ioc) + , ctx_(ctx) + , acceptor_(ioc) + , socket_(ioc) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + loop(); + } + +private: + + #include <boost/asio/yield.hpp> + + void + loop(beast::error_code ec = {}) + { + reenter(*this) + { + for(;;) + { + yield acceptor_.async_accept( + socket_, + std::bind( + &listener::loop, + shared_from_this(), + std::placeholders::_1)); + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the session and run it + std::make_shared<session>(std::move(socket_), ctx_)->run(); + } + + // Make sure each session gets its own strand + socket_ = tcp::socket(net::make_strand(ioc_)); + } + } + } + + #include <boost/asio/unyield.hpp> +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: websocket-server-async-ssl <address> <port> <threads>\n" << + "Example:\n" << + " websocket-server-async-ssl 0.0.0.0 8080 1\n"; + 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 threads = std::max<int>(1, std::atoi(argv[3])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // Create and launch a listening port + std::make_shared<listener>(ioc, ctx, tcp::endpoint{address, port})->run(); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/server/stackless/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/stackless/CMakeLists.txt new file mode 100644 index 000000000..b260ca48b --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/stackless/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/websocket/server/stackless "/") + +add_executable (websocket-server-stackless + ${BOOST_BEAST_FILES} + Jamfile + websocket_server_stackless.cpp +) + +target_link_libraries(websocket-server-stackless + lib-asio + lib-beast) + +set_property(TARGET websocket-server-stackless PROPERTY FOLDER "example-websocket-server") diff --git a/src/boost/libs/beast/example/websocket/server/stackless/Jamfile b/src/boost/libs/beast/example/websocket/server/stackless/Jamfile new file mode 100644 index 000000000..ce9ab8987 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/stackless/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 websocket-server-stackless : + websocket_server_stackless.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/server/stackless/websocket_server_stackless.cpp b/src/boost/libs/beast/example/websocket/server/stackless/websocket_server_stackless.cpp new file mode 100644 index 000000000..c57fe2899 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/stackless/websocket_server_stackless.cpp @@ -0,0 +1,282 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket server, stackless coroutine +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/asio/coroutine.hpp> +#include <boost/asio/strand.hpp> +#include <boost/asio/dispatch.hpp> +#include <algorithm> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <memory> +#include <string> +#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> + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// Echoes back all received WebSocket messages +class session + : public boost::asio::coroutine + , public std::enable_shared_from_this<session> +{ + websocket::stream<beast::tcp_stream> ws_; + beast::flat_buffer buffer_; + +public: + // Take ownership of the socket + explicit + session(tcp::socket socket) + : ws_(std::move(socket)) + { + } + + // Start the asynchronous operation + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Although not strictly necessary + // for single-threaded contexts, this example code is written to be + // thread-safe by default. + net::dispatch(ws_.get_executor(), + beast::bind_front_handler(&session::loop, + shared_from_this(), + beast::error_code{}, + 0)); + } + + #include <boost/asio/yield.hpp> + + void + loop( + beast::error_code ec, + std::size_t bytes_transferred) + { + boost::ignore_unused(bytes_transferred); + reenter(*this) + { + // Set suggested timeout settings for the websocket + ws_.set_option( + websocket::stream_base::timeout::suggested( + beast::role_type::server)); + + // Set a decorator to change the Server of the handshake + ws_.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-stackless"); + })); + + // Accept the websocket handshake + yield ws_.async_accept( + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + 0)); + if(ec) + return fail(ec, "accept"); + + for(;;) + { + // Read a message into our buffer + yield ws_.async_read( + buffer_, + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); + if(ec == websocket::error::closed) + { + // This indicates that the session was closed + return; + } + if(ec) + fail(ec, "read"); + + // Echo the message + ws_.text(ws_.got_text()); + yield ws_.async_write( + buffer_.data(), + std::bind( + &session::loop, + shared_from_this(), + std::placeholders::_1, + std::placeholders::_2)); + if(ec) + return fail(ec, "write"); + + // Clear the buffer + buffer_.consume(buffer_.size()); + } + } + } + + #include <boost/asio/unyield.hpp> +}; + +//------------------------------------------------------------------------------ + +// Accepts incoming connections and launches the sessions +class listener + : public boost::asio::coroutine + , public std::enable_shared_from_this<listener> +{ + net::io_context& ioc_; + tcp::acceptor acceptor_; + tcp::socket socket_; + +public: + listener( + net::io_context& ioc, + tcp::endpoint endpoint) + : ioc_(ioc) + , acceptor_(net::make_strand(ioc)) + , socket_(net::make_strand(ioc)) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if(ec) + { + fail(ec, "open"); + return; + } + + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if(ec) + { + fail(ec, "set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if(ec) + { + fail(ec, "bind"); + return; + } + + // Start listening for connections + acceptor_.listen( + net::socket_base::max_listen_connections, ec); + if(ec) + { + fail(ec, "listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + loop(); + } + +private: + + #include <boost/asio/yield.hpp> + + void + loop(beast::error_code ec = {}) + { + reenter(*this) + { + for(;;) + { + yield acceptor_.async_accept( + socket_, + std::bind( + &listener::loop, + shared_from_this(), + std::placeholders::_1)); + if(ec) + { + fail(ec, "accept"); + } + else + { + // Create the session and run it + std::make_shared<session>(std::move(socket_))->run(); + } + + // Make sure each session gets its own strand + socket_ = tcp::socket(net::make_strand(ioc_)); + } + } + } + + #include <boost/asio/unyield.hpp> +}; + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: websocket-server-stackless <address> <port> <threads>\n" << + "Example:\n" << + " websocket-server-stackless 0.0.0.0 8080 1\n"; + 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 threads = std::max<int>(1, std::atoi(argv[3])); + + // The io_context is required for all I/O + net::io_context ioc{threads}; + + // Create and launch a listening port + std::make_shared<listener>(ioc, tcp::endpoint{address, port})->run(); + + // Run the I/O service on the requested number of threads + std::vector<std::thread> v; + v.reserve(threads - 1); + for(auto i = threads - 1; i > 0; --i) + v.emplace_back( + [&ioc] + { + ioc.run(); + }); + ioc.run(); + + return EXIT_SUCCESS; +} diff --git a/src/boost/libs/beast/example/websocket/server/sync-ssl/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/sync-ssl/CMakeLists.txt new file mode 100644 index 000000000..0ad254ce5 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/sync-ssl/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 +# + +if (OPENSSL_FOUND) + GroupSources(include/boost/beast beast) + GroupSources(example/common common) + GroupSources(example/websocket/server/sync-ssl "/") + + add_executable (websocket-server-sync-ssl + ${BOOST_BEAST_FILES} + ${PROJECT_SOURCE_DIR}/example/common/server_certificate.hpp + Jamfile + websocket_server_sync_ssl.cpp + ) + + set_property(TARGET websocket-server-sync-ssl PROPERTY FOLDER "example-websocket-server") + + target_link_libraries (websocket-server-sync-ssl + OpenSSL::SSL OpenSSL::Crypto + lib-asio + lib-asio-ssl + lib-beast + ) + +endif() diff --git a/src/boost/libs/beast/example/websocket/server/sync-ssl/Jamfile b/src/boost/libs/beast/example/websocket/server/sync-ssl/Jamfile new file mode 100644 index 000000000..55ac35b84 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/sync-ssl/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 +# + +import ac ; + +project + : requirements + [ ac.check-library /boost/beast//lib-asio-ssl : <library>/boost/beast//lib-asio-ssl/<link>static : <build>no ] + ; + +exe websocket-server-sync-ssl : + websocket_server_sync_ssl.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/server/sync-ssl/websocket_server_sync_ssl.cpp b/src/boost/libs/beast/example/websocket/server/sync-ssl/websocket_server_sync_ssl.cpp new file mode 100644 index 000000000..5a7b4dc06 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/sync-ssl/websocket_server_sync_ssl.cpp @@ -0,0 +1,137 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket SSL server, synchronous +// +//------------------------------------------------------------------------------ + +#include "example/common/server_certificate.hpp" + +#include <boost/beast/core.hpp> +#include <boost/beast/ssl.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/beast/websocket/ssl.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/ssl/stream.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <string> +#include <thread> + +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> +namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> +using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> + +//------------------------------------------------------------------------------ + +// Echoes back all received WebSocket messages +void +do_session(tcp::socket& socket, ssl::context& ctx) +{ + try + { + // Construct the websocket stream around the socket + websocket::stream<beast::ssl_stream<tcp::socket&>> ws{socket, ctx}; + + // Perform the SSL handshake + ws.next_layer().handshake(ssl::stream_base::server); + + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-sync-ssl"); + })); + + // Accept the websocket handshake + ws.accept(); + + for(;;) + { + // This buffer will hold the incoming message + beast::flat_buffer buffer; + + // Read a message + ws.read(buffer); + + // Echo the message back + ws.text(ws.got_text()); + ws.write(buffer.data()); + } + } + catch(beast::system_error const& se) + { + // This indicates that the session was closed + if(se.code() != websocket::error::closed) + std::cerr << "Error: " << se.code().message() << std::endl; + } + catch(std::exception const& e) + { + std::cerr << "Error: " << e.what() << std::endl; + } +} + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 3) + { + std::cerr << + "Usage: websocket-server-sync-ssl <address> <port>\n" << + "Example:\n" << + " websocket-server-sync-ssl 0.0.0.0 8080\n"; + return EXIT_FAILURE; + } + auto const address = net::ip::make_address(argv[1]); + auto const port = static_cast<unsigned short>(std::atoi(argv[2])); + + // The io_context is required for all I/O + net::io_context ioc{1}; + + // The SSL context is required, and holds certificates + ssl::context ctx{ssl::context::tlsv12}; + + // This holds the self-signed certificate used by the server + load_server_certificate(ctx); + + // The acceptor receives incoming connections + tcp::acceptor acceptor{ioc, {address, port}}; + for(;;) + { + // This will receive the new connection + tcp::socket socket{ioc}; + + // Block until we get a connection + acceptor.accept(socket); + + // Launch the session, transferring ownership of the socket + std::thread{std::bind( + &do_session, + std::move(socket), + std::ref(ctx))}.detach(); + } + } + catch (const std::exception& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} diff --git a/src/boost/libs/beast/example/websocket/server/sync/CMakeLists.txt b/src/boost/libs/beast/example/websocket/server/sync/CMakeLists.txt new file mode 100644 index 000000000..486998615 --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/sync/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# 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(example/websocket/server/sync "/") + +add_executable (websocket-server-sync + ${BOOST_BEAST_FILES} + Jamfile + websocket_server_sync.cpp +) + +target_link_libraries(websocket-server-sync + lib-asio + lib-beast) + +set_property(TARGET websocket-server-sync PROPERTY FOLDER "example-websocket-server") diff --git a/src/boost/libs/beast/example/websocket/server/sync/Jamfile b/src/boost/libs/beast/example/websocket/server/sync/Jamfile new file mode 100644 index 000000000..7fd14ce4a --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/sync/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 websocket-server-sync : + websocket_server_sync.cpp + : + <variant>coverage:<build>no + <variant>ubasan:<build>no + ; diff --git a/src/boost/libs/beast/example/websocket/server/sync/websocket_server_sync.cpp b/src/boost/libs/beast/example/websocket/server/sync/websocket_server_sync.cpp new file mode 100644 index 000000000..2f75c468e --- /dev/null +++ b/src/boost/libs/beast/example/websocket/server/sync/websocket_server_sync.cpp @@ -0,0 +1,121 @@ +// +// 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 +// + +//------------------------------------------------------------------------------ +// +// Example: WebSocket server, synchronous +// +//------------------------------------------------------------------------------ + +#include <boost/beast/core.hpp> +#include <boost/beast/websocket.hpp> +#include <boost/asio/ip/tcp.hpp> +#include <cstdlib> +#include <functional> +#include <iostream> +#include <string> +#include <thread> + +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> + +//------------------------------------------------------------------------------ + +// Echoes back all received WebSocket messages +void +do_session(tcp::socket& socket) +{ + try + { + // Construct the stream by moving in the socket + websocket::stream<tcp::socket> ws{std::move(socket)}; + + // Set a decorator to change the Server of the handshake + ws.set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " websocket-server-sync"); + })); + + // Accept the websocket handshake + ws.accept(); + + for(;;) + { + // This buffer will hold the incoming message + beast::flat_buffer buffer; + + // Read a message + ws.read(buffer); + + // Echo the message back + ws.text(ws.got_text()); + ws.write(buffer.data()); + } + } + catch(beast::system_error const& se) + { + // This indicates that the session was closed + if(se.code() != websocket::error::closed) + std::cerr << "Error: " << se.code().message() << std::endl; + } + catch(std::exception const& e) + { + std::cerr << "Error: " << e.what() << std::endl; + } +} + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 3) + { + std::cerr << + "Usage: websocket-server-sync <address> <port>\n" << + "Example:\n" << + " websocket-server-sync 0.0.0.0 8080\n"; + return EXIT_FAILURE; + } + auto const address = net::ip::make_address(argv[1]); + auto const port = static_cast<unsigned short>(std::atoi(argv[2])); + + // The io_context is required for all I/O + net::io_context ioc{1}; + + // The acceptor receives incoming connections + tcp::acceptor acceptor{ioc, {address, port}}; + for(;;) + { + // This will receive the new connection + tcp::socket socket{ioc}; + + // Block until we get a connection + acceptor.accept(socket); + + // Launch the session, transferring ownership of the socket + std::thread{std::bind( + &do_session, + std::move(socket))}.detach(); + } + } + catch (const std::exception& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} |