Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2011 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 <string>
      6 
      7 #include "base/strings/string16.h"
      8 #include "base/strings/string_util.h"
      9 #include "base/strings/stringprintf.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "net/base/net_errors.h"
     12 #include "net/http/http_auth_cache.h"
     13 #include "net/http/http_auth_handler.h"
     14 #include "testing/gtest/include/gtest/gtest.h"
     15 
     16 namespace net {
     17 
     18 namespace {
     19 
     20 class MockAuthHandler : public HttpAuthHandler {
     21  public:
     22   MockAuthHandler(HttpAuth::Scheme scheme,
     23                   const std::string& realm,
     24                   HttpAuth::Target target) {
     25     // Can't use initializer list since these are members of the base class.
     26     auth_scheme_ = scheme;
     27     realm_ = realm;
     28     score_ = 1;
     29     target_ = target;
     30     properties_ = 0;
     31   }
     32 
     33   virtual HttpAuth::AuthorizationResult HandleAnotherChallenge(
     34       HttpAuth::ChallengeTokenizer* challenge) OVERRIDE {
     35     return HttpAuth::AUTHORIZATION_RESULT_REJECT;
     36   }
     37 
     38  protected:
     39   virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE {
     40     return false;  // Unused.
     41   }
     42 
     43   virtual int GenerateAuthTokenImpl(const AuthCredentials*,
     44                                     const HttpRequestInfo*,
     45                                     const CompletionCallback& callback,
     46                                     std::string* auth_token) OVERRIDE {
     47     *auth_token = "mock-credentials";
     48     return OK;
     49   }
     50 
     51 
     52  private:
     53   virtual ~MockAuthHandler() {}
     54 };
     55 
     56 const char* kRealm1 = "Realm1";
     57 const char* kRealm2 = "Realm2";
     58 const char* kRealm3 = "Realm3";
     59 const char* kRealm4 = "Realm4";
     60 const char* kRealm5 = "Realm5";
     61 const base::string16 k123(ASCIIToUTF16("123"));
     62 const base::string16 k1234(ASCIIToUTF16("1234"));
     63 const base::string16 kAdmin(ASCIIToUTF16("admin"));
     64 const base::string16 kAlice(ASCIIToUTF16("alice"));
     65 const base::string16 kAlice2(ASCIIToUTF16("alice2"));
     66 const base::string16 kPassword(ASCIIToUTF16("password"));
     67 const base::string16 kRoot(ASCIIToUTF16("root"));
     68 const base::string16 kUsername(ASCIIToUTF16("username"));
     69 const base::string16 kWileCoyote(ASCIIToUTF16("wilecoyote"));
     70 
     71 AuthCredentials CreateASCIICredentials(const char* username,
     72                                        const char* password) {
     73   return AuthCredentials(ASCIIToUTF16(username), ASCIIToUTF16(password));
     74 }
     75 
     76 }  // namespace
     77 
     78 // Test adding and looking-up cache entries (both by realm and by path).
     79 TEST(HttpAuthCacheTest, Basic) {
     80   GURL origin("http://www.google.com");
     81   HttpAuthCache cache;
     82   HttpAuthCache::Entry* entry;
     83 
     84   // Add cache entries for 4 realms: "Realm1", "Realm2", "Realm3" and
     85   // "Realm4"
     86 
     87   scoped_ptr<HttpAuthHandler> realm1_handler(
     88       new MockAuthHandler(HttpAuth::AUTH_SCHEME_BASIC,
     89                           kRealm1,
     90                           HttpAuth::AUTH_SERVER));
     91   cache.Add(origin, realm1_handler->realm(), realm1_handler->auth_scheme(),
     92             "Basic realm=Realm1",
     93             CreateASCIICredentials("realm1-user", "realm1-password"),
     94             "/foo/bar/index.html");
     95 
     96   scoped_ptr<HttpAuthHandler> realm2_handler(
     97       new MockAuthHandler(HttpAuth::AUTH_SCHEME_BASIC,
     98                           kRealm2,
     99                           HttpAuth::AUTH_SERVER));
    100   cache.Add(origin, realm2_handler->realm(), realm2_handler->auth_scheme(),
    101             "Basic realm=Realm2",
    102             CreateASCIICredentials("realm2-user", "realm2-password"),
    103             "/foo2/index.html");
    104 
    105   scoped_ptr<HttpAuthHandler> realm3_basic_handler(
    106       new MockAuthHandler(HttpAuth::AUTH_SCHEME_BASIC,
    107                           kRealm3,
    108                           HttpAuth::AUTH_PROXY));
    109   cache.Add(
    110       origin,
    111       realm3_basic_handler->realm(),
    112       realm3_basic_handler->auth_scheme(),
    113       "Basic realm=Realm3",
    114       CreateASCIICredentials("realm3-basic-user", "realm3-basic-password"),
    115       std::string());
    116 
    117   scoped_ptr<HttpAuthHandler> realm3_digest_handler(
    118       new MockAuthHandler(HttpAuth::AUTH_SCHEME_DIGEST,
    119                           kRealm3,
    120                           HttpAuth::AUTH_PROXY));
    121   cache.Add(origin, realm3_digest_handler->realm(),
    122             realm3_digest_handler->auth_scheme(), "Digest realm=Realm3",
    123             CreateASCIICredentials("realm3-digest-user",
    124                                    "realm3-digest-password"),
    125             "/baz/index.html");
    126 
    127   scoped_ptr<HttpAuthHandler> realm4_basic_handler(
    128       new MockAuthHandler(HttpAuth::AUTH_SCHEME_BASIC,
    129                           kRealm4,
    130                           HttpAuth::AUTH_SERVER));
    131   cache.Add(origin, realm4_basic_handler->realm(),
    132             realm4_basic_handler->auth_scheme(), "Basic realm=Realm4",
    133             CreateASCIICredentials("realm4-basic-user",
    134                                    "realm4-basic-password"),
    135             "/");
    136 
    137   // There is no Realm5
    138   entry = cache.Lookup(origin, kRealm5, HttpAuth::AUTH_SCHEME_BASIC);
    139   EXPECT_TRUE(NULL == entry);
    140 
    141   // While Realm3 does exist, the origin scheme is wrong.
    142   entry = cache.Lookup(GURL("https://www.google.com"), kRealm3,
    143                        HttpAuth::AUTH_SCHEME_BASIC);
    144   EXPECT_TRUE(NULL == entry);
    145 
    146   // Realm, origin scheme ok, authentication scheme wrong
    147   entry = cache.Lookup
    148       (GURL("http://www.google.com"), kRealm1, HttpAuth::AUTH_SCHEME_DIGEST);
    149   EXPECT_TRUE(NULL == entry);
    150 
    151   // Valid lookup by origin, realm, scheme.
    152   entry = cache.Lookup(
    153       GURL("http://www.google.com:80"), kRealm3, HttpAuth::AUTH_SCHEME_BASIC);
    154   ASSERT_FALSE(NULL == entry);
    155   EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme());
    156   EXPECT_EQ(kRealm3, entry->realm());
    157   EXPECT_EQ("Basic realm=Realm3", entry->auth_challenge());
    158   EXPECT_EQ(ASCIIToUTF16("realm3-basic-user"), entry->credentials().username());
    159   EXPECT_EQ(ASCIIToUTF16("realm3-basic-password"),
    160             entry->credentials().password());
    161 
    162   // Valid lookup by origin, realm, scheme when there's a duplicate
    163   // origin, realm in the cache
    164   entry = cache.Lookup(
    165       GURL("http://www.google.com:80"), kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
    166   ASSERT_FALSE(NULL == entry);
    167   EXPECT_EQ(HttpAuth::AUTH_SCHEME_DIGEST, entry->scheme());
    168   EXPECT_EQ(kRealm3, entry->realm());
    169   EXPECT_EQ("Digest realm=Realm3", entry->auth_challenge());
    170   EXPECT_EQ(ASCIIToUTF16("realm3-digest-user"),
    171             entry->credentials().username());
    172   EXPECT_EQ(ASCIIToUTF16("realm3-digest-password"),
    173             entry->credentials().password());
    174 
    175   // Valid lookup by realm.
    176   entry = cache.Lookup(origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC);
    177   ASSERT_FALSE(NULL == entry);
    178   EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme());
    179   EXPECT_EQ(kRealm2, entry->realm());
    180   EXPECT_EQ("Basic realm=Realm2", entry->auth_challenge());
    181   EXPECT_EQ(ASCIIToUTF16("realm2-user"), entry->credentials().username());
    182   EXPECT_EQ(ASCIIToUTF16("realm2-password"), entry->credentials().password());
    183 
    184   // Check that subpaths are recognized.
    185   HttpAuthCache::Entry* realm2_entry = cache.Lookup(
    186       origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC);
    187   HttpAuthCache::Entry* realm4_entry = cache.Lookup(
    188       origin, kRealm4, HttpAuth::AUTH_SCHEME_BASIC);
    189   EXPECT_FALSE(NULL == realm2_entry);
    190   EXPECT_FALSE(NULL == realm4_entry);
    191   // Realm4 applies to '/' and Realm2 applies to '/foo2/'.
    192   // LookupByPath() should return the closest enclosing path.
    193   // Positive tests:
    194   entry = cache.LookupByPath(origin, "/foo2/index.html");
    195   EXPECT_TRUE(realm2_entry == entry);
    196   entry = cache.LookupByPath(origin, "/foo2/foobar.html");
    197   EXPECT_TRUE(realm2_entry == entry);
    198   entry = cache.LookupByPath(origin, "/foo2/bar/index.html");
    199   EXPECT_TRUE(realm2_entry == entry);
    200   entry = cache.LookupByPath(origin, "/foo2/");
    201   EXPECT_TRUE(realm2_entry == entry);
    202   entry = cache.LookupByPath(origin, "/foo2");
    203   EXPECT_TRUE(realm4_entry == entry);
    204   entry = cache.LookupByPath(origin, "/");
    205   EXPECT_TRUE(realm4_entry == entry);
    206 
    207   // Negative tests:
    208   entry = cache.LookupByPath(origin, "/foo3/index.html");
    209   EXPECT_FALSE(realm2_entry == entry);
    210   entry = cache.LookupByPath(origin, std::string());
    211   EXPECT_FALSE(realm2_entry == entry);
    212 
    213   // Confirm we find the same realm, different auth scheme by path lookup
    214   HttpAuthCache::Entry* realm3_digest_entry =
    215       cache.Lookup(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
    216   EXPECT_FALSE(NULL == realm3_digest_entry);
    217   entry = cache.LookupByPath(origin, "/baz/index.html");
    218   EXPECT_TRUE(realm3_digest_entry == entry);
    219   entry = cache.LookupByPath(origin, "/baz/");
    220   EXPECT_TRUE(realm3_digest_entry == entry);
    221   entry = cache.LookupByPath(origin, "/baz");
    222   EXPECT_FALSE(realm3_digest_entry == entry);
    223 
    224   // Confirm we find the same realm, different auth scheme by path lookup
    225   HttpAuthCache::Entry* realm3DigestEntry =
    226       cache.Lookup(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
    227   EXPECT_FALSE(NULL == realm3DigestEntry);
    228   entry = cache.LookupByPath(origin, "/baz/index.html");
    229   EXPECT_TRUE(realm3DigestEntry == entry);
    230   entry = cache.LookupByPath(origin, "/baz/");
    231   EXPECT_TRUE(realm3DigestEntry == entry);
    232   entry = cache.LookupByPath(origin, "/baz");
    233   EXPECT_FALSE(realm3DigestEntry == entry);
    234 
    235   // Lookup using empty path (may be used for proxy).
    236   entry = cache.LookupByPath(origin, std::string());
    237   EXPECT_FALSE(NULL == entry);
    238   EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme());
    239   EXPECT_EQ(kRealm3, entry->realm());
    240 }
    241 
    242 TEST(HttpAuthCacheTest, AddPath) {
    243   HttpAuthCache::Entry entry;
    244 
    245   // All of these paths have a common root /1/2/2/4/5/
    246   entry.AddPath("/1/2/3/4/5/x.txt");
    247   entry.AddPath("/1/2/3/4/5/y.txt");
    248   entry.AddPath("/1/2/3/4/5/z.txt");
    249 
    250   EXPECT_EQ(1U, entry.paths_.size());
    251   EXPECT_EQ("/1/2/3/4/5/", entry.paths_.front());
    252 
    253   // Add a new entry (not a subpath).
    254   entry.AddPath("/1/XXX/q");
    255   EXPECT_EQ(2U, entry.paths_.size());
    256   EXPECT_EQ("/1/XXX/", entry.paths_.front());
    257   EXPECT_EQ("/1/2/3/4/5/", entry.paths_.back());
    258 
    259   // Add containing paths of /1/2/3/4/5/ -- should swallow up the deeper paths.
    260   entry.AddPath("/1/2/3/4/x.txt");
    261   EXPECT_EQ(2U, entry.paths_.size());
    262   EXPECT_EQ("/1/2/3/4/", entry.paths_.front());
    263   EXPECT_EQ("/1/XXX/", entry.paths_.back());
    264   entry.AddPath("/1/2/3/x");
    265   EXPECT_EQ(2U, entry.paths_.size());
    266   EXPECT_EQ("/1/2/3/", entry.paths_.front());
    267   EXPECT_EQ("/1/XXX/", entry.paths_.back());
    268 
    269   entry.AddPath("/index.html");
    270   EXPECT_EQ(1U, entry.paths_.size());
    271   EXPECT_EQ("/", entry.paths_.front());
    272 }
    273 
    274 // Calling Add when the realm entry already exists, should append that
    275 // path.
    276 TEST(HttpAuthCacheTest, AddToExistingEntry) {
    277   HttpAuthCache cache;
    278   GURL origin("http://www.foobar.com:70");
    279   const std::string auth_challenge = "Basic realm=MyRealm";
    280 
    281   scoped_ptr<HttpAuthHandler> handler(
    282       new MockAuthHandler(
    283           HttpAuth::AUTH_SCHEME_BASIC, "MyRealm", HttpAuth::AUTH_SERVER));
    284   HttpAuthCache::Entry* orig_entry = cache.Add(
    285       origin, handler->realm(), handler->auth_scheme(), auth_challenge,
    286       CreateASCIICredentials("user1", "password1"), "/x/y/z/");
    287   cache.Add(origin, handler->realm(), handler->auth_scheme(), auth_challenge,
    288             CreateASCIICredentials("user2", "password2"), "/z/y/x/");
    289   cache.Add(origin, handler->realm(), handler->auth_scheme(), auth_challenge,
    290             CreateASCIICredentials("user3", "password3"), "/z/y");
    291 
    292   HttpAuthCache::Entry* entry = cache.Lookup(
    293       origin, "MyRealm", HttpAuth::AUTH_SCHEME_BASIC);
    294 
    295   EXPECT_TRUE(entry == orig_entry);
    296   EXPECT_EQ(ASCIIToUTF16("user3"), entry->credentials().username());
    297   EXPECT_EQ(ASCIIToUTF16("password3"), entry->credentials().password());
    298 
    299   EXPECT_EQ(2U, entry->paths_.size());
    300   EXPECT_EQ("/z/", entry->paths_.front());
    301   EXPECT_EQ("/x/y/z/", entry->paths_.back());
    302 }
    303 
    304 TEST(HttpAuthCacheTest, Remove) {
    305   GURL origin("http://foobar2.com");
    306 
    307   scoped_ptr<HttpAuthHandler> realm1_handler(
    308       new MockAuthHandler(
    309           HttpAuth::AUTH_SCHEME_BASIC, kRealm1, HttpAuth::AUTH_SERVER));
    310 
    311   scoped_ptr<HttpAuthHandler> realm2_handler(
    312       new MockAuthHandler(
    313           HttpAuth::AUTH_SCHEME_BASIC, kRealm2, HttpAuth::AUTH_SERVER));
    314 
    315   scoped_ptr<HttpAuthHandler> realm3_basic_handler(
    316       new MockAuthHandler(
    317           HttpAuth::AUTH_SCHEME_BASIC, kRealm3, HttpAuth::AUTH_SERVER));
    318 
    319   scoped_ptr<HttpAuthHandler> realm3_digest_handler(
    320       new MockAuthHandler(
    321           HttpAuth::AUTH_SCHEME_DIGEST, kRealm3, HttpAuth::AUTH_SERVER));
    322 
    323   HttpAuthCache cache;
    324   cache.Add(origin, realm1_handler->realm(), realm1_handler->auth_scheme(),
    325             "basic realm=Realm1", AuthCredentials(kAlice, k123), "/");
    326   cache.Add(origin, realm2_handler->realm(), realm2_handler->auth_scheme(),
    327             "basic realm=Realm2", CreateASCIICredentials("bob", "princess"),
    328             "/");
    329   cache.Add(origin, realm3_basic_handler->realm(),
    330             realm3_basic_handler->auth_scheme(), "basic realm=Realm3",
    331             AuthCredentials(kAdmin, kPassword), "/");
    332   cache.Add(origin, realm3_digest_handler->realm(),
    333             realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
    334             AuthCredentials(kRoot, kWileCoyote), "/");
    335 
    336   // Fails, because there is no realm "Realm5".
    337   EXPECT_FALSE(cache.Remove(
    338       origin, kRealm5, HttpAuth::AUTH_SCHEME_BASIC,
    339       AuthCredentials(kAlice, k123)));
    340 
    341   // Fails because the origin is wrong.
    342   EXPECT_FALSE(cache.Remove(GURL("http://foobar2.com:100"),
    343                             kRealm1,
    344                             HttpAuth::AUTH_SCHEME_BASIC,
    345                             AuthCredentials(kAlice, k123)));
    346 
    347   // Fails because the username is wrong.
    348   EXPECT_FALSE(cache.Remove(
    349       origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
    350       AuthCredentials(kAlice2, k123)));
    351 
    352   // Fails because the password is wrong.
    353   EXPECT_FALSE(cache.Remove(
    354       origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
    355       AuthCredentials(kAlice, k1234)));
    356 
    357   // Fails because the authentication type is wrong.
    358   EXPECT_FALSE(cache.Remove(
    359       origin, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST,
    360       AuthCredentials(kAlice, k123)));
    361 
    362   // Succeeds.
    363   EXPECT_TRUE(cache.Remove(
    364       origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
    365       AuthCredentials(kAlice, k123)));
    366 
    367   // Fails because we just deleted the entry!
    368   EXPECT_FALSE(cache.Remove(
    369       origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC,
    370       AuthCredentials(kAlice, k123)));
    371 
    372   // Succeed when there are two authentication types for the same origin,realm.
    373   EXPECT_TRUE(cache.Remove(
    374       origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST,
    375       AuthCredentials(kRoot, kWileCoyote)));
    376 
    377   // Succeed as above, but when entries were added in opposite order
    378   cache.Add(origin, realm3_digest_handler->realm(),
    379             realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
    380             AuthCredentials(kRoot, kWileCoyote), "/");
    381   EXPECT_TRUE(cache.Remove(
    382       origin, kRealm3, HttpAuth::AUTH_SCHEME_BASIC,
    383       AuthCredentials(kAdmin, kPassword)));
    384 
    385   // Make sure that removing one entry still leaves the other available for
    386   // lookup.
    387   HttpAuthCache::Entry* entry = cache.Lookup(
    388       origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
    389   EXPECT_FALSE(NULL == entry);
    390 }
    391 
    392 TEST(HttpAuthCacheTest, UpdateStaleChallenge) {
    393   HttpAuthCache cache;
    394   GURL origin("http://foobar2.com");
    395   scoped_ptr<HttpAuthHandler> digest_handler(
    396       new MockAuthHandler(
    397           HttpAuth::AUTH_SCHEME_DIGEST, kRealm1, HttpAuth::AUTH_PROXY));
    398   HttpAuthCache::Entry* entry_pre = cache.Add(
    399       origin,
    400       digest_handler->realm(),
    401       digest_handler->auth_scheme(),
    402       "Digest realm=Realm1,"
    403       "nonce=\"s3MzvFhaBAA=4c520af5acd9d8d7ae26947529d18c8eae1e98f4\"",
    404       CreateASCIICredentials("realm-digest-user", "realm-digest-password"),
    405       "/baz/index.html");
    406   ASSERT_TRUE(entry_pre != NULL);
    407 
    408   EXPECT_EQ(2, entry_pre->IncrementNonceCount());
    409   EXPECT_EQ(3, entry_pre->IncrementNonceCount());
    410   EXPECT_EQ(4, entry_pre->IncrementNonceCount());
    411 
    412   bool update_success = cache.UpdateStaleChallenge(
    413       origin,
    414       digest_handler->realm(),
    415       digest_handler->auth_scheme(),
    416       "Digest realm=Realm1,"
    417       "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\","
    418       "stale=\"true\"");
    419   EXPECT_TRUE(update_success);
    420 
    421   // After the stale update, the entry should still exist in the cache and
    422   // the nonce count should be reset to 0.
    423   HttpAuthCache::Entry* entry_post = cache.Lookup(
    424       origin,
    425       digest_handler->realm(),
    426       digest_handler->auth_scheme());
    427   ASSERT_TRUE(entry_post != NULL);
    428   EXPECT_EQ(2, entry_post->IncrementNonceCount());
    429 
    430   // UpdateStaleChallenge will fail if an entry doesn't exist in the cache.
    431   bool update_failure = cache.UpdateStaleChallenge(
    432       origin,
    433       kRealm2,
    434       digest_handler->auth_scheme(),
    435       "Digest realm=Realm2,"
    436       "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\","
    437       "stale=\"true\"");
    438   EXPECT_FALSE(update_failure);
    439 }
    440 
    441 TEST(HttpAuthCacheTest, UpdateAllFrom) {
    442   GURL origin("http://example.com");
    443   std::string path("/some/path");
    444   std::string another_path("/another/path");
    445 
    446   scoped_ptr<HttpAuthHandler> realm1_handler(
    447       new MockAuthHandler(
    448           HttpAuth::AUTH_SCHEME_BASIC, kRealm1, HttpAuth::AUTH_SERVER));
    449 
    450   scoped_ptr<HttpAuthHandler> realm2_handler(
    451       new MockAuthHandler(
    452           HttpAuth::AUTH_SCHEME_BASIC, kRealm2, HttpAuth::AUTH_PROXY));
    453 
    454   scoped_ptr<HttpAuthHandler> realm3_digest_handler(
    455       new MockAuthHandler(
    456           HttpAuth::AUTH_SCHEME_DIGEST, kRealm3, HttpAuth::AUTH_SERVER));
    457 
    458   scoped_ptr<HttpAuthHandler> realm4_handler(
    459       new MockAuthHandler(
    460           HttpAuth::AUTH_SCHEME_BASIC, kRealm4, HttpAuth::AUTH_SERVER));
    461 
    462   HttpAuthCache first_cache;
    463   HttpAuthCache::Entry* entry;
    464 
    465   first_cache.Add(origin, realm1_handler->realm(),
    466                   realm1_handler->auth_scheme(), "basic realm=Realm1",
    467                   AuthCredentials(kAlice, k123), path);
    468   first_cache.Add(origin, realm2_handler->realm(),
    469                   realm2_handler->auth_scheme(), "basic realm=Realm2",
    470                   AuthCredentials(kAlice2, k1234), path);
    471   first_cache.Add(origin, realm3_digest_handler->realm(),
    472                   realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
    473                   AuthCredentials(kRoot, kWileCoyote), path);
    474   entry = first_cache.Add(
    475       origin, realm3_digest_handler->realm(),
    476       realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
    477       AuthCredentials(kRoot, kWileCoyote), another_path);
    478 
    479   EXPECT_EQ(2, entry->IncrementNonceCount());
    480 
    481   HttpAuthCache second_cache;
    482   // Will be overwritten by kRoot:kWileCoyote.
    483   second_cache.Add(origin, realm3_digest_handler->realm(),
    484                    realm3_digest_handler->auth_scheme(), "digest realm=Realm3",
    485                    AuthCredentials(kAlice2, k1234), path);
    486   // Should be left intact.
    487   second_cache.Add(origin, realm4_handler->realm(),
    488                    realm4_handler->auth_scheme(), "basic realm=Realm4",
    489                    AuthCredentials(kAdmin, kRoot), path);
    490 
    491   second_cache.UpdateAllFrom(first_cache);
    492 
    493   // Copied from first_cache.
    494   entry = second_cache.Lookup(origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC);
    495   EXPECT_TRUE(NULL != entry);
    496   EXPECT_EQ(kAlice, entry->credentials().username());
    497   EXPECT_EQ(k123, entry->credentials().password());
    498 
    499   // Copied from first_cache.
    500   entry = second_cache.Lookup(origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC);
    501   EXPECT_TRUE(NULL != entry);
    502   EXPECT_EQ(kAlice2, entry->credentials().username());
    503   EXPECT_EQ(k1234, entry->credentials().password());
    504 
    505   // Overwritten from first_cache.
    506   entry = second_cache.Lookup(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST);
    507   EXPECT_TRUE(NULL != entry);
    508   EXPECT_EQ(kRoot, entry->credentials().username());
    509   EXPECT_EQ(kWileCoyote, entry->credentials().password());
    510   // Nonce count should get copied.
    511   EXPECT_EQ(3, entry->IncrementNonceCount());
    512 
    513   // All paths should get copied.
    514   entry = second_cache.LookupByPath(origin, another_path);
    515   EXPECT_TRUE(NULL != entry);
    516   EXPECT_EQ(kRoot, entry->credentials().username());
    517   EXPECT_EQ(kWileCoyote, entry->credentials().password());
    518 
    519   // Left intact in second_cache.
    520   entry = second_cache.Lookup(origin, kRealm4, HttpAuth::AUTH_SCHEME_BASIC);
    521   EXPECT_TRUE(NULL != entry);
    522   EXPECT_EQ(kAdmin, entry->credentials().username());
    523   EXPECT_EQ(kRoot, entry->credentials().password());
    524 }
    525 
    526 // Test fixture class for eviction tests (contains helpers for bulk
    527 // insertion and existence testing).
    528 class HttpAuthCacheEvictionTest : public testing::Test {
    529  protected:
    530   HttpAuthCacheEvictionTest() : origin_("http://www.google.com") { }
    531 
    532   std::string GenerateRealm(int realm_i) {
    533     return base::StringPrintf("Realm %d", realm_i);
    534   }
    535 
    536   std::string GeneratePath(int realm_i, int path_i) {
    537     return base::StringPrintf("/%d/%d/x/y", realm_i, path_i);
    538   }
    539 
    540   void AddRealm(int realm_i) {
    541     AddPathToRealm(realm_i, 0);
    542   }
    543 
    544   void AddPathToRealm(int realm_i, int path_i) {
    545     cache_.Add(origin_,
    546                GenerateRealm(realm_i),
    547                HttpAuth::AUTH_SCHEME_BASIC,
    548                std::string(),
    549                AuthCredentials(kUsername, kPassword),
    550                GeneratePath(realm_i, path_i));
    551   }
    552 
    553   void CheckRealmExistence(int realm_i, bool exists) {
    554     const HttpAuthCache::Entry* entry =
    555         cache_.Lookup(
    556             origin_, GenerateRealm(realm_i), HttpAuth::AUTH_SCHEME_BASIC);
    557     if (exists) {
    558       EXPECT_FALSE(entry == NULL);
    559       EXPECT_EQ(GenerateRealm(realm_i), entry->realm());
    560     } else {
    561       EXPECT_TRUE(entry == NULL);
    562     }
    563   }
    564 
    565   void CheckPathExistence(int realm_i, int path_i, bool exists) {
    566     const HttpAuthCache::Entry* entry =
    567         cache_.LookupByPath(origin_, GeneratePath(realm_i, path_i));
    568     if (exists) {
    569       EXPECT_FALSE(entry == NULL);
    570       EXPECT_EQ(GenerateRealm(realm_i), entry->realm());
    571     } else {
    572       EXPECT_TRUE(entry == NULL);
    573     }
    574   }
    575 
    576   GURL origin_;
    577   HttpAuthCache cache_;
    578 
    579   static const int kMaxPaths = HttpAuthCache::kMaxNumPathsPerRealmEntry;
    580   static const int kMaxRealms = HttpAuthCache::kMaxNumRealmEntries;
    581 };
    582 
    583 // Add the maxinim number of realm entries to the cache. Each of these entries
    584 // must still be retrievable. Next add three more entries -- since the cache is
    585 // full this causes FIFO eviction of the first three entries.
    586 TEST_F(HttpAuthCacheEvictionTest, RealmEntryEviction) {
    587   for (int i = 0; i < kMaxRealms; ++i)
    588     AddRealm(i);
    589 
    590   for (int i = 0; i < kMaxRealms; ++i)
    591     CheckRealmExistence(i, true);
    592 
    593   for (int i = 0; i < 3; ++i)
    594     AddRealm(i + kMaxRealms);
    595 
    596   for (int i = 0; i < 3; ++i)
    597     CheckRealmExistence(i, false);
    598 
    599   for (int i = 0; i < kMaxRealms; ++i)
    600     CheckRealmExistence(i + 3, true);
    601 }
    602 
    603 // Add the maximum number of paths to a single realm entry. Each of these
    604 // paths should be retrievable. Next add 3 more paths -- since the cache is
    605 // full this causes FIFO eviction of the first three paths.
    606 TEST_F(HttpAuthCacheEvictionTest, RealmPathEviction) {
    607   for (int i = 0; i < kMaxPaths; ++i)
    608     AddPathToRealm(0, i);
    609 
    610   for (int i = 1; i < kMaxRealms; ++i)
    611     AddRealm(i);
    612 
    613   for (int i = 0; i < 3; ++i)
    614     AddPathToRealm(0, i + kMaxPaths);
    615 
    616   for (int i = 0; i < 3; ++i)
    617     CheckPathExistence(0, i, false);
    618 
    619   for (int i = 0; i < kMaxPaths; ++i)
    620     CheckPathExistence(0, i + 3, true);
    621 
    622   for (int i = 0; i < kMaxRealms; ++i)
    623     CheckRealmExistence(i, true);
    624 }
    625 
    626 }  // namespace net
    627