Group common example headers

This commit is contained in:
Vinnie Falco
2017-07-20 08:15:29 -07:00
parent 84d7601bdf
commit bb1dd72638
27 changed files with 143 additions and 88 deletions
+6
View File
@@ -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
)
-481
View File
@@ -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
-423
View File
@@ -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
+2 -32
View File
@@ -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>
+3 -2
View File
@@ -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>
+3 -2
View File
@@ -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>
+2 -1
View File
@@ -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>
+2 -1
View File
@@ -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>
-41
View File
@@ -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
-334
View File
@@ -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
-230
View File
@@ -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
+2 -1
View File
@@ -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>