@@ -64,9 +64,16 @@ namespace web { namespace http
6464 m_keep_alive (true )
6565 {}
6666
67+ ~linux_connection ()
68+ {
69+ close ();
70+ }
71+
6772 void close ()
6873 {
74+ // Ensures closed connections owned by request_context will not be put to pool when they are released.
6975 m_keep_alive = false ;
76+
7077 boost::system::error_code error;
7178 m_socket.shutdown (tcp::socket::shutdown_both, error);
7279 m_socket.close (error);
@@ -124,12 +131,10 @@ namespace web { namespace http
124131 ~linux_connection_pool ()
125132 {
126133 std::lock_guard<std::mutex> lock (m_connections_mutex);
127- // Close all connections. This happens when linux_client is destroyed because
128- // it only has shared_ptr reference to pool. Connections use weak_ptr instead.
134+ // Cancel the pool timer for all connections.
129135 for (auto & connection : m_connections)
130136 {
131137 connection->cancel_pool_timer ();
132- connection->close ();
133138 }
134139 }
135140
@@ -138,27 +143,20 @@ namespace web { namespace http
138143 if (connection->keep_alive () && (m_timeout_secs > 0 ))
139144 {
140145 connection->cancel ();
141- // Remove idle connections from pool after timeout.
146+ // This will destroy and remove the connection from pool after the set timeout.
147+ // We use 'this' because async calls to timer handler only occur while the pool exists.
142148 connection->start_pool_timer (m_timeout_secs, boost::bind (&linux_connection_pool::handle_pool_timer, this , boost::asio::placeholders::error, connection));
143149
144- {
145- std::lock_guard<std::mutex> lock (m_connections_mutex);
146- m_connections.insert (connection);
147- }
148- }
149- else
150- {
151- // Connection is not returned to pool => will be destroyed.
152- connection->close ();
150+ put_to_pool (connection);
153151 }
152+ // Otherwise connection is not put to the pool and it will go out of scope.
154153 }
155154
156155 std::shared_ptr<linux_connection> obtain ()
157156 {
158157 if (is_pool_empty ())
159158 {
160- // No connections in pool => create new connection instance.
161- // shared_from_this() is only to create a weak_ptr to pool.
159+ // No connections in pool => create a new connection instance.
162160 return std::make_shared<linux_connection>(m_io_service);
163161 }
164162 else
@@ -176,11 +174,15 @@ namespace web { namespace http
176174 m_connections.erase (connection);
177175 }
178176
179- void handle_pool_timer (const boost::system::error_code& ec, std::shared_ptr<linux_connection> connection)
177+ // Using weak_ptr here ensures bind() to this handler will not prevent the connection object from going out of scope.
178+ void handle_pool_timer (const boost::system::error_code& ec, std::weak_ptr<linux_connection> connection)
180179 {
181180 if (!ec)
182181 {
183- remove (connection);
182+ if (auto connection_shared = connection.lock ())
183+ {
184+ remove (connection_shared);
185+ }
184186 }
185187 }
186188
@@ -199,6 +201,12 @@ namespace web { namespace http
199201 return connection;
200202 }
201203
204+ void put_to_pool (std::shared_ptr<linux_connection> connection)
205+ {
206+ std::lock_guard<std::mutex> lock (m_connections_mutex);
207+ m_connections.insert (connection);
208+ }
209+
202210 boost::asio::io_service& m_io_service;
203211 const int m_timeout_secs;
204212 std::unordered_set<std::shared_ptr<linux_connection> > m_connections;
@@ -425,9 +433,9 @@ namespace web { namespace http
425433 {
426434 ctx->m_cancellationRegistration = request_ctx->m_request ._cancellation_token ().register_callback ([ctx]()
427435 {
428- // Cancel operations and all async handlers.
436+ // Cancel operations and all asio async handlers.
429437 ctx->m_connection ->cancel ();
430- // Shut down transmissions and close socket. Also prevents connection being pooled.
438+ // Shut down transmissions, close the socket and prevent connection from being pooled.
431439 ctx->m_connection ->close ();
432440 });
433441 }
@@ -508,8 +516,8 @@ namespace web { namespace http
508516 {
509517 ctx->m_timeout_timer .cancel ();
510518
511- ctx-> m_connection -> close ();
512- ctx->m_connection = m_pool.obtain ();
519+ // Replace the connection. This causes old connection object to go out of scope.
520+ ctx->m_connection = m_pool.obtain ();
513521
514522 auto endpoint = *endpoints;
515523 if (ctx->m_ssl_stream )
@@ -761,16 +769,18 @@ namespace web { namespace http
761769 || (boost::asio::error::connection_aborted == ec));
762770 if (socket_was_closed && ctx->m_connection ->is_reused () && ctx->m_connection ->socket ().is_open ())
763771 {
764- // Connection was closed while connection was in pool.
765- // Ensure connection is closed in a robust way.
772+ // Failed to write to socket because connection was already closed while it was in the pool.
773+ // close() here ensures socket is closed in a robust way and prevents the connection from being put to the pool again .
766774 ctx->m_connection ->close ();
767775
768- // Replace context and destroy the old one. The request,
769- // completion event and cancellation registration are copied to
770- // the new context. This will also obtain a new connection.
776+ // Create a new context and copy the requst object, completion event and
777+ // cancellation registration to maintain the old state.
778+ // This also obtains a new connection from pool .
771779 auto new_ctx = details::linux_client_request_context::create_request_context (ctx->m_http_client , ctx->m_request );
772780 new_ctx->m_request_completion = ctx->m_request_completion ;
773781 new_ctx->m_cancellationRegistration = ctx->m_cancellationRegistration ;
782+
783+ // Replace context with the new one. This causes the old context and connection to go out of scope.
774784 ctx = std::static_pointer_cast<linux_client_request_context>(new_ctx);
775785
776786 // Resend the request using the new context.
@@ -1103,7 +1113,7 @@ namespace web { namespace http
11031113 linux_client_request_context::~linux_client_request_context ()
11041114 {
11051115 m_timeout_timer.cancel ();
1106- // Give connection back to the pool where it can be reused .
1116+ // Release connection back to the pool. If connection was not closed, it will be put to the pool for reuse .
11071117 std::static_pointer_cast<linux_client>(m_http_client)->m_pool .release (m_connection);
11081118 }
11091119
0 commit comments