diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b56dc4f..92ff87aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Version 61: * Add server-framework SSL HTTP and WebSocket ports * Fix shadowing warnings * Tidy up http-crawl example +* Add multi_port to server-framework API Changes: diff --git a/doc/0_main.qbk b/doc/0_main.qbk index f291cce9..89f49917 100644 --- a/doc/0_main.qbk +++ b/doc/0_main.qbk @@ -75,10 +75,10 @@ [def __static_buffer__ [link beast.ref.beast__static_buffer `static_buffer`]] [def __static_buffer_n__ [link beast.ref.beast__static_buffer_n `static_buffer_n`]] -[import ../example/doc/core_examples.hpp] [import ../example/doc/http_examples.hpp] [import ../example/echo-op/echo_op.cpp] [import ../example/http-client/http_client.cpp] +[import ../example/server-framework/detect_ssl.hpp] [import ../example/server-framework/file_body.hpp] [import ../example/websocket-client/websocket_client.cpp] diff --git a/doc/2_examples.qbk b/doc/2_examples.qbk index 5a3bfbae..7e8c71e9 100644 --- a/doc/2_examples.qbk +++ b/doc/2_examples.qbk @@ -107,6 +107,7 @@ This is a complete program and framework of classes implementing a general purpose server that users may copy to use as the basis for writing their own servers. It serves both HTTP and WebSocket. +* [repo_file example/server-framework/detect_ssl.hpp] * [repo_file example/server-framework/file_body.hpp] * [repo_file example/server-framework/file_service.hpp] * [repo_file example/server-framework/framework.hpp] @@ -115,6 +116,7 @@ for writing their own servers. It serves both HTTP and WebSocket. * [repo_file example/server-framework/http_sync_port.hpp] * [repo_file example/server-framework/https_ports.hpp] * [repo_file example/server-framework/main.cpp] +* [repo_file example/server-framework/multi_port.hpp] * [repo_file example/server-framework/rfc7231.hpp] * [repo_file example/server-framework/server.hpp] * [repo_file example/server-framework/service_list.hpp] diff --git a/doc/3_5_detect_ssl.qbk b/doc/3_5_detect_ssl.qbk index 087e973c..a803e40f 100644 --- a/doc/3_5_detect_ssl.qbk +++ b/doc/3_5_detect_ssl.qbk @@ -18,23 +18,23 @@ Here is the declaration for a function to detect the SSL client handshake. The input to the function is simply a buffer sequence, no stream. This allows the detection algorithm to be used elsewhere. -[example_core_detect_tls_1] +[example_core_detect_ssl_1] The implementation checks the buffer for the presence of the SSL Handshake message octet sequence and returns an apporopriate value: -[example_core_detect_tls_2] +[example_core_detect_ssl_2] Now we define a stream operation. We start with the simple, synchronous version which takes the stream and buffer as input: -[example_core_detect_tls_3] +[example_core_detect_ssl_3] The synchronous algorithm is the model for building the asynchronous operation which has more boilerplate. First, we declare the asynchronous initiation function: -[example_core_detect_tls_4] +[example_core_detect_ssl_4] The implementation of the initiation function is straightforward and contains mostly boilerplate. It is to construct the return @@ -43,13 +43,13 @@ then create the composed operation and launch it. The actual code for interacting with the stream is in the composed operation, which is written as a separate class. -[example_core_detect_tls_5] +[example_core_detect_ssl_5] Now we will declare our composed operation. There is a considerable amount of necessary boilerplate to get this right, but the result is worth the effort. -[example_core_detect_tls_6] +[example_core_detect_ssl_6] The boilerplate is all done, and now we need to implemnt the function call operator that turns this composed operation a completion handler @@ -59,6 +59,9 @@ is a transformation of the synchronous version of `detect_ssl` above, but with the inversion of flow that characterizes code written in the callback style: -[example_core_detect_tls_7] +[example_core_detect_ssl_7] + +This SSL detector is used by the server framework in the example +directory. [endsect] diff --git a/doc/images/server.png b/doc/images/server.png index 52f0c2e2..fc609800 100644 Binary files a/doc/images/server.png and b/doc/images/server.png differ diff --git a/example/server-framework/README.md b/example/server-framework/README.md index 8bb4d9b4..bb2b266b 100644 --- a/example/server-framework/README.md +++ b/example/server-framework/README.md @@ -62,7 +62,7 @@ ports created in the example program, and the HTTP services contained by the HTTP ports: ServerFramework + src="https://raw.githubusercontent.com/vinniefalco/Beast/master/doc/images/server.png"> ## PortHandler Requirements ```C++ diff --git a/example/doc/core_examples.hpp b/example/server-framework/detect_ssl.hpp similarity index 96% rename from example/doc/core_examples.hpp rename to example/server-framework/detect_ssl.hpp index 268f7292..89876872 100644 --- a/example/doc/core_examples.hpp +++ b/example/server-framework/detect_ssl.hpp @@ -5,22 +5,19 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // +#ifndef BEAST_EXAMPLE_SERVER_DETECT_SSL_HPP +#define BEAST_EXAMPLE_SERVER_DETECT_SSL_HPP + #include #include -/* This file contains the functions and classes found in the documentation - - They are compiled and run as part of the unit tests, so you can copy - the code and use it in your own projects as a starting point for - building a network application. -*/ //------------------------------------------------------------------------------ // // Example: Detect TLS/SSL // //------------------------------------------------------------------------------ -//[example_core_detect_tls_1 +//[example_core_detect_ssl_1 #include #include @@ -55,7 +52,7 @@ is_ssl_handshake(ConstBufferSequence const& buffers); using namespace beast; -//[example_core_detect_tls_2 +//[example_core_detect_ssl_2 template< class ConstBufferSequence> @@ -94,7 +91,7 @@ is_ssl_handshake( //] -//[example_core_detect_tls_3 +//[example_core_detect_ssl_3 /** Detect a TLS/SSL handshake on a stream. @@ -176,7 +173,7 @@ detect_ssl( //] -//[example_core_detect_tls_4 +//[example_core_detect_ssl_4 /** Detect a TLS/SSL handshake asynchronously on a stream. @@ -234,7 +231,7 @@ async_detect_ssl( //] -//[example_core_detect_tls_5 +//[example_core_detect_ssl_5 // This is the composed operation. template< @@ -292,7 +289,7 @@ async_detect_ssl( //] -//[example_core_detect_tls_6 +//[example_core_detect_ssl_6 // Read from a stream to invoke is_tls_handshake asynchronously // @@ -389,7 +386,7 @@ public: //] -//[example_core_detect_tls_7 +//[example_core_detect_ssl_7 // detect_ssl_op is callable with the signature // void(error_code, bytes_transferred), @@ -481,4 +478,4 @@ operator()(beast::error_code ec, std::size_t bytes_transferred) //] -//------------------------------------------------------------------------------ +#endif diff --git a/example/server-framework/file_service.hpp b/example/server-framework/file_service.hpp index c13bb103..1b29f8eb 100644 --- a/example/server-framework/file_service.hpp +++ b/example/server-framework/file_service.hpp @@ -15,7 +15,9 @@ #include #include #include + #include + #include namespace framework { @@ -47,8 +49,8 @@ public: */ explicit file_service( - boost::filesystem::path const& root, - beast::string_view server) + boost::filesystem::path const& root, + beast::string_view server) : root_(root) , server_(server) { @@ -70,10 +72,33 @@ public: ec = {}; } - /** Process a request. - - - @note This is needed for to meet the requirements for @b Service + /** Try to handle a file request. + + @param stream The stream belonging to the connection. + Ownership is not transferred. + + @param ep The remote endpoint of the connection + corresponding to the stream. + + @param req The request message to attempt handling. + Ownership is not transferred. + + @param send The function to invoke with the response. + The function will have this equivalent signature: + + @code + + template + void + send(response&&); + + @endcode + + In C++14 this can be expressed using a generic lambda. In + C++11 it will require a template member function of an invocable + object. + + @return `true` if the request was handled by the service. */ template< class Stream, @@ -86,7 +111,7 @@ public: beast::http::request&& req, Send const& send) const { - // Check the method and take action + // Determine our action based on the method switch(req.method()) { case beast::http::verb::get: @@ -190,7 +215,8 @@ private: // template beast::http::response - not_found(beast::http::request const& req, + not_found( + beast::http::request const& req, boost::filesystem::path const& rel_path) const { beast::http::response res; @@ -207,7 +233,8 @@ private: // template beast::http::response - get(beast::http::request const& req, + get( + beast::http::request const& req, boost::filesystem::path const& full_path) const { beast::http::response res; @@ -224,7 +251,8 @@ private: // template beast::http::response - head(beast::http::request const& req, + head( + beast::http::request const& req, boost::filesystem::path const& full_path) const { beast::http::response res; diff --git a/example/server-framework/http_async_port.hpp b/example/server-framework/http_async_port.hpp index f766bcdd..12d224c5 100644 --- a/example/server-framework/http_async_port.hpp +++ b/example/server-framework/http_async_port.hpp @@ -31,6 +31,9 @@ namespace framework { // struct queued_http_write { + // Destructor must be virtual since we delete a + // derived class through a pointer to the base! + // virtual ~queued_http_write() = default; // When invoked, performs the write operation. @@ -49,8 +52,13 @@ template< class Handler> class queued_http_write_impl : public queued_http_write { + // The stream to write to Stream& stream_; + + // The message to send, which we acquire by move or copy beast::http::message msg_; + + // The handler to invoke when the send completes. Handler handler_; public: @@ -69,7 +77,13 @@ public: { } - // Writes the stored message + // Writes the stored message. + // + // The caller must make sure this invocation represents + // a continuation of an asynchronous operation which is + // already in the right context. For example, already + // running on the associated strand. + // void invoke() override { @@ -120,6 +134,7 @@ make_queued_http_write( template class async_http_con_base : public http_base { +protected: // This function lets us access members of the derived class Derived& impl() @@ -153,7 +168,6 @@ class async_http_con_base : public http_base // Indicates if we have a write active. bool writing_ = false; -protected: // The strand makes sure that our data is // accessed from only one thread at a time. // @@ -172,17 +186,40 @@ public: , services_(services) , id_(id) , ep_(ep) + // The buffer has a limit of 8192, otherwise // the server is vulnerable to a buffer attack. // , buffer_(8192) + , strand_(impl().stream().get_io_service()) { } + // Called to start the object after the listener accepts + // an incoming connection, when no bytes have been read yet. + // void run() { + // Just call run with an empty buffer + run(boost::asio::null_buffers{}); + } + + // Called to start the object after the + // listener accepts an incoming connection. + // + template + void + run(ConstBufferSequence const& buffers) + { + // Copy the data into the buffer for performing + // HTTP reads, so that the bytes get used. + // + buffer_.commit(boost::asio::buffer_copy( + buffer_.prepare(boost::asio::buffer_size(buffers)), + buffers)); + // Give the derived class a chance to do stuff // impl().do_handshake(); @@ -210,7 +247,6 @@ protected: } } -private: // Perform an asynchronous read for the next request header // void @@ -457,25 +493,28 @@ private: template class async_http_con - // Note that we give this object the enable_shared_from_this, and have - // the base class call impl().shared_from_this(). The reason we do that - // is so that the shared_ptr has the correct type. This lets the - // derived class (this class) use its members in calls to std::bind, - // without an ugly call to `dynamic_downcast` or other nonsense. + // Give this object the enable_shared_from_this, and have + // the base class call impl().shared_from_this(). The reason + // is so that the shared_ptr has the correct type. This lets + // the derived class (this class) use its members in calls to + // `std::bind`, without an ugly call to `dynamic_downcast` or + // other nonsense. // : public std::enable_shared_from_this> - // We want the socket to be created before the base class so we use - // the "base from member" idiom which Boost provides as a class. + // The stream should be created before the base class so + // use the "base from member" idiom. // , public base_from_member - // Declare this base last now that everything else got set up first. + // Constructs last, destroys first // , public async_http_con_base, Services...> { public: - // Construct the plain connection. + // Constructor + // + // Additional arguments are forwarded to the base class // template async_http_con( @@ -488,8 +527,11 @@ public: } // Returns the stream. - // The base class calls this to obtain the object to - // use for reading and writing HTTP messages. + // + // The base class calls this to obtain the object to use for + // reading and writing HTTP messages. This allows the same base + // class to work with different return types for `stream()` such + // as a `boost::asio::ip::tcp::socket&` or a `boost::asio::ssl::stream&` // socket_type& stream() @@ -597,8 +639,7 @@ public: log_, services_, instance_.next_id(), - ep - )->run(); + ep)->run(); } }; diff --git a/example/server-framework/http_sync_port.hpp b/example/server-framework/http_sync_port.hpp index b1adfbb2..c80e5775 100644 --- a/example/server-framework/http_sync_port.hpp +++ b/example/server-framework/http_sync_port.hpp @@ -328,22 +328,28 @@ private: template class sync_http_con - // Note that we give this object the `enable_shared_from_this`, and have - // the base class call `impl().shared_from_this()` when needed. + // Give this object the enable_shared_from_this, and have + // the base class call impl().shared_from_this(). The reason + // is so that the shared_ptr has the correct type. This lets + // the derived class (this class) use its members in calls to + // `std::bind`, without an ugly call to `dynamic_downcast` or + // other nonsense. // : public std::enable_shared_from_this> - // We want the socket to be created before the base class so we use - // the "base from member" idiom which Boost provides as a class. + // The stream should be created before the base class so + // use the "base from member" idiom. // , public base_from_member - // Declare this base last now that everything else got set up first. + // Constructs last, destroys first // , public sync_http_con_base, Services...> { public: - // Construct the plain connection. + // Constructor + // + // Additional arguments are forwarded to the base class // template sync_http_con( @@ -357,8 +363,10 @@ public: // Returns the stream. // - // The base class calls this to obtain the object to - // use for reading and writing HTTP messages. + // The base class calls this to obtain the object to use for + // reading and writing HTTP messages. This allows the same base + // class to work with different return types for `stream()` such + // as a `boost::asio::ip::tcp::socket&` or a `boost::asio::ssl::stream&` // socket_type& stream() @@ -458,8 +466,7 @@ public: log_, services_, instance_.next_id(), - ep - )->run(); + ep)->run(); } }; diff --git a/example/server-framework/https_ports.hpp b/example/server-framework/https_ports.hpp index a1e06792..c00b6851 100644 --- a/example/server-framework/https_ports.hpp +++ b/example/server-framework/https_ports.hpp @@ -24,25 +24,28 @@ namespace framework { template class sync_https_con - // Note that we give this object the enable_shared_from_this, and have - // the base class call impl().shared_from_this(). The reason we do that - // is so that the shared_ptr has the correct type. This lets the - // derived class (this class) use its members in calls to std::bind, - // without an ugly call to `dynamic_downcast` or other nonsense. + // Give this object the enable_shared_from_this, and have + // the base class call impl().shared_from_this(). The reason + // is so that the shared_ptr has the correct type. This lets + // the derived class (this class) use its members in calls to + // `std::bind`, without an ugly call to `dynamic_downcast` or + // other nonsense. // : public std::enable_shared_from_this> - // We want the socket to be created before the base class so we use - // the "base from member" idiom which Boost provides as a class. + // The stream should be created before the base class so + // use the "base from member" idiom. // , public base_from_member> - // Declare this base last now that everything else got set up first. + // Constructs last, destroys first // , public sync_http_con_base, Services...> { public: - // Construct the plain connection. + // Constructor + // + // Additional arguments are forwarded to the base class // template sync_https_con( @@ -56,8 +59,11 @@ public: } // Returns the stream. - // The base class calls this to obtain the object to - // use for reading and writing HTTP messages. + // + // The base class calls this to obtain the object to use for + // reading and writing HTTP messages. This allows the same base + // class to work with different return types for `stream()` such + // as a `boost::asio::ip::tcp::socket&` or a `boost::asio::ssl::stream&` // ssl_stream& stream() @@ -99,25 +105,28 @@ private: template class async_https_con - // Note that we give this object the enable_shared_from_this, and have - // the base class call impl().shared_from_this(). The reason we do that - // is so that the shared_ptr has the correct type. This lets the - // derived class (this class) use its members in calls to std::bind, - // without an ugly call to `dynamic_downcast` or other nonsense. + // Give this object the enable_shared_from_this, and have + // the base class call impl().shared_from_this(). The reason + // is so that the shared_ptr has the correct type. This lets + // the derived class (this class) use its members in calls to + // `std::bind`, without an ugly call to `dynamic_downcast` or + // other nonsense. // : public std::enable_shared_from_this> - // We want the socket to be created before the base class so we use - // the "base from member" idiom which Boost provides as a class. + // The stream should be created before the base class so + // use the "base from member" idiom. // , public base_from_member> - // Declare this base last now that everything else got set up first. + // Constructs last, destroys first // , public async_http_con_base, Services...> { public: - // Construct the plain connection. + // Constructor + // + // Additional arguments are forwarded to the base class // template async_https_con( @@ -131,8 +140,11 @@ public: } // Returns the stream. - // The base class calls this to obtain the object to - // use for reading and writing HTTP messages. + // + // The base class calls this to obtain the object to use for + // reading and writing HTTP messages. This allows the same base + // class to work with different return types for `stream()` such + // as a `boost::asio::ip::tcp::socket&` or a `boost::asio::ssl::stream&` // ssl_stream& stream() @@ -140,6 +152,37 @@ public: return this->member; } + // Called by the multi-port after reading some + // bytes from the stream and detecting SSL. + // + template + void + handshake(ConstBufferSequence const& buffers) + { + // Copy the caller's bytes into the buffer we + // use for reading HTTP messages, otherwise + // the memory pointed to by buffers will go out + // of scope. + // + this->buffer_.commit( + boost::asio::buffer_copy( + this->buffer_.prepare(boost::asio::buffer_size(buffers)), + buffers)); + + // Perform SSL handshake. We use the "buffered" + // overload which lets us pass those extra bytes. + // + stream().async_handshake( + boost::asio::ssl::stream_base::server, + buffers, + this->strand_.wrap( + std::bind( + &async_https_con::on_buffered_handshake, + this->shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + private: friend class async_http_con_base, Services...>; @@ -152,10 +195,11 @@ private: // stream().async_handshake( boost::asio::ssl::stream_base::server, - this->strand_.wrap(std::bind( + this->strand_.wrap( + std::bind( &async_https_con::on_handshake, - this->shared_from_this(), - std::placeholders::_1))); + this->shared_from_this(), + std::placeholders::_1))); } // Called when the SSL handshake completes @@ -169,6 +213,20 @@ private: this->do_run(); } + // Called when the buffered SSL handshake completes + void + on_buffered_handshake(error_code ec, std::size_t bytes_transferred) + { + if(ec) + return this->fail("on_handshake", ec); + + // Consume what was read but leave the rest + this->buffer_.consume(bytes_transferred); + + // No error so run the main loop + this->do_run(); + } + // Called when the end of stream is reached void do_shutdown() @@ -176,10 +234,11 @@ private: // This is an SSL shutdown // stream().async_shutdown( - this->strand_.wrap(std::bind( - &async_https_con::on_shutdown, + this->strand_.wrap( + std::bind( + &async_https_con::on_shutdown, this->shared_from_this(), - std::placeholders::_1))); + std::placeholders::_1))); } // Called when the SSL shutdown completes @@ -273,8 +332,7 @@ public: log_, services_, instance_.next_id(), - ep - )->run(); + ep)->run(); } }; @@ -348,7 +406,7 @@ public: void on_accept(socket_type&& sock, endpoint_type ep) { - // Create an HTTPS connection object + // Create an SSL connection object // and transfer ownership of the socket. // std::make_shared>( @@ -358,8 +416,7 @@ public: log_, services_, instance_.next_id(), - ep - )->run(); + ep)->run(); } }; diff --git a/example/server-framework/main.cpp b/example/server-framework/main.cpp index ff14ac7d..fcaffa8c 100644 --- a/example/server-framework/main.cpp +++ b/example/server-framework/main.cpp @@ -14,6 +14,7 @@ #if BEAST_USE_OPENSSL #include "https_ports.hpp" +#include "multi_port.hpp" #include "wss_ports.hpp" #include "ssl_certificate.hpp" #endif @@ -79,11 +80,11 @@ main( int ac, char const* av[]) { - // Helper for reporting failures - // using namespace framework; using namespace beast::http; + // Helper for reporting failures + // auto const fail = [&]( std::string const& what, @@ -141,17 +142,75 @@ main( //-------------------------------------------------------------------------- // - // Asynchronous WebSocket HTTP + // Synchronous WebSocket HTTP // - // port port + 1 + // port + 0 port + 1 // //-------------------------------------------------------------------------- { - // Install an asynchronous WebSocket echo port handler + // Create a WebSocket port + // + auto wsp = instance.make_port( + ec, + endpoint_type{addr,static_cast(port + 0)}, + instance, + std::cout, + set_ws_options{pmd}); + + if(ec) + return fail("ws_sync_port", ec); + + // Create an HTTP port + // + auto sp = instance.make_port, + file_service + >>( + ec, + endpoint_type{addr,static_cast(port + 1)}, + instance, + std::cout); + + if(ec) + return fail("http_sync_port", ec); + + // Init the ws_upgrade_service to + // forward upgrades to the WebSocket port. + // + sp->template init<0>( + ec, + *wsp // The WebSocket port handler + ); + + if(ec) + return fail("http_sync_port/ws_upgrade_service", ec); + + // Init the file_service to point to the root path. + // + sp->template init<1>( + ec, + root, // The root path + "http_sync_port" // The value for the Server field + ); + + if(ec) + return fail("http_sync_port/file_service", ec); + } + + //-------------------------------------------------------------------------- + // + // Asynchronous WebSocket HTTP + // + // port + 2 port + 3 + // + //-------------------------------------------------------------------------- + { + // Create a WebSocket port // auto wsp = instance.make_port( ec, - endpoint_type{addr, port}, + endpoint_type{addr, + static_cast(port + 2)}, instance, std::cout, set_ws_options{pmd} @@ -160,106 +219,47 @@ main( if(ec) return fail("ws_async_port", ec); - // Install an asynchronous HTTP port handler + // Create an HTTP port // auto sp = instance.make_port, file_service >>( ec, - endpoint_type{addr, - static_cast(port + 1)}, - instance, - std::cout); - - if(ec) - return fail("http_async_port", ec); - - // Set up the ws_upgrade_service. We will route upgrade - // requests to the websocket port handler created earlier. - // - sp->template init<0>( - ec, - wsp // The websocket port handler - ); - - if(ec) - return fail("http_async_port/ws_upgrade_service", ec); - - // Set up the file_service to point to the root path. - // - sp->template init<1>( - ec, - root, // The root path - "http_async_port" // The value for the Server field - ); - - if(ec) - return fail("http_async_port/file_service", ec); - } - - //-------------------------------------------------------------------------- - // - // Synchronous WebSocket HTTP - // - // port + 2 port + 3 - // - //-------------------------------------------------------------------------- - { - // Install a synchronous WebSocket echo port handler - // - auto wsp = instance.make_port( - ec, - endpoint_type{addr, - static_cast(port + 2)}, - instance, - std::cout, - set_ws_options{pmd}); - - if(ec) - return fail("ws_sync_port", ec); - - - // Install a synchronous HTTP port handler - // - auto sp = instance.make_port, - file_service - >>( - ec, endpoint_type{addr, static_cast(port + 3)}, instance, std::cout); if(ec) - return fail("http_sync_port", ec); + return fail("http_async_port", ec); - // Set up the ws_upgrade_service. We will route upgrade - // requests to the websocket port handler created earlier. + // Init the ws_upgrade_service to + // forward upgrades to the WebSocket port. // sp->template init<0>( ec, - wsp + *wsp // The websocket port handler ); if(ec) - return fail("http_sync_port/ws_upgrade_service", ec); + return fail("http_async_port/ws_upgrade_service", ec); - // Set up the file_service to point to the root path. + // Init the file_service to point to the root path. // sp->template init<1>( ec, - root, - "http_sync_port" + root, // The root path + "http_async_port" // The value for the Server field ); if(ec) - return fail("http_sync_port/file_service", ec); + return fail("http_async_port/file_service", ec); } // - // If OpenSSL is available then install some SSL-enabled ports + // The next section supports encrypted connections and requires + // an installed and configured OpenSSL as part of the build. // #if BEAST_USE_OPENSSL @@ -268,31 +268,30 @@ main( //-------------------------------------------------------------------------- // - // Asynchronous Secure WebSocket HTTPS + // Synchronous Secure WebSocket HTTPS // // port + 4 port + 5 // //-------------------------------------------------------------------------- { - // Install an asynchronous Secure WebSocket echo port handler + // Create a WebSocket port // - auto wsp = instance.make_port( + auto wsp = instance.make_port( ec, endpoint_type{addr, static_cast(port + 4)}, instance, std::cout, cert.get(), - set_ws_options{pmd} - ); + set_ws_options{pmd}); if(ec) - return fail("ws_async_port", ec); + return fail("wss_sync_port", ec); - // Install an asynchronous HTTPS port handler + // Create an HTTP port // - auto sp = instance.make_port, + auto sp = instance.make_port, file_service >>( ec, @@ -303,20 +302,82 @@ main( cert.get()); if(ec) - return fail("https_async_port", ec); + return fail("https_sync_port", ec); - // Set up the ws_upgrade_service. We will route upgrade - // requests to the websocket port handler created earlier. + // Init the ws_upgrade_service to + // forward upgrades to the WebSocket port. // sp->template init<0>( ec, - wsp // The websocket port handler + *wsp // The websocket port handler + ); + + if(ec) + return fail("http_sync_port/ws_upgrade_service", ec); + + // Init the file_service to point to the root path. + // + sp->template init<1>( + ec, + root, // The root path + "http_sync_port" // The value for the Server field + ); + + if(ec) + return fail("https_sync_port/file_service", ec); + } + + //-------------------------------------------------------------------------- + // + // Asynchronous Secure WebSocket HTTPS + // + // port + 6 port + 7 + // + //-------------------------------------------------------------------------- + { + // Create a WebSocket port + // + auto wsp = instance.make_port( + ec, + endpoint_type{addr, + static_cast(port + 6)}, + instance, + std::cout, + cert.get(), + set_ws_options{pmd} + ); + + if(ec) + return fail("ws_async_port", ec); + + // Create an HTTP port + // + auto sp = instance.make_port, + file_service + >>( + ec, + endpoint_type{addr, + static_cast(port + 7)}, + instance, + std::cout, + cert.get()); + + if(ec) + return fail("https_async_port", ec); + + // Init the ws_upgrade_service to + // forward upgrades to the WebSocket port. + // + sp->template init<0>( + ec, + *wsp // The websocket port handler ); if(ec) return fail("https_async_port/ws_upgrade_service", ec); - // Set up the file_service to point to the root path. + // Init the file_service to point to the root path. // sp->template init<1>( ec, @@ -330,63 +391,53 @@ main( //-------------------------------------------------------------------------- // - // Synchronous Secure WebSocket HTTPS + // Multi-Port HTTP, WebSockets, + // HTTPS Secure WebSockets // - // port + 6 port + 7 + // Asynchronous, all on the same port! + // + // port + 8 // //-------------------------------------------------------------------------- { - // Install a synchronous Secure WebSocket echo port handler + // Create a multi_port // - auto wsp = instance.make_port( + auto sp = instance.make_port, + file_service + >>( ec, endpoint_type{addr, - static_cast(port + 6)}, + static_cast(port + 8)}, instance, std::cout, cert.get(), set_ws_options{pmd}); if(ec) - return fail("wss_sync_port", ec); + return fail("multi_port", ec); - // Install a synchronous HTTPS port handler - // - auto sp = instance.make_port, - file_service - >>( - ec, - endpoint_type{addr, - static_cast(port + 7)}, - instance, - std::cout, - cert.get()); - - if(ec) - return fail("https_sync_port", ec); - - // Set up the ws_upgrade_service. We will route upgrade - // requests to the websocket port handler created earlier. + // Init the ws_upgrade_service to forward requests to the multi_port. // sp->template init<0>( ec, - wsp // The websocket port handler + *sp // The websocket port handler ); if(ec) - return fail("http_sync_port/ws_upgrade_service", ec); + return fail("multi_port/ws_upgrade_service", ec); - // Set up the file_service to point to the root path. + // Init the ws_upgrade_service to + // forward upgrades to the Multi port. // sp->template init<1>( ec, - root, - "https_sync_port" + root, // The root path + "multi_port" // The value for the Server field ); if(ec) - return fail("https_sync_port/file_service", ec); + return fail("multi_port/file_service", ec); } #endif diff --git a/example/server-framework/multi_port.hpp b/example/server-framework/multi_port.hpp new file mode 100644 index 00000000..934d1abe --- /dev/null +++ b/example/server-framework/multi_port.hpp @@ -0,0 +1,398 @@ +// +// 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) +// + +#ifndef BEAST_EXAMPLE_SERVER_MULTI_PORT_HPP +#define BEAST_EXAMPLE_SERVER_MULTI_PORT_HPP + +#include "detect_ssl.hpp" +#include "ws_async_port.hpp" +#include "http_async_port.hpp" +#include "https_ports.hpp" +#include "wss_ports.hpp" + +#include + +#include + +namespace framework { + +// A connection that detects an opening SSL handshake +// +// If the SSL handshake is detected, then an HTTPS connection object +// is move constructed from this object. Otherwise, this object continues +// as a normal unencrypted HTTP connection. If the underlying port has +// the ws_upgrade_service configured, the connection may be optionally +// be upgraded to WebSocket by the client. +// +template +class multi_con + + // Give this object the enable_shared_from_this, and have + // the base class call impl().shared_from_this(). The reason + // is so that the shared_ptr has the correct type. This lets + // the derived class (this class) use its members in calls to + // `std::bind`, without an ugly call to `dynamic_downcast` or + // other nonsense. + // + : public std::enable_shared_from_this> + + // The stream should be created before the base class so + // use the "base from member" idiom. + // + , public base_from_member + + // Constructs last, destroys first + // + , public async_http_con_base, Services...> +{ + // Context to use if we get an SSL handshake + boost::asio::ssl::context& ctx_; + + // Holds the data we read during ssl detection + beast::static_buffer_n<6> buffer_; + +public: + // Constructor + // + // Additional arguments are simply forwarded to the base class + // + template + multi_con( + socket_type&& sock, + boost::asio::ssl::context& ctx, + Args&&... args) + : base_from_member(std::move(sock)) + , async_http_con_base, Services...>(std::forward(args)...) + , ctx_(ctx) + { + } + + // Returns the stream. + // + // The base class calls this to obtain the object to use for + // reading and writing HTTP messages. This allows the same base + // class to work with different return types for `stream()` such + // as a `boost::asio::ip::tcp::socket&` or a `boost::asio::ssl::stream&` + // + socket_type& + stream() + { + return this->member; + } + + // Called by the port to launch the connection in detect mode + void + detect() + { + // The detect function operates asynchronously by reading + // in some data from the stream to figure out if its an SSL + // handshake. When it completes, it informs us of the result + // and also stores the bytes it read in the buffer. + // + async_detect_ssl( + stream(), + buffer_, + this->strand_.wrap( + std::bind( + &multi_con::on_detect, + this->shared_from_this(), + std::placeholders::_1, + std::placeholders::_2))); + } + +private: + // Base class needs to be a friend to call our private members + friend class async_http_con_base, Services...>; + + // Called when the handshake detection is complete + // + void + on_detect( + error_code ec, + boost::tribool result) + { + // Report failures if any + if(ec) + return this->fail("on_detect", ec); + + // Was an SSL handshake detected? + if(result) + { + // Yes, get the remote endpoint since it is + // needed to construct the new connection. + // + endpoint_type ep = stream().remote_endpoint(ec); + if(ec) + return this->fail("remote_endpoint", ec); + + // Now launch our new connection object + // + std::make_shared>( + std::move(stream()), + ctx_, + "multi_port", + this->log_, + this->services_, + this->id_, + ep + )->handshake(buffer_.data()); + + // When we return the last shared pointer to this + // object will go away and `*this` will be destroyed. + // + return; + } + + // No SSL handshake, so start the HTTP connection normally. + // + // Since we read some bytes from the connection that might + // contain an HTTP request, we pass the buffer holding those + // bytes to the base class so it can use them. + // + this->run(buffer_.data()); + } + + // This is called by the base before running the main loop. + // + void + do_handshake() + { + // Just run the main loop right away. + // + this->do_run(); + } + + // This is called when the other end closes the connection gracefully. + // + void + do_shutdown() + { + // Attempt a clean TCP/IP shutdown + // + error_code ec; + stream().shutdown( + socket_type::shutdown_both, + ec); + + // Report failure if any + // + if(ec) + return this->fail("shutdown", ec); + } +}; + +//------------------------------------------------------------------------------ + +/* An asynchronous HTTP and WebSocket port handler, plain or SSL + + This type meets the requirements of @b PortHandler. It supports a + variable list of HTTP services in its template parameter list, + and provides a synchronous connection implementation to service. + + The port will automatically detect OpenSSL handshakes and establish + encrypted connections, otherwise will use a plain unencrypted + connection. This all happens through the same port. + + In addition this port can process WebSocket upgrade requests by + launching them as a new asynchronous WebSocket connection using + either plain or OpenSSL transport. + + This class is split up into two parts, the multi_port_base, + and the multi_port, to avoid a recursive type reference when + we name the type of the ws_upgrade_service. +*/ +class multi_port_base +{ +protected: + // VFALCO We use boost::function to work around a compiler + // crash with gcc and clang using libstdc++ + + // The types of the on_stream callback + using on_new_stream_cb1 = boost::function&)>; + using on_new_stream_cb2 = boost::function>&)>; + + // Reference to the server instance that made us + server& instance_; + + // The stream to log to + std::ostream& log_; + + // The context holds the SSL certificates the server uses + boost::asio::ssl::context& ctx_; + + // Called for each new websocket stream + on_new_stream_cb1 cb1_; + on_new_stream_cb2 cb2_; + +public: + /** Constructor + + @param instance The server instance which owns this port + + @param log The stream to use for logging + + @param ctx The SSL context holding the SSL certificates to use + + @param cb A callback which will be invoked for every new + WebSocket connection. This provides an opportunity to change + the settings on the stream before it is used. The callback + should have this equivalent signature: + @code + template + void callback(beast::websocket::stream&); + @endcode + In C++14 this can be accomplished with a generic lambda. In + C++11 it will be necessary to write out a lambda manually, + with a templated operator(). + */ + template + multi_port_base( + server& instance, + std::ostream& log, + boost::asio::ssl::context& ctx, + Callback const& cb) + : instance_(instance) + , log_(log) + , ctx_(ctx) + , cb1_(cb) + , cb2_(cb) + { + } + + /** Accept a WebSocket upgrade request. + + This is used to accept a connection that has already + delivered the handshake. + + @param stream The stream corresponding to the connection. + + @param ep The remote endpoint. + + @param req The upgrade request. + */ + template + void + on_upgrade( + socket_type&& sock, + endpoint_type ep, + beast::http::request&& req) + { + // Create the connection and call the version of + // run that takes the request since we have it already + // + std::make_shared( + std::move(sock), + "multi_port", + log_, + instance_.next_id(), + ep, + cb1_ + )->run(std::move(req)); + } + + /** Accept a WebSocket upgrade request. + + This is used to accept a connection that has already + delivered the handshake. + + @param stream The stream corresponding to the connection. + + @param ep The remote endpoint. + + @param req The upgrade request. + */ + template + void + on_upgrade( + ssl_stream&& stream, + endpoint_type ep, + beast::http::request&& req) + { + std::make_shared( + std::move(stream), + "multi_port", + log_, + instance_.next_id(), + ep, + cb2_ + )->run(std::move(req)); + } +}; + +/* An asynchronous HTTP and WebSocket port handler, plain or SSL + + This class is the other half of multi_port_base. It gets the + Services... variadic type list and owns the service list. +*/ +template +class multi_port : public multi_port_base +{ + // The list of services connections created from this port will support + service_list services_; + +public: + /** Constructor + + All arguments are forwarded to the multi_port_base constructor. + */ + template + multi_port(Args&&... args) + : multi_port_base(std::forward(args)...) + { + } + + /** Initialize a service + + Every service in the list must be initialized exactly once. + + @param args Optional arguments forwarded to the service + constructor. + + @tparam Index The 0-based index of the service to initialize. + + @return A reference to the service list. This permits + calls to be chained in a single expression. + */ + template + void + init(error_code& ec, Args&&... args) + { + services_.template init( + ec, + std::forward(args)...); + } + + /** Called by the server to provide ownership of the socket for a new connection + + @param sock The socket whose ownership is to be transferred + + @ep The remote endpoint + */ + void + on_accept( + socket_type&& sock, + endpoint_type ep) + { + // Create a plain http connection object by transferring + // ownership of the socket, then launch it to perform + // the SSL handshake detection. + // + std::make_shared>( + std::move(sock), + ctx_, + "multi_port", + log_, + services_, + instance_.next_id(), + ep + )->detect(); + } +}; + +} // framework + +#endif diff --git a/example/server-framework/service_list.hpp b/example/server-framework/service_list.hpp index 872b49a3..bd82b5f7 100644 --- a/example/server-framework/service_list.hpp +++ b/example/server-framework/service_list.hpp @@ -90,7 +90,7 @@ public: to the stream. @param req The request message to attempt handling. A service - which handles the request may optionally take ownershop of the + which handles the request may optionally take ownership of the message. @param send The function to invoke with the response. The function diff --git a/example/server-framework/ws_async_port.hpp b/example/server-framework/ws_async_port.hpp index 667de190..da44defd 100644 --- a/example/server-framework/ws_async_port.hpp +++ b/example/server-framework/ws_async_port.hpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -212,22 +212,28 @@ private: // class async_ws_con - // Note that we give this object the `enable_shared_from_this`, and have - // the base class call `impl().shared_from_this()` when needed. + // Give this object the enable_shared_from_this, and have + // the base class call impl().shared_from_this(). The reason + // is so that the shared_ptr has the correct type. This lets + // the derived class (this class) use its members in calls to + // `std::bind`, without an ugly call to `dynamic_downcast` or + // other nonsense. // : public std::enable_shared_from_this - // We want the socket to be created before the base class so we use - // the "base from member" idiom which Boost provides as a class. + // The stream should be created before the base class so + // use the "base from member" idiom. // , public base_from_member> - // Declare this base last now that everything else got set up first. + // Constructs last, destroys first // , public async_ws_con_base { public: - // Construct the plain connection. + // Constructor + // + // Additional arguments are forwarded to the base class // template explicit @@ -241,7 +247,10 @@ public: // Returns the stream. // - // The base class calls this to obtain the websocket stream object. + // The base class calls this to obtain the object to use for + // reading and writing HTTP messages. This allows the same base + // class to work with different return types for `stream()` such + // as a `boost::asio::ip::tcp::socket&` or a `boost::asio::ssl::stream&` // beast::websocket::stream& stream() @@ -270,9 +279,10 @@ private: */ class ws_async_port { - // The type of the on_stream callback - using on_new_stream_cb = std::function< - void(beast::websocket::stream&)>; + // The type of the on_new_stream callback + // + using on_new_stream_cb = + boost::function&)>; server& instance_; std::ostream& log_; @@ -328,8 +338,7 @@ public: log_, instance_.next_id(), ep, - cb_ - )->run(); + cb_)->run(); } /** Accept a WebSocket upgrade request. @@ -345,7 +354,7 @@ public: */ template void - accept( + on_upgrade( socket_type&& sock, endpoint_type ep, beast::http::request&& req) @@ -356,8 +365,7 @@ public: log_, instance_.next_id(), ep, - cb_ - )->run(std::move(req)); + cb_)->run(std::move(req)); } }; diff --git a/example/server-framework/ws_sync_port.hpp b/example/server-framework/ws_sync_port.hpp index b4102972..ebfc1291 100644 --- a/example/server-framework/ws_sync_port.hpp +++ b/example/server-framework/ws_sync_port.hpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include #include @@ -21,8 +21,8 @@ namespace framework { /** A synchronous WebSocket connection. - This base class implements a WebSocket connection object - using synchronous calls. + This base class implements a WebSocket connection object using + synchronous calls over an unencrypted connection. It uses the Curiously Recurring Template pattern (CRTP) where we refer to the derived class in order to access the stream object @@ -55,6 +55,9 @@ class sync_ws_con_base public: // Constructor + // + // Additional arguments are forwarded to the base class + // template sync_ws_con_base( beast::string_view server_name, @@ -261,17 +264,21 @@ private: // class sync_ws_con - // Note that we give this object the `enable_shared_from_this`, and have - // the base class call `impl().shared_from_this()` when needed. + // Give this object the enable_shared_from_this, and have + // the base class call impl().shared_from_this(). The reason + // is so that the shared_ptr has the correct type. This lets + // the derived class (this class) use its members in calls to + // `std::bind`, without an ugly call to `dynamic_downcast` or + // other nonsense. // : public std::enable_shared_from_this - // We want the socket to be created before the base class so we use - // the "base from member" idiom which Boost provides as a class. + // The stream should be created before the base class so + // use the "base from member" idiom. // , public base_from_member> - // Declare this base last now that everything else got set up first. + // Constructs last, destroys first // , public sync_ws_con_base { @@ -290,7 +297,10 @@ public: // Returns the stream. // - // The base class calls this to obtain the websocket stream object. + // The base class calls this to obtain the object to use for + // reading and writing HTTP messages. This allows the same base + // class to work with different return types for `stream()` such + // as a `boost::asio::ip::tcp::socket&` or a `boost::asio::ssl::stream&` // beast::websocket::stream& stream() @@ -324,9 +334,10 @@ private: */ class ws_sync_port { - // The type of the on_stream callback - using on_new_stream_cb = std::function< - void(beast::websocket::stream&)>; + // The type of the on_new_stream callback + // + using on_new_stream_cb = + boost::function&)>; server& instance_; std::ostream& log_; @@ -382,8 +393,7 @@ public: log_, instance_.next_id(), ep, - cb_ - )->run(); + cb_)->run(); } /** Accept a WebSocket upgrade request. @@ -399,13 +409,13 @@ public: */ template void - accept( + on_upgrade( socket_type&& sock, endpoint_type ep, beast::http::request&& req) { // Create the connection object and run it, - // transferring ownershop of the ugprade request. + // transferring ownership of the ugprade request. // std::make_shared( std::move(sock), @@ -413,8 +423,7 @@ public: log_, instance_.next_id(), ep, - cb_ - )->run(std::move(req)); + cb_)->run(std::move(req)); } }; diff --git a/example/server-framework/ws_upgrade_service.hpp b/example/server-framework/ws_upgrade_service.hpp index 8e80dc35..282f2e00 100644 --- a/example/server-framework/ws_upgrade_service.hpp +++ b/example/server-framework/ws_upgrade_service.hpp @@ -25,7 +25,7 @@ namespace framework { template class ws_upgrade_service { - std::shared_ptr handler_; + PortHandler& handler_; public: /** Constructor @@ -34,9 +34,8 @@ public: handle WebSocket upgrade requests. */ explicit - ws_upgrade_service( - std::shared_ptr handler) - : handler_(std::move(handler)) + ws_upgrade_service(PortHandler& handler) + : handler_(handler) { } @@ -86,7 +85,7 @@ public: // Its an ugprade request, so transfer ownership // of the stream and request to the port handler. // - handler_->accept( + handler_.on_upgrade( std::move(stream), ep, std::move(req)); diff --git a/example/server-framework/wss_ports.hpp b/example/server-framework/wss_ports.hpp index 77e8ce2f..eb86adfe 100644 --- a/example/server-framework/wss_ports.hpp +++ b/example/server-framework/wss_ports.hpp @@ -19,26 +19,32 @@ namespace framework { //------------------------------------------------------------------------------ -// This class represents a synchronous Secure WebSocket connection +// A synchronous WebSocket connection over an SSL connection // class sync_wss_con - // Note that we give this object the `enable_shared_from_this`, and have - // the base class call `impl().shared_from_this()` when needed. + // Give this object the enable_shared_from_this, and have + // the base class call impl().shared_from_this(). The reason + // is so that the shared_ptr has the correct type. This lets + // the derived class (this class) use its members in calls to + // `std::bind`, without an ugly call to `dynamic_downcast` or + // other nonsense. // : public std::enable_shared_from_this - // We want the socket to be created before the base class so we use - // the "base from member" idiom which Boost provides as a class. + // The stream should be created before the base class so + // use the "base from member" idiom. // , public base_from_member>> - // Declare this base last now that everything else got set up first. + // Constructs last, destroys first // , public sync_ws_con_base { public: - // Construct the plain connection. + // Constructor + // + // Additional arguments are forwarded to the base class // template explicit @@ -51,7 +57,7 @@ public: { } - // Construct the connection from an existing, handshaked SSL stream + // Construct from an existing, handshaked SSL stream // template sync_wss_con( @@ -64,7 +70,10 @@ public: // Returns the stream. // - // The base class calls this to obtain the websocket stream object. + // The base class calls this to obtain the object to use for + // reading and writing HTTP messages. This allows the same base + // class to work with different return types for `stream()` such + // as a `boost::asio::ip::tcp::socket&` or a `boost::asio::ssl::stream&` // beast::websocket::stream>& stream() @@ -90,30 +99,32 @@ private: //------------------------------------------------------------------------------ -// This class represents an asynchronous Secure WebSocket -// connection which uses an OpenSSL socket as the stream. +// An asynchronous WebSocket connection over an SSL connection // class async_wss_con - // Note that we give this object the enable_shared_from_this, and have - // the base class call impl().shared_from_this(). The reason we do that - // is so that the shared_ptr has the correct type. This lets the - // derived class (this class) use its members in calls to std::bind, - // without an ugly call to `dynamic_downcast` or other nonsense. + // Give this object the enable_shared_from_this, and have + // the base class call impl().shared_from_this(). The reason + // is so that the shared_ptr has the correct type. This lets + // the derived class (this class) use its members in calls to + // `std::bind`, without an ugly call to `dynamic_downcast` or + // other nonsense. // : public std::enable_shared_from_this - // We want the socket to be created before the base class so we use - // the "base from member" idiom which Boost provides as a class. + // The stream should be created before the base class so + // use the "base from member" idiom. // , public base_from_member>> - // Declare this base last now that everything else got set up first. + // Constructs last, destroys first // , public async_ws_con_base { public: - // Construct the connection. + // Constructor + // + // Additional arguments are forwarded to the base class // template async_wss_con( @@ -125,7 +136,7 @@ public: { } - // Construct the connection from an existing, handshaked SSL stream + // Construct from an existing, handshaked SSL stream // template async_wss_con( @@ -137,8 +148,11 @@ public: } // Returns the stream. - // The base class calls this to obtain the object to - // use for reading and writing HTTP messages. + // + // The base class calls this to obtain the object to use for + // reading and writing HTTP messages. This allows the same base + // class to work with different return types for `stream()` such + // as a `boost::asio::ip::tcp::socket&` or a `boost::asio::ssl::stream&` // beast::websocket::stream>& stream() @@ -159,13 +173,15 @@ private: // stream().next_layer().async_handshake( boost::asio::ssl::stream_base::server, - this->strand_.wrap(std::bind( + this->strand_.wrap( + std::bind( &async_wss_con::on_handshake, - this->shared_from_this(), - std::placeholders::_1))); + this->shared_from_this(), + std::placeholders::_1))); } // Called when the SSL handshake completes + // void on_handshake(error_code ec) { @@ -191,11 +207,13 @@ class wss_sync_port // VFALCO We use boost::function to work around a compiler // crash with gcc and clang using libstdc++ - // The types of the on_stream callback - using on_new_stream_cb1 = boost::function< - void(beast::websocket::stream&)>; - using on_new_stream_cb2 = boost::function< - void(beast::websocket::stream>&)>; + // The types of the on_new_stream callbacks + // + using on_new_stream_cb1 = + boost::function&)>; + + using on_new_stream_cb2 = + boost::function>&)>; server& instance_; std::ostream& log_; @@ -259,8 +277,7 @@ public: log_, instance_.next_id(), ep, - cb2_ - )->run(); + cb2_)->run(); } /** Accept a WebSocket upgrade request. @@ -276,13 +293,13 @@ public: */ template void - accept( + on_upgrade( ssl_stream&& stream, endpoint_type ep, beast::http::request&& req) { // Create the connection object and run it, - // transferring ownershop of the ugprade request. + // transferring ownership of the ugprade request. // std::make_shared( std::move(stream), @@ -290,8 +307,7 @@ public: log_, instance_.next_id(), ep, - cb2_ - )->run(std::move(req)); + cb2_)->run(std::move(req)); } }; @@ -308,15 +324,24 @@ class wss_async_port // VFALCO We use boost::function to work around a compiler // crash with gcc and clang using libstdc++ - // The types of the on_stream callback - using on_new_stream_cb1 = boost::function< - void(beast::websocket::stream&)>; - using on_new_stream_cb2 = boost::function< - void(beast::websocket::stream>&)>; + // The types of the on_new_stream callbacks + // + using on_new_stream_cb1 = + boost::function&)>; + using on_new_stream_cb2 = + boost::function>&)>; + + // Reference to the server instance that made us server& instance_; + + // The stream to log to std::ostream& log_; + + // The context holds the SSL certificates the server uses boost::asio::ssl::context& ctx_; + + // Called for each new websocket stream on_new_stream_cb1 cb1_; on_new_stream_cb2 cb2_; @@ -376,8 +401,7 @@ public: log_, instance_.next_id(), ep, - cb2_ - )->run(); + cb2_)->run(); } /** Accept a WebSocket upgrade request. @@ -393,7 +417,7 @@ public: */ template void - accept( + on_upgrade( ssl_stream&& stream, endpoint_type ep, beast::http::request&& req) @@ -404,8 +428,7 @@ public: log_, instance_.next_id(), ep, - cb2_ - )->run(std::move(req)); + cb2_)->run(std::move(req)); } }; diff --git a/test/core/doc_examples.cpp b/test/core/doc_examples.cpp index df2f1ddb..33caa7f9 100644 --- a/test/core/doc_examples.cpp +++ b/test/core/doc_examples.cpp @@ -5,7 +5,7 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#include "example/doc/core_examples.hpp" +#include "example/server-framework/detect_ssl.hpp" #include #include diff --git a/test/server/CMakeLists.txt b/test/server/CMakeLists.txt index e1a49780..8a948b76 100644 --- a/test/server/CMakeLists.txt +++ b/test/server/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable (server-test http_sync_port.cpp https_ports.cpp main.cpp + multi_port.cpp rfc7231.cpp server.cpp service_list.cpp diff --git a/test/server/Jamfile b/test/server/Jamfile index 92b50ecc..4086abe2 100644 --- a/test/server/Jamfile +++ b/test/server/Jamfile @@ -21,6 +21,7 @@ exe server-test : ws_sync_port.cpp ws_upgrade_service.cpp #https_ports.cpp + #multi_port.cpp #ssl_certificate.cpp #ssl_stream.cpp ; diff --git a/test/server/multi_port.cpp b/test/server/multi_port.cpp new file mode 100644 index 00000000..6b108b49 --- /dev/null +++ b/test/server/multi_port.cpp @@ -0,0 +1,10 @@ +// +// 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) +// + +// Test that header file is self-contained. +#include "../../example/server-framework/multi_port.hpp" +