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:
+ 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"
+