@@ -80,7 +80,7 @@ class asio_connection
80
80
asio_connection (boost::asio::io_service& io_service, bool start_with_ssl, const std::function<void (boost::asio::ssl::context&)>& ssl_context_callback) :
81
81
m_socket (io_service),
82
82
m_ssl_context_callback (ssl_context_callback),
83
- m_pool_timer (io_service),
83
+ m_connection_timer (io_service),
84
84
m_is_reused (false ),
85
85
m_keep_alive (true )
86
86
{
@@ -103,7 +103,10 @@ class asio_connection
103
103
boost::asio::ssl::context ssl_context (boost::asio::ssl::context::sslv23);
104
104
ssl_context.set_default_verify_paths ();
105
105
ssl_context.set_options (boost::asio::ssl::context::default_workarounds);
106
- m_ssl_context_callback (ssl_context);
106
+ if (m_ssl_context_callback)
107
+ {
108
+ m_ssl_context_callback (ssl_context);
109
+ }
107
110
m_ssl_stream = utility::details::make_unique<boost::asio::ssl::stream<boost::asio::ip::tcp::socket &>>(m_socket, ssl_context);
108
111
}
109
112
@@ -127,9 +130,9 @@ class asio_connection
127
130
return error;
128
131
}
129
132
130
- void cancel_pool_timer ()
133
+ void cancel_connection_timer ()
131
134
{
132
- m_pool_timer .cancel ();
135
+ m_connection_timer .cancel ();
133
136
}
134
137
135
138
bool is_reused () const { return m_is_reused; }
@@ -218,15 +221,15 @@ class asio_connection
218
221
219
222
private:
220
223
template <typename TimeoutHandler>
221
- void start_pool_timer (int timeout_secs, const TimeoutHandler &handler)
224
+ void start_connection_timer (int timeout_secs, const TimeoutHandler &handler)
222
225
{
223
- m_pool_timer .expires_from_now (boost::posix_time::milliseconds (timeout_secs * 1000 ));
224
- m_pool_timer .async_wait (handler);
226
+ m_connection_timer .expires_from_now (boost::posix_time::milliseconds (timeout_secs * 1000 ));
227
+ m_connection_timer .async_wait (handler);
225
228
}
226
229
227
230
void start_reuse ()
228
231
{
229
- cancel_pool_timer ();
232
+ cancel_connection_timer ();
230
233
m_is_reused = true ;
231
234
}
232
235
@@ -239,7 +242,7 @@ class asio_connection
239
242
240
243
std::function<void (boost::asio::ssl::context&)> m_ssl_context_callback;
241
244
242
- boost::asio::deadline_timer m_pool_timer ;
245
+ boost::asio::deadline_timer m_connection_timer ;
243
246
bool m_is_reused;
244
247
bool m_keep_alive;
245
248
};
@@ -252,7 +255,10 @@ class asio_connection_pool
252
255
m_io_service (io_service),
253
256
m_timeout_secs (static_cast <int >(idle_timeout.count())),
254
257
m_start_with_ssl (start_with_ssl),
255
- m_ssl_context_callback (ssl_context_callback)
258
+ m_ssl_context_callback (ssl_context_callback),
259
+ m_pool_timeout_secs (60 ), // Clean this connection pool 60 secs after the last asio_client release it.
260
+ m_pool_timer (io_service),
261
+ m_use_count (0 )
256
262
{}
257
263
258
264
~asio_connection_pool ()
@@ -261,10 +267,24 @@ class asio_connection_pool
261
267
// Cancel the pool timer for all connections.
262
268
for (auto & connection : m_connections)
263
269
{
264
- connection->cancel_pool_timer ();
270
+ connection->cancel_connection_timer ();
265
271
}
266
272
}
267
273
274
+ template <typename TimeoutHandler>
275
+ void start_pool_timer (const TimeoutHandler &handler)
276
+ {
277
+ // std::lock_guard<std::mutex> lg(m_pool_timer_mutex);
278
+ m_pool_timer.expires_from_now (boost::posix_time::milliseconds (m_pool_timeout_secs * 1000 ));
279
+ m_pool_timer.async_wait (handler);
280
+ }
281
+
282
+ void cancel_pool_timer ()
283
+ {
284
+ // std::lock_guard<std::mutex> lg(m_pool_timer_mutex);
285
+ m_pool_timer.cancel ();
286
+ }
287
+
268
288
void release (const std::shared_ptr<asio_connection> &connection)
269
289
{
270
290
if (connection->keep_alive () && (m_timeout_secs > 0 ))
@@ -274,7 +294,7 @@ class asio_connection_pool
274
294
std::lock_guard<std::mutex> lock (m_connections_mutex);
275
295
// This will destroy and remove the connection from pool after the set timeout.
276
296
// We use 'this' because async calls to timer handler only occur while the pool exists.
277
- connection->start_pool_timer (m_timeout_secs, boost::bind (&asio_connection_pool::handle_pool_timer , this , boost::asio::placeholders::error, connection));
297
+ connection->start_connection_timer (m_timeout_secs, boost::bind (&asio_connection_pool::handle_connection_timer , this , boost::asio::placeholders::error, connection));
278
298
m_connections.push_back (connection);
279
299
}
280
300
// Otherwise connection is not put to the pool and it will go out of scope.
@@ -302,10 +322,15 @@ class asio_connection_pool
302
322
}
303
323
}
304
324
325
+ int &use_count ()
326
+ {
327
+ return m_use_count;
328
+ }
329
+
305
330
private:
306
331
307
332
// Using weak_ptr here ensures bind() to this handler will not prevent the connection object from going out of scope.
308
- void handle_pool_timer (const boost::system::error_code& ec, const std::weak_ptr<asio_connection> &connection)
333
+ void handle_connection_timer (const boost::system::error_code& ec, const std::weak_ptr<asio_connection> &connection)
309
334
{
310
335
if (!ec)
311
336
{
@@ -328,6 +353,12 @@ class asio_connection_pool
328
353
std::function<void (boost::asio::ssl::context&)> m_ssl_context_callback;
329
354
std::vector<std::shared_ptr<asio_connection> > m_connections;
330
355
std::mutex m_connections_mutex;
356
+
357
+ const int m_pool_timeout_secs;
358
+ std::mutex m_pool_timer_mutex;
359
+ boost::asio::deadline_timer m_pool_timer;
360
+ // std::atomic<int> m_use_count;
361
+ int m_use_count;
331
362
};
332
363
333
364
@@ -348,32 +379,56 @@ class asio_client : public _http_client_communicator
348
379
}
349
380
else
350
381
{
351
- std::string host = base_uri ().to_string ();
382
+ m_pool_key = base_uri ().to_string ();
352
383
353
384
auto &credentials = _http_client_communicator::client_config ().credentials ();
354
385
if (credentials.is_set ())
355
386
{
356
- host .append (credentials.username ());
387
+ m_pool_key .append (credentials.username ());
357
388
}
358
389
359
390
auto &proxy = _http_client_communicator::client_config ().proxy ();
360
391
if (proxy.is_specified ())
361
392
{
362
- host .append (proxy.address ().to_string ());
393
+ m_pool_key .append (proxy.address ().to_string ());
363
394
if (proxy.credentials ().is_set ())
364
395
{
365
- host .append (proxy.credentials ().username ());
396
+ m_pool_key .append (proxy.credentials ().username ());
366
397
}
367
398
}
368
399
369
- m_pool = crossplat::threadpool::shared_instance ().obtain_connection_pool (host , [this ]()
400
+ m_pool = crossplat::threadpool::shared_instance ().obtain_connection_pool (m_pool_key , [this ]()
370
401
{
371
402
return std::make_shared<asio_connection_pool>(crossplat::threadpool::shared_instance ().service (),
372
403
base_uri ().scheme () == " https" && !_http_client_communicator::client_config ().proxy ().is_specified (),
373
404
std::chrono::seconds (30 ), // Unused sockets are kept in pool for 30 seconds.
374
- this ->client_config ().get_ssl_context_callback ());
405
+ nullptr );
406
+ });
407
+
408
+ if (m_pool->use_count () == 0 )
409
+ {
410
+ m_pool->cancel_pool_timer ();
411
+ }
412
+ ++m_pool->use_count ();
413
+ }
414
+ }
415
+
416
+ ~asio_client ()
417
+ {
418
+ if (!m_pool_key.empty ())
419
+ {
420
+ crossplat::threadpool::shared_instance ().release_connection_pool (m_pool_key, [this ](std::shared_ptr<web::http::client::details::asio_connection_pool> pool)
421
+ {
422
+ if (pool)
423
+ {
424
+ --pool->use_count ();
425
+ if (pool->use_count () == 0 )
426
+ {
427
+ pool->start_pool_timer (boost::bind (&crossplat::threadpool::free_connection_pool, &crossplat::threadpool::shared_instance (), boost::asio::placeholders::error, m_pool_key));
428
+ }
429
+ }
375
430
});
376
- }
431
+ }
377
432
}
378
433
379
434
void send_request (const std::shared_ptr<request_context> &request_ctx) override ;
@@ -384,6 +439,7 @@ class asio_client : public _http_client_communicator
384
439
385
440
std::shared_ptr<asio_connection_pool> m_pool;
386
441
tcp::resolver m_resolver;
442
+ std::string m_pool_key;
387
443
};
388
444
389
445
class asio_context : public request_context , public std ::enable_shared_from_this<asio_context>
@@ -509,7 +565,7 @@ class asio_context : public request_context, public std::enable_shared_from_this
509
565
{
510
566
m_context->report_error (" Failed to send connect request to proxy." , err, httpclient_errorcode_context::writebody);
511
567
}
512
- }
568
+ }
513
569
514
570
void handle_status_line (const boost::system::error_code& ec)
515
571
{
0 commit comments