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