diff --git a/client_http.hpp b/client_http.hpp index c200ff2..7e2a008 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -82,9 +82,9 @@ namespace SimpleWeb { public: /// Set timeout on requests in seconds. Default value: 0 (no timeout). - size_t timeout = 0; + long timeout = 0; /// Set connect timeout in seconds. Default value: 0 (Config::timeout is then used instead). - size_t timeout_connect = 0; + long timeout_connect = 0; /// Set proxy server (server:port) std::string proxy_server; }; @@ -97,6 +97,12 @@ namespace SimpleWeb { std::unique_ptr socket; bool in_use = false; bool reconnecting = false; + + void close() { + error_code ec; + socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket->lowest_layer().close(ec); + } }; class Session { @@ -108,6 +114,30 @@ namespace SimpleWeb { std::unique_ptr request_buffer; std::shared_ptr response; std::function callback; + + std::unique_ptr timer; + + void set_timeout(long seconds = 0) { + if(seconds == 0) + seconds = self->config.timeout; + if(seconds == 0) { + timer = nullptr; + return; + } + + auto timer = std::unique_ptr(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: @@ -342,21 +372,6 @@ namespace SimpleWeb { return parsed_host_port; } - std::shared_ptr get_timeout_timer(std::shared_ptr &session, size_t timeout = 0) { - if(timeout == 0) - timeout = config.timeout; - if(timeout == 0) - return nullptr; - - auto timer = std::make_shared(*io_service); - timer->expires_from_now(boost::posix_time::seconds(timeout)); - timer->async_wait([this, session](const error_code &ec) mutable { - if(!ec) - this->close(session); - }); - return timer; - } - void parse_response_header(std::shared_ptr &response) const { std::string line; getline(response->content, line); @@ -384,24 +399,22 @@ namespace SimpleWeb { } void write(std::shared_ptr &session) { - auto timer = get_timeout_timer(session); - asio::async_write(*session->connection->socket, session->request_buffer->data(), [this, session, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { - if(timer) - timer->cancel(); + session->set_timeout(); + asio::async_write(*session->connection->socket, session->request_buffer->data(), [this, session](const error_code &ec, size_t /*bytes_transferred*/) mutable { + session->cancel_timeout(); if(!ec) this->read(session); else { - this->close(session); + session->connection->close(); session->callback(ec); } }); } void read(std::shared_ptr &session) { - auto timer = get_timeout_timer(session); - asio::async_read_until(*session->connection->socket, session->response->content_buffer, "\r\n\r\n", [this, session, timer](const error_code &ec, size_t bytes_transferred) mutable { - if(timer) - timer->cancel(); + session->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) mutable { + session->cancel_timeout(); if(!ec) { session->connection->reconnecting = false; @@ -413,14 +426,13 @@ namespace SimpleWeb { if(header_it != session->response->header.end()) { auto content_length = stoull(header_it->second); if(content_length > num_additional_bytes) { - auto timer = this->get_timeout_timer(session); - asio::async_read(*session->connection->socket, session->response->content_buffer, asio::transfer_exactly(content_length - num_additional_bytes), [this, session, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { - if(timer) - timer->cancel(); + session->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*/) mutable { + session->cancel_timeout(); if(!ec) session->callback(ec); else { - this->close(session); + session->connection->close(); session->callback(ec); } }); @@ -433,14 +445,13 @@ namespace SimpleWeb { 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")) { - auto timer = this->get_timeout_timer(session); - asio::async_read(*session->connection->socket, session->response->content_buffer, [this, session, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { - if(timer) - timer->cancel(); + session->set_timeout(); + asio::async_read(*session->connection->socket, session->response->content_buffer, [this, session](const error_code &ec, size_t /*bytes_transferred*/) mutable { + session->cancel_timeout(); if(!ec) session->callback(ec); else { - this->close(session); + session->connection->close(); if(ec == asio::error::eof) { error_code ec; session->callback(ec); @@ -456,11 +467,11 @@ namespace SimpleWeb { else { if(!session->connection->reconnecting) { session->connection->reconnecting = true; - this->close(session); + session->connection->close(); this->connect(session); } else { - this->close(session); + session->connection->close(); session->callback(ec); } } @@ -468,10 +479,9 @@ namespace SimpleWeb { } void read_chunked(std::shared_ptr &session, std::shared_ptr &tmp_streambuf) { - auto timer = get_timeout_timer(session); - asio::async_read_until(*session->connection->socket, session->response->content_buffer, "\r\n", [this, session, tmp_streambuf, timer](const error_code &ec, size_t bytes_transferred) mutable { - if(timer) - timer->cancel(); + session->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) mutable { + session->cancel_timeout(); if(!ec) { std::string line; getline(session->response->content, line); @@ -504,14 +514,13 @@ namespace SimpleWeb { }; if((2 + length) > num_additional_bytes) { - auto timer = this->get_timeout_timer(session); - asio::async_read(*session->connection->socket, session->response->content_buffer, asio::transfer_exactly(2 + length - num_additional_bytes), [this, session, post_process, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { - if(timer) - timer->cancel(); + session->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*/) mutable { + session->cancel_timeout(); if(!ec) post_process(); else { - this->close(session); + session->connection->close(); session->callback(ec); } }); @@ -520,17 +529,11 @@ namespace SimpleWeb { post_process(); } else { - this->close(session); + session->connection->close(); session->callback(ec); } }); } - - void close(std::shared_ptr &session) { - error_code ec; - session->connection->socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec); - session->connection->socket->lowest_layer().close(ec); - } }; template @@ -559,29 +562,26 @@ namespace SimpleWeb { void connect(std::shared_ptr &session) override { if(!session->connection->socket->lowest_layer().is_open()) { auto resolver = std::make_shared(*io_service); - auto timer = get_timeout_timer(session, config.timeout_connect); - - resolver->async_resolve(*query, [this, session, timer, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) mutable { - if(timer) - timer->cancel(); + session->set_timeout(config.timeout_connect); + resolver->async_resolve(*query, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) mutable { + session->cancel_timeout(); if(!ec) { - auto timer = this->get_timeout_timer(session, this->config.timeout_connect); - asio::async_connect(*session->connection->socket, it, [this, session, timer, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) mutable { - if(timer) - timer->cancel(); + session->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*/) mutable { + session->cancel_timeout(); if(!ec) { asio::ip::tcp::no_delay option(true); session->connection->socket->set_option(option); this->write(session); } else { - this->close(session); + session->connection->close(); session->callback(ec); } }); } else { - this->close(session); + session->connection->close(); session->callback(ec); } }); diff --git a/client_https.hpp b/client_https.hpp index c1577c2..dd3b480 100644 --- a/client_https.hpp +++ b/client_https.hpp @@ -58,10 +58,9 @@ namespace SimpleWeb { auto resolver = std::make_shared(*io_service); resolver->async_resolve(*query, [this, session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) mutable { if(!ec) { - auto timer = this->get_timeout_timer(session, this->config.timeout_connect); - asio::async_connect(session->connection->socket->lowest_layer(), it, [this, session, resolver, timer](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) mutable { - if(timer) - timer->cancel(); + session->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*/) mutable { + session->cancel_timeout(); if(!ec) { asio::ip::tcp::no_delay option(true); session->connection->socket->lowest_layer().set_option(option); @@ -72,33 +71,31 @@ namespace SimpleWeb { auto host_port = this->host + ':' + std::to_string(this->port); write_stream << "CONNECT " + host_port + " HTTP/1.1\r\n" << "Host: " << host_port << "\r\n\r\n"; - auto timer = this->get_timeout_timer(session, this->config.timeout_connect); - asio::async_write(session->connection->socket->next_layer(), *write_buffer, [this, session, write_buffer, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { - if(timer) - timer->cancel(); + session->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*/) mutable { + session->cancel_timeout(); if(!ec) { std::shared_ptr response(new Response()); - auto timer = this->get_timeout_timer(session, this->config.timeout_connect); - asio::async_read_until(session->connection->socket->next_layer(), response->content_buffer, "\r\n\r\n", [this, session, response, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { - if(timer) - timer->cancel(); + session->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*/) mutable { + session->cancel_timeout(); if(!ec) { this->parse_response_header(response); if(response->status_code.empty() || response->status_code.compare(0, 3, "200") != 0) { - this->close(session); + session->connection->close(); session->callback(make_error_code::make_error_code(errc::permission_denied)); } else this->handshake(session); } else { - this->close(session); + session->connection->close(); session->callback(ec); } }); } else { - this->close(session); + session->connection->close(); session->callback(ec); } }); @@ -107,13 +104,13 @@ namespace SimpleWeb { this->handshake(session); } else { - this->close(session); + session->connection->close(); session->callback(ec); } }); } else { - this->close(session); + session->connection->close(); session->callback(ec); } }); @@ -123,14 +120,13 @@ namespace SimpleWeb { } void handshake(std::shared_ptr &session) { - auto timer = get_timeout_timer(session, config.timeout_connect); - session->connection->socket->async_handshake(asio::ssl::stream_base::client, [this, session, timer](const error_code &ec) mutable { - if(timer) - timer->cancel(); + session->set_timeout(this->config.timeout_connect); + session->connection->socket->async_handshake(asio::ssl::stream_base::client, [this, session](const error_code &ec) mutable { + session->cancel_timeout(); if(!ec) this->write(session); else { - this->close(session); + session->connection->close(); session->callback(ec); } }); diff --git a/server_http.hpp b/server_http.hpp index 57d4a00..a0271ac 100644 --- a/server_http.hpp +++ b/server_http.hpp @@ -201,6 +201,31 @@ namespace SimpleWeb { std::shared_ptr> self; std::shared_ptr socket; std::shared_ptr request; + + std::shared_ptr timer; + + void set_timeout(long seconds) { + if(seconds == 0) { + timer = nullptr; + return; + } + + timer = std::make_shared(socket->get_io_service()); + timer->expires_from_now(boost::posix_time::seconds(seconds)); + auto socket = this->socket; + timer->async_wait([socket](const error_code &ec) { + if(!ec) { + error_code ec; + socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec); + socket->lowest_layer().close(); + } + }); + } + + void cancel_timeout() { + if(timer) + timer->cancel(); + } }; public: @@ -216,9 +241,9 @@ namespace SimpleWeb { /// Defaults to 1 thread. size_t thread_pool_size = 1; /// Timeout on request handling. Defaults to 5 seconds. - size_t timeout_request = 5; + long timeout_request = 5; /// Timeout on content handling. Defaults to 300 seconds. - size_t timeout_content = 300; + long timeout_content = 300; /// IPv4 address in dotted decimal form or IPv6 address in hexadecimal notation. /// If empty, the address will be any address. std::string address; @@ -320,29 +345,10 @@ namespace SimpleWeb { virtual void accept() = 0; - std::shared_ptr get_timeout_timer(std::shared_ptr &session, long seconds) { - if(seconds == 0) - return nullptr; - - auto timer = std::make_shared(*io_service); - timer->expires_from_now(boost::posix_time::seconds(seconds)); - timer->async_wait([session](const error_code &ec) mutable { - if(!ec) { - error_code ec; - session->socket->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec); - session->socket->lowest_layer().close(); - } - }); - return timer; - } - void read_request_and_content(std::shared_ptr &session) { - //Set timeout on the following asio::async-read or write function - auto timer = get_timeout_timer(session, config.timeout_request); - - asio::async_read_until(*session->socket, session->request->streambuf, "\r\n\r\n", [this, session, timer](const error_code &ec, size_t bytes_transferred) mutable { - if(timer) - timer->cancel(); + session->set_timeout(config.timeout_request); + asio::async_read_until(*session->socket, session->request->streambuf, "\r\n\r\n", [this, session](const error_code &ec, size_t bytes_transferred) mutable { + session->cancel_timeout(); if(!ec) { //request->streambuf.size() is not necessarily the same as bytes_transferred, from Boost-docs: //"After a successful async_read_until operation, the streambuf may contain additional data beyond the delimiter" @@ -366,11 +372,9 @@ namespace SimpleWeb { return; } if(content_length > num_additional_bytes) { - //Set timeout on the following asio::async-read or write function - auto timer = this->get_timeout_timer(session, config.timeout_content); - asio::async_read(*session->socket, session->request->streambuf, asio::transfer_exactly(content_length - num_additional_bytes), [this, session, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { - if(timer) - timer->cancel(); + session->set_timeout(config.timeout_content); + asio::async_read(*session->socket, session->request->streambuf, asio::transfer_exactly(content_length - num_additional_bytes), [this, session](const error_code &ec, size_t /*bytes_transferred*/) mutable { + session->cancel_timeout(); if(!ec) this->find_resource(session); else if(this->on_error) @@ -472,11 +476,10 @@ namespace SimpleWeb { void write_response(std::shared_ptr &session, std::function::Response> &, std::shared_ptr::Request> &)> &resource_function) { - //Set timeout on the following asio::async-read or write function - auto timer = get_timeout_timer(session, config.timeout_content); - auto self = session->self; auto request = session->request; + session->set_timeout(config.timeout_content); + auto timer = session->timer; auto response = std::shared_ptr(new Response(session->socket), [self, request, timer](Response *response_ptr) mutable { auto response = std::shared_ptr(response_ptr); self->send(response, [self, response, request, timer](const error_code &ec) mutable { diff --git a/server_https.hpp b/server_https.hpp index 73d9980..8620003 100644 --- a/server_https.hpp +++ b/server_https.hpp @@ -69,11 +69,9 @@ namespace SimpleWeb { asio::ip::tcp::no_delay option(true); session->socket->lowest_layer().set_option(option); - //Set timeout on the following asio::ssl::stream::async_handshake - auto timer = this->get_timeout_timer(session, config.timeout_request); - session->socket->async_handshake(asio::ssl::stream_base::server, [this, session, timer](const error_code &ec) mutable { - if(timer) - timer->cancel(); + session->set_timeout(config.timeout_request); + session->socket->async_handshake(asio::ssl::stream_base::server, [this, session](const error_code &ec) mutable { + session->cancel_timeout(); if(!ec) this->read_request_and_content(session); else if(this->on_error)