1
0
mirror of https://github.com/facebookincubator/mvfst.git synced 2025-08-06 22:22:38 +03:00

QUIC/HTTP3: reset idle timeout on ping

Summary:
Adds a transport callback for ping received.
Uses the callback in the HTTP3 session to reset the timeout

Reviewed By: mjoras

Differential Revision: D33101180

fbshipit-source-id: d6fcecd22cbd5c311674dd9421c0c54eb04728a0
This commit is contained in:
Luca Niccolini
2022-01-21 16:42:13 -08:00
committed by Facebook GitHub Bot
parent 5f98c25b79
commit 15390c732a
8 changed files with 72 additions and 28 deletions

View File

@@ -1124,17 +1124,24 @@ class QuicSocket {
* Invoked if the ping times out * Invoked if the ping times out
*/ */
virtual void pingTimeout() noexcept = 0; virtual void pingTimeout() noexcept = 0;
/**
* Invoked when a ping is received
*/
virtual void onPing() noexcept = 0;
}; };
/**
* Set the ping callback
*/
virtual folly::Expected<folly::Unit, LocalErrorCode> setPingCallback(
PingCallback* cb) = 0;
/** /**
* Send a ping to the peer. When the ping is acknowledged by the peer or * Send a ping to the peer. When the ping is acknowledged by the peer or
* times out, the transport will invoke the callback. * times out, the transport will invoke the callback.
*
* If 'callback' is nullptr, or pingTimeout is 0, no callback is scheduled.
*/ */
virtual void sendPing( virtual void sendPing(std::chrono::milliseconds pingTimeout) = 0;
PingCallback* callback,
std::chrono::milliseconds pingTimeout) = 0;
/** /**
* Get information on the state of the quic connection. Should only be used * Get information on the state of the quic connection. Should only be used

View File

@@ -1400,7 +1400,16 @@ folly::
} }
} }
void QuicTransportBase::handlePingCallback() { void QuicTransportBase::handlePingCallbacks() {
if (conn_->pendingEvents.notifyPingReceived && pingCallback_ != nullptr) {
conn_->pendingEvents.notifyPingReceived = false;
runOnEvbAsync([](auto self) {
if (self->pingCallback_) {
self->pingCallback_->onPing();
}
});
}
if (!conn_->pendingEvents.cancelPingTimeout) { if (!conn_->pendingEvents.cancelPingTimeout) {
return; // nothing to cancel return; // nothing to cancel
} }
@@ -1411,7 +1420,11 @@ void QuicTransportBase::handlePingCallback() {
} }
pingTimeout_.cancelTimeout(); pingTimeout_.cancelTimeout();
if (pingCallback_ != nullptr) { if (pingCallback_ != nullptr) {
runOnEvbAsync([](auto self) { self->pingCallback_->pingAcknowledged(); }); runOnEvbAsync([](auto self) {
if (self->pingCallback_) {
self->pingCallback_->pingAcknowledged();
}
});
} }
conn_->pendingEvents.cancelPingTimeout = false; conn_->pendingEvents.cancelPingTimeout = false;
} }
@@ -1704,7 +1717,7 @@ void QuicTransportBase::processCallbacksAfterNetworkData() {
} }
conn_->pendingCallbacks.clear(); conn_->pendingCallbacks.clear();
handlePingCallback(); handlePingCallbacks();
if (closeState_ != CloseState::OPEN) { if (closeState_ != CloseState::OPEN) {
return; return;
} }
@@ -2433,9 +2446,19 @@ void QuicTransportBase::checkForClosedStream() {
} }
} }
void QuicTransportBase::sendPing( folly::Expected<folly::Unit, LocalErrorCode> QuicTransportBase::setPingCallback(
PingCallback* callback, PingCallback* cb) {
std::chrono::milliseconds pingTimeout) { if (closeState_ != CloseState::OPEN) {
return folly::makeUnexpected(LocalErrorCode::CONNECTION_CLOSED);
}
VLOG(4) << "Setting ping callback "
<< " cb=" << cb << " " << *this;
pingCallback_ = cb;
return folly::unit;
}
void QuicTransportBase::sendPing(std::chrono::milliseconds pingTimeout) {
/* Step 0: Connection should not be closed */ /* Step 0: Connection should not be closed */
if (closeState_ == CloseState::CLOSED) { if (closeState_ == CloseState::CLOSED) {
return; return;
@@ -2446,8 +2469,8 @@ void QuicTransportBase::sendPing(
updateWriteLooper(true); updateWriteLooper(true);
// Step 2: Schedule the timeout on event base // Step 2: Schedule the timeout on event base
if (callback && pingTimeout != 0ms) { if (pingCallback_ && pingTimeout != 0ms) {
schedulePingTimeout(callback, pingTimeout); schedulePingTimeout(pingCallback_, pingTimeout);
} }
} }
@@ -2499,7 +2522,11 @@ void QuicTransportBase::pingTimeoutExpired() noexcept {
if (pingCallback_ == nullptr) { if (pingCallback_ == nullptr) {
return; return;
} }
runOnEvbAsync([](auto self) { self->pingCallback_->pingTimeout(); }); runOnEvbAsync([](auto self) {
if (self->pingCallback_) {
self->pingCallback_->pingTimeout();
}
});
} }
void QuicTransportBase::pathValidationTimeoutExpired() noexcept { void QuicTransportBase::pathValidationTimeoutExpired() noexcept {

View File

@@ -208,8 +208,10 @@ class QuicTransportBase : public QuicSocket, QuicStreamPrioritiesObserver {
StreamId id, StreamId id,
QuicErrorCode error) override; QuicErrorCode error) override;
void sendPing(PingCallback* callback, std::chrono::milliseconds pingTimeout) folly::Expected<folly::Unit, LocalErrorCode> setPingCallback(
override; PingCallback* cb) override;
void sendPing(std::chrono::milliseconds pingTimeout) override;
const QuicConnectionStateBase* getState() const override { const QuicConnectionStateBase* getState() const override {
return conn_.get(); return conn_.get();
@@ -702,7 +704,7 @@ class QuicTransportBase : public QuicSocket, QuicStreamPrioritiesObserver {
void updateReadLooper(); void updateReadLooper();
void updatePeekLooper(); void updatePeekLooper();
void updateWriteLooper(bool thisIteration); void updateWriteLooper(bool thisIteration);
void handlePingCallback(); void handlePingCallbacks();
void handleKnobCallbacks(); void handleKnobCallbacks();
void handleAckEventCallbacks(); void handleAckEventCallbacks();
void handleCancelByteEventCallbacks(); void handleCancelByteEventCallbacks();

View File

@@ -244,7 +244,10 @@ class MockQuicSocket : public QuicSocket {
MOCK_METHOD2( MOCK_METHOD2(
maybeResetStreamFromReadError, maybeResetStreamFromReadError,
folly::Expected<folly::Unit, LocalErrorCode>(StreamId, QuicErrorCode)); folly::Expected<folly::Unit, LocalErrorCode>(StreamId, QuicErrorCode));
MOCK_METHOD2(sendPing, void(PingCallback*, std::chrono::milliseconds)); MOCK_METHOD1(
setPingCallback,
folly::Expected<folly::Unit, LocalErrorCode>(PingCallback*));
MOCK_METHOD1(sendPing, void(std::chrono::milliseconds));
MOCK_CONST_METHOD0(getState, const QuicConnectionStateBase*()); MOCK_CONST_METHOD0(getState, const QuicConnectionStateBase*());
MOCK_METHOD0(isDetachable, bool()); MOCK_METHOD0(isDetachable, bool());
MOCK_METHOD1(attachEventBase, void(folly::EventBase*)); MOCK_METHOD1(attachEventBase, void(folly::EventBase*));

View File

@@ -131,6 +131,7 @@ class TestPingCallback : public QuicSocket::PingCallback {
public: public:
void pingAcknowledged() noexcept override {} void pingAcknowledged() noexcept override {}
void pingTimeout() noexcept override {} void pingTimeout() noexcept override {}
void onPing() noexcept override {}
}; };
class TestByteEventCallback : public QuicSocket::ByteEventCallback { class TestByteEventCallback : public QuicSocket::ByteEventCallback {
@@ -310,18 +311,16 @@ class TestQuicTransport
ackTimeout_.timeoutExpired(); ackTimeout_.timeoutExpired();
} }
void invokeSendPing( void invokeSendPing(std::chrono::milliseconds interval) {
quic::QuicSocket::PingCallback* cb, sendPing(interval);
std::chrono::milliseconds interval) {
sendPing(cb, interval);
} }
void invokeCancelPingTimeout() { void invokeCancelPingTimeout() {
pingTimeout_.cancelTimeout(); pingTimeout_.cancelTimeout();
} }
void invokeHandlePingCallback() { void invokeHandlePingCallbacks() {
handlePingCallback(); handlePingCallbacks();
} }
void invokeHandleKnobCallbacks() { void invokeHandleKnobCallbacks() {
@@ -3457,11 +3456,12 @@ TEST_F(QuicTransportImplTest, SuccessfulPing) {
auto conn = transport->transportConn; auto conn = transport->transportConn;
std::chrono::milliseconds interval(10); std::chrono::milliseconds interval(10);
TestPingCallback pingCallback; TestPingCallback pingCallback;
transport->invokeSendPing(&pingCallback, interval); transport->setPingCallback(&pingCallback);
transport->invokeSendPing(interval);
EXPECT_EQ(transport->isPingTimeoutScheduled(), true); EXPECT_EQ(transport->isPingTimeoutScheduled(), true);
EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false); EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false);
conn->pendingEvents.cancelPingTimeout = true; conn->pendingEvents.cancelPingTimeout = true;
transport->invokeHandlePingCallback(); transport->invokeHandlePingCallbacks();
evb->loopOnce(); evb->loopOnce();
EXPECT_EQ(transport->isPingTimeoutScheduled(), false); EXPECT_EQ(transport->isPingTimeoutScheduled(), false);
EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false); EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false);
@@ -3471,12 +3471,13 @@ TEST_F(QuicTransportImplTest, FailedPing) {
auto conn = transport->transportConn; auto conn = transport->transportConn;
std::chrono::milliseconds interval(10); std::chrono::milliseconds interval(10);
TestPingCallback pingCallback; TestPingCallback pingCallback;
transport->invokeSendPing(&pingCallback, interval); transport->setPingCallback(&pingCallback);
transport->invokeSendPing(interval);
EXPECT_EQ(transport->isPingTimeoutScheduled(), true); EXPECT_EQ(transport->isPingTimeoutScheduled(), true);
EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false); EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false);
conn->pendingEvents.cancelPingTimeout = true; conn->pendingEvents.cancelPingTimeout = true;
transport->invokeCancelPingTimeout(); transport->invokeCancelPingTimeout();
transport->invokeHandlePingCallback(); transport->invokeHandlePingCallbacks();
EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false); EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false);
} }

