@@ -305,7 +305,7 @@ namespace web { namespace http
305
305
long errorcodeValue = ec.value ();
306
306
307
307
// map timer cancellation to time_out
308
- if (ec == boost::system::errc::operation_canceled && m_timedout )
308
+ if (ec == boost::system::errc::operation_canceled && m_timer. has_timedout () )
309
309
{
310
310
errorcodeValue = make_error_code (std::errc::timed_out).value ();
311
311
}
@@ -339,25 +339,94 @@ namespace web { namespace http
339
339
request_context::report_error (errorcodeValue, message);
340
340
}
341
341
342
- void set_timer (const int secs)
342
+ // Simple timer class wrapping Boost deadline timer.
343
+ // Closes the connection when timer fires.
344
+ class timeout_timer
343
345
{
344
- m_timeout_timer.expires_from_now (boost::posix_time::milliseconds (secs * 1000 ));
345
- m_timeout_timer.async_wait (boost::bind (&linux_client_request_context::handle_timeout_timer, this , boost::asio::placeholders::error));
346
- }
346
+ public:
347
347
348
- void reset_timer (const int secs)
349
- {
350
- if (m_timeout_timer.expires_from_now (boost::posix_time::milliseconds (secs * 1000 )) > 0 )
348
+ timeout_timer (int seconds) :
349
+ m_timer (crossplat::threadpool::shared_instance().service()),
350
+ m_duration (boost::posix_time::milliseconds(seconds * 1000 )),
351
+ m_state (created)
352
+ {}
353
+
354
+ void set_ctx (const std::weak_ptr<linux_client_request_context> &ctx)
351
355
{
352
- m_timeout_timer.async_wait (boost::bind (&linux_client_request_context::handle_timeout_timer, this , boost::asio::placeholders::error));
356
+ m_ctx = ctx;
357
+ }
358
+
359
+ void start ()
360
+ {
361
+ assert (m_state == created);
362
+ assert (!m_ctx.expired ());
363
+ m_state = started;
364
+
365
+ m_timer.expires_from_now (m_duration);
366
+ auto ctx = m_ctx;
367
+ m_timer.async_wait ([ctx](const boost::system::error_code& ec)
368
+ {
369
+ handle_timeout (ec, ctx);
370
+ });
371
+ }
372
+
373
+ void reset ()
374
+ {
375
+ assert (m_state == started || m_state == timedout);
376
+ assert (!m_ctx.expired ());
377
+ if (m_timer.expires_from_now (m_duration) > 0 )
378
+ {
379
+ auto ctx = m_ctx;
380
+ m_timer.async_wait ([ctx](const boost::system::error_code& ec)
381
+ {
382
+ handle_timeout (ec, ctx);
383
+ });
384
+ }
385
+ }
386
+
387
+ bool has_timedout () const { return m_state == timedout; }
388
+
389
+ void stop ()
390
+ {
391
+ m_state = stopped;
392
+ m_timer.cancel ();
353
393
}
354
- }
394
+
395
+ private:
396
+
397
+ static void handle_timeout (
398
+ const boost::system::error_code& ec,
399
+ const std::weak_ptr<linux_client_request_context> &ctx)
400
+ {
401
+ if (!ec)
402
+ {
403
+ auto shared_ctx = ctx.lock ();
404
+ if (shared_ctx)
405
+ {
406
+ shared_ctx->m_timer .m_state = timedout;
407
+ shared_ctx->m_connection ->close ();
408
+ }
409
+ }
410
+ }
411
+
412
+ enum timer_state
413
+ {
414
+ created,
415
+ started,
416
+ stopped,
417
+ timedout
418
+ };
419
+
420
+ boost::posix_time::milliseconds m_duration;
421
+ timer_state m_state;
422
+ std::weak_ptr<linux_client_request_context> m_ctx;
423
+ boost::asio::deadline_timer m_timer;
424
+ };
355
425
356
426
uint64_t m_known_size;
357
427
bool m_needChunked;
358
- bool m_timedout ;
428
+ timeout_timer m_timer ;
359
429
boost::asio::streambuf m_body_buf;
360
- boost::asio::deadline_timer m_timeout_timer;
361
430
std::shared_ptr<linux_connection> m_connection;
362
431
363
432
#if defined(__APPLE__) || defined(ANDROID)
@@ -366,19 +435,6 @@ namespace web { namespace http
366
435
367
436
virtual ~linux_client_request_context ();
368
437
369
- void handle_timeout_timer (const boost::system::error_code& ec)
370
- {
371
- if (!ec)
372
- {
373
- m_timedout = true ;
374
- auto error (m_connection->cancel ());
375
- if (error)
376
- {
377
- report_error (" Failed to cancel the socket" , error);
378
- }
379
- }
380
- }
381
-
382
438
linux_client_request_context (
383
439
const std::shared_ptr<_http_client_communicator> &client,
384
440
http_request &request,
@@ -473,7 +529,8 @@ namespace web { namespace http
473
529
// Enforce HTTP connection keep alive (even for the old HTTP/1.0 protocol).
474
530
request_stream << " Connection: Keep-Alive" << CRLF << CRLF;
475
531
476
- ctx->set_timer (static_cast <int >(client_config ().timeout ().count ()));
532
+ // Start connection timeout timer.
533
+ ctx->m_timer .start ();
477
534
478
535
if (ctx->m_connection ->is_open ())
479
536
{
@@ -521,6 +578,7 @@ namespace web { namespace http
521
578
}
522
579
else
523
580
{
581
+ ctx->m_timer .reset ();
524
582
auto endpoint = *endpoints;
525
583
ctx->m_connection ->async_connect (endpoint, boost::bind (&linux_client::handle_connect, shared_from_this (), boost::asio::placeholders::error, ++endpoints, ctx));
526
584
}
@@ -544,6 +602,7 @@ namespace web { namespace http
544
602
545
603
void handle_connect (const boost::system::error_code& ec, tcp::resolver::iterator endpoints, const std::shared_ptr<linux_client_request_context> &ctx)
546
604
{
605
+ ctx->m_timer .reset ();
547
606
if (!ec)
548
607
{
549
608
write_request (ctx);
@@ -554,8 +613,6 @@ namespace web { namespace http
554
613
}
555
614
else
556
615
{
557
- ctx->m_timeout_timer .cancel ();
558
-
559
616
// Replace the connection. This causes old connection object to go out of scope.
560
617
ctx->m_connection = m_pool.obtain ();
561
618
@@ -614,6 +671,7 @@ namespace web { namespace http
614
671
return handle_write_body (ec, ctx);
615
672
}
616
673
674
+ ctx->m_timer .reset ();
617
675
auto progress = ctx->m_request ._get_impl ()->_progress_handler ();
618
676
if (progress)
619
677
{
@@ -671,6 +729,7 @@ namespace web { namespace http
671
729
return handle_write_body (ec, ctx);
672
730
}
673
731
732
+ ctx->m_timer .reset ();
674
733
auto progress = ctx->m_request ._get_impl ()->_progress_handler ();
675
734
if (progress)
676
735
{
@@ -718,6 +777,7 @@ namespace web { namespace http
718
777
}
719
778
else
720
779
{
780
+ ctx->m_timer .reset ();
721
781
if (ctx->m_needChunked )
722
782
{
723
783
handle_write_chunked_body (ec, ctx);
@@ -733,6 +793,7 @@ namespace web { namespace http
733
793
{
734
794
if (!ec)
735
795
{
796
+ ctx->m_timer .reset ();
736
797
auto progress = ctx->m_request ._get_impl ()->_progress_handler ();
737
798
if (progress)
738
799
{
@@ -760,6 +821,8 @@ namespace web { namespace http
760
821
{
761
822
if (!ec)
762
823
{
824
+ ctx->m_timer .reset ();
825
+
763
826
std::istream response_stream (&ctx->m_body_buf );
764
827
std::string http_version;
765
828
response_stream >> http_version;
@@ -902,6 +965,8 @@ namespace web { namespace http
902
965
{
903
966
if (!ec)
904
967
{
968
+ ctx->m_timer .reset ();
969
+
905
970
std::istream response_stream (&ctx->m_body_buf );
906
971
std::string line;
907
972
std::getline (response_stream, line);
@@ -930,6 +995,8 @@ namespace web { namespace http
930
995
{
931
996
if (!ec)
932
997
{
998
+ ctx->m_timer .reset ();
999
+
933
1000
ctx->m_downloaded += static_cast <uint64_t >(to_read);
934
1001
auto progress = ctx->m_request ._get_impl ()->_progress_handler ();
935
1002
if (progress)
@@ -964,8 +1031,6 @@ namespace web { namespace http
964
1031
}
965
1032
else
966
1033
{
967
- ctx->reset_timer (static_cast <int >(client_config ().timeout ().count ()));
968
-
969
1034
auto writeBuffer = ctx->_get_writebuffer ();
970
1035
writeBuffer.putn (boost::asio::buffer_cast<const uint8_t *>(ctx->m_body_buf .data ()), to_read)
971
1036
.then ([=](pplx::task<size_t > op)
@@ -1008,6 +1073,7 @@ namespace web { namespace http
1008
1073
}
1009
1074
}
1010
1075
1076
+ ctx->m_timer .reset ();
1011
1077
auto progress = ctx->m_request ._get_impl ()->_progress_handler ();
1012
1078
if (progress)
1013
1079
{
@@ -1024,8 +1090,6 @@ namespace web { namespace http
1024
1090
1025
1091
if (ctx->m_downloaded < ctx->m_known_size )
1026
1092
{
1027
- ctx->reset_timer (static_cast <int >(client_config ().timeout ().count ()));
1028
-
1029
1093
// more data need to be read
1030
1094
writeBuffer.putn (boost::asio::buffer_cast<const uint8_t *>(ctx->m_body_buf .data ()),
1031
1095
static_cast <size_t >(std::min (static_cast <uint64_t >(ctx->m_body_buf .size ()), ctx->m_known_size - ctx->m_downloaded )))
@@ -1090,8 +1154,7 @@ namespace web { namespace http
1090
1154
: request_context(client, request)
1091
1155
, m_known_size(0 )
1092
1156
, m_needChunked(false )
1093
- , m_timedout(false )
1094
- , m_timeout_timer(crossplat::threadpool::shared_instance().service())
1157
+ , m_timer(static_cast <int >(client->client_config ().timeout().count()))
1095
1158
, m_connection(connection)
1096
1159
#if defined(__APPLE__) || defined(ANDROID)
1097
1160
, m_openssl_failed(false )
@@ -1103,12 +1166,14 @@ namespace web { namespace http
1103
1166
{
1104
1167
auto client_cast (std::static_pointer_cast<linux_client>(client));
1105
1168
auto connection (client_cast->m_pool .obtain ());
1106
- return std::make_shared<linux_client_request_context>(client, request, connection);
1169
+ auto ctx = std::make_shared<linux_client_request_context>(client, request, connection);
1170
+ ctx->m_timer .set_ctx (std::weak_ptr<linux_client_request_context>(ctx));
1171
+ return ctx;
1107
1172
}
1108
1173
1109
1174
linux_client_request_context::~linux_client_request_context ()
1110
1175
{
1111
- m_timeout_timer. cancel ();
1176
+ m_timer. stop ();
1112
1177
// Release connection back to the pool. If connection was not closed, it will be put to the pool for reuse.
1113
1178
std::static_pointer_cast<linux_client>(m_http_client)->m_pool .release (m_connection);
1114
1179
}
0 commit comments