@@ -64,9 +64,16 @@ namespace web { namespace http
64
64
m_keep_alive (true )
65
65
{}
66
66
67
+ ~linux_connection ()
68
+ {
69
+ close ();
70
+ }
71
+
67
72
void close ()
68
73
{
74
+ // Ensures closed connections owned by request_context will not be put to pool when they are released.
69
75
m_keep_alive = false ;
76
+
70
77
boost::system::error_code error;
71
78
m_socket.shutdown (tcp::socket::shutdown_both, error);
72
79
m_socket.close (error);
@@ -124,12 +131,10 @@ namespace web { namespace http
124
131
~linux_connection_pool ()
125
132
{
126
133
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.
129
135
for (auto & connection : m_connections)
130
136
{
131
137
connection->cancel_pool_timer ();
132
- connection->close ();
133
138
}
134
139
}
135
140
@@ -138,27 +143,20 @@ namespace web { namespace http
138
143
if (connection->keep_alive () && (m_timeout_secs > 0 ))
139
144
{
140
145
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.
142
148
connection->start_pool_timer (m_timeout_secs, boost::bind (&linux_connection_pool::handle_pool_timer, this , boost::asio::placeholders::error, connection));
143
149
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);
153
151
}
152
+ // Otherwise connection is not put to the pool and it will go out of scope.
154
153
}
155
154
156
155
std::shared_ptr<linux_connection> obtain ()
157
156
{
158
157
if (is_pool_empty ())
159
158
{
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.
162
160
return std::make_shared<linux_connection>(m_io_service);
163
161
}
164
162
else
@@ -176,11 +174,15 @@ namespace web { namespace http
176
174
m_connections.erase (connection);
177
175
}
178
176
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)
180
179
{
181
180
if (!ec)
182
181
{
183
- remove (connection);
182
+ if (auto connection_shared = connection.lock ())
183
+ {
184
+ remove (connection_shared);
185
+ }
184
186
}
185
187
}
186
188
@@ -199,6 +201,12 @@ namespace web { namespace http
199
201
return connection;
200
202
}
201
203
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
+
202
210
boost::asio::io_service& m_io_service;
203
211
const int m_timeout_secs;
204
212
std::unordered_set<std::shared_ptr<linux_connection> > m_connections;
@@ -425,9 +433,9 @@ namespace web { namespace http
425
433
{
426
434
ctx->m_cancellationRegistration = request_ctx->m_request ._cancellation_token ().register_callback ([ctx]()
427
435
{
428
- // Cancel operations and all async handlers.
436
+ // Cancel operations and all asio async handlers.
429
437
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.
431
439
ctx->m_connection ->close ();
432
440
});
433
441
}
@@ -508,8 +516,8 @@ namespace web { namespace http
508
516
{
509
517
ctx->m_timeout_timer .cancel ();
510
518
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 ();
513
521
514
522
auto endpoint = *endpoints;
515
523
if (ctx->m_ssl_stream )
@@ -761,16 +769,18 @@ namespace web { namespace http
761
769
|| (boost::asio::error::connection_aborted == ec));
762
770
if (socket_was_closed && ctx->m_connection ->is_reused () && ctx->m_connection ->socket ().is_open ())
763
771
{
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 .
766
774
ctx->m_connection ->close ();
767
775
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 .
771
779
auto new_ctx = details::linux_client_request_context::create_request_context (ctx->m_http_client , ctx->m_request );
772
780
new_ctx->m_request_completion = ctx->m_request_completion ;
773
781
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.
774
784
ctx = std::static_pointer_cast<linux_client_request_context>(new_ctx);
775
785
776
786
// Resend the request using the new context.
@@ -1103,7 +1113,7 @@ namespace web { namespace http
1103
1113
linux_client_request_context::~linux_client_request_context ()
1104
1114
{
1105
1115
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 .
1107
1117
std::static_pointer_cast<linux_client>(m_http_client)->m_pool .release (m_connection);
1108
1118
}
1109
1119
0 commit comments