Added support for new asio api

This commit is contained in:
eidheim 2019-04-09 22:38:50 +02:00
commit 5087f5d3df
6 changed files with 142 additions and 79 deletions

91
asio_compatibility.hpp Normal file
View file

@ -0,0 +1,91 @@
#ifndef SIMPLE_WEB_ASIO_HPP
#define SIMPLE_WEB_ASIO_HPP
#include <memory>
#ifdef USE_STANDALONE_ASIO
#include <asio.hpp>
#include <asio/steady_timer.hpp>
namespace SimpleWeb {
using error_code = std::error_code;
using errc = std::errc;
using system_error = std::system_error;
namespace make_error_code = std;
} // namespace SimpleWeb
#else
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
namespace SimpleWeb {
namespace asio = boost::asio;
using error_code = boost::system::error_code;
namespace errc = boost::system::errc;
using system_error = boost::system::system_error;
namespace make_error_code = boost::system::errc;
} // namespace SimpleWeb
#endif
namespace SimpleWeb {
#if(USE_STANDALONE_ASIO && ASIO_VERSION >= 101300) || BOOST_ASIO_VERSION >= 101300 // TODO: change to 101300
using io_context = asio::io_context;
using resolver_results = asio::ip::tcp::resolver::results_type;
using async_connect_endpoint = asio::ip::tcp::endpoint;
inline void restart(io_context &context) noexcept {
context.restart();
}
inline void timer_expires_after(asio::steady_timer &timer, const asio::steady_timer::duration &duration) {
timer.expires_after(duration);
}
inline asio::ip::address make_address(const std::string &str) noexcept {
return asio::ip::make_address(str);
}
template <typename socket_type>
io_context &get_socket_context(socket_type &socket) {
return socket.get_executor().context();
}
template <typename handler_type>
void async_resolve(asio::ip::tcp::resolver &resolver, const std::pair<std::string, std::string> &host_port, handler_type &&handler) {
resolver.async_resolve(host_port.first, host_port.second, handler);
}
template <typename executor_type, typename handler_type>
void post(executor_type &executor, handler_type &&handler) {
asio::post(executor, handler);
}
template <typename executor_type, typename handler_type>
asio::executor_binder<typename asio::decay<handler_type>::type, executor_type> bind_executor(executor_type &executor, handler_type &&handler) {
return asio::bind_executor(executor, handler);
}
#else
using io_context = asio::io_service;
using resolver_results = asio::ip::tcp::resolver::iterator;
using async_connect_endpoint = asio::ip::tcp::resolver::iterator;
inline void restart(io_context &context) noexcept {
context.reset();
}
inline void timer_expires_after(asio::steady_timer &timer, const asio::steady_timer::duration &duration) {
timer.expires_from_now(duration);
}
inline asio::ip::address make_address(const std::string &str) noexcept {
return asio::ip::address::from_string(str);
}
template <typename socket_type>
io_context &get_socket_context(socket_type &socket) {
return socket.get_io_service();
}
template <typename handler_type>
void async_resolve(asio::ip::tcp::resolver &resolver, const std::pair<std::string, std::string> &host_port, handler_type &&handler) {
resolver.async_resolve(asio::ip::tcp::resolver::query(host_port.first, host_port.second), handler);
}
template <typename executor_type, typename handler_type>
void post(executor_type &executor, handler_type &&handler) {
executor.post(handler);
}
template <typename executor_type, typename handler_type>
asio::detail::wrapped_handler<asio::io_service::strand, handler_type, asio::detail::is_continuation_if_running> bind_executor(executor_type &executor, handler_type &&handler) {
return executor.wrap(handler);
}
#endif
} // namespace SimpleWeb
#endif /* SIMPLE_WEB_ASIO_HPP */

View file

