Beast.HTTP:

New classes are introduced to represent HTTP messages and their
associated bodies. The parser interface is reworked to use CRTP,
error codes, and trait checks.

New classes:

* basic_headers

  Models field/value pairs in a HTTP message.

* message

  Models a HTTP message, body behavior defined by template argument.
  Parsed message carries metadata generated during parsing.

* parser

  Produces parsed messages.

* empty_body, string_body, basic_streambuf_body

  Classes used to represent content bodies in various ways.

New functions:

* read, async_read, write, async_write

  Read and write HTTP messages on a socket.

New concepts:

* Body: Represents the HTTP Content-Body.
* Field: A HTTP header field.
* FieldSequence: A forward sequence of fields.
* Reader: Parses a Body from a stream of bytes.
* Writer: Serializes a Body to buffers.

basic_parser changes:

* add write methods which throw exceptions instead
* error_code passed via parameter instead of return value
* fold private member calls into existing callbacks
* basic_parser uses CRTP instead of virtual members
* add documentation on Derived requirements for CRTP

impl/http-parser changes:

* joyent renamed to nodejs to reflect upstream changes
This commit is contained in:
Vinnie Falco
2016-03-11 06:40:37 -05:00
parent 3461bafaa2
commit 8b62c23ab6
59 changed files with 6438 additions and 2049 deletions

19
examples/Jamfile Normal file
View File

@@ -0,0 +1,19 @@
#
# Copyright (c) 2013-2016 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)
#
import os ;
exe http_crawl :
../beast/http/src/beast_http_nodejs_parser.cpp
http_crawl.cpp
urls_large_data.cpp
;
exe http_server :
../beast/http/src/beast_http_nodejs_parser.cpp
http_server.cpp
;

94
examples/file_body.h Normal file
View File

@@ -0,0 +1,94 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
#include <beast/http/message.h>
#include <beast/http/resume_context.h>
#include <boost/asio/buffer.hpp>
#include <boost/filesystem.hpp>
#include <cstdio>
#include <cstdint>
namespace beast {
namespace http {
struct file_body
{
using value_type = std::string;
class writer
{
std::size_t size_;
std::size_t offset_ = 0;
std::string const& path_;
FILE* file_ = nullptr;
char buf_[4096];
std::size_t buf_len_;
public:
static bool constexpr is_single_pass = false;
template<bool isRequest, class Headers>
writer(message<isRequest, file_body, Headers> const& m) noexcept
: path_(m.body)
{
}
~writer()
{
if(file_)
fclose(file_);
}
void
init(error_code& ec) noexcept
{
file_ = fopen(path_.c_str(), "rb");
if(! file_)
ec = boost::system::errc::make_error_code(
static_cast<boost::system::errc::errc_t>(errno));
else
size_ = boost::filesystem::file_size(path_);
}
auto
content_length() const
{
return size_;
}
template<class Write>
boost::tribool
operator()(resume_context&&, error_code&, Write&& write)
{
buf_len_ = std::min(size_ - offset_, sizeof(buf_));
fread(buf_, 1, sizeof(buf_), file_);
offset_ += buf_len_;
write(boost::asio::buffer(buf_, buf_len_));
return offset_ >= size_;
}
};
};
} // http
} // beast
#endif

View File

