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 |