| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "quic/tools/quic_memory_cache_backend.h" |
| |
| #include "absl/strings/match.h" |
| #include "absl/strings/str_cat.h" |
| #include "quic/platform/api/quic_file_utils.h" |
| #include "quic/platform/api/quic_map_util.h" |
| #include "quic/platform/api/quic_test.h" |
| #include "quic/tools/quic_backend_response.h" |
| #include "common/platform/api/quiche_text_utils.h" |
| |
| namespace quic { |
| namespace test { |
| |
| namespace { |
| using Response = QuicBackendResponse; |
| using ServerPushInfo = QuicBackendResponse::ServerPushInfo; |
| } // namespace |
| |
| class QuicMemoryCacheBackendTest : public QuicTest { |
| protected: |
| void CreateRequest(std::string host, |
| std::string path, |
| spdy::Http2HeaderBlock* headers) { |
| (*headers)[":method"] = "GET"; |
| (*headers)[":path"] = path; |
| (*headers)[":authority"] = host; |
| (*headers)[":scheme"] = "https"; |
| } |
| |
| std::string CacheDirectory() { return QuicGetTestMemoryCachePath(); } |
| |
| QuicMemoryCacheBackend cache_; |
| }; |
| |
| TEST_F(QuicMemoryCacheBackendTest, GetResponseNoMatch) { |
| const Response* response = |
| cache_.GetResponse("mail.google.com", "/index.html"); |
| ASSERT_FALSE(response); |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, AddSimpleResponseGetResponse) { |
| std::string response_body("hello response"); |
| cache_.AddSimpleResponse("www.google.com", "/", 200, response_body); |
| |
| spdy::Http2HeaderBlock request_headers; |
| CreateRequest("www.google.com", "/", &request_headers); |
| const Response* response = cache_.GetResponse("www.google.com", "/"); |
| ASSERT_TRUE(response); |
| ASSERT_TRUE(QuicContainsKey(response->headers(), ":status")); |
| EXPECT_EQ("200", response->headers().find(":status")->second); |
| EXPECT_EQ(response_body.size(), response->body().length()); |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, AddResponse) { |
| const std::string kRequestHost = "www.foo.com"; |
| const std::string kRequestPath = "/"; |
| const std::string kResponseBody("hello response"); |
| |
| spdy::Http2HeaderBlock response_headers; |
| response_headers[":status"] = "200"; |
| response_headers["content-length"] = |
| quiche::QuicheTextUtils::Uint64ToString(kResponseBody.size()); |
| |
| spdy::Http2HeaderBlock response_trailers; |
| response_trailers["key-1"] = "value-1"; |
| response_trailers["key-2"] = "value-2"; |
| response_trailers["key-3"] = "value-3"; |
| |
| cache_.AddResponse(kRequestHost, "/", response_headers.Clone(), kResponseBody, |
| response_trailers.Clone()); |
| |
| const Response* response = cache_.GetResponse(kRequestHost, kRequestPath); |
| EXPECT_EQ(response->headers(), response_headers); |
| EXPECT_EQ(response->body(), kResponseBody); |
| EXPECT_EQ(response->trailers(), response_trailers); |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDir) { |
| cache_.InitializeBackend(CacheDirectory()); |
| const Response* response = |
| cache_.GetResponse("test.example.com", "/index.html"); |
| ASSERT_TRUE(response); |
| ASSERT_TRUE(QuicContainsKey(response->headers(), ":status")); |
| EXPECT_EQ("200", response->headers().find(":status")->second); |
| // Connection headers are not valid in HTTP/2. |
| EXPECT_FALSE(QuicContainsKey(response->headers(), "connection")); |
| EXPECT_LT(0U, response->body().length()); |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDirWithServerPushResource) { |
| cache_.InitializeBackend(CacheDirectory() + "_with_push"); |
| std::list<ServerPushInfo> resources = |
| cache_.GetServerPushResources("test.example.com/"); |
| ASSERT_EQ(1UL, resources.size()); |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, ReadsCacheDirWithServerPushResources) { |
| cache_.InitializeBackend(CacheDirectory() + "_with_push"); |
| std::list<ServerPushInfo> resources = |
| cache_.GetServerPushResources("test.example.com/index2.html"); |
| ASSERT_EQ(2UL, resources.size()); |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrl) { |
| cache_.InitializeBackend(CacheDirectory()); |
| const Response* response = |
| cache_.GetResponse("test.example.com", "/site_map.html"); |
| ASSERT_TRUE(response); |
| ASSERT_TRUE(QuicContainsKey(response->headers(), ":status")); |
| EXPECT_EQ("200", response->headers().find(":status")->second); |
| // Connection headers are not valid in HTTP/2. |
| EXPECT_FALSE(QuicContainsKey(response->headers(), "connection")); |
| EXPECT_LT(0U, response->body().length()); |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, UsesOriginalUrlOnly) { |
| // Tests that if the URL cannot be inferred correctly from the path |
| // because the directory does not include the hostname, that the |
| // X-Original-Url header's value will be used. |
| std::string dir; |
| std::string path = "map.html"; |
| for (const std::string& file : ReadFileContents(CacheDirectory())) { |
| if (absl::EndsWithIgnoreCase(file, "map.html")) { |
| dir = file; |
| dir.erase(dir.length() - path.length() - 1); |
| break; |
| } |
| } |
| ASSERT_NE("", dir); |
| |
| cache_.InitializeBackend(dir); |
| const Response* response = |
| cache_.GetResponse("test.example.com", "/site_map.html"); |
| ASSERT_TRUE(response); |
| ASSERT_TRUE(QuicContainsKey(response->headers(), ":status")); |
| EXPECT_EQ("200", response->headers().find(":status")->second); |
| // Connection headers are not valid in HTTP/2. |
| EXPECT_FALSE(QuicContainsKey(response->headers(), "connection")); |
| EXPECT_LT(0U, response->body().length()); |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, DefaultResponse) { |
| // Verify GetResponse returns nullptr when no default is set. |
| const Response* response = cache_.GetResponse("www.google.com", "/"); |
| ASSERT_FALSE(response); |
| |
| // Add a default response. |
| spdy::Http2HeaderBlock response_headers; |
| response_headers[":status"] = "200"; |
| response_headers["content-length"] = "0"; |
| Response* default_response = new Response; |
| default_response->set_headers(std::move(response_headers)); |
| cache_.AddDefaultResponse(default_response); |
| |
| // Now we should get the default response for the original request. |
| response = cache_.GetResponse("www.google.com", "/"); |
| ASSERT_TRUE(response); |
| ASSERT_TRUE(QuicContainsKey(response->headers(), ":status")); |
| EXPECT_EQ("200", response->headers().find(":status")->second); |
| |
| // Now add a set response for / and make sure it is returned |
| cache_.AddSimpleResponse("www.google.com", "/", 302, ""); |
| response = cache_.GetResponse("www.google.com", "/"); |
| ASSERT_TRUE(response); |
| ASSERT_TRUE(QuicContainsKey(response->headers(), ":status")); |
| EXPECT_EQ("302", response->headers().find(":status")->second); |
| |
| // We should get the default response for other requests. |
| response = cache_.GetResponse("www.google.com", "/asd"); |
| ASSERT_TRUE(response); |
| ASSERT_TRUE(QuicContainsKey(response->headers(), ":status")); |
| EXPECT_EQ("200", response->headers().find(":status")->second); |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, AddSimpleResponseWithServerPushResources) { |
| std::string request_host = "www.foo.com"; |
| std::string response_body("hello response"); |
| const size_t kNumResources = 5; |
| int NumResources = 5; |
| std::list<ServerPushInfo> push_resources; |
| std::string scheme = "http"; |
| for (int i = 0; i < NumResources; ++i) { |
| std::string path = |
| "/server_push_src" + quiche::QuicheTextUtils::Uint64ToString(i); |
| std::string url = scheme + "://" + request_host + path; |
| QuicUrl resource_url(url); |
| std::string body = |
| absl::StrCat("This is server push response body for ", path); |
| spdy::Http2HeaderBlock response_headers; |
| response_headers[":status"] = "200"; |
| response_headers["content-length"] = |
| quiche::QuicheTextUtils::Uint64ToString(body.size()); |
| push_resources.push_back( |
| ServerPushInfo(resource_url, response_headers.Clone(), i, body)); |
| } |
| |
| cache_.AddSimpleResponseWithServerPushResources( |
| request_host, "/", 200, response_body, push_resources); |
| |
| std::string request_url = request_host + "/"; |
| std::list<ServerPushInfo> resources = |
| cache_.GetServerPushResources(request_url); |
| ASSERT_EQ(kNumResources, resources.size()); |
| for (const auto& push_resource : push_resources) { |
| ServerPushInfo resource = resources.front(); |
| EXPECT_EQ(resource.request_url.ToString(), |
| push_resource.request_url.ToString()); |
| EXPECT_EQ(resource.priority, push_resource.priority); |
| resources.pop_front(); |
| } |
| } |
| |
| TEST_F(QuicMemoryCacheBackendTest, GetServerPushResourcesAndPushResponses) { |
| std::string request_host = "www.foo.com"; |
| std::string response_body("hello response"); |
| const size_t kNumResources = 4; |
| int NumResources = 4; |
| std::string scheme = "http"; |
| std::string push_response_status[kNumResources] = {"200", "200", "301", |
| "404"}; |
| std::list<ServerPushInfo> push_resources; |
| for (int i = 0; i < NumResources; ++i) { |
| std::string path = |
| "/server_push_src" + quiche::QuicheTextUtils::Uint64ToString(i); |
| std::string url = scheme + "://" + request_host + path; |
| QuicUrl resource_url(url); |
| std::string body = "This is server push response body for " + path; |
| spdy::Http2HeaderBlock response_headers; |
| response_headers[":status"] = push_response_status[i]; |
| response_headers["content-length"] = |
| quiche::QuicheTextUtils::Uint64ToString(body.size()); |
| push_resources.push_back( |
| ServerPushInfo(resource_url, response_headers.Clone(), i, body)); |
| } |
| cache_.AddSimpleResponseWithServerPushResources( |
| request_host, "/", 200, response_body, push_resources); |
| std::string request_url = request_host + "/"; |
| std::list<ServerPushInfo> resources = |
| cache_.GetServerPushResources(request_url); |
| ASSERT_EQ(kNumResources, resources.size()); |
| int i = 0; |
| for (const auto& push_resource : push_resources) { |
| QuicUrl url = resources.front().request_url; |
| std::string host = url.host(); |
| std::string path = url.path(); |
| const Response* response = cache_.GetResponse(host, path); |
| ASSERT_TRUE(response); |
| ASSERT_TRUE(QuicContainsKey(response->headers(), ":status")); |
| EXPECT_EQ(push_response_status[i++], |
| response->headers().find(":status")->second); |
| EXPECT_EQ(push_resource.body, response->body()); |
| resources.pop_front(); |
| } |
| } |
| |
| } // namespace test |
| } // namespace quic |