Session and Connection cleanup
This commit is contained in:
parent
653e24296b
commit
d8b8716a17
5 changed files with 172 additions and 151 deletions
142
client_http.hpp
142
client_http.hpp
|
|
@ -114,39 +114,30 @@ namespace SimpleWeb {
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class Connection {
|
class Connection : public std::enable_shared_from_this<Connection> {
|
||||||
public:
|
public:
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Connection(Args &&... args) : socket(new socket_type(std::forward<Args>(args)...)) {}
|
Connection(std::shared_ptr<bool> cancel_handlers, std::shared_ptr<SharedMutex> cancel_handlers_mutex, long timeout, Args &&... args)
|
||||||
|
: cancel_handlers(std::move(cancel_handlers)), cancel_handlers_mutex(std::move(cancel_handlers_mutex)), timeout(timeout),
|
||||||
|
socket(new socket_type(std::forward<Args>(args)...)) {}
|
||||||
|
|
||||||
|
std::shared_ptr<bool> cancel_handlers;
|
||||||
|
std::shared_ptr<SharedMutex> cancel_handlers_mutex;
|
||||||
|
long timeout;
|
||||||
|
|
||||||
std::unique_ptr<socket_type> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
|
std::unique_ptr<socket_type> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
|
||||||
std::mutex socket_close_mutex;
|
std::mutex socket_close_mutex;
|
||||||
bool in_use = false;
|
bool in_use = false;
|
||||||
bool attempt_reconnect = true;
|
bool attempt_reconnect = true;
|
||||||
|
|
||||||
|
std::unique_ptr<asio::deadline_timer> timer;
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
error_code ec;
|
error_code ec;
|
||||||
std::unique_lock<std::mutex> lock(socket_close_mutex); // the following operations seems to be needed to run sequentially
|
std::unique_lock<std::mutex> lock(socket_close_mutex); // the following operations seems to be needed to run sequentially
|
||||||
socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||||
socket->lowest_layer().close(ec);
|
socket->lowest_layer().close(ec);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
class Session {
|
|
||||||
public:
|
|
||||||
Session(std::shared_ptr<bool> cancel_handlers, std::shared_ptr<SharedMutex> cancel_handlers_mutex, long timeout,
|
|
||||||
std::shared_ptr<Connection> connection, std::unique_ptr<asio::streambuf> request_buffer)
|
|
||||||
: cancel_handlers(std::move(cancel_handlers)), cancel_handlers_mutex(std::move(cancel_handlers_mutex)), timeout(timeout),
|
|
||||||
connection(std::move(connection)), request_buffer(std::move(request_buffer)), response(new Response()) {}
|
|
||||||
std::shared_ptr<bool> cancel_handlers;
|
|
||||||
std::shared_ptr<SharedMutex> cancel_handlers_mutex;
|
|
||||||
long timeout;
|
|
||||||
|
|
||||||
std::shared_ptr<Connection> connection;
|
|
||||||
std::unique_ptr<asio::streambuf> request_buffer;
|
|
||||||
std::shared_ptr<Response> response;
|
|
||||||
std::function<void(const error_code &)> callback;
|
|
||||||
std::unique_ptr<asio::deadline_timer> timer;
|
|
||||||
|
|
||||||
void set_timeout(long seconds = 0) {
|
void set_timeout(long seconds = 0) {
|
||||||
if(seconds == 0)
|
if(seconds == 0)
|
||||||
|
|
@ -155,13 +146,12 @@ namespace SimpleWeb {
|
||||||
timer = nullptr;
|
timer = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
timer = std::unique_ptr<asio::deadline_timer>(new asio::deadline_timer(socket->get_io_service()));
|
||||||
auto timer = std::unique_ptr<asio::deadline_timer>(new asio::deadline_timer(connection->socket->get_io_service()));
|
|
||||||
timer->expires_from_now(boost::posix_time::seconds(seconds));
|
timer->expires_from_now(boost::posix_time::seconds(seconds));
|
||||||
auto connection = this->connection;
|
auto self = this->shared_from_this();
|
||||||
timer->async_wait([connection](const error_code &ec) {
|
timer->async_wait([self](const error_code &ec) {
|
||||||
if(!ec)
|
if(!ec)
|
||||||
connection->close();
|
self->close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,6 +159,24 @@ namespace SimpleWeb {
|
||||||
if(timer)
|
if(timer)
|
||||||
timer->cancel();
|
timer->cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<bool, std::unique_ptr<SharedMutex::SharedLock>> cancel_handlers_bool_and_lock() {
|
||||||
|
if(!cancel_handlers)
|
||||||
|
return {false, nullptr};
|
||||||
|
auto lock = cancel_handlers_mutex->shared_lock();
|
||||||
|
return {*cancel_handlers, std::move(lock)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Session {
|
||||||
|
public:
|
||||||
|
Session(std::shared_ptr<Connection> connection, std::unique_ptr<asio::streambuf> request_buffer)
|
||||||
|
: connection(std::move(connection)), request_buffer(std::move(request_buffer)), response(new Response()) {}
|
||||||
|
|
||||||
|
std::shared_ptr<Connection> connection;
|
||||||
|
std::unique_ptr<asio::streambuf> request_buffer;
|
||||||
|
std::shared_ptr<Response> response;
|
||||||
|
std::function<void(const error_code &)> callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -243,7 +251,7 @@ namespace SimpleWeb {
|
||||||
/// Do not use concurrently with the synchronous request functions.
|
/// Do not use concurrently with the synchronous request functions.
|
||||||
void request(const std::string &method, const std::string &path, string_view content, const CaseInsensitiveMultimap &header,
|
void request(const std::string &method, const std::string &path, string_view content, const CaseInsensitiveMultimap &header,
|
||||||
std::function<void(std::shared_ptr<Response>, const error_code &)> &&request_callback_) {
|
std::function<void(std::shared_ptr<Response>, const error_code &)> &&request_callback_) {
|
||||||
auto session = std::make_shared<Session>(cancel_handlers, cancel_handlers_mutex, config.timeout, get_connection(), create_request_header(method, path, header));
|
auto session = std::make_shared<Session>(get_connection(), create_request_header(method, path, header));
|
||||||
auto connection = session->connection;
|
auto connection = session->connection;
|
||||||
auto response = session->response;
|
auto response = session->response;
|
||||||
auto request_callback = std::make_shared<std::function<void(std::shared_ptr<Response>, const error_code &)>>(std::move(request_callback_));
|
auto request_callback = std::make_shared<std::function<void(std::shared_ptr<Response>, const error_code &)>>(std::move(request_callback_));
|
||||||
|
|
@ -301,7 +309,7 @@ namespace SimpleWeb {
|
||||||
/// Asynchronous request where setting and/or running Client's io_service is required.
|
/// Asynchronous request where setting and/or running Client's io_service is required.
|
||||||
void request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header,
|
void request(const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header,
|
||||||
std::function<void(std::shared_ptr<Response>, const error_code &)> &&request_callback_) {
|
std::function<void(std::shared_ptr<Response>, const error_code &)> &&request_callback_) {
|
||||||
auto session = std::make_shared<Session>(cancel_handlers, cancel_handlers_mutex, config.timeout, get_connection(), create_request_header(method, path, header));
|
auto session = std::make_shared<Session>(get_connection(), create_request_header(method, path, header));
|
||||||
auto connection = session->connection;
|
auto connection = session->connection;
|
||||||
auto response = session->response;
|
auto response = session->response;
|
||||||
auto request_callback = std::make_shared<std::function<void(std::shared_ptr<Response>, const error_code &)>>(std::move(request_callback_));
|
auto request_callback = std::make_shared<std::function<void(std::shared_ptr<Response>, const error_code &)>>(std::move(request_callback_));
|
||||||
|
|
@ -352,21 +360,27 @@ namespace SimpleWeb {
|
||||||
void stop() {
|
void stop() {
|
||||||
std::unique_lock<std::mutex> lock(connections_mutex);
|
std::unique_lock<std::mutex> lock(connections_mutex);
|
||||||
for(auto it = connections.begin(); it != connections.end();) {
|
for(auto it = connections.begin(); it != connections.end();) {
|
||||||
|
if(!internal_io_service) {
|
||||||
(*it)->attempt_reconnect = false;
|
(*it)->attempt_reconnect = false;
|
||||||
(*it)->close();
|
(*it)->close();
|
||||||
|
}
|
||||||
it = connections.erase(it);
|
it = connections.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~ClientBase() {
|
virtual ~ClientBase() {
|
||||||
{
|
{
|
||||||
|
if(!internal_io_service) {
|
||||||
auto lock = cancel_handlers_mutex->unique_lock();
|
auto lock = cancel_handlers_mutex->unique_lock();
|
||||||
*cancel_handlers = true;
|
*cancel_handlers = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool internal_io_service = false;
|
||||||
|
|
||||||
std::string host;
|
std::string host;
|
||||||
unsigned short port;
|
unsigned short port;
|
||||||
|
|
||||||
|
|
@ -381,7 +395,7 @@ namespace SimpleWeb {
|
||||||
size_t concurrent_synchronous_requests = 0;
|
size_t concurrent_synchronous_requests = 0;
|
||||||
std::mutex concurrent_synchronous_requests_mutex;
|
std::mutex concurrent_synchronous_requests_mutex;
|
||||||
|
|
||||||
ClientBase(const std::string &host_port, unsigned short default_port) : io_service(new asio::io_service()), cancel_handlers(new bool(false)), cancel_handlers_mutex(new SharedMutex()) {
|
ClientBase(const std::string &host_port, unsigned short default_port) : cancel_handlers(new bool(false)), cancel_handlers_mutex(new SharedMutex()) {
|
||||||
auto parsed_host_port = parse_host_port(host_port, default_port);
|
auto parsed_host_port = parse_host_port(host_port, default_port);
|
||||||
host = parsed_host_port.first;
|
host = parsed_host_port.first;
|
||||||
port = parsed_host_port.second;
|
port = parsed_host_port.second;
|
||||||
|
|
@ -390,6 +404,14 @@ namespace SimpleWeb {
|
||||||
std::shared_ptr<Connection> get_connection() {
|
std::shared_ptr<Connection> get_connection() {
|
||||||
std::shared_ptr<Connection> connection;
|
std::shared_ptr<Connection> connection;
|
||||||
std::unique_lock<std::mutex> lock(connections_mutex);
|
std::unique_lock<std::mutex> lock(connections_mutex);
|
||||||
|
|
||||||
|
if(!io_service) {
|
||||||
|
io_service = std::make_shared<asio::io_service>();
|
||||||
|
internal_io_service = true;
|
||||||
|
cancel_handlers = nullptr;
|
||||||
|
cancel_handlers_mutex = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
for(auto it = connections.begin(); it != connections.end(); ++it) {
|
for(auto it = connections.begin(); it != connections.end(); ++it) {
|
||||||
if(!(*it)->in_use && !connection) {
|
if(!(*it)->in_use && !connection) {
|
||||||
connection = *it;
|
connection = *it;
|
||||||
|
|
@ -449,11 +471,11 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(const std::shared_ptr<Session> &session) {
|
void write(const std::shared_ptr<Session> &session) {
|
||||||
session->set_timeout();
|
session->connection->set_timeout();
|
||||||
asio::async_write(*session->connection->socket, session->request_buffer->data(), [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
|
asio::async_write(*session->connection->socket, session->request_buffer->data(), [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec)
|
if(!ec)
|
||||||
this->read(session);
|
this->read(session);
|
||||||
|
|
@ -465,11 +487,11 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
void read(const std::shared_ptr<Session> &session) {
|
void read(const std::shared_ptr<Session> &session) {
|
||||||
session->set_timeout();
|
session->connection->set_timeout();
|
||||||
asio::async_read_until(*session->connection->socket, session->response->content_buffer, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) {
|
asio::async_read_until(*session->connection->socket, session->response->content_buffer, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
session->connection->attempt_reconnect = true;
|
session->connection->attempt_reconnect = true;
|
||||||
|
|
@ -482,11 +504,11 @@ namespace SimpleWeb {
|
||||||
if(header_it != session->response->header.end()) {
|
if(header_it != session->response->header.end()) {
|
||||||
auto content_length = stoull(header_it->second);
|
auto content_length = stoull(header_it->second);
|
||||||
if(content_length > num_additional_bytes) {
|
if(content_length > num_additional_bytes) {
|
||||||
session->set_timeout();
|
session->connection->set_timeout();
|
||||||
asio::async_read(*session->connection->socket, session->response->content_buffer, asio::transfer_exactly(content_length - num_additional_bytes), [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
|
asio::async_read(*session->connection->socket, session->response->content_buffer, asio::transfer_exactly(content_length - num_additional_bytes), [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec)
|
if(!ec)
|
||||||
session->callback(ec);
|
session->callback(ec);
|
||||||
|
|
@ -504,11 +526,11 @@ namespace SimpleWeb {
|
||||||
this->read_chunked(session, tmp_streambuf);
|
this->read_chunked(session, tmp_streambuf);
|
||||||
}
|
}
|
||||||
else if(session->response->http_version < "1.1" || ((header_it = session->response->header.find("Session")) != session->response->header.end() && header_it->second == "close")) {
|
else if(session->response->http_version < "1.1" || ((header_it = session->response->header.find("Session")) != session->response->header.end() && header_it->second == "close")) {
|
||||||
session->set_timeout();
|
session->connection->set_timeout();
|
||||||
asio::async_read(*session->connection->socket, session->response->content_buffer, [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
|
asio::async_read(*session->connection->socket, session->response->content_buffer, [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec)
|
if(!ec)
|
||||||
session->callback(ec);
|
session->callback(ec);
|
||||||
|
|
@ -541,11 +563,11 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_chunked(const std::shared_ptr<Session> &session, const std::shared_ptr<asio::streambuf> &tmp_streambuf) {
|
void read_chunked(const std::shared_ptr<Session> &session, const std::shared_ptr<asio::streambuf> &tmp_streambuf) {
|
||||||
session->set_timeout();
|
session->connection->set_timeout();
|
||||||
asio::async_read_until(*session->connection->socket, session->response->content_buffer, "\r\n", [this, session, tmp_streambuf](const error_code &ec, size_t bytes_transferred) {
|
asio::async_read_until(*session->connection->socket, session->response->content_buffer, "\r\n", [this, session, tmp_streambuf](const error_code &ec, size_t bytes_transferred) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
std::string line;
|
std::string line;
|
||||||
|
|
@ -579,11 +601,11 @@ namespace SimpleWeb {
|
||||||
};
|
};
|
||||||
|
|
||||||
if((2 + length) > num_additional_bytes) {
|
if((2 + length) > num_additional_bytes) {
|
||||||
session->set_timeout();
|
session->connection->set_timeout();
|
||||||
asio::async_read(*session->connection->socket, session->response->content_buffer, asio::transfer_exactly(2 + length - num_additional_bytes), [this, session, post_process](const error_code &ec, size_t /*bytes_transferred*/) {
|
asio::async_read(*session->connection->socket, session->response->content_buffer, asio::transfer_exactly(2 + length - num_additional_bytes), [this, session, post_process](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec)
|
if(!ec)
|
||||||
post_process();
|
post_process();
|
||||||
|
|
@ -616,24 +638,24 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<Connection> create_connection() override {
|
std::shared_ptr<Connection> create_connection() override {
|
||||||
return std::make_shared<Connection>(*io_service);
|
return std::make_shared<Connection>(cancel_handlers, cancel_handlers_mutex, config.timeout, *io_service);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
session->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) {
|
resolver->async_resolve(*query, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
session->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, it, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
asio::ip::tcp::no_delay option(true);
|
asio::ip::tcp::no_delay option(true);
|
||||||
|
|
|
||||||
|
|
@ -41,22 +41,22 @@ namespace SimpleWeb {
|
||||||
asio::ssl::context context;
|
asio::ssl::context context;
|
||||||
|
|
||||||
std::shared_ptr<Connection> create_connection() override {
|
std::shared_ptr<Connection> create_connection() override {
|
||||||
return std::make_shared<Connection>(*io_service, context);
|
return std::make_shared<Connection>(cancel_handlers, cancel_handlers_mutex, config.timeout, *io_service, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
resolver->async_resolve(*query, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) {
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
session->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(), it, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
asio::ip::tcp::no_delay option(true);
|
asio::ip::tcp::no_delay option(true);
|
||||||
|
|
@ -69,19 +69,19 @@ namespace SimpleWeb {
|
||||||
auto host_port = this->host + ':' + std::to_string(this->port);
|
auto host_port = this->host + ':' + std::to_string(this->port);
|
||||||
write_stream << "CONNECT " + host_port + " HTTP/1.1\r\n"
|
write_stream << "CONNECT " + host_port + " HTTP/1.1\r\n"
|
||||||
<< "Host: " << host_port << "\r\n\r\n";
|
<< "Host: " << host_port << "\r\n\r\n";
|
||||||
session->set_timeout(this->config.timeout_connect);
|
session->connection->set_timeout(this->config.timeout_connect);
|
||||||
asio::async_write(session->connection->socket->next_layer(), *write_buffer, [this, session, write_buffer](const error_code &ec, size_t /*bytes_transferred*/) {
|
asio::async_write(session->connection->socket->next_layer(), *write_buffer, [this, session, write_buffer](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
std::shared_ptr<Response> response(new Response());
|
std::shared_ptr<Response> response(new Response());
|
||||||
session->set_timeout(this->config.timeout_connect);
|
session->connection->set_timeout(this->config.timeout_connect);
|
||||||
asio::async_read_until(session->connection->socket->next_layer(), response->content_buffer, "\r\n\r\n", [this, session, response](const error_code &ec, size_t /*bytes_transferred*/) {
|
asio::async_read_until(session->connection->socket->next_layer(), response->content_buffer, "\r\n\r\n", [this, session, response](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
response->parse_header();
|
response->parse_header();
|
||||||
|
|
@ -124,11 +124,11 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
void handshake(const std::shared_ptr<Session> &session) {
|
void handshake(const std::shared_ptr<Session> &session) {
|
||||||
session->set_timeout(this->config.timeout_connect);
|
session->connection->set_timeout(this->config.timeout_connect);
|
||||||
session->connection->socket->async_handshake(asio::ssl::stream_base::client, [this, session](const error_code &ec) {
|
session->connection->socket->async_handshake(asio::ssl::stream_base::client, [this, session](const error_code &ec) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto lock = session->cancel_handlers_mutex->shared_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(*session->cancel_handlers)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec)
|
if(!ec)
|
||||||
this->write(session);
|
this->write(session);
|
||||||
|
|
|
||||||
119
server_http.hpp
119
server_http.hpp
|
|
@ -50,7 +50,7 @@ namespace SimpleWeb {
|
||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
class Response : public std::ostream {
|
class Response : public std::enable_shared_from_this<Response>, public std::ostream {
|
||||||
friend class ServerBase<socket_type>;
|
friend class ServerBase<socket_type>;
|
||||||
friend class Server<socket_type>;
|
friend class Server<socket_type>;
|
||||||
|
|
||||||
|
|
@ -86,15 +86,12 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
/// Use this function if you need to recursively send parts of a longer message
|
/// Use this function if you need to recursively send parts of a longer message
|
||||||
void send(const std::function<void(const error_code &)> &callback = nullptr) {
|
void send(const std::function<void(const error_code &)> &callback = nullptr) {
|
||||||
auto cancel = session->cancel_handlers_and_lock();
|
session->connection->set_timeout(timeout_content);
|
||||||
if(cancel.first)
|
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write
|
||||||
return;
|
asio::async_write(*session->connection->socket, streambuf, [self, callback](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
session->set_timeout(timeout_content);
|
self->session->connection->cancel_timeout();
|
||||||
auto session = this->session;
|
auto cancel_pair = self->session->connection->cancel_handlers_bool_and_lock();
|
||||||
asio::async_write(*session->connection->socket, streambuf, [session, callback](const error_code &ec, size_t /*bytes_transferred*/) {
|
if(cancel_pair.first)
|
||||||
session->cancel_timeout();
|
|
||||||
auto cancel = session->cancel_handlers_and_lock();
|
|
||||||
if(cancel.first)
|
|
||||||
return;
|
return;
|
||||||
if(callback)
|
if(callback)
|
||||||
callback(ec);
|
callback(ec);
|
||||||
|
|
@ -258,26 +255,58 @@ namespace SimpleWeb {
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
class Connection {
|
class Connection : public std::enable_shared_from_this<Connection> {
|
||||||
public:
|
public:
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Connection(Args &&... args) : socket(new socket_type(std::forward<Args>(args)...)) {}
|
Connection(std::shared_ptr<bool> cancel_handlers, std::shared_ptr<SharedMutex> cancel_handlers_mutex, Args &&... args)
|
||||||
|
: cancel_handlers(std::move(cancel_handlers)), cancel_handlers_mutex(std::move(cancel_handlers_mutex)), socket(new socket_type(std::forward<Args>(args)...)) {}
|
||||||
|
|
||||||
|
std::shared_ptr<bool> cancel_handlers;
|
||||||
|
std::shared_ptr<SharedMutex> cancel_handlers_mutex;
|
||||||
|
|
||||||
std::unique_ptr<socket_type> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
|
std::unique_ptr<socket_type> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
|
||||||
std::mutex socket_close_mutex;
|
std::mutex socket_close_mutex;
|
||||||
|
|
||||||
|
std::unique_ptr<asio::deadline_timer> timer;
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
error_code ec;
|
error_code ec;
|
||||||
std::unique_lock<std::mutex> lock(socket_close_mutex); // the following operations seems to be needed to run sequentially
|
std::unique_lock<std::mutex> lock(socket_close_mutex); // the following operations seems to be needed to run sequentially
|
||||||
socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
|
||||||
socket->lowest_layer().close(ec);
|
socket->lowest_layer().close(ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_timeout(long seconds) {
|
||||||
|
if(seconds == 0) {
|
||||||
|
timer = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = std::unique_ptr<asio::deadline_timer>(new asio::deadline_timer(socket->get_io_service()));
|
||||||
|
timer->expires_from_now(boost::posix_time::seconds(seconds));
|
||||||
|
auto self = this->shared_from_this();
|
||||||
|
timer->async_wait([self](const error_code &ec) {
|
||||||
|
if(!ec)
|
||||||
|
self->close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancel_timeout() {
|
||||||
|
if(timer)
|
||||||
|
timer->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<bool, std::unique_ptr<SharedMutex::SharedLock>> cancel_handlers_bool_and_lock() {
|
||||||
|
if(!cancel_handlers)
|
||||||
|
return {false, nullptr};
|
||||||
|
auto lock = cancel_handlers_mutex->shared_lock();
|
||||||
|
return {*cancel_handlers, std::move(lock)};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Session {
|
class Session {
|
||||||
public:
|
public:
|
||||||
Session(std::shared_ptr<bool> cancel_handlers, std::shared_ptr<SharedMutex> cancel_handlers_mutex, std::shared_ptr<Connection> connection)
|
Session(std::shared_ptr<Connection> connection) : connection(std::move(connection)) {
|
||||||
: cancel_handlers(std::move(cancel_handlers)), cancel_handlers_mutex(std::move(cancel_handlers_mutex)), connection(std::move(connection)) {
|
|
||||||
try {
|
try {
|
||||||
auto remote_endpoint = this->connection->socket->lowest_layer().remote_endpoint();
|
auto remote_endpoint = this->connection->socket->lowest_layer().remote_endpoint();
|
||||||
request = std::shared_ptr<Request>(new Request(remote_endpoint.address().to_string(), remote_endpoint.port()));
|
request = std::shared_ptr<Request>(new Request(remote_endpoint.address().to_string(), remote_endpoint.port()));
|
||||||
|
|
@ -287,39 +316,8 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<bool> cancel_handlers;
|
|
||||||
std::shared_ptr<SharedMutex> cancel_handlers_mutex;
|
|
||||||
|
|
||||||
std::shared_ptr<Connection> connection;
|
std::shared_ptr<Connection> connection;
|
||||||
std::shared_ptr<Request> request;
|
std::shared_ptr<Request> request;
|
||||||
std::unique_ptr<asio::deadline_timer> timer;
|
|
||||||
|
|
||||||
std::pair<bool, std::unique_ptr<SharedMutex::SharedLock>> cancel_handlers_and_lock() {
|
|
||||||
if(!cancel_handlers)
|
|
||||||
return {false, nullptr};
|
|
||||||
auto lock = cancel_handlers_mutex->shared_lock();
|
|
||||||
return {*cancel_handlers, std::move(lock)};
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_timeout(long seconds) {
|
|
||||||
if(seconds == 0) {
|
|
||||||
timer = nullptr;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
timer = std::unique_ptr<asio::deadline_timer>(new asio::deadline_timer(connection->socket->get_io_service()));
|
|
||||||
timer->expires_from_now(boost::posix_time::seconds(seconds));
|
|
||||||
auto connection = this->connection;
|
|
||||||
timer->async_wait([connection](const error_code &ec) {
|
|
||||||
if(!ec)
|
|
||||||
connection->close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void cancel_timeout() {
|
|
||||||
if(timer)
|
|
||||||
timer->cancel();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -377,6 +375,7 @@ namespace SimpleWeb {
|
||||||
io_service = std::make_shared<asio::io_service>();
|
io_service = std::make_shared<asio::io_service>();
|
||||||
internal_io_service = true;
|
internal_io_service = true;
|
||||||
cancel_handlers = nullptr;
|
cancel_handlers = nullptr;
|
||||||
|
cancel_handlers_mutex = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(io_service->stopped())
|
if(io_service->stopped())
|
||||||
|
|
@ -463,7 +462,7 @@ namespace SimpleWeb {
|
||||||
std::shared_ptr<Connection> create_connection(Args &&... args) {
|
std::shared_ptr<Connection> create_connection(Args &&... args) {
|
||||||
auto connections = this->connections;
|
auto connections = this->connections;
|
||||||
auto connections_mutex = this->connections_mutex;
|
auto connections_mutex = this->connections_mutex;
|
||||||
auto connection = std::shared_ptr<Connection>(new Connection(std::forward<Args>(args)...), [connections, connections_mutex](Connection *connection) {
|
auto connection = std::shared_ptr<Connection>(new Connection(cancel_handlers, cancel_handlers_mutex, std::forward<Args>(args)...), [connections, connections_mutex](Connection *connection) {
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(*connections_mutex);
|
std::unique_lock<std::mutex> lock(*connections_mutex);
|
||||||
auto it = connections->find(connection);
|
auto it = connections->find(connection);
|
||||||
|
|
@ -480,11 +479,11 @@ namespace SimpleWeb {
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_request_and_content(const std::shared_ptr<Session> &session) {
|
void read_request_and_content(const std::shared_ptr<Session> &session) {
|
||||||
session->set_timeout(config.timeout_request);
|
session->connection->set_timeout(config.timeout_request);
|
||||||
asio::async_read_until(*session->connection->socket, session->request->streambuf, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) {
|
asio::async_read_until(*session->connection->socket, session->request->streambuf, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto cancel = session->cancel_handlers_and_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(cancel.first)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec) {
|
if(!ec) {
|
||||||
//request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
|
//request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
|
||||||
|
|
@ -509,11 +508,11 @@ namespace SimpleWeb {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(content_length > num_additional_bytes) {
|
if(content_length > num_additional_bytes) {
|
||||||
session->set_timeout(config.timeout_content);
|
session->connection->set_timeout(config.timeout_content);
|
||||||
asio::async_read(*session->connection->socket, session->request->streambuf, asio::transfer_exactly(content_length - num_additional_bytes), [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
|
asio::async_read(*session->connection->socket, session->request->streambuf, asio::transfer_exactly(content_length - num_additional_bytes), [this, session](const error_code &ec, size_t /*bytes_transferred*/) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto cancel = session->cancel_handlers_and_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(cancel.first)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec)
|
if(!ec)
|
||||||
this->find_resource(session);
|
this->find_resource(session);
|
||||||
|
|
@ -560,7 +559,7 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
void write_response(const std::shared_ptr<Session> &session,
|
void write_response(const std::shared_ptr<Session> &session,
|
||||||
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)> &resource_function) {
|
std::function<void(std::shared_ptr<typename ServerBase<socket_type>::Response>, std::shared_ptr<typename ServerBase<socket_type>::Request>)> &resource_function) {
|
||||||
session->set_timeout(config.timeout_content);
|
session->connection->set_timeout(config.timeout_content);
|
||||||
auto response = std::shared_ptr<Response>(new Response(session, config.timeout_content), [this](Response *response_ptr) {
|
auto response = std::shared_ptr<Response>(new Response(session, config.timeout_content), [this](Response *response_ptr) {
|
||||||
auto response = std::shared_ptr<Response>(response_ptr);
|
auto response = std::shared_ptr<Response>(response_ptr);
|
||||||
response->send([this, response](const error_code &ec) {
|
response->send([this, response](const error_code &ec) {
|
||||||
|
|
@ -573,13 +572,13 @@ namespace SimpleWeb {
|
||||||
if(case_insensitive_equal(it->second, "close"))
|
if(case_insensitive_equal(it->second, "close"))
|
||||||
return;
|
return;
|
||||||
else if(case_insensitive_equal(it->second, "keep-alive")) {
|
else if(case_insensitive_equal(it->second, "keep-alive")) {
|
||||||
auto new_session = std::make_shared<Session>(this->cancel_handlers, this->cancel_handlers_mutex, response->session->connection);
|
auto new_session = std::make_shared<Session>(response->session->connection);
|
||||||
this->read_request_and_content(new_session);
|
this->read_request_and_content(new_session);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(response->session->request->http_version >= "1.1") {
|
if(response->session->request->http_version >= "1.1") {
|
||||||
auto new_session = std::make_shared<Session>(this->cancel_handlers, this->cancel_handlers_mutex, response->session->connection);
|
auto new_session = std::make_shared<Session>(response->session->connection);
|
||||||
this->read_request_and_content(new_session);
|
this->read_request_and_content(new_session);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -612,11 +611,11 @@ namespace SimpleWeb {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void accept() override {
|
void accept() override {
|
||||||
auto session = std::make_shared<Session>(cancel_handlers, cancel_handlers_mutex, create_connection(*io_service));
|
auto session = std::make_shared<Session>(create_connection(*io_service));
|
||||||
|
|
||||||
acceptor->async_accept(*session->connection->socket, [this, session](const error_code &ec) {
|
acceptor->async_accept(*session->connection->socket, [this, session](const error_code &ec) {
|
||||||
auto cancel = session->cancel_handlers_and_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(cancel.first)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//Immediately start accepting a new connection (unless io_service has been stopped)
|
//Immediately start accepting a new connection (unless io_service has been stopped)
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,11 @@ namespace SimpleWeb {
|
||||||
asio::ssl::context context;
|
asio::ssl::context context;
|
||||||
|
|
||||||
void accept() override {
|
void accept() override {
|
||||||
auto session = std::make_shared<Session>(cancel_handlers, cancel_handlers_mutex, create_connection(*io_service, context));
|
auto session = std::make_shared<Session>(create_connection(*io_service, context));
|
||||||
|
|
||||||
acceptor->async_accept(session->connection->socket->lowest_layer(), [this, session](const error_code &ec) {
|
acceptor->async_accept(session->connection->socket->lowest_layer(), [this, session](const error_code &ec) {
|
||||||
auto cancel = session->cancel_handlers_and_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(cancel.first)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(ec != asio::error::operation_aborted)
|
if(ec != asio::error::operation_aborted)
|
||||||
|
|
@ -63,11 +63,11 @@ namespace SimpleWeb {
|
||||||
error_code ec;
|
error_code ec;
|
||||||
session->connection->socket->lowest_layer().set_option(option, ec);
|
session->connection->socket->lowest_layer().set_option(option, ec);
|
||||||
|
|
||||||
session->set_timeout(config.timeout_request);
|
session->connection->set_timeout(config.timeout_request);
|
||||||
session->connection->socket->async_handshake(asio::ssl::stream_base::server, [this, session](const error_code &ec) {
|
session->connection->socket->async_handshake(asio::ssl::stream_base::server, [this, session](const error_code &ec) {
|
||||||
session->cancel_timeout();
|
session->connection->cancel_timeout();
|
||||||
auto cancel = session->cancel_handlers_and_lock();
|
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
|
||||||
if(cancel.first)
|
if(cancel_pair.first)
|
||||||
return;
|
return;
|
||||||
if(!ec)
|
if(!ec)
|
||||||
this->read_request_and_content(session);
|
this->read_request_and_content(session);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ public:
|
||||||
void accept() override {}
|
void accept() override {}
|
||||||
|
|
||||||
void parse_request_test() {
|
void parse_request_test() {
|
||||||
auto session = std::make_shared<Session>(cancel_handlers, cancel_handlers_mutex, create_connection(*io_service));
|
auto session = std::make_shared<Session>(create_connection(*io_service));
|
||||||
|
|
||||||
std::ostream stream(&session->request->content.streambuf);
|
std::ostream stream(&session->request->content.streambuf);
|
||||||
stream << "GET /test/ HTTP/1.1\r\n";
|
stream << "GET /test/ HTTP/1.1\r\n";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue