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
*/
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
* times out, the transport will invoke the callback.
*
* If 'callback' is nullptr, or pingTimeout is 0, no callback is scheduled.
*/
virtual void sendPing(
PingCallback* callback,
std::chrono::milliseconds pingTimeout) = 0;
virtual void sendPing(std::chrono::milliseconds pingTimeout) = 0;
/**
* 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) {
return; // nothing to cancel
}
@@ -1411,7 +1420,11 @@ void QuicTransportBase::handlePingCallback() {
}
pingTimeout_.cancelTimeout();
if (pingCallback_ != nullptr) {
runOnEvbAsync([](auto self) { self->pingCallback_->pingAcknowledged(); });
runOnEvbAsync([](auto self) {
if (self->pingCallback_) {
self->pingCallback_->pingAcknowledged();
}
});
}
conn_->pendingEvents.cancelPingTimeout = false;
}
@@ -1704,7 +1717,7 @@ void QuicTransportBase::processCallbacksAfterNetworkData() {
}
conn_->pendingCallbacks.clear();
handlePingCallback();
handlePingCallbacks();
if (closeState_ != CloseState::OPEN) {
return;
}
@@ -2433,9 +2446,19 @@ void QuicTransportBase::checkForClosedStream() {
}
}
void QuicTransportBase::sendPing(
PingCallback* callback,
std::chrono::milliseconds pingTimeout) {
folly::Expected<folly::Unit, LocalErrorCode> QuicTransportBase::setPingCallback(
PingCallback* cb) {
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 */
if (closeState_ == CloseState::CLOSED) {
return;
@@ -2446,8 +2469,8 @@ void QuicTransportBase::sendPing(
updateWriteLooper(true);
// Step 2: Schedule the timeout on event base
if (callback && pingTimeout != 0ms) {
schedulePingTimeout(callback, pingTimeout);
if (pingCallback_ && pingTimeout != 0ms) {
schedulePingTimeout(pingCallback_, pingTimeout);
}
}
@@ -2499,7 +2522,11 @@ void QuicTransportBase::pingTimeoutExpired() noexcept {
if (pingCallback_ == nullptr) {
return;
}
runOnEvbAsync([](auto self) { self->pingCallback_->pingTimeout(); });
runOnEvbAsync([](auto self) {
if (self->pingCallback_) {
self->pingCallback_->pingTimeout();
}
});
}
void QuicTransportBase::pathValidationTimeoutExpired() noexcept {

View File

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

View File

@@ -244,7 +244,10 @@ class MockQuicSocket : public QuicSocket {
MOCK_METHOD2(
maybeResetStreamFromReadError,
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_METHOD0(isDetachable, bool());
MOCK_METHOD1(attachEventBase, void(folly::EventBase*));

View File

@@ -131,6 +131,7 @@ class TestPingCallback : public QuicSocket::PingCallback {
public:
void pingAcknowledged() noexcept override {}
void pingTimeout() noexcept override {}
void onPing() noexcept override {}
};
class TestByteEventCallback : public QuicSocket::ByteEventCallback {
@@ -310,18 +311,16 @@ class TestQuicTransport
ackTimeout_.timeoutExpired();
}
void invokeSendPing(
quic::QuicSocket::PingCallback* cb,
std::chrono::milliseconds interval) {
sendPing(cb, interval);
void invokeSendPing(std::chrono::milliseconds interval) {
sendPing(interval);
}
void invokeCancelPingTimeout() {
pingTimeout_.cancelTimeout();
}
void invokeHandlePingCallback() {
handlePingCallback();
void invokeHandlePingCallbacks() {
handlePingCallbacks();
}
void invokeHandleKnobCallbacks() {
@@ -3457,11 +3456,12 @@ TEST_F(QuicTransportImplTest, SuccessfulPing) {
auto conn = transport->transportConn;
std::chrono::milliseconds interval(10);
TestPingCallback pingCallback;
transport->invokeSendPing(&pingCallback, interval);
transport->setPingCallback(&pingCallback);
transport->invokeSendPing(interval);
EXPECT_EQ(transport->isPingTimeoutScheduled(), true);
EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false);
conn->pendingEvents.cancelPingTimeout = true;
transport->invokeHandlePingCallback();
transport->invokeHandlePingCallbacks();
evb->loopOnce();
EXPECT_EQ(transport->isPingTimeoutScheduled(), false);
EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false);
@@ -3471,12 +3471,13 @@ TEST_F(QuicTransportImplTest, FailedPing) {
auto conn = transport->transportConn;
std::chrono::milliseconds interval(10);
TestPingCallback pingCallback;
transport->invokeSendPing(&pingCallback, interval);
transport->setPingCallback(&pingCallback);
transport->invokeSendPing(interval);
EXPECT_EQ(transport->isPingTimeoutScheduled(), true);
EXPECT_EQ(conn->pendingEvents.cancelPingTimeout, false);
conn->pendingEvents.cancelPingTimeout = true;
transport->invokeCancelPingTimeout();
transport->invokeHandlePingCallback();
transport->invokeHandlePingCallbacks();
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.
// So, make Ping frames count towards ack policy
pktHasRetransmittableData = true;
conn_->pendingEvents.notifyPingReceived = true;
break;
case QuicFrame::Type::PaddingFrame:
break;

View File

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

View File

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