Skip to content

Instantly share code, notes, and snippets.

@zxmarcos
Created September 2, 2016 23:33
Show Gist options
  • Save zxmarcos/ca3eed8d33014dcfaf531a5c1cc2dfd3 to your computer and use it in GitHub Desktop.
Save zxmarcos/ca3eed8d33014dcfaf531a5c1cc2dfd3 to your computer and use it in GitHub Desktop.
#include <boost/asio/spawn.hpp>
#include <boost/http/buffered_socket.hpp>
#include <boost/http/algorithm/query.hpp>
#include <boost/http/file_server.hpp>
#include <spdlog/spdlog.h>
namespace http_server
{
namespace asio = boost::asio;
namespace http = boost::http;
using tcp = asio::ip::tcp;
static std::shared_ptr<spdlog::logger> logger;
class HttpConnection;
class HttpConnection : public std::enable_shared_from_this<HttpConnection>
{
http::buffered_socket socket_;
int counter_;
std::string method_;
std::string path_;
http::message message_;
HttpConnection(asio::io_service &ios, int counter)
: socket_(ios)
, counter_(counter)
{
}
public:
void operator()(asio::yield_context yield);
tcp::socket &tcp_layer()
{
return socket_.next_layer();
}
static std::shared_ptr<HttpConnection> MakeConnection(asio::io_service &ios, int counter)
{
return std::shared_ptr<HttpConnection>{ new HttpConnection(ios, counter) };
}
}
;
void HttpConnection::operator()(asio::yield_context yield)
{
auto self = shared_from_this();
try
{
while (socket_.is_open())
{
// Lê uma requisição HTTP
socket_.async_read_request(method_, path_, message_, yield);
message_.body().clear();
// Envia a messagem 100-continue, para pedir o corpo da mensagem.
if (http::request_continue_required(message_))
{
socket_.async_write_response_continue(yield);
}
while (socket_.read_state() != http::read_state::empty)
{
switch (socket_.read_state())
{
case http::read_state::message_ready:
message_.body().clear();
socket_.async_read_some(message_, yield);
break;
case http::read_state::body_ready:
socket_.async_read_trailers(message_, yield);
break;
default:
;
}
}
logger->info("Método: {} uri: {}", method_, path_);
auto body = std::string(std::begin(message_.body()), std::end(message_.body()));
logger->info(body);
http::message reply;
http::async_response_transmit_file(socket_, message_, reply, "/root/test.html", yield);
logger->info("Arquivo transmitido com sucesso!");
}
}
catch (boost::system::system_error &e)
{
if (e.code() == boost::system::error_code{ asio::error::eof })
{
logger->info("{} conexão fechada, EOF", counter_);
}
else
{
logger->error("{} exceção recebida {}", counter_, e.what());
}
}
catch (std::exception &e)
{
logger->error("{} exceção recebida {}", counter_, e.what());
}
}
class HttpServer
{
boost::asio::io_service ios_;
public:
void Start();
};
void HttpServer::Start()
{
if (!logger)
{
logger = spdlog::stdout_logger_mt("HTTP", true);
}
logger->info("Iniciando servidor HTTP");
// Cria um acceptor na porta 8080
tcp::acceptor acceptor(ios_, tcp::endpoint(tcp::v4(), 8080));
auto do_accept = [&](asio::yield_context yield)
{
int counter = 0;
while (true)
{
// Contador de conexões recebidas...
++counter;
try
{
auto connection = HttpConnection::MakeConnection(ios_, counter);
// Aceita uma nova conexão assincronamente.
acceptor.async_accept(connection->tcp_layer(), yield);
logger->info("Nova conexão aceita de {}", connection->tcp_layer().remote_endpoint().address().to_string());
// Controla as mensagens nesta conexão que acabamos de aceitar.
auto handle_connection = [connection](asio::yield_context yield) mutable
{
(*connection)(yield);
};
spawn(ios_, handle_connection);
}
catch (std::exception &e)
{
logger->critical("Abortando, exceção recebida: {}", e.what());
break;
}
}
};
spawn(ios_, do_accept);
ios_.run();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment