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:
committed by
Facebook GitHub Bot
parent
5f98c25b79
commit
15390c732a
@@ -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
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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();
|
||||||
|
@@ -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*));
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user