Session and Connection cleanup

This commit is contained in:
eidheim 2017-07-09 12:54:56 +02:00
commit d8b8716a17
5 changed files with 172 additions and 151 deletions

View file

@ -50,7 +50,7 @@ namespace SimpleWeb {
class Session;
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 Server<socket_type>;
@ -86,15 +86,12 @@ namespace SimpleWeb {
/// 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) {
auto cancel = session->cancel_handlers_and_lock();
if(cancel.first)
return;
session->set_timeout(timeout_content);
auto session = this->session;
asio::async_write(*session->connection->socket, streambuf, [session, callback](const error_code &ec, size_t /*bytes_transferred*/) {
session->cancel_timeout();
auto cancel = session->cancel_handlers_and_lock();
if(cancel.first)
session->connection->set_timeout(timeout_content);
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write
asio::async_write(*session->connection->socket, streambuf, [self, callback](const error_code &ec, size_t /*bytes_transferred*/) {
self->session->connection->cancel_timeout();
auto cancel_pair = self->session->connection->cancel_handlers_bool_and_lock();
if(cancel_pair.first)
return;
if(callback)
callback(ec);
@ -258,26 +255,58 @@ namespace SimpleWeb {
};
protected:
class Connection {
class Connection : public std::enable_shared_from_this<Connection> {
public:
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::mutex socket_close_mutex;
std::unique_ptr<asio::deadline_timer> timer;
void close() {
error_code ec;
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().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 {
public:
Session(std::shared_ptr<bool> cancel_handlers, std::shared_ptr<SharedMutex> cancel_handlers_mutex, std::shared_ptr<Connection> connection)
: cancel_handlers(std::move(cancel_handlers)), cancel_handlers_mutex(std::move(cancel_handlers_mutex)), connection(std::move(connection)) {
Session(std::shared_ptr<Connection> connection) : connection(std::move(connection)) {
try {
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()));
@ -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<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:
@ -377,6 +375,7 @@ namespace SimpleWeb {
io_service = std::make_shared<asio::io_service>();
internal_io_service = true;
cancel_handlers = nullptr;
cancel_handlers_mutex = nullptr;
}
if(io_service->stopped())
@ -463,7 +462,7 @@ namespace SimpleWeb {
std::shared_ptr<Connection> create_connection(Args &&... args) {
auto connections = this->connections;
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);
auto it = connections->find(connection);
@ -480,11 +479,11 @@ namespace SimpleWeb {
}
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) {
session->cancel_timeout();
auto cancel = session->cancel_handlers_and_lock();
if(cancel.first)
session->connection->cancel_timeout();
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
if(cancel_pair.first)
return;
if(!ec) {
//request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs:
@ -509,11 +508,11 @@ namespace SimpleWeb {
return;
}
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*/) {
session->cancel_timeout();
auto cancel = session->cancel_handlers_and_lock();
if(cancel.first)
session->connection->cancel_timeout();
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
if(cancel_pair.first)
return;
if(!ec)
this->find_resource(session);
@ -560,7 +559,7 @@ namespace SimpleWeb {
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) {
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>(response_ptr);
response->send([this, response](const error_code &ec) {
@ -573,13 +572,13 @@ namespace SimpleWeb {
if(case_insensitive_equal(it->second, "close"))
return;
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);
return;
}
}
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);
return;
}
@ -612,11 +611,11 @@ namespace SimpleWeb {
protected:
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) {
auto cancel = session->cancel_handlers_and_lock();
if(cancel.first)
auto cancel_pair = session->connection->cancel_handlers_bool_and_lock();
if(cancel_pair.first)
return;
//Immediately start accepting a new connection (unless io_service has been stopped)