root/net/http/http_auth_handler_digest_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. RespondToChallenge
  2. TEST
  3. TEST
  4. TEST
  5. TEST
  6. TEST
  7. TEST
  8. TEST
  9. TEST
  10. TEST
  11. TEST
  12. TEST

// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>

#include "base/basictypes.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_handler_digest.h"
#include "net/http/http_request_info.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace net {

namespace {

const char* const kSimpleChallenge =
  "Digest realm=\"Oblivion\", nonce=\"nonce-value\"";

// RespondToChallenge creates an HttpAuthHandlerDigest for the specified
// |challenge|, and generates a response to the challenge which is returned in
// |token|.
//
// The return value indicates whether the |token| was successfully created.
//
// If |target| is HttpAuth::AUTH_PROXY, then |proxy_name| specifies the source
// of the |challenge|. Otherwise, the scheme and host and port of |request_url|
// indicates the origin of the challenge.
bool RespondToChallenge(HttpAuth::Target target,
                        const std::string& proxy_name,
                        const std::string& request_url,
                        const std::string& challenge,
                        std::string* token) {
  // Input validation.
  if (token == NULL) {
    ADD_FAILURE() << "|token| must be non-NULL";
    return false;
  }
  EXPECT_TRUE(target != HttpAuth::AUTH_PROXY || !proxy_name.empty());
  EXPECT_FALSE(request_url.empty());
  EXPECT_FALSE(challenge.empty());

  token->clear();
  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
      new HttpAuthHandlerDigest::Factory());
  HttpAuthHandlerDigest::NonceGenerator* nonce_generator =
      new HttpAuthHandlerDigest::FixedNonceGenerator("client_nonce");
  factory->set_nonce_generator(nonce_generator);
  scoped_ptr<HttpAuthHandler> handler;

  // Create a handler for a particular challenge.
  GURL url_origin(target == HttpAuth::AUTH_SERVER ? request_url : proxy_name);
  int rv_create = factory->CreateAuthHandlerFromString(
    challenge, target, url_origin.GetOrigin(), BoundNetLog(), &handler);
  if (rv_create != OK || handler.get() == NULL) {
    ADD_FAILURE() << "Unable to create auth handler.";
    return false;
  }

  // Create a token in response to the challenge.
  // NOTE: HttpAuthHandlerDigest's implementation of GenerateAuthToken always
  // completes synchronously. That's why this test can get away with a
  // TestCompletionCallback without an IO thread.
  TestCompletionCallback callback;
  scoped_ptr<HttpRequestInfo> request(new HttpRequestInfo());
  request->url = GURL(request_url);
  AuthCredentials credentials(base::ASCIIToUTF16("foo"),
                              base::ASCIIToUTF16("bar"));
  int rv_generate = handler->GenerateAuthToken(
      &credentials, request.get(), callback.callback(), token);
  if (rv_generate != OK) {
    ADD_FAILURE() << "Problems generating auth token";
    return false;
  }

  return true;
}

}  // namespace


