http_auth_controller.cc 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  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
  3. // found in the LICENSE file.
  4. #include "net/http/http_auth_controller.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/callback_helpers.h"
  8. #include "base/metrics/histogram_macros.h"
  9. #include "base/strings/string_util.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "base/values.h"
  12. #include "net/base/auth.h"
  13. #include "net/base/url_util.h"
  14. #include "net/dns/host_resolver.h"
  15. #include "net/http/http_auth_handler.h"
  16. #include "net/http/http_auth_handler_factory.h"
  17. #include "net/http/http_network_session.h"
  18. #include "net/http/http_request_headers.h"
  19. #include "net/http/http_request_info.h"
  20. #include "net/http/http_response_headers.h"
  21. #include "net/log/net_log_event_type.h"
  22. #include "net/log/net_log_source.h"
  23. #include "net/log/net_log_source_type.h"
  24. #include "net/log/net_log_with_source.h"
  25. #include "url/scheme_host_port.h"
  26. namespace net {
  27. namespace {
  28. base::Value ControllerParamsToValue(HttpAuth::Target target, const GURL& url) {
  29. base::Value::Dict params;
  30. params.Set("target", HttpAuth::GetAuthTargetString(target));
  31. params.Set("url", url.spec());
  32. return base::Value(std::move(params));
  33. }
  34. } // namespace
  35. HttpAuthController::HttpAuthController(
  36. HttpAuth::Target target,
  37. const GURL& auth_url,
  38. const NetworkIsolationKey& network_isolation_key,
  39. HttpAuthCache* http_auth_cache,
  40. HttpAuthHandlerFactory* http_auth_handler_factory,
  41. HostResolver* host_resolver)
  42. : target_(target),
  43. auth_url_(auth_url),
  44. auth_scheme_host_port_(auth_url),
  45. auth_path_(auth_url.path()),
  46. network_isolation_key_(network_isolation_key),
  47. http_auth_cache_(http_auth_cache),
  48. http_auth_handler_factory_(http_auth_handler_factory),
  49. host_resolver_(host_resolver) {
  50. DCHECK(target != HttpAuth::AUTH_PROXY || auth_path_ == "/");
  51. DCHECK(auth_scheme_host_port_.IsValid());
  52. }
  53. HttpAuthController::~HttpAuthController() {
  54. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  55. if (net_log_.source().IsValid())
  56. net_log_.EndEvent(NetLogEventType::AUTH_CONTROLLER);
  57. }
  58. void HttpAuthController::BindToCallingNetLog(
  59. const NetLogWithSource& caller_net_log) {
  60. if (!net_log_.source().IsValid()) {
  61. net_log_ = NetLogWithSource::Make(caller_net_log.net_log(),
  62. NetLogSourceType::HTTP_AUTH_CONTROLLER);
  63. net_log_.BeginEvent(NetLogEventType::AUTH_CONTROLLER, [&] {
  64. return ControllerParamsToValue(target_, auth_url_);
  65. });
  66. }
  67. caller_net_log.AddEventReferencingSource(
  68. NetLogEventType::AUTH_BOUND_TO_CONTROLLER, net_log_.source());
  69. }
  70. int HttpAuthController::MaybeGenerateAuthToken(
  71. const HttpRequestInfo* request,
  72. CompletionOnceCallback callback,
  73. const NetLogWithSource& caller_net_log) {
  74. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  75. DCHECK(!auth_info_);
  76. bool needs_auth = HaveAuth() || SelectPreemptiveAuth(caller_net_log);
  77. if (!needs_auth)
  78. return OK;
  79. net_log_.BeginEventReferencingSource(NetLogEventType::AUTH_GENERATE_TOKEN,
  80. caller_net_log.source());
  81. const AuthCredentials* credentials = nullptr;
  82. if (identity_.source != HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS)
  83. credentials = &identity_.credentials;
  84. DCHECK(auth_token_.empty());
  85. DCHECK(callback_.is_null());
  86. int rv = handler_->GenerateAuthToken(
  87. credentials, request,
  88. base::BindOnce(&HttpAuthController::OnGenerateAuthTokenDone,
  89. base::Unretained(this)),
  90. &auth_token_);
  91. if (rv == ERR_IO_PENDING) {
  92. callback_ = std::move(callback);
  93. return rv;
  94. }
  95. return HandleGenerateTokenResult(rv);
  96. }
  97. bool HttpAuthController::SelectPreemptiveAuth(
  98. const NetLogWithSource& caller_net_log) {
  99. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  100. DCHECK(!HaveAuth());
  101. DCHECK(identity_.invalid);
  102. // Don't do preemptive authorization if the URL contains a username:password,
  103. // since we must first be challenged in order to use the URL's identity.
  104. if (auth_url_.has_username())
  105. return false;
  106. // SelectPreemptiveAuth() is on the critical path for each request, so it
  107. // is expected to be fast. LookupByPath() is fast in the common case, since
  108. // the number of http auth cache entries is expected to be very small.
  109. // (For most users in fact, it will be 0.)
  110. HttpAuthCache::Entry* entry = http_auth_cache_->LookupByPath(
  111. auth_scheme_host_port_, target_, network_isolation_key_, auth_path_);
  112. if (!entry)
  113. return false;
  114. BindToCallingNetLog(caller_net_log);
  115. // Try to create a handler using the previous auth challenge.
  116. std::unique_ptr<HttpAuthHandler> handler_preemptive;
  117. int rv_create =
  118. http_auth_handler_factory_->CreatePreemptiveAuthHandlerFromString(
  119. entry->auth_challenge(), target_, network_isolation_key_,
  120. auth_scheme_host_port_, entry->IncrementNonceCount(), net_log_,
  121. host_resolver_, &handler_preemptive);
  122. if (rv_create != OK)
  123. return false;
  124. // Set the state
  125. identity_.source = HttpAuth::IDENT_SRC_PATH_LOOKUP;
  126. identity_.invalid = false;
  127. identity_.credentials = entry->credentials();
  128. handler_.swap(handler_preemptive);
  129. return true;
  130. }
  131. void HttpAuthController::AddAuthorizationHeader(
  132. HttpRequestHeaders* authorization_headers) {
  133. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  134. DCHECK(HaveAuth());
  135. // auth_token_ can be empty if we encountered a permanent error with
  136. // the auth scheme and want to retry.
  137. if (!auth_token_.empty()) {
  138. authorization_headers->SetHeader(
  139. HttpAuth::GetAuthorizationHeaderName(target_), auth_token_);
  140. auth_token_.clear();
  141. }
  142. }
  143. int HttpAuthController::HandleAuthChallenge(
  144. scoped_refptr<HttpResponseHeaders> headers,
  145. const SSLInfo& ssl_info,
  146. bool do_not_send_server_auth,
  147. bool establishing_tunnel,
  148. const NetLogWithSource& caller_net_log) {
  149. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  150. DCHECK(headers.get());
  151. DCHECK(auth_scheme_host_port_.IsValid());
  152. DCHECK(!auth_info_);
  153. BindToCallingNetLog(caller_net_log);
  154. net_log_.BeginEventReferencingSource(NetLogEventType::AUTH_HANDLE_CHALLENGE,
  155. caller_net_log.source());
  156. // Give the existing auth handler first try at the authentication headers.
  157. // This will also evict the entry in the HttpAuthCache if the previous
  158. // challenge appeared to be rejected, or is using a stale nonce in the Digest
  159. // case.
  160. if (HaveAuth()) {
  161. std::string challenge_used;
  162. HttpAuth::AuthorizationResult result = HttpAuth::HandleChallengeResponse(
  163. handler_.get(), *headers, target_, disabled_schemes_, &challenge_used);
  164. switch (result) {
  165. case HttpAuth::AUTHORIZATION_RESULT_ACCEPT:
  166. break;
  167. case HttpAuth::AUTHORIZATION_RESULT_INVALID:
  168. InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
  169. break;
  170. case HttpAuth::AUTHORIZATION_RESULT_REJECT:
  171. InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
  172. break;
  173. case HttpAuth::AUTHORIZATION_RESULT_STALE:
  174. if (http_auth_cache_->UpdateStaleChallenge(
  175. auth_scheme_host_port_, target_, handler_->realm(),
  176. handler_->auth_scheme(), network_isolation_key_,
  177. challenge_used)) {
  178. InvalidateCurrentHandler(INVALIDATE_HANDLER);
  179. } else {
  180. // It's possible that a server could incorrectly issue a stale
  181. // response when the entry is not in the cache. Just evict the
  182. // current value from the cache.
  183. InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
  184. }
  185. break;
  186. case HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM:
  187. // If the server changes the authentication realm in a
  188. // subsequent challenge, invalidate cached credentials for the
  189. // previous realm. If the server rejects a preemptive
  190. // authorization and requests credentials for a different
  191. // realm, we keep the cached credentials.
  192. InvalidateCurrentHandler(
  193. (identity_.source == HttpAuth::IDENT_SRC_PATH_LOOKUP) ?
  194. INVALIDATE_HANDLER :
  195. INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
  196. break;
  197. default:
  198. NOTREACHED();
  199. break;
  200. }
  201. }
  202. identity_.invalid = true;
  203. bool can_send_auth = (target_ != HttpAuth::AUTH_SERVER ||
  204. !do_not_send_server_auth);
  205. do {
  206. if (!handler_.get() && can_send_auth) {
  207. // Find the best authentication challenge that we support.
  208. HttpAuth::ChooseBestChallenge(http_auth_handler_factory_, *headers,
  209. ssl_info, network_isolation_key_, target_,
  210. auth_scheme_host_port_, disabled_schemes_,
  211. net_log_, host_resolver_, &handler_);
  212. }
  213. if (!handler_.get()) {
  214. if (establishing_tunnel) {
  215. // We are establishing a tunnel, we can't show the error page because an
  216. // active network attacker could control its contents. Instead, we just
  217. // fail to establish the tunnel.
  218. DCHECK_EQ(target_, HttpAuth::AUTH_PROXY);
  219. net_log_.EndEventWithNetErrorCode(
  220. NetLogEventType::AUTH_HANDLE_CHALLENGE, ERR_PROXY_AUTH_UNSUPPORTED);
  221. return ERR_PROXY_AUTH_UNSUPPORTED;
  222. }
  223. // We found no supported challenge -- let the transaction continue so we
  224. // end up displaying the error page.
  225. net_log_.EndEvent(NetLogEventType::AUTH_HANDLE_CHALLENGE);
  226. return OK;
  227. }
  228. if (handler_->NeedsIdentity()) {
  229. // Pick a new auth identity to try, by looking to the URL and auth cache.
  230. // If an identity to try is found, it is saved to identity_.
  231. SelectNextAuthIdentityToTry();
  232. } else {
  233. // Proceed with the existing identity or a null identity.
  234. identity_.invalid = false;
  235. }
  236. // From this point on, we are restartable.
  237. if (identity_.invalid) {
  238. // We have exhausted all identity possibilities.
  239. if (!handler_->AllowsExplicitCredentials()) {
  240. // If the handler doesn't accept explicit credentials, then we need to
  241. // choose a different auth scheme.
  242. InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
  243. } else {
  244. // Pass the challenge information back to the client.
  245. PopulateAuthChallenge();
  246. }
  247. }
  248. // If we get here and we don't have a handler_, that's because we
  249. // invalidated it due to not having any viable identities to use with it. Go
  250. // back and try again.
  251. // TODO(asanka): Instead we should create a priority list of
  252. // <handler,identity> and iterate through that.
  253. } while(!handler_.get());
  254. net_log_.EndEvent(NetLogEventType::AUTH_HANDLE_CHALLENGE);
  255. return OK;
  256. }
  257. void HttpAuthController::ResetAuth(const AuthCredentials& credentials) {
  258. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  259. DCHECK(identity_.invalid || credentials.Empty());
  260. if (identity_.invalid) {
  261. // Update the credentials.
  262. identity_.source = HttpAuth::IDENT_SRC_EXTERNAL;
  263. identity_.invalid = false;
  264. identity_.credentials = credentials;
  265. // auth_info_ is no longer necessary.
  266. auth_info_ = absl::nullopt;
  267. }
  268. DCHECK(identity_.source != HttpAuth::IDENT_SRC_PATH_LOOKUP);
  269. // Add the auth entry to the cache before restarting. We don't know whether
  270. // the identity is valid yet, but if it is valid we want other transactions
  271. // to know about it. If an entry for (origin, handler->realm()) already
  272. // exists, we update it.
  273. //
  274. // If identity_.source is HttpAuth::IDENT_SRC_NONE or
  275. // HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS, identity_ contains no
  276. // identity because identity is not required yet or we're using default
  277. // credentials.
  278. //
  279. // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in
  280. // round 1 and round 2, which is redundant but correct. It would be nice
  281. // to add an auth entry to the cache only once, preferrably in round 1.
  282. // See http://crbug.com/21015.
  283. switch (identity_.source) {
  284. case HttpAuth::IDENT_SRC_NONE:
  285. case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
  286. break;
  287. default:
  288. http_auth_cache_->Add(auth_scheme_host_port_, target_, handler_->realm(),
  289. handler_->auth_scheme(), network_isolation_key_,
  290. handler_->challenge(), identity_.credentials,
  291. auth_path_);
  292. break;
  293. }
  294. }
  295. bool HttpAuthController::HaveAuthHandler() const {
  296. return handler_.get() != nullptr;
  297. }
  298. bool HttpAuthController::HaveAuth() const {
  299. return handler_.get() && !identity_.invalid;
  300. }
  301. bool HttpAuthController::NeedsHTTP11() const {
  302. return handler_ && handler_->is_connection_based();
  303. }
  304. void HttpAuthController::InvalidateCurrentHandler(
  305. InvalidateHandlerAction action) {
  306. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  307. DCHECK(handler_.get());
  308. switch (action) {
  309. case INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS:
  310. InvalidateRejectedAuthFromCache();
  311. break;
  312. case INVALIDATE_HANDLER_AND_DISABLE_SCHEME:
  313. DisableAuthScheme(handler_->auth_scheme());
  314. break;
  315. case INVALIDATE_HANDLER:
  316. PrepareIdentityForReuse();
  317. break;
  318. }
  319. handler_.reset();
  320. identity_ = HttpAuth::Identity();
  321. }
  322. void HttpAuthController::InvalidateRejectedAuthFromCache() {
  323. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  324. DCHECK(HaveAuth());
  325. // Clear the cache entry for the identity we just failed on.
  326. // Note: we require the credentials to match before invalidating
  327. // since the entry in the cache may be newer than what we used last time.
  328. http_auth_cache_->Remove(auth_scheme_host_port_, target_, handler_->realm(),
  329. handler_->auth_scheme(), network_isolation_key_,
  330. identity_.credentials);
  331. }
  332. void HttpAuthController::PrepareIdentityForReuse() {
  333. if (identity_.invalid)
  334. return;
  335. switch (identity_.source) {
  336. case HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS:
  337. DCHECK(default_credentials_used_);
  338. default_credentials_used_ = false;
  339. break;
  340. case HttpAuth::IDENT_SRC_URL:
  341. DCHECK(embedded_identity_used_);
  342. embedded_identity_used_ = false;
  343. break;
  344. case HttpAuth::IDENT_SRC_NONE:
  345. case HttpAuth::IDENT_SRC_PATH_LOOKUP:
  346. case HttpAuth::IDENT_SRC_REALM_LOOKUP:
  347. case HttpAuth::IDENT_SRC_EXTERNAL:
  348. break;
  349. }
  350. }
  351. bool HttpAuthController::SelectNextAuthIdentityToTry() {
  352. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  353. DCHECK(handler_.get());
  354. DCHECK(identity_.invalid);
  355. // Try to use the username:password encoded into the URL first.
  356. if (target_ == HttpAuth::AUTH_SERVER && auth_url_.has_username() &&
  357. !embedded_identity_used_) {
  358. identity_.source = HttpAuth::IDENT_SRC_URL;
  359. identity_.invalid = false;
  360. // Extract the username:password from the URL.
  361. std::u16string username;
  362. std::u16string password;
  363. GetIdentityFromURL(auth_url_, &username, &password);
  364. identity_.credentials.Set(username, password);
  365. embedded_identity_used_ = true;
  366. // TODO(eroman): If the password is blank, should we also try combining
  367. // with a password from the cache?
  368. UMA_HISTOGRAM_BOOLEAN("net.HttpIdentSrcURL", true);
  369. return true;
  370. }
  371. // Check the auth cache for a realm entry.
  372. HttpAuthCache::Entry* entry = http_auth_cache_->Lookup(
  373. auth_scheme_host_port_, target_, handler_->realm(),
  374. handler_->auth_scheme(), network_isolation_key_);
  375. if (entry) {
  376. identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP;
  377. identity_.invalid = false;
  378. identity_.credentials = entry->credentials();
  379. return true;
  380. }
  381. // Use default credentials (single sign-on) if they're allowed and this is the
  382. // first attempt at using an identity. Do not allow multiple times as it will
  383. // infinite loop. We use default credentials after checking the auth cache so
  384. // that if single sign-on doesn't work, we won't try default credentials for
  385. // future transactions.
  386. if (!default_credentials_used_ && handler_->AllowsDefaultCredentials()) {
  387. identity_.source = HttpAuth::IDENT_SRC_DEFAULT_CREDENTIALS;
  388. identity_.invalid = false;
  389. default_credentials_used_ = true;
  390. return true;
  391. }
  392. return false;
  393. }
  394. void HttpAuthController::PopulateAuthChallenge() {
  395. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  396. // Populates response_.auth_challenge with the authentication challenge info.
  397. // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo().
  398. auth_info_ = AuthChallengeInfo();
  399. auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY);
  400. auth_info_->challenger = auth_scheme_host_port_;
  401. auth_info_->scheme = HttpAuth::SchemeToString(handler_->auth_scheme());
  402. auth_info_->realm = handler_->realm();
  403. auth_info_->path = auth_path_;
  404. auth_info_->challenge = handler_->challenge();
  405. }
  406. int HttpAuthController::HandleGenerateTokenResult(int result) {
  407. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  408. net_log_.EndEventWithNetErrorCode(NetLogEventType::AUTH_GENERATE_TOKEN,
  409. result);
  410. switch (result) {
  411. // Occurs if the credential handle is found to be invalid at the point it is
  412. // exercised (i.e. GenerateAuthToken stage). We are going to consider this
  413. // to be an error that invalidates the identity but not necessarily the
  414. // scheme. Doing so allows a different identity to be used with the same
  415. // scheme. See https://crbug.com/648366.
  416. case ERR_INVALID_HANDLE:
  417. // If the GenerateAuthToken call fails with this error, this means that the
  418. // handler can no longer be used. However, the authentication scheme is
  419. // considered still usable. This allows a scheme that attempted and failed
  420. // to use default credentials to recover and use explicit credentials.
  421. //
  422. // The current handler may be tied to external state that is no longer
  423. // valid, hence should be discarded. Since the scheme is still valid, a new
  424. // handler can be created for the current scheme.
  425. case ERR_INVALID_AUTH_CREDENTIALS:
  426. InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_CACHED_CREDENTIALS);
  427. auth_token_.clear();
  428. return OK;
  429. // Occurs with GSSAPI, if the user has not already logged in.
  430. case ERR_MISSING_AUTH_CREDENTIALS:
  431. // Can occur with GSSAPI or SSPI if the underlying library reports
  432. // a permanent error.
  433. case ERR_UNSUPPORTED_AUTH_SCHEME:
  434. // These two error codes represent failures we aren't handling.
  435. case ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS:
  436. case ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS:
  437. // Can be returned by SSPI if the authenticating authority or
  438. // target is not known.
  439. case ERR_MISCONFIGURED_AUTH_ENVIRONMENT:
  440. // In these cases, disable the current scheme as it cannot
  441. // succeed.
  442. InvalidateCurrentHandler(INVALIDATE_HANDLER_AND_DISABLE_SCHEME);
  443. auth_token_.clear();
  444. return OK;
  445. default:
  446. return result;
  447. }
  448. }
  449. void HttpAuthController::OnGenerateAuthTokenDone(int result) {
  450. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  451. result = HandleGenerateTokenResult(result);
  452. if (!callback_.is_null()) {
  453. std::move(callback_).Run(result);
  454. }
  455. }
  456. void HttpAuthController::TakeAuthInfo(
  457. absl::optional<AuthChallengeInfo>* other) {
  458. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  459. auth_info_.swap(*other);
  460. }
  461. bool HttpAuthController::IsAuthSchemeDisabled(HttpAuth::Scheme scheme) const {
  462. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  463. return disabled_schemes_.find(scheme) != disabled_schemes_.end();
  464. }
  465. void HttpAuthController::DisableAuthScheme(HttpAuth::Scheme scheme) {
  466. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  467. disabled_schemes_.insert(scheme);
  468. }
  469. void HttpAuthController::DisableEmbeddedIdentity() {
  470. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  471. embedded_identity_used_ = true;
  472. }
  473. void HttpAuthController::OnConnectionClosed() {
  474. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  475. InvalidateCurrentHandler(INVALIDATE_HANDLER);
  476. }
  477. } // namespace net