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