mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-16 00:55:49 +00:00
Fix off by one in pending i/o count on HTTPClient
This commit is contained in:
@@ -92,7 +92,6 @@ public:
|
|||||||
boost::asio::io_service io_service;
|
boost::asio::io_service io_service;
|
||||||
async_get (io_service, nullptr, url);
|
async_get (io_service, nullptr, url);
|
||||||
io_service.run ();
|
io_service.run ();
|
||||||
cancel ();
|
|
||||||
return result ();
|
return result ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,6 +223,7 @@ private:
|
|||||||
, m_context (boost::asio::ssl::context::sslv23)
|
, m_context (boost::asio::ssl::context::sslv23)
|
||||||
, m_buffer (bufferSize)
|
, m_buffer (bufferSize)
|
||||||
, m_parser (HTTPParser::typeResponse)
|
, m_parser (HTTPParser::typeResponse)
|
||||||
|
, m_timer_set (false)
|
||||||
, m_timer_canceled (false)
|
, m_timer_canceled (false)
|
||||||
, m_timer_expired (false)
|
, m_timer_expired (false)
|
||||||
, m_messageLimitBytes (messageLimitBytes)
|
, m_messageLimitBytes (messageLimitBytes)
|
||||||
@@ -241,6 +241,7 @@ private:
|
|||||||
m_timer.expires_from_now (
|
m_timer.expires_from_now (
|
||||||
boost::posix_time::milliseconds (
|
boost::posix_time::milliseconds (
|
||||||
long (timeoutSeconds * 1000)));
|
long (timeoutSeconds * 1000)));
|
||||||
|
m_timer_set = true;
|
||||||
|
|
||||||
++m_io_pending;
|
++m_io_pending;
|
||||||
m_timer.async_wait (TimerHandler (this));
|
m_timer.async_wait (TimerHandler (this));
|
||||||
@@ -261,7 +262,11 @@ private:
|
|||||||
//
|
//
|
||||||
void cancel ()
|
void cancel ()
|
||||||
{
|
{
|
||||||
cancel_all ();
|
cancel_timer ();
|
||||||
|
m_resolver.cancel ();
|
||||||
|
error_code ec;
|
||||||
|
m_socket.close (ec);
|
||||||
|
|
||||||
m_done.wait ();
|
m_done.wait ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +275,7 @@ private:
|
|||||||
|
|
||||||
// Counts a pending i/o as canceled
|
// Counts a pending i/o as canceled
|
||||||
//
|
//
|
||||||
void cancel_io ()
|
void io_canceled ()
|
||||||
{
|
{
|
||||||
bassert (m_io_pending.get () > 0);
|
bassert (m_io_pending.get () > 0);
|
||||||
if (--m_io_pending == 0)
|
if (--m_io_pending == 0)
|
||||||
@@ -280,23 +285,42 @@ private:
|
|||||||
// Cancels the deadline timer.
|
// Cancels the deadline timer.
|
||||||
//
|
//
|
||||||
void cancel_timer ()
|
void cancel_timer ()
|
||||||
|
{
|
||||||
|
// Make sure the timer was set (versus infinite timeout)
|
||||||
|
if (m_timer_set)
|
||||||
|
{
|
||||||
|
// See if it was already canceled.
|
||||||
|
if (! m_timer_canceled)
|
||||||
{
|
{
|
||||||
m_timer_canceled = true;
|
m_timer_canceled = true;
|
||||||
error_code ec;
|
error_code ec;
|
||||||
m_timer.cancel (ec);
|
m_timer.cancel (ec);
|
||||||
|
|
||||||
|
// At this point, there will either be a pending completion
|
||||||
|
// or a pending abort for the handler. If its a completion,
|
||||||
|
// they will see that the timer was canceled (since we're on
|
||||||
|
// a strand, everything is serialized). If its an abort it
|
||||||
|
// counts as a cancellation anyway. Either way, we will deduct
|
||||||
|
// one i/o from the pending i/o count.
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called to notify the original handler the operation is complete.
|
// Called to notify the original handler the operation is complete.
|
||||||
//
|
//
|
||||||
void complete (error_code const& ec)
|
void complete (error_code const& ec)
|
||||||
{
|
{
|
||||||
|
// Set the error code in the result.
|
||||||
m_owner.m_result.error = ec;
|
m_owner.m_result.error = ec;
|
||||||
|
|
||||||
|
// Cancel the deadline timer. This ensures that
|
||||||
|
// we will not return 'timeout' to the caller later.
|
||||||
|
//
|
||||||
cancel_timer ();
|
cancel_timer ();
|
||||||
|
|
||||||
bassert (m_io_pending.get () > 0);
|
bassert (m_io_pending.get () > 0);
|
||||||
|
|
||||||
cancel_io ();
|
io_canceled ();
|
||||||
|
|
||||||
// We call the handler directly since we know
|
// We call the handler directly since we know
|
||||||
// we are already in the right context, and
|
// we are already in the right context, and
|
||||||
@@ -314,62 +338,66 @@ private:
|
|||||||
if (m_timer_expired ||
|
if (m_timer_expired ||
|
||||||
ec == boost::asio::error::operation_aborted)
|
ec == boost::asio::error::operation_aborted)
|
||||||
{
|
{
|
||||||
cancel_io ();
|
// Timer expired, or the operation was aborted due to
|
||||||
|
// cancel, so we deduct one i/o and return immediately.
|
||||||
|
//
|
||||||
|
io_canceled ();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (ec != 0 && ec != boost::asio::error::eof)
|
|
||||||
|
if (ec != 0 && ec != boost::asio::error::eof)
|
||||||
{
|
{
|
||||||
|
// A real error happened, and the timer didn't expire, so
|
||||||
|
// notify the original handler that the operation is complete.
|
||||||
|
//
|
||||||
complete (ec);
|
complete (ec);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// Process the completion as usual. If the caller does not
|
||||||
}
|
// call another initiating function, it is their responsibility
|
||||||
|
// to call io_canceled() to deduce one pending i/o.
|
||||||
// Cancels/closes all i/o objects.
|
|
||||||
//
|
//
|
||||||
void cancel_all ()
|
return false;
|
||||||
{
|
|
||||||
cancel_timer ();
|
|
||||||
m_resolver.cancel ();
|
|
||||||
error_code ec;
|
|
||||||
m_socket.close (ec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the deadline timer expires or is canceled.
|
// Called when the deadline timer expires or is canceled.
|
||||||
//
|
//
|
||||||
void timerCompletion (error_code ec)
|
void timerCompletion (error_code ec)
|
||||||
{
|
{
|
||||||
if (ec == boost::asio::error::operation_aborted)
|
bassert (m_timer_set);
|
||||||
|
|
||||||
|
if (m_timer_canceled || ec == boost::asio::error::operation_aborted)
|
||||||
{
|
{
|
||||||
bassert (m_timer_canceled);
|
// If the cancel flag is set or the operation was aborted it
|
||||||
return cancel_io ();
|
// means we canceled the timer so deduct one i/o and return.
|
||||||
|
//
|
||||||
|
io_canceled ();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_invariant (ec == 0);
|
bassert (ec == 0);
|
||||||
|
|
||||||
// Handle the case where the timer completion has already
|
// The timer expired, so this is a real timeout scenario.
|
||||||
// been queued for dispatch but we have finished the operation
|
// We want to set the error code, notify the handler, and cancel
|
||||||
// and queued the completion handler for dispatch.
|
// all other pending i/o.
|
||||||
//
|
//
|
||||||
if (! m_timer_canceled)
|
|
||||||
{
|
|
||||||
m_timer_expired = true;
|
m_timer_expired = true;
|
||||||
|
|
||||||
ec = error_code (boost::asio::error::timed_out,
|
ec = error_code (boost::asio::error::timed_out,
|
||||||
boost::asio::error::get_system_category ());
|
boost::asio::error::get_system_category ());
|
||||||
|
|
||||||
complete (ec);
|
// Cancel pending name resolution
|
||||||
|
|
||||||
io_complete (ec);
|
|
||||||
|
|
||||||
m_resolver.cancel ();
|
m_resolver.cancel ();
|
||||||
|
|
||||||
|
// Close the socket. This will cancel up to 2 pending i/o
|
||||||
m_socket.close (ec);
|
m_socket.close (ec);
|
||||||
}
|
|
||||||
else
|
// Notify the original handler of a timeout error.
|
||||||
{
|
// The call to complete() consumes one pending i/o, which
|
||||||
cancel_io ();
|
// we need since this function counts as one completion.
|
||||||
}
|
//
|
||||||
|
complete (ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
@@ -594,7 +622,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deduct one i/o since we aren't issuing any new one
|
// deduct one i/o since we aren't issuing any new one
|
||||||
cancel_io ();
|
io_canceled ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_complete (error_code ec, std::size_t bytes_transferred)
|
void read_complete (error_code ec, std::size_t bytes_transferred)
|
||||||
@@ -671,6 +699,7 @@ private:
|
|||||||
State m_state;
|
State m_state;
|
||||||
HTTPParser m_parser;
|
HTTPParser m_parser;
|
||||||
String m_get_string;
|
String m_get_string;
|
||||||
|
bool m_timer_set;
|
||||||
bool m_timer_canceled;
|
bool m_timer_canceled;
|
||||||
bool m_timer_expired;
|
bool m_timer_expired;
|
||||||
std::size_t m_messageLimitBytes;
|
std::size_t m_messageLimitBytes;
|
||||||
|
|||||||
Reference in New Issue
Block a user