Add QuicMemoryCacheBackend::AddResponseWithEarlyHints()
This method will be used to write unittests/browser_tests in
the Chromium repository.
PiperOrigin-RevId: 363061099
Change-Id: Iff231ffc8f5192e57982f69b629c6cb8e7d1cd8a
diff --git a/quic/tools/quic_backend_response.h b/quic/tools/quic_backend_response.h
index a7edd78..1c5cad8 100644
--- a/quic/tools/quic_backend_response.h
+++ b/quic/tools/quic_backend_response.h
@@ -50,11 +50,20 @@
~QuicBackendResponse();
+ const std::vector<spdy::Http2HeaderBlock>& early_hints() const {
+ return early_hints_;
+ }
SpecialResponseType response_type() const { return response_type_; }
const spdy::Http2HeaderBlock& headers() const { return headers_; }
const spdy::Http2HeaderBlock& trailers() const { return trailers_; }
const absl::string_view body() const { return absl::string_view(body_); }
+ void AddEarlyHints(const spdy::Http2HeaderBlock& headers) {
+ spdy::Http2HeaderBlock hints = headers.Clone();
+ hints[":status"] = "103";
+ early_hints_.push_back(std::move(hints));
+ }
+
void set_response_type(SpecialResponseType response_type) {
response_type_ = response_type;
}
@@ -70,6 +79,7 @@
}
private:
+ std::vector<spdy::Http2HeaderBlock> early_hints_;
SpecialResponseType response_type_;
spdy::Http2HeaderBlock headers_;
spdy::Http2HeaderBlock trailers_;
diff --git a/quic/tools/quic_memory_cache_backend.cc b/quic/tools/quic_memory_cache_backend.cc
index 1e116ca..f1acc4a 100644
--- a/quic/tools/quic_memory_cache_backend.cc
+++ b/quic/tools/quic_memory_cache_backend.cc
@@ -199,7 +199,7 @@
absl::string_view response_body) {
AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE,
std::move(response_headers), response_body,
- Http2HeaderBlock());
+ Http2HeaderBlock(), std::vector<spdy::Http2HeaderBlock>());
}
void QuicMemoryCacheBackend::AddResponse(absl::string_view host,
@@ -209,7 +209,19 @@
Http2HeaderBlock response_trailers) {
AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE,
std::move(response_headers), response_body,
- std::move(response_trailers));
+ std::move(response_trailers),
+ std::vector<spdy::Http2HeaderBlock>());
+}
+
+void QuicMemoryCacheBackend::AddResponseWithEarlyHints(
+ absl::string_view host,
+ absl::string_view path,
+ spdy::Http2HeaderBlock response_headers,
+ absl::string_view response_body,
+ const std::vector<spdy::Http2HeaderBlock>& early_hints) {
+ AddResponseImpl(host, path, QuicBackendResponse::REGULAR_RESPONSE,
+ std::move(response_headers), response_body,
+ Http2HeaderBlock(), early_hints);
}
void QuicMemoryCacheBackend::AddSpecialResponse(
@@ -217,7 +229,7 @@
absl::string_view path,
SpecialResponseType response_type) {
AddResponseImpl(host, path, response_type, Http2HeaderBlock(), "",
- Http2HeaderBlock());
+ Http2HeaderBlock(), std::vector<spdy::Http2HeaderBlock>());
}
void QuicMemoryCacheBackend::AddSpecialResponse(
@@ -227,7 +239,8 @@
absl::string_view response_body,
SpecialResponseType response_type) {
AddResponseImpl(host, path, response_type, std::move(response_headers),
- response_body, Http2HeaderBlock());
+ response_body, Http2HeaderBlock(),
+ std::vector<spdy::Http2HeaderBlock>());
}
QuicMemoryCacheBackend::QuicMemoryCacheBackend() : cache_initialized_(false) {}
@@ -362,7 +375,8 @@
SpecialResponseType response_type,
Http2HeaderBlock response_headers,
absl::string_view response_body,
- Http2HeaderBlock response_trailers) {
+ Http2HeaderBlock response_trailers,
+ const std::vector<spdy::Http2HeaderBlock>& early_hints) {
QuicWriterMutexLock lock(&response_mutex_);
QUICHE_DCHECK(!host.empty())
@@ -378,6 +392,9 @@
new_response->set_headers(std::move(response_headers));
new_response->set_body(response_body);
new_response->set_trailers(std::move(response_trailers));
+ for (auto& headers : early_hints) {
+ new_response->AddEarlyHints(headers);
+ }
QUIC_DVLOG(1) << "Add response with key " << key;
responses_[key] = std::move(new_response);
}
diff --git a/quic/tools/quic_memory_cache_backend.h b/quic/tools/quic_memory_cache_backend.h
index 5e4b267..8ba6d60 100644
--- a/quic/tools/quic_memory_cache_backend.h
+++ b/quic/tools/quic_memory_cache_backend.h
@@ -110,6 +110,14 @@
absl::string_view response_body,
spdy::Http2HeaderBlock response_trailers);
+ // Add a response, with 103 Early Hints, to the cache.
+ void AddResponseWithEarlyHints(
+ absl::string_view host,
+ absl::string_view path,
+ spdy::Http2HeaderBlock response_headers,
+ absl::string_view response_body,
+ const std::vector<spdy::Http2HeaderBlock>& early_hints);
+
// Simulate a special behavior at a particular path.
void AddSpecialResponse(
absl::string_view host,
@@ -152,7 +160,8 @@
QuicBackendResponse::SpecialResponseType response_type,
spdy::Http2HeaderBlock response_headers,
absl::string_view response_body,
- spdy::Http2HeaderBlock response_trailers);
+ spdy::Http2HeaderBlock response_trailers,
+ const std::vector<spdy::Http2HeaderBlock>& early_hints);
std::string GetKey(absl::string_view host, absl::string_view path) const;
diff --git a/quic/tools/quic_simple_server_stream.cc b/quic/tools/quic_simple_server_stream.cc
index fef2d3e..6bc93cb 100644
--- a/quic/tools/quic_simple_server_stream.cc
+++ b/quic/tools/quic_simple_server_stream.cc
@@ -206,6 +206,13 @@
return;
}
+ // Send Early Hints first.
+ for (const auto& headers : response->early_hints()) {
+ QUIC_DVLOG(1) << "Stream " << id() << " sending an Early Hints response: "
+ << headers.DebugString();
+ WriteHeaders(headers.Clone(), false, nullptr);
+ }
+
if (response->response_type() == QuicBackendResponse::CLOSE_CONNECTION) {
QUIC_DVLOG(1) << "Special response: closing connection.";
OnUnrecoverableError(QUIC_NO_ERROR, "Toy server forcing close");
diff --git a/quic/tools/quic_simple_server_stream_test.cc b/quic/tools/quic_simple_server_stream_test.cc
index 13b0335..dd3b8cd 100644
--- a/quic/tools/quic_simple_server_stream_test.cc
+++ b/quic/tools/quic_simple_server_stream_test.cc
@@ -59,12 +59,17 @@
~TestStream() override = default;
MOCK_METHOD(void, WriteHeadersMock, (bool fin), ());
+ MOCK_METHOD(void, WriteEarlyHintsHeadersMock, (bool fin), ());
- size_t WriteHeaders(spdy::Http2HeaderBlock /*header_block*/,
+ size_t WriteHeaders(spdy::Http2HeaderBlock header_block,
bool fin,
QuicReferenceCountedPointer<QuicAckListenerInterface>
/*ack_listener*/) override {
- WriteHeadersMock(fin);
+ if (header_block[":status"] == "103") {
+ WriteEarlyHintsHeadersMock(fin);
+ } else {
+ WriteHeadersMock(fin);
+ }
return 0;
}
@@ -581,6 +586,50 @@
EXPECT_EQ(*request_headers, session_.original_request_headers_);
}
+TEST_P(QuicSimpleServerStreamTest, SendResponseWithEarlyHints) {
+ std::string host = "www.google.com";
+ std::string request_path = "/foo";
+ std::string body = "Yummm";
+
+ // Add a request and response with early hints.
+ spdy::Http2HeaderBlock* request_headers = stream_->mutable_headers();
+ (*request_headers)[":path"] = request_path;
+ (*request_headers)[":authority"] = host;
+ (*request_headers)[":method"] = "GET";
+
+ std::unique_ptr<char[]> buffer;
+ QuicByteCount header_length =
+ HttpEncoder::SerializeDataFrameHeader(body.length(), &buffer);
+ std::vector<spdy::Http2HeaderBlock> early_hints;
+ // Add two Early Hints.
+ const size_t kNumEarlyHintsResponses = 2;
+ for (size_t i = 0; i < kNumEarlyHintsResponses; ++i) {
+ spdy::Http2HeaderBlock hints;
+ hints["link"] = "</image.png>; rel=preload; as=image";
+ early_hints.push_back(std::move(hints));
+ }
+
+ response_headers_[":status"] = "200";
+ response_headers_["content-length"] = "5";
+ memory_cache_backend_.AddResponseWithEarlyHints(
+ host, request_path, std::move(response_headers_), body, early_hints);
+ QuicStreamPeer::SetFinReceived(stream_);
+
+ InSequence s;
+ for (size_t i = 0; i < kNumEarlyHintsResponses; ++i) {
+ EXPECT_CALL(*stream_, WriteEarlyHintsHeadersMock(false));
+ }
+ EXPECT_CALL(*stream_, WriteHeadersMock(false));
+ if (UsesHttp3()) {
+ EXPECT_CALL(session_, WritevData(_, header_length, _, NO_FIN, _, _));
+ }
+ EXPECT_CALL(session_, WritevData(_, body.length(), _, FIN, _, _));
+
+ stream_->DoSendResponse();
+ 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]) {