Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(132)

Unified Diff: net/http/http_network_transaction.cc

Issue 2101014: Eliminate the establishing_tunnel_ internal state and move to explicit... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « net/http/http_network_transaction.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/http/http_network_transaction.cc
===================================================================
--- net/http/http_network_transaction.cc (revision 48060)
+++ net/http/http_network_transaction.cc (working copy)
@@ -292,7 +292,6 @@
headers_valid_(false),
logged_response_time(false),
using_ssl_(false),
- establishing_tunnel_(false),
using_spdy_(false),
alternate_protocol_mode_(
g_use_alternate_protocols ? kUnspecified :
@@ -480,7 +479,11 @@
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.
- next_state_ = STATE_SEND_REQUEST;
+ if (using_ssl_ && proxy_info_.is_http() &&
+ ssl_connect_start_time_.is_null())
eroman 2010/05/26 03:21:27 Using the time variable feels a bit hockey.
+ next_state_ = STATE_TUNNEL_SEND_REQUEST;
+ else
+ next_state_ = STATE_SEND_REQUEST;
connection_->set_is_reused(true);
reused_socket_ = true;
} else {
@@ -507,25 +510,25 @@
next_state = STATE_SPDY_READ_BODY;
} else {
DCHECK(!spdy_stream_.get());
- scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
- DCHECK(headers.get());
next_state = STATE_READ_BODY;
if (!connection_->is_initialized())
return 0; // connection_->has been reset. Treat like EOF.
+ }
- 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
- // 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, headers->response_code());
- LogBlockedTunnelResponse(headers->response_code());
- return ERR_TUNNEL_CONNECTION_FAILED;
- }
+ scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
+ DCHECK(headers.get());
+ if (headers->response_code() == 407) {
eroman 2010/05/26 03:21:27 Before it was only failing in the case where the U
+ // 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(proxy_info_.is_http());
+ LogBlockedTunnelResponse(headers->response_code());
+ return ERR_TUNNEL_CONNECTION_FAILED;
}
read_buf_ = buf;
@@ -551,6 +554,9 @@
return LOAD_STATE_RESOLVING_PROXY_FOR_URL;
case STATE_INIT_CONNECTION_COMPLETE:
return connection_->GetLoadState();
+ case STATE_TUNNEL_SEND_REQUEST_COMPLETE:
+ case STATE_TUNNEL_READ_HEADERS_COMPLETE:
+ return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
case STATE_SEND_REQUEST_COMPLETE:
return LOAD_STATE_SENDING_REQUEST;
case STATE_READ_HEADERS_COMPLETE:
@@ -620,6 +626,28 @@
case STATE_INIT_CONNECTION_COMPLETE:
rv = DoInitConnectionComplete(rv);
break;
+ case STATE_TUNNEL_SEND_REQUEST:
+ DCHECK_EQ(OK, rv);
+ net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
+ NULL);
eroman 2010/05/26 03:21:27 nit: line these up with the paren of the previous
+ rv = DoTunnelSendRequest();
+ break;
+ case STATE_TUNNEL_SEND_REQUEST_COMPLETE:
+ rv = DoTunnelSendRequestComplete(rv);
+ net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST,
+ NULL);
+ break;
+ case STATE_TUNNEL_READ_HEADERS:
+ 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);
+ break;
case STATE_SSL_CONNECT:
DCHECK_EQ(OK, rv);
rv = DoSSLConnect();
@@ -921,18 +949,137 @@
// Now we have a TCP connected socket. Perform other connection setup as
// needed.
UpdateConnectionTypeHistograms(CONNECTION_HTTP);
- if (using_ssl_ && (proxy_info_.is_direct() || proxy_info_.is_socks())) {
- next_state_ = STATE_SSL_CONNECT;
+ if (using_ssl_) {
+ if (proxy_info_.is_direct() || proxy_info_.is_socks())
+ next_state_ = STATE_SSL_CONNECT;
+ else
+ next_state_ = STATE_TUNNEL_SEND_REQUEST;
} else {
next_state_ = STATE_SEND_REQUEST;
- if (using_ssl_)
- establishing_tunnel_ = true;
}
}
return OK;
}
+void HttpNetworkTransaction::ClearTunnelState() {
+ http_stream_.reset();
+ request_headers_.clear();
+ response_ = HttpResponseInfo();
+ headers_valid_ = false;
+}
+
+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()) {
+ // Figure out if we can/should add Proxy-Authentication headers.
+ bool have_proxy_auth =
+ HaveAuth(HttpAuth::AUTH_PROXY) ||
+ SelectPreemptiveAuth(HttpAuth::AUTH_PROXY);
+
+ std::string request_line;
+ HttpRequestHeaders request_headers;
+ HttpRequestHeaders authorization_headers;
+
+ // TODO(wtc): If BuildAuthorizationHeader fails (returns an authorization
+ // header with no credentials), we should return an error to prevent
+ // entering an infinite auth restart loop. See http://crbug.com/21050.
+ if (have_proxy_auth)
+ AddAuthorizationHeader(HttpAuth::AUTH_PROXY, &authorization_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;
eroman 2010/05/26 03:21:27 No call to ClearTunnelState() in this case?
+
+ next_state_ = STATE_TUNNEL_READ_HEADERS;
+ return OK;
+}
+
+int HttpNetworkTransaction::DoTunnelReadHeaders() {
+ next_state_ = STATE_TUNNEL_READ_HEADERS_COMPLETE;
+
+ return http_stream_->ReadResponseHeaders(&io_callback_);
+}
+
+int HttpNetworkTransaction::DoTunnelReadHeadersComplete(int result) {
+ if (result < 0) {
+ if (result == ERR_CONNECTION_CLOSED)
+ result = ERR_TUNNEL_CONNECTION_FAILED;
+ ClearTunnelState();
eroman 2010/05/26 03:21:27 The repeated calls to ClearTunnelState() seems pre
+ return result;
+ }
+
+ // Require the "HTTP/1.x" status line for SSL CONNECT.
+ if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) {
+ ClearTunnelState();
+ return ERR_TUNNEL_CONNECTION_FAILED;
+ }
+
+ if (net_log_.HasListener()) {
+ net_log_.AddEvent(
+ NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
+ new NetLogHttpResponseParameter(response_.headers));
+ }
+
+ 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;
+
+ // 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);
+
+ 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;
+}
+
int HttpNetworkTransaction::DoSSLConnect() {
next_state_ = STATE_SSL_CONNECT_COMPLETE;
@@ -1031,7 +1178,7 @@
next_state_ = STATE_SEND_REQUEST_COMPLETE;
UploadDataStream* request_body = NULL;
- if (!establishing_tunnel_ && request_->upload_data) {
+ if (request_->upload_data) {
int error_code;
request_body = UploadDataStream::Create(request_->upload_data, &error_code);
if (!request_body)
@@ -1064,27 +1211,15 @@
if (have_server_auth)
AddAuthorizationHeader(HttpAuth::AUTH_SERVER, &authorization_headers);
- if (establishing_tunnel_) {
- 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));
- }
- } else {
- BuildRequestHeaders(request_, authorization_headers, request_body,
- !using_ssl_ && proxy_info_.is_http(), &request_line,
- &request_headers);
- if (net_log_.HasListener()) {
- net_log_.AddEvent(
- NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS,
- new NetLogHttpRequestParameter(
- request_line, request_headers));
- }
+ BuildRequestHeaders(request_, authorization_headers, request_body,
+ !using_ssl_ && proxy_info_.is_http(), &request_line,
+ &request_headers);
+ if (net_log_.HasListener()) {
+ net_log_.AddEvent(
+ NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS,
+ new NetLogHttpRequestParameter(
+ request_line, request_headers));
}
-
request_headers_ = request_line + request_headers.ToString();
}
@@ -1111,11 +1246,6 @@
}
int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() {
- if (establishing_tunnel_) {
- // The connection was closed before the tunnel could be established.
- return ERR_TUNNEL_CONNECTION_FAILED;
- }
-
if (!response_.headers) {
// The connection was closed before any data was sent. Likely an error
// rather than empty HTTP/0.9 response.
@@ -1184,22 +1314,12 @@
}
if (net_log_.HasListener()) {
- if (establishing_tunnel_) {
- net_log_.AddEvent(
- NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS,
- new NetLogHttpResponseParameter(response_.headers));
- } else {
- net_log_.AddEvent(
- NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS,
- new NetLogHttpResponseParameter(response_.headers));
- }
+ net_log_.AddEvent(
+ NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS,
+ new NetLogHttpResponseParameter(response_.headers));
}
if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) {
- // Require the "HTTP/1.x" status line for SSL CONNECT.
- if (establishing_tunnel_)
- return ERR_TUNNEL_CONNECTION_FAILED;
-
// HTTP/0.9 doesn't support the PUT method, so lack of response headers
// indicates a buggy server. See:
// https://bugzilla.mozilla.org/show_bug.cgi?id=193921
@@ -1207,46 +1327,6 @@
return ERR_METHOD_NOT_SUPPORTED;
}
- if (establishing_tunnel_) {
- 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_SSL_CONNECT;
- // Reset for the real request and response headers.
- request_headers_.clear();
- http_stream_.reset(NULL);
- headers_valid_ = false;
- establishing_tunnel_ = false;
- // TODO(mbelshe): We should put in a test case to trip this code path.
- response_ = HttpResponseInfo();
- 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.
- break;
- 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;
- }
- }
-
// Check for an intermediate 100 Continue response. An origin server is
// allowed to send this response even if we didn't ask for it, so we just
// need to skip over it.
@@ -1262,11 +1342,11 @@
endpoint_,
session_->mutable_alternate_protocols());
- int rv = HandleAuthChallenge();
+ int rv = HandleAuthChallenge(false);
if (rv != OK)
return rv;
- if (using_ssl_ && !establishing_tunnel_) {
+ if (using_ssl_) {
SSLClientSocket* ssl_socket =
reinterpret_cast<SSLClientSocket*>(connection_->socket());
ssl_socket->GetSSLInfo(&response_.ssl_info);
@@ -1305,9 +1385,6 @@
int HttpNetworkTransaction::DoReadBodyComplete(int result) {
// We are done with the Read call.
- DCHECK(!establishing_tunnel_) <<
- "We should never read a response body of a tunnel.";
-
bool done = false, keep_alive = false;
if (result <= 0)
done = true;
@@ -1721,8 +1798,7 @@
// NOTE: we resend a request only if we reused a keep-alive connection.
// This automatically prevents an infinite resend loop because we'll run
// out of the cached keep-alive connections eventually.
- if (establishing_tunnel_ ||
- !connection_->ShouldResendFailedRequest(error) ||
+ if (!connection_->ShouldResendFailedRequest(error) ||
GetResponseHeaders()) { // We have received some response headers.
return false;
}
@@ -1801,12 +1877,11 @@
}
bool HttpNetworkTransaction::ShouldApplyProxyAuth() const {
- return (!using_ssl_ && proxy_info_.is_http()) || establishing_tunnel_;
+ return !using_ssl_ && proxy_info_.is_http();
}
bool HttpNetworkTransaction::ShouldApplyServerAuth() const {
- return !establishing_tunnel_ &&
- !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA);
+ return !(request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA);
}
void HttpNetworkTransaction::AddAuthorizationHeader(
@@ -2007,7 +2082,7 @@
return msg;
}
-int HttpNetworkTransaction::HandleAuthChallenge() {
+int HttpNetworkTransaction::HandleAuthChallenge(bool establishing_tunnel) {
scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders();
DCHECK(headers);
@@ -2050,7 +2125,7 @@
}
if (!auth_handler_[target]) {
- if (establishing_tunnel_) {
+ if (establishing_tunnel) {
LOG(ERROR) << "Can't perform auth to the " << AuthTargetString(target)
<< " " << auth_origin << " when establishing a tunnel"
<< AuthChallengeLogMessage();
« no previous file with comments | « net/http/http_network_transaction.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698