summaryrefslogtreecommitdiffstats
path: root/net/http/http_network_transaction.cc
diff options
context:
space:
mode:
Diffstat (limited to 'net/http/http_network_transaction.cc')
-rw-r--r--net/http/http_network_transaction.cc338
1 files changed, 83 insertions, 255 deletions
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);