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

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