diff --git a/example/server-framework/README.md b/example/server-framework/README.md
index aadfc9d3..8bb4d9b4 100644
--- a/example/server-framework/README.md
+++ b/example/server-framework/README.md
@@ -39,8 +39,6 @@ A port handler takes the stream object resulting form an incoming connection
request and constructs a handler-specific connection object which provides
the desired behavior.
-## Service
-
The HTTP ports which come with the example have a system built in which allows
installation of framework and user-defined "HTTP services". These services
inform connections using the port on how to handle specific requests. This is
@@ -66,3 +64,74 @@ the HTTP ports:
+## PortHandler Requirements
+```C++
+/** An synchronous 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.
+*/
+struct PortHandler
+{
+ /** Accept a TCP/IP socket.
+
+ 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);
+};
+```
+
+## Service Requirements
+
+```C++
+struct Service
+{
+ /** Initialize the service
+
+ @param ec Set to the error, if any occurred
+ */
+ void
+ init(error_code& ec);
+
+ /** Maybe respond to an HTTP request
+
+ Upon handling the response, the service may optionally
+ take ownership of either the stream, the request, or both.
+
+ @param stream The stream representing the connection
+
+ @param ep The remote endpoint of the stream
+
+ @param req The HTTP request
+
+ @param send A function object which operates on a single
+ argument of type beast::http::message. The function object
+ has this equivalent signature:
+ @code
+ template
+ void send(beast::http::response&& res);
+ @endcode
+
+ @return `true` if the service handled the response.
+ */
+ template<
+ class Stream,
+ class Body, class Fields,
+ class Send>
+ bool
+ respond(
+ Stream&& stream,
+ endpoint_type const& ep,
+ beast::http::request&& req,
+ Send const& send) const
+};
+```
diff --git a/example/server-framework/http_async_port.hpp b/example/server-framework/http_async_port.hpp
index 36d12658..5b266c91 100644
--- a/example/server-framework/http_async_port.hpp
+++ b/example/server-framework/http_async_port.hpp
@@ -27,7 +27,7 @@
namespace framework {
-// Base class for a type-erased, queued asynchronous HTTP write
+// Base class for a type-erased, queued asynchronous HTTP write operation
//
struct queued_http_write
{
@@ -111,7 +111,7 @@ make_queued_http_write(
asynchronous calls.
It uses the Curiously Recurring Template pattern (CRTP) where
- we refer to the derivd class in order to access the stream object
+ we refer to the derived class in order to access the stream object
to use for reading and writing. This lets the same class be used
for plain and SSL stream objects.
@@ -492,7 +492,12 @@ class http_async_port
service_list services_;
public:
- // Constructor
+ /** Constructor
+
+ @param instance The server instance which owns this port
+
+ @param log The stream to use for logging
+ */
http_async_port(
server& instance,
std::ostream& log)
diff --git a/example/server-framework/http_sync_port.hpp b/example/server-framework/http_sync_port.hpp
index 3b724c3b..891619e2 100644
--- a/example/server-framework/http_sync_port.hpp
+++ b/example/server-framework/http_sync_port.hpp
@@ -35,7 +35,7 @@ namespace framework {
synchronous calls.
It uses the Curiously Recurring Template pattern (CRTP) where
- we refer to the derivd class in order to access the stream object
+ we refer to the derived class in order to access the stream object
to use for reading and writing. This lets the same class be used
for plain and SSL stream objects.
@@ -45,6 +45,7 @@ template
class sync_http_con
: public http_base
{
+ // This function lets us access members of the derived class
Derived&
impl()
{
@@ -271,6 +272,7 @@ public:
}
// Returns the stream.
+ //
// The base class calls this to obtain the object to
// use for reading and writing HTTP messages.
//
@@ -297,7 +299,12 @@ class http_sync_port
service_list services_;
public:
- // Constructor
+ /** Constructor
+
+ @param instance The server instance which owns this port
+
+ @param log The stream to use for logging
+ */
http_sync_port(
server& instance,
std::ostream& log)
diff --git a/example/server-framework/server.hpp b/example/server-framework/server.hpp
index 70f21489..6157714c 100644
--- a/example/server-framework/server.hpp
+++ b/example/server-framework/server.hpp
@@ -154,6 +154,10 @@ public:
//------------------------------------------------------------------------------
+/* This implementation class wraps the PortHandler and
+ manages the listening socket. Upon an incoming connection
+ it transfers ownership of the socket to the PortHandler.
+*/
template
class port
: public std::enable_shared_from_this<
@@ -228,7 +232,11 @@ private:
if(ec == boost::asio::error::operation_aborted)
return;
if(! ec)
+ {
+ // Transfer ownership of the socket to the PortHandler
+ //
handler_.on_accept(std::move(sock_), ep_);
+ }
acceptor_.async_accept(sock_, ep_,
std::bind(&port::on_accept, this->shared_from_this(),
std::placeholders::_1));
diff --git a/example/server-framework/service_list.hpp b/example/server-framework/service_list.hpp
index 2d47f39f..872b49a3 100644
--- a/example/server-framework/service_list.hpp
+++ b/example/server-framework/service_list.hpp
@@ -24,38 +24,6 @@ namespace framework {
`true` if the request is processed or `false` if it does not
process the request.
- @b Service requirements
-
- @code
-
- struct Service
- {
- // Initialize the service
- //
- void
- init(error_code& ec);
-
- // Maybe respond to an HTTP request
- //
- // Returns `true` if it handled the response.
- //
- // Upon handling the response, the service may optionally
- // take ownership of either the stream, the request, or both.
- //
- template<
- class Stream,
- class Body, class Fields,
- class Send>
- bool
- respond(
- Stream&&,
- endpoint_type const& ep,
- beast::http::request&& req,
- Send const& send) const
- };
-
- @endcode
-
@see file_service, ws_upgrade_service
*/
template
diff --git a/example/server-framework/write_msg.hpp b/example/server-framework/write_msg.hpp
index e5d25c0e..4a27f110 100644
--- a/example/server-framework/write_msg.hpp
+++ b/example/server-framework/write_msg.hpp
@@ -35,9 +35,19 @@ template<
bool isRequest, class Body, class Fields>
class write_msg_op
{
+ // This composed operation has a state which is not trivial
+ // to copy (msg) so we need to store the state in an allocated
+ // object.
+ //
struct data
{
+ // The stream we are writing to
AsyncWriteStream& stream;
+
+ // The message we are sending. Note that this composed
+ // operation takes ownership of the message and destroys
+ // it when it is done.
+ //
beast::http::message msg;
data(
@@ -50,19 +60,40 @@ class write_msg_op
}
};
+ // `handler_ptr` is a utility which helps to manage a composed
+ // operation's state. It is similar to a shared pointer, but
+ // it uses handler allocation hooks to allocate and free memory,
+ // and it also helps to meet Asio's deallocate-before-invocation
+ // guarantee.
+ //
beast::handler_ptr d_;
public:
+ // Asio can move and copy the handler, we support both
write_msg_op(write_msg_op&&) = default;
write_msg_op(write_msg_op const&) = default;
- template
- write_msg_op(DeducedHandler&& h, AsyncWriteStream& s, Args&&... args)
+ // Constructor
+ //
+ // We take the handler as a template type to
+ // support both const and rvalue references.
+ //
+ template<
+ class DeducedHandler,
+ class... Args>
+ write_msg_op(
+ DeducedHandler&& h,
+ AsyncWriteStream& s,
+ Args&&... args)
: d_(std::forward(h),
s, std::forward(args)...)
{
}
+ // Entry point
+ //
+ // The initiation function calls this to start the operation
+ //
void
operator()()
{
@@ -71,12 +102,22 @@ public:
d.stream, d.msg, std::move(*this));
}
+ // Completion handler
+ //
+ // This gets called when beast::http::async_write completes
+ //
void
operator()(error_code ec)
{
d_.invoke(ec);
}
+ //
+ // These hooks are necessary for Asio
+ //
+ // The meaning is explained in the Beast documentation
+ //
+
friend
void* asio_handler_allocate(
std::size_t size, write_msg_op* op)
diff --git a/example/server-framework/ws_async_port.hpp b/example/server-framework/ws_async_port.hpp
index 71a80044..ff607e4a 100644
--- a/example/server-framework/ws_async_port.hpp
+++ b/example/server-framework/ws_async_port.hpp
@@ -21,13 +21,11 @@ namespace framework {
// This object holds the state of the connection
// including, most importantly, the socket or stream.
//
-// `Stream` is the type of socket or stream used as the
-// transport. Examples include boost::asio::ip::tcp::socket
-// or `ssl_stream`.
//
template
class async_ws_con
{
+ // This function lets us access members of the derived class
Derived&
impl()
{
@@ -201,12 +199,28 @@ 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
+
+ // 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
+
+ // 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>
+
+ // Declare this base last now that everything else got set up first.
+ //
, public async_ws_con
{
public:
+ // Construct the plain connection.
+ //
template
explicit
async_ws_con_plain(
@@ -217,6 +231,10 @@ public:
{
}
+ // Returns the stream.
+ //
+ // The base class calls this to obtain the websocket stream object.
+ //
beast::websocket::stream&
ws()
{
@@ -226,7 +244,7 @@ public:
//------------------------------------------------------------------------------
-/** An synchronous WebSocket @b PortHandler which implements echo.
+/** 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
@@ -272,10 +290,10 @@ public:
{
}
- /** Accept a TCP/IP async_ws_con.
+ /** Accept a TCP/IP connection.
This function is called when the server has accepted an
- incoming async_ws_con.
+ incoming connection.
@param sock The connected socket.
@@ -298,10 +316,10 @@ public:
/** Accept a WebSocket upgrade request.
- This is used to accept a async_ws_con that has already
+ This is used to accept a connection that has already
delivered the handshake.
- @param stream The stream corresponding to the async_ws_con.
+ @param stream The stream corresponding to the connection.
@param ep The remote endpoint.
@@ -310,12 +328,12 @@ public:
template
void
accept(
- socket_type&& stream,
+ socket_type&& sock,
endpoint_type ep,
beast::http::request&& req)
{
std::make_shared(
- std::move(stream),
+ std::move(sock),
"ws_async_port",
log_,
instance_.next_id(),
diff --git a/example/server-framework/ws_sync_port.hpp b/example/server-framework/ws_sync_port.hpp
index 5636a9c3..4d128a80 100644
--- a/example/server-framework/ws_sync_port.hpp
+++ b/example/server-framework/ws_sync_port.hpp
@@ -19,16 +19,20 @@
namespace framework {
-// The connection object holds the state of the connection
-// including, most importantly, the socket or stream.
-//
-// `Stream` is the type of socket or stream used as the
-// transport. Examples include boost::asio::ip::tcp::socket
-// or `ssl_stream`.
-//
+/** A synchronous WebSocket connection.
+
+ This base class implements a WebSocket connection object
+ using synchronous calls.
+
+ It uses the Curiously Recurring Template pattern (CRTP) where
+ we refer to the derived class in order to access the stream object
+ to use for reading and writing. This lets the same class be used
+ for plain and SSL stream objects.
+*/
template
class sync_ws_con
{
+ // This function lets us access members of the derived class
Derived&
impl()
{
@@ -237,12 +241,28 @@ 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
+
+ // 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
+
+ // 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>
+
+ // Declare this base last now that everything else got set up first.
+ //
, public sync_ws_con
{
public:
+ // Construct the plain connection.
+ //
template
explicit
sync_ws_con_plain(
@@ -253,6 +273,10 @@ public:
{
}
+ // Returns the stream.
+ //
+ // The base class calls this to obtain the websocket stream object.
+ //
beast::websocket::stream&
ws()
{