@@ -127,6 +127,8 @@ namespace web { namespace http
127
127
~linux_connection_pool ()
128
128
{
129
129
std::lock_guard<std::mutex> lock (m_connections_mutex);
130
+ // Close all connections. This happens when linux_client is destroyed because
131
+ // it only has shared_ptr reference to pool. Connections use weak_ptr instead.
130
132
for (auto & connection : m_connections)
131
133
{
132
134
connection->close (true );
@@ -155,15 +157,16 @@ namespace web { namespace http
155
157
156
158
std::shared_ptr<linux_connection> obtain ()
157
159
{
158
- if (is_connections_empty ())
160
+ if (is_pool_empty ())
159
161
{
160
162
// No connections in pool => create new connection instance.
163
+ // shared_from_this() is only to create a weak_ptr to pool.
161
164
return std::make_shared<linux_connection>(shared_from_this (), m_io_service);
162
165
}
163
166
else
164
167
{
165
168
// Reuse connection from pool.
166
- auto connection (obtain_begin ());
169
+ auto connection (get_head ());
167
170
connection->start_reuse ();
168
171
return connection;
169
172
}
@@ -176,13 +179,13 @@ namespace web { namespace http
176
179
}
177
180
178
181
private:
179
- bool is_connections_empty ()
182
+ bool is_pool_empty ()
180
183
{
181
184
std::lock_guard<std::mutex> lock (m_connections_mutex);
182
185
return m_connections.empty ();
183
186
}
184
187
185
- std::shared_ptr<linux_connection> obtain_begin ()
188
+ std::shared_ptr<linux_connection> get_head ()
186
189
{
187
190
std::lock_guard<std::mutex> lock (m_connections_mutex);
188
191
auto connection (*m_connections.begin ());
@@ -349,10 +352,10 @@ namespace web { namespace http
349
352
}
350
353
request_stream << " :" << port << CRLF;
351
354
352
- // Manipulate headers
355
+ // Copy headers to keep the original request state intact.
353
356
auto headers (ctx->m_request .headers ());
354
357
355
- // Check user specified transfer-encoding
358
+ // Check user specified transfer-encoding.
356
359
std::string transferencoding;
357
360
if (headers.match (header_names::transfer_encoding, transferencoding) && transferencoding == " chunked" )
358
361
{
@@ -363,6 +366,7 @@ namespace web { namespace http
363
366
364
367
if (headers.match (header_names::content_length, ctx->m_known_size ))
365
368
{
369
+ // Have request body if content length header field is non-zero.
366
370
has_body = (0 != ctx->m_known_size );
367
371
}
368
372
else
@@ -387,17 +391,21 @@ namespace web { namespace http
387
391
}
388
392
389
393
request_stream << flatten_http_headers (headers);
394
+ // Enforce HTTP connection keep alive (even for the old HTTP/1.0 protocol).
390
395
request_stream << " Connection: Keep-Alive" << CRLF;
391
396
request_stream << CRLF;
392
397
393
398
ctx->set_timer (static_cast <int >(client_config ().timeout ().count ()));
394
399
395
400
if (ctx->m_connection ->socket ().is_open ())
396
401
{
402
+ // If socket is already open (connection is reused), try to write the request directly.
397
403
write_request (ctx);
398
404
}
399
405
else
400
406
{
407
+ // If the connection is new (unresolved and unconnected socket), then start async
408
+ // call to resolve first, leading eventually to request write.
401
409
tcp::resolver::query query (host, utility::conversions::print_string (port));
402
410
403
411
m_resolver.async_resolve (query, boost::bind (&linux_client::handle_resolve, shared_from_this (), boost::asio::placeholders::error, boost::asio::placeholders::iterator, ctx));
@@ -408,7 +416,9 @@ namespace web { namespace http
408
416
{
409
417
ctx->m_cancellationRegistration = request_ctx->m_request ._cancellation_token ().register_callback ([ctx]()
410
418
{
419
+ // Cancel operations and all async handlers.
411
420
ctx->m_connection ->cancel ();
421
+ // Shut down transmissions and close socket. Also prevents connection being pooled.
412
422
ctx->m_connection ->close (false );
413
423
});
414
424
}
@@ -742,17 +752,19 @@ namespace web { namespace http
742
752
|| (boost::asio::error::connection_aborted == ec));
743
753
if (socket_was_closed && ctx->m_connection ->is_reused () && ctx->m_connection ->socket ().is_open ())
744
754
{
745
- // Connection was closed by the server for some reason during the connection was
746
- // being pooled. We re-send the request to get a new connection .
755
+ // Connection was closed while connection was in pool.
756
+ // Ensure connection is closed in a robust way .
747
757
ctx->m_connection ->close (false );
748
758
759
+ // Replace context and destroy the old one. The request,
760
+ // completion event and cancellation registration are copied to
761
+ // the new context. This will also obtain a new connection.
749
762
auto new_ctx = details::linux_client_request_context::create_request_context (ctx->m_http_client , ctx->m_request );
750
763
new_ctx->m_request_completion = ctx->m_request_completion ;
751
764
new_ctx->m_cancellationRegistration = ctx->m_cancellationRegistration ;
752
-
753
- // Close and destroy and the old socket.
754
765
ctx = std::static_pointer_cast<linux_client_request_context>(new_ctx);
755
766
767
+ // Resend the request using the new context.
756
768
send_request (ctx);
757
769
}
758
770
else
@@ -1082,6 +1094,7 @@ namespace web { namespace http
1082
1094
linux_client_request_context::~linux_client_request_context ()
1083
1095
{
1084
1096
m_timeout_timer.cancel ();
1097
+ // Give connection back to the pool where it can be reused.
1085
1098
std::static_pointer_cast<linux_client>(m_http_client)->m_pool ->release (m_connection);
1086
1099
}
1087
1100
@@ -1091,8 +1104,11 @@ namespace web { namespace http
1091
1104
{
1092
1105
if (auto pool_ptr = m_pool_weak.lock ())
1093
1106
{
1107
+ // Remove connection from pool only if pool still exists (lock succeeds).
1094
1108
pool_ptr->remove (shared_from_this ());
1095
1109
}
1110
+ // If lock fails, pool has been destroyed and we let connection object
1111
+ // to expire via shared_ptr. This should happen when this method returns.
1096
1112
}
1097
1113
}
1098
1114
0 commit comments