forked from boostorg/beast
Group common example headers
This commit is contained in:
@@ -3,9 +3,15 @@
|
||||
GroupSources(include/beast beast)
|
||||
|
||||
GroupSources(example/server-framework "/")
|
||||
GroupSources(example/common "common")
|
||||
|
||||
file(GLOB_RECURSE SERVER_INCLUDES
|
||||
${PROJECT_SOURCE_DIR}/example/server-framework/*.hpp
|
||||
)
|
||||
|
||||
add_executable (server-framework
|
||||
${BEAST_INCLUDES}
|
||||
${COMMON_INCLUDES}
|
||||
${SERVER_INCLUDES}
|
||||
main.cpp
|
||||
)
|
||||
|
||||
@@ -1,481 +0,0 @@
|
||||
//
|
||||
// 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_DETECT_SSL_HPP
|
||||
#define BEAST_EXAMPLE_SERVER_DETECT_SSL_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/config.hpp>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//
|
||||
// Example: Detect TLS/SSL
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//[example_core_detect_ssl_1
|
||||
|
||||
#include <beast.hpp>
|
||||
#include <boost/logic/tribool.hpp>
|
||||
|
||||
/** Return `true` if a buffer contains a TLS/SSL client handshake.
|
||||
|
||||
This function returns `true` if the beginning of the buffer
|
||||
indicates that a TLS handshake is being negotiated, and that
|
||||
there are at least four octets in the buffer.
|
||||
|
||||
If the content of the buffer cannot possibly be a TLS handshake
|
||||
request, the function returns `false`. Otherwise, if additional
|
||||
octets are required, `boost::indeterminate` is returned.
|
||||
|
||||
@param buffer The input buffer to inspect. This type must meet
|
||||
the requirements of @b ConstBufferSequence.
|
||||
|
||||
@return `boost::tribool` indicating whether the buffer contains
|
||||
a TLS client handshake, does not contain a handshake, or needs
|
||||
additional octets.
|
||||
|
||||
@see
|
||||
|
||||
http://www.ietf.org/rfc/rfc2246.txt
|
||||
7.4. Handshake protocol
|
||||
*/
|
||||
template<class ConstBufferSequence>
|
||||
boost::tribool
|
||||
is_ssl_handshake(ConstBufferSequence const& buffers);
|
||||
|
||||
//]
|
||||
|
||||
using namespace beast;
|
||||
|
||||
//[example_core_detect_ssl_2
|
||||
|
||||
template<
|
||||
class ConstBufferSequence>
|
||||
boost::tribool
|
||||
is_ssl_handshake(
|
||||
ConstBufferSequence const& buffers)
|
||||
{
|
||||
// Make sure buffers meets the requirements
|
||||
static_assert(is_const_buffer_sequence<ConstBufferSequence>::value,
|
||||
"ConstBufferSequence requirements not met");
|
||||
|
||||
// We need at least one byte to really do anything
|
||||
if(boost::asio::buffer_size(buffers) < 1)
|
||||
return boost::indeterminate;
|
||||
|
||||
// Extract the first byte, which holds the
|
||||
// "message" type for the Handshake protocol.
|
||||
unsigned char v;
|
||||
boost::asio::buffer_copy(boost::asio::buffer(&v, 1), buffers);
|
||||
|
||||
// Check that the message type is "SSL Handshake" (rfc2246)
|
||||
if(v != 0x16)
|
||||
{
|
||||
// This is definitely not a handshake
|
||||
return false;
|
||||
}
|
||||
|
||||
// At least four bytes are needed for the handshake
|
||||
// so make sure that we get them before returning `true`
|
||||
if(boost::asio::buffer_size(buffers) < 4)
|
||||
return boost::indeterminate;
|
||||
|
||||
// This can only be a TLS/SSL handshake
|
||||
return true;
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_core_detect_ssl_3
|
||||
|
||||
/** Detect a TLS/SSL handshake on a stream.
|
||||
|
||||
This function reads from a stream to determine if a TLS/SSL
|
||||
handshake is being received. The function call will block
|
||||
until one of the following conditions is true:
|
||||
|
||||
@li The disposition of the handshake is determined
|
||||
|
||||
@li An error occurs
|
||||
|
||||
Octets read from the stream will be stored in the passed dynamic
|
||||
buffer, which may be used to perform the TLS handshake if the
|
||||
detector returns true, or otherwise consumed by the caller based
|
||||
on the expected protocol.
|
||||
|
||||
@param stream The stream to read from. This type must meet the
|
||||
requirements of @b SyncReadStream.
|
||||
|
||||
@param buffer The dynamic buffer to use. This type must meet the
|
||||
requirements of @b DynamicBuffer.
|
||||
|
||||
@param ec Set to the error if any occurred.
|
||||
|
||||
@return `boost::tribool` indicating whether the buffer contains
|
||||
a TLS client handshake, does not contain a handshake, or needs
|
||||
additional octets. If an error occurs, the return value is
|
||||
undefined.
|
||||
*/
|
||||
template<
|
||||
class SyncReadStream,
|
||||
class DynamicBuffer>
|
||||
boost::tribool
|
||||
detect_ssl(
|
||||
SyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
error_code& ec)
|
||||
{
|
||||
// Make sure arguments meet the requirements
|
||||
static_assert(is_sync_read_stream<SyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
|
||||
// Loop until an error occurs or we get a definitive answer
|
||||
for(;;)
|
||||
{
|
||||
// There could already be data in the buffer
|
||||
// so we do this first, before reading from the stream.
|
||||
auto const result = is_ssl_handshake(buffer.data());
|
||||
|
||||
// If we got an answer, return it
|
||||
if(! boost::indeterminate(result))
|
||||
{
|
||||
ec = {}; // indicate no error
|
||||
return result;
|
||||
}
|
||||
|
||||
// The algorithm should never need more than 4 bytes
|
||||
BOOST_ASSERT(buffer.size() < 4);
|
||||
|
||||
// Create up to 4 bytes of space in the buffer's output area.
|
||||
auto const mutable_buffer = buffer.prepare(4 - buffer.size());
|
||||
|
||||
// Try to fill our buffer by reading from the stream
|
||||
std::size_t const bytes_transferred = stream.read_some(mutable_buffer, ec);
|
||||
|
||||
// Check for an error
|
||||
if(ec)
|
||||
break;
|
||||
|
||||
// Commit what we read into the buffer's input area.
|
||||
buffer.commit(bytes_transferred);
|
||||
}
|
||||
|
||||
// error
|
||||
return false;
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_core_detect_ssl_4
|
||||
|
||||
/** Detect a TLS/SSL handshake asynchronously on a stream.
|
||||
|
||||
This function is used to asynchronously determine if a TLS/SSL
|
||||
handshake is being received.
|
||||
The function call always returns immediately. The asynchronous
|
||||
operation will continue until one of the following conditions
|
||||
is true:
|
||||
|
||||
@li The disposition of the handshake is determined
|
||||
|
||||
@li An error occurs
|
||||
|
||||
This operation is implemented in terms of zero or more calls to
|
||||
the next layer's `async_read_some` function, and is known as a
|
||||
<em>composed operation</em>. The program must ensure that the
|
||||
stream performs no other operations until this operation completes.
|
||||
|
||||
Octets read from the stream will be stored in the passed dynamic
|
||||
buffer, which may be used to perform the TLS handshake if the
|
||||
detector returns true, or otherwise consumed by the caller based
|
||||
on the expected protocol.
|
||||
|
||||
@param stream The stream to read from. This type must meet the
|
||||
requirements of @b AsyncReadStream.
|
||||
|
||||
@param buffer The dynamic buffer to use. This type must meet the
|
||||
requirements of @b DynamicBuffer.
|
||||
|
||||
@param handler The handler to be called when the request
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code
|
||||
void handler(
|
||||
error_code const& error, // Set to the error, if any
|
||||
boost::tribool result // The result of the detector
|
||||
);
|
||||
@endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
class CompletionToken>
|
||||
async_return_type< /*< The [link beast.ref.beast__async_return_type `async_return_type`] customizes the return value based on the completion token >*/
|
||||
CompletionToken,
|
||||
void(error_code, boost::tribool)> /*< This is the signature for the completion handler >*/
|
||||
async_detect_ssl(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
CompletionToken&& token);
|
||||
|
||||
//]
|
||||
|
||||
//[example_core_detect_ssl_5
|
||||
|
||||
// This is the composed operation.
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
class Handler>
|
||||
class detect_ssl_op;
|
||||
|
||||
// Here is the implementation of the asynchronous initation function
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
class CompletionToken>
|
||||
async_return_type<
|
||||
CompletionToken,
|
||||
void(error_code, boost::tribool)>
|
||||
async_detect_ssl(
|
||||
AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer,
|
||||
CompletionToken&& token)
|
||||
{
|
||||
// Make sure arguments meet the requirements
|
||||
static_assert(is_async_read_stream<AsyncReadStream>::value,
|
||||
"SyncReadStream requirements not met");
|
||||
static_assert(is_dynamic_buffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
|
||||
// This helper manages some of the handler's lifetime and
|
||||
// uses the result and handler specializations associated with
|
||||
// the completion token to help customize the return value.
|
||||
//
|
||||
beast::async_completion<
|
||||
CompletionToken, void(beast::error_code, boost::tribool)> init{token};
|
||||
|
||||
// Create the composed operation and launch it. This is a constructor
|
||||
// call followed by invocation of operator(). We use handler_type
|
||||
// to convert the completion token into the correct handler type,
|
||||
// allowing user defined specializations of the async result template
|
||||
// to take effect.
|
||||
//
|
||||
detect_ssl_op<AsyncReadStream, DynamicBuffer, handler_type<
|
||||
CompletionToken, void(error_code, boost::tribool)>>{
|
||||
stream, buffer, init.completion_handler}(
|
||||
beast::error_code{}, 0);
|
||||
|
||||
// This hook lets the caller see a return value when appropriate.
|
||||
// For example this might return std::future<error_code, boost::tribool> if
|
||||
// CompletionToken is boost::asio::use_future.
|
||||
//
|
||||
// If a coroutine is used for the token, the return value from
|
||||
// this function will be the `boost::tribool` representing the result.
|
||||
//
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_core_detect_ssl_6
|
||||
|
||||
// Read from a stream to invoke is_tls_handshake asynchronously
|
||||
//
|
||||
template<
|
||||
class AsyncReadStream,
|
||||
class DynamicBuffer,
|
||||
class Handler>
|
||||
class detect_ssl_op
|
||||
{
|
||||
// This composed operation has trivial state,
|
||||
// so it is just kept inside the class and can
|
||||
// be cheaply copied as needed by the implementation.
|
||||
|
||||
// Indicates what step in the operation's state
|
||||
// machine to perform next, starting from zero.
|
||||
int step_ = 0;
|
||||
|
||||
AsyncReadStream& stream_;
|
||||
DynamicBuffer& buffer_;
|
||||
Handler handler_;
|
||||
boost::tribool result_ = false;
|
||||
|
||||
public:
|
||||
// Boost.Asio requires that handlers are CopyConstructible.
|
||||
// The state for this operation is cheap to copy.
|
||||
detect_ssl_op(detect_ssl_op const&) = default;
|
||||
|
||||
// The constructor just keeps references the callers varaibles.
|
||||
//
|
||||
template<class DeducedHandler>
|
||||
detect_ssl_op(AsyncReadStream& stream,
|
||||
DynamicBuffer& buffer, DeducedHandler&& handler)
|
||||
: stream_(stream)
|
||||
, buffer_(buffer)
|
||||
, handler_(std::forward<DeducedHandler>(handler))
|
||||
{
|
||||
}
|
||||
|
||||
// Determines if the next asynchronous operation represents a
|
||||
// continuation of the asynchronous flow of control associated
|
||||
// with the final handler. If we are past step two, it means
|
||||
// we have performed an asynchronous operation therefore any
|
||||
// subsequent operation would represent a continuation.
|
||||
// Otherwise, we propagate the handler's associated value of
|
||||
// is_continuation. Getting this right means the implementation
|
||||
// may schedule the invokation of the invoked functions more
|
||||
// efficiently.
|
||||
//
|
||||
friend bool asio_handler_is_continuation(detect_ssl_op* op)
|
||||
{
|
||||
// This next call is structured to permit argument
|
||||
// dependent lookup to take effect.
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
|
||||
// Always use std::addressof to pass the pointer to the handler,
|
||||
// otherwise an unwanted overload of operator& may be called instead.
|
||||
return op->step_ > 2 ||
|
||||
asio_handler_is_continuation(std::addressof(op->handler_));
|
||||
}
|
||||
|
||||
// Handler hook forwarding. These free functions invoke the hooks
|
||||
// associated with the final completion handler. In effect, they
|
||||
// make the Asio implementation treat our composed operation the
|
||||
// same way it would treat the final completion handler for the
|
||||
// purpose of memory allocation and invocation.
|
||||
//
|
||||
// Our implementation just passes through the call to the hook
|
||||
// associated with the final handler.
|
||||
|
||||
friend void* asio_handler_allocate(std::size_t size, detect_ssl_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(size, std::addressof(op->handler_));
|
||||
}
|
||||
|
||||
friend void asio_handler_deallocate(void* p, std::size_t size, detect_ssl_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
return asio_handler_deallocate(p, size, std::addressof(op->handler_));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend void asio_handler_invoke(Function&& f, detect_ssl_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
return asio_handler_invoke(f, std::addressof(op->handler_));
|
||||
}
|
||||
|
||||
// Our main entry point. This will get called as our
|
||||
// intermediate operations complete. Definition below.
|
||||
//
|
||||
void operator()(beast::error_code ec, std::size_t bytes_transferred);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_core_detect_ssl_7
|
||||
|
||||
// detect_ssl_op is callable with the signature
|
||||
// void(error_code, bytes_transferred),
|
||||
// allowing `*this` to be used as a ReadHandler
|
||||
//
|
||||
template<
|
||||
class AsyncStream,
|
||||
class DynamicBuffer,
|
||||
class Handler>
|
||||
void
|
||||
detect_ssl_op<AsyncStream, DynamicBuffer, Handler>::
|
||||
operator()(beast::error_code ec, std::size_t bytes_transferred)
|
||||
{
|
||||
// Execute the state machine
|
||||
switch(step_)
|
||||
{
|
||||
// Initial state
|
||||
case 0:
|
||||
// See if we can detect the handshake
|
||||
result_ = is_ssl_handshake(buffer_.data());
|
||||
|
||||
// If there's a result, call the handler
|
||||
if(! boost::indeterminate(result_))
|
||||
{
|
||||
// We need to invoke the handler, but the guarantee
|
||||
// is that the handler will not be called before the
|
||||
// call to async_detect_ssl returns, so we must post
|
||||
// the operation to the io_service. The helper function
|
||||
// `bind_handler` lets us bind arguments in a safe way
|
||||
// that preserves the type customization hooks of the
|
||||
// original handler.
|
||||
step_ = 1;
|
||||
return stream_.get_io_service().post(
|
||||
bind_handler(std::move(*this), ec, 0));
|
||||
}
|
||||
|
||||
// The algorithm should never need more than 4 bytes
|
||||
BOOST_ASSERT(buffer_.size() < 4);
|
||||
|
||||
step_ = 2;
|
||||
|
||||
do_read:
|
||||
// We need more bytes, but no more than four total.
|
||||
return stream_.async_read_some(buffer_.prepare(4 - buffer_.size()), std::move(*this));
|
||||
|
||||
case 1:
|
||||
// Call the handler
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Set this so that asio_handler_is_continuation knows that
|
||||
// the next asynchronous operation represents a continuation
|
||||
// of the initial asynchronous operation.
|
||||
step_ = 3;
|
||||
BOOST_FALLTHROUGH;
|
||||
|
||||
case 3:
|
||||
if(ec)
|
||||
{
|
||||
// Deliver the error to the handler
|
||||
result_ = false;
|
||||
|
||||
// We don't need bind_handler here because we were invoked
|
||||
// as a result of an intermediate asynchronous operation.
|
||||
break;
|
||||
}
|
||||
|
||||
// Commit the bytes that we read
|
||||
buffer_.commit(bytes_transferred);
|
||||
|
||||
// See if we can detect the handshake
|
||||
result_ = is_ssl_handshake(buffer_.data());
|
||||
|
||||
// If it is detected, call the handler
|
||||
if(! boost::indeterminate(result_))
|
||||
{
|
||||
// We don't need bind_handler here because we were invoked
|
||||
// as a result of an intermediate asynchronous operation.
|
||||
break;
|
||||
}
|
||||
|
||||
// Read some more
|
||||
goto do_read;
|
||||
}
|
||||
|
||||
// Invoke the final handler.
|
||||
handler_(ec, result_);
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
#endif
|
||||
@@ -1,423 +0,0 @@
|
||||
//
|
||||
// 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_HTTP_SERVER_FILE_BODY_HPP
|
||||
#define BEAST_EXAMPLE_HTTP_SERVER_FILE_BODY_HPP
|
||||
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
//[example_http_file_body_1
|
||||
|
||||
/** A message body represented by a file on the filesystem.
|
||||
|
||||
Messages with this type have bodies represented by a
|
||||
file on the file system. When parsing a message using
|
||||
this body type, the data is stored in the file pointed
|
||||
to by the path, which must be writable. When serializing,
|
||||
the implementation will read the file and present those
|
||||
octets as the body content. This may be used to serve
|
||||
content from a directory as part of a web service.
|
||||
*/
|
||||
struct file_body
|
||||
{
|
||||
/** The type of the @ref message::body member.
|
||||
|
||||
Messages declared using `file_body` will have this
|
||||
type for the body member. We use a path indicating
|
||||
the location on the file system for which the data
|
||||
will be read or written.
|
||||
*/
|
||||
using value_type = boost::filesystem::path;
|
||||
|
||||
/** Returns the content length of the body in a message.
|
||||
|
||||
This optional static function returns the size of the
|
||||
body in bytes. It is called from @ref message::size to
|
||||
return the payload size, and from @ref message::prepare
|
||||
to automatically set the Content-Length field. If this
|
||||
function is omitted from a body type, calls to
|
||||
@ref message::prepare will set the chunked transfer
|
||||
encoding.
|
||||
|
||||
@param m The message containing a file body to check.
|
||||
|
||||
@return The size of the file in bytes.
|
||||
*/
|
||||
static
|
||||
std::uint64_t
|
||||
size(value_type const& v);
|
||||
|
||||
/** Algorithm for retrieving buffers when serializing.
|
||||
|
||||
Objects of this type are created during serialization
|
||||
to extract the buffers representing the body.
|
||||
*/
|
||||
class reader;
|
||||
|
||||
/** Algorithm for storing buffers when parsing.
|
||||
|
||||
Objects of this type are created during parsing
|
||||
to store incoming buffers representing the body.
|
||||
*/
|
||||
class writer;
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_2
|
||||
|
||||
inline
|
||||
std::uint64_t
|
||||
file_body::
|
||||
size(value_type const& v)
|
||||
{
|
||||
return boost::filesystem::file_size(v);
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_3
|
||||
|
||||
class file_body::reader
|
||||
{
|
||||
value_type const& path_; // Path of the file
|
||||
FILE* file_ = nullptr; // File handle
|
||||
std::uint64_t remain_ = 0; // The number of unread bytes
|
||||
char buf_[4096]; // Small buffer for reading
|
||||
|
||||
public:
|
||||
// This nested type informs the serializer that it should
|
||||
// wait until after sending the header to initialize the
|
||||
// reader. We set this to true, otherwise opening the file
|
||||
// during `init` could introduce latency which delays
|
||||
// the remote endpoint from receiving the header quickly.
|
||||
//
|
||||
using is_deferred = std::true_type;
|
||||
|
||||
// The type of buffer sequence returned by `get`.
|
||||
//
|
||||
using const_buffers_type =
|
||||
boost::asio::const_buffers_1;
|
||||
|
||||
// Constructor.
|
||||
//
|
||||
// This is called after the header is serialized, because
|
||||
// we declared `is_deferred` to be `std::true_type`.
|
||||
// `m` holds the message we are sending, which will
|
||||
// always have the `file_body` as the body type.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
reader(beast::http::message<isRequest, file_body, Fields> const& m);
|
||||
|
||||
// Destructor
|
||||
~reader();
|
||||
|
||||
// This function is called once before serialization
|
||||
// of the body is started.
|
||||
//
|
||||
void
|
||||
init(beast::error_code& ec);
|
||||
|
||||
// This function is called zero or more times to
|
||||
// retrieve buffers. A return value of `boost::none`
|
||||
// means there are no more buffers. Otherwise,
|
||||
// the contained pair will have the next buffer
|
||||
// to serialize, and a `bool` indicating whether
|
||||
// or not there may be additional buffers.
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
get(beast::error_code& ec);
|
||||
|
||||
// This function is called when reading is complete.
|
||||
// It is an opportunity to perform any final actions
|
||||
// which might fail, in order to return an error code.
|
||||
// Operations that might fail should not be attemped in
|
||||
// destructors, since an exception thrown from there
|
||||
// would terminate the program.
|
||||
void
|
||||
finish(beast::error_code& ec);
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_4
|
||||
|
||||
// Here we just stash a reference to the path for later.
|
||||
// Rather than dealing with messy constructor exceptions,
|
||||
// we save the things that might fail for the call to `init`.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
file_body::reader::
|
||||
reader(beast::http::message<isRequest, file_body, Fields> const& m)
|
||||
: path_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
// This gets called right after construction, and provides
|
||||
// the opportunity to return an error code. The error code
|
||||
// will be propagated to the serializer and eventually
|
||||
// returned to the caller.
|
||||
//
|
||||
inline
|
||||
void
|
||||
file_body::reader::
|
||||
init(beast::error_code& ec)
|
||||
{
|
||||
// Attempt to open the file for reading
|
||||
file_ = fopen(path_.string().c_str(), "rb");
|
||||
|
||||
if(! file_)
|
||||
{
|
||||
// Convert the old-school `errno` into
|
||||
// an error code using the generic category.
|
||||
ec = beast::error_code{errno, beast::generic_category()};
|
||||
return;
|
||||
}
|
||||
|
||||
// The file was opened successfully, get the size
|
||||
// of the file to know how much we need to read.
|
||||
ec = {};
|
||||
remain_ = boost::filesystem::file_size(path_);
|
||||
}
|
||||
|
||||
// This function is called repeatedly by the serializer to
|
||||
// retrieve the buffers representing the body. Our strategy
|
||||
// is to read into our buffer and return it until we have
|
||||
// read through the whole file.
|
||||
//
|
||||
inline
|
||||
auto
|
||||
file_body::reader::
|
||||
get(beast::error_code& ec) ->
|
||||
boost::optional<std::pair<const_buffers_type, bool>>
|
||||
{
|
||||
// Calculate the smaller of our buffer size,
|
||||
// or the amount of unread data in the file.
|
||||
auto const amount = remain_ > sizeof(buf_) ?
|
||||
sizeof(buf_) : static_cast<std::size_t>(remain_);
|
||||
|
||||
// Check for an empty file
|
||||
if(amount == 0)
|
||||
{
|
||||
ec = {};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Now read the next buffer
|
||||
auto const nread = fread(buf_, 1, amount, file_);
|
||||
|
||||
// Handle any errors
|
||||
if(ferror(file_))
|
||||
{
|
||||
// Convert the old-school `errno` into
|
||||
// an error code using the generic category.
|
||||
ec = beast::error_code{errno, beast::generic_category()};
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
// Make sure there is forward progress
|
||||
BOOST_ASSERT(nread != 0);
|
||||
BOOST_ASSERT(nread <= remain_);
|
||||
|
||||
// Update the amount remaining based on what we got
|
||||
remain_ -= nread;
|
||||
|
||||
// Return the buffer to the caller.
|
||||
//
|
||||
// The second element of the pair indicates whether or
|
||||
// not there is more data. As long as there is some
|
||||
// unread bytes, there will be more data. Otherwise,
|
||||
// we set this bool to `false` so we will not be called
|
||||
// again.
|
||||
//
|
||||
ec = {};
|
||||
return {{
|
||||
const_buffers_type{buf_, nread}, // buffer to return.
|
||||
remain_ > 0 // `true` if there are more buffers.
|
||||
}};
|
||||
}
|
||||
|
||||
// Called after reading is done when there's no error.
|
||||
inline
|
||||
void
|
||||
file_body::reader::
|
||||
finish(beast::error_code& ec)
|
||||
{
|
||||
// Functions which pass back errors are
|
||||
// responsible for clearing the error code.
|
||||
ec = {};
|
||||
}
|
||||
|
||||
// The destructor is always invoked if construction succeeds.
|
||||
//
|
||||
inline
|
||||
file_body::reader::
|
||||
~reader()
|
||||
{
|
||||
// Just close the file if its open
|
||||
if(file_)
|
||||
fclose(file_);
|
||||
|
||||
// In theory fclose() can fail but how would we handle it?
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_5
|
||||
|
||||
class file_body::writer
|
||||
{
|
||||
value_type const& path_; // A path to the file
|
||||
FILE* file_ = nullptr; // The file handle
|
||||
|
||||
public:
|
||||
// Constructor.
|
||||
//
|
||||
// This is called after the header is parsed and
|
||||
// indicates that a non-zero sized body may be present.
|
||||
// `m` holds the message we are receiving, which will
|
||||
// always have the `file_body` as the body type.
|
||||
//
|
||||
template<bool isRequest, class Fields>
|
||||
explicit
|
||||
writer(beast::http::message<isRequest, file_body, Fields>& m);
|
||||
|
||||
// This function is called once before parsing
|
||||
// of the body is started.
|
||||
//
|
||||
void
|
||||
init(boost::optional<std::uint64_t> const& content_length, beast::error_code& ec);
|
||||
|
||||
// This function is called one or more times to store
|
||||
// buffer sequences corresponding to the incoming body.
|
||||
//
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
put(ConstBufferSequence const& buffers, beast::error_code& ec);
|
||||
|
||||
// This function is called when writing is complete.
|
||||
// It is an opportunity to perform any final actions
|
||||
// which might fail, in order to return an error code.
|
||||
// Operations that might fail should not be attemped in
|
||||
// destructors, since an exception thrown from there
|
||||
// would terminate the program.
|
||||
//
|
||||
void
|
||||
finish(beast::error_code& ec);
|
||||
|
||||
// Destructor.
|
||||
//
|
||||
// Avoid calling anything that might fail here.
|
||||
//
|
||||
~writer();
|
||||
};
|
||||
|
||||
//]
|
||||
|
||||
//[example_http_file_body_6
|
||||
|
||||
// Just stash a reference to the path so we can open the file later.
|
||||
template<bool isRequest, class Fields>
|
||||
file_body::writer::
|
||||
writer(beast::http::message<isRequest, file_body, Fields>& m)
|
||||
: path_(m.body)
|
||||
{
|
||||
}
|
||||
|
||||
// This gets called once when we know there's a body.
|
||||
// If the content_length is set, it lets us know the exact size
|
||||
// of the body. An implementation could use this to optimize its
|
||||
// storage strategy. For example by attempting to reserve space
|
||||
// ahead of time.
|
||||
//
|
||||
inline
|
||||
void
|
||||
file_body::writer::
|
||||
init(boost::optional<std::uint64_t> const& content_length, beast::error_code& ec)
|
||||
{
|
||||
boost::ignore_unused(content_length);
|
||||
|
||||
// Attempt to open the file for writing
|
||||
file_ = fopen(path_.string().c_str(), "wb");
|
||||
|
||||
if(! file_)
|
||||
{
|
||||
// Convert the old-school `errno` into
|
||||
// an error code using the generic category.
|
||||
ec = beast::error_code{errno, beast::generic_category()};
|
||||
return;
|
||||
}
|
||||
|
||||
// Indicate success
|
||||
ec = {};
|
||||
}
|
||||
|
||||
// This will get called one or more times with body buffers
|
||||
//
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
file_body::writer::
|
||||
put(ConstBufferSequence const& buffers, beast::error_code& ec)
|
||||
{
|
||||
// Loop over all the buffers in the sequence,
|
||||
// and write each one to the file.
|
||||
for(boost::asio::const_buffer buffer : buffers)
|
||||
{
|
||||
// Write this buffer to the file
|
||||
fwrite(
|
||||
boost::asio::buffer_cast<void const*>(buffer), 1,
|
||||
boost::asio::buffer_size(buffer),
|
||||
file_);
|
||||
|
||||
// Handle any errors
|
||||
if(ferror(file_))
|
||||
{
|
||||
// Convert the old-school `errno` into
|
||||
// an error code using the generic category.
|
||||
ec = beast::error_code{errno, beast::generic_category()};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate success
|
||||
ec = {};
|
||||
}
|
||||
|
||||
// Called after writing is done when there's no error.
|
||||
inline
|
||||
void
|
||||
file_body::writer::
|
||||
finish(beast::error_code& ec)
|
||||
{
|
||||
// This has to be cleared before returning, to
|
||||
// indicate no error. The specification requires it.
|
||||
ec = {};
|
||||
}
|
||||
|
||||
// The destructor is always invoked if construction succeeds
|
||||
//
|
||||
inline
|
||||
file_body::writer::
|
||||
~writer()
|
||||
{
|
||||
// Just close the file if its open
|
||||
if(file_)
|
||||
fclose(file_);
|
||||
|
||||
// In theory fclose() can fail but how would we handle it?
|
||||
}
|
||||
|
||||
//]
|
||||
|
||||
#endif
|
||||
@@ -8,8 +8,9 @@
|
||||
#ifndef BEAST_EXAMPLE_SERVER_FILE_SERVICE_HPP
|
||||
#define BEAST_EXAMPLE_SERVER_FILE_SERVICE_HPP
|
||||
|
||||
#include "file_body.hpp"
|
||||
#include "framework.hpp"
|
||||
#include "../common/file_body.hpp"
|
||||
#include "../common/mime_types.hpp"
|
||||
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/http/empty_body.hpp>
|
||||
@@ -182,37 +183,6 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
// Return a reasonable mime type based on the extension of a file.
|
||||
//
|
||||
beast::string_view
|
||||
mime_type(boost::filesystem::path const& path) const
|
||||
{
|
||||
using beast::iequals;
|
||||
auto const ext = path.extension().string();
|
||||
if(iequals(ext, ".txt")) return "text/plain";
|
||||
if(iequals(ext, ".htm")) return "text/html";
|
||||
if(iequals(ext, ".html")) return "text/html";
|
||||
if(iequals(ext, ".php")) return "text/html";
|
||||
if(iequals(ext, ".css")) return "text/css";
|
||||
if(iequals(ext, ".js")) return "application/javascript";
|
||||
if(iequals(ext, ".json")) return "application/json";
|
||||
if(iequals(ext, ".xml")) return "application/xml";
|
||||
if(iequals(ext, ".swf")) return "application/x-shockwave-flash";
|
||||
if(iequals(ext, ".flv")) return "video/x-flv";
|
||||
if(iequals(ext, ".png")) return "image/png";
|
||||
if(iequals(ext, ".jpe")) return "image/jpeg";
|
||||
if(iequals(ext, ".jpeg")) return "image/jpeg";
|
||||
if(iequals(ext, ".jpg")) return "image/jpeg";
|
||||
if(iequals(ext, ".gif")) return "image/gif";
|
||||
if(iequals(ext, ".bmp")) return "image/bmp";
|
||||
if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon";
|
||||
if(iequals(ext, ".tiff")) return "image/tiff";
|
||||
if(iequals(ext, ".tif")) return "image/tiff";
|
||||
if(iequals(ext, ".svg")) return "image/svg+xml";
|
||||
if(iequals(ext, ".svgz")) return "image/svg+xml";
|
||||
return "application/text";
|
||||
}
|
||||
|
||||
// Return an HTTP Not Found response
|
||||
//
|
||||
template<class Body, class Fields>
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
#include "server.hpp"
|
||||
|
||||
#include "http_base.hpp"
|
||||
#include "rfc7231.hpp"
|
||||
#include "service_list.hpp"
|
||||
#include "write_msg.hpp"
|
||||
|
||||
#include "../common/rfc7231.hpp"
|
||||
#include "../common/write_msg.hpp"
|
||||
|
||||
#include <beast/core/flat_buffer.hpp>
|
||||
#include <beast/http/dynamic_body.hpp>
|
||||
|
||||
@@ -11,9 +11,10 @@
|
||||
#include "server.hpp"
|
||||
|
||||
#include "http_base.hpp"
|
||||
#include "rfc7231.hpp"
|
||||
#include "service_list.hpp"
|
||||
#include "write_msg.hpp"
|
||||
|
||||
#include "../common/rfc7231.hpp"
|
||||
#include "../common/write_msg.hpp"
|
||||
|
||||
#include <beast/core/flat_buffer.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
#include "http_sync_port.hpp"
|
||||
#include "http_async_port.hpp"
|
||||
#include "ssl_stream.hpp"
|
||||
|
||||
#include "../common/ssl_stream.hpp"
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@
|
||||
#ifndef BEAST_EXAMPLE_SERVER_MULTI_PORT_HPP
|
||||
#define BEAST_EXAMPLE_SERVER_MULTI_PORT_HPP
|
||||
|
||||
#include "detect_ssl.hpp"
|
||||
#include "ws_async_port.hpp"
|
||||
#include "http_async_port.hpp"
|
||||
#include "https_ports.hpp"
|
||||
#include "wss_ports.hpp"
|
||||
|
||||
#include "../common/detect_ssl.hpp"
|
||||
|
||||
#include <beast/core.hpp>
|
||||
|
||||
#include <boost/function.hpp>
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
//
|
||||
// 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_RFC7231_HPP
|
||||
#define BEAST_EXAMPLE_SERVER_RFC7231_HPP
|
||||
|
||||
#include <beast/core/string.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
|
||||
namespace framework {
|
||||
namespace rfc7231 {
|
||||
|
||||
// This aggregates a collection of algorithms
|
||||
// corresponding to specifications in rfc7231:
|
||||
//
|
||||
// https://tools.ietf.org/html/rfc7231
|
||||
//
|
||||
|
||||
/** Returns `true` if the message specifies Expect: 100-continue
|
||||
|
||||
@param req The request to check
|
||||
|
||||
@see https://tools.ietf.org/html/rfc7231#section-5.1.1
|
||||
*/
|
||||
template<class Body, class Allocator>
|
||||
bool
|
||||
is_expect_100_continue(beast::http::request<
|
||||
Body, beast::http::basic_fields<Allocator>> const& req)
|
||||
{
|
||||
return beast::iequals(
|
||||
req[beast::http::field::expect], "100-continue");
|
||||
}
|
||||
|
||||
} // rfc7231
|
||||
} // framework
|
||||
|
||||
#endif
|
||||
@@ -1,334 +0,0 @@
|
||||
//
|
||||
// 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_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>
|
||||
|
||||
/** C++11 enabled SSL socket wrapper
|
||||
|
||||
This wrapper provides an interface identical to `boost::asio::ssl::stream`,
|
||||
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.
|
||||
using native_handle_type = typename stream_type::native_handle_type;
|
||||
|
||||
/// Structure for use with deprecated impl_type.
|
||||
using impl_struct = typename stream_type::impl_struct;
|
||||
|
||||
/// (Deprecated: Use native_handle_type.) The underlying implementation type.
|
||||
using impl_type = typename stream_type::impl_type;
|
||||
|
||||
/// The type of the next layer.
|
||||
using next_layer_type = typename stream_type::next_layer_type;
|
||||
|
||||
/// The type of the lowest layer.
|
||||
using lowest_layer_type = typename stream_type::lowest_layer_type;
|
||||
|
||||
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&
|
||||
get_io_service()
|
||||
{
|
||||
return p_->get_io_service();
|
||||
}
|
||||
|
||||
native_handle_type
|
||||
native_handle()
|
||||
{
|
||||
return p_->native_handle();
|
||||
}
|
||||
|
||||
impl_type
|
||||
impl()
|
||||
{
|
||||
return p_->impl();
|
||||
}
|
||||
|
||||
next_layer_type const&
|
||||
next_layer() const
|
||||
{
|
||||
return p_->next_layer();
|
||||
}
|
||||
|
||||
next_layer_type&
|
||||
next_layer()
|
||||
{
|
||||
return p_->next_layer();
|
||||
}
|
||||
|
||||
lowest_layer_type&
|
||||
lowest_layer()
|
||||
{
|
||||
return p_->lowest_layer();
|
||||
}
|
||||
|
||||
lowest_layer_type const&
|
||||
lowest_layer() const
|
||||
{
|
||||
return p_->lowest_layer();
|
||||
}
|
||||
|
||||
void
|
||||
set_verify_mode(boost::asio::ssl::verify_mode v)
|
||||
{
|
||||
p_->set_verify_mode(v);
|
||||
}
|
||||
|
||||
boost::system::error_code
|
||||
set_verify_mode(boost::asio::ssl::verify_mode v,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
return p_->set_verify_mode(v, ec);
|
||||
}
|
||||
|
||||
void
|
||||
set_verify_depth(int depth)
|
||||
{
|
||||
p_->set_verify_depth(depth);
|
||||
}
|
||||
|
||||
boost::system::error_code
|
||||
set_verify_depth(
|
||||
int depth, boost::system::error_code& ec)
|
||||
{
|
||||
return p_->set_verify_depth(depth, ec);
|
||||
}
|
||||
|
||||
template<class VerifyCallback>
|
||||
void
|
||||
set_verify_callback(VerifyCallback callback)
|
||||
{
|
||||
p_->set_verify_callback(callback);
|
||||
}
|
||||
|
||||
template<class VerifyCallback>
|
||||
boost::system::error_code
|
||||
set_verify_callback(VerifyCallback callback,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
return p_->set_verify_callback(callback, ec);
|
||||
}
|
||||
|
||||
void
|
||||
handshake(handshake_type type)
|
||||
{
|
||||
p_->handshake(type);
|
||||
}
|
||||
|
||||
boost::system::error_code
|
||||
handshake(handshake_type type,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
return p_->handshake(type, ec);
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
void
|
||||
handshake(
|
||||
handshake_type type, ConstBufferSequence const& buffers)
|
||||
{
|
||||
p_->handshake(type, buffers);
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
boost::system::error_code
|
||||
handshake(handshake_type type,
|
||||
ConstBufferSequence const& buffers,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
return p_->handshake(type, buffers, ec);
|
||||
}
|
||||
|
||||
template<class HandshakeHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(HandshakeHandler,
|
||||
void(boost::system::error_code))
|
||||
async_handshake(handshake_type type,
|
||||
BOOST_ASIO_MOVE_ARG(HandshakeHandler) handler)
|
||||
{
|
||||
return p_->async_handshake(type,
|
||||
BOOST_ASIO_MOVE_CAST(HandshakeHandler)(handler));
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence, class BufferedHandshakeHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(BufferedHandshakeHandler,
|
||||
void (boost::system::error_code, std::size_t))
|
||||
async_handshake(handshake_type type, ConstBufferSequence const& buffers,
|
||||
BOOST_ASIO_MOVE_ARG(BufferedHandshakeHandler) handler)
|
||||
{
|
||||
return p_->async_handshake(type, buffers,
|
||||
BOOST_ASIO_MOVE_CAST(BufferedHandshakeHandler)(handler));
|
||||
}
|
||||
|
||||
void
|
||||
shutdown()
|
||||
{
|
||||
p_->shutdown();
|
||||
}
|
||||
|
||||
boost::system::error_code
|
||||
shutdown(boost::system::error_code& ec)
|
||||
{
|
||||
return p_->shutdown(ec);
|
||||
}
|
||||
|
||||
template<class ShutdownHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ShutdownHandler,
|
||||
void (boost::system::error_code))
|
||||
async_shutdown(BOOST_ASIO_MOVE_ARG(ShutdownHandler) handler)
|
||||
{
|
||||
return p_->async_shutdown(
|
||||
BOOST_ASIO_MOVE_CAST(ShutdownHandler)(handler));
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers)
|
||||
{
|
||||
return p_->write_some(buffers);
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence>
|
||||
std::size_t
|
||||
write_some(ConstBufferSequence const& buffers,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
return p_->write_some(buffers, ec);
|
||||
}
|
||||
|
||||
template<class ConstBufferSequence, class WriteHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(WriteHandler,
|
||||
void (boost::system::error_code, std::size_t))
|
||||
async_write_some(ConstBufferSequence const& buffers,
|
||||
BOOST_ASIO_MOVE_ARG(WriteHandler) handler)
|
||||
{
|
||||
return p_->async_write_some(buffers,
|
||||
BOOST_ASIO_MOVE_CAST(WriteHandler)(handler));
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
read_some(MutableBufferSequence const& buffers)
|
||||
{
|
||||
return p_->read_some(buffers);
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence>
|
||||
std::size_t
|
||||
read_some(MutableBufferSequence const& buffers,
|
||||
boost::system::error_code& ec)
|
||||
{
|
||||
return p_->read_some(buffers, ec);
|
||||
}
|
||||
|
||||
template<class MutableBufferSequence, class ReadHandler>
|
||||
BOOST_ASIO_INITFN_RESULT_TYPE(ReadHandler,
|
||||
void(boost::system::error_code, std::size_t))
|
||||
async_read_some(MutableBufferSequence const& buffers,
|
||||
BOOST_ASIO_MOVE_ARG(ReadHandler) handler)
|
||||
{
|
||||
return p_->async_read_some(buffers,
|
||||
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);
|
||||
};
|
||||
|
||||
// These hooks are used to inform beast::websocket::stream on
|
||||
// how to tear down the connection as part of the WebSocket
|
||||
// protocol specifications
|
||||
|
||||
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
|
||||
@@ -1,230 +0,0 @@
|
||||
//
|
||||
// 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_WRITE_MSG_HPP
|
||||
#define BEAST_EXAMPLE_SERVER_WRITE_MSG_HPP
|
||||
|
||||
#include "server.hpp"
|
||||
|
||||
#include <beast/core/async_result.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/type_traits.hpp>
|
||||
#include <beast/http/message.hpp>
|
||||
#include <beast/http/write.hpp>
|
||||
#include <beast/http/type_traits.hpp>
|
||||
#include <boost/asio/handler_alloc_hook.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
#include <boost/asio/handler_invoke_hook.hpp>
|
||||
|
||||
namespace framework {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/** Composed operation to send an HTTP message
|
||||
|
||||
This implements the composed operation needed for the
|
||||
@ref async_write_msg function.
|
||||
*/
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
class Handler,
|
||||
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<isRequest, Body, Fields> msg;
|
||||
|
||||
data(
|
||||
Handler& handler,
|
||||
AsyncWriteStream& stream_,
|
||||
beast::http::message<isRequest, Body, Fields>&& msg_)
|
||||
: stream(stream_)
|
||||
, msg(std::move(msg_))
|
||||
{
|
||||
boost::ignore_unused(handler);
|
||||
}
|
||||
};
|
||||
|
||||
// `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<data, Handler> 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;
|
||||
|
||||
// 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<DeducedHandler>(h),
|
||||
s, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
// Entry point
|
||||
//
|
||||
// The initiation function calls this to start the operation
|
||||
//
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
auto& d = *d_;
|
||||
beast::http::async_write(
|
||||
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)
|
||||
{
|
||||
using boost::asio::asio_handler_allocate;
|
||||
return asio_handler_allocate(
|
||||
size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
void asio_handler_deallocate(
|
||||
void* p, std::size_t size, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_deallocate;
|
||||
asio_handler_deallocate(
|
||||
p, size, std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
friend
|
||||
bool asio_handler_is_continuation(write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(std::addressof(op->d_.handler()));
|
||||
}
|
||||
|
||||
template<class Function>
|
||||
friend
|
||||
void asio_handler_invoke(Function&& f, write_msg_op* op)
|
||||
{
|
||||
using boost::asio::asio_handler_invoke;
|
||||
asio_handler_invoke(
|
||||
f, std::addressof(op->d_.handler()));
|
||||
}
|
||||
};
|
||||
|
||||
} // detail
|
||||
|
||||
/** Write an HTTP message to a stream asynchronously
|
||||
|
||||
This function is used to write a complete message to a stream asynchronously
|
||||
using HTTP/1. The function call always returns immediately. The asynchronous
|
||||
operation will continue until one of the following conditions is true:
|
||||
|
||||
@li The entire message is written.
|
||||
|
||||
@li An error occurs.
|
||||
|
||||
This operation is implemented in terms of zero or more calls to the stream's
|
||||
`async_write_some` function, and is known as a <em>composed operation</em>.
|
||||
The program must ensure that the stream performs no other write operations
|
||||
until this operation completes. The algorithm will use a temporary
|
||||
@ref serializer with an empty chunk decorator to produce buffers. If
|
||||
the semantics of the message indicate that the connection should be
|
||||
closed after the message is sent, the error delivered by this function
|
||||
will be @ref error::end_of_stream
|
||||
|
||||
@param stream The stream to which the data is to be written.
|
||||
The type must support the @b AsyncWriteStream concept.
|
||||
|
||||
@param msg The message to write. The function will take ownership
|
||||
of the object as if by move constrction.
|
||||
|
||||
@param handler The handler to be called when the operation
|
||||
completes. Copies will be made of the handler as required.
|
||||
The equivalent function signature of the handler must be:
|
||||
@code void handler(
|
||||
error_code const& error // result of operation
|
||||
); @endcode
|
||||
Regardless of whether the asynchronous operation completes
|
||||
immediately or not, the handler will not be invoked from within
|
||||
this function. Invocation of the handler will be performed in a
|
||||
manner equivalent to using `boost::asio::io_service::post`.
|
||||
*/
|
||||
template<
|
||||
class AsyncWriteStream,
|
||||
bool isRequest, class Body, class Fields,
|
||||
class WriteHandler>
|
||||
beast::async_return_type<WriteHandler, void(error_code)>
|
||||
async_write_msg(
|
||||
AsyncWriteStream& stream,
|
||||
beast::http::message<isRequest, Body, Fields>&& msg,
|
||||
WriteHandler&& handler)
|
||||
{
|
||||
static_assert(
|
||||
beast::is_async_write_stream<AsyncWriteStream>::value,
|
||||
"AsyncWriteStream requirements not met");
|
||||
|
||||
static_assert(beast::http::is_body<Body>::value,
|
||||
"Body requirements not met");
|
||||
|
||||
static_assert(beast::http::is_body_reader<Body>::value,
|
||||
"BodyReader requirements not met");
|
||||
|
||||
beast::async_completion<WriteHandler, void(error_code)> init{handler};
|
||||
|
||||
detail::write_msg_op<
|
||||
AsyncWriteStream,
|
||||
beast::handler_type<WriteHandler, void(error_code)>,
|
||||
isRequest, Body, Fields>{
|
||||
init.completion_handler,
|
||||
stream,
|
||||
std::move(msg)}();
|
||||
|
||||
return init.result.get();
|
||||
}
|
||||
|
||||
} // framework
|
||||
|
||||
#endif
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
#include "ws_sync_port.hpp"
|
||||
#include "ws_async_port.hpp"
|
||||
#include "ssl_stream.hpp"
|
||||
|
||||
#include "../common/ssl_stream.hpp"
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
Reference in New Issue
Block a user