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