Cleanup of timeout handling: timeouts are no longer reset on partial reads and sends

This commit is contained in:
eidheim 2020-02-04 10:01:49 +01:00
commit 84455ac966
3 changed files with 18 additions and 40 deletions

View file

@ -150,11 +150,10 @@ namespace SimpleWeb {
class Connection : public std::enable_shared_from_this<Connection> { class Connection : public std::enable_shared_from_this<Connection> {
public: public:
template <typename... Args> template <typename... Args>
Connection(std::shared_ptr<ScopeRunner> handler_runner_, long timeout, Args &&... args) noexcept Connection(std::shared_ptr<ScopeRunner> handler_runner_, Args &&... args) noexcept
: handler_runner(std::move(handler_runner_)), timeout(timeout), socket(new socket_type(std::forward<Args>(args)...)) {} : handler_runner(std::move(handler_runner_)), socket(new socket_type(std::forward<Args>(args)...)) {}
std::shared_ptr<ScopeRunner> handler_runner; std::shared_ptr<ScopeRunner> handler_runner;
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
bool in_use = false; bool in_use = false;
@ -168,9 +167,7 @@ namespace SimpleWeb {
socket->lowest_layer().cancel(ec); socket->lowest_layer().cancel(ec);
} }
void set_timeout(long seconds = 0) noexcept { void set_timeout(long seconds) noexcept {
if(seconds == 0)
seconds = timeout;
if(seconds == 0) { if(seconds == 0) {
timer = nullptr; timer = nullptr;
return; return;
@ -314,10 +311,12 @@ namespace SimpleWeb {
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_));
session->callback = [this, session_weak, request_callback](const error_code &ec) { session->callback = [this, session_weak, request_callback](const error_code &ec) {
if(auto session = session_weak.lock()) { if(auto session = session_weak.lock()) {
if(session->response->content.end) {
session->connection->cancel_timeout();
session->connection->in_use = false;
}
{ {
LockGuard lock(this->connections_mutex); LockGuard lock(this->connections_mutex);
if(session->response->content.end)
session->connection->in_use = false;
// Remove unused connections, but keep one open for HTTP persistent connection: // Remove unused connections, but keep one open for HTTP persistent connection:
std::size_t unused_connections = 0; std::size_t unused_connections = 0;
@ -389,10 +388,12 @@ namespace SimpleWeb {
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_));
session->callback = [this, session_weak, request_callback](const error_code &ec) { session->callback = [this, session_weak, request_callback](const error_code &ec) {
if(auto session = session_weak.lock()) { if(auto session = session_weak.lock()) {
if(session->response->content.end) {
session->connection->cancel_timeout();
session->connection->in_use = false;
}
{ {
LockGuard lock(this->connections_mutex); LockGuard lock(this->connections_mutex);
if(session->response->content.end)
session->connection->in_use = false;
// Remove unused connections, but keep one open for HTTP persistent connection: // Remove unused connections, but keep one open for HTTP persistent connection:
std::size_t unused_connections = 0; std::size_t unused_connections = 0;
@ -551,9 +552,8 @@ namespace SimpleWeb {
} }
void write(const std::shared_ptr<Session> &session) { void write(const std::shared_ptr<Session> &session) {
session->connection->set_timeout(); session->connection->set_timeout(config.timeout);
asio::async_write(*session->connection->socket, session->request_streambuf->data(), [this, session](const error_code &ec, std::size_t /*bytes_transferred*/) { asio::async_write(*session->connection->socket, session->request_streambuf->data(), [this, session](const error_code &ec, std::size_t /*bytes_transferred*/) {
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;
@ -569,9 +569,7 @@ namespace SimpleWeb {
} }
void read(const std::shared_ptr<Session> &session) { void read(const std::shared_ptr<Session> &session) {
session->connection->set_timeout();
asio::async_read_until(*session->connection->socket, session->response->streambuf, HeaderEndMatch(), [this, session](const error_code &ec, std::size_t bytes_transferred) { asio::async_read_until(*session->connection->socket, session->response->streambuf, HeaderEndMatch(), [this, session](const error_code &ec, std::size_t bytes_transferred) {
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;
@ -652,9 +650,7 @@ namespace SimpleWeb {
} }
void read_content(const std::shared_ptr<Session> &session, std::size_t remaining_length) { void read_content(const std::shared_ptr<Session> &session, std::size_t remaining_length) {
session->connection->set_timeout();
asio::async_read(*session->connection->socket, session->response->streambuf, asio::transfer_exactly(remaining_length), [this, session, remaining_length](const error_code &ec, std::size_t bytes_transferred) { asio::async_read(*session->connection->socket, session->response->streambuf, asio::transfer_exactly(remaining_length), [this, session, remaining_length](const error_code &ec, std::size_t bytes_transferred) {
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;
@ -675,9 +671,7 @@ namespace SimpleWeb {
} }
void read_content(const std::shared_ptr<Session> &session) { void read_content(const std::shared_ptr<Session> &session) {
session->connection->set_timeout();
asio::async_read(*session->connection->socket, session->response->streambuf, [this, session](const error_code &ec_, std::size_t /*bytes_transferred*/) { asio::async_read(*session->connection->socket, session->response->streambuf, [this, session](const error_code &ec_, std::size_t /*bytes_transferred*/) {
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;
@ -704,9 +698,7 @@ namespace SimpleWeb {
} }
void read_chunked_transfer_encoded(const std::shared_ptr<Session> &session, const std::shared_ptr<asio::streambuf> &chunk_size_streambuf) { void read_chunked_transfer_encoded(const std::shared_ptr<Session> &session, const std::shared_ptr<asio::streambuf> &chunk_size_streambuf) {
session->connection->set_timeout();
asio::async_read_until(*session->connection->socket, *chunk_size_streambuf, "\r\n", [this, session, chunk_size_streambuf](const error_code &ec, size_t bytes_transferred) { asio::async_read_until(*session->connection->socket, *chunk_size_streambuf, "\r\n", [this, session, chunk_size_streambuf](const error_code &ec, size_t bytes_transferred) {
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;
@ -747,9 +739,7 @@ namespace SimpleWeb {
} }
if((2 + chunk_size) > num_additional_bytes) { if((2 + chunk_size) > num_additional_bytes) {
session->connection->set_timeout();
asio::async_read(*session->connection->socket, session->response->streambuf, asio::transfer_exactly(2 + chunk_size - num_additional_bytes), [this, session, chunk_size_streambuf](const error_code &ec, size_t /*bytes_transferred*/) { asio::async_read(*session->connection->socket, session->response->streambuf, asio::transfer_exactly(2 + chunk_size - num_additional_bytes), [this, session, chunk_size_streambuf](const error_code &ec, size_t /*bytes_transferred*/) {
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;
@ -781,9 +771,7 @@ namespace SimpleWeb {
} }
void read_server_sent_event(const std::shared_ptr<Session> &session, const std::shared_ptr<asio::streambuf> &events_streambuf) { void read_server_sent_event(const std::shared_ptr<Session> &session, const std::shared_ptr<asio::streambuf> &events_streambuf) {
session->connection->set_timeout();
asio::async_read_until(*session->connection->socket, *events_streambuf, HeaderEndMatch(), [this, session, events_streambuf](const error_code &ec, std::size_t /*bytes_transferred*/) { asio::async_read_until(*session->connection->socket, *events_streambuf, HeaderEndMatch(), [this, session, events_streambuf](const error_code &ec, std::size_t /*bytes_transferred*/) {
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;
@ -825,7 +813,7 @@ namespace SimpleWeb {
protected: protected:
std::shared_ptr<Connection> create_connection() noexcept override { std::shared_ptr<Connection> create_connection() noexcept override {
return std::make_shared<Connection>(handler_runner, config.timeout, *io_service); return std::make_shared<Connection>(handler_runner, *io_service);
} }
void connect(const std::shared_ptr<Session> &session) override { void connect(const std::shared_ptr<Session> &session) override {

View file

@ -50,7 +50,7 @@ namespace SimpleWeb {
asio::ssl::context context; asio::ssl::context context;
std::shared_ptr<Connection> create_connection() noexcept override { std::shared_ptr<Connection> create_connection() noexcept override {
return std::make_shared<Connection>(handler_runner, config.timeout, *io_service, context); return std::make_shared<Connection>(handler_runner, *io_service, context);
} }
void connect(const std::shared_ptr<Session> &session) override { void connect(const std::shared_ptr<Session> &session) override {

View file

@ -74,9 +74,7 @@ namespace SimpleWeb {
void send_from_queue() REQUIRES(send_queue_mutex) { void send_from_queue() REQUIRES(send_queue_mutex) {
auto self = this->shared_from_this(); auto self = this->shared_from_this();
session->connection->set_timeout(timeout_content);
asio::async_write(*self->session->connection->socket, *send_queue.begin()->first, [self](const error_code &ec, std::size_t /*bytes_transferred*/) { asio::async_write(*self->session->connection->socket, *send_queue.begin()->first, [self](const error_code &ec, std::size_t /*bytes_transferred*/) {
self->session->connection->set_timeout(self->timeout_content); // Set timeout for next send
auto lock = self->session->connection->handler_runner->continue_lock(); auto lock = self->session->connection->handler_runner->continue_lock();
if(!lock) if(!lock)
return; return;
@ -111,10 +109,8 @@ namespace SimpleWeb {
} }
void send_on_delete(const std::function<void(const error_code &)> &callback = nullptr) noexcept { void send_on_delete(const std::function<void(const error_code &)> &callback = nullptr) noexcept {
session->connection->set_timeout(timeout_content);
auto self = this->shared_from_this(); // Keep Response instance alive through the following async_write 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, std::size_t /*bytes_transferred*/) { asio::async_write(*session->connection->socket, *streambuf, [self, callback](const error_code &ec, std::size_t /*bytes_transferred*/) {
self->session->connection->cancel_timeout();
auto lock = self->session->connection->handler_runner->continue_lock(); auto lock = self->session->connection->handler_runner->continue_lock();
if(!lock) if(!lock)
return; return;
@ -338,9 +334,9 @@ namespace SimpleWeb {
/// If io_service is not set, number of threads that the server will use when start() is called. /// If io_service is not set, number of threads that the server will use when start() is called.
/// Defaults to 1 thread. /// Defaults to 1 thread.
std::size_t thread_pool_size = 1; std::size_t thread_pool_size = 1;
/// Timeout on request handling. Defaults to 5 seconds. /// Timeout on request completion. Defaults to 5 seconds.
long timeout_request = 5; long timeout_request = 5;
/// Timeout on content handling. Defaults to 300 seconds. /// Timeout on request/response content completion. Defaults to 300 seconds.
long timeout_content = 300; long timeout_content = 300;
/// Maximum size of request stream buffer. Defaults to architecture maximum. /// Maximum size of request stream buffer. Defaults to architecture maximum.
/// Reaching this limit will result in a message_size error code. /// Reaching this limit will result in a message_size error code.
@ -522,7 +518,7 @@ namespace SimpleWeb {
void read(const std::shared_ptr<Session> &session) { void read(const std::shared_ptr<Session> &session) {
session->connection->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, std::size_t bytes_transferred) { asio::async_read_until(*session->connection->socket, session->request->streambuf, "\r\n\r\n", [this, session](const error_code &ec, std::size_t bytes_transferred) {
session->connection->cancel_timeout(); session->connection->set_timeout(config.timeout_content);
auto lock = session->connection->handler_runner->continue_lock(); auto lock = session->connection->handler_runner->continue_lock();
if(!lock) if(!lock)
return; return;
@ -562,9 +558,7 @@ namespace SimpleWeb {
return; return;
} }
if(content_length > num_additional_bytes) { if(content_length > num_additional_bytes) {
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, std::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, std::size_t /*bytes_transferred*/) {
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;
@ -599,9 +593,7 @@ namespace SimpleWeb {
} }
void read_chunked_transfer_encoded(const std::shared_ptr<Session> &session, const std::shared_ptr<asio::streambuf> &chunk_size_streambuf) { void read_chunked_transfer_encoded(const std::shared_ptr<Session> &session, const std::shared_ptr<asio::streambuf> &chunk_size_streambuf) {
session->connection->set_timeout(config.timeout_content);
asio::async_read_until(*session->connection->socket, *chunk_size_streambuf, "\r\n", [this, session, chunk_size_streambuf](const error_code &ec, size_t bytes_transferred) { asio::async_read_until(*session->connection->socket, *chunk_size_streambuf, "\r\n", [this, session, chunk_size_streambuf](const error_code &ec, size_t bytes_transferred) {
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;
@ -641,9 +633,7 @@ namespace SimpleWeb {
} }
if((2 + chunk_size) > num_additional_bytes) { if((2 + chunk_size) > num_additional_bytes) {
session->connection->set_timeout(config.timeout_content);
asio::async_read(*session->connection->socket, session->request->streambuf, asio::transfer_exactly(2 + chunk_size - num_additional_bytes), [this, session, chunk_size_streambuf, chunk_size](const error_code &ec, size_t /*bytes_transferred*/) { asio::async_read(*session->connection->socket, session->request->streambuf, asio::transfer_exactly(2 + chunk_size - num_additional_bytes), [this, session, chunk_size_streambuf, chunk_size](const error_code &ec, size_t /*bytes_transferred*/) {
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;
@ -716,10 +706,10 @@ namespace SimpleWeb {
void write(const std::shared_ptr<Session> &session, void write(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->connection->set_timeout(config.timeout_content); // Set timeout for first send
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_on_delete([this, response](const error_code &ec) { response->send_on_delete([this, response](const error_code &ec) {
response->session->connection->cancel_timeout();
if(!ec) { if(!ec) {
if(response->close_connection_after_response) if(response->close_connection_after_response)
return; return;