mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 05:17:26 +02:00
Add server-framework SSL HTTP and WebSocket ports
This commit is contained in:
@ -8,6 +8,7 @@ Version 61:
|
||||
* Tidy up names in error categories
|
||||
* Flush the output stream in the example
|
||||
* Clean close in Secure WebSocket client
|
||||
* Add server-framework SSL HTTP and WebSocket ports
|
||||
|
||||
API Changes:
|
||||
|
||||
|
@ -70,6 +70,13 @@ endif()
|
||||
|
||||
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)
|
||||
|
||||
if (NOT OPENSSL_FOUND)
|
||||
message("OpenSSL not found. Not building SSL tests and examples")
|
||||
else()
|
||||
if (OPENSSL_FOUND)
|
||||
add_subdirectory (example/ssl-http-client)
|
||||
add_subdirectory (example/ssl-websocket-client)
|
||||
add_subdirectory (test/websocket/ssl)
|
||||
|
@ -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_base.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/rfc7231.hpp]
|
||||
* [repo_file example/server-framework/server.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/ws_async_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/wss_ports.hpp]
|
||||
|
||||
[endsect]
|
||||
|
||||
|
@ -4,6 +4,10 @@ GroupSources(include/beast beast)
|
||||
|
||||
GroupSources(example/server-framework "/")
|
||||
|
||||
if (OPENSSL_FOUND)
|
||||
include_directories(${OPENSSL_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
add_executable (server-framework
|
||||
${BEAST_INCLUDES}
|
||||
${SERVER_INCLUDES}
|
||||
@ -15,3 +19,7 @@ target_link_libraries(
|
||||
Beast
|
||||
${Boost_PROGRAM_OPTIONS_LIBRARY}
|
||||
${Boost_FILESYSTEM_LIBRARY})
|
||||
|
||||
if (OPENSSL_FOUND)
|
||||
target_link_libraries(server-framework ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
|
@ -118,7 +118,7 @@ make_queued_http_write(
|
||||
@tparam Services The list of services this connection will support.
|
||||
*/
|
||||
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
|
||||
Derived&
|
||||
@ -161,7 +161,7 @@ protected:
|
||||
|
||||
public:
|
||||
// Constructor
|
||||
async_http_con(
|
||||
async_http_con_base(
|
||||
beast::string_view server_name,
|
||||
std::ostream& log,
|
||||
service_list<Services...> const& services,
|
||||
@ -180,15 +180,36 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
// Called by the port after creating the object
|
||||
void
|
||||
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();
|
||||
}
|
||||
|
||||
// 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:
|
||||
// Perform an asynchronous read for the next request header
|
||||
//
|
||||
@ -214,7 +235,7 @@ private:
|
||||
buffer_,
|
||||
*parser_,
|
||||
strand_.wrap(std::bind(
|
||||
&async_http_con::on_read_header,
|
||||
&async_http_con_base::on_read_header,
|
||||
impl().shared_from_this(),
|
||||
std::placeholders::_1)));
|
||||
}
|
||||
@ -228,12 +249,12 @@ private:
|
||||
struct send_lambda
|
||||
{
|
||||
// holds "this"
|
||||
async_http_con& self_;
|
||||
async_http_con_base& self_;
|
||||
|
||||
public:
|
||||
// capture "this"
|
||||
explicit
|
||||
send_lambda(async_http_con& self)
|
||||
send_lambda(async_http_con_base& self)
|
||||
: self_(self)
|
||||
{
|
||||
}
|
||||
@ -251,9 +272,17 @@ private:
|
||||
void
|
||||
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
|
||||
// into the completion will go out of scope and eventually
|
||||
// we will get destroyed.
|
||||
// this will get destroyed.
|
||||
//
|
||||
if(ec)
|
||||
return fail("on_read", ec);
|
||||
@ -281,7 +310,7 @@ private:
|
||||
buffer_,
|
||||
*parser_,
|
||||
strand_.wrap(std::bind(
|
||||
&async_http_con::on_read,
|
||||
&async_http_con_base::on_read,
|
||||
impl().shared_from_this(),
|
||||
std::placeholders::_1)));
|
||||
}
|
||||
@ -290,6 +319,13 @@ private:
|
||||
void
|
||||
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
|
||||
auto& req = parser_->get();
|
||||
|
||||
@ -298,10 +334,10 @@ private:
|
||||
//
|
||||
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(
|
||||
impl().stream(),
|
||||
std::move(impl().stream()),
|
||||
ep_,
|
||||
std::move(req),
|
||||
send))
|
||||
@ -319,7 +355,7 @@ private:
|
||||
if(! impl().stream().lowest_layer().is_open())
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
@ -357,7 +393,7 @@ private:
|
||||
impl().stream(),
|
||||
std::move(res),
|
||||
strand_.wrap(std::bind(
|
||||
&async_http_con::on_write,
|
||||
&async_http_con_base::on_write,
|
||||
impl().shared_from_this(),
|
||||
std::placeholders::_1)));
|
||||
}
|
||||
@ -369,7 +405,7 @@ private:
|
||||
impl().stream(),
|
||||
std::move(res),
|
||||
strand_.wrap(std::bind(
|
||||
&async_http_con::on_write,
|
||||
&async_http_con_base::on_write,
|
||||
impl().shared_from_this(),
|
||||
std::placeholders::_1))));
|
||||
}
|
||||
@ -381,6 +417,14 @@ private:
|
||||
// Make sure our state is what we think it is
|
||||
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
|
||||
if(ec)
|
||||
return fail("on_write", ec);
|
||||
@ -403,23 +447,6 @@ private:
|
||||
// Delete the item since we used it
|
||||
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.
|
||||
//
|
||||
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
|
||||
// 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,
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
, public async_http_con<async_http_con_plain<Services...>, Services...>
|
||||
, public async_http_con_base<async_http_con<Services...>, Services...>
|
||||
{
|
||||
public:
|
||||
// Construct the plain connection.
|
||||
//
|
||||
template<class... Args>
|
||||
async_http_con_plain(
|
||||
async_http_con(
|
||||
socket_type&& sock,
|
||||
Args&&... args)
|
||||
: 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)...)
|
||||
{
|
||||
}
|
||||
@ -469,6 +496,31 @@ public:
|
||||
{
|
||||
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
|
||||
// and transfer ownership of the socket.
|
||||
//
|
||||
std::make_shared<async_http_con_plain<Services...>>(
|
||||
std::make_shared<async_http_con<Services...>>(
|
||||
std::move(sock),
|
||||
"http_async_port",
|
||||
log_,
|
||||
|
@ -42,7 +42,7 @@ namespace framework {
|
||||
@tparam Services The list of services this connection will support.
|
||||
*/
|
||||
template<class Derived, class... Services>
|
||||
class sync_http_con
|
||||
class sync_http_con_base
|
||||
: public http_base
|
||||
{
|
||||
// This function lets us access members of the derived class
|
||||
@ -71,7 +71,7 @@ class sync_http_con
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
sync_http_con(
|
||||
sync_http_con_base(
|
||||
beast::string_view server_name,
|
||||
std::ostream& log,
|
||||
service_list<Services...> const& services,
|
||||
@ -90,19 +90,36 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
// This is called to start the connection after
|
||||
// it is accepted.
|
||||
//
|
||||
void
|
||||
run()
|
||||
{
|
||||
// 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.
|
||||
//
|
||||
std::thread{
|
||||
&sync_http_con::do_run,
|
||||
&sync_http_con_base::do_run,
|
||||
impl().shared_from_this()
|
||||
}.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:
|
||||
// This lambda is passed to the service list to handle
|
||||
// the case of sending request objects of varying types.
|
||||
@ -113,7 +130,7 @@ private:
|
||||
struct send_lambda
|
||||
{
|
||||
// holds "this"
|
||||
sync_http_con& self_;
|
||||
sync_http_con_base& self_;
|
||||
|
||||
// holds the captured error code
|
||||
error_code& ec_;
|
||||
@ -123,7 +140,7 @@ private:
|
||||
//
|
||||
// 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)
|
||||
, ec_(ec)
|
||||
{
|
||||
@ -146,6 +163,16 @@ private:
|
||||
void
|
||||
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
|
||||
// reading a request and sending a response. On
|
||||
// error we log and return, which destroys the thread
|
||||
@ -153,8 +180,6 @@ private:
|
||||
//
|
||||
for(;;)
|
||||
{
|
||||
error_code ec;
|
||||
|
||||
// Arguments passed to the parser constructor are
|
||||
// forwarded to the message object. A single argument
|
||||
// is forwarded to the body constructor.
|
||||
@ -167,8 +192,20 @@ private:
|
||||
// Read the header first
|
||||
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)
|
||||
return fail("on_read", ec);
|
||||
return fail("read_header", ec);
|
||||
|
||||
send_lambda send{*this, ec};
|
||||
|
||||
@ -182,58 +219,105 @@ private:
|
||||
// so send the appropriate response synchronously.
|
||||
//
|
||||
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.
|
||||
//
|
||||
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(
|
||||
impl().stream(),
|
||||
ep_,
|
||||
std::move(req),
|
||||
send))
|
||||
std::move(impl().stream()),
|
||||
ep_,
|
||||
std::move(req),
|
||||
send))
|
||||
{
|
||||
// No service handled the request,
|
||||
// send a Bad Request result to the client.
|
||||
//
|
||||
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
|
||||
{
|
||||
// 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
|
||||
// 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
|
||||
// let this sync_http_con object get destroyed.
|
||||
// let this sync_http_con_base object get destroyed.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(ec)
|
||||
return fail("on_write", ec);
|
||||
|
||||
// Theres no pipelining possible in a synchronous server
|
||||
// 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.
|
||||
//
|
||||
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
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
, public sync_http_con<sync_http_con_plain<Services...>, Services...>
|
||||
, public sync_http_con_base<sync_http_con<Services...>, Services...>
|
||||
{
|
||||
public:
|
||||
// Construct the plain connection.
|
||||
//
|
||||
template<class... Args>
|
||||
sync_http_con_plain(
|
||||
sync_http_con(
|
||||
socket_type&& sock,
|
||||
Args&&... args)
|
||||
: 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)...)
|
||||
{
|
||||
}
|
||||
@ -281,6 +365,29 @@ public:
|
||||
{
|
||||
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
|
||||
// and transfer ownership of the socket.
|
||||
//
|
||||
std::make_shared<sync_http_con_plain<Services...>>(
|
||||
std::make_shared<sync_http_con<Services...>>(
|
||||
std::move(sock),
|
||||
"http_sync_port",
|
||||
log_,
|
||||
|
368
example/server-framework/https_ports.hpp
Normal file
368
example/server-framework/https_ports.hpp
Normal 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
|
@ -12,6 +12,12 @@
|
||||
#include "ws_async_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 "ws_upgrade_service.hpp"
|
||||
|
||||
@ -133,6 +139,13 @@ main(
|
||||
// Create our server instance with the specified number of threads
|
||||
server instance{threads};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Asynchronous WebSocket HTTP
|
||||
//
|
||||
// port port + 1
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
{
|
||||
// Install an asynchronous WebSocket echo port handler
|
||||
//
|
||||
@ -185,6 +198,13 @@ main(
|
||||
return fail("http_async_port/file_service", ec);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
//
|
||||
// Synchronous WebSocket HTTP
|
||||
//
|
||||
// port + 2 port + 3
|
||||
//
|
||||
//--------------------------------------------------------------------------
|
||||
{
|
||||
// Install a synchronous WebSocket echo port handler
|
||||
//
|
||||
@ -238,5 +258,138 @@ main(
|
||||
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();
|
||||
}
|
||||
|
146
example/server-framework/ssl_certificate.hpp
Normal file
146
example/server-framework/ssl_certificate.hpp
Normal 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
|
@ -8,24 +8,40 @@
|
||||
#ifndef 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/ssl/stream.hpp>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Movable SSL socket wrapper
|
||||
/** C++11 enabled SSL socket wrapper
|
||||
|
||||
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>
|
||||
class ssl_stream
|
||||
: 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>;
|
||||
|
||||
std::unique_ptr<stream_type> p_;
|
||||
boost::asio::ssl::context* ctx_;
|
||||
|
||||
public:
|
||||
/// The native handle type of the SSL stream.
|
||||
@ -43,15 +59,30 @@ public:
|
||||
/// The type of the lowest layer.
|
||||
using lowest_layer_type = typename stream_type::lowest_layer_type;
|
||||
|
||||
ssl_stream(ssl_stream&&) = default;
|
||||
ssl_stream(ssl_stream const&) = delete;
|
||||
ssl_stream& operator=(ssl_stream&&) = default;
|
||||
ssl_stream& operator=(ssl_stream const&) = delete;
|
||||
|
||||
template<class... Args>
|
||||
ssl_stream(Args&&... args)
|
||||
: p_(new stream_type{std::forward<Args>(args)...)
|
||||
ssl_stream(boost::asio::ip::tcp::socket&& sock, boost::asio::ssl::context& ctx)
|
||||
: p_(new stream_type{sock.get_io_service(), ctx})
|
||||
, ctx_(&ctx)
|
||||
{
|
||||
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&
|
||||
@ -184,7 +215,7 @@ public:
|
||||
BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler)
|
||||
{
|
||||
return p_->async_handshake(type, buffers,
|
||||
BOOST_ASIO__MOVE_CAST(BufferedHandshakeHandler)(handler));
|
||||
BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler));
|
||||
}
|
||||
|
||||
void
|
||||
@ -251,14 +282,53 @@ public:
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||
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)
|
||||
{
|
||||
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
|
@ -23,7 +23,7 @@ namespace framework {
|
||||
//
|
||||
//
|
||||
template<class Derived>
|
||||
class async_ws_con
|
||||
class async_ws_con_base
|
||||
{
|
||||
// This function lets us access members of the derived class
|
||||
Derived&
|
||||
@ -58,7 +58,7 @@ protected:
|
||||
public:
|
||||
// Constructor
|
||||
template<class Callback>
|
||||
async_ws_con(
|
||||
async_ws_con_base(
|
||||
beast::string_view server_name,
|
||||
std::ostream& log,
|
||||
std::size_t id,
|
||||
@ -72,9 +72,9 @@ public:
|
||||
// Limit of 1MB on messages
|
||||
, buffer_(1024 * 1024)
|
||||
|
||||
, strand_(impl().ws().get_io_service())
|
||||
, strand_(impl().stream().get_io_service())
|
||||
{
|
||||
cb(impl().ws());
|
||||
cb(impl().stream());
|
||||
}
|
||||
|
||||
// Run the connection
|
||||
@ -82,18 +82,7 @@ public:
|
||||
void
|
||||
run()
|
||||
{
|
||||
// Read the WebSocket upgrade request and attempt
|
||||
// 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)));
|
||||
impl().do_handshake();
|
||||
}
|
||||
|
||||
// Run the connection.
|
||||
@ -109,17 +98,47 @@ public:
|
||||
// the request by parameter, instead of reading
|
||||
// it from the network.
|
||||
//
|
||||
impl().ws().async_accept_ex(req,
|
||||
impl().stream().async_accept_ex(req,
|
||||
[&](beast::websocket::response_type& res)
|
||||
{
|
||||
res.set(beast::http::field::server, server_name_);
|
||||
},
|
||||
strand_.wrap(std::bind(
|
||||
&async_ws_con::on_accept,
|
||||
&async_ws_con_base::on_accept,
|
||||
impl().shared_from_this(),
|
||||
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:
|
||||
// Called when accept_ex completes
|
||||
//
|
||||
@ -136,10 +155,10 @@ private:
|
||||
void
|
||||
do_read()
|
||||
{
|
||||
impl().ws().async_read(
|
||||
impl().stream().async_read(
|
||||
buffer_,
|
||||
strand_.wrap(std::bind(
|
||||
&async_ws_con::on_read,
|
||||
&async_ws_con_base::on_read,
|
||||
impl().shared_from_this(),
|
||||
std::placeholders::_1)));
|
||||
}
|
||||
@ -155,14 +174,14 @@ private:
|
||||
// Set the outgoing message type. We will use
|
||||
// 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
|
||||
//
|
||||
impl().ws().async_write(
|
||||
impl().stream().async_write(
|
||||
buffer_.data(),
|
||||
strand_.wrap(std::bind(
|
||||
&async_ws_con::on_write,
|
||||
&async_ws_con_base::on_write,
|
||||
impl().shared_from_this(),
|
||||
std::placeholders::_1)));
|
||||
}
|
||||
@ -184,17 +203,6 @@ private:
|
||||
//
|
||||
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
|
||||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
, public async_ws_con<async_ws_con_plain>
|
||||
, public async_ws_con_base<async_ws_con>
|
||||
{
|
||||
public:
|
||||
// Construct the plain connection.
|
||||
//
|
||||
template<class... Args>
|
||||
explicit
|
||||
async_ws_con_plain(
|
||||
async_ws_con(
|
||||
socket_type&& sock,
|
||||
Args&&... args)
|
||||
: 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.
|
||||
//
|
||||
beast::websocket::stream<socket_type>&
|
||||
ws()
|
||||
stream()
|
||||
{
|
||||
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,
|
||||
endpoint_type ep)
|
||||
{
|
||||
std::make_shared<async_ws_con_plain>(
|
||||
std::make_shared<async_ws_con>(
|
||||
std::move(sock),
|
||||
"ws_async_port",
|
||||
log_,
|
||||
@ -332,7 +350,7 @@ public:
|
||||
endpoint_type ep,
|
||||
beast::http::request<Body, Fields>&& req)
|
||||
{
|
||||
std::make_shared<async_ws_con_plain>(
|
||||
std::make_shared<async_ws_con>(
|
||||
std::move(sock),
|
||||
"ws_async_port",
|
||||
log_,
|
||||
|
@ -30,7 +30,7 @@ namespace framework {
|
||||
for plain and SSL stream objects.
|
||||
*/
|
||||
template<class Derived>
|
||||
class sync_ws_con
|
||||
class sync_ws_con_base
|
||||
{
|
||||
// This function lets us access members of the derived class
|
||||
Derived&
|
||||
@ -56,7 +56,7 @@ class sync_ws_con
|
||||
public:
|
||||
// Constructor
|
||||
template<class Callback>
|
||||
sync_ws_con(
|
||||
sync_ws_con_base(
|
||||
beast::string_view server_name,
|
||||
std::ostream& log,
|
||||
std::size_t id,
|
||||
@ -67,22 +67,23 @@ public:
|
||||
, id_(id)
|
||||
, 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
|
||||
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
|
||||
// into the function. The last reference to the shared
|
||||
// pointer will go away when the thread exits, thus
|
||||
// destroying the connection object.
|
||||
//
|
||||
std::thread{
|
||||
&sync_ws_con::do_accept,
|
||||
&sync_ws_con_base::do_accept,
|
||||
impl().shared_from_this()
|
||||
}.detach();
|
||||
}
|
||||
@ -106,7 +107,58 @@ public:
|
||||
}}.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:
|
||||
// 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
|
||||
// an already-received request. In C++14 we could simply use
|
||||
// a lambda capture but this example requires only C++11 so
|
||||
@ -116,7 +168,7 @@ private:
|
||||
template<class Body, class Fields>
|
||||
class lambda
|
||||
{
|
||||
std::shared_ptr<sync_ws_con> self_;
|
||||
std::shared_ptr<sync_ws_con_base> self_;
|
||||
beast::http::request<Body, Fields> req_;
|
||||
|
||||
public:
|
||||
@ -125,7 +177,7 @@ private:
|
||||
// This is the equivalent of the capture section of the lambda.
|
||||
//
|
||||
lambda(
|
||||
std::shared_ptr<sync_ws_con> self,
|
||||
std::shared_ptr<sync_ws_con_base> self,
|
||||
beast::http::request<Body, Fields>&& req)
|
||||
: self_(std::move(self))
|
||||
, req_(std::move(req))
|
||||
@ -151,7 +203,7 @@ private:
|
||||
// the request by parameter, instead of reading
|
||||
// it from the network.
|
||||
//
|
||||
self_->impl().ws().accept_ex(req,
|
||||
self_->impl().stream().accept_ex(req,
|
||||
[&](beast::websocket::response_type& res)
|
||||
{
|
||||
res.insert(beast::http::field::server, self_->server_name_);
|
||||
@ -159,55 +211,18 @@ private:
|
||||
ec);
|
||||
}
|
||||
|
||||
// Run the connection
|
||||
//
|
||||
self_->do_run(ec);
|
||||
if(ec)
|
||||
return self_->fail("accept", ec);
|
||||
|
||||
self_->do_run();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
do_accept()
|
||||
do_run()
|
||||
{
|
||||
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.
|
||||
//
|
||||
for(;;)
|
||||
@ -219,7 +234,7 @@ private:
|
||||
|
||||
// Read the message
|
||||
//
|
||||
impl().ws().read(buffer, ec);
|
||||
impl().stream().read(buffer, ec);
|
||||
|
||||
if(ec)
|
||||
return fail("read", ec);
|
||||
@ -227,11 +242,11 @@ private:
|
||||
// Set the outgoing message type. We will use
|
||||
// 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
|
||||
//
|
||||
impl().ws().write(buffer.data(), ec);
|
||||
impl().stream().write(buffer.data(), ec);
|
||||
|
||||
if(ec)
|
||||
return fail("write", ec);
|
||||
@ -244,12 +259,12 @@ private:
|
||||
// This class represents a synchronous WebSocket connection
|
||||
// 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
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
, public sync_ws_con<sync_ws_con_plain>
|
||||
, public sync_ws_con_base<sync_ws_con>
|
||||
{
|
||||
public:
|
||||
// Construct the plain connection.
|
||||
//
|
||||
template<class... Args>
|
||||
explicit
|
||||
sync_ws_con_plain(
|
||||
sync_ws_con(
|
||||
socket_type&& sock,
|
||||
Args&&... args)
|
||||
: 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.
|
||||
//
|
||||
beast::websocket::stream<socket_type>&
|
||||
ws()
|
||||
stream()
|
||||
{
|
||||
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
|
||||
//
|
||||
std::make_shared<sync_ws_con_plain>(
|
||||
std::make_shared<sync_ws_con>(
|
||||
std::move(sock),
|
||||
"ws_sync_port",
|
||||
log_,
|
||||
@ -377,7 +407,7 @@ public:
|
||||
// Create the connection object and run it,
|
||||
// transferring ownershop of the ugprade request.
|
||||
//
|
||||
std::make_shared<sync_ws_con_plain>(
|
||||
std::make_shared<sync_ws_con>(
|
||||
std::move(sock),
|
||||
"ws_sync_port",
|
||||
log_,
|
||||
|
414
example/server-framework/wss_ports.hpp
Normal file
414
example/server-framework/wss_ports.hpp
Normal 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
|
@ -5,6 +5,16 @@ GroupSources(include/beast beast)
|
||||
|
||||
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
|
||||
${BEAST_INCLUDES}
|
||||
${SERVER_INCLUDES}
|
||||
@ -14,15 +24,22 @@ add_executable (server-test
|
||||
http_async_port.cpp
|
||||
http_base.cpp
|
||||
http_sync_port.cpp
|
||||
https_ports.cpp
|
||||
main.cpp
|
||||
rfc7231.cpp
|
||||
server.cpp
|
||||
service_list.cpp
|
||||
ssl_certificate
|
||||
write_msg.cpp
|
||||
ws_async_port.cpp
|
||||
ws_sync_port.cpp
|
||||
ws_upgrade_service.cpp
|
||||
wss_ports.cpp
|
||||
${SSL_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(server-test Beast)
|
||||
|
||||
if (OPENSSL_FOUND)
|
||||
target_link_libraries(server-test ${OPENSSL_LIBRARIES})
|
||||
endif()
|
||||
|
@ -20,4 +20,7 @@ exe server-test :
|
||||
ws_async_port.cpp
|
||||
ws_sync_port.cpp
|
||||
ws_upgrade_service.cpp
|
||||
#https_ports.cpp
|
||||
#ssl_certificate.cpp
|
||||
#ssl_stream.cpp
|
||||
;
|
||||
|
13
test/server/https_ports.cpp
Normal file
13
test/server/https_ports.cpp
Normal 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
|
13
test/server/ssl_certificate.cpp
Normal file
13
test/server/ssl_certificate.cpp
Normal 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
|
13
test/server/ssl_stream.cpp
Normal file
13
test/server/ssl_stream.cpp
Normal 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
13
test/server/wss_ports.cpp
Normal 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
|
Reference in New Issue
Block a user