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
16namespace net {
17
18namespace {
19
20class 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
56const char* kRealm1 = "Realm1";
57const char* kRealm2 = "Realm2";
58const char* kRealm3 = "Realm3";
59const char* kRealm4 = "Realm4";
60const char* kRealm5 = "Realm5";
61const base::string16 k123(ASCIIToUTF16("123"));
62const base::string16 k1234(ASCIIToUTF16("1234"));
63const base::string16 kAdmin(ASCIIToUTF16("admin"));
64const base::string16 kAlice(ASCIIToUTF16("alice"));
65const base::string16 kAlice2(ASCIIToUTF16("alice2"));
66const base::string16 kPassword(ASCIIToUTF16("password"));
67const base::string16 kRoot(ASCIIToUTF16("root"));
68const base::string16 kUsername(ASCIIToUTF16("username"));
69const base::string16 kWileCoyote(ASCIIToUTF16("wilecoyote"));
70
71AuthCredentials 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).
79TEST(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
242TEST(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.
276TEST(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
304TEST(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
392TEST(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
441TEST(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).
528class 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.
586TEST_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.
606TEST_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