| 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
|
|
|