| // 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 "net/third_party/quiche/src/quic/tools/quic_memory_cache_backend.h" | 
 |  | 
 | #include "absl/strings/match.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_file_utils.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" | 
 | #include "net/third_party/quiche/src/quic/tools/quic_backend_response.h" | 
 | #include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" | 
 | #include "net/third_party/quiche/src/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 = | 
 |         quiche::QuicheStrCat("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 |