Index: net/http/http_proxy_client_socket.cc |
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc |
deleted file mode 100644 |
index 97945d919341e45be655f4e0ebc9f5a5ff89fce0..0000000000000000000000000000000000000000 |
--- a/net/http/http_proxy_client_socket.cc |
+++ /dev/null |
@@ -1,555 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "net/http/http_proxy_client_socket.h" |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/strings/string_util.h" |
-#include "base/strings/stringprintf.h" |
-#include "net/base/auth.h" |
-#include "net/base/host_port_pair.h" |
-#include "net/base/io_buffer.h" |
-#include "net/base/net_log.h" |
-#include "net/base/net_util.h" |
-#include "net/base/proxy_delegate.h" |
-#include "net/http/http_basic_stream.h" |
-#include "net/http/http_network_session.h" |
-#include "net/http/http_request_info.h" |
-#include "net/http/http_response_headers.h" |
-#include "net/http/http_stream_parser.h" |
-#include "net/http/proxy_connect_redirect_http_stream.h" |
-#include "net/socket/client_socket_handle.h" |
-#include "url/gurl.h" |
- |
-namespace net { |
- |
-HttpProxyClientSocket::HttpProxyClientSocket( |
- ClientSocketHandle* transport_socket, |
- const GURL& request_url, |
- const std::string& user_agent, |
- const HostPortPair& endpoint, |
- const HostPortPair& proxy_server, |
- HttpAuthCache* http_auth_cache, |
- HttpAuthHandlerFactory* http_auth_handler_factory, |
- bool tunnel, |
- bool using_spdy, |
- NextProto protocol_negotiated, |
- ProxyDelegate* proxy_delegate, |
- bool is_https_proxy) |
- : io_callback_(base::Bind(&HttpProxyClientSocket::OnIOComplete, |
- base::Unretained(this))), |
- next_state_(STATE_NONE), |
- transport_(transport_socket), |
- endpoint_(endpoint), |
- auth_(tunnel ? |
- new HttpAuthController(HttpAuth::AUTH_PROXY, |
- GURL((is_https_proxy ? "https://" : "http://") |
- + proxy_server.ToString()), |
- http_auth_cache, |
- http_auth_handler_factory) |
- : NULL), |
- tunnel_(tunnel), |
- using_spdy_(using_spdy), |
- protocol_negotiated_(protocol_negotiated), |
- is_https_proxy_(is_https_proxy), |
- redirect_has_load_timing_info_(false), |
- proxy_server_(proxy_server), |
- proxy_delegate_(proxy_delegate), |
- net_log_(transport_socket->socket()->NetLog()) { |
- // Synthesize the bits of a request that we actually use. |
- request_.url = request_url; |
- request_.method = "GET"; |
- if (!user_agent.empty()) |
- request_.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, |
- user_agent); |
-} |
- |
-HttpProxyClientSocket::~HttpProxyClientSocket() { |
- Disconnect(); |
-} |
- |
-int HttpProxyClientSocket::RestartWithAuth(const CompletionCallback& callback) { |
- DCHECK_EQ(STATE_NONE, next_state_); |
- DCHECK(user_callback_.is_null()); |
- |
- int rv = PrepareForAuthRestart(); |
- if (rv != OK) |
- return rv; |
- |
- rv = DoLoop(OK); |
- if (rv == ERR_IO_PENDING) { |
- if (!callback.is_null()) |
- user_callback_ = callback; |
- } |
- |
- return rv; |
-} |
- |
-const scoped_refptr<HttpAuthController>& |
-HttpProxyClientSocket::GetAuthController() const { |
- return auth_; |
-} |
- |
-bool HttpProxyClientSocket::IsUsingSpdy() const { |
- return using_spdy_; |
-} |
- |
-NextProto HttpProxyClientSocket::GetProtocolNegotiated() const { |
- return protocol_negotiated_; |
-} |
- |
-const HttpResponseInfo* HttpProxyClientSocket::GetConnectResponseInfo() const { |
- return response_.headers.get() ? &response_ : NULL; |
-} |
- |
-HttpStream* HttpProxyClientSocket::CreateConnectResponseStream() { |
- return new ProxyConnectRedirectHttpStream( |
- redirect_has_load_timing_info_ ? &redirect_load_timing_info_ : NULL); |
-} |
- |
- |
-int HttpProxyClientSocket::Connect(const CompletionCallback& callback) { |
- DCHECK(transport_.get()); |
- DCHECK(transport_->socket()); |
- DCHECK(user_callback_.is_null()); |
- |
- // TODO(rch): figure out the right way to set up a tunnel with SPDY. |
- // This approach sends the complete HTTPS request to the proxy |
- // which allows the proxy to see "private" data. Instead, we should |
- // create an SSL tunnel to the origin server using the CONNECT method |
- // inside a single SPDY stream. |
- if (using_spdy_ || !tunnel_) |
- next_state_ = STATE_DONE; |
- if (next_state_ == STATE_DONE) |
- return OK; |
- |
- DCHECK_EQ(STATE_NONE, next_state_); |
- next_state_ = STATE_GENERATE_AUTH_TOKEN; |
- |
- int rv = DoLoop(OK); |
- if (rv == ERR_IO_PENDING) |
- user_callback_ = callback; |
- return rv; |
-} |
- |
-void HttpProxyClientSocket::Disconnect() { |
- if (transport_.get()) |
- transport_->socket()->Disconnect(); |
- |
- // Reset other states to make sure they aren't mistakenly used later. |
- // These are the states initialized by Connect(). |
- next_state_ = STATE_NONE; |
- user_callback_.Reset(); |
-} |
- |
-bool HttpProxyClientSocket::IsConnected() const { |
- return next_state_ == STATE_DONE && transport_->socket()->IsConnected(); |
-} |
- |
-bool HttpProxyClientSocket::IsConnectedAndIdle() const { |
- return next_state_ == STATE_DONE && |
- transport_->socket()->IsConnectedAndIdle(); |
-} |
- |
-const BoundNetLog& HttpProxyClientSocket::NetLog() const { |
- return net_log_; |
-} |
- |
-void HttpProxyClientSocket::SetSubresourceSpeculation() { |
- if (transport_.get() && transport_->socket()) { |
- transport_->socket()->SetSubresourceSpeculation(); |
- } else { |
- NOTREACHED(); |
- } |
-} |
- |
-void HttpProxyClientSocket::SetOmniboxSpeculation() { |
- if (transport_.get() && transport_->socket()) { |
- transport_->socket()->SetOmniboxSpeculation(); |
- } else { |
- NOTREACHED(); |
- } |
-} |
- |
-bool HttpProxyClientSocket::WasEverUsed() const { |
- if (transport_.get() && transport_->socket()) { |
- return transport_->socket()->WasEverUsed(); |
- } |
- NOTREACHED(); |
- return false; |
-} |
- |
-bool HttpProxyClientSocket::UsingTCPFastOpen() const { |
- if (transport_.get() && transport_->socket()) { |
- return transport_->socket()->UsingTCPFastOpen(); |
- } |
- NOTREACHED(); |
- return false; |
-} |
- |
-bool HttpProxyClientSocket::WasNpnNegotiated() const { |
- if (transport_.get() && transport_->socket()) { |
- return transport_->socket()->WasNpnNegotiated(); |
- } |
- NOTREACHED(); |
- return false; |
-} |
- |
-NextProto HttpProxyClientSocket::GetNegotiatedProtocol() const { |
- if (transport_.get() && transport_->socket()) { |
- return transport_->socket()->GetNegotiatedProtocol(); |
- } |
- NOTREACHED(); |
- return kProtoUnknown; |
-} |
- |
-bool HttpProxyClientSocket::GetSSLInfo(SSLInfo* ssl_info) { |
- if (transport_.get() && transport_->socket()) { |
- return transport_->socket()->GetSSLInfo(ssl_info); |
- } |
- NOTREACHED(); |
- return false; |
-} |
- |
-int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len, |
- const CompletionCallback& callback) { |
- DCHECK(user_callback_.is_null()); |
- if (next_state_ != STATE_DONE) { |
- // We're trying to read the body of the response but we're still trying |
- // to establish an SSL tunnel through the proxy. We can't read these |
- // bytes when establishing a tunnel because they might be controlled by |
- // an active network attacker. We don't worry about this for HTTP |
- // because an active network attacker can already control HTTP sessions. |
- // We reach this case when the user cancels a 407 proxy auth prompt. |
- // See http://crbug.com/8473. |
- DCHECK_EQ(407, response_.headers->response_code()); |
- LogBlockedTunnelResponse(); |
- |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- } |
- |
- return transport_->socket()->Read(buf, buf_len, callback); |
-} |
- |
-int HttpProxyClientSocket::Write(IOBuffer* buf, int buf_len, |
- const CompletionCallback& callback) { |
- DCHECK_EQ(STATE_DONE, next_state_); |
- DCHECK(user_callback_.is_null()); |
- |
- return transport_->socket()->Write(buf, buf_len, callback); |
-} |
- |
-int HttpProxyClientSocket::SetReceiveBufferSize(int32 size) { |
- return transport_->socket()->SetReceiveBufferSize(size); |
-} |
- |
-int HttpProxyClientSocket::SetSendBufferSize(int32 size) { |
- return transport_->socket()->SetSendBufferSize(size); |
-} |
- |
-int HttpProxyClientSocket::GetPeerAddress(IPEndPoint* address) const { |
- return transport_->socket()->GetPeerAddress(address); |
-} |
- |
-int HttpProxyClientSocket::GetLocalAddress(IPEndPoint* address) const { |
- return transport_->socket()->GetLocalAddress(address); |
-} |
- |
-int HttpProxyClientSocket::PrepareForAuthRestart() { |
- if (!response_.headers.get()) |
- return ERR_CONNECTION_RESET; |
- |
- bool keep_alive = false; |
- if (response_.headers->IsKeepAlive() && |
- http_stream_parser_->CanFindEndOfResponse()) { |
- if (!http_stream_parser_->IsResponseBodyComplete()) { |
- next_state_ = STATE_DRAIN_BODY; |
- drain_buf_ = new IOBuffer(kDrainBodyBufferSize); |
- return OK; |
- } |
- keep_alive = true; |
- } |
- |
- // We don't need to drain the response body, so we act as if we had drained |
- // the response body. |
- return DidDrainBodyForAuthRestart(keep_alive); |
-} |
- |
-int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) { |
- if (keep_alive && transport_->socket()->IsConnectedAndIdle()) { |
- next_state_ = STATE_GENERATE_AUTH_TOKEN; |
- transport_->set_reuse_type(ClientSocketHandle::REUSED_IDLE); |
- } else { |
- // This assumes that the underlying transport socket is a TCP socket, |
- // since only TCP sockets are restartable. |
- next_state_ = STATE_TCP_RESTART; |
- transport_->socket()->Disconnect(); |
- } |
- |
- // Reset the other member variables. |
- drain_buf_ = NULL; |
- parser_buf_ = NULL; |
- http_stream_parser_.reset(); |
- request_line_.clear(); |
- request_headers_.Clear(); |
- response_ = HttpResponseInfo(); |
- return OK; |
-} |
- |
-void HttpProxyClientSocket::LogBlockedTunnelResponse() const { |
- ProxyClientSocket::LogBlockedTunnelResponse( |
- response_.headers->response_code(), |
- request_.url, |
- is_https_proxy_); |
-} |
- |
-void HttpProxyClientSocket::DoCallback(int result) { |
- DCHECK_NE(ERR_IO_PENDING, result); |
- DCHECK(!user_callback_.is_null()); |
- |
- // Since Run() may result in Read being called, |
- // clear user_callback_ up front. |
- CompletionCallback c = user_callback_; |
- user_callback_.Reset(); |
- c.Run(result); |
-} |
- |
-void HttpProxyClientSocket::OnIOComplete(int result) { |
- DCHECK_NE(STATE_NONE, next_state_); |
- DCHECK_NE(STATE_DONE, next_state_); |
- int rv = DoLoop(result); |
- if (rv != ERR_IO_PENDING) |
- DoCallback(rv); |
-} |
- |
-int HttpProxyClientSocket::DoLoop(int last_io_result) { |
- DCHECK_NE(next_state_, STATE_NONE); |
- DCHECK_NE(next_state_, STATE_DONE); |
- int rv = last_io_result; |
- do { |
- State state = next_state_; |
- next_state_ = STATE_NONE; |
- switch (state) { |
- case STATE_GENERATE_AUTH_TOKEN: |
- DCHECK_EQ(OK, rv); |
- rv = DoGenerateAuthToken(); |
- break; |
- case STATE_GENERATE_AUTH_TOKEN_COMPLETE: |
- rv = DoGenerateAuthTokenComplete(rv); |
- break; |
- case STATE_SEND_REQUEST: |
- DCHECK_EQ(OK, rv); |
- net_log_.BeginEvent( |
- NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST); |
- rv = DoSendRequest(); |
- break; |
- case STATE_SEND_REQUEST_COMPLETE: |
- rv = DoSendRequestComplete(rv); |
- net_log_.EndEventWithNetErrorCode( |
- NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv); |
- break; |
- case STATE_READ_HEADERS: |
- DCHECK_EQ(OK, rv); |
- net_log_.BeginEvent( |
- NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS); |
- rv = DoReadHeaders(); |
- break; |
- case STATE_READ_HEADERS_COMPLETE: |
- rv = DoReadHeadersComplete(rv); |
- net_log_.EndEventWithNetErrorCode( |
- NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv); |
- break; |
- case STATE_DRAIN_BODY: |
- DCHECK_EQ(OK, rv); |
- rv = DoDrainBody(); |
- break; |
- case STATE_DRAIN_BODY_COMPLETE: |
- rv = DoDrainBodyComplete(rv); |
- break; |
- case STATE_TCP_RESTART: |
- DCHECK_EQ(OK, rv); |
- rv = DoTCPRestart(); |
- break; |
- case STATE_TCP_RESTART_COMPLETE: |
- rv = DoTCPRestartComplete(rv); |
- break; |
- case STATE_DONE: |
- break; |
- default: |
- NOTREACHED() << "bad state"; |
- rv = ERR_UNEXPECTED; |
- break; |
- } |
- } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE && |
- next_state_ != STATE_DONE); |
- return rv; |
-} |
- |
-int HttpProxyClientSocket::DoGenerateAuthToken() { |
- next_state_ = STATE_GENERATE_AUTH_TOKEN_COMPLETE; |
- return auth_->MaybeGenerateAuthToken(&request_, io_callback_, net_log_); |
-} |
- |
-int HttpProxyClientSocket::DoGenerateAuthTokenComplete(int result) { |
- DCHECK_NE(ERR_IO_PENDING, result); |
- if (result == OK) |
- next_state_ = STATE_SEND_REQUEST; |
- return result; |
-} |
- |
-int HttpProxyClientSocket::DoSendRequest() { |
- next_state_ = STATE_SEND_REQUEST_COMPLETE; |
- |
- // This is constructed lazily (instead of within our Start method), so that |
- // we have proxy info available. |
- if (request_line_.empty()) { |
- DCHECK(request_headers_.IsEmpty()); |
- HttpRequestHeaders authorization_headers; |
- if (auth_->HaveAuth()) |
- auth_->AddAuthorizationHeader(&authorization_headers); |
- if (proxy_delegate_) { |
- proxy_delegate_->OnBeforeTunnelRequest(proxy_server_, |
- &authorization_headers); |
- } |
- BuildTunnelRequest(request_, authorization_headers, endpoint_, |
- &request_line_, &request_headers_); |
- |
- net_log_.AddEvent( |
- NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, |
- base::Bind(&HttpRequestHeaders::NetLogCallback, |
- base::Unretained(&request_headers_), |
- &request_line_)); |
- } |
- |
- parser_buf_ = new GrowableIOBuffer(); |
- http_stream_parser_.reset(new HttpStreamParser( |
- transport_.get(), &request_, parser_buf_.get(), net_log_)); |
- return http_stream_parser_->SendRequest( |
- request_line_, request_headers_, &response_, io_callback_); |
-} |
- |
-int HttpProxyClientSocket::DoSendRequestComplete(int result) { |
- if (result < 0) |
- return result; |
- |
- next_state_ = STATE_READ_HEADERS; |
- return OK; |
-} |
- |
-int HttpProxyClientSocket::DoReadHeaders() { |
- next_state_ = STATE_READ_HEADERS_COMPLETE; |
- return http_stream_parser_->ReadResponseHeaders(io_callback_); |
-} |
- |
-int HttpProxyClientSocket::DoReadHeadersComplete(int result) { |
- if (result < 0) |
- return result; |
- |
- // Require the "HTTP/1.x" status line for SSL CONNECT. |
- if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- |
- net_log_.AddEvent( |
- NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, |
- base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers)); |
- |
- if (proxy_delegate_) { |
- proxy_delegate_->OnTunnelHeadersReceived( |
- HostPortPair::FromURL(request_.url), |
- proxy_server_, |
- *response_.headers); |
- } |
- |
- switch (response_.headers->response_code()) { |
- case 200: // OK |
- if (http_stream_parser_->IsMoreDataBuffered()) |
- // The proxy sent extraneous data after the headers. |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- |
- next_state_ = STATE_DONE; |
- return OK; |
- |
- // We aren't able to CONNECT to the remote host through the proxy. We |
- // need to be very suspicious about the response because an active network |
- // attacker can force us into this state by masquerading as the proxy. |
- // The only safe thing to do here is to fail the connection because our |
- // client is expecting an SSL protected response. |
- // See http://crbug.com/7338. |
- |
- case 302: // Found / Moved Temporarily |
- // Attempt to follow redirects from HTTPS proxies, but only if we can |
- // sanitize the response. This still allows a rogue HTTPS proxy to |
- // redirect an HTTPS site load to a similar-looking site, but no longer |
- // allows it to impersonate the site the user requested. |
- if (!is_https_proxy_ || !SanitizeProxyRedirect(&response_)) { |
- LogBlockedTunnelResponse(); |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- } |
- |
- redirect_has_load_timing_info_ = transport_->GetLoadTimingInfo( |
- http_stream_parser_->IsConnectionReused(), |
- &redirect_load_timing_info_); |
- transport_.reset(); |
- http_stream_parser_.reset(); |
- return ERR_HTTPS_PROXY_TUNNEL_RESPONSE; |
- |
- case 407: // Proxy Authentication Required |
- // We need this status code to allow proxy authentication. Our |
- // authentication code is smart enough to avoid being tricked by an |
- // active network attacker. |
- // The next state is intentionally not set as it should be STATE_NONE; |
- if (!SanitizeProxyAuth(&response_)) { |
- LogBlockedTunnelResponse(); |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- } |
- return HandleProxyAuthChallenge(auth_.get(), &response_, net_log_); |
- |
- default: |
- // Ignore response to avoid letting the proxy impersonate the target |
- // server. (See http://crbug.com/137891.) |
- // We lose something by doing this. We have seen proxy 403, 404, and |
- // 501 response bodies that contain a useful error message. For |
- // example, Squid uses a 404 response to report the DNS error: "The |
- // domain name does not exist." |
- LogBlockedTunnelResponse(); |
- return ERR_TUNNEL_CONNECTION_FAILED; |
- } |
-} |
- |
-int HttpProxyClientSocket::DoDrainBody() { |
- DCHECK(drain_buf_.get()); |
- DCHECK(transport_->is_initialized()); |
- next_state_ = STATE_DRAIN_BODY_COMPLETE; |
- return http_stream_parser_->ReadResponseBody( |
- drain_buf_.get(), kDrainBodyBufferSize, io_callback_); |
-} |
- |
-int HttpProxyClientSocket::DoDrainBodyComplete(int result) { |
- if (result < 0) |
- return result; |
- |
- if (http_stream_parser_->IsResponseBodyComplete()) |
- return DidDrainBodyForAuthRestart(true); |
- |
- // Keep draining. |
- next_state_ = STATE_DRAIN_BODY; |
- return OK; |
-} |
- |
-int HttpProxyClientSocket::DoTCPRestart() { |
- next_state_ = STATE_TCP_RESTART_COMPLETE; |
- return transport_->socket()->Connect( |
- base::Bind(&HttpProxyClientSocket::OnIOComplete, base::Unretained(this))); |
-} |
- |
-int HttpProxyClientSocket::DoTCPRestartComplete(int result) { |
- if (result != OK) |
- return result; |
- |
- next_state_ = STATE_GENERATE_AUTH_TOKEN; |
- return result; |
-} |
- |
-} // namespace net |