@ -1,6 +1,7 @@
#ifndef CLIENT_HTTP_HPP #ifndef CLIENT_HTTP_HPP
#define CLIENT_HTTP_HPP #define CLIENT_HTTP_HPP
#include "asio_compatibility.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <limits> #include <limits>
#include <mutex> #include <mutex>
@ -8,27 +9,6 @@
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
#ifdef USE_STANDALONE_ASIO
#include <asio.hpp>
#include <asio/steady_timer.hpp>
namespace SimpleWeb {
using error_code = std::error_code;
using errc = std::errc;
using system_error = std::system_error;
namespace make_error_code = std;
} // namespace SimpleWeb
#else
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
namespace SimpleWeb {
namespace asio = boost::asio;
using error_code = boost::system::error_code;
namespace errc = boost::system::errc;
using system_error = boost::system::system_error;
namespace make_error_code = boost::system::errc;
} // namespace SimpleWeb
#endif
namespace SimpleWeb { namespace SimpleWeb {
template <class socket_type> template <class socket_type>
class Client; class Client;
@ -119,8 +99,12 @@ namespace SimpleWeb {
timer = nullptr; timer = nullptr;
return; return;
} }
timer = std::unique_ptr<asio::steady_timer>(new asio::steady_timer(socket->get_io_service())); timer = std::unique_ptr<asio::steady_timer>(new asio::steady_timer(get_socket_context(*socket)));
timer->expires_from_now(std::chrono::seconds(seconds)); try {
timer_expires_after(*timer, std::chrono::seconds(seconds));
}
catch(...) {
}
auto self = this->shared_from_this(); auto self = this->shared_from_this();
timer->async_wait([self](const error_code &ec) { timer->async_wait([self](const error_code &ec) {
if(!ec) { if(!ec) {
@ -132,8 +116,11 @@ namespace SimpleWeb {
void cancel_timeout() noexcept { void cancel_timeout() noexcept {
if(timer) { if(timer) {
error_code ec; try {
timer->cancel(ec); timer->cancel();
}
catch(...) {
}
} }
} }
}; };
@ -155,7 +142,7 @@ namespace SimpleWeb {
/// If you have your own asio::io_service, store its pointer here before calling request(). /// If you have your own asio::io_service, store its pointer here before calling request().
/// When using asynchronous requests, running the io_service is up to the programmer. /// When using asynchronous requests, running the io_service is up to the programmer.
std::shared_ptr<asio::io_service> io_service; std::shared_ptr<io_context> io_service;
/// Convenience function to perform synchronous request. The io_service is run within this function. /// Convenience function to perform synchronous request. The io_service is run within this function.
/// If reusing the io_service for other tasks, use the asynchronous request functions instead. /// If reusing the io_service for other tasks, use the asynchronous request functions instead.
@ -178,7 +165,7 @@ namespace SimpleWeb {
std::lock_guard<std::mutex> lock(concurrent_synchronous_requests_mutex); std::lock_guard<std::mutex> lock(concurrent_synchronous_requests_mutex);
--concurrent_synchronous_requests; --concurrent_synchronous_requests;
if(!concurrent_synchronous_requests) if(!concurrent_synchronous_requests)
io_service->reset(); restart(*io_service);
} }
if(ec) if(ec)
@ -208,7 +195,7 @@ namespace SimpleWeb {
std::lock_guard<std::mutex> lock(concurrent_synchronous_requests_mutex); std::lock_guard<std::mutex> lock(concurrent_synchronous_requests_mutex);
--concurrent_synchronous_requests; --concurrent_synchronous_requests;
if(!concurrent_synchronous_requests) if(!concurrent_synchronous_requests)
io_service->reset(); restart(*io_service);
} }
if(ec) if(ec)
@ -362,7 +349,7 @@ namespace SimpleWeb {
unsigned short port; unsigned short port;
unsigned short default_port; unsigned short default_port;
std::unique_ptr<asio::ip::tcp::resolver::query> query; std::unique_ptr<std::pair<std::string, std::string>> host_port;
std::unordered_set<std::shared_ptr<Connection>> connections; std::unordered_set<std::shared_ptr<Connection>> connections;
std::mutex connections_mutex; std::mutex connections_mutex;
@ -383,7 +370,7 @@ namespace SimpleWeb {
std::lock_guard<std::mutex> lock(connections_mutex); std::lock_guard<std::mutex> lock(connections_mutex);
if(!io_service) { if(!io_service) {
io_service = std::make_shared<asio::io_service>(); io_service = std::make_shared<io_context>();
internal_io_service = true; internal_io_service = true;
} }
@ -400,12 +387,12 @@ namespace SimpleWeb {
connection->attempt_reconnect = true; connection->attempt_reconnect = true;
connection->in_use = true; connection->in_use = true;
if(!query) { if(!host_port) {
if(config.proxy_server.empty()) if(config.proxy_server.empty())
query = std::unique_ptr<asio::ip::tcp::resolver::query>(new asio::ip::tcp::resolver::query(host, std::to_string(port))); host_port = std::unique_ptr<std::pair<std::string, std::string>>(new std::pair<std::string, std::string>(host, std::to_string(port)));
else { else {
auto proxy_host_port = parse_host_port(config.proxy_server, 8080); auto proxy_host_port = parse_host_port(config.proxy_server, 8080);
query = std::unique_ptr<asio::ip::tcp::resolver::query>(new asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); host_port = std::unique_ptr<std::pair<std::string, std::string>>(new std::pair<std::string, std::string>(proxy_host_port.first, std::to_string(proxy_host_port.second)));
} }
} }
@ -656,14 +643,14 @@ namespace SimpleWeb {
if(!session->connection->socket->lowest_layer().is_open()) { if(!session->connection->socket->lowest_layer().is_open()) {
auto resolver = std::make_shared<asio::ip::tcp::resolver>(*io_service); auto resolver = std::make_shared<asio::ip::tcp::resolver>(*io_service);
session->connection->set_timeout(config.timeout_connect); session->connection->set_timeout(config.timeout_connect);
resolver->async_resolve(*query, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) { async_resolve(*resolver, *host_port, [this, session, resolver](const error_code &ec, resolver_results results) {
session->connection->cancel_timeout(); session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock(); auto lock = session->connection->handler_runner->continue_lock();
if(!lock) if(!lock)
return; return;
if(!ec) { if(!ec) {
session->connection->set_timeout(config.timeout_connect); session->connection->set_timeout(config.timeout_connect);
asio::async_connect(*session->connection->socket, it, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) { asio::async_connect(*session->connection->socket, results, [this, session, resolver](const error_code &ec, async_connect_endpoint /*endpoint*/) {
session->connection->cancel_timeout(); session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock(); auto lock = session->connection->handler_runner->continue_lock();
if(!lock) if(!lock)

View file

@ -47,13 +47,13 @@ namespace SimpleWeb {
void connect(const std::shared_ptr<Session> &session) override { void connect(const std::shared_ptr<Session> &session) override {
if(!session->connection->socket->lowest_layer().is_open()) { if(!session->connection->socket->lowest_layer().is_open()) {
auto resolver = std::make_shared<asio::ip::tcp::resolver>(*io_service); auto resolver = std::make_shared<asio::ip::tcp::resolver>(*io_service);
resolver->async_resolve(*query, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) { async_resolve(*resolver, *host_port, [this, session, resolver](const error_code &ec, resolver_results results) {
auto lock = session->connection->handler_runner->continue_lock(); auto lock = session->connection->handler_runner->continue_lock();
if(!lock) if(!lock)
return; return;
if(!ec) { if(!ec) {
session->connection->set_timeout(this->config.timeout_connect); session->connection->set_timeout(this->config.timeout_connect);
asio::async_connect(session->connection->socket->lowest_layer(), it, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) { asio::async_connect(session->connection->socket->lowest_layer(), results, [this, session, resolver](const error_code &ec, async_connect_endpoint /*endpoint*/) {
session->connection->cancel_timeout(); session->connection->cancel_timeout();
auto lock = session->connection->handler_runner->continue_lock(); auto lock = session->connection->handler_runner->continue_lock();
if(!lock) if(!lock)

View file

@ -1,6 +1,7 @@
#ifndef SERVER_HTTP_HPP #ifndef SERVER_HTTP_HPP
#define SERVER_HTTP_HPP #define SERVER_HTTP_HPP
#include "asio_compatibility.hpp"
#include "utility.hpp" #include "utility.hpp"
#include <functional> #include <functional>
#include <iostream> #include <iostream>
@ -12,25 +13,6 @@
#include <thread> #include <thread>
#include <unordered_set> #include <unordered_set>
#ifdef USE_STANDALONE_ASIO
#include <asio.hpp>
#include <asio/steady_timer.hpp>
namespace SimpleWeb {
using error_code = std::error_code;
using errc = std::errc;
namespace make_error_code = std;
} // namespace SimpleWeb
#else
#include <boost/asio.hpp>
#include <boost/asio/steady_timer.hpp>
namespace SimpleWeb {
namespace asio = boost::asio;
using error_code = boost::system::error_code;
namespace errc = boost::system::errc;
namespace make_error_code = boost::system::errc;
} // namespace SimpleWeb
#endif
// Late 2017 TODO: remove the following checks and always use std::regex // Late 2017 TODO: remove the following checks and always use std::regex
#ifdef USE_BOOST_REGEX #ifdef USE_BOOST_REGEX
#include <boost/regex.hpp> #include <boost/regex.hpp>
@ -63,10 +45,10 @@ namespace SimpleWeb {
std::shared_ptr<Session> session; std::shared_ptr<Session> session;
long timeout_content; long timeout_content;
asio::io_service::strand strand; io_context::strand strand;
std::list<std::pair<std::shared_ptr<asio::streambuf>, std::function<void(const error_code &)>>> send_queue; std::list<std::pair<std::shared_ptr<asio::streambuf>, std::function<void(const error_code &)>>> send_queue;
Response(std::shared_ptr<Session> session_, long timeout_content) noexcept : std::ostream(nullptr), session(std::move(session_)), timeout_content(timeout_content), strand(session->connection->socket->get_io_service()) { Response(std::shared_ptr<Session> session_, long timeout_content) noexcept : std::ostream(nullptr), session(std::move(session_)), timeout_content(timeout_content), strand(get_socket_context(*session->connection->socket)) {
rdbuf(streambuf.get()); rdbuf(streambuf.get());
} }
@ -90,8 +72,8 @@ namespace SimpleWeb {
void send_from_queue() { void send_from_queue() {
auto self = this->shared_from_this(); auto self = this->shared_from_this();
strand.post([self]() { SimpleWeb::post(strand, [self]() {
asio::async_write(*self->session->connection->socket, *self->send_queue.begin()->first, self->strand.wrap([self](const error_code &ec, std::size_t /*bytes_transferred*/) { asio::async_write(*self->session->connection->socket, *self->send_queue.begin()->first, SimpleWeb::bind_executor(self->strand, [self](const error_code &ec, std::size_t /*bytes_transferred*/) {
auto lock = self->session->connection->handler_runner->continue_lock(); auto lock = self->session->connection->handler_runner->continue_lock();
if(!lock) if(!lock)
return; return;
@ -142,7 +124,7 @@ namespace SimpleWeb {
rdbuf(this->streambuf.get()); rdbuf(this->streambuf.get());
auto self = this->shared_from_this(); auto self = this->shared_from_this();
strand.post([self, streambuf, callback]() { SimpleWeb::post(strand, [self, streambuf, callback]() {
self->send_queue.emplace_back(streambuf, callback); self->send_queue.emplace_back(streambuf, callback);
if(self->send_queue.size() == 1) if(self->send_queue.size() == 1)
self->send_from_queue(); self->send_from_queue();
@ -296,8 +278,12 @@ namespace SimpleWeb {
return; return;
} }
timer = std::unique_ptr<asio::steady_timer>(new asio::steady_timer(socket->get_io_service())); timer = std::unique_ptr<asio::steady_timer>(new asio::steady_timer(get_socket_context(*socket)));
timer->expires_from_now(std::chrono::seconds(seconds)); try {
timer_expires_after(*timer, std::chrono::seconds(seconds));
}
catch(...) {
}
auto self = this->shared_from_this(); auto self = this->shared_from_this();
timer->async_wait([self](const error_code &ec) { timer->async_wait([self](const error_code &ec) {
if(!ec) if(!ec)
@ -307,8 +293,11 @@ namespace SimpleWeb {
void cancel_timeout() noexcept { void cancel_timeout() noexcept {
if(timer) { if(timer) {
error_code ec; try {
timer->cancel(ec); timer->cancel();
}
catch(...) {
}
} }
} }
}; };
@ -380,7 +369,7 @@ namespace SimpleWeb {
std::function<void(std::unique_ptr<socket_type> &, std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade; std::function<void(std::unique_ptr<socket_type> &, std::shared_ptr<typename ServerBase<socket_type>::Request>)> on_upgrade;
/// If you have your own asio::io_service, store its pointer here before running start(). /// If you have your own asio::io_service, store its pointer here before running start().
std::shared_ptr<asio::io_service> io_service; std::shared_ptr<io_context> io_service;
/// If you know the server port in advance, use start() instead. /// If you know the server port in advance, use start() instead.
/// Returns assigned port. If io_service is not set, an internal io_service is created instead. /// Returns assigned port. If io_service is not set, an internal io_service is created instead.
@ -388,12 +377,12 @@ namespace SimpleWeb {
unsigned short bind() { unsigned short bind() {
asio::ip::tcp::endpoint endpoint; asio::ip::tcp::endpoint endpoint;
if(config.address.size() > 0) if(config.address.size() > 0)
endpoint = asio::ip::tcp::endpoint(asio::ip::address::from_string(config.address), config.port); endpoint = asio::ip::tcp::endpoint(make_address(config.address), config.port);
else else
endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v6(), config.port); endpoint = asio::ip::tcp::endpoint(asio::ip::tcp::v6(), config.port);
if(!io_service) { if(!io_service) {
io_service = std::make_shared<asio::io_service>(); io_service = std::make_shared<io_context>();
internal_io_service = true; internal_io_service = true;
} }
@ -424,7 +413,7 @@ namespace SimpleWeb {
if(internal_io_service) { if(internal_io_service) {
if(io_service->stopped()) if(io_service->stopped())
io_service->reset(); restart(*io_service);
// If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling // If thread_pool_size>1, start m_io_service.run() in (thread_pool_size-1) threads for thread-pooling
threads.clear(); threads.clear();

View file

@ -4,10 +4,6 @@
using namespace std; using namespace std;
#ifndef USE_STANDALONE_ASIO
namespace asio = boost::asio;
#endif
using HttpServer = SimpleWeb::Server<SimpleWeb::HTTP>; using HttpServer = SimpleWeb::Server<SimpleWeb::HTTP>;
using HttpClient = SimpleWeb::Client<SimpleWeb::HTTP>; using HttpClient = SimpleWeb::Client<SimpleWeb::HTTP>;
@ -315,7 +311,7 @@ int main() {
for(auto &thread : threads) for(auto &thread : threads)
thread.join(); thread.join();
ASSERT(client.connections.size() == 100); ASSERT(client.connections.size() == 100);
client.io_service->reset(); SimpleWeb::restart(*client.io_service);
client.io_service->run(); client.io_service->run();
ASSERT(client.connections.size() == 1); ASSERT(client.connections.size() == 1);
for(auto call : calls) for(auto call : calls)
@ -390,7 +386,7 @@ int main() {
// Test Client client's stop() // Test Client client's stop()
for(size_t c = 0; c < 40; ++c) { for(size_t c = 0; c < 40; ++c) {
auto io_service = make_shared<asio::io_service>(); auto io_service = make_shared<SimpleWeb::io_context>();
bool call = false; bool call = false;
HttpClient client("localhost:8080"); HttpClient client("localhost:8080");
client.io_service = io_service; client.io_service = io_service;
@ -410,7 +406,7 @@ int main() {
// Test Client destructor that should cancel the client's request // Test Client destructor that should cancel the client's request
for(size_t c = 0; c < 40; ++c) { for(size_t c = 0; c < 40; ++c) {
auto io_service = make_shared<asio::io_service>(); auto io_service = make_shared<SimpleWeb::io_context>();
{ {
HttpClient client("localhost:8080"); HttpClient client("localhost:8080");
client.io_service = io_service; client.io_service = io_service;
@ -431,7 +427,7 @@ int main() {
// Test server destructor // Test server destructor
{ {
auto io_service = make_shared<asio::io_service>(); auto io_service = make_shared<SimpleWeb::io_context>();
bool call = false; bool call = false;
bool client_catch = false; bool client_catch = false;
{ {

View file

@ -147,7 +147,7 @@ int main() {
ASSERT(fields_result1 == fields_result2 && fields_result1 == fields); ASSERT(fields_result1 == fields_result2 && fields_result1 == fields);
auto serverTest = make_shared<ServerTest>(); auto serverTest = make_shared<ServerTest>();
serverTest->io_service = std::make_shared<asio::io_service>(); serverTest->io_service = std::make_shared<io_context>();
serverTest->parse_request_test(); serverTest->parse_request_test();
@ -160,7 +160,7 @@ int main() {
clientTest2->parse_response_header_test(); clientTest2->parse_response_header_test();
asio::io_service io_service; io_context io_service;
asio::ip::tcp::socket socket(io_service); asio::ip::tcp::socket socket(io_service);
SimpleWeb::Server<HTTP>::Request request(static_cast<size_t>(-1), nullptr); SimpleWeb::Server<HTTP>::Request request(static_cast<size_t>(-1), nullptr);
{ {