Add server-framework SSL HTTP and WebSocket ports

This commit is contained in:
Vinnie Falco
2017-06-17 13:42:13 -07:00
parent 74891560ae
commit 65932ee343
19 changed files with 1648 additions and 200 deletions

View File

@@ -8,6 +8,7 @@ Version 61:
* Tidy up names in error categories * Tidy up names in error categories
* Flush the output stream in the example * Flush the output stream in the example
* Clean close in Secure WebSocket client * Clean close in Secure WebSocket client
* Add server-framework SSL HTTP and WebSocket ports
API Changes: API Changes:

View File

@@ -70,6 +70,13 @@ endif()
find_package(OpenSSL) find_package(OpenSSL)
if (OPENSSL_FOUND)
add_definitions (-DBEAST_USE_OPENSSL=1)
else()
add_definitions (-DBEAST_USE_OPENSSL=0)
message("OpenSSL not found.")
endif()
# #
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
@@ -182,9 +189,7 @@ add_subdirectory (test)
add_subdirectory (example) add_subdirectory (example)
if (NOT OPENSSL_FOUND) if (OPENSSL_FOUND)
message("OpenSSL not found. Not building SSL tests and examples")
else()
add_subdirectory (example/ssl-http-client) add_subdirectory (example/ssl-http-client)
add_subdirectory (example/ssl-websocket-client) add_subdirectory (example/ssl-websocket-client)
add_subdirectory (test/websocket/ssl) add_subdirectory (test/websocket/ssl)

View File

@@ -113,14 +113,18 @@ for writing their own servers. It serves both HTTP and WebSocket.
* [repo_file example/server-framework/http_async_port.hpp] * [repo_file example/server-framework/http_async_port.hpp]
* [repo_file example/server-framework/http_base.hpp] * [repo_file example/server-framework/http_base.hpp]
* [repo_file example/server-framework/http_sync_port.hpp] * [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/main.cpp]
* [repo_file example/server-framework/rfc7231.hpp] * [repo_file example/server-framework/rfc7231.hpp]
* [repo_file example/server-framework/server.hpp] * [repo_file example/server-framework/server.hpp]
* [repo_file example/server-framework/service_list.hpp] * [repo_file example/server-framework/service_list.hpp]
* [repo_file example/server-framework/ssl_certificate.hpp]
* [repo_file example/server-framework/ssl_stream.hpp]
* [repo_file example/server-framework/write_msg.hpp] * [repo_file example/server-framework/write_msg.hpp]
* [repo_file example/server-framework/ws_async_port.hpp] * [repo_file example/server-framework/ws_async_port.hpp]
* [repo_file example/server-framework/ws_sync_port.hpp] * [repo_file example/server-framework/ws_sync_port.hpp]
* [repo_file example/server-framework/ws_upgrade_service.hpp] * [repo_file example/server-framework/ws_upgrade_service.hpp]
* [repo_file example/server-framework/wss_ports.hpp]
[endsect] [endsect]

View File

@@ -4,6 +4,10 @@ GroupSources(include/beast beast)
GroupSources(example/server-framework "/") GroupSources(example/server-framework "/")
if (OPENSSL_FOUND)
include_directories(${OPENSSL_INCLUDE_DIR})
endif()
add_executable (server-framework add_executable (server-framework
${BEAST_INCLUDES} ${BEAST_INCLUDES}
${SERVER_INCLUDES} ${SERVER_INCLUDES}
@@ -15,3 +19,7 @@ target_link_libraries(
Beast Beast
${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}) ${Boost_FILESYSTEM_LIBRARY})
if (OPENSSL_FOUND)
target_link_libraries(server-framework ${OPENSSL_LIBRARIES})
endif()

View File

@@ -118,7 +118,7 @@ make_queued_http_write(
@tparam Services The list of services this connection will support. @tparam Services The list of services this connection will support.
*/ */
template<class Derived, class... Services> template<class Derived, class... Services>
class async_http_con : public http_base class async_http_con_base : public http_base
{ {
// This function lets us access members of the derived class // This function lets us access members of the derived class
Derived& Derived&
@@ -161,7 +161,7 @@ protected:
public: public:
// Constructor // Constructor
async_http_con( async_http_con_base(
beast::string_view server_name, beast::string_view server_name,
std::ostream& log, std::ostream& log,
service_list<Services...> const& services, service_list<Services...> const& services,
@@ -180,15 +180,36 @@ public:
{ {
} }
// Called by the port after creating the object
void void
run() run()
{ {
// Start reading the header for the first request. // Give the derived class a chance to do stuff
// //
impl().do_handshake();
}
protected:
void
do_run()
{
do_read_header(); do_read_header();
} }
// Called when a failure occurs
//
void
fail(std::string what, error_code ec)
{
// Don't log operation aborted since those happen normally.
//
if(ec && ec != boost::asio::error::operation_aborted)
{
log_ <<
"[#" << id_ << " " << ep_ << "] " <<
what << ": " << ec.message() << std::endl;
}
}
private: private:
// Perform an asynchronous read for the next request header // Perform an asynchronous read for the next request header
// //
@@ -214,7 +235,7 @@ private:
buffer_, buffer_,
*parser_, *parser_,
strand_.wrap(std::bind( strand_.wrap(std::bind(
&async_http_con::on_read_header, &async_http_con_base::on_read_header,
impl().shared_from_this(), impl().shared_from_this(),
std::placeholders::_1))); std::placeholders::_1)));
} }
@@ -228,12 +249,12 @@ private:
struct send_lambda struct send_lambda
{ {
// holds "this" // holds "this"
async_http_con& self_; async_http_con_base& self_;
public: public:
// capture "this" // capture "this"
explicit explicit
send_lambda(async_http_con& self) send_lambda(async_http_con_base& self)
: self_(self) : self_(self)
{ {
} }
@@ -251,9 +272,17 @@ private:
void void
on_read_header(error_code ec) on_read_header(error_code ec)
{ {
// This happens when the other end closes gracefully
//
if(ec == beast::http::error::end_of_stream)
{
// VFALCO what about the write queue?
return impl().do_shutdown();
}
// On failure we just return, the shared_ptr that is bound // On failure we just return, the shared_ptr that is bound
// into the completion will go out of scope and eventually // into the completion will go out of scope and eventually
// we will get destroyed. // this will get destroyed.
// //
if(ec) if(ec)
return fail("on_read", ec); return fail("on_read", ec);
@@ -281,7 +310,7 @@ private:
buffer_, buffer_,
*parser_, *parser_,
strand_.wrap(std::bind( strand_.wrap(std::bind(
&async_http_con::on_read, &async_http_con_base::on_read,
impl().shared_from_this(), impl().shared_from_this(),
std::placeholders::_1))); std::placeholders::_1)));
} }
@@ -290,6 +319,13 @@ private:
void void
on_read(error_code ec) on_read(error_code ec)
{ {
// Shouldn't be getting end_of_stream here;
// that would mean that we got an incomplete
// message, counting as an error.
//
if(ec)
return fail("on_read", ec);
// Grab a reference to the request again // Grab a reference to the request again
auto& req = parser_->get(); auto& req = parser_->get();
@@ -298,10 +334,10 @@ private:
// //
send_lambda send{*this}; send_lambda send{*this};
// Give each services a chance to handle the request // Give each service a chance to handle the request
// //
if(! services_.respond( if(! services_.respond(
impl().stream(), std::move(impl().stream()),
ep_, ep_,
std::move(req), std::move(req),
send)) send))
@@ -319,7 +355,7 @@ private:
if(! impl().stream().lowest_layer().is_open()) if(! impl().stream().lowest_layer().is_open())
{ {
// They took ownership so just return and // They took ownership so just return and
// let this async_http_con object get destroyed. // let this async_http_con_base object get destroyed.
// //
return; return;
} }
@@ -357,7 +393,7 @@ private:
impl().stream(), impl().stream(),
std::move(res), std::move(res),
strand_.wrap(std::bind( strand_.wrap(std::bind(
&async_http_con::on_write, &async_http_con_base::on_write,
impl().shared_from_this(), impl().shared_from_this(),
std::placeholders::_1))); std::placeholders::_1)));
} }
@@ -369,7 +405,7 @@ private:
impl().stream(), impl().stream(),
std::move(res), std::move(res),
strand_.wrap(std::bind( strand_.wrap(std::bind(
&async_http_con::on_write, &async_http_con_base::on_write,
impl().shared_from_this(), impl().shared_from_this(),
std::placeholders::_1)))); std::placeholders::_1))));
} }
@@ -381,6 +417,14 @@ private:
// Make sure our state is what we think it is // Make sure our state is what we think it is
BOOST_ASSERT(writing_); BOOST_ASSERT(writing_);
// This happens when we send an HTTP message
// whose semantics indicate that the connection
// should be closed afterwards. For example if
// we send a Connection: close.
//
if(ec == beast::http::error::end_of_stream)
return impl().do_shutdown();
// On failure just log and return // On failure just log and return
if(ec) if(ec)
return fail("on_write", ec); return fail("on_write", ec);
@@ -403,23 +447,6 @@ private:
// Delete the item since we used it // Delete the item since we used it
queue_.erase(queue_.begin()); queue_.erase(queue_.begin());
} }
// Called when a failure occurs
//
void
fail(std::string what, error_code ec)
{
// Don't log end of stream or operation aborted
// since those happen under normal circumstances.
//
if( ec != beast::http::error::end_of_stream &&
ec != boost::asio::error::operation_aborted)
{
log_ <<
"[#" << id_ << " " << ep_ << "] " <<
what << ": " << ec.message() << std::endl;
}
}
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -428,7 +455,7 @@ private:
// uses a plain TCP/IP socket (no encryption) as the stream. // uses a plain TCP/IP socket (no encryption) as the stream.
// //
template<class... Services> template<class... Services>
class async_http_con_plain class async_http_con
// Note that we give this object the enable_shared_from_this, and have // 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 // the base class call impl().shared_from_this(). The reason we do that
@@ -436,7 +463,7 @@ class async_http_con_plain
// derived class (this class) use its members in calls to std::bind, // derived class (this class) use its members in calls to std::bind,
// without an ugly call to `dynamic_downcast` or other nonsense. // without an ugly call to `dynamic_downcast` or other nonsense.
// //
: public std::enable_shared_from_this<async_http_con_plain<Services...>> : public std::enable_shared_from_this<async_http_con<Services...>>
// We want the socket to be created before the base class so we use // 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 "base from member" idiom which Boost provides as a class.
@@ -445,17 +472,17 @@ class async_http_con_plain
// Declare this base last now that everything else got set up first. // Declare this base last now that everything else got set up first.
// //
, public async_http_con<async_http_con_plain<Services...>, Services...> , public async_http_con_base<async_http_con<Services...>, Services...>
{ {
public: public:
// Construct the plain connection. // Construct the plain connection.
// //
template<class... Args> template<class... Args>
async_http_con_plain( async_http_con(
socket_type&& sock, socket_type&& sock,
Args&&... args) Args&&... args)
: base_from_member<socket_type>(std::move(sock)) : base_from_member<socket_type>(std::move(sock))
, async_http_con<async_http_con_plain<Services...>, Services...>( , async_http_con_base<async_http_con<Services...>, Services...>(
std::forward<Args>(args)...) std::forward<Args>(args)...)
{ {
} }
@@ -469,6 +496,31 @@ public:
{ {
return this->member; return this->member;
} }
private:
// Base class needs to be a friend to call our private members
friend class async_http_con_base<async_http_con<Services...>, Services...>;
// This is called by the base before running the main loop.
//
void
do_handshake()
{
// Run the main loop right away
//
this->do_run();
}
// This is called when the other end closes the connection gracefully.
//
void
do_shutdown()
{
error_code ec;
stream().shutdown(socket_type::shutdown_both, ec);
if(ec)
return this->fail("shutdown", ec);
}
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -539,7 +591,7 @@ public:
// Create a plain http connection object // Create a plain http connection object
// and transfer ownership of the socket. // and transfer ownership of the socket.
// //
std::make_shared<async_http_con_plain<Services...>>( std::make_shared<async_http_con<Services...>>(
std::move(sock), std::move(sock),
"http_async_port", "http_async_port",
log_, log_,

View File

@@ -42,7 +42,7 @@ namespace framework {
@tparam Services The list of services this connection will support. @tparam Services The list of services this connection will support.
*/ */
template<class Derived, class... Services> template<class Derived, class... Services>
class sync_http_con class sync_http_con_base
: public http_base : public http_base
{ {
// This function lets us access members of the derived class // This function lets us access members of the derived class
@@ -71,7 +71,7 @@ class sync_http_con
public: public:
/// Constructor /// Constructor
sync_http_con( sync_http_con_base(
beast::string_view server_name, beast::string_view server_name,
std::ostream& log, std::ostream& log,
service_list<Services...> const& services, service_list<Services...> const& services,
@@ -90,19 +90,36 @@ public:
{ {
} }
// This is called to start the connection after
// it is accepted.
//
void void
run() run()
{ {
// Bind a shared pointer into the lambda for the // Bind a shared pointer into the lambda for the
// thread, so the sync_http_con is destroyed after // thread, so the sync_http_con_base is destroyed after
// the thread function exits. // the thread function exits.
// //
std::thread{ std::thread{
&sync_http_con::do_run, &sync_http_con_base::do_run,
impl().shared_from_this() impl().shared_from_this()
}.detach(); }.detach();
} }
protected:
// Called when a failure occurs
//
void
fail(std::string what, error_code ec)
{
if(ec)
{
log_ <<
"[#" << id_ << " " << ep_ << "] " <<
what << ": " << ec.message() << std::endl;
}
}
private: private:
// This lambda is passed to the service list to handle // This lambda is passed to the service list to handle
// the case of sending request objects of varying types. // the case of sending request objects of varying types.
@@ -113,7 +130,7 @@ private:
struct send_lambda struct send_lambda
{ {
// holds "this" // holds "this"
sync_http_con& self_; sync_http_con_base& self_;
// holds the captured error code // holds the captured error code
error_code& ec_; error_code& ec_;
@@ -123,7 +140,7 @@ private:
// //
// Capture "this" and "ec" // Capture "this" and "ec"
// //
send_lambda(sync_http_con& self, error_code& ec) send_lambda(sync_http_con_base& self, error_code& ec)
: self_(self) : self_(self)
, ec_(ec) , ec_(ec)
{ {
@@ -146,6 +163,16 @@ private:
void void
do_run() do_run()
{ {
error_code ec;
// Give the derived class a chance to do stuff before we
// enter the main loop. This is for SSL connections really.
//
impl().do_handshake(ec);
if(ec)
return fail("handshake", ec);
// The main connection loop, we alternate between // The main connection loop, we alternate between
// reading a request and sending a response. On // reading a request and sending a response. On
// error we log and return, which destroys the thread // error we log and return, which destroys the thread
@@ -153,8 +180,6 @@ private:
// //
for(;;) for(;;)
{ {
error_code ec;
// Arguments passed to the parser constructor are // Arguments passed to the parser constructor are
// forwarded to the message object. A single argument // forwarded to the message object. A single argument
// is forwarded to the body constructor. // is forwarded to the body constructor.
@@ -167,8 +192,20 @@ private:
// Read the header first // Read the header first
beast::http::read_header(impl().stream(), buffer_, parser, ec); beast::http::read_header(impl().stream(), buffer_, parser, ec);
// This happens when the other end closes gracefully
//
if(ec == beast::http::error::end_of_stream)
{
// Give the derived class a chance to do stuff
impl().do_shutdown(ec);
if(ec)
return fail("shutdown", ec);
return;
}
// Any other error and we fail the connection
if(ec) if(ec)
return fail("on_read", ec); return fail("read_header", ec);
send_lambda send{*this, ec}; send_lambda send{*this, ec};
@@ -182,58 +219,105 @@ private:
// so send the appropriate response synchronously. // so send the appropriate response synchronously.
// //
send(this->continue_100(req)); send(this->continue_100(req));
// This happens when we send an HTTP message
// whose semantics indicate that the connection
// should be closed afterwards. For example if
// we send a Connection: close.
//
if(ec == beast::http::error::end_of_stream)
{
// Give the derived class a chance to do stuff
impl().do_shutdown(ec);
if(ec)
return fail("shutdown", ec);
return;
}
// Have to check the error every time we call the lambda
//
if(ec)
return fail("write", ec);
} }
// Read the rest of the message, if any. // Read the rest of the message, if any.
// //
beast::http::read(impl().stream(), buffer_, parser, ec); beast::http::read(impl().stream(), buffer_, parser, ec);
// Give each services a chance to handle the request // Shouldn't be getting end_of_stream here;
// that would mean that we got an incomplete
// message, counting as an error.
//
if(ec)
return fail("read", ec);
// Give each service a chance to handle the request
// //
if(! services_.respond( if(! services_.respond(
impl().stream(), std::move(impl().stream()),
ep_, ep_,
std::move(req), std::move(req),
send)) send))
{ {
// No service handled the request, // No service handled the request,
// send a Bad Request result to the client. // send a Bad Request result to the client.
// //
send(this->bad_request(req)); send(this->bad_request(req));
// This happens when we send an HTTP message
// whose semantics indicate that the connection
// should be closed afterwards. For example if
// we send a Connection: close.
//
if(ec == beast::http::error::end_of_stream)
{
// Give the derived class a chance to do stuff
impl().do_shutdown(ec);
if(ec)
return fail("shutdown", ec);
return;
}
// Have to check the error every time we call the lambda
//
if(ec)
return fail("write", ec);
} }
else else
{ {
// This happens when we send an HTTP message
// whose semantics indicate that the connection
// should be closed afterwards. For example if
// we send a Connection: close.
//
if(ec == beast::http::error::end_of_stream)
{
// Give the derived class a chance to do stuff
impl().do_shutdown(ec);
if(ec)
return fail("shutdown", ec);
return;
}
// Have to check the error every time we call the lambda
//
if(ec)
return fail("write", ec);
// See if the service that handled the // See if the service that handled the
// response took ownership of the stream. // response took ownership of the stream.
if(! impl().stream().is_open()) if(! impl().stream().lowest_layer().is_open())
{ {
// They took ownership so just return and // They took ownership so just return and
// let this sync_http_con object get destroyed. // let this sync_http_con_base object get destroyed.
return; return;
} }
} }
if(ec)
return fail("on_write", ec);
// Theres no pipelining possible in a synchronous server // Theres no pipelining possible in a synchronous server
// because we can't do reads and writes at the same time. // because we can't do reads and writes at the same time.
} }
} }
// Called when a failure occurs
//
void
fail(std::string what, error_code ec)
{
if( ec != beast::http::error::end_of_stream &&
ec != boost::asio::error::operation_aborted)
{
log_ <<
"[#" << id_ << " " << ep_ << "] " <<
what << ": " << ec.message() << std::endl;
}
}
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -242,12 +326,12 @@ private:
// uses a plain TCP/IP socket (no encryption) as the stream. // uses a plain TCP/IP socket (no encryption) as the stream.
// //
template<class... Services> template<class... Services>
class sync_http_con_plain class sync_http_con
// Note that we give this object the `enable_shared_from_this`, and have // Note that we give this object the `enable_shared_from_this`, and have
// the base class call `impl().shared_from_this()` when needed. // the base class call `impl().shared_from_this()` when needed.
// //
: public std::enable_shared_from_this<sync_http_con_plain<Services...>> : public std::enable_shared_from_this<sync_http_con<Services...>>
// We want the socket to be created before the base class so we use // 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 "base from member" idiom which Boost provides as a class.
@@ -256,17 +340,17 @@ class sync_http_con_plain
// Declare this base last now that everything else got set up first. // Declare this base last now that everything else got set up first.
// //
, public sync_http_con<sync_http_con_plain<Services...>, Services...> , public sync_http_con_base<sync_http_con<Services...>, Services...>
{ {
public: public:
// Construct the plain connection. // Construct the plain connection.
// //
template<class... Args> template<class... Args>
sync_http_con_plain( sync_http_con(
socket_type&& sock, socket_type&& sock,
Args&&... args) Args&&... args)
: base_from_member<socket_type>(std::move(sock)) : base_from_member<socket_type>(std::move(sock))
, sync_http_con<sync_http_con_plain<Services...>, Services...>( , sync_http_con_base<sync_http_con<Services...>, Services...>(
std::forward<Args>(args)...) std::forward<Args>(args)...)
{ {
} }
@@ -281,6 +365,29 @@ public:
{ {
return this->member; return this->member;
} }
private:
// Base class needs to be a friend to call our private members
friend class sync_http_con_base<sync_http_con<Services...>, Services...>;
// This is called by the base before running the main loop.
// There's nothing to do for a plain connection.
//
void
do_handshake(error_code& ec)
{
// This is required by the specifications for error_code
//
ec = {};
}
// This is called when the other end closes the connection gracefully.
//
void
do_shutdown(error_code& ec)
{
stream().shutdown(socket_type::shutdown_both, ec);
}
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -345,7 +452,7 @@ public:
// Create a plain http connection object // Create a plain http connection object
// and transfer ownership of the socket. // and transfer ownership of the socket.
// //
std::make_shared<sync_http_con_plain<Services...>>( std::make_shared<sync_http_con<Services...>>(
std::move(sock), std::move(sock),
"http_sync_port", "http_sync_port",
log_, log_,

View File

@@ -0,0 +1,368 @@
//
// 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_HTTPS_PORTS_HPP
#define BEAST_EXAMPLE_SERVER_HTTPS_PORTS_HPP
#include "http_sync_port.hpp"
#include "http_async_port.hpp"
#include "ssl_stream.hpp"
#include <boost/asio/ssl.hpp>
namespace framework {
//------------------------------------------------------------------------------
// This class represents a synchronous HTTP connection which
// uses an OpenSSL socket as the stream.
//
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.
//
: 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.
//
, public base_from_member<ssl_stream<socket_type>>
// Declare this base last now that everything else got set up first.
//
, public sync_http_con_base<sync_https_con<Services...>, Services...>
{
public:
// Construct the plain connection.
//
template<class... Args>
sync_https_con(
socket_type&& sock,
boost::asio::ssl::context& ctx,
Args&&... args)
: base_from_member<ssl_stream<socket_type>>(std::move(sock), ctx)
, sync_http_con_base<sync_https_con<Services...>, Services...>(
std::forward<Args>(args)...)
{
}
// Returns the stream.
// The base class calls this to obtain the object to
// use for reading and writing HTTP messages.
//
ssl_stream<socket_type>&
stream()
{
return this->member;
}
private:
friend class sync_http_con_base<sync_https_con<Services...>, Services...>;
// This is called by the base before running the main loop.
//
void
do_handshake(error_code& ec)
{
// Perform the SSL handshake
//
stream().handshake(boost::asio::ssl::stream_base::server, ec);
}
// This is called when the other end closes the connection gracefully.
//
void
do_shutdown(error_code& ec)
{
// Note that this is an SSL shutdown
//
stream().shutdown(ec);
if(ec)
return this->fail("shutdown", ec);
}
};
//------------------------------------------------------------------------------
// This class represents an asynchronous HTTP connection which
// uses an OpenSSL socket as the stream.
//
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.
//
: 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.
//
, public base_from_member<ssl_stream<socket_type>>
// Declare this base last now that everything else got set up first.
//
, public async_http_con_base<async_https_con<Services...>, Services...>
{
public:
// Construct the plain connection.
//
template<class... Args>
async_https_con(
socket_type&& sock,
boost::asio::ssl::context& ctx,
Args&&... args)
: base_from_member<ssl_stream<socket_type>>(std::move(sock), ctx)
, async_http_con_base<async_https_con<Services...>, Services...>(
std::forward<Args>(args)...)
{
}
// Returns the stream.
// The base class calls this to obtain the object to
// use for reading and writing HTTP messages.
//
ssl_stream<socket_type>&
stream()
{
return this->member;
}
private:
friend class async_http_con_base<async_https_con<Services...>, Services...>;
// Called by the base class before starting the main loop.
//
void
do_handshake()
{
// This is SSL so perform the handshake
//
stream().async_handshake(
boost::asio::ssl::stream_base::server,
this->strand_.wrap(std::bind(
&async_https_con::on_handshake,
this->shared_from_this(),
std::placeholders::_1)));
}
// Called when the SSL handshake completes
void
on_handshake(error_code ec)
{
if(ec)
return this->fail("on_handshake", ec);
// No error so run the main loop
this->do_run();
}
// Called when the end of stream is reached
void
do_shutdown()
{
// This is an SSL shutdown
//
stream().async_shutdown(
this->strand_.wrap(std::bind(
&async_https_con::on_shutdown,
this->shared_from_this(),
std::placeholders::_1)));
}
// Called when the SSL shutdown completes
void
on_shutdown(error_code ec)
{
if(ec)
return this->fail("on_shutdown", ec);
}
};
//------------------------------------------------------------------------------
/* A synchronous HTTPS port handler
This type meets the requirements of @b PortHandler. It supports
variable list of HTTP services in its template parameter list,
and provides a synchronous connection implementation to service
*/
template<class... Services>
class https_sync_port
{
// Reference to the server instance that made us
server& instance_;
// The stream to log to
std::ostream& log_;
// The list of services connections created from this port will support
service_list<Services...> services_;
// The SSL context containing the server's credentials
boost::asio::ssl::context& ctx_;
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
*/
https_sync_port(
server& instance,
std::ostream& log,
boost::asio::ssl::context& ctx)
: instance_(instance)
, log_(log)
, ctx_(ctx)
{
}
/** 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 an HTTPS connection object
// and transfer ownership of the socket.
//
std::make_shared<sync_https_con<Services...>>(
std::move(sock),
ctx_,
"https_sync_port",
log_,
services_,
instance_.next_id(),
ep
)->run();
}
};
//------------------------------------------------------------------------------
/* An asynchronous HTTPS port handler
This type meets the requirements of @b PortHandler. It supports
variable list of HTTP services in its template parameter list,
and provides a synchronous connection implementation to service
*/
template<class... Services>
class https_async_port
{
// Reference to the server instance that made us
server& instance_;
// The stream to log to
std::ostream& log_;
// The list of services connections created from this port will support
service_list<Services...> services_;
// The SSL context containing the server's credentials
boost::asio::ssl::context& ctx_;
public:
/** Constructor
@param instance The server instance which owns this port
@param log The stream to use for logging
*/
https_async_port(
server& instance,
std::ostream& log,
boost::asio::ssl::context& ctx)
: instance_(instance)
, log_(log)
, ctx_(ctx)
{
}
/** 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 an HTTPS connection object
// and transfer ownership of the socket.
//
std::make_shared<async_https_con<Services...>>(
std::move(sock),
ctx_,
"https_async_port",
log_,
services_,
instance_.next_id(),
ep
)->run();
}
};
} // framework
#endif

View File

@@ -12,6 +12,12 @@
#include "ws_async_port.hpp" #include "ws_async_port.hpp"
#include "ws_sync_port.hpp" #include "ws_sync_port.hpp"
#if BEAST_USE_OPENSSL
#include "https_ports.hpp"
#include "wss_ports.hpp"
#include "ssl_certificate.hpp"
#endif
#include "file_service.hpp" #include "file_service.hpp"
#include "ws_upgrade_service.hpp" #include "ws_upgrade_service.hpp"
@@ -133,6 +139,13 @@ main(
// Create our server instance with the specified number of threads // Create our server instance with the specified number of threads
server instance{threads}; server instance{threads};
//--------------------------------------------------------------------------
//
// Asynchronous WebSocket HTTP
//
// port port + 1
//
//--------------------------------------------------------------------------
{ {
// Install an asynchronous WebSocket echo port handler // Install an asynchronous WebSocket echo port handler
// //
@@ -185,6 +198,13 @@ main(
return fail("http_async_port/file_service", ec); return fail("http_async_port/file_service", ec);
} }
//--------------------------------------------------------------------------
//
// Synchronous WebSocket HTTP
//
// port + 2 port + 3
//
//--------------------------------------------------------------------------
{ {
// Install a synchronous WebSocket echo port handler // Install a synchronous WebSocket echo port handler
// //
@@ -238,5 +258,138 @@ main(
return fail("http_sync_port/file_service", ec); return fail("http_sync_port/file_service", ec);
} }
//
// If OpenSSL is available then install some SSL-enabled ports
//
#if BEAST_USE_OPENSSL
ssl_certificate cert;
//--------------------------------------------------------------------------
//
// Asynchronous Secure WebSocket HTTPS
//
// port + 4 port + 5
//
//--------------------------------------------------------------------------
{
// Install an asynchronous Secure WebSocket echo port handler
//
auto wsp = instance.make_port<wss_async_port>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(port + 4)},
instance,
std::cout,
cert.get(),
set_ws_options{pmd}
);
if(ec)
return fail("ws_async_port", ec);
// Install an asynchronous HTTPS port handler
//
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 + 5)},
instance,
std::cout,
cert.get());
if(ec)
return fail("https_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("https_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
"https_async_port" // The value for the Server field
);
if(ec)
return fail("https_async_port/file_service", ec);
}
//--------------------------------------------------------------------------
//
// Synchronous Secure WebSocket HTTPS
//
// port + 6 port + 7
//
//--------------------------------------------------------------------------
{
// Install a synchronous Secure WebSocket echo port handler
//
auto wsp = instance.make_port<wss_sync_port>(
ec,
endpoint_type{addr,
static_cast<unsigned short>(port + 6)},
instance,
std::cout,
cert.get(),
set_ws_options{pmd});
if(ec)
return fail("wss_sync_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.
//
sp->template init<0>(
ec,
wsp // The websocket port handler
);
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,
"https_sync_port"
);
if(ec)
return fail("https_sync_port/file_service", ec);
}
#endif
sig_wait(); sig_wait();
} }

View File

@@ -0,0 +1,146 @@
//
// 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_SSL_CERTIFICATE_HPP
#define BEAST_EXAMPLE_SERVER_SSL_CERTIFICATE_HPP
#include <boost/asio/buffer.hpp>
#include <boost/asio/ssl/context.hpp>
#include <cstddef>
#include <memory>
namespace framework {
// This sets up the self-signed certificate that the server
// uses for its encrypted connections
class ssl_certificate
{
// The template argument is gratuitous, to
// make the definition header-only without
// also making it inline.
//
template<class = void>
void
construct();
boost::asio::ssl::context ctx_;
public:
ssl_certificate()
: ctx_(boost::asio::ssl::context::sslv23)
{
construct();
}
boost::asio::ssl::context&
get()
{
return ctx_;
}
};
template<class>
void
ssl_certificate::construct()
{
/*
The certificate was generated from CMD.EXE on Windows 10 using:
winpty openssl dhparam -out dh.pem 2048
winpty openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 10000 -out cert.pem -subj "//C=US\ST=CA\L=Los Angeles\O=Beast\CN=www.example.com"
*/
std::string const cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIDaDCCAlCgAwIBAgIJAO8vBu8i8exWMA0GCSqGSIb3DQEBCwUAMEkxCzAJBgNV\n"
"BAYTAlVTMQswCQYDVQQIDAJDQTEtMCsGA1UEBwwkTG9zIEFuZ2VsZXNPPUJlYXN0\n"
"Q049d3d3LmV4YW1wbGUuY29tMB4XDTE3MDUwMzE4MzkxMloXDTQ0MDkxODE4Mzkx\n"
"MlowSTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMS0wKwYDVQQHDCRMb3MgQW5n\n"
"ZWxlc089QmVhc3RDTj13d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA\n"
"A4IBDwAwggEKAoIBAQDJ7BRKFO8fqmsEXw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcF\n"
"xqGitbnLIrOgiJpRAPLy5MNcAXE1strVGfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7b\n"
"Fu8TsCzO6XrxpnVtWk506YZ7ToTa5UjHfBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO\n"
"9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wWKIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBp\n"
"yY8anC8u4LPbmgW0/U31PH0rRVfGcBbZsAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrv\n"
"enu2tOK9Qx6GEzXh3sekZkxcgh+NlIxCNxu//Dk9AgMBAAGjUzBRMB0GA1UdDgQW\n"
"BBTZh0N9Ne1OD7GBGJYz4PNESHuXezAfBgNVHSMEGDAWgBTZh0N9Ne1OD7GBGJYz\n"
"4PNESHuXezAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCmTJVT\n"
"LH5Cru1vXtzb3N9dyolcVH82xFVwPewArchgq+CEkajOU9bnzCqvhM4CryBb4cUs\n"
"gqXWp85hAh55uBOqXb2yyESEleMCJEiVTwm/m26FdONvEGptsiCmF5Gxi0YRtn8N\n"
"V+KhrQaAyLrLdPYI7TrwAOisq2I1cD0mt+xgwuv/654Rl3IhOMx+fKWKJ9qLAiaE\n"
"fQyshjlPP9mYVxWOxqctUdQ8UnsUKKGEUcVrA08i1OAnVKlPFjKBvk+r7jpsTPcr\n"
"9pWXTO9JrYMML7d+XRSZA1n3856OqZDX4403+9FnXCvfcLZLLKTBvwwFgEFGpzjK\n"
"UEVbkhd5qstF6qWK\n"
"-----END CERTIFICATE-----\n";
std::string const key =
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ7BRKFO8fqmsE\n"
"Xw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcFxqGitbnLIrOgiJpRAPLy5MNcAXE1strV\n"
"GfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7bFu8TsCzO6XrxpnVtWk506YZ7ToTa5UjH\n"
"fBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wW\n"
"KIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBpyY8anC8u4LPbmgW0/U31PH0rRVfGcBbZ\n"
"sAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrvenu2tOK9Qx6GEzXh3sekZkxcgh+NlIxC\n"
"Nxu//Dk9AgMBAAECggEBAK1gV8uETg4SdfE67f9v/5uyK0DYQH1ro4C7hNiUycTB\n"
"oiYDd6YOA4m4MiQVJuuGtRR5+IR3eI1zFRMFSJs4UqYChNwqQGys7CVsKpplQOW+\n"
"1BCqkH2HN/Ix5662Dv3mHJemLCKUON77IJKoq0/xuZ04mc9csykox6grFWB3pjXY\n"
"OEn9U8pt5KNldWfpfAZ7xu9WfyvthGXlhfwKEetOuHfAQv7FF6s25UIEU6Hmnwp9\n"
"VmYp2twfMGdztz/gfFjKOGxf92RG+FMSkyAPq/vhyB7oQWxa+vdBn6BSdsfn27Qs\n"
"bTvXrGe4FYcbuw4WkAKTljZX7TUegkXiwFoSps0jegECgYEA7o5AcRTZVUmmSs8W\n"
"PUHn89UEuDAMFVk7grG1bg8exLQSpugCykcqXt1WNrqB7x6nB+dbVANWNhSmhgCg\n"
"VrV941vbx8ketqZ9YInSbGPWIU/tss3r8Yx2Ct3mQpvpGC6iGHzEc/NHJP8Efvh/\n"
"CcUWmLjLGJYYeP5oNu5cncC3fXUCgYEA2LANATm0A6sFVGe3sSLO9un1brA4zlZE\n"
"Hjd3KOZnMPt73B426qUOcw5B2wIS8GJsUES0P94pKg83oyzmoUV9vJpJLjHA4qmL\n"
"CDAd6CjAmE5ea4dFdZwDDS8F9FntJMdPQJA9vq+JaeS+k7ds3+7oiNe+RUIHR1Sz\n"
"VEAKh3Xw66kCgYB7KO/2Mchesu5qku2tZJhHF4QfP5cNcos511uO3bmJ3ln+16uR\n"
"GRqz7Vu0V6f7dvzPJM/O2QYqV5D9f9dHzN2YgvU9+QSlUeFK9PyxPv3vJt/WP1//\n"
"zf+nbpaRbwLxnCnNsKSQJFpnrE166/pSZfFbmZQpNlyeIuJU8czZGQTifQKBgHXe\n"
"/pQGEZhVNab+bHwdFTxXdDzr+1qyrodJYLaM7uFES9InVXQ6qSuJO+WosSi2QXlA\n"
"hlSfwwCwGnHXAPYFWSp5Owm34tbpp0mi8wHQ+UNgjhgsE2qwnTBUvgZ3zHpPORtD\n"
"23KZBkTmO40bIEyIJ1IZGdWO32q79nkEBTY+v/lRAoGBAI1rbouFYPBrTYQ9kcjt\n"
"1yfu4JF5MvO9JrHQ9tOwkqDmNCWx9xWXbgydsn/eFtuUMULWsG3lNjfst/Esb8ch\n"
"k5cZd6pdJZa4/vhEwrYYSuEjMCnRb0lUsm7TsHxQrUd6Fi/mUuFU/haC0o0chLq7\n"
"pVOUFq5mW8p0zbtfHbjkgxyF\n"
"-----END PRIVATE KEY-----\n";
std::string const dh =
"-----BEGIN DH PARAMETERS-----\n"
"MIIBCAKCAQEArzQc5mpm0Fs8yahDeySj31JZlwEphUdZ9StM2D8+Fo7TMduGtSi+\n"
"/HRWVwHcTFAgrxVdm+dl474mOUqqaz4MpzIb6+6OVfWHbQJmXPepZKyu4LgUPvY/\n"
"4q3/iDMjIS0fLOu/bLuObwU5ccZmDgfhmz1GanRlTQOiYRty3FiOATWZBRh6uv4u\n"
"tff4A9Bm3V9tLx9S6djq31w31Gl7OQhryodW28kc16t9TvO1BzcV3HjRPwpe701X\n"
"oEEZdnZWANkkpR/m/pfgdmGPU66S2sXMHgsliViQWpDCYeehrvFRHEdR9NV+XJfC\n"
"QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n"
"-----END DH PARAMETERS-----\n";
ctx_.set_password_callback(
[](std::size_t size,
boost::asio::ssl::context_base::password_purpose)
{
return "test";
});
ctx_.set_options(
boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::single_dh_use);
ctx_.use_certificate_chain(
boost::asio::buffer(cert.data(), cert.size()));
ctx_.use_private_key(
boost::asio::buffer(key.data(), key.size()),
boost::asio::ssl::context::file_format::pem);
ctx_.use_tmp_dh(
boost::asio::buffer(dh.data(), dh.size()));
}
} // framework
#endif

View File

@@ -8,24 +8,40 @@
#ifndef BEAST_EXAMPLE_SERVER_SSL_STREAM_HPP #ifndef BEAST_EXAMPLE_SERVER_SSL_STREAM_HPP
#define BEAST_EXAMPLE_SERVER_SSL_STREAM_HPP #define BEAST_EXAMPLE_SERVER_SSL_STREAM_HPP
// This include is necessary to work with `ssl::stream` and `beast::websocket::stream`
#include <beast/websocket/ssl.hpp>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp> #include <boost/asio/ssl/stream.hpp>
#include <cstddef>
#include <memory> #include <memory>
#include <type_traits>
#include <utility>
namespace beast { /** C++11 enabled SSL socket wrapper
/** Movable SSL socket wrapper
This wrapper provides an interface identical to `boost::asio::ssl::stream`, This wrapper provides an interface identical to `boost::asio::ssl::stream`,
which is additionally move constructible and move assignable. with the following additional properties:
@li Satisfies @b MoveConstructible
@li Satisfies @b MoveAssignable
@li Constructible from a moved socket.
*/ */
template<class NextLayer> template<class NextLayer>
class ssl_stream class ssl_stream
: public boost::asio::ssl::stream_base : public boost::asio::ssl::stream_base
{ {
// only works for boost::asio::ip::tcp::socket
// for now because of the move limitations
static_assert(std::is_same<NextLayer, boost::asio::ip::tcp::socket>::value,
"NextLayer requirements not met");
using stream_type = boost::asio::ssl::stream<NextLayer>; using stream_type = boost::asio::ssl::stream<NextLayer>;
std::unique_ptr<stream_type> p_; std::unique_ptr<stream_type> p_;
boost::asio::ssl::context* ctx_;
public: public:
/// The native handle type of the SSL stream. /// The native handle type of the SSL stream.
@@ -43,15 +59,30 @@ public:
/// The type of the lowest layer. /// The type of the lowest layer.
using lowest_layer_type = typename stream_type::lowest_layer_type; using lowest_layer_type = typename stream_type::lowest_layer_type;
ssl_stream(ssl_stream&&) = default; ssl_stream(boost::asio::ip::tcp::socket&& sock, boost::asio::ssl::context& ctx)
ssl_stream(ssl_stream const&) = delete; : p_(new stream_type{sock.get_io_service(), ctx})
ssl_stream& operator=(ssl_stream&&) = default; , ctx_(&ctx)
ssl_stream& operator=(ssl_stream const&) = delete;
template<class... Args>
ssl_stream(Args&&... args)
: p_(new stream_type{std::forward<Args>(args)...)
{ {
p_->next_layer() = std::move(sock);
}
ssl_stream(ssl_stream&& other)
: p_(new stream_type(other.get_io_service(), *other.ctx_))
, ctx_(other.ctx_)
{
using std::swap;
swap(p_, other.p_);
}
ssl_stream& operator=(ssl_stream&& other)
{
std::unique_ptr<stream_type> p(
new stream_type{other.get_io_service(), other.ctx_});
using std::swap;
swap(p_, p);
swap(p_, other.p_);
ctx_ = other.ctx_;
return *this;
} }
boost::asio::io_service& boost::asio::io_service&
@@ -184,7 +215,7 @@ public:
BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler) BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler)
{ {
return p_->async_handshake(type, buffers, return p_->async_handshake(type, buffers,
BOOST_ASIO__MOVE_CAST(BufferedHandshakeHandler)(handler)); BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler));
} }
void void
@@ -251,14 +282,53 @@ public:
template<class MutableBufferSequence, class ReadHandler> template<class MutableBufferSequence, class ReadHandler>
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler, BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
void(boost::system::error_code, std::size_t)) void(boost::system::error_code, std::size_t))
async_read_some(MutableBufferSequence& buffers, async_read_some(MutableBufferSequence const& buffers,
BOOST_ASIO_MOVE_ARG(ReadHandler) handler) BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
{ {
return p_->async_read_some(buffers, return p_->async_read_some(buffers,
BOOST_ASIO_MOVE_CAST(ReadHandler)(handler)) BOOST_ASIO_MOVE_CAST(ReadHandler)(handler));
} }
template<class SyncStream>
friend
void
teardown(beast::websocket::teardown_tag,
ssl_stream<SyncStream>& stream,
boost::system::error_code& ec);
template<class AsyncStream, class TeardownHandler>
friend
void
async_teardown(beast::websocket::teardown_tag,
ssl_stream<AsyncStream>& stream, TeardownHandler&& handler);
}; };
} // beast // These hooks are used to inform beast::websocket::stream on
// how to tear down the connection as part of the WebSocket
// protocol specifications
#endif template<class SyncStream>
inline
void
teardown(beast::websocket::teardown_tag,
ssl_stream<SyncStream>& stream,
boost::system::error_code& ec)
{
// Just forward it to the wrapped ssl::stream
using beast::websocket::teardown;
teardown(beast::websocket::teardown_tag{}, *stream.p_, ec);
}
template<class AsyncStream, class TeardownHandler>
inline
void
async_teardown(beast::websocket::teardown_tag,
ssl_stream<AsyncStream>& stream, TeardownHandler&& handler)
{
// Just forward it to the wrapped ssl::stream
using beast::websocket::async_teardown;
async_teardown(beast::websocket::teardown_tag{},
*stream.p_, std::forward<TeardownHandler>(handler));
}
#endif

View File

@@ -23,7 +23,7 @@ namespace framework {
// //
// //
template<class Derived> template<class Derived>
class async_ws_con class async_ws_con_base
{ {
// This function lets us access members of the derived class // This function lets us access members of the derived class
Derived& Derived&
@@ -58,7 +58,7 @@ protected:
public: public:
// Constructor // Constructor
template<class Callback> template<class Callback>
async_ws_con( async_ws_con_base(
beast::string_view server_name, beast::string_view server_name,
std::ostream& log, std::ostream& log,
std::size_t id, std::size_t id,
@@ -72,9 +72,9 @@ public:
// Limit of 1MB on messages // Limit of 1MB on messages
, buffer_(1024 * 1024) , buffer_(1024 * 1024)
, strand_(impl().ws().get_io_service()) , strand_(impl().stream().get_io_service())
{ {
cb(impl().ws()); cb(impl().stream());
} }
// Run the connection // Run the connection
@@ -82,18 +82,7 @@ public:
void void
run() run()
{ {
// Read the WebSocket upgrade request and attempt impl().do_handshake();
// to send back the response.
//
impl().ws().async_accept_ex(
[&](beast::websocket::response_type& res)
{
res.set(beast::http::field::server, server_name_);
},
strand_.wrap(std::bind(
&async_ws_con::on_accept,
impl().shared_from_this(),
std::placeholders::_1)));
} }
// Run the connection. // Run the connection.
@@ -109,17 +98,47 @@ public:
// the request by parameter, instead of reading // the request by parameter, instead of reading
// it from the network. // it from the network.
// //
impl().ws().async_accept_ex(req, impl().stream().async_accept_ex(req,
[&](beast::websocket::response_type& res) [&](beast::websocket::response_type& res)
{ {
res.set(beast::http::field::server, server_name_); res.set(beast::http::field::server, server_name_);
}, },
strand_.wrap(std::bind( strand_.wrap(std::bind(
&async_ws_con::on_accept, &async_ws_con_base::on_accept,
impl().shared_from_this(), impl().shared_from_this(),
std::placeholders::_1))); std::placeholders::_1)));
} }
protected:
// Performs the WebSocket handshake
void
do_accept()
{
// Read the WebSocket upgrade request and attempt
// to send back the response.
//
impl().stream().async_accept_ex(
[&](beast::websocket::response_type& res)
{
res.set(beast::http::field::server, server_name_);
},
strand_.wrap(std::bind(
&async_ws_con_base::on_accept,
impl().shared_from_this(),
std::placeholders::_1)));
}
// This helper reports failures
//
void
fail(std::string what, error_code ec)
{
if(ec != beast::websocket::error::closed)
log_ <<
"[#" << id_ << " " << ep_ << "] " <<
what << ": " << ec.message() << std::endl;
}
private: private:
// Called when accept_ex completes // Called when accept_ex completes
// //
@@ -136,10 +155,10 @@ private:
void void
do_read() do_read()
{ {
impl().ws().async_read( impl().stream().async_read(
buffer_, buffer_,
strand_.wrap(std::bind( strand_.wrap(std::bind(
&async_ws_con::on_read, &async_ws_con_base::on_read,
impl().shared_from_this(), impl().shared_from_this(),
std::placeholders::_1))); std::placeholders::_1)));
} }
@@ -155,14 +174,14 @@ private:
// Set the outgoing message type. We will use // Set the outgoing message type. We will use
// the same setting as the message we just read. // the same setting as the message we just read.
// //
impl().ws().binary(impl().ws().got_binary()); impl().stream().binary(impl().stream().got_binary());
// Now echo back the message // Now echo back the message
// //
impl().ws().async_write( impl().stream().async_write(
buffer_.data(), buffer_.data(),
strand_.wrap(std::bind( strand_.wrap(std::bind(
&async_ws_con::on_write, &async_ws_con_base::on_write,
impl().shared_from_this(), impl().shared_from_this(),
std::placeholders::_1))); std::placeholders::_1)));
} }
@@ -184,17 +203,6 @@ private:
// //
do_read(); do_read();
} }
// This helper reports failures
//
void
fail(std::string what, error_code ec)
{
if(ec != beast::websocket::error::closed)
log_ <<
"[#" << id_ << " " << ep_ << "] " <<
what << ": " << ec.message() << std::endl;
}
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -202,12 +210,12 @@ private:
// This class represents an asynchronous WebSocket connection // This class represents an asynchronous WebSocket connection
// which uses a plain TCP/IP socket (no encryption) as the stream. // which uses a plain TCP/IP socket (no encryption) as the stream.
// //
class async_ws_con_plain class async_ws_con
// Note that we give this object the `enable_shared_from_this`, and have // Note that we give this object the `enable_shared_from_this`, and have
// the base class call `impl().shared_from_this()` when needed. // the base class call `impl().shared_from_this()` when needed.
// //
: public std::enable_shared_from_this<async_ws_con_plain> : public std::enable_shared_from_this<async_ws_con>
// We want the socket to be created before the base class so we use // 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 "base from member" idiom which Boost provides as a class.
@@ -216,18 +224,18 @@ class async_ws_con_plain
// Declare this base last now that everything else got set up first. // Declare this base last now that everything else got set up first.
// //
, public async_ws_con<async_ws_con_plain> , public async_ws_con_base<async_ws_con>
{ {
public: public:
// Construct the plain connection. // Construct the plain connection.
// //
template<class... Args> template<class... Args>
explicit explicit
async_ws_con_plain( async_ws_con(
socket_type&& sock, socket_type&& sock,
Args&&... args) Args&&... args)
: base_from_member<beast::websocket::stream<socket_type>>(std::move(sock)) : base_from_member<beast::websocket::stream<socket_type>>(std::move(sock))
, async_ws_con<async_ws_con_plain>(std::forward<Args>(args)...) , async_ws_con_base<async_ws_con>(std::forward<Args>(args)...)
{ {
} }
@@ -236,10 +244,20 @@ public:
// The base class calls this to obtain the websocket stream object. // The base class calls this to obtain the websocket stream object.
// //
beast::websocket::stream<socket_type>& beast::websocket::stream<socket_type>&
ws() stream()
{ {
return this->member; return this->member;
} }
private:
// Base class needs to be a friend to call our private members
friend async_ws_con_base<async_ws_con>;
void
do_handshake()
{
do_accept();
}
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -304,7 +322,7 @@ public:
socket_type&& sock, socket_type&& sock,
endpoint_type ep) endpoint_type ep)
{ {
std::make_shared<async_ws_con_plain>( std::make_shared<async_ws_con>(
std::move(sock), std::move(sock),
"ws_async_port", "ws_async_port",
log_, log_,
@@ -332,7 +350,7 @@ public:
endpoint_type ep, endpoint_type ep,
beast::http::request<Body, Fields>&& req) beast::http::request<Body, Fields>&& req)
{ {
std::make_shared<async_ws_con_plain>( std::make_shared<async_ws_con>(
std::move(sock), std::move(sock),
"ws_async_port", "ws_async_port",
log_, log_,

View File

@@ -30,7 +30,7 @@ namespace framework {
for plain and SSL stream objects. for plain and SSL stream objects.
*/ */
template<class Derived> template<class Derived>
class sync_ws_con class sync_ws_con_base
{ {
// This function lets us access members of the derived class // This function lets us access members of the derived class
Derived& Derived&
@@ -56,7 +56,7 @@ class sync_ws_con
public: public:
// Constructor // Constructor
template<class Callback> template<class Callback>
sync_ws_con( sync_ws_con_base(
beast::string_view server_name, beast::string_view server_name,
std::ostream& log, std::ostream& log,
std::size_t id, std::size_t id,
@@ -67,22 +67,23 @@ public:
, id_(id) , id_(id)
, ep_(ep) , ep_(ep)
{ {
cb(impl().ws()); cb(impl().stream());
} }
// Run the connection. // Run the connection. This is called for the case
// where we have not received the upgrade request yet.
// //
void void
run() run()
{ {
// We run the do_accept function in its own thread, // We run the do_run function in its own thread,
// and bind a shared pointer to the connection object // and bind a shared pointer to the connection object
// into the function. The last reference to the shared // into the function. The last reference to the shared
// pointer will go away when the thread exits, thus // pointer will go away when the thread exits, thus
// destroying the connection object. // destroying the connection object.
// //
std::thread{ std::thread{
&sync_ws_con::do_accept, &sync_ws_con_base::do_accept,
impl().shared_from_this() impl().shared_from_this()
}.detach(); }.detach();
} }
@@ -106,7 +107,58 @@ public:
}}.detach(); }}.detach();
} }
protected:
// Called when a failure occurs
//
void
fail(std::string what, error_code ec)
{
// Don't report the "closed" error since that
// happens under normal circumstances.
//
if(ec && ec != beast::websocket::error::closed)
{
log_ <<
"[#" << id_ << " " << ep_ << "] " <<
what << ": " << ec.message() << std::endl;
log_.flush();
}
}
private: private:
// This function performs the WebSocket handshake
// and runs the main loop upon success.
void
do_accept()
{
error_code ec;
// Give the derived class a chance to do stuff before we
// enter the main loop. This is for SSL connections really.
//
impl().do_handshake(ec);
if(ec)
return fail("handshake", ec);
// Read the WebSocket upgrade request and attempt
// to send back the response.
//
impl().stream().accept_ex(
[&](beast::websocket::response_type& res)
{
res.insert(beast::http::field::server, server_name_);
},
ec);
if(ec)
return fail("accept", ec);
// Run the connection
//
do_run();
}
// This is the lambda used when launching a connection from // This is the lambda used when launching a connection from
// an already-received request. In C++14 we could simply use // an already-received request. In C++14 we could simply use
// a lambda capture but this example requires only C++11 so // a lambda capture but this example requires only C++11 so
@@ -116,7 +168,7 @@ private:
template<class Body, class Fields> template<class Body, class Fields>
class lambda class lambda
{ {
std::shared_ptr<sync_ws_con> self_; std::shared_ptr<sync_ws_con_base> self_;
beast::http::request<Body, Fields> req_; beast::http::request<Body, Fields> req_;
public: public:
@@ -125,7 +177,7 @@ private:
// This is the equivalent of the capture section of the lambda. // This is the equivalent of the capture section of the lambda.
// //
lambda( lambda(
std::shared_ptr<sync_ws_con> self, std::shared_ptr<sync_ws_con_base> self,
beast::http::request<Body, Fields>&& req) beast::http::request<Body, Fields>&& req)
: self_(std::move(self)) : self_(std::move(self))
, req_(std::move(req)) , req_(std::move(req))
@@ -151,7 +203,7 @@ private:
// the request by parameter, instead of reading // the request by parameter, instead of reading
// it from the network. // it from the network.
// //
self_->impl().ws().accept_ex(req, self_->impl().stream().accept_ex(req,
[&](beast::websocket::response_type& res) [&](beast::websocket::response_type& res)
{ {
res.insert(beast::http::field::server, self_->server_name_); res.insert(beast::http::field::server, self_->server_name_);
@@ -159,55 +211,18 @@ private:
ec); ec);
} }
// Run the connection if(ec)
// return self_->fail("accept", ec);
self_->do_run(ec);
self_->do_run();
} }
}; };
void void
do_accept() do_run()
{ {
error_code ec; error_code ec;
// Read the WebSocket upgrade request and attempt
// to send back the response.
//
impl().ws().accept_ex(
[&](beast::websocket::response_type& res)
{
res.insert(beast::http::field::server, server_name_);
},
ec);
// Run the connection
//
do_run(ec);
}
void
do_run(error_code ec)
{
// Helper lambda to report a failure
//
auto const fail =
[&](std::string const& what, error_code ev)
{
if(ev != beast::websocket::error::closed)
log_ <<
"[#" << id_ << " " << ep_ << "] " <<
what << ": " << ev.message() << std::endl;
};
// Check for an error upon entry. This will
// come from one of the two calls to accept()
//
if(ec)
{
fail("accept", ec);
return;
}
// Loop, reading messages and echoing them back. // Loop, reading messages and echoing them back.
// //
for(;;) for(;;)
@@ -219,7 +234,7 @@ private:
// Read the message // Read the message
// //
impl().ws().read(buffer, ec); impl().stream().read(buffer, ec);
if(ec) if(ec)
return fail("read", ec); return fail("read", ec);
@@ -227,11 +242,11 @@ private:
// Set the outgoing message type. We will use // Set the outgoing message type. We will use
// the same setting as the message we just read. // the same setting as the message we just read.
// //
impl().ws().binary(impl().ws().got_binary()); impl().stream().binary(impl().stream().got_binary());
// Now echo back the message // Now echo back the message
// //
impl().ws().write(buffer.data(), ec); impl().stream().write(buffer.data(), ec);
if(ec) if(ec)
return fail("write", ec); return fail("write", ec);
@@ -244,12 +259,12 @@ private:
// This class represents a synchronous WebSocket connection // This class represents a synchronous WebSocket connection
// which uses a plain TCP/IP socket (no encryption) as the stream. // which uses a plain TCP/IP socket (no encryption) as the stream.
// //
class sync_ws_con_plain class sync_ws_con
// Note that we give this object the `enable_shared_from_this`, and have // Note that we give this object the `enable_shared_from_this`, and have
// the base class call `impl().shared_from_this()` when needed. // the base class call `impl().shared_from_this()` when needed.
// //
: public std::enable_shared_from_this<sync_ws_con_plain> : public std::enable_shared_from_this<sync_ws_con>
// We want the socket to be created before the base class so we use // 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 "base from member" idiom which Boost provides as a class.
@@ -258,18 +273,18 @@ class sync_ws_con_plain
// Declare this base last now that everything else got set up first. // Declare this base last now that everything else got set up first.
// //
, public sync_ws_con<sync_ws_con_plain> , public sync_ws_con_base<sync_ws_con>
{ {
public: public:
// Construct the plain connection. // Construct the plain connection.
// //
template<class... Args> template<class... Args>
explicit explicit
sync_ws_con_plain( sync_ws_con(
socket_type&& sock, socket_type&& sock,
Args&&... args) Args&&... args)
: base_from_member<beast::websocket::stream<socket_type>>(std::move(sock)) : base_from_member<beast::websocket::stream<socket_type>>(std::move(sock))
, sync_ws_con<sync_ws_con_plain>(std::forward<Args>(args)...) , sync_ws_con_base<sync_ws_con>(std::forward<Args>(args)...)
{ {
} }
@@ -278,10 +293,25 @@ public:
// The base class calls this to obtain the websocket stream object. // The base class calls this to obtain the websocket stream object.
// //
beast::websocket::stream<socket_type>& beast::websocket::stream<socket_type>&
ws() stream()
{ {
return this->member; return this->member;
} }
private:
// Base class needs to be a friend to call our private members
friend class sync_ws_con_base<sync_ws_con>;
// This is called by the base before running the main loop.
// There's nothing to do for a plain connection.
//
void
do_handshake(error_code& ec)
{
// This is required by the specifications for error_code
//
ec = {};
}
}; };
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -346,7 +376,7 @@ public:
{ {
// Create our connection object and run it // Create our connection object and run it
// //
std::make_shared<sync_ws_con_plain>( std::make_shared<sync_ws_con>(
std::move(sock), std::move(sock),
"ws_sync_port", "ws_sync_port",
log_, log_,
@@ -377,7 +407,7 @@ public:
// Create the connection object and run it, // Create the connection object and run it,
// transferring ownershop of the ugprade request. // transferring ownershop of the ugprade request.
// //
std::make_shared<sync_ws_con_plain>( std::make_shared<sync_ws_con>(
std::move(sock), std::move(sock),
"ws_sync_port", "ws_sync_port",
log_, log_,

View File

@@ -0,0 +1,414 @@
//
// 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_WSS_PORTS_HPP
#define BEAST_EXAMPLE_SERVER_WSS_PORTS_HPP
#include "ws_sync_port.hpp"
#include "ws_async_port.hpp"
#include "ssl_stream.hpp"
#include <boost/asio/ssl.hpp>
#include <boost/function.hpp>
namespace framework {
//------------------------------------------------------------------------------
// This class represents a synchronous Secure WebSocket 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.
//
: 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.
//
, public base_from_member<beast::websocket::stream<ssl_stream<socket_type>>>
// Declare this base last now that everything else got set up first.
//
, public sync_ws_con_base<sync_wss_con>
{
public:
// Construct the plain connection.
//
template<class... Args>
explicit
sync_wss_con(
socket_type&& sock,
boost::asio::ssl::context& ctx,
Args&&... args)
: base_from_member<beast::websocket::stream<ssl_stream<socket_type>>>(std::move(sock), ctx)
, sync_ws_con_base<sync_wss_con>(std::forward<Args>(args)...)
{
}
// Construct the connection from an existing, handshaked SSL stream
//
template<class... Args>
sync_wss_con(
ssl_stream<socket_type>&& stream,
Args&&... args)
: base_from_member<beast::websocket::stream<ssl_stream<socket_type>>>(std::move(stream))
, sync_ws_con_base<sync_wss_con>(std::forward<Args>(args)...)
{
}
// Returns the stream.
//
// The base class calls this to obtain the websocket stream object.
//
beast::websocket::stream<ssl_stream<socket_type>>&
stream()
{
return this->member;
}
private:
friend class sync_ws_con_base<sync_wss_con>;
// This is called by the base before running the main loop.
//
void
do_handshake(error_code& ec)
{
// Perform the SSL handshake
//
// We use next_layer() to get at the underlying ssl_stream
//
stream().next_layer().handshake(boost::asio::ssl::stream_base::server, ec);
}
};
//------------------------------------------------------------------------------
// This class represents an asynchronous Secure WebSocket
// connection which uses an OpenSSL socket as the stream.
//
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.
//
: 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.
//
, public base_from_member<beast::websocket::stream<ssl_stream<socket_type>>>
// Declare this base last now that everything else got set up first.
//
, public async_ws_con_base<async_wss_con>
{
public:
// Construct the connection.
//
template<class... Args>
async_wss_con(
socket_type&& sock,
boost::asio::ssl::context& ctx,
Args&&... args)
: base_from_member<beast::websocket::stream<ssl_stream<socket_type>>>(std::move(sock), ctx)
, async_ws_con_base<async_wss_con>(std::forward<Args>(args)...)
{
}
// Construct the connection from an existing, handshaked SSL stream
//
template<class... Args>
async_wss_con(
ssl_stream<socket_type>&& stream,
Args&&... args)
: base_from_member<beast::websocket::stream<ssl_stream<socket_type>>>(std::move(stream))
, async_ws_con_base<async_wss_con>(std::forward<Args>(args)...)
{
}
// Returns the stream.
// The base class calls this to obtain the object to
// use for reading and writing HTTP messages.
//
beast::websocket::stream<ssl_stream<socket_type>>&
stream()
{
return this->member;
}
private:
friend class async_ws_con_base<async_wss_con>;
// Called by the port to start the connection
// after creating the object
//
void
do_handshake()
{
// This is SSL so perform the handshake first
//
stream().next_layer().async_handshake(
boost::asio::ssl::stream_base::server,
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)
{
if(ec)
return this->fail("on_handshake", ec);
// Move on to accepting the WebSocket handshake
//
this->do_accept();
}
};
//------------------------------------------------------------------------------
/** A synchronous Secure WebSocket @b PortHandler which implements echo.
This is a port handler which accepts Secure WebSocket upgrade
HTTP requests and implements the echo protocol. All received
WebSocket messages will be echoed back to the remote host.
*/
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>>&)>;
server& instance_;
std::ostream& log_;
boost::asio::ssl::context& ctx_;
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>
wss_sync_port(
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 TCP/IP connection.
This function is called when the server has accepted an
incoming connection.
@param sock The connected socket.
@param ep The endpoint of the remote host.
*/
void
on_accept(socket_type&& sock, endpoint_type ep)
{
// Create our connection object and run it
//
std::make_shared<sync_wss_con>(
std::move(sock),
ctx_,
"wss_sync_port",
log_,
instance_.next_id(),
ep,
cb2_
)->run();
}
/** 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
accept(
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.
//
std::make_shared<sync_wss_con>(
std::move(stream),
"wss_sync_port",
log_,
instance_.next_id(),
ep,
cb2_
)->run(std::move(req));
}
};
//------------------------------------------------------------------------------
/** An asynchronous WebSocket @b PortHandler which implements echo.
This is a port handler which accepts WebSocket upgrade HTTP
requests and implements the echo protocol. All received
WebSocket messages will be echoed back to the remote host.
*/
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>>&)>;
server& instance_;
std::ostream& log_;
boost::asio::ssl::context& ctx_;
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>
wss_async_port(
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 TCP/IP connection.
This function is called when the server has accepted an
incoming connection.
@param sock The connected socket.
@param ep The endpoint of the remote host.
*/
void
on_accept(
socket_type&& sock,
endpoint_type ep)
{
std::make_shared<async_wss_con>(
std::move(sock),
ctx_,
"wss_async_port",
log_,
instance_.next_id(),
ep,
cb2_
)->run();
}
/** 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
accept(
ssl_stream<socket_type>&& stream,
endpoint_type ep,
beast::http::request<Body, Fields>&& req)
{
std::make_shared<async_wss_con>(
std::move(stream),
"wss_async_port",
log_,
instance_.next_id(),
ep,
cb2_
)->run(std::move(req));
}
};
} // framework
#endif

View File

@@ -5,6 +5,16 @@ GroupSources(include/beast beast)
GroupSources(test/server "/") GroupSources(test/server "/")
if (OPENSSL_FOUND)
include_directories(${OPENSSL_INCLUDE_DIR})
set(SSL_SOURCES
https_ports.cpp
ssl_stream.cpp
)
else ()
set(SSL_SOURCES "")
endif()
add_executable (server-test add_executable (server-test
${BEAST_INCLUDES} ${BEAST_INCLUDES}
${SERVER_INCLUDES} ${SERVER_INCLUDES}
@@ -14,15 +24,22 @@ add_executable (server-test
http_async_port.cpp http_async_port.cpp
http_base.cpp http_base.cpp
http_sync_port.cpp http_sync_port.cpp
https_ports.cpp
main.cpp main.cpp
rfc7231.cpp rfc7231.cpp
server.cpp server.cpp
service_list.cpp service_list.cpp
ssl_certificate
write_msg.cpp write_msg.cpp
ws_async_port.cpp ws_async_port.cpp
ws_sync_port.cpp ws_sync_port.cpp
ws_upgrade_service.cpp ws_upgrade_service.cpp
wss_ports.cpp
${SSL_SOURCES}
) )
target_link_libraries(server-test Beast) target_link_libraries(server-test Beast)
if (OPENSSL_FOUND)
target_link_libraries(server-test ${OPENSSL_LIBRARIES})
endif()

View File

@@ -20,4 +20,7 @@ exe server-test :
ws_async_port.cpp ws_async_port.cpp
ws_sync_port.cpp ws_sync_port.cpp
ws_upgrade_service.cpp ws_upgrade_service.cpp
#https_ports.cpp
#ssl_certificate.cpp
#ssl_stream.cpp
; ;

View File

@@ -0,0 +1,13 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#if BEAST_USE_OPENSSL
// Test that header file is self-contained.
#include "../../example/server-framework/https_ports.hpp"
#endif

View File

@@ -0,0 +1,13 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#if BEAST_USE_OPENSSL
// Test that header file is self-contained.
#include "../../example/server-framework/ssl_certificate.hpp"
#endif

View File

@@ -0,0 +1,13 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#if BEAST_USE_OPENSSL
// Test that header file is self-contained.
#include "../../example/server-framework/ssl_stream.hpp"
#endif

13
test/server/wss_ports.cpp Normal file
View File

@@ -0,0 +1,13 @@
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#if BEAST_USE_OPENSSL
// Test that header file is self-contained.
#include "../../example/server-framework/wss_ports.hpp"
#endif