Skip to content

Commit 834eeab

Browse files
committed
Created timeout_timer class for Boost deadline timer.
1 parent 69c96ec commit 834eeab

File tree

1 file changed

+101
-36
lines changed

1 file changed

+101
-36
lines changed

Release/src/http/client/http_linux.cpp

Lines changed: 101 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ namespace web { namespace http
305305
long errorcodeValue = ec.value();
306306

307307
// 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())
309309
{
310310
errorcodeValue = make_error_code(std::errc::timed_out).value();
311311
}
@@ -339,25 +339,94 @@ namespace web { namespace http
339339
request_context::report_error(errorcodeValue, message);
340340
}
341341

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
343345
{
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:
347347

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)
351355
{
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();
353393
}
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+
};
355425

356426
uint64_t m_known_size;
357427
bool m_needChunked;
358-
bool m_timedout;
428+
timeout_timer m_timer;
359429
boost::asio::streambuf m_body_buf;
360-
boost::asio::deadline_timer m_timeout_timer;
361430
std::shared_ptr<linux_connection> m_connection;
362431

363432
#if defined(__APPLE__) || defined(ANDROID)
@@ -366,19 +435,6 @@ namespace web { namespace http
366435

367436
virtual ~linux_client_request_context();
368437

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-
382438
linux_client_request_context(
383439
const std::shared_ptr<_http_client_communicator> &client,
384440
http_request &request,
@@ -473,7 +529,8 @@ namespace web { namespace http
473529
// Enforce HTTP connection keep alive (even for the old HTTP/1.0 protocol).
474530
request_stream << "Connection: Keep-Alive" << CRLF << CRLF;
475531

476-
ctx->set_timer(static_cast<int>(client_config().timeout().count()));
532+
// Start connection timeout timer.
533+
ctx->m_timer.start();
477534

478535
if (ctx->m_connection->is_open())
479536
{
@@ -521,6 +578,7 @@ namespace web { namespace http
521578
}
522579
else
523580
{
581+
ctx->m_timer.reset();
524582
auto endpoint = *endpoints;
525583
ctx->m_connection->async_connect(endpoint, boost::bind(&linux_client::handle_connect, shared_from_this(), boost::asio::placeholders::error, ++endpoints, ctx));
526584
}
@@ -544,6 +602,7 @@ namespace web { namespace http
544602

545603
void handle_connect(const boost::system::error_code& ec, tcp::resolver::iterator endpoints, const std::shared_ptr<linux_client_request_context> &ctx)
546604
{
605+
ctx->m_timer.reset();
547606
if (!ec)
548607
{
549608
write_request(ctx);
@@ -554,8 +613,6 @@ namespace web { namespace http
554613
}
555614
else
556615
{
557-
ctx->m_timeout_timer.cancel();
558-
559616
// Replace the connection. This causes old connection object to go out of scope.
560617
ctx->m_connection = m_pool.obtain();
561618

@@ -614,6 +671,7 @@ namespace web { namespace http
614671
return handle_write_body(ec, ctx);
615672
}
616673

674+
ctx->m_timer.reset();
617675
auto progress = ctx->m_request._get_impl()->_progress_handler();
618676
if (progress)
619677
{
@@ -671,6 +729,7 @@ namespace web { namespace http
671729
return handle_write_body(ec, ctx);
672730
}
673731

732+
ctx->m_timer.reset();
674733
auto progress = ctx->m_request._get_impl()->_progress_handler();
675734
if (progress)
676735
{
@@ -718,6 +777,7 @@ namespace web { namespace http
718777
}
719778
else
720779
{
780+
ctx->m_timer.reset();
721781
if (ctx->m_needChunked)
722782
{
723783
handle_write_chunked_body(ec, ctx);
@@ -733,6 +793,7 @@ namespace web { namespace http
733793
{
734794
if (!ec)
735795
{
796+
ctx->m_timer.reset();
736797
auto progress = ctx->m_request._get_impl()->_progress_handler();
737798
if (progress)
738799
{
@@ -760,6 +821,8 @@ namespace web { namespace http
760821
{
761822
if (!ec)
762823
{
824+
ctx->m_timer.reset();
825+
763826
std::istream response_stream(&ctx->m_body_buf);
764827
std::string http_version;
765828
response_stream >> http_version;
@@ -902,6 +965,8 @@ namespace web { namespace http
902965
{
903966
if (!ec)
904967
{
968+
ctx->m_timer.reset();
969+
905970
std::istream response_stream(&ctx->m_body_buf);
906971
std::string line;
907972
std::getline(response_stream, line);
@@ -930,6 +995,8 @@ namespace web { namespace http
930995
{
931996
if (!ec)
932997
{
998+
ctx->m_timer.reset();
999+
9331000
ctx->m_downloaded += static_cast<uint64_t>(to_read);
9341001
auto progress = ctx->m_request._get_impl()->_progress_handler();
9351002
if (progress)
@@ -964,8 +1031,6 @@ namespace web { namespace http
9641031
}
9651032
else
9661033
{
967-
ctx->reset_timer(static_cast<int>(client_config().timeout().count()));
968-
9691034
auto writeBuffer = ctx->_get_writebuffer();
9701035
writeBuffer.putn(boost::asio::buffer_cast<const uint8_t *>(ctx->m_body_buf.data()), to_read)
9711036
.then([=](pplx::task<size_t> op)
@@ -1008,6 +1073,7 @@ namespace web { namespace http
10081073
}
10091074
}
10101075

1076+
ctx->m_timer.reset();
10111077
auto progress = ctx->m_request._get_impl()->_progress_handler();
10121078
if (progress)
10131079
{
@@ -1024,8 +1090,6 @@ namespace web { namespace http
10241090

10251091
if (ctx->m_downloaded < ctx->m_known_size)
10261092
{
1027-
ctx->reset_timer(static_cast<int>(client_config().timeout().count()));
1028-
10291093
// more data need to be read
10301094
writeBuffer.putn(boost::asio::buffer_cast<const uint8_t *>(ctx->m_body_buf.data()),
10311095
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
10901154
: request_context(client, request)
10911155
, m_known_size(0)
10921156
, 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()))
10951158
, m_connection(connection)
10961159
#if defined(__APPLE__) || defined(ANDROID)
10971160
, m_openssl_failed(false)
@@ -1103,12 +1166,14 @@ namespace web { namespace http
11031166
{
11041167
auto client_cast(std::static_pointer_cast<linux_client>(client));
11051168
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;
11071172
}
11081173

11091174
linux_client_request_context::~linux_client_request_context()
11101175
{
1111-
m_timeout_timer.cancel();
1176+
m_timer.stop();
11121177
// Release connection back to the pool. If connection was not closed, it will be put to the pool for reuse.
11131178
std::static_pointer_cast<linux_client>(m_http_client)->m_pool.release(m_connection);
11141179
}

0 commit comments

Comments
 (0)