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
16using base::ASCIIToUTF16;
17
18namespace net {
19
20namespace {
21
22class 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
58const char* kRealm1 = "Realm1";
59const char* kRealm2 = "Realm2";
60const char* kRealm3 = "Realm3";
61const char* kRealm4 = "Realm4";
62const char* kRealm5 = "Realm5";
63const base::string16 k123(ASCIIToUTF16("123"));
64const base::string16 k1234(ASCIIToUTF16("1234"));
65const base::string16 kAdmin(ASCIIToUTF16("admin"));
66const base::string16 kAlice(ASCIIToUTF16("alice"));
67const base::string16 kAlice2(ASCIIToUTF16("alice2"));
68const base::string16 kPassword(ASCIIToUTF16("password"));
69const base::string16 kRoot(ASCIIToUTF16("root"));
70const base::string16 kUsername(ASCIIToUTF16("username"));
71const base::string16 kWileCoyote(ASCIIToUTF16("wilecoyote"));
72
73AuthCredentials 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).
81TEST(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
244TEST(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.
278TEST(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
306TEST(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
394TEST(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
443TEST(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).
530class 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.
588TEST_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.
608TEST_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