Allow setting a delay for a response.
This serves as infrastructure for testing that timing metrics
measure what they're supposed to
(see https://chromium-review.googlesource.com/c/chromium/src/+/3815441)
PiperOrigin-RevId: 468108169
diff --git a/quiche/quic/test_tools/quic_test_utils.cc b/quiche/quic/test_tools/quic_test_utils.cc
index 03948b7..39ba0a7 100644
--- a/quiche/quic/test_tools/quic_test_utils.cc
+++ b/quiche/quic/test_tools/quic_test_utils.cc
@@ -455,6 +455,8 @@
const QuicClock* MockQuicConnectionHelper::GetClock() const { return &clock_; }
+QuicClock* MockQuicConnectionHelper::GetClock() { return &clock_; }
+
QuicRandom* MockQuicConnectionHelper::GetRandomGenerator() {
return &random_generator_;
}
@@ -483,8 +485,8 @@
clock_.AdvanceTime(delta);
}
-MockQuicConnection::MockQuicConnection(MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory,
+MockQuicConnection::MockQuicConnection(QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
Perspective perspective)
: MockQuicConnection(TestConnectionId(),
QuicSocketAddress(TestPeerIPAddress(), kTestPort),
@@ -492,16 +494,16 @@
ParsedVersionOfIndex(CurrentSupportedVersions(), 0)) {}
MockQuicConnection::MockQuicConnection(QuicSocketAddress address,
- MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
Perspective perspective)
: MockQuicConnection(TestConnectionId(), address, helper, alarm_factory,
perspective,
ParsedVersionOfIndex(CurrentSupportedVersions(), 0)) {}
MockQuicConnection::MockQuicConnection(QuicConnectionId connection_id,
- MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
Perspective perspective)
: MockQuicConnection(connection_id,
QuicSocketAddress(TestPeerIPAddress(), kTestPort),
@@ -509,7 +511,7 @@
ParsedVersionOfIndex(CurrentSupportedVersions(), 0)) {}
MockQuicConnection::MockQuicConnection(
- MockQuicConnectionHelper* helper, MockAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
Perspective perspective, const ParsedQuicVersionVector& supported_versions)
: MockQuicConnection(
TestConnectionId(), QuicSocketAddress(TestPeerIPAddress(), kTestPort),
@@ -517,7 +519,7 @@
MockQuicConnection::MockQuicConnection(
QuicConnectionId connection_id, QuicSocketAddress initial_peer_address,
- MockQuicConnectionHelper* helper, MockAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
Perspective perspective, const ParsedQuicVersionVector& supported_versions)
: QuicConnection(
connection_id,
@@ -546,13 +548,13 @@
return false;
}
-PacketSavingConnection::PacketSavingConnection(MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory,
- Perspective perspective)
+PacketSavingConnection::PacketSavingConnection(
+ QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
+ Perspective perspective)
: MockQuicConnection(helper, alarm_factory, perspective) {}
PacketSavingConnection::PacketSavingConnection(
- MockQuicConnectionHelper* helper, MockAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
Perspective perspective, const ParsedQuicVersionVector& supported_versions)
: MockQuicConnection(helper, alarm_factory, perspective,
supported_versions) {}
@@ -1125,7 +1127,7 @@
void CreateClientSessionForTest(
QuicServerId server_id, QuicTime::Delta connection_start_time,
const ParsedQuicVersionVector& supported_versions,
- MockQuicConnectionHelper* helper, MockAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
QuicCryptoClientConfig* crypto_client_config,
PacketSavingConnection** client_connection,
TestQuicSpdyClientSession** client_session) {
@@ -1148,7 +1150,7 @@
void CreateServerSessionForTest(
QuicServerId /*server_id*/, QuicTime::Delta connection_start_time,
ParsedQuicVersionVector supported_versions,
- MockQuicConnectionHelper* helper, MockAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
QuicCryptoServerConfig* server_crypto_config,
QuicCompressedCertsCache* compressed_certs_cache,
PacketSavingConnection** server_connection,
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h
index 5e1397c..af9cf2c 100644
--- a/quiche/quic/test_tools/quic_test_utils.h
+++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -517,6 +517,7 @@
MockQuicConnectionHelper& operator=(const MockQuicConnectionHelper&) = delete;
~MockQuicConnectionHelper() override;
const QuicClock* GetClock() const override;
+ QuicClock* GetClock();
QuicRandom* GetRandomGenerator() override;
quiche::QuicheBufferAllocator* GetStreamSendBufferAllocator() override;
void AdvanceTime(QuicTime::Delta delta);
@@ -581,36 +582,35 @@
class MockQuicConnection : public QuicConnection {
public:
// Uses a ConnectionId of 42 and 127.0.0.1:123.
- MockQuicConnection(MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory, Perspective perspective);
+ MockQuicConnection(QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory, Perspective perspective);
// Uses a ConnectionId of 42.
MockQuicConnection(QuicSocketAddress address,
- MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory, Perspective perspective);
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory, Perspective perspective);
// Uses 127.0.0.1:123.
MockQuicConnection(QuicConnectionId connection_id,
- MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory, Perspective perspective);
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory, Perspective perspective);
// Uses a ConnectionId of 42, and 127.0.0.1:123.
- MockQuicConnection(MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory, Perspective perspective,
+ MockQuicConnection(QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory, Perspective perspective,
const ParsedQuicVersionVector& supported_versions);
MockQuicConnection(QuicConnectionId connection_id, QuicSocketAddress address,
- MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory, Perspective perspective,
+ QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory, Perspective perspective,
const ParsedQuicVersionVector& supported_versions);
MockQuicConnection(const MockQuicConnection&) = delete;
MockQuicConnection& operator=(const MockQuicConnection&) = delete;
~MockQuicConnection() override;
- // If the constructor that uses a MockQuicConnectionHelper has been used then
- // this method
- // will advance the time of the MockClock.
+ // If the constructor that uses a QuicConnectionHelperInterface has been used
+ // then this method will advance the time of the MockClock.
void AdvanceTime(QuicTime::Delta delta);
MOCK_METHOD(void, ProcessUdpPacket,
@@ -692,6 +692,7 @@
}
bool OnProtocolVersionMismatch(ParsedQuicVersion version) override;
+ void OnIdleNetworkDetected() override {}
bool ReallySendControlFrame(const QuicFrame& frame) {
return QuicConnection::SendControlFrame(frame);
@@ -722,12 +723,12 @@
class PacketSavingConnection : public MockQuicConnection {
public:
- PacketSavingConnection(MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory,
+ PacketSavingConnection(QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
Perspective perspective);
- PacketSavingConnection(MockQuicConnectionHelper* helper,
- MockAlarmFactory* alarm_factory,
+ PacketSavingConnection(QuicConnectionHelperInterface* helper,
+ QuicAlarmFactory* alarm_factory,
Perspective perspective,
const ParsedQuicVersionVector& supported_versions);
PacketSavingConnection(const PacketSavingConnection&) = delete;
@@ -1516,7 +1517,7 @@
void CreateClientSessionForTest(
QuicServerId server_id, QuicTime::Delta connection_start_time,
const ParsedQuicVersionVector& supported_versions,
- MockQuicConnectionHelper* helper, MockAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
QuicCryptoClientConfig* crypto_client_config,
PacketSavingConnection** client_connection,
TestQuicSpdyClientSession** client_session);
@@ -1539,7 +1540,7 @@
void CreateServerSessionForTest(
QuicServerId server_id, QuicTime::Delta connection_start_time,
ParsedQuicVersionVector supported_versions,
- MockQuicConnectionHelper* helper, MockAlarmFactory* alarm_factory,
+ QuicConnectionHelperInterface* helper, QuicAlarmFactory* alarm_factory,
QuicCryptoServerConfig* server_crypto_config,
QuicCompressedCertsCache* compressed_certs_cache,
PacketSavingConnection** server_connection,
diff --git a/quiche/quic/tools/quic_backend_response.cc b/quiche/quic/tools/quic_backend_response.cc
index 8fce5d5..8a54204 100644
--- a/quiche/quic/tools/quic_backend_response.cc
+++ b/quiche/quic/tools/quic_backend_response.cc
@@ -20,7 +20,8 @@
priority(other.priority),
body(other.body) {}
-QuicBackendResponse::QuicBackendResponse() : response_type_(REGULAR_RESPONSE) {}
+QuicBackendResponse::QuicBackendResponse()
+ : response_type_(REGULAR_RESPONSE), delay_(QuicTime::Delta::Zero()) {}
QuicBackendResponse::~QuicBackendResponse() = default;
diff --git a/quiche/quic/tools/quic_backend_response.h b/quiche/quic/tools/quic_backend_response.h
index 8e52e2e..6d1b105 100644
--- a/quiche/quic/tools/quic_backend_response.h
+++ b/quiche/quic/tools/quic_backend_response.h
@@ -6,6 +6,7 @@
#define QUICHE_QUIC_TOOLS_QUIC_BACKEND_RESPONSE_H_
#include "absl/strings/string_view.h"
+#include "quiche/quic/core/quic_time.h"
#include "quiche/quic/tools/quic_url.h"
#include "quiche/spdy/core/http2_header_block.h"
#include "quiche/spdy/core/spdy_protocol.h"
@@ -78,12 +79,18 @@
body_.assign(body.data(), body.size());
}
+ // This would simulate a delay before sending the response
+ // back to the client. Intended for testing purposes.
+ void set_delay(QuicTime::Delta delay) { delay_ = delay; }
+ QuicTime::Delta delay() const { return delay_; }
+
private:
std::vector<spdy::Http2HeaderBlock> early_hints_;
SpecialResponseType response_type_;
spdy::Http2HeaderBlock headers_;
spdy::Http2HeaderBlock trailers_;
std::string body_;
+ QuicTime::Delta delay_;
};
} // namespace quic
diff --git a/quiche/quic/tools/quic_memory_cache_backend.cc b/quiche/quic/tools/quic_memory_cache_backend.cc
index 0e50139..0fe984d 100644
--- a/quiche/quic/tools/quic_memory_cache_backend.cc
+++ b/quiche/quic/tools/quic_memory_cache_backend.cc
@@ -215,6 +215,17 @@
std::vector<spdy::Http2HeaderBlock>());
}
+bool QuicMemoryCacheBackend::SetResponseDelay(absl::string_view host,
+ absl::string_view path,
+ QuicTime::Delta delay) {
+ QuicWriterMutexLock lock(&response_mutex_);
+ auto it = responses_.find(GetKey(host, path));
+ if (it == responses_.end()) return false;
+
+ it->second->set_delay(delay);
+ return true;
+}
+
void QuicMemoryCacheBackend::AddResponseWithEarlyHints(
absl::string_view host, absl::string_view path,
spdy::Http2HeaderBlock response_headers, absl::string_view response_body,
diff --git a/quiche/quic/tools/quic_memory_cache_backend.h b/quiche/quic/tools/quic_memory_cache_backend.h
index 7dfd372..ccdaf59 100644
--- a/quiche/quic/tools/quic_memory_cache_backend.h
+++ b/quiche/quic/tools/quic_memory_cache_backend.h
@@ -121,6 +121,12 @@
spdy::Http2HeaderBlock response_headers, absl::string_view response_body,
QuicBackendResponse::SpecialResponseType response_type);
+ // Finds a response with the given host and path, and assign it a simulated
+ // delay. Returns true if the requisite response was found and the delay was
+ // set.
+ bool SetResponseDelay(absl::string_view host, absl::string_view path,
+ QuicTime::Delta delay);
+
// Sets a default response in case of cache misses. Takes ownership of
// 'response'.
void AddDefaultResponse(QuicBackendResponse* response);
diff --git a/quiche/quic/tools/quic_simple_server_stream.cc b/quiche/quic/tools/quic_simple_server_stream.cc
index 1b66aac..beea65a 100644
--- a/quiche/quic/tools/quic_simple_server_stream.cc
+++ b/quiche/quic/tools/quic_simple_server_stream.cc
@@ -245,6 +245,31 @@
return spdy_session()->peer_address().host().ToString();
}
+namespace {
+
+class DelayedResponseAlarm : public QuicAlarm::DelegateWithContext {
+ public:
+ DelayedResponseAlarm(QuicSimpleServerStream* stream,
+ const QuicBackendResponse* response)
+ : QuicAlarm::DelegateWithContext(
+ stream->spdy_session()->connection()->context()),
+ stream_(stream),
+ response_(response) {
+ stream_ = stream;
+ response_ = response;
+ }
+
+ ~DelayedResponseAlarm() override = default;
+
+ void OnAlarm() override { stream_->Respond(response_); }
+
+ private:
+ QuicSimpleServerStream* stream_;
+ const QuicBackendResponse* response_;
+};
+
+} // namespace
+
void QuicSimpleServerStream::OnResponseBackendComplete(
const QuicBackendResponse* response) {
if (response == nullptr) {
@@ -253,6 +278,19 @@
return;
}
+ auto delay = response->delay();
+ if (delay.IsZero()) {
+ Respond(response);
+ return;
+ }
+
+ auto* connection = session()->connection();
+ delayed_response_alarm_.reset(connection->alarm_factory()->CreateAlarm(
+ new DelayedResponseAlarm(this, response)));
+ delayed_response_alarm_->Set(connection->clock()->Now() + delay);
+}
+
+void QuicSimpleServerStream::Respond(const QuicBackendResponse* response) {
// Send Early Hints first.
for (const auto& headers : response->early_hints()) {
QUIC_DVLOG(1) << "Stream " << id() << " sending an Early Hints response: "
diff --git a/quiche/quic/tools/quic_simple_server_stream.h b/quiche/quic/tools/quic_simple_server_stream.h
index 62c6339..3bcd9ed 100644
--- a/quiche/quic/tools/quic_simple_server_stream.h
+++ b/quiche/quic/tools/quic_simple_server_stream.h
@@ -61,6 +61,8 @@
void SendStreamData(absl::string_view data, bool close_stream) override;
void TerminateStreamWithError(QuicResetStreamError error) override;
+ void Respond(const QuicBackendResponse* response);
+
protected:
// Handles fresh body data whenever received when method is CONNECT.
void HandleRequestConnectData(bool fin_received);
@@ -118,6 +120,8 @@
// Whether response headers have already been sent.
bool response_sent_ = false;
+ std::unique_ptr<QuicAlarm> delayed_response_alarm_;
+
QuicSimpleServerBackend* quic_simple_server_backend_; // Not owned.
};
diff --git a/quiche/quic/tools/quic_simple_server_stream_test.cc b/quiche/quic/tools/quic_simple_server_stream_test.cc
index 1aa0a7f..92d06bd 100644
--- a/quiche/quic/tools/quic_simple_server_stream_test.cc
+++ b/quiche/quic/tools/quic_simple_server_stream_test.cc
@@ -15,6 +15,8 @@
#include "quiche/quic/core/crypto/null_encrypter.h"
#include "quiche/quic/core/http/http_encoder.h"
#include "quiche/quic/core/http/spdy_utils.h"
+#include "quiche/quic/core/quic_alarm_factory.h"
+#include "quiche/quic/core/quic_default_clock.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
@@ -29,6 +31,7 @@
#include "quiche/quic/test_tools/quic_spdy_session_peer.h"
#include "quiche/quic/test_tools/quic_stream_peer.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
+#include "quiche/quic/test_tools/simulator/simulator.h"
#include "quiche/quic/tools/quic_backend_response.h"
#include "quiche/quic/tools/quic_memory_cache_backend.h"
#include "quiche/quic/tools/quic_simple_server_backend.h"
@@ -63,6 +66,7 @@
~TestStream() override = default;
+ MOCK_METHOD(void, FireAlarmMock, (), ());
MOCK_METHOD(void, WriteHeadersMock, (bool fin), ());
MOCK_METHOD(void, WriteEarlyHintsHeadersMock, (bool fin), ());
MOCK_METHOD(void, WriteOrBufferBody, (absl::string_view data, bool fin),
@@ -205,7 +209,7 @@
public:
QuicSimpleServerStreamTest()
: connection_(new StrictMock<MockQuicConnection>(
- &helper_, &alarm_factory_, Perspective::IS_SERVER,
+ &simulator_, simulator_.GetAlarmFactory(), Perspective::IS_SERVER,
SupportedVersions(GetParam()))),
crypto_config_(new QuicCryptoServerConfig(
QuicCryptoServerConfig::TESTING, QuicRandom::GetInstance(),
@@ -275,9 +279,9 @@
stream_->ReplaceBackend(replacement_backend_.get());
}
+ quic::simulator::Simulator simulator_;
spdy::Http2HeaderBlock response_headers_;
MockQuicConnectionHelper helper_;
- MockAlarmFactory alarm_factory_;
StrictMock<MockQuicConnection>* connection_;
StrictMock<MockQuicSessionVisitor> session_owner_;
StrictMock<MockQuicCryptoServerStreamHelper> session_helper_;
@@ -565,6 +569,61 @@
EXPECT_TRUE(stream_->write_side_closed());
}
+class AlarmTestDelegate : public QuicAlarm::DelegateWithoutContext {
+ public:
+ AlarmTestDelegate(TestStream* stream) : stream_(stream) {}
+
+ void OnAlarm() override { stream_->FireAlarmMock(); }
+
+ private:
+ TestStream* stream_;
+};
+
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithDelay) {
+ // Add a request and response with valid headers.
+ spdy::Http2HeaderBlock* request_headers = stream_->mutable_headers();
+ std::string host = "www.google.com";
+ std::string path = "/bar";
+ (*request_headers)[":path"] = path;
+ (*request_headers)[":authority"] = host;
+ (*request_headers)[":method"] = "GET";
+
+ response_headers_[":status"] = "200";
+ response_headers_["content-length"] = "5";
+ std::string body = "Yummm";
+ QuicTime::Delta delay = QuicTime::Delta::FromMilliseconds(3000);
+
+ quiche::QuicheBuffer header = HttpEncoder::SerializeDataFrameHeader(
+ body.length(), quiche::SimpleBufferAllocator::Get());
+
+ memory_cache_backend_.AddResponse(host, path, std::move(response_headers_),
+ body);
+ auto did_delay_succeed =
+ memory_cache_backend_.SetResponseDelay(host, path, delay);
+ EXPECT_TRUE(did_delay_succeed);
+ auto did_invalid_delay_succeed =
+ memory_cache_backend_.SetResponseDelay(host, "nonsense", delay);
+ EXPECT_FALSE(did_invalid_delay_succeed);
+ std::unique_ptr<QuicAlarm> alarm(connection_->alarm_factory()->CreateAlarm(
+ new AlarmTestDelegate(stream_)));
+ alarm->Set(connection_->clock()->Now() + delay);
+ QuicStreamPeer::SetFinReceived(stream_);
+ InSequence s;
+ EXPECT_CALL(*stream_, FireAlarmMock());
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+
+ if (UsesHttp3()) {
+ EXPECT_CALL(session_, WritevData(_, header.size(), _, NO_FIN, _, _));
+ }
+ EXPECT_CALL(session_, WritevData(_, body.length(), _, FIN, _, _));
+
+ stream_->DoSendResponse();
+ simulator_.RunFor(delay);
+
+ EXPECT_FALSE(QuicStreamPeer::read_side_closed(stream_));
+ EXPECT_TRUE(stream_->write_side_closed());
+}
+
TEST_P(QuicSimpleServerStreamTest, PushResponseOnClientInitiatedStream) {
// EXPECT_QUIC_BUG tests are expensive so only run one instance of them.
if (GetParam() != AllSupportedVersions()[0]) {