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/basictypes.h"
8#include "base/strings/string_util.h"
9#include "base/strings/utf_string_conversions.h"
10#include "net/base/net_errors.h"
11#include "net/base/test_completion_callback.h"
12#include "net/http/http_auth_challenge_tokenizer.h"
13#include "net/http/http_auth_handler_digest.h"
14#include "net/http/http_request_info.h"
15#include "testing/gtest/include/gtest/gtest.h"
16
17namespace net {
18
19namespace {
20
21const char* const kSimpleChallenge =
22  "Digest realm=\"Oblivion\", nonce=\"nonce-value\"";
23
24// RespondToChallenge creates an HttpAuthHandlerDigest for the specified
25// |challenge|, and generates a response to the challenge which is returned in
26// |token|.
27//
28// The return value indicates whether the |token| was successfully created.
29//
30// If |target| is HttpAuth::AUTH_PROXY, then |proxy_name| specifies the source
31// of the |challenge|. Otherwise, the scheme and host and port of |request_url|
32// indicates the origin of the challenge.
33bool RespondToChallenge(HttpAuth::Target target,
34                        const std::string& proxy_name,
35                        const std::string& request_url,
36                        const std::string& challenge,
37                        std::string* token) {
38  // Input validation.
39  if (token == NULL) {
40    ADD_FAILURE() << "|token| must be non-NULL";
41    return false;
42  }
43  EXPECT_TRUE(target != HttpAuth::AUTH_PROXY || !proxy_name.empty());
44  EXPECT_FALSE(request_url.empty());
45  EXPECT_FALSE(challenge.empty());
46
47  token->clear();
48  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
49      new HttpAuthHandlerDigest::Factory());
50  HttpAuthHandlerDigest::NonceGenerator* nonce_generator =
51      new HttpAuthHandlerDigest::FixedNonceGenerator("client_nonce");
52  factory->set_nonce_generator(nonce_generator);
53  scoped_ptr<HttpAuthHandler> handler;
54
55  // Create a handler for a particular challenge.
56  GURL url_origin(target == HttpAuth::AUTH_SERVER ? request_url : proxy_name);
57  int rv_create = factory->CreateAuthHandlerFromString(
58    challenge, target, url_origin.GetOrigin(), BoundNetLog(), &handler);
59  if (rv_create != OK || handler.get() == NULL) {
60    ADD_FAILURE() << "Unable to create auth handler.";
61    return false;
62  }
63
64  // Create a token in response to the challenge.
65  // NOTE: HttpAuthHandlerDigest's implementation of GenerateAuthToken always
66  // completes synchronously. That's why this test can get away with a
67  // TestCompletionCallback without an IO thread.
68  TestCompletionCallback callback;
69  scoped_ptr<HttpRequestInfo> request(new HttpRequestInfo());
70  request->url = GURL(request_url);
71  AuthCredentials credentials(base::ASCIIToUTF16("foo"),
72                              base::ASCIIToUTF16("bar"));
73  int rv_generate = handler->GenerateAuthToken(
74      &credentials, request.get(), callback.callback(), token);
75  if (rv_generate != OK) {
76    ADD_FAILURE() << "Problems generating auth token";
77    return false;
78  }
79
80  return true;
81}
82
83}  // namespace
84
85
86TEST(HttpAuthHandlerDigestTest, ParseChallenge) {
87  static const struct {
88    // The challenge string.
89    const char* challenge;
90    // Expected return value of ParseChallenge.
91    bool parsed_success;
92    // The expected values that were parsed.
93    const char* parsed_realm;
94    const char* parsed_nonce;
95    const char* parsed_domain;
96    const char* parsed_opaque;
97    bool parsed_stale;
98    int parsed_algorithm;
99    int parsed_qop;
100  } tests[] = {
101    { // Check that a minimal challenge works correctly.
102      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\"",
103      true,
104      "Thunder Bluff",
105      "xyz",
106      "",
107      "",
108      false,
109      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
110      HttpAuthHandlerDigest::QOP_UNSPECIFIED
111    },
112
113    { // Realm does not need to be quoted, even though RFC2617 requires it.
114      "Digest nonce=\"xyz\", realm=ThunderBluff",
115      true,
116      "ThunderBluff",
117      "xyz",
118      "",
119      "",
120      false,
121      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
122      HttpAuthHandlerDigest::QOP_UNSPECIFIED
123    },
124
125    { // We allow the realm to be omitted, and will default it to empty string.
126      // See http://crbug.com/20984.
127      "Digest nonce=\"xyz\"",
128      true,
129      "",
130      "xyz",
131      "",
132      "",
133      false,
134      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
135      HttpAuthHandlerDigest::QOP_UNSPECIFIED
136    },
137
138    { // Try with realm set to empty string.
139      "Digest realm=\"\", nonce=\"xyz\"",
140      true,
141      "",
142      "xyz",
143      "",
144      "",
145      false,
146      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
147      HttpAuthHandlerDigest::QOP_UNSPECIFIED
148    },
149
150    // Handle ISO-8859-1 character as part of the realm. The realm is converted
151    // to UTF-8. However, the credentials will still use the original encoding.
152    {
153      "Digest nonce=\"xyz\", realm=\"foo-\xE5\"",
154      true,
155      "foo-\xC3\xA5",
156      "xyz",
157      "",
158      "",
159      false,
160      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
161      HttpAuthHandlerDigest::QOP_UNSPECIFIED,
162    },
163
164    { // At a minimum, a nonce must be provided.
165      "Digest realm=\"Thunder Bluff\"",
166      false,
167      "",
168      "",
169      "",
170      "",
171      false,
172      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
173      HttpAuthHandlerDigest::QOP_UNSPECIFIED
174    },
175
176    { // The nonce does not need to be quoted, even though RFC2617
177      // requires it.
178      "Digest nonce=xyz, realm=\"Thunder Bluff\"",
179      true,
180      "Thunder Bluff",
181      "xyz",
182      "",
183      "",
184      false,
185      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
186      HttpAuthHandlerDigest::QOP_UNSPECIFIED
187    },
188
189    { // Unknown authentication parameters are ignored.
190      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", foo=\"bar\"",
191      true,
192      "Thunder Bluff",
193      "xyz",
194      "",
195      "",
196      false,
197      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
198      HttpAuthHandlerDigest::QOP_UNSPECIFIED
199    },
200
201    { // Check that when algorithm has an unsupported value, parsing fails.
202      "Digest nonce=\"xyz\", algorithm=\"awezum\", realm=\"Thunder\"",
203      false,
204      // The remaining values don't matter (but some have been set already).
205      "",
206      "xyz",
207      "",
208      "",
209      false,
210      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
211      HttpAuthHandlerDigest::QOP_UNSPECIFIED
212    },
213
214    { // Check that algorithm's value is case insensitive, and that MD5 is
215      // a supported algorithm.
216      "Digest nonce=\"xyz\", algorithm=\"mD5\", realm=\"Oblivion\"",
217      true,
218      "Oblivion",
219      "xyz",
220      "",
221      "",
222      false,
223      HttpAuthHandlerDigest::ALGORITHM_MD5,
224      HttpAuthHandlerDigest::QOP_UNSPECIFIED
225    },
226
227    { // Check that md5-sess is a supported algorithm.
228      "Digest nonce=\"xyz\", algorithm=\"md5-sess\", realm=\"Oblivion\"",
229      true,
230      "Oblivion",
231      "xyz",
232      "",
233      "",
234      false,
235      HttpAuthHandlerDigest::ALGORITHM_MD5_SESS,
236      HttpAuthHandlerDigest::QOP_UNSPECIFIED,
237    },
238
239    { // Check that qop's value is case insensitive, and that auth is known.
240      "Digest nonce=\"xyz\", realm=\"Oblivion\", qop=\"aUth\"",
241      true,
242      "Oblivion",
243      "xyz",
244      "",
245      "",
246      false,
247      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
248      HttpAuthHandlerDigest::QOP_AUTH
249    },
250
251    { // auth-int is not handled, but will fall back to default qop.
252      "Digest nonce=\"xyz\", realm=\"Oblivion\", qop=\"auth-int\"",
253      true,
254      "Oblivion",
255      "xyz",
256      "",
257      "",
258      false,
259      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
260      HttpAuthHandlerDigest::QOP_UNSPECIFIED
261    },
262
263    { // Unknown qop values are ignored.
264      "Digest nonce=\"xyz\", realm=\"Oblivion\", qop=\"auth,foo\"",
265      true,
266      "Oblivion",
267      "xyz",
268      "",
269      "",
270      false,
271      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
272      HttpAuthHandlerDigest::QOP_AUTH
273    },
274
275    { // If auth-int is included with auth, then use auth.
276      "Digest nonce=\"xyz\", realm=\"Oblivion\", qop=\"auth,auth-int\"",
277      true,
278      "Oblivion",
279      "xyz",
280      "",
281      "",
282      false,
283      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
284      HttpAuthHandlerDigest::QOP_AUTH
285    },
286
287    { // Opaque parameter parsing should work correctly.
288      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", opaque=\"foobar\"",
289      true,
290      "Thunder Bluff",
291      "xyz",
292      "",
293      "foobar",
294      false,
295      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
296      HttpAuthHandlerDigest::QOP_UNSPECIFIED
297    },
298
299    { // Opaque parameters do not need to be quoted, even though RFC2617
300      // seems to require it.
301      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", opaque=foobar",
302      true,
303      "Thunder Bluff",
304      "xyz",
305      "",
306      "foobar",
307      false,
308      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
309      HttpAuthHandlerDigest::QOP_UNSPECIFIED
310    },
311
312    { // Domain can be parsed.
313      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", "
314      "domain=\"http://intranet.example.com/protection\"",
315      true,
316      "Thunder Bluff",
317      "xyz",
318      "http://intranet.example.com/protection",
319      "",
320      false,
321      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
322      HttpAuthHandlerDigest::QOP_UNSPECIFIED
323    },
324
325    { // Multiple domains can be parsed.
326      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", "
327      "domain=\"http://intranet.example.com/protection http://www.google.com\"",
328      true,
329      "Thunder Bluff",
330      "xyz",
331      "http://intranet.example.com/protection http://www.google.com",
332      "",
333      false,
334      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
335      HttpAuthHandlerDigest::QOP_UNSPECIFIED
336    },
337
338    { // If a non-Digest scheme is somehow passed in, it should be rejected.
339      "Basic realm=\"foo\"",
340      false,
341      "",
342      "",
343      "",
344      "",
345      false,
346      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
347      HttpAuthHandlerDigest::QOP_UNSPECIFIED
348    },
349  };
350
351  GURL origin("http://www.example.com");
352  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
353      new HttpAuthHandlerDigest::Factory());
354  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
355    scoped_ptr<HttpAuthHandler> handler;
356    int rv = factory->CreateAuthHandlerFromString(tests[i].challenge,
357                                                  HttpAuth::AUTH_SERVER,
358                                                  origin,
359                                                  BoundNetLog(),
360                                                  &handler);
361    if (tests[i].parsed_success) {
362      EXPECT_EQ(OK, rv);
363    } else {
364      EXPECT_NE(OK, rv);
365      EXPECT_TRUE(handler.get() == NULL);
366      continue;
367    }
368    ASSERT_TRUE(handler.get() != NULL);
369    HttpAuthHandlerDigest* digest =
370        static_cast<HttpAuthHandlerDigest*>(handler.get());
371    EXPECT_STREQ(tests[i].parsed_realm, digest->realm_.c_str());
372    EXPECT_STREQ(tests[i].parsed_nonce, digest->nonce_.c_str());
373    EXPECT_STREQ(tests[i].parsed_domain, digest->domain_.c_str());
374    EXPECT_STREQ(tests[i].parsed_opaque, digest->opaque_.c_str());
375    EXPECT_EQ(tests[i].parsed_stale, digest->stale_);
376    EXPECT_EQ(tests[i].parsed_algorithm, digest->algorithm_);
377    EXPECT_EQ(tests[i].parsed_qop, digest->qop_);
378    EXPECT_TRUE(handler->encrypts_identity());
379    EXPECT_FALSE(handler->is_connection_based());
380    EXPECT_TRUE(handler->NeedsIdentity());
381    EXPECT_FALSE(handler->AllowsDefaultCredentials());
382  }
383}
384
385TEST(HttpAuthHandlerDigestTest, AssembleCredentials) {
386  static const struct {
387    const char* req_method;
388    const char* req_path;
389    const char* challenge;
390    const char* username;
391    const char* password;
392    const char* cnonce;
393    int nonce_count;
394    const char* expected_creds;
395  } tests[] = {
396    { // MD5 with username/password
397      "GET",
398      "/test/drealm1",
399
400      // Challenge
401      "Digest realm=\"DRealm1\", "
402      "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\", "
403      "algorithm=MD5, qop=\"auth\"",
404
405      "foo", "bar", // username/password
406      "082c875dcb2ca740", // cnonce
407      1, // nc
408
409      // Authorization
410      "Digest username=\"foo\", realm=\"DRealm1\", "
411      "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\", "
412      "uri=\"/test/drealm1\", algorithm=MD5, "
413      "response=\"bcfaa62f1186a31ff1b474a19a17cf57\", "
414      "qop=auth, nc=00000001, cnonce=\"082c875dcb2ca740\""
415    },
416
417    { // MD5 with username but empty password. username has space in it.
418      "GET",
419      "/test/drealm1/",
420
421      // Challenge
422      "Digest realm=\"DRealm1\", "
423      "nonce=\"Ure30oRXBAA=7eca98bbf521ac6642820b11b86bd2d9ed7edc70\", "
424      "algorithm=MD5, qop=\"auth\"",
425
426      "foo bar", "", // Username/password
427      "082c875dcb2ca740", // cnonce
428      1, // nc
429
430      // Authorization
431      "Digest username=\"foo bar\", realm=\"DRealm1\", "
432      "nonce=\"Ure30oRXBAA=7eca98bbf521ac6642820b11b86bd2d9ed7edc70\", "
433      "uri=\"/test/drealm1/\", algorithm=MD5, "
434      "response=\"93c9c6d5930af3b0eb26c745e02b04a0\", "
435      "qop=auth, nc=00000001, cnonce=\"082c875dcb2ca740\""
436    },
437
438    { // MD5 with no username.
439      "GET",
440      "/test/drealm1/",
441
442      // Challenge
443      "Digest realm=\"DRealm1\", "
444      "nonce=\"7thGplhaBAA=41fb92453c49799cf353c8cd0aabee02d61a98a8\", "
445      "algorithm=MD5, qop=\"auth\"",
446
447      "", "pass", // Username/password
448      "6509bc74daed8263", // cnonce
449      1, // nc
450
451      // Authorization
452      "Digest username=\"\", realm=\"DRealm1\", "
453      "nonce=\"7thGplhaBAA=41fb92453c49799cf353c8cd0aabee02d61a98a8\", "
454      "uri=\"/test/drealm1/\", algorithm=MD5, "
455      "response=\"bc597110f41a62d07f8b70b6977fcb61\", "
456      "qop=auth, nc=00000001, cnonce=\"6509bc74daed8263\""
457    },
458
459    { // MD5 with no username and no password.
460      "GET",
461      "/test/drealm1/",
462
463      // Challenge
464      "Digest realm=\"DRealm1\", "
465      "nonce=\"s3MzvFhaBAA=4c520af5acd9d8d7ae26947529d18c8eae1e98f4\", "
466      "algorithm=MD5, qop=\"auth\"",
467
468      "", "", // Username/password
469      "1522e61005789929", // cnonce
470      1, // nc
471
472      // Authorization
473      "Digest username=\"\", realm=\"DRealm1\", "
474      "nonce=\"s3MzvFhaBAA=4c520af5acd9d8d7ae26947529d18c8eae1e98f4\", "
475      "uri=\"/test/drealm1/\", algorithm=MD5, "
476      "response=\"22cfa2b30cb500a9591c6d55ec5590a8\", "
477      "qop=auth, nc=00000001, cnonce=\"1522e61005789929\""
478    },
479
480    { // No algorithm, and no qop.
481      "GET",
482      "/",
483
484      // Challenge
485      "Digest realm=\"Oblivion\", nonce=\"nonce-value\"",
486
487      "FooBar", "pass", // Username/password
488      "", // cnonce
489      1, // nc
490
491      // Authorization
492      "Digest username=\"FooBar\", realm=\"Oblivion\", "
493      "nonce=\"nonce-value\", uri=\"/\", "
494      "response=\"f72ff54ebde2f928860f806ec04acd1b\""
495    },
496
497    { // MD5-sess
498      "GET",
499      "/",
500
501      // Challenge
502      "Digest realm=\"Baztastic\", nonce=\"AAAAAAAA\", "
503      "algorithm=\"md5-sess\", qop=auth",
504
505      "USER", "123", // Username/password
506      "15c07961ed8575c4", // cnonce
507      1, // nc
508
509      // Authorization
510      "Digest username=\"USER\", realm=\"Baztastic\", "
511      "nonce=\"AAAAAAAA\", uri=\"/\", algorithm=MD5-sess, "
512      "response=\"cbc1139821ee7192069580570c541a03\", "
513      "qop=auth, nc=00000001, cnonce=\"15c07961ed8575c4\""
514    }
515  };
516  GURL origin("http://www.example.com");
517  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
518      new HttpAuthHandlerDigest::Factory());
519  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
520    scoped_ptr<HttpAuthHandler> handler;
521    int rv = factory->CreateAuthHandlerFromString(tests[i].challenge,
522                                                  HttpAuth::AUTH_SERVER,
523                                                  origin,
524                                                  BoundNetLog(),
525                                                  &handler);
526    EXPECT_EQ(OK, rv);
527    ASSERT_TRUE(handler != NULL);
528
529    HttpAuthHandlerDigest* digest =
530        static_cast<HttpAuthHandlerDigest*>(handler.get());
531    std::string creds =
532        digest->AssembleCredentials(tests[i].req_method,
533                                    tests[i].req_path,
534                                    AuthCredentials(
535                                        base::ASCIIToUTF16(tests[i].username),
536                                        base::ASCIIToUTF16(tests[i].password)),
537                                    tests[i].cnonce,
538                                    tests[i].nonce_count);
539
540    EXPECT_STREQ(tests[i].expected_creds, creds.c_str());
541  }
542}
543
544TEST(HttpAuthHandlerDigest, HandleAnotherChallenge) {
545  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
546      new HttpAuthHandlerDigest::Factory());
547  scoped_ptr<HttpAuthHandler> handler;
548  std::string default_challenge =
549      "Digest realm=\"Oblivion\", nonce=\"nonce-value\"";
550  GURL origin("intranet.google.com");
551  int rv = factory->CreateAuthHandlerFromString(
552      default_challenge, HttpAuth::AUTH_SERVER, origin, BoundNetLog(),
553      &handler);
554  EXPECT_EQ(OK, rv);
555  ASSERT_TRUE(handler.get() != NULL);
556  HttpAuthChallengeTokenizer tok_default(default_challenge.begin(),
557                                         default_challenge.end());
558  EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
559            handler->HandleAnotherChallenge(&tok_default));
560
561  std::string stale_challenge = default_challenge + ", stale=true";
562  HttpAuthChallengeTokenizer tok_stale(stale_challenge.begin(),
563                                       stale_challenge.end());
564  EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_STALE,
565            handler->HandleAnotherChallenge(&tok_stale));
566
567  std::string stale_false_challenge = default_challenge + ", stale=false";
568  HttpAuthChallengeTokenizer tok_stale_false(stale_false_challenge.begin(),
569                                             stale_false_challenge.end());
570  EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
571            handler->HandleAnotherChallenge(&tok_stale_false));
572
573  std::string realm_change_challenge =
574      "Digest realm=\"SomethingElse\", nonce=\"nonce-value2\"";
575  HttpAuthChallengeTokenizer tok_realm_change(realm_change_challenge.begin(),
576                                              realm_change_challenge.end());
577  EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM,
578            handler->HandleAnotherChallenge(&tok_realm_change));
579}
580
581TEST(HttpAuthHandlerDigest, RespondToServerChallenge) {
582  std::string auth_token;
583  EXPECT_TRUE(RespondToChallenge(
584      HttpAuth::AUTH_SERVER,
585      std::string(),
586      "http://www.example.com/path/to/resource",
587      kSimpleChallenge,
588      &auth_token));
589  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
590            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
591            "response=\"6779f90bd0d658f937c1af967614fe84\"",
592            auth_token);
593}
594
595TEST(HttpAuthHandlerDigest, RespondToHttpsServerChallenge) {
596  std::string auth_token;
597  EXPECT_TRUE(RespondToChallenge(
598      HttpAuth::AUTH_SERVER,
599      std::string(),
600      "https://www.example.com/path/to/resource",
601      kSimpleChallenge,
602      &auth_token));
603  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
604            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
605            "response=\"6779f90bd0d658f937c1af967614fe84\"",
606            auth_token);
607}
608
609TEST(HttpAuthHandlerDigest, RespondToProxyChallenge) {
610  std::string auth_token;
611  EXPECT_TRUE(RespondToChallenge(
612      HttpAuth::AUTH_PROXY,
613      "http://proxy.intranet.corp.com:3128",
614      "http://www.example.com/path/to/resource",
615      kSimpleChallenge,
616      &auth_token));
617  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
618            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
619            "response=\"6779f90bd0d658f937c1af967614fe84\"",
620            auth_token);
621}
622
623TEST(HttpAuthHandlerDigest, RespondToProxyChallengeHttps) {
624  std::string auth_token;
625  EXPECT_TRUE(RespondToChallenge(
626      HttpAuth::AUTH_PROXY,
627      "http://proxy.intranet.corp.com:3128",
628      "https://www.example.com/path/to/resource",
629      kSimpleChallenge,
630      &auth_token));
631  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
632            "nonce=\"nonce-value\", uri=\"www.example.com:443\", "
633            "response=\"3270da8467afbe9ddf2334a48d46e9b9\"",
634            auth_token);
635}
636
637TEST(HttpAuthHandlerDigest, RespondToProxyChallengeWs) {
638  std::string auth_token;
639  EXPECT_TRUE(RespondToChallenge(
640      HttpAuth::AUTH_PROXY,
641      "http://proxy.intranet.corp.com:3128",
642      "ws://www.example.com/echo",
643      kSimpleChallenge,
644      &auth_token));
645  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
646            "nonce=\"nonce-value\", uri=\"www.example.com:80\", "
647            "response=\"aa1df184f68d5b6ab9d9aa4f88e41b4c\"",
648            auth_token);
649}
650
651TEST(HttpAuthHandlerDigest, RespondToProxyChallengeWss) {
652  std::string auth_token;
653  EXPECT_TRUE(RespondToChallenge(
654      HttpAuth::AUTH_PROXY,
655      "http://proxy.intranet.corp.com:3128",
656      "wss://www.example.com/echo",
657      kSimpleChallenge,
658      &auth_token));
659  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
660            "nonce=\"nonce-value\", uri=\"www.example.com:443\", "
661            "response=\"3270da8467afbe9ddf2334a48d46e9b9\"",
662            auth_token);
663}
664
665TEST(HttpAuthHandlerDigest, RespondToChallengeAuthQop) {
666  std::string auth_token;
667  EXPECT_TRUE(RespondToChallenge(
668      HttpAuth::AUTH_SERVER,
669      std::string(),
670      "http://www.example.com/path/to/resource",
671      "Digest realm=\"Oblivion\", nonce=\"nonce-value\", qop=\"auth\"",
672      &auth_token));
673  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
674            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
675            "response=\"5b1459beda5cee30d6ff9e970a69c0ea\", "
676            "qop=auth, nc=00000001, cnonce=\"client_nonce\"",
677            auth_token);
678}
679
680TEST(HttpAuthHandlerDigest, RespondToChallengeOpaque) {
681  std::string auth_token;
682  EXPECT_TRUE(RespondToChallenge(
683      HttpAuth::AUTH_SERVER,
684      std::string(),
685      "http://www.example.com/path/to/resource",
686      "Digest realm=\"Oblivion\", nonce=\"nonce-value\", "
687      "qop=\"auth\", opaque=\"opaque text\"",
688      &auth_token));
689  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
690            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
691            "response=\"5b1459beda5cee30d6ff9e970a69c0ea\", "
692            "opaque=\"opaque text\", "
693            "qop=auth, nc=00000001, cnonce=\"client_nonce\"",
694            auth_token);
695}
696
697
698} // namespace net
699