summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/base/net_error_list.h11
-rw-r--r--net/http/http_auth_controller.cc7
-rw-r--r--net/http/http_auth_controller.h7
-rw-r--r--net/http/http_net_log_params.h84
-rw-r--r--net/http/http_network_transaction.cc338
-rw-r--r--net/http/http_network_transaction.h29
-rw-r--r--net/http/http_network_transaction_unittest.cc14
-rw-r--r--net/http/http_proxy_client_socket.cc421
-rw-r--r--net/http/http_proxy_client_socket.h146
-rw-r--r--net/net.gyp3
-rw-r--r--net/socket_stream/socket_stream.cc4
11 files changed, 780 insertions, 284 deletions
diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h
index e138ffe..d834fa5 100644
--- a/net/base/net_error_list.h
+++ b/net/base/net_error_list.h
@@ -111,8 +111,9 @@ NET_ERROR(SSL_VERSION_OR_CIPHER_MISMATCH, -113)
// The server requested a renegotiation (rehandshake).
NET_ERROR(SSL_RENEGOTIATION_REQUESTED, -114)
-// The proxy requested authentication (for tunnel establishment).
-NET_ERROR(PROXY_AUTH_REQUESTED, -115)
+// The proxy requested authentication (for tunnel establishment) with an
+// unsupported method.
+NET_ERROR(PROXY_AUTH_UNSUPPORTED, -115)
// During SSL renegotiation (rehandshake), the server sent a certificate with
// an error.
@@ -156,9 +157,15 @@ NET_ERROR(SSL_DECOMPRESSION_FAILURE_ALERT, -125)
// from servers with buggy DEFLATE support.
NET_ERROR(SSL_BAD_RECORD_MAC_ALERT, -126)
+// The proxy requested authentication (for tunnel establishment).
+NET_ERROR(PROXY_AUTH_REQUESTED, -127)
+
// A known TLS strict server didn't offer the renegotiation extension.
NET_ERROR(SSL_UNSAFE_NEGOTIATION, -128)
+// The socket needs a fresh connection in order to proceed.
+NET_ERROR(RETRY_CONNECTION, -129)
+
// Certificate error codes
//
// The values of certificate error codes must be consecutive.
diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc
index a40b5d9..cb899cb 100644
--- a/net/http/http_auth_controller.cc
+++ b/net/http/http_auth_controller.cc
@@ -126,7 +126,7 @@ void HttpAuthController::AddAuthorizationHeader(
int HttpAuthController::HandleAuthChallenge(
scoped_refptr<HttpResponseHeaders> headers,
- int load_flags,
+ bool do_not_send_server_auth,
bool establishing_tunnel) {
DCHECK(headers);
DCHECK(auth_origin_.is_valid());
@@ -150,8 +150,7 @@ int HttpAuthController::HandleAuthChallenge(
identity_.invalid = true;
- if (target_ != HttpAuth::AUTH_SERVER ||
- !(load_flags & LOAD_DO_NOT_SEND_AUTH_DATA)) {
+ if (target_ != HttpAuth::AUTH_SERVER || !do_not_send_server_auth) {
// Find the best authentication challenge that we support.
HttpAuth::ChooseBestChallenge(session_->http_auth_handler_factory(),
headers, target_, auth_origin_, net_log_,
@@ -169,7 +168,7 @@ int HttpAuthController::HandleAuthChallenge(
// active network attacker could control its contents. Instead, we just
// fail to establish the tunnel.
DCHECK(target_ == HttpAuth::AUTH_PROXY);
- return ERR_PROXY_AUTH_REQUESTED;
+ return ERR_PROXY_AUTH_UNSUPPORTED;
}
// We found no supported challenge -- let the transaction continue
// so we end up displaying the error page.
diff --git a/net/http/http_auth_controller.h b/net/http/http_auth_controller.h
index 6a74dea..016f88e 100644
--- a/net/http/http_auth_controller.h
+++ b/net/http/http_auth_controller.h
@@ -46,7 +46,8 @@ class HttpAuthController {
// |HandleAuthChallenge()| returns OK on success, or a network error code
// otherwise. It may also populate |auth_info_|.
int HandleAuthChallenge(scoped_refptr<HttpResponseHeaders> headers,
- int load_flags, bool establishing_tunnel);
+ bool do_not_send_server_auth,
+ bool establishing_tunnel);
// Store the supplied credentials and prepare to restart the auth.
void ResetAuth(const std::wstring& username, const std::wstring& password);
@@ -63,6 +64,10 @@ class HttpAuthController {
return auth_info_;
}
+ void set_net_log(const BoundNetLog& net_log) {
+ net_log_ = net_log;
+ }
+
private:
// Searches the auth cache for an entry that encompasses the request's path.
// If such an entry is found, updates |identity_| and |handler_| with the
diff --git a/net/http/http_net_log_params.h b/net/http/http_net_log_params.h
new file mode 100644
index 0000000..563c799
--- /dev/null
+++ b/net/http/http_net_log_params.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2010 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.
+
+#ifndef NET_HTTP_HTTP_NET_LOG_PARAMS_H_
+#define NET_HTTP_HTTP_NET_LOG_PARAMS_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "base/values.h"
+#include "net/base/net_log.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
+
+namespace net {
+
+class NetLogHttpRequestParameter : public NetLog::EventParameters {
+ public:
+ NetLogHttpRequestParameter(const std::string& line,
+ const HttpRequestHeaders& headers)
+ : line_(line) {
+ headers_.CopyFrom(headers);
+ }
+
+ Value* ToValue() const {
+ DictionaryValue* dict = new DictionaryValue();
+ dict->SetString(L"line", line_);
+ ListValue* headers = new ListValue();
+ HttpRequestHeaders::Iterator iterator(headers_);
+ while (iterator.GetNext()) {
+ headers->Append(
+ new StringValue(StringPrintf("%s: %s",
+ iterator.name().c_str(),
+ iterator.value().c_str())));
+ }
+ dict->Set(L"headers", headers);
+ return dict;
+ }
+
+ private:
+ ~NetLogHttpRequestParameter() {}
+
+ const std::string line_;
+ HttpRequestHeaders headers_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetLogHttpRequestParameter);
+};
+
+class NetLogHttpResponseParameter : public NetLog::EventParameters {
+ public:
+ explicit NetLogHttpResponseParameter(
+ const scoped_refptr<HttpResponseHeaders>& headers)
+ : headers_(headers) {}
+
+ Value* ToValue() const {
+ DictionaryValue* dict = new DictionaryValue();
+ ListValue* headers = new ListValue();
+ headers->Append(new StringValue(headers_->GetStatusLine()));
+ void* iterator = NULL;
+ std::string name;
+ std::string value;
+ while (headers_->EnumerateHeaderLines(&iterator, &name, &value)) {
+ headers->Append(
+ new StringValue(StringPrintf("%s: %s", name.c_str(), value.c_str())));
+ }
+ dict->Set(L"headers", headers);
+ return dict;
+ }
+
+ private:
+ ~NetLogHttpResponseParameter() {}
+
+ const scoped_refptr<HttpResponseHeaders> headers_;
+
+ DISALLOW_COPY_AND_ASSIGN(NetLogHttpResponseParameter);
+};
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_NET_LOG_PARAMS_H_
+
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 1d58863..c0fe07b 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -12,7 +12,6 @@
#include "base/stats_counters.h"
#include "base/stl_util-inl.h"
#include "base/string_util.h"
-#include "base/values.h"
#include "build/build_config.h"
#include "googleurl/src/gurl.h"
#include "net/base/connection_type_histograms.h"
@@ -29,7 +28,9 @@
#include "net/http/http_auth_handler_factory.h"
#include "net/http/http_basic_stream.h"
#include "net/http/http_chunked_decoder.h"
+#include "net/http/http_net_log_params.h"
#include "net/http/http_network_session.h"
+#include "net/http/http_proxy_client_socket.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
@@ -127,32 +128,6 @@ void BuildRequestHeaders(const HttpRequestInfo* request_info,
request_headers->MergeFrom(stripped_extra_headers);
}
-// The HTTP CONNECT method for establishing a tunnel connection is documented
-// in draft-luotonen-web-proxy-tunneling-01.txt and RFC 2817, Sections 5.2 and
-// 5.3.
-void BuildTunnelRequest(const HttpRequestInfo* request_info,
- const HttpRequestHeaders& authorization_headers,
- const HostPortPair& endpoint,
- std::string* request_line,
- HttpRequestHeaders* request_headers) {
- // RFC 2616 Section 9 says the Host request-header field MUST accompany all
- // HTTP/1.1 requests. Add "Proxy-Connection: keep-alive" for compat with
- // HTTP/1.0 proxies such as Squid (required for NTLM authentication).
- *request_line = StringPrintf(
- "CONNECT %s HTTP/1.1\r\n", endpoint.ToString().c_str());
- request_headers->SetHeader(HttpRequestHeaders::kHost,
- GetHostAndOptionalPort(request_info->url));
- request_headers->SetHeader(HttpRequestHeaders::kProxyConnection,
- "keep-alive");
-
- std::string user_agent;
- if (request_info->extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,
- &user_agent))
- request_headers->SetHeader(HttpRequestHeaders::kUserAgent, user_agent);
-
- request_headers->MergeFrom(authorization_headers);
-}
-
void ProcessAlternateProtocol(const HttpResponseHeaders& headers,
const HostPortPair& http_host_port_pair,
HttpAlternateProtocols* alternate_protocols) {
@@ -208,67 +183,6 @@ void ProcessAlternateProtocol(const HttpResponseHeaders& headers,
host_port, port, HttpAlternateProtocols::NPN_SPDY_1);
}
-class NetLogHttpRequestParameter : public NetLog::EventParameters {
- public:
- NetLogHttpRequestParameter(const std::string& line,
- const HttpRequestHeaders& headers)
- : line_(line) {
- headers_.CopyFrom(headers);
- }
-
- Value* ToValue() const {
- DictionaryValue* dict = new DictionaryValue();
- dict->SetString(L"line", line_);
- ListValue* headers = new ListValue();
- HttpRequestHeaders::Iterator iterator(headers_);
- while (iterator.GetNext()) {
- headers->Append(
- new StringValue(StringPrintf("%s: %s",
- iterator.name().c_str(),
- iterator.value().c_str())));
- }
- dict->Set(L"headers", headers);
- return dict;
- }
-
- private:
- ~NetLogHttpRequestParameter() {}
-
- const std::string line_;
- HttpRequestHeaders headers_;
-
- DISALLOW_COPY_AND_ASSIGN(NetLogHttpRequestParameter);
-};
-
-class NetLogHttpResponseParameter : public NetLog::EventParameters {
- public:
- explicit NetLogHttpResponseParameter(
- const scoped_refptr<HttpResponseHeaders>& headers)
- : headers_(headers) {}
-
- Value* ToValue() const {
- DictionaryValue* dict = new DictionaryValue();
- ListValue* headers = new ListValue();
- headers->Append(new StringValue(headers_->GetStatusLine()));
- void* iterator = NULL;
- std::string name;
- std::string value;
- while (headers_->EnumerateHeaderLines(&iterator, &name, &value)) {
- headers->Append(
- new StringValue(StringPrintf("%s: %s", name.c_str(), value.c_str())));
- }
- dict->Set(L"headers", headers);
- return dict;
- }
-
- private:
- ~NetLogHttpResponseParameter() {}
-
- const scoped_refptr<HttpResponseHeaders> headers_;
-
- DISALLOW_COPY_AND_ASSIGN(NetLogHttpResponseParameter);
-};
-
} // namespace
//-----------------------------------------------------------------------------
@@ -294,7 +208,8 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session)
g_use_alternate_protocols ? kUnspecified :
kDoNotUseAlternateProtocol),
read_buf_len_(0),
- next_state_(STATE_NONE) {
+ next_state_(STATE_NONE),
+ establishing_tunnel_(false) {
session->ssl_config_service()->GetSSLConfig(&ssl_config_);
if (g_next_protos)
ssl_config_.next_protos = *g_next_protos;
@@ -394,9 +309,17 @@ int HttpNetworkTransaction::RestartWithAuth(
}
pending_auth_target_ = HttpAuth::AUTH_NONE;
- auth_controllers_[target]->ResetAuth(username, password);
-
- PrepareForAuthRestart(target);
+ if (target == HttpAuth::AUTH_PROXY && using_ssl_ && proxy_info_.is_http()) {
+ DCHECK(establishing_tunnel_);
+ ResetStateForRestart();
+ tunnel_credentials_.username = username;
+ tunnel_credentials_.password = password;
+ tunnel_credentials_.invalid = false;
+ next_state_ = STATE_TUNNEL_RESTART_WITH_AUTH;
+ } else {
+ auth_controllers_[target]->ResetAuth(username, password);
+ PrepareForAuthRestart(target);
+ }
DCHECK(user_callback_ == NULL);
int rv = DoLoop(OK);
@@ -408,6 +331,7 @@ int HttpNetworkTransaction::RestartWithAuth(
void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) {
DCHECK(HaveAuth(target));
+ DCHECK(!establishing_tunnel_);
bool keep_alive = false;
// Even if the server says the connection is keep-alive, we have to be
// able to find the end of each response in order to reuse the connection.
@@ -430,14 +354,11 @@ void HttpNetworkTransaction::PrepareForAuthRestart(HttpAuth::Target target) {
}
void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) {
+ DCHECK(!establishing_tunnel_);
if (keep_alive && connection_->socket()->IsConnectedAndIdle()) {
// We should call connection_->set_idle_time(), but this doesn't occur
// often enough to be worth the trouble.
- if (using_ssl_ && proxy_info_.is_http() &&
- ssl_connect_start_time_.is_null())
- next_state_ = STATE_TUNNEL_GENERATE_AUTH_TOKEN;
- else
- next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
+ next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
connection_->set_is_reused(true);
reused_socket_ = true;
} else {
@@ -472,7 +393,7 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
DCHECK(headers.get());
- if (headers->response_code() == 407) {
+ if (establishing_tunnel_) {
// 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
@@ -481,7 +402,10 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
// We reach this case when the user cancels a 407 proxy auth prompt.
// See http://crbug.com/8473.
DCHECK(proxy_info_.is_http());
- LogBlockedTunnelResponse(headers->response_code());
+ DCHECK_EQ(headers->response_code(), 407);
+ LOG(WARNING) << "Blocked proxy response with status "
+ << headers->response_code() << " to CONNECT request for "
+ << GetHostAndPort(request_->url) << ".";
return ERR_TUNNEL_CONNECTION_FAILED;
}
@@ -508,9 +432,7 @@ LoadState HttpNetworkTransaction::GetLoadState() const {
return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
case STATE_INIT_CONNECTION_COMPLETE:
return connection_->GetLoadState();
- case STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE:
- case STATE_TUNNEL_SEND_REQUEST_COMPLETE:
- case STATE_TUNNEL_READ_HEADERS_COMPLETE:
+ case STATE_TUNNEL_CONNECT_COMPLETE:
return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
case STATE_SSL_CONNECT_COMPLETE:
return LOAD_STATE_SSL_HANDSHAKE;
@@ -598,34 +520,16 @@ int HttpNetworkTransaction::DoLoop(int result) {
case STATE_INIT_CONNECTION_COMPLETE:
rv = DoInitConnectionComplete(rv);
break;
- case STATE_TUNNEL_GENERATE_AUTH_TOKEN:
- DCHECK_EQ(OK, rv);
- rv = DoTunnelGenerateAuthToken();
- break;
- case STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE:
- rv = DoTunnelGenerateAuthTokenComplete(rv);
- break;
- case STATE_TUNNEL_SEND_REQUEST:
+ case STATE_TUNNEL_CONNECT:
DCHECK_EQ(OK, rv);
- net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
- NULL);
- rv = DoTunnelSendRequest();
+ rv = DoTunnelConnect();
break;
- case STATE_TUNNEL_SEND_REQUEST_COMPLETE:
- rv = DoTunnelSendRequestComplete(rv);
- net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
- NULL);
+ case STATE_TUNNEL_CONNECT_COMPLETE:
+ rv = DoTunnelConnectComplete(rv);
break;
- case STATE_TUNNEL_READ_HEADERS:
+ case STATE_TUNNEL_RESTART_WITH_AUTH:
DCHECK_EQ(OK, rv);
- net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS,
- NULL);
- rv = DoTunnelReadHeaders();
- break;
- case STATE_TUNNEL_READ_HEADERS_COMPLETE:
- rv = DoTunnelReadHeadersComplete(rv);
- net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS,
- NULL);
+ rv = DoTunnelRestartWithAuth();
break;
case STATE_SSL_CONNECT:
DCHECK_EQ(OK, rv);
@@ -948,7 +852,7 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
if (proxy_info_.is_direct() || proxy_info_.is_socks())
next_state_ = STATE_SSL_CONNECT;
else
- next_state_ = STATE_TUNNEL_GENERATE_AUTH_TOKEN;
+ next_state_ = STATE_TUNNEL_CONNECT;
} else {
next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN;
}
@@ -957,126 +861,61 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
return OK;
}
-void HttpNetworkTransaction::ClearTunnelState() {
- http_stream_.reset();
- request_headers_.clear();
- response_ = HttpResponseInfo();
- headers_valid_ = false;
-}
-
-int HttpNetworkTransaction::DoTunnelGenerateAuthToken() {
- next_state_ = STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE;
- return auth_controllers_[HttpAuth::AUTH_PROXY]->MaybeGenerateAuthToken(
- request_, &io_callback_);
-}
+int HttpNetworkTransaction::DoTunnelConnect() {
+ next_state_ = STATE_TUNNEL_CONNECT_COMPLETE;
+ establishing_tunnel_ = true;
-int HttpNetworkTransaction::DoTunnelGenerateAuthTokenComplete(int rv) {
- DCHECK_NE(ERR_IO_PENDING, rv);
- if (rv == OK)
- next_state_ = STATE_TUNNEL_SEND_REQUEST;
- return rv;
-}
-
-int HttpNetworkTransaction::DoTunnelSendRequest() {
- next_state_ = STATE_TUNNEL_SEND_REQUEST_COMPLETE;
-
- // This is constructed lazily (instead of within our Start method), so that
- // we have proxy info available.
- if (request_headers_.empty()) {
- HttpRequestHeaders authorization_headers;
- if (HaveAuth(HttpAuth::AUTH_PROXY))
- auth_controllers_[HttpAuth::AUTH_PROXY]->AddAuthorizationHeader(
- &authorization_headers);
- std::string request_line;
- HttpRequestHeaders request_headers;
- BuildTunnelRequest(request_, authorization_headers, endpoint_,
- &request_line, &request_headers);
- if (net_log_.HasListener()) {
- net_log_.AddEvent(
- NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
- new NetLogHttpRequestParameter(
- request_line, request_headers));
- }
- request_headers_ = request_line + request_headers.ToString();
- }
-
- http_stream_.reset(new HttpBasicStream(connection_.get(), net_log_));
-
- return http_stream_->SendRequest(request_, request_headers_, NULL, &response_,
- &io_callback_);
-}
-
-int HttpNetworkTransaction::DoTunnelSendRequestComplete(int result) {
- if (result < 0)
- return result;
-
- next_state_ = STATE_TUNNEL_READ_HEADERS;
- return OK;
-}
-
-int HttpNetworkTransaction::DoTunnelReadHeaders() {
- next_state_ = STATE_TUNNEL_READ_HEADERS_COMPLETE;
-
- return http_stream_->ReadResponseHeaders(&io_callback_);
+ // Add a tunnel socket on top of our existing transport socket.
+ ClientSocket* socket = connection_->release_socket();
+ ClientSocketHandle* transport_socket_handle = new ClientSocketHandle();
+ transport_socket_handle->set_socket(socket);
+ socket = new HttpProxyClientSocket(transport_socket_handle, request_->url,
+ endpoint_, auth_controllers_[HttpAuth::AUTH_PROXY].release(), true);
+ connection_->set_socket(socket);
+ return connection_->socket()->Connect(&io_callback_);
}
-int HttpNetworkTransaction::DoTunnelReadHeadersComplete(int result) {
- if (result < 0) {
- if (result == ERR_CONNECTION_CLOSED)
- result = ERR_TUNNEL_CONNECTION_FAILED;
- ClearTunnelState();
- return result;
+int HttpNetworkTransaction::DoTunnelConnectComplete(int result) {
+ if (result == OK) {
+ next_state_ = STATE_SSL_CONNECT;
+ establishing_tunnel_ = false;
}
- // Require the "HTTP/1.x" status line for SSL CONNECT.
- if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) {
- ClearTunnelState();
- return ERR_TUNNEL_CONNECTION_FAILED;
- }
+ if (result == ERR_RETRY_CONNECTION) {
+ HttpProxyClientSocket* tunnel_socket =
+ reinterpret_cast<HttpProxyClientSocket*>(connection_->socket());
+ auth_controllers_[HttpAuth::AUTH_PROXY].reset(
+ tunnel_socket->TakeAuthController());
+ next_state_ = STATE_INIT_CONNECTION;
+ connection_->socket()->Disconnect();
+ connection_->Reset();
+ result = OK;
+ } else if (result == ERR_PROXY_AUTH_REQUESTED) {
+ HttpProxyClientSocket* tunnel_socket =
+ reinterpret_cast<HttpProxyClientSocket*>(connection_->socket());
+ const HttpResponseInfo* auth_response = tunnel_socket->GetResponseInfo();
- if (net_log_.HasListener()) {
- net_log_.AddEvent(
- NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
- new NetLogHttpResponseParameter(response_.headers));
+ response_.headers = auth_response->headers;
+ headers_valid_ = true;
+ response_.auth_challenge = auth_response->auth_challenge;
+ pending_auth_target_ = HttpAuth::AUTH_PROXY;
+ result = OK;
}
+ return result;
+}
- int rv = result;
- switch (response_.headers->response_code()) {
- case 200: // OK
- if (http_stream_->IsMoreDataBuffered()) {
- // The proxy sent extraneous data after the headers.
- rv = ERR_TUNNEL_CONNECTION_FAILED;
- } else {
- next_state_ = STATE_SSL_CONNECT;
- rv = OK;
- }
- break;
+int HttpNetworkTransaction::DoTunnelRestartWithAuth() {
+ DCHECK(establishing_tunnel_);
+ DCHECK(!tunnel_credentials_.invalid);
+ next_state_ = STATE_TUNNEL_CONNECT_COMPLETE;
- // 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 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.
- headers_valid_ = true;
- return HandleAuthChallenge(true);
+ HttpProxyClientSocket* tunnel_socket =
+ reinterpret_cast<HttpProxyClientSocket*>(connection_->socket());
+ tunnel_credentials_.invalid = true;
- default:
- // For all other status codes, we conservatively fail the CONNECT
- // request.
- // 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(response_.headers->response_code());
- rv = ERR_TUNNEL_CONNECTION_FAILED;
- }
- ClearTunnelState();
- return rv;
+ return tunnel_socket->RestartWithAuth(tunnel_credentials_.username,
+ tunnel_credentials_.password,
+ &io_callback_);
}
int HttpNetworkTransaction::DoSSLConnect() {
@@ -1253,8 +1092,7 @@ int HttpNetworkTransaction::DoSendRequest() {
if (net_log_.HasListener()) {
net_log_.AddEvent(
NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS,
- new NetLogHttpRequestParameter(
- request_line, request_headers));
+ new NetLogHttpRequestParameter(request_line, request_headers));
}
request_headers_ = request_line + request_headers.ToString();
@@ -1718,13 +1556,6 @@ void HttpNetworkTransaction::LogTransactionMetrics() const {
}
}
-void HttpNetworkTransaction::LogBlockedTunnelResponse(
- int response_code) const {
- LOG(WARNING) << "Blocked proxy response with status " << response_code
- << " to CONNECT request for "
- << GetHostAndPort(request_->url) << ".";
-}
-
int HttpNetworkTransaction::HandleCertificateError(int error) {
DCHECK(using_ssl_);
DCHECK(IsCertificateError(error));
@@ -1976,9 +1807,9 @@ int HttpNetworkTransaction::HandleAuthChallenge(bool establishing_tunnel) {
if (target == HttpAuth::AUTH_PROXY && proxy_info_.is_direct())
return ERR_UNEXPECTED_PROXY_AUTH;
- int rv = auth_controllers_[target]->HandleAuthChallenge(headers,
- request_->load_flags,
- establishing_tunnel);
+ int rv = auth_controllers_[target]->HandleAuthChallenge(
+ headers, (request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA) != 0,
+ establishing_tunnel);
if (auth_controllers_[target]->HaveAuthHandler())
pending_auth_target_ = target;
@@ -2037,12 +1868,9 @@ std::string HttpNetworkTransaction::DescribeState(State state) {
STATE_CASE(STATE_RESOLVE_PROXY_COMPLETE);
STATE_CASE(STATE_INIT_CONNECTION);
STATE_CASE(STATE_INIT_CONNECTION_COMPLETE);
- STATE_CASE(STATE_TUNNEL_GENERATE_AUTH_TOKEN);
- STATE_CASE(STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE);
- STATE_CASE(STATE_TUNNEL_SEND_REQUEST);
- STATE_CASE(STATE_TUNNEL_SEND_REQUEST_COMPLETE);
- STATE_CASE(STATE_TUNNEL_READ_HEADERS);
- STATE_CASE(STATE_TUNNEL_READ_HEADERS_COMPLETE);
+ STATE_CASE(STATE_TUNNEL_CONNECT);
+ STATE_CASE(STATE_TUNNEL_CONNECT_COMPLETE);
+ STATE_CASE(STATE_TUNNEL_RESTART_WITH_AUTH);
STATE_CASE(STATE_SSL_CONNECT);
STATE_CASE(STATE_SSL_CONNECT_COMPLETE);
STATE_CASE(STATE_GENERATE_PROXY_AUTH_TOKEN);
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 2a33663..0c8d029 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -83,12 +83,9 @@ class HttpNetworkTransaction : public HttpTransaction {
STATE_RESOLVE_PROXY_COMPLETE,
STATE_INIT_CONNECTION,
STATE_INIT_CONNECTION_COMPLETE,
- STATE_TUNNEL_GENERATE_AUTH_TOKEN,
- STATE_TUNNEL_GENERATE_AUTH_TOKEN_COMPLETE,
- STATE_TUNNEL_SEND_REQUEST,
- STATE_TUNNEL_SEND_REQUEST_COMPLETE,
- STATE_TUNNEL_READ_HEADERS,
- STATE_TUNNEL_READ_HEADERS_COMPLETE,
+ STATE_TUNNEL_CONNECT,
+ STATE_TUNNEL_CONNECT_COMPLETE,
+ STATE_TUNNEL_RESTART_WITH_AUTH,
STATE_SSL_CONNECT,
STATE_SSL_CONNECT_COMPLETE,
STATE_GENERATE_PROXY_AUTH_TOKEN,
@@ -132,12 +129,9 @@ class HttpNetworkTransaction : public HttpTransaction {
int DoResolveProxyComplete(int result);
int DoInitConnection();
int DoInitConnectionComplete(int result);
- int DoTunnelGenerateAuthToken();
- int DoTunnelGenerateAuthTokenComplete(int result);
- int DoTunnelSendRequest();
- int DoTunnelSendRequestComplete(int result);
- int DoTunnelReadHeaders();
- int DoTunnelReadHeadersComplete(int result);
+ int DoTunnelConnect();
+ int DoTunnelConnectComplete(int result);
+ int DoTunnelRestartWithAuth();
int DoSSLConnect();
int DoSSLConnectComplete(int result);
int DoGenerateProxyAuthToken();
@@ -227,9 +221,6 @@ class HttpNetworkTransaction : public HttpTransaction {
// Resets the members of the transaction so it can be restarted.
void ResetStateForRestart();
- // Clear the state used to setup the tunnel.
- void ClearTunnelState();
-
// Returns true if we should try to add a Proxy-Authorization header
bool ShouldApplyProxyAuth() const;
@@ -327,6 +318,14 @@ class HttpNetworkTransaction : public HttpTransaction {
// specified by the URL, due to Alternate-Protocol or fixed testing ports.
HostPortPair endpoint_;
+ // Stores login and password between |RestartWithAuth|
+ // and |DoTunnelRestartWithAuth|.
+ HttpAuth::Identity tunnel_credentials_;
+
+ // True when the tunnel is in the process of being established - we can't
+ // read from the socket until the tunnel is done.
+ bool establishing_tunnel_;
+
DISALLOW_COPY_AND_ASSIGN(HttpNetworkTransaction);
};
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 945450c..36b3ce2 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -83,7 +83,8 @@ struct SessionDependencies {
proxy_service(ProxyService::CreateNull()),
ssl_config_service(new SSLConfigServiceDefaults),
http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()),
- spdy_session_pool(new SpdySessionPool()) {}
+ spdy_session_pool(new SpdySessionPool()),
+ net_log(NULL) {}
// Custom proxy service dependency.
explicit SessionDependencies(ProxyService* proxy_service)
@@ -91,7 +92,8 @@ struct SessionDependencies {
proxy_service(proxy_service),
ssl_config_service(new SSLConfigServiceDefaults),
http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()),
- spdy_session_pool(new SpdySessionPool()) {}
+ spdy_session_pool(new SpdySessionPool()),
+ net_log(NULL) {}
scoped_refptr<MockHostResolverBase> host_resolver;
scoped_refptr<ProxyService> proxy_service;
@@ -99,6 +101,7 @@ struct SessionDependencies {
MockClientSocketFactory socket_factory;
scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory;
scoped_refptr<SpdySessionPool> spdy_session_pool;
+ NetLog* net_log;
};
ProxyService* CreateFixedProxyService(const std::string& proxy) {
@@ -115,7 +118,7 @@ HttpNetworkSession* CreateSession(SessionDependencies* session_deps) {
session_deps->spdy_session_pool,
session_deps->http_auth_handler_factory.get(),
NULL,
- NULL);
+ session_deps->net_log);
}
class HttpNetworkTransactionTest : public PlatformTest {
@@ -1297,6 +1300,8 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveImpatientServer) {
TEST_F(HttpNetworkTransactionTest, BasicAuthProxyKeepAlive) {
// Configure against proxy server "myproxy:70".
SessionDependencies session_deps(CreateFixedProxyService("myproxy:70"));
+ CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
+ session_deps.net_log = log.bound().net_log();
scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session));
@@ -1345,7 +1350,6 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyKeepAlive) {
TestCompletionCallback callback1;
- CapturingBoundNetLog log(CapturingNetLog::kUnbounded);
int rv = trans->Start(&request, &callback1, log.bound());
EXPECT_EQ(ERR_IO_PENDING, rv);
@@ -1636,7 +1640,7 @@ TEST_F(HttpNetworkTransactionTest, ConnectStatus406) {
TEST_F(HttpNetworkTransactionTest, ConnectStatus407) {
ConnectStatusHelperWithExpectedStatus(
MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"),
- ERR_PROXY_AUTH_REQUESTED);
+ ERR_PROXY_AUTH_UNSUPPORTED);
}
TEST_F(HttpNetworkTransactionTest, ConnectStatus408) {
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
new file mode 100644
index 0000000..cdfa183
--- /dev/null
+++ b/net/http/http_proxy_client_socket.cc
@@ -0,0 +1,421 @@
+// Copyright (c) 2010 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/string_util.h"
+#include "googleurl/src/gurl.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/http/http_basic_stream.h"
+#include "net/http/http_net_log_params.h"
+#include "net/http/http_network_session.h"
+#include "net/http/http_request_info.h"
+#include "net/socket/client_socket_handle.h"
+
+namespace net {
+
+namespace {
+
+// The HTTP CONNECT method for establishing a tunnel connection is documented
+// in draft-luotonen-web-proxy-tunneling-01.txt and RFC 2817, Sections 5.2 and
+// 5.3.
+void BuildTunnelRequest(const HttpRequestInfo* request_info,
+ const HttpRequestHeaders& authorization_headers,
+ const HostPortPair& endpoint,
+ std::string* request_line,
+ HttpRequestHeaders* request_headers) {
+ // RFC 2616 Section 9 says the Host request-header field MUST accompany all
+ // HTTP/1.1 requests. Add "Proxy-Connection: keep-alive" for compat with
+ // HTTP/1.0 proxies such as Squid (required for NTLM authentication).
+ *request_line = StringPrintf(
+ "CONNECT %s HTTP/1.1\r\n", endpoint.ToString().c_str());
+ request_headers->SetHeader(HttpRequestHeaders::kHost,
+ GetHostAndOptionalPort(request_info->url));
+ request_headers->SetHeader(HttpRequestHeaders::kProxyConnection,
+ "keep-alive");
+
+ std::string user_agent;
+ if (request_info->extra_headers.GetHeader(HttpRequestHeaders::kUserAgent,
+ &user_agent))
+ request_headers->SetHeader(HttpRequestHeaders::kUserAgent, user_agent);
+
+ request_headers->MergeFrom(authorization_headers);
+}
+
+} // namespace
+
+HttpProxyClientSocket::HttpProxyClientSocket(
+ ClientSocketHandle* transport_socket, const GURL& request_url,
+ const HostPortPair& endpoint, HttpAuthController* auth, bool tunnel)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(
+ io_callback_(this, &HttpProxyClientSocket::OnIOComplete)),
+ next_state_(STATE_NONE),
+ user_callback_(NULL),
+ transport_(transport_socket),
+ tunnel_(tunnel),
+ auth_(auth),
+ endpoint_(endpoint),
+ net_log_(transport_socket->socket()->NetLog()) {
+ DCHECK_EQ(tunnel, auth != NULL);
+ if (tunnel)
+ auth->set_net_log(net_log_);
+ // Synthesize the bits of a request that we actually use.
+ request_.url = request_url;
+ request_.method = "GET";
+}
+
+HttpProxyClientSocket::~HttpProxyClientSocket() {
+ Disconnect();
+}
+
+int HttpProxyClientSocket::Connect(CompletionCallback* callback) {
+ DCHECK(transport_.get());
+ DCHECK(transport_->socket());
+ DCHECK(transport_->socket()->IsConnected());
+ DCHECK(!user_callback_);
+
+ if (!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;
+}
+
+int HttpProxyClientSocket::RestartWithAuth(const std::wstring& username,
+ const std::wstring& password,
+ CompletionCallback* callback) {
+ DCHECK_EQ(STATE_NONE, next_state_);
+ DCHECK(!user_callback_);
+
+ auth_->ResetAuth(username, password);
+
+ int rv = PrepareForAuthRestart();
+ if (rv != OK)
+ return rv;
+
+ rv = DoLoop(OK);
+ if (rv == ERR_IO_PENDING)
+ user_callback_ = callback;
+ return rv;
+}
+
+int HttpProxyClientSocket::PrepareForAuthRestart() {
+ bool keep_alive = false;
+ if (response_.headers->IsKeepAlive() &&
+ http_stream_->CanFindEndOfResponse()) {
+ if (!http_stream_->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_is_reused(true);
+ } else {
+ transport_->socket()->Disconnect();
+ return ERR_RETRY_CONNECTION;
+ }
+
+ // Reset the other member variables.
+ drain_buf_ = NULL;
+ http_stream_.reset();
+ request_headers_.clear();
+ response_ = HttpResponseInfo();
+ return OK;
+}
+
+void HttpProxyClientSocket::LogBlockedTunnelResponse(int response_code) const {
+ LOG(WARNING) << "Blocked proxy response with status " << response_code
+ << " to CONNECT request for "
+ << GetHostAndPort(request_.url) << ".";
+}
+
+void HttpProxyClientSocket::Disconnect() {
+ 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_ = NULL;
+}
+
+bool HttpProxyClientSocket::IsConnected() const {
+ return next_state_ == STATE_DONE && transport_->socket()->IsConnected();
+}
+
+bool HttpProxyClientSocket::IsConnectedAndIdle() const {
+ return next_state_ == STATE_DONE
+ && transport_->socket()->IsConnectedAndIdle();
+}
+
+int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK(!user_callback_);
+ 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(response_.headers->response_code());
+
+ return ERR_TUNNEL_CONNECTION_FAILED;
+ }
+
+ return transport_->socket()->Read(buf, buf_len, callback);
+}
+
+int HttpProxyClientSocket::Write(IOBuffer* buf, int buf_len,
+ CompletionCallback* callback) {
+ DCHECK_EQ(STATE_DONE, next_state_);
+ DCHECK(!user_callback_);
+
+ return transport_->socket()->Write(buf, buf_len, callback);
+}
+
+bool HttpProxyClientSocket::SetReceiveBufferSize(int32 size) {
+ return transport_->socket()->SetReceiveBufferSize(size);
+}
+
+bool HttpProxyClientSocket::SetSendBufferSize(int32 size) {
+ return transport_->socket()->SetSendBufferSize(size);
+}
+
+int HttpProxyClientSocket::GetPeerAddress(AddressList* address) const {
+ return transport_->socket()->GetPeerAddress(address);
+}
+
+void HttpProxyClientSocket::DoCallback(int result) {
+ DCHECK_NE(ERR_IO_PENDING, result);
+ DCHECK(user_callback_);
+
+ // Since Run() may result in Read being called,
+ // clear user_callback_ up front.
+ CompletionCallback* c = user_callback_;
+ user_callback_ = NULL;
+ 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,
+ NULL);
+ rv = DoSendRequest();
+ break;
+ case STATE_SEND_REQUEST_COMPLETE:
+ rv = DoSendRequestComplete(rv);
+ net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
+ NULL);
+ break;
+ case STATE_READ_HEADERS:
+ DCHECK_EQ(OK, rv);
+ net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS,
+ NULL);
+ rv = DoReadHeaders();
+ break;
+ case STATE_READ_HEADERS_COMPLETE:
+ rv = DoReadHeadersComplete(rv);
+ net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS,
+ NULL);
+ break;
+ case STATE_DRAIN_BODY:
+ DCHECK_EQ(OK, rv);
+ rv = DoDrainBody();
+ break;
+ case STATE_DRAIN_BODY_COMPLETE:
+ rv = DoDrainBodyComplete(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_);
+}
+
+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_headers_.empty()) {
+ HttpRequestHeaders authorization_headers;
+ if (auth_->HaveAuth())
+ auth_->AddAuthorizationHeader(&authorization_headers);
+ std::string request_line;
+ HttpRequestHeaders request_headers;
+ BuildTunnelRequest(&request_, authorization_headers, endpoint_,
+ &request_line, &request_headers);
+ if (net_log_.HasListener()) {
+ net_log_.AddEvent(
+ NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS,
+ new NetLogHttpRequestParameter(
+ request_line, request_headers));
+ }
+ request_headers_ = request_line + request_headers.ToString();
+ }
+
+ http_stream_.reset(new HttpBasicStream(transport_.get(), net_log_));
+
+ return http_stream_->SendRequest(&request_, request_headers_, NULL,
+ &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_->ReadResponseHeaders(&io_callback_);
+}
+
+int HttpProxyClientSocket::DoReadHeadersComplete(int result) {
+ if (result < 0) {
+ if (result == ERR_CONNECTION_CLOSED)
+ result = ERR_TUNNEL_CONNECTION_FAILED;
+ return result;
+ }
+
+ // Require the "HTTP/1.x" status line for SSL CONNECT.
+ if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0))
+ return ERR_TUNNEL_CONNECTION_FAILED;
+
+ if (net_log_.HasListener()) {
+ net_log_.AddEvent(
+ NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
+ new NetLogHttpResponseParameter(response_.headers));
+ }
+
+ switch (response_.headers->response_code()) {
+ case 200: // OK
+ if (http_stream_->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 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;
+ return HandleAuthChallenge();
+
+ default:
+ // For all other status codes, we conservatively fail the CONNECT
+ // request.
+ // 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(response_.headers->response_code());
+ return ERR_TUNNEL_CONNECTION_FAILED;
+ }
+}
+
+int HttpProxyClientSocket::DoDrainBody() {
+ DCHECK(drain_buf_);
+ DCHECK(transport_->is_initialized());
+ next_state_ = STATE_DRAIN_BODY_COMPLETE;
+ return http_stream_->ReadResponseBody(drain_buf_, kDrainBodyBufferSize,
+ &io_callback_);
+}
+
+int HttpProxyClientSocket::DoDrainBodyComplete(int result) {
+ if (result < 0)
+ return result;
+
+ if (http_stream_->IsResponseBodyComplete())
+ return DidDrainBodyForAuthRestart(true);
+
+ // Keep draining.
+ next_state_ = STATE_DRAIN_BODY;
+ return OK;
+}
+
+int HttpProxyClientSocket::HandleAuthChallenge() {
+ DCHECK(response_.headers);
+
+ int rv = auth_->HandleAuthChallenge(response_.headers, false, true);
+ response_.auth_challenge = auth_->auth_info();
+ if (rv == OK)
+ return ERR_PROXY_AUTH_REQUESTED;
+
+ return rv;
+}
+
+} // namespace net
diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h
new file mode 100644
index 0000000..6ced031
--- /dev/null
+++ b/net/http/http_proxy_client_socket.h
@@ -0,0 +1,146 @@
+// Copyright (c) 2010 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.
+
+#ifndef NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_H_
+#define NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/ref_counted.h"
+#include "net/base/completion_callback.h"
+#include "net/base/host_port_pair.h"
+#include "net/base/net_log.h"
+#include "net/http/http_auth_controller.h"
+#include "net/http/http_request_headers.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_response_info.h"
+#include "net/socket/client_socket.h"
+
+class GURL;
+
+namespace net {
+
+class AddressList;
+class ClientSocketHandle;
+class HttpStream;
+class IOBuffer;;
+
+class HttpProxyClientSocket : public ClientSocket {
+ public:
+ // Takes ownership of |auth| and the |transport_socket|, which should
+ // already be connected by the time Connect() is called. If tunnel is true
+ // then on Connect() this socket will establish an Http tunnel.
+ HttpProxyClientSocket(ClientSocketHandle* transport_socket,
+ const GURL& request_url, const HostPortPair& endpoint,
+ HttpAuthController* auth, bool tunnel);
+
+ // On destruction Disconnect() is called.
+ virtual ~HttpProxyClientSocket();
+
+ // If Connect (or its callback) returns PROXY_AUTH_REQUESTED, then
+ // credentials can be provided by calling RestartWithAuth.
+ int RestartWithAuth(const std::wstring& username,
+ const std::wstring& password,
+ CompletionCallback* callback);
+
+ const HttpResponseInfo* GetResponseInfo() const {
+ return response_.headers ? &response_ : NULL;
+ }
+
+ HttpAuthController* TakeAuthController() {
+ return auth_.release();
+ }
+
+ // ClientSocket methods:
+
+ // Authenticates to the Http Proxy and then passes data freely.
+ virtual int Connect(CompletionCallback* callback);
+ virtual void Disconnect();
+ virtual bool IsConnected() const;
+ virtual bool IsConnectedAndIdle() const;
+ virtual const BoundNetLog& NetLog() const { return net_log_; }
+
+ // Socket methods:
+ virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback);
+ virtual int Write(IOBuffer* buf, int buf_len, CompletionCallback* callback);
+
+ virtual bool SetReceiveBufferSize(int32 size);
+ virtual bool SetSendBufferSize(int32 size);
+
+ virtual int GetPeerAddress(AddressList* address) const;
+
+ private:
+ enum State {
+ STATE_NONE,
+ STATE_GENERATE_AUTH_TOKEN,
+ STATE_GENERATE_AUTH_TOKEN_COMPLETE,
+ STATE_SEND_REQUEST,
+ STATE_SEND_REQUEST_COMPLETE,
+ STATE_READ_HEADERS,
+ STATE_READ_HEADERS_COMPLETE,
+ STATE_RESOLVE_CANONICAL_NAME,
+ STATE_RESOLVE_CANONICAL_NAME_COMPLETE,
+ STATE_DRAIN_BODY,
+ STATE_DRAIN_BODY_COMPLETE,
+ STATE_DONE,
+ };
+
+ // The size in bytes of the buffer we use to drain the response body that
+ // we want to throw away. The response body is typically a small error
+ // page just a few hundred bytes long.
+ enum { kDrainBodyBufferSize = 1024 };
+
+ int PrepareForAuthRestart();
+ int DidDrainBodyForAuthRestart(bool keep_alive);
+
+ int HandleAuthChallenge();
+
+ void LogBlockedTunnelResponse(int response_code) const;
+
+ void DoCallback(int result);
+ void OnIOComplete(int result);
+
+ int DoLoop(int last_io_result);
+ int DoGenerateAuthToken();
+ int DoGenerateAuthTokenComplete(int result);
+ int DoSendRequest();
+ int DoSendRequestComplete(int result);
+ int DoReadHeaders();
+ int DoReadHeadersComplete(int result);
+ int DoDrainBody();
+ int DoDrainBodyComplete(int result);
+
+ CompletionCallbackImpl<HttpProxyClientSocket> io_callback_;
+ State next_state_;
+
+ // Stores the callback to the layer above, called on completing Connect().
+ CompletionCallback* user_callback_;
+
+ // Stores the underlying socket.
+ scoped_ptr<ClientSocketHandle> transport_;
+
+ bool tunnel_;
+
+ std::string request_headers_;
+
+ scoped_ptr<HttpStream> http_stream_;
+ HttpRequestInfo request_;
+ HttpResponseInfo response_;
+ scoped_ptr<HttpAuthController> auth_;
+
+ // The hostname and port of the endpoint. This is not necessarily the one
+ // specified by the URL, due to Alternate-Protocol or fixed testing ports.
+ HostPortPair endpoint_;
+
+ scoped_refptr<IOBuffer> drain_buf_;
+
+ BoundNetLog net_log_;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpProxyClientSocket);
+};
+
+} // namespace net
+
+#endif // NET_HTTP_HTTP_PROXY_CLIENT_SOCKET_H_
diff --git a/net/net.gyp b/net/net.gyp
index b7c2de74..3fc102a 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -359,6 +359,7 @@
'http/http_cache_transaction.h',
'http/http_chunked_decoder.cc',
'http/http_chunked_decoder.h',
+ 'http/http_net_log_params.h',
'http/http_network_delegate.h',
'http/http_network_layer.cc',
'http/http_network_layer.h',
@@ -382,6 +383,8 @@
'http/url_security_manager.cc',
'http/url_security_manager_posix.cc',
'http/url_security_manager_win.cc',
+ 'http/http_proxy_client_socket.cc',
+ 'http/http_proxy_client_socket.h',
'http/http_util.cc',
'http/http_util_icu.cc',
'http/http_util.h',
diff --git a/net/socket_stream/socket_stream.cc b/net/socket_stream/socket_stream.cc
index d38242c..2bf39be 100644
--- a/net/socket_stream/socket_stream.cc
+++ b/net/socket_stream/socket_stream.cc
@@ -706,7 +706,7 @@ int SocketStream::DoReadTunnelHeadersComplete(int result) {
return OK;
case 407: // Proxy Authentication Required.
result = HandleAuthChallenge(headers.get());
- if (result == ERR_PROXY_AUTH_REQUESTED &&
+ if (result == ERR_PROXY_AUTH_UNSUPPORTED &&
auth_handler_.get() && delegate_) {
DCHECK(!proxy_info_.is_empty());
auth_info_ = new AuthChallengeInfo;
@@ -924,7 +924,7 @@ int SocketStream::HandleAuthChallenge(const HttpResponseHeaders* headers) {
auth_identity_.password = entry->password();
// Restart with auth info.
}
- return ERR_PROXY_AUTH_REQUESTED;
+ return ERR_PROXY_AUTH_UNSUPPORTED;
} else {
auth_identity_.invalid = false;
}