From 362c1a2591e116ea2e843a085e0b9053faa9d6ea Mon Sep 17 00:00:00 2001 From: eidheim Date: Sun, 25 Jun 2017 07:20:03 +0200 Subject: [PATCH] Now closes unwanted client connections when a request is finished. Also added additional tests --- client_http.hpp | 50 +++++++++++++++++++++++++++++++++++------------ tests/io_test.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 13 deletions(-) diff --git a/client_http.hpp b/client_http.hpp index 732ecfd..95f0381 100644 --- a/client_http.hpp +++ b/client_http.hpp @@ -165,11 +165,26 @@ namespace SimpleWeb { auto connection=session->connection; auto response=session->response; auto request_callback=std::make_shared, const error_code&)>>(std::move(request_callback_)); + auto connections=this->connections; auto connections_mutex=this->connections_mutex; - session->callback=[connection, response, request_callback, connections_mutex](const error_code &ec) { + session->callback=[connection, response, request_callback, connections, connections_mutex](const error_code &ec) { { std::lock_guard lock(*connections_mutex); connection->in_use=false; + + // Remove unused connections, but keep one open for HTTP persistent connection: + size_t unused_connections=0; + for(auto it=connections->begin();it!=connections->end();) { + if((*it)->in_use) + ++it; + else { + ++unused_connections; + if(unused_connections>1) + it=connections->erase(it); + else + ++it; + } + } } if(*request_callback) @@ -208,11 +223,26 @@ namespace SimpleWeb { auto connection=session->connection; auto response=session->response; auto request_callback=std::make_shared, const error_code&)>>(std::move(request_callback_)); + auto connections=this->connections; auto connections_mutex=this->connections_mutex; - session->callback=[connection, response, request_callback, connections_mutex](const error_code &ec) { + session->callback=[connection, response, request_callback, connections, connections_mutex](const error_code &ec) { { std::lock_guard lock(*connections_mutex); connection->in_use=false; + + // Remove unused connections, but keep one open for HTTP persistent connection: + size_t unused_connections=0; + for(auto it=connections->begin();it!=connections->end();) { + if((*it)->in_use) + ++it; + else { + ++unused_connections; + if(unused_connections>1) + it=connections->erase(it); + else + ++it; + } + } } if(*request_callback) @@ -242,10 +272,10 @@ namespace SimpleWeb { std::string host; unsigned short port; - std::vector> connections; + std::shared_ptr>> connections; std::shared_ptr connections_mutex; - ClientBase(const std::string& host_port, unsigned short default_port) : io_service(new asio::io_service()), connections_mutex(new std::mutex()) { + ClientBase(const std::string& host_port, unsigned short default_port) : io_service(new asio::io_service()), connections(new std::vector>()), connections_mutex(new std::mutex()) { auto parsed_host_port=parse_host_port(host_port, default_port); host=parsed_host_port.first; port=parsed_host_port.second; @@ -254,19 +284,15 @@ namespace SimpleWeb { std::shared_ptr get_connection() { std::shared_ptr connection; std::lock_guard lock(*connections_mutex); - for(auto it=connections.begin();it!=connections.end();) { - if((*it)->in_use) - ++it; - else if(!connection) { + for(auto it=connections->begin();it!=connections->end();++it) { + if(!(*it)->in_use && !connection) { connection=*it; - ++it; + break; } - else - it=connections.erase(it); } if(!connection) { connection=create_connection(); - connections.emplace_back(connection); + connections->emplace_back(connection); } connection->reconnecting=false; connection->in_use=true; diff --git a/tests/io_test.cpp b/tests/io_test.cpp index 4db55f2..ac3bbad 100644 --- a/tests/io_test.cpp +++ b/tests/io_test.cpp @@ -129,13 +129,16 @@ int main() { { HttpClient client("localhost:8080"); - // test performing the stream version of the request methods first + HttpClient::Connection *connection; { + // test performing the stream version of the request methods first stringstream output; stringstream content("A string"); auto r=client.request("POST", "/string", content); output << r->content.rdbuf(); assert(output.str()=="A string"); + assert(client.connections->size()==1); + connection=client.connections->front().get(); } { @@ -143,6 +146,8 @@ int main() { auto r=client.request("POST", "/string", "A string"); output << r->content.rdbuf(); assert(output.str()=="A string"); + assert(client.connections->size()==1); + assert(connection==client.connections->front().get()); } { @@ -150,6 +155,8 @@ int main() { auto r=client.request("GET", "/header", "", {{"test1", "test"}, {"test2", "ing"}}); output << r->content.rdbuf(); assert(output.str()=="testing"); + assert(client.connections->size()==1); + assert(connection==client.connections->front().get()); } } @@ -183,13 +190,51 @@ int main() { } for(auto &thread: threads) thread.join(); + assert(client.connections->size()==100); client.io_service->reset(); client.io_service->run(); + assert(client.connections->size()==1); for(auto call: calls) assert(call); } } + { + HttpClient client("localhost:8080"); + assert(client.connections->size()==0); + for(size_t c=0;c<5000;++c) { + auto r1=client.request("POST", "/string", "A string"); + assert(SimpleWeb::status_code(r1->status_code)==SimpleWeb::StatusCode::success_ok); + assert(r1->content.string()=="A string"); + assert(client.connections->size()==1); + + stringstream content("A string"); + auto r2 = client.request("POST", "/string", content); + assert(SimpleWeb::status_code(r2->status_code) == SimpleWeb::StatusCode::success_ok); + assert(r2->content.string() == "A string"); + assert(client.connections->size() == 1); + } + } + + for(size_t c=0;c<500;++c) { + { + HttpClient client("localhost:8080"); + auto r=client.request("POST", "/string", "A string"); + assert(SimpleWeb::status_code(r->status_code)==SimpleWeb::StatusCode::success_ok); + assert(r->content.string()=="A string"); + assert(client.connections->size()==1); + } + + { + HttpClient client("localhost:8080"); + stringstream content("A string"); + auto r = client.request("POST", "/string", content); + assert(SimpleWeb::status_code(r->status_code) == SimpleWeb::StatusCode::success_ok); + assert(r->content.string() == "A string"); + assert(client.connections->size() == 1); + } + } + server.stop(); server_thread.join();