diff --git a/client_http.hpp b/client_http.hpp index b52a209..c200ff2 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -33,7 +33,7 @@ namespace SimpleWeb { class Client; template - class ClientBase { + class ClientBase : public std::enable_shared_from_this> { ClientBase(const ClientBase &) = delete; ClientBase &operator=(const ClientBase &) = delete; @@ -92,32 +92,18 @@ namespace SimpleWeb { protected: class Connection { public: - Connection(const std::string &host, unsigned short port, const Config &config, std::unique_ptr &&socket) - : host(host), port(port), config(config), socket(std::move(socket)) { - if(config.proxy_server.empty()) - query = std::unique_ptr(new asio::ip::tcp::resolver::query(host, std::to_string(port))); - else { - auto proxy_host_port = parse_host_port(config.proxy_server, 8080); - query = std::unique_ptr(new asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); - } - } - - std::string host; - unsigned short port; - Config config; + Connection(std::unique_ptr &&socket) : socket(std::move(socket)) {} std::unique_ptr socket; bool in_use = false; bool reconnecting = false; - - std::unique_ptr query; }; class Session { public: - Session(const std::shared_ptr &io_service, const std::shared_ptr &connection, std::unique_ptr &&request_buffer) - : io_service(io_service), connection(connection), request_buffer(std::move(request_buffer)), response(new Response()) {} - std::shared_ptr io_service; + Session(std::shared_ptr> &&self, const std::shared_ptr &connection, std::unique_ptr &&request_buffer) + : self(std::move(self)), connection(connection), request_buffer(std::move(request_buffer)), response(new Response()) {} + std::shared_ptr> self; std::shared_ptr connection; std::unique_ptr request_buffer; std::shared_ptr response; @@ -171,7 +157,7 @@ namespace SimpleWeb { /// Asynchronous request where setting and/or running Client's io_service is required. void request(const std::string &method, const std::string &path, string_view content, const CaseInsensitiveMultimap &header, std::function &, const error_code &)> &&request_callback_) { - auto session = std::make_shared(io_service, get_connection(), create_request_header(method, path, header)); + auto session = std::make_shared(this->shared_from_this(), get_connection(), create_request_header(method, path, header)); auto connection = session->connection; auto response = session->response; auto request_callback = std::make_shared &, const error_code &)>>(std::move(request_callback_)); @@ -207,7 +193,7 @@ namespace SimpleWeb { write_stream << "\r\n" << content; - Client::connect(session); + connect(session); } /// Asynchronous request where setting and/or running Client's io_service is required. @@ -230,7 +216,7 @@ namespace SimpleWeb { /// 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, std::function &, const error_code &)> &&request_callback_) { - auto session = std::make_shared(io_service, get_connection(), create_request_header(method, path, header)); + auto session = std::make_shared(this->shared_from_this(), get_connection(), create_request_header(method, path, header)); auto connection = session->connection; auto response = session->response; auto request_callback = std::make_shared &, const error_code &)>>(std::move(request_callback_)); @@ -270,7 +256,7 @@ namespace SimpleWeb { if(content_length > 0) write_stream << content.rdbuf(); - Client::connect(session); + connect(session); } /// Asynchronous request where setting and/or running Client's io_service is required. @@ -283,6 +269,8 @@ namespace SimpleWeb { std::string host; unsigned short port; + std::unique_ptr query; + std::shared_ptr>> connections; std::shared_ptr connections_mutex; @@ -308,10 +296,21 @@ namespace SimpleWeb { } connection->reconnecting = false; connection->in_use = true; + + if(!query) { + if(config.proxy_server.empty()) + query = std::unique_ptr(new asio::ip::tcp::resolver::query(host, std::to_string(port))); + else { + auto proxy_host_port = parse_host_port(config.proxy_server, 8080); + query = std::unique_ptr(new asio::ip::tcp::resolver::query(proxy_host_port.first, std::to_string(proxy_host_port.second))); + } + } + return connection; } virtual std::shared_ptr create_connection() = 0; + virtual void connect(std::shared_ptr &) = 0; std::unique_ptr create_request_header(const std::string &method, const std::string &path, const CaseInsensitiveMultimap &header) const { auto corrected_path = path; @@ -329,7 +328,7 @@ namespace SimpleWeb { return request_buffer; } - static std::pair parse_host_port(const std::string &host_port, unsigned short default_port) { + std::pair parse_host_port(const std::string &host_port, unsigned short default_port) const { std::pair parsed_host_port; size_t host_end = host_port.find(':'); if(host_end == std::string::npos) { @@ -343,22 +342,22 @@ namespace SimpleWeb { return parsed_host_port; } - static std::shared_ptr get_timeout_timer(std::shared_ptr &session, size_t timeout = 0) { + std::shared_ptr get_timeout_timer(std::shared_ptr &session, size_t timeout = 0) { if(timeout == 0) - timeout = session->connection->config.timeout; + timeout = config.timeout; if(timeout == 0) return nullptr; - auto timer = std::make_shared(*session->io_service); + auto timer = std::make_shared(*io_service); timer->expires_from_now(boost::posix_time::seconds(timeout)); - timer->async_wait([session](const error_code &ec) mutable { + timer->async_wait([this, session](const error_code &ec) mutable { if(!ec) - close(session); + this->close(session); }); return timer; } - static void parse_response_header(std::shared_ptr &response) { + void parse_response_header(std::shared_ptr &response) const { std::string line; getline(response->content, line); size_t version_end = line.find(' '); @@ -384,23 +383,23 @@ namespace SimpleWeb { } } - static void write(std::shared_ptr &session) { + void write(std::shared_ptr &session) { auto timer = get_timeout_timer(session); - asio::async_write(*session->connection->socket, session->request_buffer->data(), [session, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { + 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(); if(!ec) - read(session); + this->read(session); else { - close(session); + this->close(session); session->callback(ec); } }); } - static void read(std::shared_ptr &session) { + 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", [session, timer](const error_code &ec, size_t bytes_transferred) mutable { + 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(); if(!ec) { @@ -408,20 +407,20 @@ namespace SimpleWeb { size_t num_additional_bytes = session->response->content_buffer.size() - bytes_transferred; - parse_response_header(session->response); + this->parse_response_header(session->response); auto header_it = session->response->header.find("Content-Length"); if(header_it != session->response->header.end()) { auto content_length = stoull(header_it->second); if(content_length > num_additional_bytes) { - auto timer = get_timeout_timer(session); - asio::async_read(*session->connection->socket, session->response->content_buffer, asio::transfer_exactly(content_length - num_additional_bytes), [session, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { + 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(); if(!ec) session->callback(ec); else { - close(session); + this->close(session); session->callback(ec); } }); @@ -431,17 +430,17 @@ namespace SimpleWeb { } else if((header_it = session->response->header.find("Transfer-Encoding")) != session->response->header.end() && header_it->second == "chunked") { auto tmp_streambuf = std::make_shared(); - 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")) { - auto timer = get_timeout_timer(session); - asio::async_read(*session->connection->socket, session->response->content_buffer, [session, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { + 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(); if(!ec) session->callback(ec); else { - close(session); + this->close(session); if(ec == asio::error::eof) { error_code ec; session->callback(ec); @@ -457,20 +456,20 @@ namespace SimpleWeb { else { if(!session->connection->reconnecting) { session->connection->reconnecting = true; - close(session); - Client::connect(session); + this->close(session); + this->connect(session); } else { - close(session); + this->close(session); session->callback(ec); } } }); } - static void read_chunked(std::shared_ptr &session, std::shared_ptr &tmp_streambuf) { + 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", [session, tmp_streambuf, timer](const error_code &ec, size_t bytes_transferred) mutable { + 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(); if(!ec) { @@ -482,7 +481,7 @@ namespace SimpleWeb { auto num_additional_bytes = static_cast(session->response->content_buffer.size() - bytes_transferred); - auto post_process = [session, tmp_streambuf, length]() mutable { + auto post_process = [this, session, tmp_streambuf, length]() mutable { std::ostream tmp_stream(tmp_streambuf.get()); if(length > 0) { std::vector buffer(static_cast(length)); @@ -495,7 +494,7 @@ namespace SimpleWeb { session->response->content.get(); if(length > 0) - read_chunked(session, tmp_streambuf); + this->read_chunked(session, tmp_streambuf); else { std::ostream response_stream(&session->response->content_buffer); response_stream << tmp_stream.rdbuf(); @@ -505,14 +504,14 @@ namespace SimpleWeb { }; if((2 + length) > num_additional_bytes) { - auto timer = get_timeout_timer(session); - asio::async_read(*session->connection->socket, session->response->content_buffer, asio::transfer_exactly(2 + length - num_additional_bytes), [session, post_process, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { + 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(); if(!ec) post_process(); else { - close(session); + this->close(session); session->callback(ec); } }); @@ -521,13 +520,13 @@ namespace SimpleWeb { post_process(); } else { - close(session); + this->close(session); session->callback(ec); } }); } - static void close(std::shared_ptr &session) { + 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); @@ -554,34 +553,35 @@ namespace SimpleWeb { Client(const std::string &server_port_path) : ClientBase::ClientBase(server_port_path, 80) {} std::shared_ptr create_connection() override { - return std::make_shared(host, port, config, std::unique_ptr(new HTTP(*io_service))); + return std::make_shared(std::unique_ptr(new HTTP(*io_service))); } - static void connect(std::shared_ptr &session) { + void connect(std::shared_ptr &session) override { if(!session->connection->socket->lowest_layer().is_open()) { - auto resolver = std::make_shared(*session->io_service); - auto timer = get_timeout_timer(session, session->connection->config.timeout_connect); - resolver->async_resolve(*session->connection->query, [session, timer, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) mutable { + 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(); if(!ec) { - auto timer = get_timeout_timer(session, session->connection->config.timeout_connect); - asio::async_connect(*session->connection->socket, it, [session, timer, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) mutable { + 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(); if(!ec) { asio::ip::tcp::no_delay option(true); session->connection->socket->set_option(option); - write(session); + this->write(session); } else { - close(session); + this->close(session); session->callback(ec); } }); } else { - close(session); + this->close(session); session->callback(ec); } }); diff --git a/client_https.hpp b/client_https.hpp index 7acbc22..c1577c2 100644 --- a/client_https.hpp +++ b/client_https.hpp @@ -50,70 +50,70 @@ namespace SimpleWeb { asio::ssl::context context; std::shared_ptr create_connection() override { - return std::make_shared(host, port, config, std::unique_ptr(new HTTPS(*io_service, context))); + return std::make_shared(std::unique_ptr(new HTTPS(*io_service, context))); } - static void connect(std::shared_ptr &session) { + void connect(std::shared_ptr &session) override { if(!session->connection->socket->lowest_layer().is_open()) { - auto resolver = std::make_shared(*session->io_service); - resolver->async_resolve(*session->connection->query, [session, resolver](const error_code &ec, asio::ip::tcp::resolver::iterator it) mutable { + 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 = get_timeout_timer(session, session->connection->config.timeout_connect); - asio::async_connect(session->connection->socket->lowest_layer(), it, [session, resolver, timer](const error_code &ec, asio::ip::tcp::resolver::iterator /*it*/) mutable { + 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(); if(!ec) { asio::ip::tcp::no_delay option(true); session->connection->socket->lowest_layer().set_option(option); - if(!session->connection->config.proxy_server.empty()) { + if(!this->config.proxy_server.empty()) { auto write_buffer = std::make_shared(); std::ostream write_stream(write_buffer.get()); - auto host_port = session->connection->host + ':' + std::to_string(session->connection->port); + 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 = get_timeout_timer(session, session->connection->config.timeout_connect); - asio::async_write(session->connection->socket->next_layer(), *write_buffer, [session, write_buffer, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { + 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(); if(!ec) { std::shared_ptr response(new Response()); - auto timer = get_timeout_timer(session, session->connection->config.timeout_connect); - asio::async_read_until(session->connection->socket->next_layer(), response->content_buffer, "\r\n\r\n", [session, response, timer](const error_code &ec, size_t /*bytes_transferred*/) mutable { + 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(); if(!ec) { - parse_response_header(response); + this->parse_response_header(response); if(response->status_code.empty() || response->status_code.compare(0, 3, "200") != 0) { - close(session); + this->close(session); session->callback(make_error_code::make_error_code(errc::permission_denied)); } else - handshake(session); + this->handshake(session); } else { - close(session); + this->close(session); session->callback(ec); } }); } else { - close(session); + this->close(session); session->callback(ec); } }); } else - handshake(session); + this->handshake(session); } else { - close(session); + this->close(session); session->callback(ec); } }); } else { - close(session); + this->close(session); session->callback(ec); } }); @@ -122,15 +122,15 @@ namespace SimpleWeb { write(session); } - static void handshake(std::shared_ptr &session) { - auto timer = get_timeout_timer(session, session->connection->config.timeout_connect); - session->connection->socket->async_handshake(asio::ssl::stream_base::client, [session, timer](const error_code &ec) mutable { + 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(); if(!ec) - write(session); + this->write(session); else { - close(session); + this->close(session); session->callback(ec); } }); diff --git a/tests/parse_test.cpp b/tests/parse_test.cpp index 61c08e9..3cf10d6 100644 --- a/tests/parse_test.cpp +++ b/tests/parse_test.cpp @@ -10,7 +10,7 @@ class ServerTest : public ServerBase { public: ServerTest() : ServerBase::ServerBase(8080) {} - void accept() {} + void accept() override {} void parse_request_test() { HTTP socket(*io_service); @@ -59,6 +59,8 @@ public: return nullptr; } + void connect(std::shared_ptr &) override {} + void constructor_parse_test1() { assert(host == "test.org"); assert(port == 8080);