2016-05-07 17:06:46 -04:00
|
|
|
//
|
|
|
|
|
// 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
|
|
|
|
|
|
2016-04-29 06:04:40 -04:00
|
|
|
#include "file_body.hpp"
|
2016-06-20 10:48:53 -04:00
|
|
|
#include "mime_type.hpp"
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2016-06-20 10:48:53 -04:00
|
|
|
#include <beast/http.hpp>
|
2017-01-05 09:07:18 -05:00
|
|
|
#include <beast/core/handler_helpers.hpp>
|
2017-01-02 13:29:48 -05:00
|
|
|
#include <beast/core/handler_ptr.hpp>
|
2016-05-07 14:57:15 -04:00
|
|
|
#include <beast/core/placeholders.hpp>
|
2016-06-20 10:48:53 -04:00
|
|
|
#include <beast/core/streambuf.hpp>
|
2017-07-20 08:01:46 -07:00
|
|
|
#include <boost/asio.hpp>
|
2016-08-26 07:54:20 -04:00
|
|
|
#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;
|
|
|
|
|
|
2016-10-09 06:34:35 -04:00
|
|
|
using req_type = request<string_body>;
|
|
|
|
|
using resp_type = response<file_body>;
|
2017-07-20 08:01:46 -07:00
|
|
|
|
2016-06-20 10:48:53 -04: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_;
|
2016-06-20 10:48:53 -04:00
|
|
|
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,
|
2016-08-26 07:54:20 -04:00
|
|
|
std::size_t threads, std::string const& root)
|
2016-06-20 10:48:53 -04:00
|
|
|
: 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);
|
2016-08-26 07:54:20 -04:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-20 10:48:53 -04:00
|
|
|
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:
|
2016-06-20 10:48:53 -04:00
|
|
|
template<class Stream, class Handler,
|
2016-11-10 05:34:49 -05:00
|
|
|
bool isRequest, class Body, class Fields>
|
2016-06-20 10:48:53 -04:00
|
|
|
class write_op
|
|
|
|
|
{
|
|
|
|
|
struct data
|
|
|
|
|
{
|
2017-01-02 13:29:48 -05:00
|
|
|
bool cont;
|
2016-06-20 10:48:53 -04:00
|
|
|
Stream& s;
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields> m;
|
2016-06-20 10:48:53 -04:00
|
|
|
|
2017-01-02 13:29:48 -05:00
|
|
|
data(Handler& handler, Stream& s_,
|
2016-11-10 05:34:49 -05:00
|
|
|
message<isRequest, Body, Fields>&& m_)
|
2017-01-05 09:07:18 -05:00
|
|
|
: cont(beast_asio_helpers::
|
2017-01-02 13:29:48 -05:00
|
|
|
is_continuation(handler))
|
|
|
|
|
, s(s_)
|
2016-06-20 10:48:53 -04:00
|
|
|
, m(std::move(m_))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2017-01-02 13:29:48 -05:00
|
|
|
handler_ptr<data, Handler> d_;
|
2016-06-20 10:48:53 -04:00
|
|
|
|
|
|
|
|
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)
|
2017-01-29 19:46:17 -05:00
|
|
|
: d_(std::forward<DeducedHandler>(h),
|
|
|
|
|
s, std::forward<Args>(args)...)
|
2016-06-20 10:48:53 -04:00
|
|
|
{
|
|
|
|
|
(*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;
|
|
|
|
|
}
|
2017-01-02 13:29:48 -05:00
|
|
|
d_.invoke(ec);
|
2016-06-20 10:48:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend
|
|
|
|
|
void* asio_handler_allocate(
|
|
|
|
|
std::size_t size, write_op* op)
|
|
|
|
|
{
|
2017-01-05 09:07:18 -05:00
|
|
|
return beast_asio_helpers::
|
2017-01-02 13:29:48 -05:00
|
|
|
allocate(size, op->d_.handler());
|
2016-06-20 10:48:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend
|
|
|
|
|
void asio_handler_deallocate(
|
|
|
|
|
void* p, std::size_t size, write_op* op)
|
|
|
|
|
{
|
2017-01-05 09:07:18 -05:00
|
|
|
return beast_asio_helpers::
|
2017-01-02 13:29:48 -05:00
|
|
|
deallocate(p, size, op->d_.handler());
|
2016-06-20 10:48:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
friend
|
|
|
|
|
bool asio_handler_is_continuation(write_op* op)
|
|
|
|
|
{
|
|
|
|
|
return op->d_->cont;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-26 08:01:44 -04:00
|
|
|
template<class Function>
|
2016-06-20 10:48:53 -04:00
|
|
|
friend
|
|
|
|
|
void asio_handler_invoke(Function&& f, write_op* op)
|
|
|
|
|
{
|
2017-01-05 09:07:18 -05:00
|
|
|
return beast_asio_helpers::
|
2017-01-02 13:29:48 -05:00
|
|
|
invoke(f, op->d_.handler());
|
2016-06-20 10:48:53 -04:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template<class Stream,
|
2016-11-10 05:34:49 -05:00
|
|
|
bool isRequest, class Body, class Fields,
|
2016-06-20 10:48:53 -04:00
|
|
|
class DeducedHandler>
|
|
|
|
|
static
|
|
|
|
|
void
|
2016-10-09 06:34:35 -04:00
|
|
|
async_write(Stream& stream, message<
|
2016-11-10 05:34:49 -05:00
|
|
|
isRequest, Body, Fields>&& msg,
|
2016-06-20 10:48:53 -04:00
|
|
|
DeducedHandler&& handler)
|
|
|
|
|
{
|
|
|
|
|
write_op<Stream, typename std::decay<DeducedHandler>::type,
|
2016-11-10 05:34:49 -05:00
|
|
|
isRequest, Body, Fields>{std::forward<DeducedHandler>(
|
2016-06-20 10:48:53 -04:00
|
|
|
handler), stream, std::move(msg)};
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-20 08:01:46 -07:00
|
|
|
class peer : public std::enable_shared_from_this<peer>
|
|
|
|
|
{
|
|
|
|
|
int id_;
|
2016-06-20 10:48:53 -04:00
|
|
|
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;
|
|
|
|
|
|
2016-06-20 10:48:53 -04:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-20 10:48:53 -04:00
|
|
|
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()
|
|
|
|
|
{
|
2016-06-20 10:48:53 -04:00
|
|
|
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)));
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-20 10:48:53 -04:00
|
|
|
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";
|
2016-06-20 10:48:53 -04:00
|
|
|
path = server_.root_ + path;
|
2017-07-20 08:01:46 -07:00
|
|
|
if(! boost::filesystem::exists(path))
|
|
|
|
|
{
|
2016-10-09 06:34:35 -04:00
|
|
|
response<string_body> res;
|
2016-06-20 10:48:53 -04:00
|
|
|
res.status = 404;
|
|
|
|
|
res.reason = "Not Found";
|
|
|
|
|
res.version = req_.version;
|
2016-11-10 05:34:49 -05:00
|
|
|
res.fields.insert("Server", "http_async_server");
|
|
|
|
|
res.fields.insert("Content-Type", "text/html");
|
2016-06-20 10:48:53 -04:00
|
|
|
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;
|
|
|
|
|
}
|
2016-06-20 10:48:53 -04:00
|
|
|
try
|
|
|
|
|
{
|
2016-09-25 09:14:27 -04:00
|
|
|
resp_type res;
|
|
|
|
|
res.status = 200;
|
|
|
|
|
res.reason = "OK";
|
|
|
|
|
res.version = req_.version;
|
2016-11-10 05:34:49 -05:00
|
|
|
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;
|
2016-06-20 10:48:53 -04:00
|
|
|
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));
|
2016-06-20 10:48:53 -04:00
|
|
|
}
|
|
|
|
|
catch(std::exception const& e)
|
|
|
|
|
{
|
2016-10-09 06:34:35 -04:00
|
|
|
response<string_body> res;
|
2016-06-20 10:48:53 -04:00
|
|
|
res.status = 500;
|
|
|
|
|
res.reason = "Internal Error";
|
|
|
|
|
res.version = req_.version;
|
2016-11-10 05:34:49 -05:00
|
|
|
res.fields.insert("Server", "http_async_server");
|
|
|
|
|
res.fields.insert("Content-Type", "text/html");
|
2016-06-20 10:48:53 -04:00
|
|
|
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));
|
2016-06-20 10:48:53 -04:00
|
|
|
}
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void on_write(error_code ec)
|
|
|
|
|
{
|
|
|
|
|
if(ec)
|
|
|
|
|
fail(ec, "write");
|
2016-06-20 10:48:53 -04:00
|
|
|
do_read();
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void
|
2016-06-20 10:48:53 -04:00
|
|
|
log_args()
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-20 10:48:53 -04:00
|
|
|
template<class Arg, class... Args>
|
2017-07-20 08:01:46 -07:00
|
|
|
void
|
2016-06-20 10:48:53 -04:00
|
|
|
log_args(Arg const& arg, Args const&... args)
|
2017-07-20 08:01:46 -07:00
|
|
|
{
|
2016-06-20 10:48:53 -04: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;
|
2016-06-20 10:48:53 -04:00
|
|
|
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));
|
2016-06-20 10:48:53 -04:00
|
|
|
std::make_shared<peer>(std::move(sock), *this)->run();
|
2017-07-20 08:01:46 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} // http
|
|
|
|
|
} // beast
|
|
|
|
|
|
|
|
|
|
#endif
|