View File

@@ -566,6 +566,7 @@ void QuicClientTransport::processPacketData(
// Ping isn't retransmittable. But we would like to ack them early. // Ping isn't retransmittable. But we would like to ack them early.
// So, make Ping frames count towards ack policy // So, make Ping frames count towards ack policy
pktHasRetransmittableData = true; pktHasRetransmittableData = true;
conn_->pendingEvents.notifyPingReceived = true;
break; break;
case QuicFrame::Type::PaddingFrame: case QuicFrame::Type::PaddingFrame:
break; break;

View File

@@ -1149,6 +1149,7 @@ void onServerReadDataFromOpen(
// Ping isn't retransmittable data. But we would like to ack them // Ping isn't retransmittable data. But we would like to ack them
// early. // early.
pktHasRetransmittableData = true; pktHasRetransmittableData = true;
conn.pendingEvents.notifyPingReceived = true;
break; break;
case QuicFrame::Type::PaddingFrame: case QuicFrame::Type::PaddingFrame:
break; break;

View File

@@ -560,6 +560,8 @@ struct QuicConnectionStateBase : public folly::DelayedDestruction {
bool cancelPingTimeout{false}; bool cancelPingTimeout{false};
bool notifyPingReceived{false};
// close transport when the next packet number reaches kMaxPacketNum // close transport when the next packet number reaches kMaxPacketNum
bool closeTransport{false}; bool closeTransport{false};