@@ -0,0 +1,205 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#include "file_body.h"
#include "http_stream.h"
#include <beast/asio/placeholders.h>
#include <boost/asio.hpp>
#include <cstdio>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
namespace beast {
namespace http {
class http_async_server
{
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using req_type = request<string_body>;
using resp_type = response<file_body>;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::string root_;
std::vector<std::thread> thread_;
public:
http_async_server(endpoint_type const& ep,
int threads, std::string const& root)
: sock_(ios_)
, acceptor_(ios_)
, root_(root)
{
acceptor_.open(ep.protocol());
acceptor_.bind(ep);
acceptor_.listen(
boost::asio::socket_base::max_connections);
acceptor_.async_accept(sock_,
std::bind(&http_async_server::on_accept, this,
beast::asio::placeholders::error));
thread_.reserve(threads);
for(int i = 0; i < threads; ++i)
thread_.emplace_back(
[&] { ios_.run(); });
}
~http_async_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
for(auto& t : thread_)
t.join();
}
private:
class peer : public std::enable_shared_from_this<peer>
{
int id_;
stream<socket_type> stream_;
boost::asio::io_service::strand strand_;
std::string root_;
req_type req_;
public:
peer(peer&&) = default;
peer(peer const&) = default;
peer& operator=(peer&&) = delete;
peer& operator=(peer const&) = delete;
explicit
peer(socket_type&& sock, std::string const& root)
: id_([]
{
static int n = 0;
return ++n;
}())
, stream_(std::move(sock))
, strand_(stream_.get_io_service())
, root_(root)
{
}
void run()
{
do_read();
}
void do_read()
{
stream_.async_read(req_, strand_.wrap(
std::bind(&peer::on_read, shared_from_this(),
asio::placeholders::error)));
}
void on_read(error_code ec)
{
if(ec)
return fail(ec, "read");
do_read();
auto path = req_.url;
if(path == "/")
path = "/index.html";
path = root_ + path;
if(! boost::filesystem::exists(path))
{
response<string_body> resp(
{404, "Not Found", req_.version});
resp.headers.replace("Server", "http_async_server");
resp.body = "The file '" + path + "' was not found";
stream_.async_write(std::move(resp),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
return;
}
response<file_body> resp(
{200, "OK", req_.version});
resp.headers.replace("Server", "http_async_server");
resp.headers.replace("Content-Type", "text/html");
resp.body = path;
stream_.async_write(std::move(resp),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
}
void on_write(error_code ec)
{
if(ec)
fail(ec, "write");
}
private:
void
fail(error_code ec, std::string what)
{
if(ec != boost::asio::error::operation_aborted)
{
std::cerr <<
"#" << std::to_string(id_) << " " <<
what << ": " << ec.message() << std::endl;
}
}
};
void
fail(error_code ec, std::string what)
{
std::cerr <<
what << ": " << ec.message() << std::endl;
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
maybe_throw(ec, "accept");
socket_type sock(std::move(sock_));
acceptor_.async_accept(sock_,
std::bind(&http_async_server::on_accept, this,
asio::placeholders::error));
std::make_shared<peer>(std::move(sock), root_)->run();
}
};
} // http
} // beast
#endif

67
examples/http_crawl.cpp Normal file
View File

@@ -0,0 +1,67 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include "http_stream.h"
#include "urls_large_data.h"
#include <boost/asio.hpp>
#include <iostream>
using namespace beast::http;
using namespace boost::asio;
template<class String>
void
err(error_code const& ec, String const& what)
{
std::cerr << what << ": " << ec.message() << std::endl;
}
int main(int, char const*[])
{
io_service ios;
for(auto const& host : urls_large_data())
{
try
{
ip::tcp::resolver r(ios);
auto it = r.resolve(
ip::tcp::resolver::query{host, "http"});
stream<ip::tcp::socket> hs(ios);
connect(hs.lowest_layer(), it);
auto ep = hs.lowest_layer().remote_endpoint();
request<empty_body> req({method_t::http_get, "/", 11});
req.headers.insert("Host", host +
std::string(":") + std::to_string(ep.port()));
req.headers.insert("User-Agent", "beast/http");
hs.write(req);
response<string_body> resp;
hs.read(resp);
std::cout << resp;
}
catch(boost::system::system_error const& ec)
{
std::cerr << host << ": " << ec.what();
}
catch(...)
{
std::cerr << host << ": unknown exception" << std::endl;
}
}
}

81
examples/http_server.cpp Normal file
View File

@@ -0,0 +1,81 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#include "http_async_server.h"
#include "http_sync_server.h"
#include "sig_wait.h"
#include <boost/program_options.hpp>
#include <iostream>
int main(int ac, char const* av[])
{
using namespace beast::http;
namespace po = boost::program_options;
po::options_description desc("Options");
desc.add_options()
("root,r", po::value<std::string>()->implicit_value("."),
"Set the root directory for serving files")
("port,p", po::value<std::uint16_t>()->implicit_value(8080),
"Set the port number for the server")
("ip", po::value<std::string>()->implicit_value("0.0.0.0"),
"Set the IP address to bind to, \"0.0.0.0\" for all")
("threads,n", po::value<std::size_t>()->implicit_value(4),
"Set the number of threads to use")
("sync,s", "Launch a synchronous server")
;
po::variables_map vm;
po::store(po::parse_command_line(ac, av, desc), vm);
std::string root = ".";
if(vm.count("root"))
root = vm["root"].as<std::string>();
std::uint16_t port = 8080;
if(vm.count("port"))
port = vm["port"].as<std::uint16_t>();
std::string ip = "0.0.0.0";
if(vm.count("ip"))
ip = vm["ip"].as<std::string>();
std::size_t threads = 4;
if(vm.count("threads"))
threads = vm["threads"].as<std::size_t>();
bool sync = vm.count("sync") > 0;
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
endpoint_type ep{address_type::from_string(ip), port};
if(sync)
{
http_sync_server server(ep, root);
sig_wait();
}
else
{
http_async_server server(ep, threads, root);
sig_wait();
}
}

View File

@@ -0,0 +1,31 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_EXAMPLE_SERVER_RESPONSE_H_INCLUDED
#define BEAST_HTTP_EXAMPLE_SERVER_RESPONSE_H_INCLUDED
namespace beast {
namespace http {
namespace example {
} // example
} // http
} // beast
#endif

488
examples/http_stream.h Normal file
View File

@@ -0,0 +1,488 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_STREAM_H_INCLUDED
#define BEAST_HTTP_STREAM_H_INCLUDED
#include <beast/http.h>
#include <beast/asio/basic_streambuf.h>
#include <boost/asio/io_service.hpp>
#include <boost/intrusive/list.hpp>
#include <memory>
namespace beast {
namespace http {
namespace detail {
class stream_base
{
protected:
struct op
: boost::intrusive::list_base_hook<
boost::intrusive::link_mode<
boost::intrusive::normal_link>>
{
virtual ~op() = default;
virtual void operator()() = 0;
virtual void cancel() = 0;
};
using op_list = typename boost::intrusive::make_list<
op, boost::intrusive::constant_time_size<false>>::type;
op_list wr_q_;
bool wr_active_ = false;
};
} // detail
/** Provides message-oriented functionality using HTTP.
The stream class template provides asynchronous and blocking
message-oriented functionality necessary for clients and servers
to utilize the HTTP protocol.
@par Thread Safety
@e Distinct @e objects: Safe.@n
@e Shared @e objects: Unsafe. The application must ensure that
all asynchronous operations are performed within the same
implicit or explicit strand.
@par Example
To use the class template with an `ip::tcp::socket`, you would write:
@code
http::stream<ip::tcp::socket> hs(io_service);
@endcode
Alternatively, you can write:
@code
ip::tcp::socket sock(io_service);
http::stream<ip::tcp::socket&> hs(sock);
@endcode
@note A stream object must not be destroyed while there are
pending asynchronous operations associated with it.
@par Concepts
AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
*/
template<class NextLayer,
class Allocator = std::allocator<char>>
class stream : public detail::stream_base
{
NextLayer next_layer_;
basic_streambuf<Allocator> rd_buf_;
public:
/// The type of the next layer.
using next_layer_type =
std::remove_reference_t<NextLayer>;
/// The type of the lowest layer.
using lowest_layer_type =
typename next_layer_type::lowest_layer_type;
/// The type of endpoint of the lowest layer.
using endpoint_type =
typename lowest_layer_type::endpoint_type;
/// The protocol of the next layer.
using protocol_type =
typename lowest_layer_type::protocol_type;
/// The type of resolver of the next layer.
using resolver_type =
typename protocol_type::resolver;
/** Destructor.
@note A stream object must not be destroyed while there
are pending asynchronous operations associated with it.
*/
~stream();
/** Move constructor.
Undefined behavior if operations are active or pending.
*/
stream(stream&&) = default;
/** Move assignment.
Undefined behavior if operations are active or pending.
*/
stream& operator=(stream&&) = default;
/** Construct a HTTP stream.
This constructor creates a HTTP stream and initialises
the next layer.
@throws Any exceptions thrown by the Stream constructor.
@param args The arguments to be passed to initialise the
next layer. The arguments are forwarded to the next layer's
constructor.
*/
template<class... Args>
explicit
stream(Args&&... args);
/** Get the io_service associated with the stream.
This function may be used to obtain the io_service object
that the stream uses to dispatch handlers for asynchronous
operations.
@return A reference to the io_service object that the stream
will use to dispatch handlers. Ownership is not transferred
to the caller.
*/
boost::asio::io_service&
get_io_service()
{
return next_layer_.lowest_layer().get_io_service();
}
/** Get a reference to the next layer.
This function returns a reference to the next layer
in a stack of stream layers.
@return A reference to the next layer in the stack of
stream layers. Ownership is not transferred to the caller.
*/
next_layer_type&
next_layer()
{
return next_layer_;
}
/** Get a reference to the next layer.
This function returns a reference to the next layer in a
stack of stream layers.
@return A reference to the next layer in the stack of
stream layers. Ownership is not transferred to the caller.
*/
next_layer_type const&
next_layer() const
{
return next_layer_;
}
/** Get a reference to the lowest layer.
This function returns a reference to the lowest layer
in a stack of stream layers.
@return A reference to the lowest layer in the stack of
stream layers. Ownership is not transferred to the caller.
*/
lowest_layer_type&
lowest_layer()
{
return next_layer_.lowest_layer();
}
/** Get a reference to the lowest layer.
This function returns a reference to the lowest layer
in a stack of stream layers.
@return A reference to the lowest layer in the stack of
stream layers. Ownership is not transferred to the caller.
*/
lowest_layer_type const&
lowest_layer() const
{
return next_layer_.lowest_layer();
}
/** Cancel pending operations.
This will cancel all of the asynchronous operations pending,
including pipelined writes that have not been started. Handlers for
canceled writes will be called with
`boost::asio::error::operation_aborted`.
@throws boost::system::system_error Thrown on failure.
*/
void
cancel()
{
error_code ec;
cancel(ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Cancel pending operations.
This will cancel all of the asynchronous operations pending,
including pipelined writes that have not been started. Handlers for
canceled writes will be called with
`boost::asio::error::operation_aborted`.
@param ec Set to indicate what error occurred, if any.
*/
void
cancel(error_code& ec);
/** Read a HTTP message from the stream.
This function is used to read a single HTTP message from the stream.
The call will block until one of the followign conditions is true:
@li A message has been read.
@li An error occurred.
The operation is implemented in terms of zero or more calls to the
next layer's `read_some` function.
@param msg An object used to store the message. The previous
contents of the object will be overwritten.
@throws boost::system::system_error Thrown on failure.
*/
template<bool isRequest, class Body, class Headers>
void
read(message<isRequest, Body, Headers>& msg)
{
error_code ec;
read(msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Read a HTTP message from the stream.
This function is used to read a single HTTP message from the stream.
The call will block until one of the followign conditions is true:
@li A message has been read.
@li An error occurred.
The operation is implemented in terms of zero or more calls to the
next layer's `read_some` function.
@param msg An object used to store the message. The previous
contents of the object will be overwritten.
@param ec Set to indicate what error occurred, if any.
*/
template<bool isRequest, class Body, class Headers>
void
read(message<isRequest, Body, Headers>& msg,
error_code& ec);
/** Start reading a HTTP message from the stream asynchronously.
This function is used to asynchronously read a single HTTP message
from the stream. The function call always returns immediately. The
asynchronous operation will continue until one of the following
conditions is true:
@li The message has been written.
@li An error occurred.
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 composed
operation. The program must ensure that the stream performs no other
read operations or any other composed operations that perform reads
until this operation completes.
@param msg An object used to store the message. The previous
contents of the object will be overwritten. Ownership of the message
is not transferred; the caller must guarantee that the object remains
valid until the handler is called.
@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 // 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<bool isRequest, class Body, class Headers,
class ReadHandler>
#if GENERATING_DOCS
void_or_deduced
#else
auto
#endif
async_read(message<isRequest, Body, Headers>& msg,
ReadHandler&& handler);
/** Write a HTTP message to the stream.
This function is used to write a single HTTP message to the
stream. The call will block until one of the following conditions
is true:
@li The entire message is sent.
@li An error occurred.
If the semantics of the message require that the connection is
closed to indicate the end of the content body,
`boost::asio::error::eof` is thrown after the message is sent.
successfuly. The caller is responsible for actually closing the
connection. For regular TCP/IP streams this means shutting down the
send side, while SSL streams may call the SSL shutdown function.
@param msg The message to send.
@throws boost::system::system_error Thrown on failure.
*/
template<bool isRequest, class Body, class Headers>
void
write(message<isRequest, Body, Headers> const& msg)
{
error_code ec;
write(msg, ec);
if(ec)
throw boost::system::system_error{ec};
}
/** Write a HTTP message to the stream.
This function is used to write a single HTTP message to the
stream. The call will block until one of the following conditions
is true:
@li The entire message is sent.
@li An error occurred.
If the semantics of the message require that the connection is
closed to indicate the end of the content body,
`boost::asio::error::eof` is returned after the message is sent.
successfuly. The caller is responsible for actually closing the
connection. For regular TCP/IP streams this means shutting down the
send side, while SSL streams may call the SSL shutdown function.
@param msg The message to send.
@param ec Set to the error, if any occurred.
*/
template<bool isRequest, class Body, class Headers>
void
write(message<isRequest, Body, Headers> const& msg,
error_code& ec);
/** Start pipelining a HTTP message to the stream asynchronously.
This function is used to queue a message to be sent on the stream.
Unlike the free function, this version will place the message on an
outgoing message queue if there is already a write pending.
If the semantics of the message require that the connection is
closed to indicate the end of the content body, the handler
is called with the error `boost::asio::error::eof` after the message
has been sent successfully. The caller is responsible for actually
closing the connection. For regular TCP/IP streams this means
shutting down the send side, while SSL streams may call the SSL
`async_shutdown` function.
@param msg The message to send. A copy of the message will be made.
@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 // 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<bool isRequest, class Body, class Headers,
class WriteHandler>
#if GENERATING_DOCS
void_or_deduced
#else
auto
#endif
async_write(message<isRequest, Body, Headers> const& msg,
WriteHandler&& handler);
/** Start pipelining a HTTP message to the stream asynchronously.
This function is used to queue a message to be sent on the stream.
Unlike the free function, this version will place the message on an
outgoing message queue if there is already a write pending.
If the semantics of the message require that the connection is
closed to indicate the end of the content body, the handler
is called with the error boost::asio::error::eof. The caller is
responsible for actually closing the connection. For regular
TCP/IP streams this means shutting down the send side, while SSL
streams may call the SSL async_shutdown function.
@param msg The message to send. Ownership of the message, which
must be movable, is transferred to the implementation. The message
will not be destroyed until the asynchronous operation completes.
@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 // 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<bool isRequest, class Body, class Headers,
class WriteHandler>
#if GENERATING_DOCS
void_or_deduced
#else
auto
#endif
async_write(message<isRequest, Body, Headers>&& msg,
WriteHandler&& handler);
private:
template<bool, class, class, class> class read_op;
template<bool, class, class, class> class write_op;
void
cancel_all();
};
} // http
} // beast
#include "http_stream.ipp"
#endif

419
examples/http_stream.ipp Normal file
View File

@@ -0,0 +1,419 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_HTTP_STREAM_IPP_INCLUDED
#define BEAST_HTTP_STREAM_IPP_INCLUDED
#include <beast/asio/async_completion.h>
#include <beast/asio/bind_handler.h>
#include <beast/asio/handler_alloc.h>
#include <beast/http/read.h>
#include <beast/http/type_check.h>
#include <beast/http/write.h>
#include <cassert>
namespace beast {
namespace http {
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class Handler>
class stream<NextLayer, Allocator>::read_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
stream<NextLayer>& s;
message<isRequest, Body, Headers>& m;
Handler h;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& s_,
message<isRequest, Body, Headers>& m_)
: s(s_)
, m(m_)
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
read_op(read_op&&) = default;
read_op(read_op const&) = default;
template<class DeducedHandler, class... Args>
read_op(DeducedHandler&& h,
stream<NextLayer>& s, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), s,
std::forward<Args>(args)...))
{
(*this)(error_code{}, false);
}
void operator()(error_code const& ec, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, read_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, read_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(read_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, read_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers, class Handler>
void
stream<NextLayer, Allocator>::
read_op<isRequest, Body, Headers, Handler>::
operator()(error_code const& ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
d.state = 99;
beast::http::async_read(d.s.next_layer_,
d.s.rd_buf_, d.m, std::move(*this));
return;
}
}
d.h(ec);
}
//------------------------------------------------------------------------------
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class Handler>
class stream<NextLayer, Allocator>::write_op : public op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
stream<NextLayer>& s;
message<isRequest, Body, Headers> m;
Handler h;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& s_,
message<isRequest, Body, Headers> const& m_,
bool cont_)
: s(s_)
, m(m_)
, h(std::forward<DeducedHandler>(h_))
, cont(cont_)
{
}
template<class DeducedHandler>
data(DeducedHandler&& h_, stream<NextLayer>& s_,
message<isRequest, Body, Headers>&& m_,
bool cont_)
: s(s_)
, m(std::move(m_))
, h(std::forward<DeducedHandler>(h_))
, cont(cont_)
{
}
};
std::shared_ptr<data> d_;
public:
write_op(write_op&&) = default;
write_op(write_op const&) = default;
template<class DeducedHandler, class... Args>
write_op(DeducedHandler&& h,
stream<NextLayer>& s, Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), s,
std::forward<Args>(args)...))
{
}
void
operator()() override
{
(*this)(error_code{}, false);
}
void cancel() override;
void operator()(error_code const& ec, bool again = true);
friend
auto asio_handler_allocate(
std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
auto asio_handler_deallocate(
void* p, std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
auto asio_handler_is_continuation(write_op* op)
{
return op->d_->cont;
}
template <class Function>
friend
auto asio_handler_invoke(Function&& f, write_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers, class Handler>
void
stream<NextLayer, Allocator>::
write_op<isRequest, Body, Headers, Handler>::
cancel()
{
auto& d = *d_;
d.s.get_io_service().post(
bind_handler(std::move(*this),
boost::asio::error::operation_aborted));
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers, class Handler>
void
stream<NextLayer, Allocator>::
write_op<isRequest, Body, Headers, Handler>::
operator()(error_code const& ec, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
d.state = 99;
beast::http::async_write(d.s.next_layer_,
d.m, std::move(*this));
return;
}
}
d.h(ec);
if(! d.s.wr_q_.empty())
{
auto& op = d.s.wr_q_.front();
op();
// VFALCO Use allocator
delete &op;
d.s.wr_q_.pop_front();
}
else
{
d.s.wr_active_ = false;
}
}
//------------------------------------------------------------------------------
template<class NextLayer, class Allocator>
stream<NextLayer, Allocator>::
~stream()
{
// Can't destroy with pending operations!
assert(wr_q_.empty());
}
template<class NextLayer, class Allocator>
template<class... Args>
stream<NextLayer, Allocator>::
stream(Args&&... args)
: next_layer_(std::forward<Args>(args)...)
{
}
template<class NextLayer, class Allocator>
void
stream<NextLayer, Allocator>::
cancel(error_code& ec)
{
cancel_all();
lowest_layer().cancel(ec);
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers>
void
stream<NextLayer, Allocator>::
read(message<isRequest, Body, Headers>& msg,
error_code& ec)
{
beast::http::read(next_layer_, rd_buf_, msg, ec);
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class ReadHandler>
auto
stream<NextLayer, Allocator>::
async_read(message<isRequest, Body, Headers>& msg,
ReadHandler&& handler)
{
async_completion<
ReadHandler, void(error_code)
> completion(handler);
read_op<isRequest, Body, Headers,
decltype(completion.handler)>{
completion.handler, *this, msg};
return completion.result.get();
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers>
void
stream<NextLayer, Allocator>::
write(message<isRequest, Body, Headers> const& msg,
error_code& ec)
{
beast::http::write(next_layer_, msg, ec);
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class WriteHandler>
auto
stream<NextLayer, Allocator>::
async_write(message<isRequest, Body, Headers> const& msg,
WriteHandler&& handler)
{
async_completion<
WriteHandler, void(error_code)> completion(handler);
auto const cont = wr_active_ ||
boost_asio_handler_cont_helpers::is_continuation(handler);
if(! wr_active_)
{
wr_active_ = true;
write_op<isRequest, Body, Headers,
decltype(completion.handler)>{
completion.handler, *this, msg, cont }();
}
else
{
// VFALCO Use allocator
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
decltype(completion.handler)>(
completion.handler, *this, msg, cont));
}
return completion.result.get();
}
template<class NextLayer, class Allocator>
template<bool isRequest, class Body, class Headers,
class WriteHandler>
auto
stream<NextLayer, Allocator>::
async_write(message<isRequest, Body, Headers>&& msg,
WriteHandler&& handler)
{
async_completion<
WriteHandler, void(error_code)> completion(handler);
auto const cont = wr_active_ ||
boost_asio_handler_cont_helpers::is_continuation(handler);
if(! wr_active_)
{
wr_active_ = true;
write_op<isRequest, Body, Headers,
decltype(completion.handler)>{completion.handler,
*this, std::move(msg), cont}();
}
else
{
// VFALCO Use allocator
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
decltype(completion.handler)>(completion.handler,
*this, std::move(msg), cont));
}
return completion.result.get();
}
template<class NextLayer, class Allocator>
void
stream<NextLayer, Allocator>::
cancel_all()
{
for(auto it = wr_q_.begin(); it != wr_q_.end();)
{
auto& op = *it++;
op.cancel();
// VFALCO Use allocator
delete &op;
}
wr_q_.clear();
}
} // http
} // beast
#endif

170
examples/http_sync_server.h Normal file
View File

@@ -0,0 +1,170 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
#include "file_body.h"
#include "http_stream.h"
#include <beast/unit_test/suite.h>
#include <boost/asio.hpp>
#include <cstdint>
#include <cstdio>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <utility>
#include <iostream>
namespace beast {
namespace http {
class http_sync_server
{
using endpoint_type = boost::asio::ip::tcp::endpoint;
using address_type = boost::asio::ip::address;
using socket_type = boost::asio::ip::tcp::socket;
using req_type = request<string_body>;
using resp_type = response<file_body>;
boost::asio::io_service ios_;
socket_type sock_;
boost::asio::ip::tcp::acceptor acceptor_;
std::string root_;
std::thread thread_;
public:
http_sync_server(endpoint_type const& ep,
std::string const& root)
: sock_(ios_)
, acceptor_(ios_)
, root_(root)
{
acceptor_.open(ep.protocol());
acceptor_.bind(ep);
acceptor_.listen(
boost::asio::socket_base::max_connections);
acceptor_.async_accept(sock_,
std::bind(&http_sync_server::on_accept, this,
beast::asio::placeholders::error));
thread_ = std::thread{[&]{ ios_.run(); }};
}
~http_sync_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
thread_.join();
}
void
fail(error_code ec, std::string what)
{
std::cerr <<
what << ": " << ec.message() << std::endl;
}
void
maybe_throw(error_code ec, std::string what)
{
if(ec)
{
fail(ec, what);
throw ec;
}
}
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
maybe_throw(ec, "accept");
static int id_ = 0;
std::thread{
[
id = ++id_,
this,
sock = std::move(sock_),
work = boost::asio::io_service::work{ios_}
]() mutable
{
do_peer(id, std::move(sock));
}}.detach();
acceptor_.async_accept(sock_,
std::bind(&http_sync_server::on_accept, this,
asio::placeholders::error));
}
void
fail(int id, error_code const& ec)
{
if(ec != boost::asio::error::operation_aborted &&
ec != boost::asio::error::eof)
std::cerr <<
"#" << std::to_string(id) << " " << std::endl;
}
void
do_peer(int id, socket_type&& sock)
{
http::stream<socket_type> hs(std::move(sock));
error_code ec;
for(;;)
{
req_type req;
hs.read(req, ec);
if(ec)
break;
auto path = req.url;
if(path == "/")
path = "/index.html";
path = root_ + path;
if(! boost::filesystem::exists(path))
{
response<string_body> resp(
{404, "Not Found", req.version});
resp.headers.replace("Server", "http_sync_server");
resp.body = "The file '" + path + "' was not found";
hs.write(resp, ec);
if(ec)
break;
}
response<file_body> resp(
{200, "OK", req.version});
resp.headers.replace("Server", "http_sync_server");
resp.headers.replace("Content-Type", "text/html");
resp.body = path;
hs.write(resp, ec);
if(ec)
break;
}
fail(id, ec);
}
};
} // http
} // beast
#endif

49
examples/sig_wait.h Normal file
View File

@@ -0,0 +1,49 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED
#define BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED
#include <boost/asio.hpp>
#include <condition_variable>
#include <mutex>
// Block until SIGINT or SIGTERM
inline
void
sig_wait()
{
boost::asio::io_service ios;
boost::asio::signal_set signals(
ios, SIGINT, SIGTERM);
std::mutex m;
bool stop = false;
std::condition_variable cv;
signals.async_wait(
[&](boost::system::error_code const&, int)
{
std::lock_guard<std::mutex> lock(m);
stop = true;
cv.notify_one();
});
std::unique_lock<std::mutex> lock(m);
cv.wait(lock, [&]{ return stop; });
}
#endif

10031
examples/urls_large_data.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,28 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef URLS_LARGE_DATA_H_INCLUDED
#define URLS_LARGE_DATA_H_INCLUDED
#include <vector>
std::vector<char const*> const&
urls_large_data();
#endif