Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/string_util.h"
      6 #include "net/http/http_auth_cache.h"
      7 
      8 #include "testing/gtest/include/gtest/gtest.h"
      9 
     10 namespace net {
     11 
     12 namespace {
     13 
     14 class MockAuthHandler : public HttpAuthHandler {
     15  public:
     16   MockAuthHandler(const char* scheme, const std::string& realm,
     17                   HttpAuth::Target target) {
     18     // Can't use initializer list since these are members of the base class.
     19     scheme_ = scheme;
     20     realm_ = realm;
     21     score_ = 1;
     22     target_ = target;
     23     properties_ = 0;
     24   }
     25 
     26  virtual std::string GenerateCredentials(const std::wstring&,
     27                                           const std::wstring&,
     28                                           const HttpRequestInfo*,
     29                                           const ProxyInfo*) {
     30     return "mock-credentials";  // Unused.
     31   }
     32 
     33  protected:
     34   virtual bool Init(std::string::const_iterator, std::string::const_iterator) {
     35     return false;  // Unused.
     36   }
     37 
     38  private:
     39   ~MockAuthHandler() {}
     40 };
     41 
     42 }  // namespace
     43 
     44 // Test adding and looking-up cache entries (both by realm and by path).
     45 TEST(HttpAuthCacheTest, Basic) {
     46   GURL origin("http://www.google.com");
     47   HttpAuthCache cache;
     48   HttpAuthCache::Entry* entry;
     49 
     50   // Add cache entries for 3 realms: "Realm1", "Realm2", "Realm3"
     51 
     52   scoped_refptr<HttpAuthHandler> realm1_handler =
     53       new MockAuthHandler("basic", "Realm1", HttpAuth::AUTH_SERVER);
     54   cache.Add(origin, realm1_handler, L"realm1-user", L"realm1-password",
     55       "/foo/bar/index.html");
     56 
     57   scoped_refptr<HttpAuthHandler> realm2_handler =
     58       new MockAuthHandler("basic", "Realm2", HttpAuth::AUTH_SERVER);
     59   cache.Add(origin, realm2_handler, L"realm2-user", L"realm2-password",
     60       "/foo2/index.html");
     61 
     62   scoped_refptr<HttpAuthHandler> realm3_handler =
     63       new MockAuthHandler("basic", "Realm3", HttpAuth::AUTH_PROXY);
     64   cache.Add(origin, realm3_handler, L"realm3-user", L"realm3-password", "");
     65 
     66   // There is no Realm4
     67   entry = cache.LookupByRealm(origin, "Realm4");
     68   EXPECT_TRUE(NULL == entry);
     69 
     70   // While Realm3 does exist, the origin scheme is wrong.
     71   entry = cache.LookupByRealm(GURL("https://www.google.com"), "Realm3");
     72   EXPECT_TRUE(NULL == entry);
     73 
     74   // Valid lookup by realm.
     75   entry = cache.LookupByRealm(GURL("http://www.google.com:80"), "Realm3");
     76   EXPECT_FALSE(NULL == entry);
     77   EXPECT_TRUE(entry->handler() == realm3_handler.get());
     78   EXPECT_EQ(L"realm3-user", entry->username());
     79   EXPECT_EQ(L"realm3-password", entry->password());
     80 
     81   // Valid lookup by realm.
     82   entry = cache.LookupByRealm(origin, "Realm2");
     83   EXPECT_FALSE(NULL == entry);
     84   EXPECT_TRUE(entry->handler() == realm2_handler.get());
     85   EXPECT_EQ(L"realm2-user", entry->username());
     86   EXPECT_EQ(L"realm2-password", entry->password());
     87 
     88   // Check that subpaths are recognized.
     89   HttpAuthCache::Entry* realm2Entry = cache.LookupByRealm(origin, "Realm2");
     90   EXPECT_FALSE(NULL == realm2Entry);
     91   // Positive tests:
     92   entry = cache.LookupByPath(origin, "/foo2/index.html");
     93   EXPECT_TRUE(realm2Entry == entry);
     94   entry = cache.LookupByPath(origin, "/foo2/foobar.html");
     95   EXPECT_TRUE(realm2Entry == entry);
     96   entry = cache.LookupByPath(origin, "/foo2/bar/index.html");
     97   EXPECT_TRUE(realm2Entry == entry);
     98   entry = cache.LookupByPath(origin, "/foo2/");
     99   EXPECT_TRUE(realm2Entry == entry);
    100   // Negative tests:
    101   entry = cache.LookupByPath(origin, "/foo2");
    102   EXPECT_FALSE(realm2Entry == entry);
    103   entry = cache.LookupByPath(origin, "/foo3/index.html");
    104   EXPECT_FALSE(realm2Entry == entry);
    105   entry = cache.LookupByPath(origin, "");
    106   EXPECT_FALSE(realm2Entry == entry);
    107   entry = cache.LookupByPath(origin, "/");
    108   EXPECT_FALSE(realm2Entry == entry);
    109 
    110   // Lookup using empty path (may be used for proxy).
    111   entry = cache.LookupByPath(origin, "");
    112   EXPECT_FALSE(NULL == entry);
    113   EXPECT_TRUE(entry->handler() == realm3_handler.get());
    114   EXPECT_EQ("Realm3", entry->realm());
    115 }
    116 
    117 TEST(HttpAuthCacheTest, AddPath) {
    118   HttpAuthCache::Entry entry;
    119 
    120   // All of these paths have a common root /1/2/2/4/5/
    121   entry.AddPath("/1/2/3/4/5/x.txt");
    122   entry.AddPath("/1/2/3/4/5/y.txt");
    123   entry.AddPath("/1/2/3/4/5/z.txt");
    124 
    125   EXPECT_EQ(1U, entry.paths_.size());
    126   EXPECT_EQ("/1/2/3/4/5/", entry.paths_.front());
    127 
    128   // Add a new entry (not a subpath).
    129   entry.AddPath("/1/XXX/q");
    130   EXPECT_EQ(2U, entry.paths_.size());
    131   EXPECT_EQ("/1/XXX/", entry.paths_.front());
    132   EXPECT_EQ("/1/2/3/4/5/", entry.paths_.back());
    133 
    134   // Add containing paths of /1/2/3/4/5/ -- should swallow up the deeper paths.
    135   entry.AddPath("/1/2/3/4/x.txt");
    136   EXPECT_EQ(2U, entry.paths_.size());
    137   EXPECT_EQ("/1/2/3/4/", entry.paths_.front());
    138   EXPECT_EQ("/1/XXX/", entry.paths_.back());
    139   entry.AddPath("/1/2/3/x");
    140   EXPECT_EQ(2U, entry.paths_.size());
    141   EXPECT_EQ("/1/2/3/", entry.paths_.front());
    142   EXPECT_EQ("/1/XXX/", entry.paths_.back());
    143 
    144   entry.AddPath("/index.html");
    145   EXPECT_EQ(1U, entry.paths_.size());
    146   EXPECT_EQ("/", entry.paths_.front());
    147 }
    148 
    149 // Calling Add when the realm entry already exists, should append that
    150 // path.
    151 TEST(HttpAuthCacheTest, AddToExistingEntry) {
    152   HttpAuthCache cache;
    153   GURL origin("http://www.foobar.com:70");
    154 
    155   scoped_refptr<HttpAuthHandler> handler =
    156       new MockAuthHandler("basic", "MyRealm", HttpAuth::AUTH_SERVER);
    157 
    158   HttpAuthCache::Entry* orig_entry = cache.Add(
    159       origin, handler, L"user1", L"password1", "/x/y/z/");
    160   cache.Add(origin, handler, L"user2", L"password2", "/z/y/x/");
    161   cache.Add(origin, handler, L"user3", L"password3", "/z/y");
    162 
    163   HttpAuthCache::Entry* entry = cache.LookupByRealm(origin, "MyRealm");
    164 
    165   EXPECT_TRUE(entry == orig_entry);
    166   EXPECT_EQ(L"user3", entry->username());
    167   EXPECT_EQ(L"password3", entry->password());
    168 
    169   EXPECT_EQ(2U, entry->paths_.size());
    170   EXPECT_EQ("/z/", entry->paths_.front());
    171   EXPECT_EQ("/x/y/z/", entry->paths_.back());
    172 }
    173 
    174 TEST(HttpAuthCacheTest, Remove) {
    175   GURL origin("http://foobar2.com");
    176 
    177   scoped_refptr<HttpAuthHandler> realm1_handler =
    178       new MockAuthHandler("basic", "Realm1", HttpAuth::AUTH_SERVER);
    179 
    180   scoped_refptr<HttpAuthHandler> realm2_handler =
    181       new MockAuthHandler("basic", "Realm2", HttpAuth::AUTH_SERVER);
    182 
    183   scoped_refptr<HttpAuthHandler> realm3_handler =
    184       new MockAuthHandler("basic", "Realm3", HttpAuth::AUTH_SERVER);
    185 
    186   HttpAuthCache cache;
    187   cache.Add(origin, realm1_handler, L"alice", L"123", "/");
    188   cache.Add(origin, realm2_handler, L"bob", L"princess", "/");
    189   cache.Add(origin, realm3_handler, L"admin", L"password", "/");
    190 
    191   // Fails, because there is no realm "Realm4".
    192   EXPECT_FALSE(cache.Remove(origin, "Realm4", L"alice", L"123"));
    193 
    194   // Fails because the origin is wrong.
    195   EXPECT_FALSE(cache.Remove(
    196       GURL("http://foobar2.com:100"), "Realm1", L"alice", L"123"));
    197 
    198   // Fails because the username is wrong.
    199   EXPECT_FALSE(cache.Remove(origin, "Realm1", L"alice2", L"123"));
    200 
    201   // Fails because the password is wrong.
    202   EXPECT_FALSE(cache.Remove(origin, "Realm1", L"alice", L"1234"));
    203 
    204   // Succeeds.
    205   EXPECT_TRUE(cache.Remove(origin, "Realm1", L"alice", L"123"));
    206 
    207   // Fails because we just deleted the entry!
    208   EXPECT_FALSE(cache.Remove(origin, "Realm1", L"alice", L"123"));
    209 }
    210 
    211 // Test fixture class for eviction tests (contains helpers for bulk
    212 // insertion and existence testing).
    213 class HttpAuthCacheEvictionTest : public testing::Test {
    214  protected:
    215   HttpAuthCacheEvictionTest() : origin_("http://www.google.com") { }
    216 
    217   std::string GenerateRealm(int realm_i) {
    218     return StringPrintf("Realm %d", realm_i);
    219   }
    220 
    221   std::string GeneratePath(int realm_i, int path_i) {
    222     return StringPrintf("/%d/%d/x/y", realm_i, path_i);
    223   }
    224 
    225   void AddRealm(int realm_i) {
    226     AddPathToRealm(realm_i, 0);
    227   }
    228 
    229   void AddPathToRealm(int realm_i, int path_i) {
    230     scoped_refptr<HttpAuthHandler> handler = new MockAuthHandler("basic",
    231         GenerateRealm(realm_i), HttpAuth::AUTH_SERVER);
    232     std::string path = GeneratePath(realm_i, path_i);
    233     cache_.Add(origin_, handler, L"username", L"password", path);
    234   }
    235 
    236   void CheckRealmExistence(int realm_i, bool exists) {
    237     const HttpAuthCache::Entry* entry =
    238         cache_.LookupByRealm(origin_, GenerateRealm(realm_i));
    239     if (exists) {
    240       EXPECT_FALSE(entry == NULL);
    241       EXPECT_EQ(GenerateRealm(realm_i), entry->realm());
    242     } else {
    243       EXPECT_TRUE(entry == NULL);
    244     }
    245   }
    246 
    247   void CheckPathExistence(int realm_i, int path_i, bool exists) {
    248     const HttpAuthCache::Entry* entry =
    249         cache_.LookupByPath(origin_, GeneratePath(realm_i, path_i));
    250     if (exists) {
    251       EXPECT_FALSE(entry == NULL);
    252       EXPECT_EQ(GenerateRealm(realm_i), entry->realm());
    253     } else {
    254       EXPECT_TRUE(entry == NULL);
    255     }
    256   }
    257 
    258   GURL origin_;
    259   HttpAuthCache cache_;
    260 
    261   static const int kMaxPaths = HttpAuthCache::kMaxNumPathsPerRealmEntry;
    262   static const int kMaxRealms = HttpAuthCache::kMaxNumRealmEntries;
    263 };
    264 
    265 // Add the maxinim number of realm entries to the cache. Each of these entries
    266 // must still be retrievable. Next add three more entries -- since the cache is
    267 // full this causes FIFO eviction of the first three entries.
    268 TEST_F(HttpAuthCacheEvictionTest, RealmEntryEviction) {
    269   for (int i = 0; i < kMaxRealms; ++i)
    270     AddRealm(i);
    271 
    272   for (int i = 0; i < kMaxRealms; ++i)
    273     CheckRealmExistence(i, true);
    274 
    275   for (int i = 0; i < 3; ++i)
    276     AddRealm(i + kMaxRealms);
    277 
    278   for (int i = 0; i < 3; ++i)
    279     CheckRealmExistence(i, false);
    280 
    281   for (int i = 0; i < kMaxRealms; ++i)
    282     CheckRealmExistence(i + 3, true);
    283 }
    284 
    285 // Add the maximum number of paths to a single realm entry. Each of these
    286 // paths should be retrievable. Next add 3 more paths -- since the cache is
    287 // full this causes FIFO eviction of the first three paths.
    288 TEST_F(HttpAuthCacheEvictionTest, RealmPathEviction) {
    289   for (int i = 0; i < kMaxPaths; ++i)
    290     AddPathToRealm(0, i);
    291 
    292   for (int i = 1; i < kMaxRealms; ++i)
    293     AddRealm(i);
    294 
    295   for (int i = 0; i < 3; ++i)
    296     AddPathToRealm(0, i + kMaxPaths);
    297 
    298   for (int i = 0; i < 3; ++i)
    299     CheckPathExistence(0, i, false);
    300 
    301   for (int i = 0; i < kMaxPaths; ++i)
    302     CheckPathExistence(0, i + 3, true);
    303 
    304   for (int i = 0; i < kMaxRealms; ++i)
    305     CheckRealmExistence(i, true);
    306 }
    307 
    308 }  // namespace net
    309