TEST(HttpAuthHandlerDigestTest, ParseChallenge) {
  static const struct {
    // The challenge string.
    const char* challenge;
    // Expected return value of ParseChallenge.
    bool parsed_success;
    // The expected values that were parsed.
    const char* parsed_realm;
    const char* parsed_nonce;
    const char* parsed_domain;
    const char* parsed_opaque;
    bool parsed_stale;
    int parsed_algorithm;
    int parsed_qop;
  } tests[] = {
    { // Check that a minimal challenge works correctly.
      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\"",
      true,
      "Thunder Bluff",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Realm does not need to be quoted, even though RFC2617 requires it.
      "Digest nonce=\"xyz\", realm=ThunderBluff",
      true,
      "ThunderBluff",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // We allow the realm to be omitted, and will default it to empty string.
      // See http://crbug.com/20984.
      "Digest nonce=\"xyz\"",
      true,
      "",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Try with realm set to empty string.
      "Digest realm=\"\", nonce=\"xyz\"",
      true,
      "",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    // Handle ISO-8859-1 character as part of the realm. The realm is converted
    // to UTF-8. However, the credentials will still use the original encoding.
    {
      "Digest nonce=\"xyz\", realm=\"foo-\xE5\"",
      true,
      "foo-\xC3\xA5",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED,
    },

    { // At a minimum, a nonce must be provided.
      "Digest realm=\"Thunder Bluff\"",
      false,
      "",
      "",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // The nonce does not need to be quoted, even though RFC2617
      // requires it.
      "Digest nonce=xyz, realm=\"Thunder Bluff\"",
      true,
      "Thunder Bluff",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Unknown authentication parameters are ignored.
      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", foo=\"bar\"",
      true,
      "Thunder Bluff",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Check that when algorithm has an unsupported value, parsing fails.
      "Digest nonce=\"xyz\", algorithm=\"awezum\", realm=\"Thunder\"",
      false,
      // The remaining values don't matter (but some have been set already).
      "",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Check that algorithm's value is case insensitive, and that MD5 is
      // a supported algorithm.
      "Digest nonce=\"xyz\", algorithm=\"mD5\", realm=\"Oblivion\"",
      true,
      "Oblivion",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_MD5,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Check that md5-sess is a supported algorithm.
      "Digest nonce=\"xyz\", algorithm=\"md5-sess\", realm=\"Oblivion\"",
      true,
      "Oblivion",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_MD5_SESS,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED,
    },

    { // Check that qop's value is case insensitive, and that auth is known.
      "Digest nonce=\"xyz\", realm=\"Oblivion\", qop=\"aUth\"",
      true,
      "Oblivion",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_AUTH
    },

    { // auth-int is not handled, but will fall back to default qop.
      "Digest nonce=\"xyz\", realm=\"Oblivion\", qop=\"auth-int\"",
      true,
      "Oblivion",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Unknown qop values are ignored.
      "Digest nonce=\"xyz\", realm=\"Oblivion\", qop=\"auth,foo\"",
      true,
      "Oblivion",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_AUTH
    },

    { // If auth-int is included with auth, then use auth.
      "Digest nonce=\"xyz\", realm=\"Oblivion\", qop=\"auth,auth-int\"",
      true,
      "Oblivion",
      "xyz",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_AUTH
    },

    { // Opaque parameter parsing should work correctly.
      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", opaque=\"foobar\"",
      true,
      "Thunder Bluff",
      "xyz",
      "",
      "foobar",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Opaque parameters do not need to be quoted, even though RFC2617
      // seems to require it.
      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", opaque=foobar",
      true,
      "Thunder Bluff",
      "xyz",
      "",
      "foobar",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Domain can be parsed.
      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", "
      "domain=\"http://intranet.example.com/protection\"",
      true,
      "Thunder Bluff",
      "xyz",
      "http://intranet.example.com/protection",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // Multiple domains can be parsed.
      "Digest nonce=\"xyz\", realm=\"Thunder Bluff\", "
      "domain=\"http://intranet.example.com/protection http://www.google.com\"",
      true,
      "Thunder Bluff",
      "xyz",
      "http://intranet.example.com/protection http://www.google.com",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },

    { // If a non-Digest scheme is somehow passed in, it should be rejected.
      "Basic realm=\"foo\"",
      false,
      "",
      "",
      "",
      "",
      false,
      HttpAuthHandlerDigest::ALGORITHM_UNSPECIFIED,
      HttpAuthHandlerDigest::QOP_UNSPECIFIED
    },
  };

  GURL origin("http://www.example.com");
  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
      new HttpAuthHandlerDigest::Factory());
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
    scoped_ptr<HttpAuthHandler> handler;
    int rv = factory->CreateAuthHandlerFromString(tests[i].challenge,
                                                  HttpAuth::AUTH_SERVER,
                                                  origin,
                                                  BoundNetLog(),
                                                  &handler);
    if (tests[i].parsed_success) {
      EXPECT_EQ(OK, rv);
    } else {
      EXPECT_NE(OK, rv);
      EXPECT_TRUE(handler.get() == NULL);
      continue;
    }
    ASSERT_TRUE(handler.get() != NULL);
    HttpAuthHandlerDigest* digest =
        static_cast<HttpAuthHandlerDigest*>(handler.get());
    EXPECT_STREQ(tests[i].parsed_realm, digest->realm_.c_str());
    EXPECT_STREQ(tests[i].parsed_nonce, digest->nonce_.c_str());
    EXPECT_STREQ(tests[i].parsed_domain, digest->domain_.c_str());
    EXPECT_STREQ(tests[i].parsed_opaque, digest->opaque_.c_str());
    EXPECT_EQ(tests[i].parsed_stale, digest->stale_);
    EXPECT_EQ(tests[i].parsed_algorithm, digest->algorithm_);
    EXPECT_EQ(tests[i].parsed_qop, digest->qop_);
    EXPECT_TRUE(handler->encrypts_identity());
    EXPECT_FALSE(handler->is_connection_based());
    EXPECT_TRUE(handler->NeedsIdentity());
    EXPECT_FALSE(handler->AllowsDefaultCredentials());
  }
}

