| OLD | NEW |
| 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2009 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_network_transaction.h" | 5 #include "net/http/http_network_transaction.h" |
| 6 | 6 |
| 7 #include "base/scoped_ptr.h" | 7 #include "base/scoped_ptr.h" |
| 8 #include "base/compiler_specific.h" | 8 #include "base/compiler_specific.h" |
| 9 #include "base/field_trial.h" | 9 #include "base/field_trial.h" |
| 10 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 session_(session), | 137 session_(session), |
| 138 request_(NULL), | 138 request_(NULL), |
| 139 pac_request_(NULL), | 139 pac_request_(NULL), |
| 140 socket_factory_(csf), | 140 socket_factory_(csf), |
| 141 connection_(session->connection_pool()), | 141 connection_(session->connection_pool()), |
| 142 reused_socket_(false), | 142 reused_socket_(false), |
| 143 using_ssl_(false), | 143 using_ssl_(false), |
| 144 proxy_mode_(kDirectConnection), | 144 proxy_mode_(kDirectConnection), |
| 145 establishing_tunnel_(false), | 145 establishing_tunnel_(false), |
| 146 reading_body_from_socket_(false), | 146 reading_body_from_socket_(false), |
| 147 embedded_identity_used_(false), |
| 147 request_headers_(new RequestHeaders()), | 148 request_headers_(new RequestHeaders()), |
| 148 request_headers_bytes_sent_(0), | 149 request_headers_bytes_sent_(0), |
| 149 header_buf_(new ResponseHeaders()), | 150 header_buf_(new ResponseHeaders()), |
| 150 header_buf_capacity_(0), | 151 header_buf_capacity_(0), |
| 151 header_buf_len_(0), | 152 header_buf_len_(0), |
| 152 header_buf_body_offset_(-1), | 153 header_buf_body_offset_(-1), |
| 153 header_buf_http_offset_(-1), | 154 header_buf_http_offset_(-1), |
| 154 response_body_length_(-1), // -1 means unspecified. | 155 response_body_length_(-1), // -1 means unspecified. |
| 155 response_body_read_(0), | 156 response_body_read_(0), |
| 156 read_buf_len_(0), | 157 read_buf_len_(0), |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 247 DCHECK(auth_identity_[target].source != HttpAuth::IDENT_SRC_PATH_LOOKUP); | 248 DCHECK(auth_identity_[target].source != HttpAuth::IDENT_SRC_PATH_LOOKUP); |
| 248 | 249 |
| 249 // Add the auth entry to the cache before restarting. We don't know whether | 250 // Add the auth entry to the cache before restarting. We don't know whether |
| 250 // the identity is valid yet, but if it is valid we want other transactions | 251 // the identity is valid yet, but if it is valid we want other transactions |
| 251 // to know about it. If an entry for (origin, handler->realm()) already | 252 // to know about it. If an entry for (origin, handler->realm()) already |
| 252 // exists, we update it. | 253 // exists, we update it. |
| 253 // | 254 // |
| 254 // If auth_identity_[target].source is HttpAuth::IDENT_SRC_NONE, | 255 // If auth_identity_[target].source is HttpAuth::IDENT_SRC_NONE, |
| 255 // auth_identity_[target] contains no identity because identity is not | 256 // auth_identity_[target] contains no identity because identity is not |
| 256 // required yet. | 257 // required yet. |
| 258 // |
| 259 // TODO(wtc): For NTLM_SSPI, we add the same auth entry to the cache in |
| 260 // round 1 and round 2, which is redundant but correct. It would be nice |
| 261 // to add an auth entry to the cache only once, preferrably in round 1. |
| 262 // See http://crbug.com/21015. |
| 257 bool has_auth_identity = | 263 bool has_auth_identity = |
| 258 auth_identity_[target].source != HttpAuth::IDENT_SRC_NONE; | 264 auth_identity_[target].source != HttpAuth::IDENT_SRC_NONE; |
| 259 if (has_auth_identity) { | 265 if (has_auth_identity) { |
| 260 session_->auth_cache()->Add(AuthOrigin(target), auth_handler_[target], | 266 session_->auth_cache()->Add(AuthOrigin(target), auth_handler_[target], |
| 261 auth_identity_[target].username, auth_identity_[target].password, | 267 auth_identity_[target].username, auth_identity_[target].password, |
| 262 AuthPath(target)); | 268 AuthPath(target)); |
| 263 } | 269 } |
| 264 | 270 |
| 265 bool keep_alive = false; | 271 bool keep_alive = false; |
| 266 if (response_.headers->IsKeepAlive()) { | 272 if (response_.headers->IsKeepAlive()) { |
| 267 // If there is a response body of known length, we need to drain it first. | 273 // If there is a response body of known length, we need to drain it first. |
| 268 if (response_body_length_ > 0 || chunked_decoder_.get()) { | 274 if (response_body_length_ > 0 || chunked_decoder_.get()) { |
| 269 next_state_ = STATE_DRAIN_BODY_FOR_AUTH_RESTART; | 275 next_state_ = STATE_DRAIN_BODY_FOR_AUTH_RESTART; |
| 270 read_buf_ = new IOBuffer(kDrainBodyBufferSize); // A bit bucket | 276 read_buf_ = new IOBuffer(kDrainBodyBufferSize); // A bit bucket |
| 271 read_buf_len_ = kDrainBodyBufferSize; | 277 read_buf_len_ = kDrainBodyBufferSize; |
| 272 return; | 278 return; |
| 273 } | 279 } |
| 274 if (response_body_length_ == 0) // No response body to drain. | 280 if (response_body_length_ == 0) // No response body to drain. |
| 275 keep_alive = true; | 281 keep_alive = true; |
| 276 // response_body_length_ is -1 and we're not using chunked encoding. We | 282 // response_body_length_ is -1 and we're not using chunked encoding. We |
| 277 // don't know the length of the response body, so we can't reuse this | 283 // don't know the length of the response body, so we can't reuse this |
| 278 // connection even though the server says it's keep-alive. | 284 // connection even though the server says it's keep-alive. |
| 279 } | 285 } |
| 280 | 286 |
| 281 // If the auth scheme is connection-based but the proxy/server mistakenly | |
| 282 // marks the connection as non-keep-alive, the auth is going to fail, so log | |
| 283 // an error message. | |
| 284 if (!keep_alive && auth_handler_[target]->is_connection_based() && | |
| 285 has_auth_identity) { | |
| 286 LOG(ERROR) << "Can't perform " << auth_handler_[target]->scheme() | |
| 287 << " auth to the " << AuthTargetString(target) << " " | |
| 288 << AuthOrigin(target) << " over a non-keep-alive connection"; | |
| 289 | |
| 290 HttpVersion http_version = response_.headers->GetHttpVersion(); | |
| 291 LOG(ERROR) << " HTTP version is " << http_version.major_value() << "." | |
| 292 << http_version.minor_value(); | |
| 293 | |
| 294 std::string header_val; | |
| 295 void* iter = NULL; | |
| 296 while (response_.headers->EnumerateHeader(&iter, "connection", | |
| 297 &header_val)) { | |
| 298 LOG(ERROR) << " Has header Connection: " << header_val; | |
| 299 } | |
| 300 | |
| 301 iter = NULL; | |
| 302 while (response_.headers->EnumerateHeader(&iter, "proxy-connection", | |
| 303 &header_val)) { | |
| 304 LOG(ERROR) << " Has header Proxy-Connection: " << header_val; | |
| 305 } | |
| 306 | |
| 307 // RFC 4559 requires that a proxy indicate its support of NTLM/Negotiate | |
| 308 // authentication with a "Proxy-Support: Session-Based-Authentication" | |
| 309 // response header. | |
| 310 iter = NULL; | |
| 311 while (response_.headers->EnumerateHeader(&iter, "proxy-support", | |
| 312 &header_val)) { | |
| 313 LOG(ERROR) << " Has header Proxy-Support: " << header_val; | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 // We don't need to drain the response body, so we act as if we had drained | 287 // We don't need to drain the response body, so we act as if we had drained |
| 318 // the response body. | 288 // the response body. |
| 319 DidDrainBodyForAuthRestart(keep_alive); | 289 DidDrainBodyForAuthRestart(keep_alive); |
| 320 } | 290 } |
| 321 | 291 |
| 322 void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { | 292 void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { |
| 323 if (keep_alive) { | 293 if (keep_alive) { |
| 324 next_state_ = STATE_WRITE_HEADERS; | 294 next_state_ = STATE_WRITE_HEADERS; |
| 325 reused_socket_ = true; | 295 reused_socket_ = true; |
| 326 } else { | 296 } else { |
| (...skipping 399 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 726 ShouldApplyProxyAuth() && | 696 ShouldApplyProxyAuth() && |
| 727 (HaveAuth(HttpAuth::AUTH_PROXY) || | 697 (HaveAuth(HttpAuth::AUTH_PROXY) || |
| 728 SelectPreemptiveAuth(HttpAuth::AUTH_PROXY)); | 698 SelectPreemptiveAuth(HttpAuth::AUTH_PROXY)); |
| 729 bool have_server_auth = | 699 bool have_server_auth = |
| 730 ShouldApplyServerAuth() && | 700 ShouldApplyServerAuth() && |
| 731 (HaveAuth(HttpAuth::AUTH_SERVER) || | 701 (HaveAuth(HttpAuth::AUTH_SERVER) || |
| 732 SelectPreemptiveAuth(HttpAuth::AUTH_SERVER)); | 702 SelectPreemptiveAuth(HttpAuth::AUTH_SERVER)); |
| 733 | 703 |
| 734 std::string authorization_headers; | 704 std::string authorization_headers; |
| 735 | 705 |
| 706 // TODO(wtc): If BuildAuthorizationHeader fails (returns an authorization |
| 707 // header with no credentials), we should return an error to prevent |
| 708 // entering an infinite auth restart loop. See http://crbug.com/21050. |
| 736 if (have_proxy_auth) | 709 if (have_proxy_auth) |
| 737 authorization_headers.append( | 710 authorization_headers.append( |
| 738 BuildAuthorizationHeader(HttpAuth::AUTH_PROXY)); | 711 BuildAuthorizationHeader(HttpAuth::AUTH_PROXY)); |
| 739 if (have_server_auth) | 712 if (have_server_auth) |
| 740 authorization_headers.append( | 713 authorization_headers.append( |
| 741 BuildAuthorizationHeader(HttpAuth::AUTH_SERVER)); | 714 BuildAuthorizationHeader(HttpAuth::AUTH_SERVER)); |
| 742 | 715 |
| 743 if (establishing_tunnel_) { | 716 if (establishing_tunnel_) { |
| 744 BuildTunnelRequest(request_, authorization_headers, | 717 BuildTunnelRequest(request_, authorization_headers, |
| 745 &request_headers_->headers_); | 718 &request_headers_->headers_); |
| (...skipping 864 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1610 } | 1583 } |
| 1611 return false; | 1584 return false; |
| 1612 } | 1585 } |
| 1613 | 1586 |
| 1614 bool HttpNetworkTransaction::SelectNextAuthIdentityToTry( | 1587 bool HttpNetworkTransaction::SelectNextAuthIdentityToTry( |
| 1615 HttpAuth::Target target) { | 1588 HttpAuth::Target target) { |
| 1616 DCHECK(auth_handler_[target]); | 1589 DCHECK(auth_handler_[target]); |
| 1617 DCHECK(auth_identity_[target].invalid); | 1590 DCHECK(auth_identity_[target].invalid); |
| 1618 | 1591 |
| 1619 // Try to use the username/password encoded into the URL first. | 1592 // Try to use the username/password encoded into the URL first. |
| 1620 // (By checking source == IDENT_SRC_NONE, we make sure that this | |
| 1621 // is only done once for the transaction.) | |
| 1622 if (target == HttpAuth::AUTH_SERVER && request_->url.has_username() && | 1593 if (target == HttpAuth::AUTH_SERVER && request_->url.has_username() && |
| 1623 auth_identity_[target].source == HttpAuth::IDENT_SRC_NONE) { | 1594 !embedded_identity_used_) { |
| 1624 auth_identity_[target].source = HttpAuth::IDENT_SRC_URL; | 1595 auth_identity_[target].source = HttpAuth::IDENT_SRC_URL; |
| 1625 auth_identity_[target].invalid = false; | 1596 auth_identity_[target].invalid = false; |
| 1626 // TODO(wtc) It may be necessary to unescape the username and password | 1597 // TODO(wtc) It may be necessary to unescape the username and password |
| 1627 // after extracting them from the URL. We should be careful about | 1598 // after extracting them from the URL. We should be careful about |
| 1628 // embedded nulls in that case. | 1599 // embedded nulls in that case. |
| 1629 auth_identity_[target].username = ASCIIToWide(request_->url.username()); | 1600 auth_identity_[target].username = ASCIIToWide(request_->url.username()); |
| 1630 auth_identity_[target].password = ASCIIToWide(request_->url.password()); | 1601 auth_identity_[target].password = ASCIIToWide(request_->url.password()); |
| 1602 embedded_identity_used_ = true; |
| 1631 // TODO(eroman): If the password is blank, should we also try combining | 1603 // TODO(eroman): If the password is blank, should we also try combining |
| 1632 // with a password from the cache? | 1604 // with a password from the cache? |
| 1633 return true; | 1605 return true; |
| 1634 } | 1606 } |
| 1635 | 1607 |
| 1636 // Check the auth cache for a realm entry. | 1608 // Check the auth cache for a realm entry. |
| 1637 HttpAuthCache::Entry* entry = session_->auth_cache()->LookupByRealm( | 1609 HttpAuthCache::Entry* entry = session_->auth_cache()->LookupByRealm( |
| 1638 AuthOrigin(target), auth_handler_[target]->realm()); | 1610 AuthOrigin(target), auth_handler_[target]->realm()); |
| 1639 | 1611 |
| 1640 if (entry) { | 1612 if (entry) { |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1704 HttpAuth::AUTH_PROXY : HttpAuth::AUTH_SERVER; | 1676 HttpAuth::AUTH_PROXY : HttpAuth::AUTH_SERVER; |
| 1705 | 1677 |
| 1706 LOG(INFO) << "The " << AuthTargetString(target) << " " | 1678 LOG(INFO) << "The " << AuthTargetString(target) << " " |
| 1707 << AuthOrigin(target) << " requested auth" | 1679 << AuthOrigin(target) << " requested auth" |
| 1708 << AuthChallengeLogMessage(); | 1680 << AuthChallengeLogMessage(); |
| 1709 | 1681 |
| 1710 if (target == HttpAuth::AUTH_PROXY && proxy_info_.is_direct()) | 1682 if (target == HttpAuth::AUTH_PROXY && proxy_info_.is_direct()) |
| 1711 return ERR_UNEXPECTED_PROXY_AUTH; | 1683 return ERR_UNEXPECTED_PROXY_AUTH; |
| 1712 | 1684 |
| 1713 // The auth we tried just failed, hence it can't be valid. Remove it from | 1685 // The auth we tried just failed, hence it can't be valid. Remove it from |
| 1714 // the cache so it won't be used again, unless it's a null identity. | 1686 // the cache so it won't be used again. |
| 1715 if (HaveAuth(target) && | 1687 // TODO(wtc): IsFinalRound is not the right condition. In a multi-round |
| 1716 auth_identity_[target].source != HttpAuth::IDENT_SRC_NONE) | 1688 // auth sequence, the server may fail the auth in round 1 if our first |
| 1689 // authorization header is broken. We should inspect response_.headers to |
| 1690 // determine if the server already failed the auth or wants us to continue. |
| 1691 // See http://crbug.com/21015. |
| 1692 if (HaveAuth(target) && auth_handler_[target]->IsFinalRound()) { |
| 1717 InvalidateRejectedAuthFromCache(target); | 1693 InvalidateRejectedAuthFromCache(target); |
| 1694 auth_handler_[target] = NULL; |
| 1695 auth_identity_[target] = HttpAuth::Identity(); |
| 1696 } |
| 1718 | 1697 |
| 1719 auth_identity_[target].invalid = true; | 1698 auth_identity_[target].invalid = true; |
| 1720 | 1699 |
| 1721 if (target != HttpAuth::AUTH_SERVER || | 1700 if (target != HttpAuth::AUTH_SERVER || |
| 1722 !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA)) { | 1701 !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA)) { |
| 1723 // Find the best authentication challenge that we support. | 1702 // Find the best authentication challenge that we support. |
| 1724 HttpAuth::ChooseBestChallenge(response_.headers.get(), | 1703 HttpAuth::ChooseBestChallenge(response_.headers.get(), |
| 1725 target, | 1704 target, |
| 1705 AuthOrigin(target), |
| 1726 &auth_handler_[target]); | 1706 &auth_handler_[target]); |
| 1727 } | 1707 } |
| 1728 | 1708 |
| 1729 if (!auth_handler_[target]) { | 1709 if (!auth_handler_[target]) { |
| 1730 if (establishing_tunnel_) { | 1710 if (establishing_tunnel_) { |
| 1731 LOG(ERROR) << "Can't perform auth to the " << AuthTargetString(target) | 1711 LOG(ERROR) << "Can't perform auth to the " << AuthTargetString(target) |
| 1732 << " " << AuthOrigin(target) | 1712 << " " << AuthOrigin(target) |
| 1733 << " when establishing a tunnel" | 1713 << " when establishing a tunnel" |
| 1734 << AuthChallengeLogMessage(); | 1714 << AuthChallengeLogMessage(); |
| 1735 | 1715 |
| 1736 // We are establishing a tunnel, we can't show the error page because an | 1716 // We are establishing a tunnel, we can't show the error page because an |
| 1737 // active network attacker could control its contents. Instead, we just | 1717 // active network attacker could control its contents. Instead, we just |
| 1738 // fail to establish the tunnel. | 1718 // fail to establish the tunnel. |
| 1739 DCHECK(target == HttpAuth::AUTH_PROXY); | 1719 DCHECK(target == HttpAuth::AUTH_PROXY); |
| 1740 return ERR_PROXY_AUTH_REQUESTED; | 1720 return ERR_PROXY_AUTH_REQUESTED; |
| 1741 } | 1721 } |
| 1742 // We found no supported challenge -- let the transaction continue | 1722 // We found no supported challenge -- let the transaction continue |
| 1743 // so we end up displaying the error page. | 1723 // so we end up displaying the error page. |
| 1744 return OK; | 1724 return OK; |
| 1745 } | 1725 } |
| 1746 | 1726 |
| 1747 if (auth_handler_[target]->NeedsIdentity()) { | 1727 if (auth_handler_[target]->NeedsIdentity()) { |
| 1748 // Pick a new auth identity to try, by looking to the URL and auth cache. | 1728 // Pick a new auth identity to try, by looking to the URL and auth cache. |
| 1749 // If an identity to try is found, it is saved to auth_identity_[target]. | 1729 // If an identity to try is found, it is saved to auth_identity_[target]. |
| 1750 SelectNextAuthIdentityToTry(target); | 1730 SelectNextAuthIdentityToTry(target); |
| 1751 } else { | 1731 } else { |
| 1752 // Proceed with a null identity. | 1732 // Proceed with the existing identity or a null identity. |
| 1753 // | 1733 // |
| 1754 // TODO(wtc): Add a safeguard against infinite transaction restarts, if | 1734 // TODO(wtc): Add a safeguard against infinite transaction restarts, if |
| 1755 // the server keeps returning "NTLM". | 1735 // the server keeps returning "NTLM". |
| 1756 auth_identity_[target].source = HttpAuth::IDENT_SRC_NONE; | |
| 1757 auth_identity_[target].invalid = false; | 1736 auth_identity_[target].invalid = false; |
| 1758 auth_identity_[target].username.clear(); | |
| 1759 auth_identity_[target].password.clear(); | |
| 1760 } | 1737 } |
| 1761 | 1738 |
| 1762 // Make a note that we are waiting for auth. This variable is inspected | 1739 // Make a note that we are waiting for auth. This variable is inspected |
| 1763 // when the client calls RestartWithAuth() to pick up where we left off. | 1740 // when the client calls RestartWithAuth() to pick up where we left off. |
| 1764 pending_auth_target_ = target; | 1741 pending_auth_target_ = target; |
| 1765 | 1742 |
| 1766 if (auth_identity_[target].invalid) { | 1743 if (auth_identity_[target].invalid) { |
| 1767 // We have exhausted all identity possibilities, all we can do now is | 1744 // We have exhausted all identity possibilities, all we can do now is |
| 1768 // pass the challenge information back to the client. | 1745 // pass the challenge information back to the client. |
| 1769 PopulateAuthChallenge(target); | 1746 PopulateAuthChallenge(target); |
| 1770 } | 1747 } |
| 1771 return OK; | 1748 return OK; |
| 1772 } | 1749 } |
| 1773 | 1750 |
| 1774 void HttpNetworkTransaction::PopulateAuthChallenge(HttpAuth::Target target) { | 1751 void HttpNetworkTransaction::PopulateAuthChallenge(HttpAuth::Target target) { |
| 1775 // Populates response_.auth_challenge with the authentication challenge info. | 1752 // Populates response_.auth_challenge with the authentication challenge info. |
| 1776 // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo(). | 1753 // This info is consumed by URLRequestHttpJob::GetAuthChallengeInfo(). |
| 1777 | 1754 |
| 1778 AuthChallengeInfo* auth_info = new AuthChallengeInfo; | 1755 AuthChallengeInfo* auth_info = new AuthChallengeInfo; |
| 1779 auth_info->is_proxy = target == HttpAuth::AUTH_PROXY; | 1756 auth_info->is_proxy = target == HttpAuth::AUTH_PROXY; |
| 1757 auth_info->host_and_port = ASCIIToWide(GetHostAndPort(AuthOrigin(target))); |
| 1780 auth_info->scheme = ASCIIToWide(auth_handler_[target]->scheme()); | 1758 auth_info->scheme = ASCIIToWide(auth_handler_[target]->scheme()); |
| 1781 // TODO(eroman): decode realm according to RFC 2047. | 1759 // TODO(eroman): decode realm according to RFC 2047. |
| 1782 auth_info->realm = ASCIIToWide(auth_handler_[target]->realm()); | 1760 auth_info->realm = ASCIIToWide(auth_handler_[target]->realm()); |
| 1783 | |
| 1784 std::string host_and_port; | |
| 1785 if (target == HttpAuth::AUTH_PROXY) { | |
| 1786 host_and_port = proxy_info_.proxy_server().host_and_port(); | |
| 1787 } else { | |
| 1788 DCHECK(target == HttpAuth::AUTH_SERVER); | |
| 1789 host_and_port = GetHostAndPort(request_->url); | |
| 1790 } | |
| 1791 auth_info->host_and_port = ASCIIToWide(host_and_port); | |
| 1792 response_.auth_challenge = auth_info; | 1761 response_.auth_challenge = auth_info; |
| 1793 } | 1762 } |
| 1794 | 1763 |
| 1795 } // namespace net | 1764 } // namespace net |
| OLD | NEW |