Files
boost_beast/examples/http_async_server.hpp

329 lines
9.0 KiB
C++
Raw Normal View History

//
// 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)
//
2017-07-20 08:01:46 -07:00
#ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
#include "file_body.hpp"
#include "mime_type.hpp"
2017-07-20 08:01:46 -07:00
#include <beast/http.hpp>
#include <beast/core/placeholders.hpp>
#include <beast/core/streambuf.hpp>
2017-07-20 08:01:46 -07:00
#include <boost/asio.hpp>
#include <cstddef>
2017-07-20 08:01:46 -07:00
#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>;
2017-07-20 08:01:46 -07:00
std::mutex m_;
bool log_ = true;
2017-07-20 08:01:46 -07:00
boost::asio::io_service ios_;
boost::asio::ip::tcp::acceptor acceptor_;
socket_type sock_;
2017-07-20 08:01:46 -07:00
std::string root_;
std::vector<std::thread> thread_;
public:
http_async_server(endpoint_type const& ep,
std::size_t threads, std::string const& root)
: acceptor_(ios_)
, sock_(ios_)
2017-07-20 08:01:46 -07:00
, 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(std::size_t i = 0; i < threads; ++i)
2017-07-20 08:01:46 -07:00
thread_.emplace_back(
[&] { ios_.run(); });
}
~http_async_server()
{
error_code ec;
ios_.dispatch(
[&]{ acceptor_.close(ec); });
for(auto& t : thread_)
t.join();
}
template<class... Args>
void
log(Args const&... args)
{
if(log_)
{
std::lock_guard<std::mutex> lock(m_);
log_args(args...);
}
}
2017-07-20 08:01:46 -07:00
private:
template<class Stream, class Handler,
bool isRequest, class Body, class Fields>
class write_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
Stream& s;
message<isRequest, Body, Fields> m;
Handler h;
bool cont;
template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_,
message<isRequest, Body, Fields>&& m_)
: s(s_)
, m(std::move(m_))
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
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& 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 ec, bool again = true)
{
auto& d = *d_;
d.cont = d.cont || again;
if(! again)
{
beast::http::async_write(d.s, d.m, std::move(*this));
return;
}
d.h(ec);
}
friend
void* asio_handler_allocate(
std::size_t size, write_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
void 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
bool asio_handler_is_continuation(write_op* op)
{
return op->d_->cont;
}
2016-08-26 08:01:44 -04:00
template<class Function>
friend
void asio_handler_invoke(Function&& f, write_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class Stream,
bool isRequest, class Body, class Fields,
class DeducedHandler>
static
void
async_write(Stream& stream, message<
isRequest, Body, Fields>&& msg,
DeducedHandler&& handler)
{
write_op<Stream, typename std::decay<DeducedHandler>::type,
isRequest, Body, Fields>{std::forward<DeducedHandler>(
handler), stream, std::move(msg)};
}
2017-07-20 08:01:46 -07:00
class peer : public std::enable_shared_from_this<peer>
{
int id_;
streambuf sb_;
socket_type sock_;
http_async_server& server_;
2017-07-20 08:01:46 -07:00
boost::asio::io_service::strand strand_;
req_type req_;
public:
peer(peer&&) = default;
peer(peer const&) = default;
peer& operator=(peer&&) = delete;
peer& operator=(peer const&) = delete;
peer(socket_type&& sock, http_async_server& server)
: sock_(std::move(sock))
, server_(server)
, strand_(sock_.get_io_service())
2017-07-20 08:01:46 -07:00
{
static int n = 0;
id_ = ++n;
}
void
fail(error_code ec, std::string what)
{
if(ec != boost::asio::error::operation_aborted)
server_.log("#", id_, " ", what, ": ", ec.message(), "\n");
}
2017-07-20 08:01:46 -07:00
void run()
{
do_read();
}
void do_read()
{
async_read(sock_, sb_, req_, strand_.wrap(
2017-07-20 08:01:46 -07:00
std::bind(&peer::on_read, shared_from_this(),
asio::placeholders::error)));
}
void on_read(error_code const& ec)
2017-07-20 08:01:46 -07:00
{
if(ec)
return fail(ec, "read");
auto path = req_.url;
if(path == "/")
path = "/index.html";
path = server_.root_ + path;
2017-07-20 08:01:46 -07:00
if(! boost::filesystem::exists(path))
{
response<string_body> res;
res.status = 404;
res.reason = "Not Found";
res.version = req_.version;
res.fields.insert("Server", "http_async_server");
res.fields.insert("Content-Type", "text/html");
res.body = "The file '" + path + "' was not found";
prepare(res);
async_write(sock_, std::move(res),
2017-07-20 08:01:46 -07:00
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
return;
}
try
{
2016-09-25 09:14:27 -04:00
resp_type res;
res.status = 200;
res.reason = "OK";
res.version = req_.version;
res.fields.insert("Server", "http_async_server");
res.fields.insert("Content-Type", mime_type(path));
2016-09-25 09:14:27 -04:00
res.body = path;
prepare(res);
2016-09-25 09:14:27 -04:00
async_write(sock_, std::move(res),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
}
catch(std::exception const& e)
{
response<string_body> res;
res.status = 500;
res.reason = "Internal Error";
res.version = req_.version;
res.fields.insert("Server", "http_async_server");
res.fields.insert("Content-Type", "text/html");
res.body =
std::string{"An internal error occurred"} + e.what();
prepare(res);
2016-09-25 09:14:27 -04:00
async_write(sock_, std::move(res),
std::bind(&peer::on_write, shared_from_this(),
asio::placeholders::error));
}
2017-07-20 08:01:46 -07:00
}
void on_write(error_code ec)
{
if(ec)
fail(ec, "write");
do_read();
2017-07-20 08:01:46 -07:00
}
};
void
log_args()
2017-07-20 08:01:46 -07:00
{
}
template<class Arg, class... Args>
2017-07-20 08:01:46 -07:00
void
log_args(Arg const& arg, Args const&... args)
2017-07-20 08:01:46 -07:00
{
std::cerr << arg;
log_args(args...);
}
void
fail(error_code ec, std::string what)
{
log(what, ": ", ec.message(), "\n");
2017-07-20 08:01:46 -07:00
}
void
on_accept(error_code ec)
{
if(! acceptor_.is_open())
return;
if(ec)
return fail(ec, "accept");
2017-07-20 08:01:46 -07:00
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), *this)->run();
2017-07-20 08:01:46 -07:00
}
};
} // http
} // beast
#endif