Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(866)

Side by Side Diff: net/http/http_auth_controller.cc

Issue 1391053002: [net/http auth] Make HttpAuthHandler challenge handling asynchronous. Base URL: https://chromium.googlesource.com/chromium/src.git@auth-handler-init-split
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/http/http_auth_controller.h ('k') | net/http/http_auth_controller_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/http/http_auth_controller.h" 5 #include "net/http/http_auth_controller.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/bind_helpers.h" 8 #include "base/bind_helpers.h"
9 #include "base/callback_helpers.h"
9 #include "base/metrics/histogram_macros.h" 10 #include "base/metrics/histogram_macros.h"
10 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h" 12 #include "base/strings/utf_string_conversions.h"
12 #include "base/threading/platform_thread.h" 13 #include "base/threading/platform_thread.h"
14 #include "base/values.h"
13 #include "net/base/auth.h" 15 #include "net/base/auth.h"
14 #include "net/base/net_util.h" 16 #include "net/base/net_util.h"
15 #include "net/dns/host_resolver.h" 17 #include "net/dns/host_resolver.h"
16 #include "net/http/http_auth_challenge_tokenizer.h" 18 #include "net/http/http_auth_challenge_tokenizer.h"
19 #include "net/http/http_auth_challenge_tokenizer.h"
17 #include "net/http/http_auth_handler.h" 20 #include "net/http/http_auth_handler.h"
18 #include "net/http/http_auth_handler_factory.h" 21 #include "net/http/http_auth_handler_factory.h"
19 #include "net/http/http_network_session.h" 22 #include "net/http/http_network_session.h"
20 #include "net/http/http_request_headers.h" 23 #include "net/http/http_request_headers.h"
21 #include "net/http/http_request_info.h" 24 #include "net/http/http_request_info.h"
22 #include "net/http/http_response_headers.h" 25 #include "net/http/http_response_headers.h"
23 26
24 namespace net { 27 namespace net {
25 28
26 namespace { 29 namespace {
27 30
28 // Returns a log message for all the response headers related to the auth
29 // challenge.
30 std::string AuthChallengeLogMessage(HttpResponseHeaders* headers) {
31 std::string msg;
32 std::string header_val;
33 void* iter = NULL;
34 while (headers->EnumerateHeader(&iter, "proxy-authenticate", &header_val)) {
35 msg.append("\n Has header Proxy-Authenticate: ");
36 msg.append(header_val);
37 }
38
39 iter = NULL;
40 while (headers->EnumerateHeader(&iter, "www-authenticate", &header_val)) {
41 msg.append("\n Has header WWW-Authenticate: ");
42 msg.append(header_val);
43 }
44
45 // RFC 4559 requires that a proxy indicate its support of NTLM/Negotiate
46 // authentication with a "Proxy-Support: Session-Based-Authentication"
47 // response header.
48 iter = NULL;
49 while (headers->EnumerateHeader(&iter, "proxy-support", &header_val)) {
50 msg.append("\n Has header Proxy-Support: ");
51 msg.append(header_val);
52 }
53
54 return msg;
55 }
56
57 enum AuthEvent { 31 enum AuthEvent {
58 AUTH_EVENT_START = 0, 32 AUTH_EVENT_START = 0,
59 AUTH_EVENT_REJECT, 33 AUTH_EVENT_REJECT,
60 AUTH_EVENT_MAX, 34 AUTH_EVENT_MAX,
61 }; 35 };
62 36
63 enum AuthTarget { 37 enum AuthTarget {
64 AUTH_TARGET_PROXY = 0, 38 AUTH_TARGET_PROXY = 0,
65 AUTH_TARGET_SECURE_PROXY, 39 AUTH_TARGET_SECURE_PROXY,
66 AUTH_TARGET_SERVER, 40 AUTH_TARGET_SERVER,
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 if (auth_event != AUTH_EVENT_START) 152 if (auth_event != AUTH_EVENT_START)
179 return; 153 return;
180 static const int kTargetBucketsEnd = AUTH_SCHEME_MAX * AUTH_TARGET_MAX; 154 static const int kTargetBucketsEnd = AUTH_SCHEME_MAX * AUTH_TARGET_MAX;
181 AuthTarget auth_target = DetermineAuthTarget(handler); 155 AuthTarget auth_target = DetermineAuthTarget(handler);
182 int target_bucket = auth_scheme * AUTH_TARGET_MAX + auth_target; 156 int target_bucket = auth_scheme * AUTH_TARGET_MAX + auth_target;
183 DCHECK(target_bucket >= 0 && target_bucket < kTargetBucketsEnd); 157 DCHECK(target_bucket >= 0 && target_bucket < kTargetBucketsEnd);
184 UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthTarget", target_bucket, 158 UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthTarget", target_bucket,
185 kTargetBucketsEnd); 159 kTargetBucketsEnd);
186 } 160 }
187 161
162 // Hardcoded map of HTTP authentication scheme priorities. Higher priorities are
163 // preferred over lower.
164 struct SchemePriority {
165 const char* scheme;
166 int priority;
167 } kSchemeScores[] = {
168 {"basic", 1},
169 {"digest", 2},
170 {"ntlm", 3},
171 {"negotiate", 4},
172 };
173
174 // Priority assigned to unknown authentication schemes. They are currently
175 // ranked lower than Basic, which might be a bit too conservative.
176 const int kSchemePriorityDefault = 0;
177
178 // Higher priority schemes are preferred over lower priority schemes.
179 int GetSchemePriority(const std::string& scheme) {
180 DCHECK(HttpAuth::IsValidNormalizedScheme(scheme));
181 for (const auto& iter : kSchemeScores) {
182 if (scheme == iter.scheme)
183 return iter.priority;
184 }
185 return kSchemePriorityDefault;
186 }
187
188 } // namespace 188 } // namespace
189 189
190 HttpAuthController::HttpAuthController( 190 HttpAuthController::HttpAuthController(
191 HttpAuth::Target target, 191 HttpAuth::Target target,
192 const GURL& auth_url, 192 const GURL& auth_url,
193 HttpAuthCache* http_auth_cache, 193 HttpAuthCache* http_auth_cache,
194 HttpAuthHandlerFactory* http_auth_handler_factory) 194 HttpAuthHandlerFactory* http_auth_handler_factory)
195 : target_(target), 195 : target_(target),
196 auth_url_(auth_url), 196 auth_url_(auth_url),
197 auth_origin_(auth_url.GetOrigin()), 197 auth_origin_(auth_url.GetOrigin()),
198 auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()), 198 auth_path_(HttpAuth::AUTH_PROXY ? std::string() : auth_url.path()),
199 embedded_identity_used_(false), 199 embedded_identity_used_(false),
200 default_credentials_used_(false), 200 default_credentials_used_(false),
201 http_auth_cache_(http_auth_cache), 201 http_auth_cache_(http_auth_cache),
202 http_auth_handler_factory_(http_auth_handler_factory) { 202 http_auth_handler_factory_(http_auth_handler_factory),
203 weak_ptr_factory_(this) {
204 io_callback_ = base::Bind(&HttpAuthController::OnIOComplete,
205 weak_ptr_factory_.GetWeakPtr());
203 } 206 }
204 207
205 HttpAuthController::~HttpAuthController() { 208 HttpAuthController::~HttpAuthController() {
206 DCHECK(CalledOnValidThread()); 209 DCHECK(CalledOnValidThread());
207 } 210 }
208 211
209 int HttpAuthController::MaybeGenerateAuthToken( 212 int HttpAuthController::MaybeGenerateAuthToken(
210 const HttpRequestInfo* request, const CompletionCallback& callback, 213 const HttpRequestInfo* request, const CompletionCallback& callback,
211 const BoundNetLog& net_log) { 214 const BoundNetLog& net_log) {
212 DCHECK(CalledOnValidThread()); 215 DCHECK(CalledOnValidThread());
213 DCHECK(request); 216 DCHECK(callback_.is_null());
217 DCHECK(!callback.is_null());
218
214 bool needs_auth = HaveAuth() || SelectPreemptiveAuth(net_log); 219 bool needs_auth = HaveAuth() || SelectPreemptiveAuth(net_log);
215 if (!needs_auth) 220 if (!needs_auth)
216 return OK; 221 return OK;
217 const AuthCredentials* credentials = NULL; 222 request_info_ = request;
218 if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS) 223 next_state_ = STATE_GENERATE_TOKEN;
219 credentials = &identity_.credentials; 224 net_log_ = net_log;
220 DCHECK(auth_token_.empty()); 225 int rv = DoLoop(OK);
221 DCHECK(callback_.is_null());
222 int rv = handler_->GenerateAuthToken(
223 credentials, *request,
224 base::Bind(&HttpAuthController::OnIOComplete, base::Unretained(this)),
225 &auth_token_);
226 if (DisableOnAuthHandlerResult(rv))
227 rv = OK;
228 if (rv == ERR_IO_PENDING) 226 if (rv == ERR_IO_PENDING)
229 callback_ = callback; 227 callback_ = callback;
230 else
231 OnIOComplete(rv);
232 return rv; 228 return rv;
233 } 229 }
234 230
235 bool HttpAuthController::SelectPreemptiveAuth(const BoundNetLog& net_log) { 231 bool HttpAuthController::SelectPreemptiveAuth(const BoundNetLog& net_log) {
236 DCHECK(CalledOnValidThread()); 232 DCHECK(CalledOnValidThread());
237 DCHECK(!HaveAuth()); 233 DCHECK(!HaveAuth());
238 DCHECK(identity_.invalid); 234 DCHECK(identity_.invalid);
239 235
240 // Don't do preemptive authorization if the URL contains a username:password, 236 // Don't do preemptive authorization if the URL contains a username:password,
241 // since we must first be challenged in order to use the URL's identity. 237 // since we must first be challenged in order to use the URL's identity.
242 if (auth_url_.has_username()) 238 if (auth_url_.has_username())
243 return false; 239 return false;
244 240
245 // SelectPreemptiveAuth() is on the critical path for each request, so it 241 // SelectPreemptiveAuth() is on the critical path for each request, so it
246 // is expected to be fast. LookupByPath() is fast in the common case, since 242 // is expected to be fast. LookupByPath() is fast in the common case, since
247 // the number of http auth cache entries is expected to be very small. 243 // the number of http auth cache entries is expected to be very small.
248 // (For most users in fact, it will be 0.) 244 // (For most users in fact, it will be 0.)
249 HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath( 245 HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath(
250 auth_origin_, auth_path_); 246 auth_origin_, auth_path_);
251 if (!entry) 247 if (!entry)
252 return false; 248 return false;
253 249
254 // Try to create a handler using the previous auth challenge. 250 // Try to create a handler using the previous auth challenge.
255 std::string challenge = entry->auth_challenge();
256 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end());
257 scoped_ptr<HttpAuthHandler> handler_preemptive = 251 scoped_ptr<HttpAuthHandler> handler_preemptive =
258 http_auth_handler_factory_->CreateAndInitPreemptiveAuthHandler( 252 http_auth_handler_factory_->CreateAndInitPreemptiveAuthHandler(
259 entry, tokenizer, target_, net_log); 253 entry, target_, net_log);
260 if (!handler_preemptive) 254 if (!handler_preemptive)
261 return false; 255 return false;
262 256
263 // Set the state 257 // Set the state
264 identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP; 258 identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
265 identity_.invalid = false; 259 identity_.invalid = false;
266 identity_.credentials = entry->credentials(); 260 identity_.credentials = entry->credentials();
267 handler_.swap(handler_preemptive); 261 handler_.swap(handler_preemptive);
268 return true; 262 return true;
269 } 263 }
270 264
271 void HttpAuthController::AddAuthorizationHeader( 265 void HttpAuthController::AddAuthorizationHeader(
272 HttpRequestHeaders* authorization_headers) { 266 HttpRequestHeaders* authorization_headers) {
273 DCHECK(CalledOnValidThread()); 267 DCHECK(CalledOnValidThread());
274 DCHECK(HaveAuth()); 268 DCHECK(HaveAuth());
275 // auth_token_ can be empty if we encountered a permanent error with 269 // auth_token_ can be empty if we encountered a permanent error with
276 // the auth scheme and want to retry. 270 // the auth scheme and want to retry.
277 if (!auth_token_.empty()) { 271 if (!auth_token_.empty()) {
278 authorization_headers->SetHeader( 272 authorization_headers->SetHeader(
279 HttpAuth::GetAuthorizationHeaderName(target_), auth_token_); 273 HttpAuth::GetAuthorizationHeaderName(target_), auth_token_);
280 auth_token_.clear(); 274 auth_token_.clear();
281 } 275 }
282 } 276 }
283 277
284 int HttpAuthController::HandleAuthChallenge( 278 int HttpAuthController::HandleAuthChallenge(
285 scoped_refptr<HttpResponseHeaders> headers, 279 const HttpResponseInfo& response_info,
286 bool do_not_send_server_auth, 280 const CompletionCallback& callback,
287 bool establishing_tunnel,
288 const BoundNetLog& net_log) { 281 const BoundNetLog& net_log) {
289 DCHECK(CalledOnValidThread()); 282 DCHECK(CalledOnValidThread());
290 DCHECK(headers.get()); 283 DCHECK(response_info.headers.get());
291 DCHECK(auth_origin_.is_valid()); 284 DCHECK(auth_origin_.is_valid());
292 VLOG(1) << "The " << HttpAuth::GetAuthTargetString(target_) << " " 285 DCHECK(callback_.is_null());
293 << auth_origin_ << " requested auth " 286 DCHECK(!callback.is_null());
294 << AuthChallengeLogMessage(headers.get());
295 287
296 // Give the existing auth handler first try at the authentication headers. 288 next_state_ = HaveAuthHandler() ? STATE_HANDLE_ANOTHER_CHALLENGE
297 // This will also evict the entry in the HttpAuthCache if the previous 289 : STATE_CHOOSE_CHALLENGE;
298 // challenge appeared to be rejected, or is using a stale nonce in the Digest 290 response_info_ = &response_info;
299 // case. 291 net_log_ = net_log;
300 if (HaveAuth()) { 292 int result = DoLoop(OK);
301 std::string challenge_used;
302 HttpAuth::AuthorizationResult result =
303 HttpAuth::HandleChallengeResponse(handler_.get(),
304 headers.get(),
305 target_,
306 disabled_schemes_,
307 &challenge_used);
308 switch (result) {
309 case HttpAuth::AUTHORIZATION_RESULT_ACCEPT:
310 break;
311 case HttpAuth::AUTHORIZATION_RESULT_INVALID:
312 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
313 break;
314 case HttpAuth::AUTHORIZATION_RESULT_REJECT:
315 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT);
316 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
317 break;
318 case HttpAuth::AUTHORIZATION_RESULT_STALE:
319 if (http_auth_cache_->UpdateStaleChallenge(auth_origin_,
320 handler_->realm(),
321 handler_->auth_scheme(),
322 challenge_used)) {
323 InvalidateCurrentHandler(INVALIDATE_HANDLER);
324 } else {
325 // It's possible that a server could incorrectly issue a stale
326 // response when the entry is not in the cache. Just evict the
327 // current value from the cache.
328 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
329 }
330 break;
331 case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM:
332 // If the server changes the authentication realm in a
333 // subsequent challenge, invalidate cached credentials for the
334 // previous realm. If the server rejects a preemptive
335 // authorization and requests credentials for a different
336 // realm, we keep the cached credentials.
337 InvalidateCurrentHandler(
338 (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP) ?
339 INVALIDATE_HANDLER :
340 INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
341 break;
342 default:
343 NOTREACHED();
344 break;
345 }
346 }
347 293
348 identity_.invalid = true; 294 if (result == ERR_IO_PENDING)
349 295 callback_ = callback;
350 bool can_send_auth = (target_ != HttpAuth::AUTH_SERVER || 296 return result;
351 !do_not_send_server_auth);
352
353 do {
354 if (!handler_.get() && can_send_auth) {
355 // Find the best authentication challenge that we support.
356 HttpAuth::ChooseBestChallenge(http_auth_handler_factory_,
357 headers.get(),
358 target_,
359 auth_origin_,
360 disabled_schemes_,
361 net_log,
362 &handler_);
363 if (handler_.get())
364 HistogramAuthEvent(handler_.get(), AUTH_EVENT_START);
365 }
366
367 if (!handler_.get()) {
368 if (establishing_tunnel) {
369 LOG(ERROR) << "Can't perform auth to the "
370 << HttpAuth::GetAuthTargetString(target_) << " "
371 << auth_origin_ << " when establishing a tunnel"
372 << AuthChallengeLogMessage(headers.get());
373
374 // We are establishing a tunnel, we can't show the error page because an
375 // active network attacker could control its contents. Instead, we just
376 // fail to establish the tunnel.
377 DCHECK(target_ == HttpAuth::AUTH_PROXY);
378 return ERR_PROXY_AUTH_UNSUPPORTED;
379 }
380 // We found no supported challenge -- let the transaction continue so we
381 // end up displaying the error page.
382 return OK;
383 }
384
385 if (handler_->NeedsIdentity()) {
386 // Pick a new auth identity to try, by looking to the URL and auth cache.
387 // If an identity to try is found, it is saved to identity_.
388 SelectNextAuthIdentityToTry();
389 } else {
390 // Proceed with the existing identity or a null identity.
391 identity_.invalid = false;
392 }
393
394 // From this point on, we are restartable.
395
396 if (identity_.invalid) {
397 // We have exhausted all identity possibilities.
398 if (!handler_->AllowsExplicitCredentials()) {
399 // If the handler doesn't accept explicit credentials, then we need to
400 // choose a different auth scheme.
401 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT);
402 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
403 } else {
404 // Pass the challenge information back to the client.
405 PopulateAuthChallenge();
406 }
407 } else {
408 auth_info_ = NULL;
409 }
410
411 // If we get here and we don't have a handler_, that's because we
412 // invalidated it due to not having any viable identities to use with it. Go
413 // back and try again.
414 // TODO(asanka): Instead we should create a priority list of
415 // <handler,identity> and iterate through that.
416 } while(!handler_.get());
417 return OK;
418 } 297 }
419 298
420 void HttpAuthController::ResetAuth(const AuthCredentials& credentials) { 299 void HttpAuthController::ResetAuth(const AuthCredentials& credentials) {
421 DCHECK(CalledOnValidThread()); 300 DCHECK(CalledOnValidThread());
422 DCHECK(identity_.invalid || credentials.Empty()); 301 DCHECK(identity_.invalid || credentials.Empty());
423 302
424 if (identity_.invalid) { 303 if (identity_.invalid) {
425 // Update the credentials. 304 // Update the credentials.
426 identity_.source = HttpAuth::IDENT_SRC_EXTERNAL; 305 identity_.source = HttpAuth::IDENT_SRC_EXTERNAL;
427 identity_.invalid = false; 306 identity_.invalid = false;
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after
532 identity_.invalid = false; 411 identity_.invalid = false;
533 default_credentials_used_ = true; 412 default_credentials_used_ = true;
534 return true; 413 return true;
535 } 414 }
536 415
537 return false; 416 return false;
538 } 417 }
539 418
540 void HttpAuthController::PopulateAuthChallenge() { 419 void HttpAuthController::PopulateAuthChallenge() {
541 DCHECK(CalledOnValidThread()); 420 DCHECK(CalledOnValidThread());
421 DCHECK(!auth_info_);
542 422
543 // Populates response_.auth_challenge with the authentication challenge info. 423 // Populates response_.auth_challenge with the authentication challenge info.
544 // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo(). 424 // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo().
545 425
546 auth_info_ = new AuthChallengeInfo; 426 auth_info_ = new AuthChallengeInfo;
547 auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY); 427 auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY);
548 auth_info_->challenger = HostPortPair::FromURL(auth_origin_); 428 auth_info_->challenger = HostPortPair::FromURL(auth_origin_);
549 auth_info_->scheme = handler_->auth_scheme(); 429 auth_info_->scheme = handler_->auth_scheme();
550 auth_info_->realm = handler_->realm(); 430 auth_info_->realm = handler_->realm();
551 } 431 }
(...skipping 23 matching lines...) Expand all
575 auth_token_.clear(); 455 auth_token_.clear();
576 return true; 456 return true;
577 457
578 default: 458 default:
579 return false; 459 return false;
580 } 460 }
581 } 461 }
582 462
583 void HttpAuthController::OnIOComplete(int result) { 463 void HttpAuthController::OnIOComplete(int result) {
584 DCHECK(CalledOnValidThread()); 464 DCHECK(CalledOnValidThread());
585 if (DisableOnAuthHandlerResult(result)) 465 result = DoLoop(result);
586 result = OK; 466
587 if (!callback_.is_null()) { 467 if (result != ERR_IO_PENDING && !callback_.is_null()) {
588 CompletionCallback c = callback_; 468 base::ResetAndReturn(&callback_).Run(result);
589 callback_.Reset();
590 c.Run(result);
591 } 469 }
592 } 470 }
593 471
594 scoped_refptr<AuthChallengeInfo> HttpAuthController::auth_info() { 472 scoped_refptr<AuthChallengeInfo> HttpAuthController::auth_info() {
595 DCHECK(CalledOnValidThread()); 473 DCHECK(CalledOnValidThread());
596 return auth_info_; 474 return auth_info_;
597 } 475 }
598 476
599 bool HttpAuthController::IsAuthSchemeDisabled(const std::string& scheme) const { 477 bool HttpAuthController::IsAuthSchemeDisabled(const std::string& scheme) const {
600 DCHECK(CalledOnValidThread()); 478 DCHECK(CalledOnValidThread());
601 return disabled_schemes_.Contains(scheme); 479 return disabled_schemes_.Contains(scheme);
602 } 480 }
603 481
604 void HttpAuthController::DisableAuthScheme(const std::string& scheme) { 482 void HttpAuthController::DisableAuthScheme(const std::string& scheme) {
605 DCHECK(CalledOnValidThread()); 483 DCHECK(CalledOnValidThread());
606 disabled_schemes_.Add(scheme); 484 disabled_schemes_.Add(scheme);
607 } 485 }
608 486
609 void HttpAuthController::DisableEmbeddedIdentity() { 487 void HttpAuthController::DisableEmbeddedIdentity() {
610 DCHECK(CalledOnValidThread()); 488 DCHECK(CalledOnValidThread());
611 embedded_identity_used_ = true; 489 embedded_identity_used_ = true;
612 } 490 }
613 491
492 int HttpAuthController::DoLoop(int result) {
493 DCHECK(CalledOnValidThread());
494
495 do {
496 State state = next_state_;
497 next_state_ = STATE_NONE;
498
499 switch (state) {
500 case STATE_CHOOSE_CHALLENGE:
501 DCHECK_EQ(OK, result);
502 result = DoChooseChallenge();
503 break;
504
505 case STATE_TRY_NEXT_CHALLENGE:
506 DCHECK_EQ(OK, result);
507 result = DoTryNextChallenge();
508 break;
509
510 case STATE_TRY_NEXT_CHALLENGE_COMPLETE:
511 result = DoTryNextChallengeComplete(result);
512 break;
513
514 case STATE_CHOOSE_IDENTITY:
515 DCHECK_EQ(OK, result);
516 result = DoChooseIdentity();
517 break;
518
519 case STATE_HANDLE_ANOTHER_CHALLENGE:
520 DCHECK_EQ(OK, result);
521 result = DoHandleAnotherChallenge();
522 break;
523
524 case STATE_HANDLE_ANOTHER_CHALLENGE_COMPLETE:
525 result = DoHandleAnotherChallengeComplete(result);
526 break;
527
528 case STATE_GENERATE_TOKEN:
529 DCHECK_EQ(OK, result);
530 result = DoGenerateToken();
531 break;
532
533 case STATE_GENERATE_TOKEN_COMPLETE:
534 result = DoGenerateTokenComplete(result);
535 break;
536
537 case STATE_NONE:
538 NOTREACHED() << "next_state_: " << next_state_;
539 }
540 } while (next_state_ != STATE_NONE && result != ERR_IO_PENDING);
541
542 if (result != ERR_IO_PENDING) {
543 // Exiting the state machine.
544 request_info_ = nullptr;
545 response_info_ = nullptr;
546 net_log_ = BoundNetLog();
547 }
548
549 return result;
550 }
551
552 int HttpAuthController::DoChooseChallenge() {
553 DCHECK(response_info_);
554 DCHECK(response_info_->headers.get());
555 DCHECK(!handler_);
556 DCHECK(auth_token_.empty());
557
558 auth_info_ = nullptr;
559
560 std::priority_queue<CandidateChallenge> old_challenges;
561 candidate_challenges_.swap(old_challenges);
562
563 void* iter = nullptr;
564 const std::string header_name = HttpAuth::GetChallengeHeaderName(target_);
565 std::string current_challenge;
566 while (response_info_->headers->EnumerateHeader(&iter, header_name,
567 &current_challenge)) {
568 HttpAuthChallengeTokenizer tokenizer(current_challenge.begin(),
569 current_challenge.end());
570 std::string current_scheme = tokenizer.NormalizedScheme();
571 if (current_scheme.empty())
572 continue;
573 if (IsAuthSchemeDisabled(current_scheme))
574 continue;
575
576 CandidateChallenge candidate_challenge;
577 candidate_challenge.scheme = current_scheme;
578 candidate_challenge.priority = GetSchemePriority(current_scheme);
579 candidate_challenge.challenge = current_challenge;
580 candidate_challenges_.push(candidate_challenge);
581 }
582 next_state_ = STATE_TRY_NEXT_CHALLENGE;
583 return OK;
584 }
585
586 int HttpAuthController::DoTryNextChallenge() {
587 DCHECK(!handler_);
588 DCHECK(auth_token_.empty());
589 DCHECK(response_info_);
590
591 for (; !handler_ && !candidate_challenges_.empty();
592 candidate_challenges_.pop()) {
593 const CandidateChallenge& candidate = candidate_challenges_.top();
594 selected_auth_challenge_ = candidate.challenge;
595 if (IsAuthSchemeDisabled(candidate.scheme))
596 continue;
597 handler_ = http_auth_handler_factory_->CreateAuthHandlerForScheme(
598 candidate.scheme);
599 }
600
601 if (!handler_)
602 return OK;
603
604 HttpAuthChallengeTokenizer tokenizer(selected_auth_challenge_.begin(),
605 selected_auth_challenge_.end());
606 next_state_ = STATE_TRY_NEXT_CHALLENGE_COMPLETE;
607 return handler_->HandleInitialChallenge(tokenizer, *response_info_, target_,
608 auth_origin_, net_log_, io_callback_);
609 }
610
611 int HttpAuthController::DoTryNextChallengeComplete(int result) {
612 DCHECK(handler_);
613
614 if (result != OK) {
615 InvalidateCurrentHandler(INVALIDATE_HANDLER);
616 next_state_ = STATE_TRY_NEXT_CHALLENGE;
617 return OK;
618 }
619
620 HistogramAuthEvent(handler_.get(), AUTH_EVENT_START);
621 next_state_ = STATE_CHOOSE_IDENTITY;
622 return OK;
623 }
624
625 int HttpAuthController::DoChooseIdentity() {
626 DCHECK(handler_.get());
627 DCHECK(identity_.invalid);
628 DCHECK(!auth_info_);
629
630 if (handler_->NeedsIdentity()) {
631 SelectNextAuthIdentityToTry();
632 } else {
633 // TODO(asanka): This is a false flag and shouldn't be necessary. Instead
634 // rely on NeedsIdentity() to determine if the handler needs an identity.
635 identity_.invalid = false;
636 }
637
638 if (!identity_.invalid) {
639 return OK;
640 }
641
642 if (!handler_->AllowsExplicitCredentials()) {
643 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT);
644 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
645 next_state_ = STATE_TRY_NEXT_CHALLENGE;
646 return OK;
647 }
648
649 PopulateAuthChallenge();
650 return OK;
651 }
652
653 int HttpAuthController::DoHandleAnotherChallenge() {
654 DCHECK(HaveAuth());
655 DCHECK(response_info_);
656 DCHECK(response_info_->headers.get());
657 DCHECK(auth_token_.empty());
658
659 next_state_ = STATE_HANDLE_ANOTHER_CHALLENGE_COMPLETE;
660 auth_info_ = nullptr;
661
662 selected_auth_challenge_.clear();
663 if (disabled_schemes_.Contains(handler_->auth_scheme()))
664 return HttpAuth::AUTHORIZATION_RESULT_REJECT;
665
666 std::string current_scheme_name = handler_->auth_scheme();
667 std::string challenge_header_name = HttpAuth::GetChallengeHeaderName(target_);
668
669 std::string challenge;
670 void* iter = nullptr;
671 HttpAuth::AuthorizationResult authorization_result =
672 HttpAuth::AUTHORIZATION_RESULT_INVALID;
673 while (response_info_->headers->EnumerateHeader(&iter, challenge_header_name,
674 &challenge)) {
675 HttpAuthChallengeTokenizer tokenizer(challenge.begin(), challenge.end());
676 if (!tokenizer.SchemeIs(current_scheme_name))
677 continue;
678
679 authorization_result = handler_->HandleAnotherChallenge(tokenizer);
680 if (authorization_result != HttpAuth::AUTHORIZATION_RESULT_INVALID) {
681 selected_auth_challenge_ = challenge;
682 return authorization_result;
683 }
684 }
685
686 return HttpAuth::AUTHORIZATION_RESULT_REJECT;
687 }
688
689 int HttpAuthController::DoHandleAnotherChallengeComplete(int result) {
690 if (result < 0)
691 return result;
692
693 HttpAuth::AuthorizationResult auth_result =
694 static_cast<HttpAuth::AuthorizationResult>(result);
695 switch (auth_result) {
696 case HttpAuth::AUTHORIZATION_RESULT_ACCEPT:
697 return OK;
698
699 case HttpAuth::AUTHORIZATION_RESULT_INVALID:
700 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
701 break;
702
703 case HttpAuth::AUTHORIZATION_RESULT_REJECT:
704 HistogramAuthEvent(handler_.get(), AUTH_EVENT_REJECT);
705 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
706 break;
707
708 case HttpAuth::AUTHORIZATION_RESULT_STALE:
709 if (http_auth_cache_->UpdateStaleChallenge(
710 auth_origin_, handler_->realm(), handler_->auth_scheme(),
711 selected_auth_challenge_)) {
712 InvalidateCurrentHandler(INVALIDATE_HANDLER);
713 } else {
714 InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
715 }
716 break;
717
718 case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM:
719 InvalidateCurrentHandler(identity_.source ==
720 HttpAuth::IDENT_SRC_PATH_LOOKUP
721 ? INVALIDATE_HANDLER
722 : INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
723 break;
724 }
725
726 DCHECK(!handler_.get());
727 next_state_ = STATE_CHOOSE_CHALLENGE;
728 return OK;
729 }
730
731 int HttpAuthController::DoGenerateToken() {
732 DCHECK(handler_.get());
733 DCHECK(!identity_.invalid || !handler_->NeedsIdentity());
734 DCHECK(request_info_);
735 DCHECK(HaveAuth());
736 DCHECK(auth_token_.empty());
737
738 next_state_ = STATE_GENERATE_TOKEN_COMPLETE;
739 const AuthCredentials* credentials =
740 identity_.source == HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS
741 ? nullptr
742 : &identity_.credentials;
743 return handler_->GenerateAuthToken(credentials, *request_info_, io_callback_,
744 &auth_token_);
745 }
746
747 int HttpAuthController::DoGenerateTokenComplete(int result) {
748 if (DisableOnAuthHandlerResult(result))
749 result = OK;
750 return result;
751 }
752
614 } // namespace net 753 } // namespace net
OLDNEW
« no previous file with comments | « net/http/http_auth_controller.h ('k') | net/http/http_auth_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698