TEST(HttpAuthHandlerDigestTest, AssembleCredentials) {
  static const struct {
    const char* req_method;
    const char* req_path;
    const char* challenge;
    const char* username;
    const char* password;
    const char* cnonce;
    int nonce_count;
    const char* expected_creds;
  } tests[] = {
    { // MD5 with username/password
      "GET",
      "/test/drealm1",

      // Challenge
      "Digest realm=\"DRealm1\", "
      "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\", "
      "algorithm=MD5, qop=\"auth\"",

      "foo", "bar", // username/password
      "082c875dcb2ca740", // cnonce
      1, // nc

      // Authorization
      "Digest username=\"foo\", realm=\"DRealm1\", "
      "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\", "
      "uri=\"/test/drealm1\", algorithm=MD5, "
      "response=\"bcfaa62f1186a31ff1b474a19a17cf57\", "
      "qop=auth, nc=00000001, cnonce=\"082c875dcb2ca740\""
    },

    { // MD5 with username but empty password. username has space in it.
      "GET",
      "/test/drealm1/",

      // Challenge
      "Digest realm=\"DRealm1\", "
      "nonce=\"Ure30oRXBAA=7eca98bbf521ac6642820b11b86bd2d9ed7edc70\", "
      "algorithm=MD5, qop=\"auth\"",

      "foo bar", "", // Username/password
      "082c875dcb2ca740", // cnonce
      1, // nc

      // Authorization
      "Digest username=\"foo bar\", realm=\"DRealm1\", "
      "nonce=\"Ure30oRXBAA=7eca98bbf521ac6642820b11b86bd2d9ed7edc70\", "
      "uri=\"/test/drealm1/\", algorithm=MD5, "
      "response=\"93c9c6d5930af3b0eb26c745e02b04a0\", "
      "qop=auth, nc=00000001, cnonce=\"082c875dcb2ca740\""
    },

    { // MD5 with no username.
      "GET",
      "/test/drealm1/",

      // Challenge
      "Digest realm=\"DRealm1\", "
      "nonce=\"7thGplhaBAA=41fb92453c49799cf353c8cd0aabee02d61a98a8\", "
      "algorithm=MD5, qop=\"auth\"",

      "", "pass", // Username/password
      "6509bc74daed8263", // cnonce
      1, // nc

      // Authorization
      "Digest username=\"\", realm=\"DRealm1\", "
      "nonce=\"7thGplhaBAA=41fb92453c49799cf353c8cd0aabee02d61a98a8\", "
      "uri=\"/test/drealm1/\", algorithm=MD5, "
      "response=\"bc597110f41a62d07f8b70b6977fcb61\", "
      "qop=auth, nc=00000001, cnonce=\"6509bc74daed8263\""
    },

    { // MD5 with no username and no password.
      "GET",
      "/test/drealm1/",

      // Challenge
      "Digest realm=\"DRealm1\", "
      "nonce=\"s3MzvFhaBAA=4c520af5acd9d8d7ae26947529d18c8eae1e98f4\", "
      "algorithm=MD5, qop=\"auth\"",

      "", "", // Username/password
      "1522e61005789929", // cnonce
      1, // nc

      // Authorization
      "Digest username=\"\", realm=\"DRealm1\", "
      "nonce=\"s3MzvFhaBAA=4c520af5acd9d8d7ae26947529d18c8eae1e98f4\", "
      "uri=\"/test/drealm1/\", algorithm=MD5, "
      "response=\"22cfa2b30cb500a9591c6d55ec5590a8\", "
      "qop=auth, nc=00000001, cnonce=\"1522e61005789929\""
    },

    { // No algorithm, and no qop.
      "GET",
      "/",

      // Challenge
      "Digest realm=\"Oblivion\", nonce=\"nonce-value\"",

      "FooBar", "pass", // Username/password
      "", // cnonce
      1, // nc

      // Authorization
      "Digest username=\"FooBar\", realm=\"Oblivion\", "
      "nonce=\"nonce-value\", uri=\"/\", "
      "response=\"f72ff54ebde2f928860f806ec04acd1b\""
    },

    { // MD5-sess
      "GET",
      "/",

      // Challenge
      "Digest realm=\"Baztastic\", nonce=\"AAAAAAAA\", "
      "algorithm=\"md5-sess\", qop=auth",

      "USER", "123", // Username/password
      "15c07961ed8575c4", // cnonce
      1, // nc

      // Authorization
      "Digest username=\"USER\", realm=\"Baztastic\", "
      "nonce=\"AAAAAAAA\", uri=\"/\", algorithm=MD5-sess, "
      "response=\"cbc1139821ee7192069580570c541a03\", "
      "qop=auth, nc=00000001, cnonce=\"15c07961ed8575c4\""
    }
  };
  GURL origin("http://www.example.com");
  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
      new HttpAuthHandlerDigest::Factory());
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
    scoped_ptr<HttpAuthHandler> handler;
    int rv = factory->CreateAuthHandlerFromString(tests[i].challenge,
                                                  HttpAuth::AUTH_SERVER,
                                                  origin,
                                                  BoundNetLog(),
                                                  &handler);
    EXPECT_EQ(OK, rv);
    ASSERT_TRUE(handler != NULL);

    HttpAuthHandlerDigest* digest =
        static_cast<HttpAuthHandlerDigest*>(handler.get());
    std::string creds =
        digest->AssembleCredentials(tests[i].req_method,
                                    tests[i].req_path,
                                    AuthCredentials(
                                        base::ASCIIToUTF16(tests[i].username),
                                        base::ASCIIToUTF16(tests[i].password)),
                                    tests[i].cnonce,
                                    tests[i].nonce_count);

    EXPECT_STREQ(tests[i].expected_creds, creds.c_str());
  }
}

