| Index: net/http/http_auth.cc
|
| diff --git a/net/http/http_auth.cc b/net/http/http_auth.cc
|
| index 7752d743a22a356ea659577e858ad8aa3f2c017b..28fa76b006123a9de1d6464641956b99e34914e0 100644
|
| --- a/net/http/http_auth.cc
|
| +++ b/net/http/http_auth.cc
|
| @@ -7,49 +7,120 @@
|
| #include <algorithm>
|
|
|
| #include "base/basictypes.h"
|
| +#include "base/bind.h"
|
| #include "base/strings/string_tokenizer.h"
|
| #include "base/strings/string_util.h"
|
| +#include "base/values.h"
|
| #include "net/base/net_errors.h"
|
| #include "net/http/http_auth_challenge_tokenizer.h"
|
| #include "net/http/http_auth_handler.h"
|
| #include "net/http/http_auth_handler_factory.h"
|
| +#include "net/http/http_auth_scheme_set.h"
|
| #include "net/http/http_request_headers.h"
|
| #include "net/http/http_response_headers.h"
|
| #include "net/http/http_util.h"
|
|
|
| namespace net {
|
|
|
| +namespace {
|
| +
|
| +// Hardcoded map of HTTP authentication scheme priorities. Higher priorities are
|
| +// preferred over lower.
|
| +struct SchemePriority {
|
| + const char* scheme;
|
| + int priority;
|
| +} kSchemeScores[] = {
|
| + {"basic", 1},
|
| + {"digest", 2},
|
| + {"ntlm", 3},
|
| + {"negotiate", 4},
|
| +};
|
| +
|
| +// Priority assigned to unknown authentication schemes. They are currently
|
| +// ranked lower than Basic, which might be a bit too conservative.
|
| +const int kSchemePriorityDefault = 0;
|
| +
|
| +// Not a valid priority.
|
| +const int kSchemePriorityInvalid = -1;
|
| +
|
| +// Higher priority schemes are preferred over lower priority schemes.
|
| +int GetSchemePriority(const std::string& scheme) {
|
| + DCHECK(HttpAuth::IsValidNormalizedScheme(scheme));
|
| + for (const auto& iter : kSchemeScores) {
|
| + if (scheme == iter.scheme)
|
| + return iter.priority;
|
| + }
|
| + return kSchemePriorityDefault;
|
| +}
|
| +
|
| +scoped_ptr<base::Value> AuthHandlerCreationFailureParams(
|
| + const std::string* challenge,
|
| + int error,
|
| + NetLogCaptureMode capture_mode) {
|
| + scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
|
| + dict->SetString("challenge", *challenge);
|
| + dict->SetInteger("net_error", error);
|
| + return dict.Pass();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| HttpAuth::Identity::Identity() : source(IDENT_SRC_NONE), invalid(true) {}
|
|
|
| +HttpAuth::Identity::~Identity() {}
|
| +
|
| // static
|
| void HttpAuth::ChooseBestChallenge(
|
| HttpAuthHandlerFactory* http_auth_handler_factory,
|
| const HttpResponseHeaders* headers,
|
| Target target,
|
| const GURL& origin,
|
| - const std::set<Scheme>& disabled_schemes,
|
| + const HttpAuthSchemeSet& disabled_schemes,
|
| const BoundNetLog& net_log,
|
| scoped_ptr<HttpAuthHandler>* handler) {
|
| DCHECK(http_auth_handler_factory);
|
| - DCHECK(handler->get() == NULL);
|
| + DCHECK(!handler->get());
|
|
|
| - // Choose the challenge whose authentication handler gives the maximum score.
|
| + int best_priority = kSchemePriorityInvalid;
|
| scoped_ptr<HttpAuthHandler> best;
|
| const std::string header_name = GetChallengeHeaderName(target);
|
| std::string cur_challenge;
|
| - void* iter = NULL;
|
| + void* iter = nullptr;
|
| +
|
| while (headers->EnumerateHeader(&iter, header_name, &cur_challenge)) {
|
| - scoped_ptr<HttpAuthHandler> cur;
|
| - int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
|
| - cur_challenge, target, origin, net_log, &cur);
|
| + HttpAuthChallengeTokenizer challenge_tokenizer(cur_challenge.begin(),
|
| + cur_challenge.end());
|
| + std::string cur_auth_scheme = challenge_tokenizer.NormalizedScheme();
|
| + if (cur_auth_scheme.empty())
|
| + continue;
|
| +
|
| + if (disabled_schemes.Contains(cur_auth_scheme))
|
| + continue;
|
| +
|
| + // Always prefer the highest priority scheme available. If two handlers are
|
| + // of equal priority, then the handler based on the earlier challenge wins.
|
| + int cur_priority = GetSchemePriority(cur_auth_scheme);
|
| + if (best.get() && best_priority >= cur_priority)
|
| + continue;
|
| +
|
| + // Immediately trying to create a handler may be wasteful because we may see
|
| + // a higher ranking challenge later on in the headers. However, this is
|
| + // rare. Servers typically specify higher priority schemes before lower
|
| + // priority schemes.
|
| + scoped_ptr<HttpAuthHandler> current_handler;
|
| + int rv = http_auth_handler_factory->CreateAuthHandler(
|
| + &challenge_tokenizer, target, origin,
|
| + HttpAuthHandlerFactory::CREATE_CHALLENGE, 1, net_log, ¤t_handler);
|
| if (rv != OK) {
|
| - VLOG(1) << "Unable to create AuthHandler. Status: "
|
| - << ErrorToString(rv) << " Challenge: " << cur_challenge;
|
| + net_log.AddEvent(
|
| + NetLog::TYPE_AUTH_HANDLER_CREATION_FAILURE,
|
| + base::Bind(&AuthHandlerCreationFailureParams, &cur_challenge, rv));
|
| continue;
|
| }
|
| - if (cur.get() && (!best.get() || best->score() < cur->score()) &&
|
| - (disabled_schemes.find(cur->auth_scheme()) == disabled_schemes.end()))
|
| - best.swap(cur);
|
| + DCHECK(current_handler.get());
|
| + DCHECK_EQ(cur_auth_scheme, current_handler->auth_scheme());
|
| + best.swap(current_handler);
|
| + best_priority = cur_priority;
|
| }
|
| handler->swap(best);
|
| }
|
| @@ -59,16 +130,15 @@ HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse(
|
| HttpAuthHandler* handler,
|
| const HttpResponseHeaders* headers,
|
| Target target,
|
| - const std::set<Scheme>& disabled_schemes,
|
| + const HttpAuthSchemeSet& disabled_schemes,
|
| std::string* challenge_used) {
|
| DCHECK(handler);
|
| DCHECK(headers);
|
| DCHECK(challenge_used);
|
| challenge_used->clear();
|
| - HttpAuth::Scheme current_scheme = handler->auth_scheme();
|
| - if (disabled_schemes.find(current_scheme) != disabled_schemes.end())
|
| + const std::string& current_scheme = handler->auth_scheme();
|
| + if (disabled_schemes.Contains(current_scheme))
|
| return HttpAuth::AUTHORIZATION_RESULT_REJECT;
|
| - std::string current_scheme_name = SchemeToString(current_scheme);
|
| const std::string header_name = GetChallengeHeaderName(target);
|
| void* iter = NULL;
|
| std::string challenge;
|
| @@ -76,8 +146,7 @@ HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse(
|
| HttpAuth::AUTHORIZATION_RESULT_INVALID;
|
| while (headers->EnumerateHeader(&iter, header_name, &challenge)) {
|
| HttpAuthChallengeTokenizer props(challenge.begin(), challenge.end());
|
| - if (!base::LowerCaseEqualsASCII(props.scheme(),
|
| - current_scheme_name.c_str()))
|
| + if (!props.SchemeIs(current_scheme))
|
| continue;
|
| authorization_result = handler->HandleAnotherChallenge(&props);
|
| if (authorization_result != HttpAuth::AUTHORIZATION_RESULT_INVALID) {
|
| @@ -129,22 +198,9 @@ std::string HttpAuth::GetAuthTargetString(Target target) {
|
| }
|
|
|
| // static
|
| -const char* HttpAuth::SchemeToString(Scheme scheme) {
|
| - static const char* const kSchemeNames[] = {
|
| - "basic",
|
| - "digest",
|
| - "ntlm",
|
| - "negotiate",
|
| - "spdyproxy",
|
| - "mock",
|
| - };
|
| - static_assert(arraysize(kSchemeNames) == AUTH_SCHEME_MAX,
|
| - "http auth scheme names incorrect size");
|
| - if (scheme < AUTH_SCHEME_BASIC || scheme >= AUTH_SCHEME_MAX) {
|
| - NOTREACHED();
|
| - return "invalid_scheme";
|
| - }
|
| - return kSchemeNames[scheme];
|
| +bool HttpAuth::IsValidNormalizedScheme(const std::string& scheme) {
|
| + return HttpUtil::IsToken(scheme.begin(), scheme.end()) &&
|
| + base::ToLowerASCII(scheme) == scheme;
|
| }
|
|
|
| } // namespace net
|
|
|