Add multi_port to server-framework

This commit is contained in:
Vinnie Falco
2017-06-18 06:51:51 -07:00
parent f6e0e5ef47
commit a4a4121444
22 changed files with 937 additions and 301 deletions

View File

@ -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:

View File

@ -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]

View File

@ -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]

View File

@ -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]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -62,7 +62,7 @@ ports created in the example program, and the HTTP services contained by
the HTTP ports:
<img width="880" height = "344" alt = "ServerFramework"
src="https://raw.githubusercontent.com/vinniefalco/Beast/server/doc/images/server.png">
src="https://raw.githubusercontent.com/vinniefalco/Beast/master/doc/images/server.png">
## PortHandler Requirements
```C++

View File

@ -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 <boost/assert.hpp>
#include <boost/config.hpp>
/* 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 <beast.hpp>
#include <boost/logic/tribool.hpp>
@ -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

View File

@ -15,7 +15,9 @@
#include <beast/http/empty_body.hpp>
#include <beast/http/message.hpp>
#include <beast/http/string_body.hpp>
#include <boost/filesystem/path.hpp>
#include <string>
namespace framework {
@ -70,10 +72,33 @@ public:
ec = {};
}
/** Process a request.
/** Try to handle a file request.
@param stream The stream belonging to the connection.
Ownership is not transferred.
@note This is needed for to meet the requirements for @b Service
@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<class Body, class Fields>
void
send(response<Body, Fields>&&);
@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<Body, Fields>&& 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<class Body, class Fields>
beast::http::response<beast::http::string_body>
not_found(beast::http::request<Body, Fields> const& req,
not_found(
beast::http::request<Body, Fields> const& req,
boost::filesystem::path const& rel_path) const
{
beast::http::response<beast::http::string_body> res;
@ -207,7 +233,8 @@ private:
//
template<class Body, class Fields>
beast::http::response<file_body>
get(beast::http::request<Body, Fields> const& req,
get(
beast::http::request<Body, Fields> const& req,
boost::filesystem::path const& full_path) const
{
beast::http::response<file_body> res;
@ -224,7 +251,8 @@ private:
//
template<class Body, class Fields>
beast::http::response<beast::http::empty_body>
head(beast::http::request<Body, Fields> const& req,
head(
beast::http::request<Body, Fields> const& req,
boost::filesystem::path const& full_path) const
{
beast::http::response<beast::http::empty_body> res;

View File

@ -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<isRequest, Body, Fields> 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 Derived, class... Services>
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<class ConstBufferSequence>
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... Services>
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<async_http_con<Services...>>
// 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<socket_type>
// Declare this base last now that everything else got set up first.
// Constructs last, destroys first
//
, public async_http_con_base<async_http_con<Services...>, Services...>
{
public:
// Construct the plain connection.
// Constructor
//
// Additional arguments are forwarded to the base class
//
template<class... Args>
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();
}
};

View File

@ -328,22 +328,28 @@ private:
template<class... Services>
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<sync_http_con<Services...>>
// 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<socket_type>
// Declare this base last now that everything else got set up first.
// Constructs last, destroys first
//
, public sync_http_con_base<sync_http_con<Services...>, Services...>
{
public:
// Construct the plain connection.
// Constructor
//
// Additional arguments are forwarded to the base class
//
template<class... Args>
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();
}
};

View File

@ -24,25 +24,28 @@ namespace framework {
template<class... Services>
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<sync_https_con<Services...>>
// 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<ssl_stream<socket_type>>
// Declare this base last now that everything else got set up first.
// Constructs last, destroys first
//
, public sync_http_con_base<sync_https_con<Services...>, Services...>
{
public:
// Construct the plain connection.
// Constructor
//
// Additional arguments are forwarded to the base class
//
template<class... Args>
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<socket_type>&
stream()
@ -99,25 +105,28 @@ private:
template<class... Services>
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<async_https_con<Services...>>
// 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<ssl_stream<socket_type>>
// Declare this base last now that everything else got set up first.
// Constructs last, destroys first
//
, public async_http_con_base<async_https_con<Services...>, Services...>
{
public:
// Construct the plain connection.
// Constructor
//
// Additional arguments are forwarded to the base class
//
template<class... Args>
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<socket_type>&
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<class ConstBufferSequence>
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<async_https_con<Services...>, Services...>;
@ -152,7 +195,8 @@ 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)));
@ -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,7 +234,8 @@ private:
// This is an SSL shutdown
//
stream().async_shutdown(
this->strand_.wrap(std::bind(
this->strand_.wrap(
std::bind(
&async_https_con::on_shutdown,
this->shared_from_this(),
std::placeholders::_1)));
@ -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<async_https_con<Services...>>(
@ -358,8 +416,7 @@ public:
log_,
services_,
instance_.next_id(),
ep
)->run();
ep)->run();
}
};

View File

@ -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<ws_sync_port>(
ec,
endpoint_type{addr,static_cast<unsigned short>(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<http_sync_port<
ws_upgrade_service<ws_sync_port>,
file_service
>>(
ec,
endpoint_type{addr,static_cast<unsigned short>(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<ws_async_port>(
ec,
endpoint_type{addr, port},
endpoint_type{addr,
static_cast<unsigned short>(port + 2)},
instance,
std::cout,
set_ws_options{pmd}
@ -160,7 +219,7 @@ main(
if(ec)
return fail("ws_async_port", ec);
// Install an asynchronous HTTP port handler
// Create an HTTP port
//
auto sp = instance.make_port<http_async_port<
ws_upgrade_service<ws_async_port>,
@ -168,25 +227,25 @@ main(
>>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(port + 1)},
static_cast<unsigned short>(port + 3)},
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.
// 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_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,
@ -198,68 +257,9 @@ main(
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<ws_sync_port>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(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<http_sync_port<
ws_upgrade_service<ws_sync_port>,
file_service
>>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(port + 3)},
instance,
std::cout);
if(ec)
return fail("http_sync_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
);
if(ec)
return fail("http_sync_port/ws_upgrade_service", ec);
// Set up the file_service to point to the root path.
//
sp->template init<1>(
ec,
root,
"http_sync_port"
);
if(ec)
return fail("http_sync_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<wss_async_port>(
auto wsp = instance.make_port<wss_sync_port>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(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<https_async_port<
ws_upgrade_service<wss_async_port>,
auto sp = instance.make_port<https_sync_port<
ws_upgrade_service<wss_sync_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<wss_async_port>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(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<https_async_port<
ws_upgrade_service<wss_async_port>,
file_service
>>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(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<wss_sync_port>(
auto sp = instance.make_port<multi_port<
ws_upgrade_service<multi_port_base>,
file_service
>>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(port + 6)},
static_cast<unsigned short>(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<https_sync_port<
ws_upgrade_service<wss_sync_port>,
file_service
>>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(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

View File

@ -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 <beast/core.hpp>
#include <boost/function.hpp>
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... Services>
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<multi_con<Services...>>
// The stream should be created before the base class so
// use the "base from member" idiom.
//
, public base_from_member<socket_type>
// Constructs last, destroys first
//
, public async_http_con_base<multi_con<Services...>, 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<class... Args>
multi_con(
socket_type&& sock,
boost::asio::ssl::context& ctx,
Args&&... args)
: base_from_member<socket_type>(std::move(sock))
, async_http_con_base<multi_con<Services...>, Services...>(std::forward<Args>(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<multi_con<Services...>, 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<async_https_con<Services...>>(
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<void(beast::websocket::stream<socket_type>&)>;
using on_new_stream_cb2 = boost::function<void(beast::websocket::stream<ssl_stream<socket_type>>&)>;
// 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<class NextLayer>
void callback(beast::websocket::stream<NextLayer>&);
@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<class Callback>
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<class Body, class Fields>
void
on_upgrade(
socket_type&& sock,
endpoint_type ep,
beast::http::request<Body, Fields>&& req)
{
// Create the connection and call the version of
// run that takes the request since we have it already
//
std::make_shared<async_ws_con>(
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<class Body, class Fields>
void
on_upgrade(
ssl_stream<socket_type>&& stream,
endpoint_type ep,
beast::http::request<Body, Fields>&& req)
{
std::make_shared<async_wss_con>(
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... Services>
class multi_port : public multi_port_base
{
// The list of services connections created from this port will support
service_list<Services...> services_;
public:
/** Constructor
All arguments are forwarded to the multi_port_base constructor.
*/
template<class... Args>
multi_port(Args&&... args)
: multi_port_base(std::forward<Args>(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<std::size_t Index, class... Args>
void
init(error_code& ec, Args&&... args)
{
services_.template init<Index>(
ec,
std::forward<Args>(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<multi_con<Services...>>(
std::move(sock),
ctx_,
"multi_port",
log_,
services_,
instance_.next_id(),
ep
)->detect();
}
};
} // framework
#endif

View File

@ -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

View File

@ -12,7 +12,7 @@
#include <beast/core/multi_buffer.hpp>
#include <beast/websocket/stream.hpp>
#include <functional>
#include <boost/function.hpp>
#include <memory>
#include <ostream>
@ -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<async_ws_con>
// 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<beast::websocket::stream<socket_type>>
// Declare this base last now that everything else got set up first.
// Constructs last, destroys first
//
, public async_ws_con_base<async_ws_con>
{
public:
// Construct the plain connection.
// Constructor
//
// Additional arguments are forwarded to the base class
//
template<class... Args>
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<socket_type>&
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<socket_type>&)>;
// The type of the on_new_stream callback
//
using on_new_stream_cb =
boost::function<void(beast::websocket::stream<socket_type>&)>;
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<class Body, class Fields>
void
accept(
on_upgrade(
socket_type&& sock,
endpoint_type ep,
beast::http::request<Body, Fields>&& req)
@ -356,8 +365,7 @@ public:
log_,
instance_.next_id(),
ep,
cb_
)->run(std::move(req));
cb_)->run(std::move(req));
}
};

View File

@ -12,7 +12,7 @@
#include <beast/core/multi_buffer.hpp>
#include <beast/websocket.hpp>
#include <functional>
#include <boost/function.hpp>
#include <memory>
#include <ostream>
#include <thread>
@ -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<class Callback>
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<sync_ws_con>
// 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<beast::websocket::stream<socket_type>>
// Declare this base last now that everything else got set up first.
// Constructs last, destroys first
//
, public sync_ws_con_base<sync_ws_con>
{
@ -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<socket_type>&
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<socket_type>&)>;
// The type of the on_new_stream callback
//
using on_new_stream_cb =
boost::function<void(beast::websocket::stream<socket_type>&)>;
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<class Body, class Fields>
void
accept(
on_upgrade(
socket_type&& sock,
endpoint_type ep,
beast::http::request<Body, Fields>&& req)
{
// Create the connection object and run it,
// transferring ownershop of the ugprade request.
// transferring ownership of the ugprade request.
//
std::make_shared<sync_ws_con>(
std::move(sock),
@ -413,8 +423,7 @@ public:
log_,
instance_.next_id(),
ep,
cb_
)->run(std::move(req));
cb_)->run(std::move(req));
}
};

View File

@ -25,7 +25,7 @@ namespace framework {
template<class PortHandler>
class ws_upgrade_service
{
std::shared_ptr<PortHandler> handler_;
PortHandler& handler_;
public:
/** Constructor
@ -34,9 +34,8 @@ public:
handle WebSocket upgrade requests.
*/
explicit
ws_upgrade_service(
std::shared_ptr<PortHandler> 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));

View File

@ -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<sync_wss_con>
// 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<beast::websocket::stream<ssl_stream<socket_type>>>
// Declare this base last now that everything else got set up first.
// Constructs last, destroys first
//
, public sync_ws_con_base<sync_wss_con>
{
public:
// Construct the plain connection.
// Constructor
//
// Additional arguments are forwarded to the base class
//
template<class... Args>
explicit
@ -51,7 +57,7 @@ public:
{
}
// Construct the connection from an existing, handshaked SSL stream
// Construct from an existing, handshaked SSL stream
//
template<class... Args>
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<ssl_stream<socket_type>>&
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<async_wss_con>
// 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<beast::websocket::stream<ssl_stream<socket_type>>>
// Declare this base last now that everything else got set up first.
// Constructs last, destroys first
//
, public async_ws_con_base<async_wss_con>
{
public:
// Construct the connection.
// Constructor
//
// Additional arguments are forwarded to the base class
//
template<class... Args>
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<class... Args>
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<ssl_stream<socket_type>>&
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)));
}
// 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<socket_type>&)>;
using on_new_stream_cb2 = boost::function<
void(beast::websocket::stream<ssl_stream<socket_type>>&)>;
// The types of the on_new_stream callbacks
//
using on_new_stream_cb1 =
boost::function<void(beast::websocket::stream<socket_type>&)>;
using on_new_stream_cb2 =
boost::function<void(beast::websocket::stream<ssl_stream<socket_type>>&)>;
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<class Body, class Fields>
void
accept(
on_upgrade(
ssl_stream<socket_type>&& stream,
endpoint_type ep,
beast::http::request<Body, Fields>&& req)
{
// Create the connection object and run it,
// transferring ownershop of the ugprade request.
// transferring ownership of the ugprade request.
//
std::make_shared<sync_wss_con>(
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<socket_type>&)>;
using on_new_stream_cb2 = boost::function<
void(beast::websocket::stream<ssl_stream<socket_type>>&)>;
// The types of the on_new_stream callbacks
//
using on_new_stream_cb1 =
boost::function<void(beast::websocket::stream<socket_type>&)>;
using on_new_stream_cb2 =
boost::function<void(beast::websocket::stream<ssl_stream<socket_type>>&)>;
// 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<class Body, class Fields>
void
accept(
on_upgrade(
ssl_stream<socket_type>&& stream,
endpoint_type ep,
beast::http::request<Body, Fields>&& req)
@ -404,8 +428,7 @@ public:
log_,
instance_.next_id(),
ep,
cb2_
)->run(std::move(req));
cb2_)->run(std::move(req));
}
};

View File

@ -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 <beast/core/flat_buffer.hpp>
#include <beast/core/ostream.hpp>

View File

@ -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

View File

@ -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
;

View File

@ -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"