TEST(HttpAuthHandlerDigest, HandleAnotherChallenge) {
  scoped_ptr<HttpAuthHandlerDigest::Factory> factory(
      new HttpAuthHandlerDigest::Factory());
  scoped_ptr<HttpAuthHandler> handler;
  std::string default_challenge =
      "Digest realm=\"Oblivion\", nonce=\"nonce-value\"";
  GURL origin("intranet.google.com");
  int rv = factory->CreateAuthHandlerFromString(
      default_challenge, HttpAuth::AUTH_SERVER, origin, BoundNetLog(),
      &handler);
  EXPECT_EQ(OK, rv);
  ASSERT_TRUE(handler.get() != NULL);
  HttpAuthChallengeTokenizer tok_default(default_challenge.begin(),
                                         default_challenge.end());
  EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
            handler->HandleAnotherChallenge(&tok_default));

  std::string stale_challenge = default_challenge + ", stale=true";
  HttpAuthChallengeTokenizer tok_stale(stale_challenge.begin(),
                                       stale_challenge.end());
  EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_STALE,
            handler->HandleAnotherChallenge(&tok_stale));

  std::string stale_false_challenge = default_challenge + ", stale=false";
  HttpAuthChallengeTokenizer tok_stale_false(stale_false_challenge.begin(),
                                             stale_false_challenge.end());
  EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT,
            handler->HandleAnotherChallenge(&tok_stale_false));

  std::string realm_change_challenge =
      "Digest realm=\"SomethingElse\", nonce=\"nonce-value2\"";
  HttpAuthChallengeTokenizer tok_realm_change(realm_change_challenge.begin(),
                                              realm_change_challenge.end());
  EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM,
            handler->HandleAnotherChallenge(&tok_realm_change));
}

TEST(HttpAuthHandlerDigest, RespondToServerChallenge) {
  std::string auth_token;
  EXPECT_TRUE(RespondToChallenge(
      HttpAuth::AUTH_SERVER,
      std::string(),
      "http://www.example.com/path/to/resource",
      kSimpleChallenge,
      &auth_token));
  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
            "response=\"6779f90bd0d658f937c1af967614fe84\"",
            auth_token);
}

