// 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 #include "base/files/file_path.h" #include "base/memory/singleton.h" #include "base/path_service.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "net/spdy/spdy_framer.h" #include "net/tools/balsa/balsa_headers.h" #include "net/tools/quic/quic_in_memory_cache.h" #include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h" #include "testing/gtest/include/gtest/gtest.h" using base::IntToString; using base::StringPiece; using net::SpdyHeaderBlock; using std::string; namespace net { namespace tools { namespace test { namespace { typedef QuicInMemoryCache::Response Response; typedef QuicInMemoryCache::ServerPushInfo ServerPushInfo; }; // namespace class QuicInMemoryCacheTest : public ::testing::Test { protected: QuicInMemoryCacheTest() { QuicInMemoryCachePeer::ResetForTests(); } ~QuicInMemoryCacheTest() override { QuicInMemoryCachePeer::ResetForTests(); } void CreateRequest(string host, string path, BalsaHeaders* headers) { headers->SetRequestFirstlineFromStringPieces("GET", path, "HTTP/1.1"); headers->ReplaceOrAppendHeader("host", host); } string CacheDirectory() { base::FilePath path; PathService::Get(base::DIR_SOURCE_ROOT, &path); path = path.AppendASCII("net").AppendASCII("data").AppendASCII( "quic_in_memory_cache_data"); // The file path is known to be an ascii string. return path.MaybeAsASCII(); } }; TEST_F(QuicInMemoryCacheTest, GetResponseNoMatch) { const QuicInMemoryCache::Response* response = QuicInMemoryCache::GetInstance()->GetResponse("mail.google.com", "/index.html"); ASSERT_FALSE(response); } TEST_F(QuicInMemoryCacheTest, AddSimpleResponseGetResponse) { string response_body("hello response"); QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance(); cache->AddSimpleResponse("www.google.com", "/", 200, response_body); BalsaHeaders request_headers; CreateRequest("www.google.com", "/", &request_headers); const QuicInMemoryCache::Response* response = cache->GetResponse("www.google.com", "/"); ASSERT_TRUE(response); ASSERT_TRUE(ContainsKey(response->headers(), ":status")); EXPECT_EQ("200", response->headers().find(":status")->second); EXPECT_EQ(response_body.size(), response->body().length()); } TEST_F(QuicInMemoryCacheTest, AddResponse) { const string kRequestHost = "www.foo.com"; const string kRequestPath = "/"; const string kResponseBody("hello response"); SpdyHeaderBlock response_headers; response_headers[":version"] = "HTTP/1.1"; response_headers[":status"] = "200"; response_headers["content-length"] = IntToString(kResponseBody.size()); SpdyHeaderBlock response_trailers; response_trailers["key-1"] = "value-1"; response_trailers["key-2"] = "value-2"; response_trailers["key-3"] = "value-3"; QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance(); cache->AddResponse(kRequestHost, "/", response_headers, kResponseBody, response_trailers); const QuicInMemoryCache::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(QuicInMemoryCacheTest, ReadsCacheDir) { QuicInMemoryCache::GetInstance()->InitializeFromDirectory(CacheDirectory()); const QuicInMemoryCache::Response* response = QuicInMemoryCache::GetInstance()->GetResponse("quic.test.url", "/index.html"); ASSERT_TRUE(response); ASSERT_TRUE(ContainsKey(response->headers(), ":status")); EXPECT_EQ("200", response->headers().find(":status")->second); ASSERT_TRUE(ContainsKey(response->headers(), "connection")); EXPECT_EQ("close", response->headers().find("connection")->second); EXPECT_LT(0U, response->body().length()); } TEST_F(QuicInMemoryCacheTest, UsesOriginalUrl) { QuicInMemoryCache::GetInstance()->InitializeFromDirectory(CacheDirectory()); const QuicInMemoryCache::Response* response = QuicInMemoryCache::GetInstance()->GetResponse("quic.test.url", "/index.html"); ASSERT_TRUE(response); ASSERT_TRUE(ContainsKey(response->headers(), ":status")); EXPECT_EQ("200", response->headers().find(":status")->second); ASSERT_TRUE(ContainsKey(response->headers(), "connection")); EXPECT_EQ("close", response->headers().find("connection")->second); EXPECT_LT(0U, response->body().length()); } TEST_F(QuicInMemoryCacheTest, DefaultResponse) { // Verify GetResponse returns nullptr when no default is set. QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance(); const QuicInMemoryCache::Response* response = cache->GetResponse("www.google.com", "/"); ASSERT_FALSE(response); // Add a default response. SpdyHeaderBlock response_headers; response_headers[":version"] = "HTTP/1.1"; response_headers[":status"] = "200"; response_headers["content-length"] = "0"; QuicInMemoryCache::Response* default_response = new QuicInMemoryCache::Response; default_response->set_headers(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(ContainsKey(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(ContainsKey(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(ContainsKey(response->headers(), ":status")); EXPECT_EQ("200", response->headers().find(":status")->second); } TEST_F(QuicInMemoryCacheTest, AddSimpleResponseWithServerPushResources) { string request_host = "www.foo.com"; string response_body("hello response"); const size_t kNumResources = 5; int NumResources = 5; list push_resources; string scheme = "http"; for (int i = 0; i < NumResources; ++i) { string path = "/server_push_src" + base::IntToString(i); string url = scheme + "://" + request_host + path; GURL resource_url(url); string body = "This is server push response body for " + path; SpdyHeaderBlock response_headers; response_headers[":version"] = "HTTP/1.1"; response_headers[":status"] = "200"; response_headers["content-length"] = base::UintToString(body.size()); push_resources.push_back( ServerPushInfo(resource_url, response_headers, i, body)); } QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance(); cache->AddSimpleResponseWithServerPushResources( request_host, "/", 200, response_body, push_resources); string request_url = request_host + "/"; std::list 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.spec(), push_resource.request_url.spec()); EXPECT_EQ(resource.priority, push_resource.priority); resources.pop_front(); } } TEST_F(QuicInMemoryCacheTest, GetServerPushResourcesAndPushResponses) { string request_host = "www.foo.com"; string response_body("hello response"); const size_t kNumResources = 4; int NumResources = 4; string scheme = "http"; string push_response_status[kNumResources] = {"200", "200", "301", "404"}; list push_resources; for (int i = 0; i < NumResources; ++i) { string path = "/server_push_src" + base::IntToString(i); string url = scheme + "://" + request_host + path; GURL resource_url(url); string body = "This is server push response body for " + path; SpdyHeaderBlock response_headers; response_headers[":version"] = "HTTP/1.1"; response_headers[":status"] = push_response_status[i]; response_headers["content-length"] = base::UintToString(body.size()); push_resources.push_back( ServerPushInfo(resource_url, response_headers, i, body)); } QuicInMemoryCache* cache = QuicInMemoryCache::GetInstance(); cache->AddSimpleResponseWithServerPushResources( request_host, "/", 200, response_body, push_resources); string request_url = request_host + "/"; std::list resources = cache->GetServerPushResources(request_url); ASSERT_EQ(kNumResources, resources.size()); int i = 0; for (const auto& push_resource : push_resources) { GURL url = resources.front().request_url; string host = url.host(); string path = url.path(); const QuicInMemoryCache::Response* response = cache->GetResponse(host, path); ASSERT_TRUE(response); ASSERT_TRUE(ContainsKey(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 tools } // namespace net