TEST(HttpAuthHandlerDigest, RespondToHttpsServerChallenge) {
  std::string auth_token;
  EXPECT_TRUE(RespondToChallenge(
      HttpAuth::AUTH_SERVER,
      std::string(),
      "https://www.example.com/path/to/resource",
      kSimpleChallenge,
      &auth_token));
  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
            "response=\"6779f90bd0d658f937c1af967614fe84\"",
            auth_token);
}

TEST(HttpAuthHandlerDigest, RespondToProxyChallenge) {
  std::string auth_token;
  EXPECT_TRUE(RespondToChallenge(
      HttpAuth::AUTH_PROXY,
      "http://proxy.intranet.corp.com:3128",
      "http://www.example.com/path/to/resource",
      kSimpleChallenge,
      &auth_token));
  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
            "response=\"6779f90bd0d658f937c1af967614fe84\"",
            auth_token);
}

TEST(HttpAuthHandlerDigest, RespondToProxyChallengeHttps) {
  std::string auth_token;
  EXPECT_TRUE(RespondToChallenge(
      HttpAuth::AUTH_PROXY,
      "http://proxy.intranet.corp.com:3128",
      "https://www.example.com/path/to/resource",
      kSimpleChallenge,
      &auth_token));
  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
            "nonce=\"nonce-value\", uri=\"www.example.com:443\", "
            "response=\"3270da8467afbe9ddf2334a48d46e9b9\"",
            auth_token);
}

TEST(HttpAuthHandlerDigest, RespondToProxyChallengeWs) {
  std::string auth_token;
  EXPECT_TRUE(RespondToChallenge(
      HttpAuth::AUTH_PROXY,
      "http://proxy.intranet.corp.com:3128",
      "ws://www.example.com/echo",
      kSimpleChallenge,
      &auth_token));
  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
            "nonce=\"nonce-value\", uri=\"www.example.com:80\", "
            "response=\"aa1df184f68d5b6ab9d9aa4f88e41b4c\"",
            auth_token);
}

TEST(HttpAuthHandlerDigest, RespondToProxyChallengeWss) {
  std::string auth_token;
  EXPECT_TRUE(RespondToChallenge(
      HttpAuth::AUTH_PROXY,
      "http://proxy.intranet.corp.com:3128",
      "wss://www.example.com/echo",
      kSimpleChallenge,
      &auth_token));
  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
            "nonce=\"nonce-value\", uri=\"www.example.com:443\", "
            "response=\"3270da8467afbe9ddf2334a48d46e9b9\"",
            auth_token);
}

TEST(HttpAuthHandlerDigest, RespondToChallengeAuthQop) {
  std::string auth_token;
  EXPECT_TRUE(RespondToChallenge(
      HttpAuth::AUTH_SERVER,
      std::string(),
      "http://www.example.com/path/to/resource",
      "Digest realm=\"Oblivion\", nonce=\"nonce-value\", qop=\"auth\"",
      &auth_token));
  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
            "response=\"5b1459beda5cee30d6ff9e970a69c0ea\", "
            "qop=auth, nc=00000001, cnonce=\"client_nonce\"",
            auth_token);
}

TEST(HttpAuthHandlerDigest, RespondToChallengeOpaque) {
  std::string auth_token;
  EXPECT_TRUE(RespondToChallenge(
      HttpAuth::AUTH_SERVER,
      std::string(),
      "http://www.example.com/path/to/resource",
      "Digest realm=\"Oblivion\", nonce=\"nonce-value\", "
      "qop=\"auth\", opaque=\"opaque text\"",
      &auth_token));
  EXPECT_EQ("Digest username=\"foo\", realm=\"Oblivion\", "
            "nonce=\"nonce-value\", uri=\"/path/to/resource\", "
            "response=\"5b1459beda5cee30d6ff9e970a69c0ea\", "
            "opaque=\"opaque text\", "
            "qop=auth, nc=00000001, cnonce=\"client_nonce\"",
            auth_token);
}


} // namespace net

/* [<][>][^][v][top][bottom][index][help] */