summaryrefslogtreecommitdiffstats
path: root/net/http
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-17 03:58:29 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-17 03:58:29 +0000
commit511f6f525dacf2512439b12784186074a0f6cd5c (patch)
tree5fa90bbe0fa255de3d78d00dcffd333830e13aea /net/http
parent8aad42dcc8d683242c730901c51061de710046b9 (diff)
downloadchromium_src-511f6f525dacf2512439b12784186074a0f6cd5c.zip
chromium_src-511f6f525dacf2512439b12784186074a0f6cd5c.tar.gz
chromium_src-511f6f525dacf2512439b12784186074a0f6cd5c.tar.bz2
Allow a non-200 (or non-407) response for a CONNECT request from an HTTPS proxy
to be consumed by chrome. Among other things, this will allow the proxy to inform the user that the hostname could not be resolved or similar conditions. This adds a new OnHttpsProxyTunnelConnectionResponse method to StreamRequest::Delegate which is invoked when an HTTPS proxy returns a non-200, non-407 response. The method is called with an HttpResponseInfor argument to access the request headers, and an HttpStream argument to access the response body. BUG=none TEST=HttpNetworkTransactionTest Review URL: http://codereview.chromium.org/4935001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@69513 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/http')
-rw-r--r--net/http/http_basic_stream.cc6
-rw-r--r--net/http/http_basic_stream.h8
-rw-r--r--net/http/http_network_transaction.cc23
-rw-r--r--net/http/http_network_transaction.h2
-rw-r--r--net/http/http_network_transaction_unittest.cc237
-rw-r--r--net/http/http_proxy_client_socket.cc16
-rw-r--r--net/http/http_proxy_client_socket.h15
-rw-r--r--net/http/http_proxy_client_socket_pool.cc7
-rw-r--r--net/http/http_proxy_client_socket_pool.h3
-rw-r--r--net/http/http_proxy_client_socket_pool_unittest.cc15
-rw-r--r--net/http/http_stream_request.cc40
-rw-r--r--net/http/http_stream_request.h2
-rw-r--r--net/http/proxy_client_socket.h35
-rw-r--r--net/http/stream_factory.h7
14 files changed, 389 insertions, 27 deletions
diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc
index 64352c4..061bb30 100644
--- a/net/http/http_basic_stream.cc
+++ b/net/http/http_basic_stream.cc
@@ -16,8 +16,10 @@
namespace net {
HttpBasicStream::HttpBasicStream(ClientSocketHandle* connection,
+ HttpStreamParser* parser,
bool using_proxy)
: read_buf_(new GrowableIOBuffer()),
+ parser_(parser),
connection_(connection),
using_proxy_(using_proxy),
request_info_(NULL) {
@@ -26,6 +28,7 @@ HttpBasicStream::HttpBasicStream(ClientSocketHandle* connection,
int HttpBasicStream::InitializeStream(const HttpRequestInfo* request_info,
const BoundNetLog& net_log,
CompletionCallback* callback) {
+ DCHECK(!parser_.get());
request_info_ = request_info;
parser_.reset(new HttpStreamParser(connection_.get(), request_info,
read_buf_, net_log));
@@ -38,6 +41,7 @@ int HttpBasicStream::SendRequest(const HttpRequestHeaders& headers,
HttpResponseInfo* response,
CompletionCallback* callback) {
DCHECK(parser_.get());
+ DCHECK(request_info_);
const std::string path = using_proxy_ ?
HttpUtil::SpecForRequest(request_info_->url) :
HttpUtil::PathForRequest(request_info_->url);
@@ -75,7 +79,7 @@ HttpStream* HttpBasicStream::RenewStreamForAuth() {
DCHECK(IsResponseBodyComplete());
DCHECK(!IsMoreDataBuffered());
parser_.reset();
- return new HttpBasicStream(connection_.release(), using_proxy_);
+ return new HttpBasicStream(connection_.release(), NULL, using_proxy_);
}
bool HttpBasicStream::IsResponseBodyComplete() const {
diff --git a/net/http/http_basic_stream.h b/net/http/http_basic_stream.h
index 83136c0..918596a 100644
--- a/net/http/http_basic_stream.h
+++ b/net/http/http_basic_stream.h
@@ -30,7 +30,13 @@ class UploadDataStream;
class HttpBasicStream : public HttpStream {
public:
- HttpBasicStream(ClientSocketHandle* connection, bool using_proxy);
+ // Constructs a new HttpBasicStream. If |parser| is NULL, then
+ // InitializeStream should be called to initialize it correctly. If
+ // |parser| is non-null, then InitializeStream should not be called,
+ // as the stream is already initialized.
+ HttpBasicStream(ClientSocketHandle* connection,
+ HttpStreamParser* parser,
+ bool using_proxy);
virtual ~HttpBasicStream();
// HttpStream methods:
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index c84dfa4..64a4fa7 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -285,11 +285,13 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
scoped_refptr<HttpResponseHeaders> headers(GetResponseHeaders());
if (headers_valid_ && headers.get() && stream_request_.get()) {
// 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
+ // to establish an SSL tunnel through an HTTP 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.
+ // We reach this case when the user cancels a 407 proxy auth prompt. We
+ // also don't worry about this for an HTTPS Proxy, because the
+ // communication with the proxy is secure.
// See http://crbug.com/8473.
DCHECK(proxy_info_.is_http() || proxy_info_.is_https());
DCHECK_EQ(headers->response_code(), 407);
@@ -301,7 +303,6 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len,
// Are we using SPDY or HTTP?
next_state = STATE_READ_BODY;
- DCHECK(stream_->GetResponseInfo()->headers);
read_buf_ = buf;
read_buf_len_ = buf_len;
@@ -410,6 +411,18 @@ void HttpNetworkTransaction::OnNeedsClientAuth(
OnIOComplete(ERR_SSL_CLIENT_AUTH_CERT_NEEDED);
}
+void HttpNetworkTransaction::OnHttpsProxyTunnelResponse(
+ const HttpResponseInfo& response_info,
+ HttpStream* stream) {
+ DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_);
+
+ headers_valid_ = true;
+ response_ = response_info;
+ stream_.reset(stream);
+ stream_request_.reset(); // we're done with the stream request
+ OnIOComplete(ERR_HTTPS_PROXY_TUNNEL_RESPONSE);
+}
+
bool HttpNetworkTransaction::is_https_request() const {
return request_->url.SchemeIs("https");
}
@@ -535,6 +548,10 @@ int HttpNetworkTransaction::DoCreateStreamComplete(int result) {
DCHECK(stream_.get());
} else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
result = HandleCertificateRequest(result);
+ } else if (result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
+ // Return OK and let the caller read the proxy's error page
+ next_state_ = STATE_NONE;
+ return OK;
}
// At this point we are done with the stream_request_.
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 255fcdf..dfcee41f 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -65,6 +65,8 @@ class HttpNetworkTransaction : public HttpTransaction,
const HttpResponseInfo& response_info,
HttpAuthController* auth_controller);
virtual void OnNeedsClientAuth(SSLCertRequestInfo* cert_info);
+ virtual void OnHttpsProxyTunnelResponse(const HttpResponseInfo& response_info,
+ HttpStream* stream);
private:
FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionTest, ResetStateForRestart);
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 17c3b78d..260de13 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -2280,10 +2280,11 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectFailure) {
EXPECT_EQ(ERR_IO_PENDING, rv);
rv = callback1.WaitForResult();
- EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv);
+ EXPECT_EQ(OK, rv);
const HttpResponseInfo* response = trans->GetResponseInfo();
- ASSERT_TRUE(response == NULL);
+ ASSERT_FALSE(response == NULL);
+ EXPECT_EQ(500, response->headers->response_code());
}
// Test the challenge-response-retry sequence through an HTTPS Proxy
@@ -4471,6 +4472,238 @@ TEST_F(HttpNetworkTransactionTest, HTTPSViaHttpsProxy) {
EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
}
+// Test an HTTPS Proxy's ability to redirect a CONNECT request
+TEST_F(HttpNetworkTransactionTest, RedirectOfHttpsConnectViaHttpsProxy) {
+ SessionDependencies session_deps(
+ ProxyService::CreateFixed("https://proxy:70"));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ MockWrite data_writes[] = {
+ MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 302 Redirect\r\n"),
+ MockRead("Location: http://login.example.com/\r\n"),
+ MockRead("Content-Length: 0\r\n\r\n"),
+ MockRead(false, OK),
+ };
+
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes));
+ SSLSocketDataProvider proxy_ssl(true, OK); // SSL to the proxy
+
+ session_deps.socket_factory.AddSocketDataProvider(&data);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&proxy_ssl);
+
+ TestCompletionCallback callback;
+
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+ int rv = trans->Start(&request, &callback, BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+
+ ASSERT_FALSE(response == NULL);
+
+ EXPECT_EQ(302, response->headers->response_code());
+ std::string url;
+ EXPECT_TRUE(response->headers->IsRedirect(&url));
+ EXPECT_EQ("http://login.example.com/", url);
+}
+
+// Test an HTTPS (SPDY) Proxy's ability to redirect a CONNECT request
+TEST_F(HttpNetworkTransactionTest, RedirectOfHttpsConnectViaSpdyProxy) {
+ SessionDependencies session_deps(
+ ProxyService::CreateFixed("https://proxy:70"));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructSpdyConnect(NULL, 0, 1));
+ scoped_ptr<spdy::SpdyFrame> goaway(ConstructSpdyRstStream(1, spdy::CANCEL));
+ MockWrite data_writes[] = {
+ CreateMockWrite(*conn.get(), 0, false),
+ };
+
+ static const char* const kExtraHeaders[] = {
+ "location",
+ "http://login.example.com/",
+ };
+ scoped_ptr<spdy::SpdyFrame> resp(
+ ConstructSpdySynReplyError("302 Redirect", kExtraHeaders,
+ arraysize(kExtraHeaders)/2, 1));
+ MockRead data_reads[] = {
+ CreateMockRead(*resp.get(), 1, false),
+ MockRead(true, 0, 2), // EOF
+ };
+
+ scoped_refptr<DelayedSocketData> data(
+ new DelayedSocketData(
+ 1, // wait for one write to finish before reading.
+ data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes)));
+ SSLSocketDataProvider proxy_ssl(true, OK); // SSL to the proxy
+ proxy_ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+ proxy_ssl.next_proto = "spdy/2";
+ proxy_ssl.was_npn_negotiated = true;
+
+ session_deps.socket_factory.AddSocketDataProvider(data.get());
+ session_deps.socket_factory.AddSSLSocketDataProvider(&proxy_ssl);
+
+ TestCompletionCallback callback;
+
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+ int rv = trans->Start(&request, &callback, BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+
+ ASSERT_FALSE(response == NULL);
+
+ EXPECT_EQ(302, response->headers->response_code());
+ std::string url;
+ EXPECT_TRUE(response->headers->IsRedirect(&url));
+ EXPECT_EQ("http://login.example.com/", url);
+}
+
+// Test an HTTPS Proxy's ability to provide a response to a CONNECT request
+TEST_F(HttpNetworkTransactionTest, ErrorResponseTofHttpsConnectViaHttpsProxy) {
+ SessionDependencies session_deps(
+ ProxyService::CreateFixed("https://proxy:70"));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ MockWrite data_writes[] = {
+ MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Proxy-Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 404 Not Found\r\n"),
+ MockRead("Content-Length: 23\r\n\r\n"),
+ MockRead("The host does not exist"),
+ MockRead(false, OK),
+ };
+
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes));
+ SSLSocketDataProvider proxy_ssl(true, OK); // SSL to the proxy
+
+ session_deps.socket_factory.AddSocketDataProvider(&data);
+ session_deps.socket_factory.AddSSLSocketDataProvider(&proxy_ssl);
+
+ TestCompletionCallback callback;
+
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+ int rv = trans->Start(&request, &callback, BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+
+ ASSERT_FALSE(response == NULL);
+
+ EXPECT_EQ(404, response->headers->response_code());
+ EXPECT_EQ(23, response->headers->GetContentLength());
+ EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion());
+ EXPECT_FALSE(response->ssl_info.is_valid());
+
+ std::string response_data;
+ ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+ EXPECT_EQ("The host does not exist", response_data);
+}
+
+// Test an HTTPS (SPDY) Proxy's ability to provide a response to a CONNECT
+// request
+TEST_F(HttpNetworkTransactionTest, ErrorResponseTofHttpsConnectViaSpdyProxy) {
+ SessionDependencies session_deps(
+ ProxyService::CreateFixed("https://proxy:70"));
+
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ scoped_ptr<spdy::SpdyFrame> conn(ConstructSpdyConnect(NULL, 0, 1));
+ scoped_ptr<spdy::SpdyFrame> goaway(ConstructSpdyRstStream(1, spdy::CANCEL));
+ MockWrite data_writes[] = {
+ CreateMockWrite(*conn.get(), 0, false),
+ };
+
+ static const char* const kExtraHeaders[] = {
+ "location",
+ "http://login.example.com/",
+ };
+ scoped_ptr<spdy::SpdyFrame> resp(
+ ConstructSpdySynReplyError("404 Not Found", kExtraHeaders,
+ arraysize(kExtraHeaders)/2, 1));
+ scoped_ptr<spdy::SpdyFrame> body(
+ ConstructSpdyBodyFrame(1, "The host does not exist", 23, true));
+ MockRead data_reads[] = {
+ CreateMockRead(*resp.get(), 1, false),
+ CreateMockRead(*body.get(), 2, false),
+ MockRead(true, 0, 3), // EOF
+ };
+
+ scoped_refptr<DelayedSocketData> data(
+ new DelayedSocketData(
+ 1, // wait for one write to finish before reading.
+ data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes)));
+ SSLSocketDataProvider proxy_ssl(true, OK); // SSL to the proxy
+ proxy_ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+ proxy_ssl.next_proto = "spdy/2";
+ proxy_ssl.was_npn_negotiated = true;
+
+ session_deps.socket_factory.AddSocketDataProvider(data.get());
+ session_deps.socket_factory.AddSSLSocketDataProvider(&proxy_ssl);
+
+ TestCompletionCallback callback;
+
+ scoped_ptr<HttpTransaction> trans(
+ new HttpNetworkTransaction(CreateSession(&session_deps)));
+
+ int rv = trans->Start(&request, &callback, BoundNetLog());
+ EXPECT_EQ(ERR_IO_PENDING, rv);
+
+ rv = callback.WaitForResult();
+ EXPECT_EQ(OK, rv);
+ const HttpResponseInfo* response = trans->GetResponseInfo();
+
+ ASSERT_FALSE(response == NULL);
+
+ EXPECT_EQ(404, response->headers->response_code());
+ EXPECT_FALSE(response->ssl_info.is_valid());
+
+ std::string response_data;
+ ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
+ EXPECT_EQ("The host does not exist", response_data);
+}
+
// Test HTTPS connections to a site with a bad certificate, going through an
// HTTPS proxy
TEST_F(HttpNetworkTransactionTest, HTTPSBadCertificateViaHttpsProxy) {
diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc
index c2c2899..2f44428 100644
--- a/net/http/http_proxy_client_socket.cc
+++ b/net/http/http_proxy_client_socket.cc
@@ -12,6 +12,7 @@
#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_proxy_utils.h"
@@ -31,7 +32,8 @@ HttpProxyClientSocket::HttpProxyClientSocket(
HttpAuthCache* http_auth_cache,
HttpAuthHandlerFactory* http_auth_handler_factory,
bool tunnel,
- bool using_spdy)
+ bool using_spdy,
+ bool is_https_proxy)
: ALLOW_THIS_IN_INITIALIZER_LIST(
io_callback_(this, &HttpProxyClientSocket::OnIOComplete)),
next_state_(STATE_NONE),
@@ -46,6 +48,7 @@ HttpProxyClientSocket::HttpProxyClientSocket(
: NULL),
tunnel_(tunnel),
using_spdy_(using_spdy),
+ is_https_proxy_(is_https_proxy),
net_log_(transport_socket->socket()->NetLog()) {
// Synthesize the bits of a request that we actually use.
request_.url = request_url;
@@ -59,6 +62,12 @@ HttpProxyClientSocket::~HttpProxyClientSocket() {
Disconnect();
}
+HttpStream* HttpProxyClientSocket::CreateConnectResponseStream() {
+ return new HttpBasicStream(transport_.release(),
+ http_stream_parser_.release(), false);
+}
+
+
int HttpProxyClientSocket::Connect(CompletionCallback* callback) {
DCHECK(transport_.get());
DCHECK(transport_->socket());
@@ -145,7 +154,8 @@ void HttpProxyClientSocket::LogBlockedTunnelResponse(int response_code) const {
}
void HttpProxyClientSocket::Disconnect() {
- transport_->socket()->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().
@@ -409,6 +419,8 @@ int HttpProxyClientSocket::DoReadHeadersComplete(int result) {
return HandleAuthChallenge();
default:
+ if (is_https_proxy_)
+ return ERR_HTTPS_PROXY_TUNNEL_RESPONSE;
// For all other status codes, we conservatively fail the CONNECT
// request.
// We lose something by doing this. We have seen proxy 403, 404, and
diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h
index 8e345b9..325951d 100644
--- a/net/http/http_proxy_client_socket.h
+++ b/net/http/http_proxy_client_socket.h
@@ -17,7 +17,7 @@
#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"
+#include "net/http/proxy_client_socket.h"
class GURL;
@@ -32,7 +32,7 @@ class HttpStream;
class HttpStreamParser;
class IOBuffer;
-class HttpProxyClientSocket : public ClientSocket {
+class HttpProxyClientSocket : public ProxyClientSocket {
public:
// Takes ownership of |transport_socket|, which should already be connected
// by the time Connect() is called. If tunnel is true then on Connect()
@@ -45,7 +45,8 @@ class HttpProxyClientSocket : public ClientSocket {
HttpAuthCache* http_auth_cache,
HttpAuthHandlerFactory* http_auth_handler_factory,
bool tunnel,
- bool using_spdy);
+ bool using_spdy,
+ bool is_https_proxy);
// On destruction Disconnect() is called.
virtual ~HttpProxyClientSocket();
@@ -55,10 +56,12 @@ class HttpProxyClientSocket : public ClientSocket {
// RestartWithAuth.
int RestartWithAuth(CompletionCallback* callback);
- const HttpResponseInfo* GetResponseInfo() const {
+ const HttpResponseInfo* GetConnectResponseInfo() const {
return response_.headers ? &response_ : NULL;
}
+ virtual HttpStream* CreateConnectResponseStream();
+
const scoped_refptr<HttpAuthController>& auth_controller() {
return auth_;
}
@@ -146,7 +149,7 @@ class HttpProxyClientSocket : public ClientSocket {
scoped_refptr<IOBuffer> drain_buf_;
// Stores the underlying socket.
- const scoped_ptr<ClientSocketHandle> transport_;
+ scoped_ptr<ClientSocketHandle> transport_;
// 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.
@@ -155,6 +158,8 @@ class HttpProxyClientSocket : public ClientSocket {
const bool tunnel_;
// If true, then the connection to the proxy is a SPDY connection.
const bool using_spdy_;
+ // If true, then SSL is used to communicate with this proxy
+ const bool is_https_proxy_;
std::string request_line_;
HttpRequestHeaders request_headers_;
diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc
index 13fa0a1..d2e3ccb 100644
--- a/net/http/http_proxy_client_socket_pool.cc
+++ b/net/http/http_proxy_client_socket_pool.cc
@@ -318,13 +318,16 @@ int HttpProxyConnectJob::DoHttpProxyConnect() {
params_->http_auth_cache(),
params_->http_auth_handler_factory(),
params_->tunnel(),
- using_spdy_));
+ using_spdy_,
+ params_->ssl_params() != NULL));
return transport_socket_->Connect(&callback_);
}
int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) {
- if (result == OK || result == ERR_PROXY_AUTH_REQUESTED)
+ if (result == OK || result == ERR_PROXY_AUTH_REQUESTED ||
+ result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
set_socket(transport_socket_.release());
+ }
return result;
}
diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h
index fb3967d..91963d8 100644
--- a/net/http/http_proxy_client_socket_pool.h
+++ b/net/http/http_proxy_client_socket_pool.h
@@ -15,6 +15,7 @@
#include "net/base/host_port_pair.h"
#include "net/http/http_auth.h"
#include "net/http/http_response_info.h"
+#include "net/http/proxy_client_socket.h"
#include "net/socket/client_socket_pool_base.h"
#include "net/socket/client_socket_pool_histograms.h"
#include "net/socket/client_socket_pool.h"
@@ -157,7 +158,7 @@ class HttpProxyConnectJob : public ConnectJob {
State next_state_;
CompletionCallbackImpl<HttpProxyConnectJob> callback_;
scoped_ptr<ClientSocketHandle> transport_socket_handle_;
- scoped_ptr<ClientSocket> transport_socket_;
+ scoped_ptr<ProxyClientSocket> transport_socket_;
bool using_spdy_;
HttpResponseInfo error_response_info_;
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index 478a312..8c6d545 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -502,9 +502,18 @@ TEST_P(HttpProxyClientSocketPoolTest, TunnelSetupError) {
data_->RunFor(2);
- EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, callback_.WaitForResult());
- EXPECT_FALSE(handle_.is_initialized());
- EXPECT_FALSE(handle_.socket());
+ rv = callback_.WaitForResult();
+ if (GetParam() == HTTP) {
+ // HTTP Proxy CONNECT responses are not trustworthy
+ EXPECT_EQ(ERR_TUNNEL_CONNECTION_FAILED, rv);
+ EXPECT_FALSE(handle_.is_initialized());
+ EXPECT_FALSE(handle_.socket());
+ } else {
+ // HTTPS or SPDY Proxy CONNECT responses are trustworthy
+ EXPECT_EQ(ERR_HTTPS_PROXY_TUNNEL_RESPONSE, rv);
+ EXPECT_TRUE(handle_.is_initialized());
+ EXPECT_TRUE(handle_.socket());
+ }
}
// It would be nice to also test the timeouts in HttpProxyClientSocketPool.
diff --git a/net/http/http_stream_request.cc b/net/http/http_stream_request.cc
index 3f6b419..485dba7 100644
--- a/net/http/http_stream_request.cc
+++ b/net/http/http_stream_request.cc
@@ -206,6 +206,12 @@ void HttpStreamRequest::OnNeedsClientAuthCallback(
delegate_->OnNeedsClientAuth(cert_info);
}
+void HttpStreamRequest::OnHttpsProxyTunnelResponseCallback(
+ const HttpResponseInfo& response_info,
+ HttpStream* stream) {
+ delegate_->OnHttpsProxyTunnelResponse(response_info, stream);
+}
+
void HttpStreamRequest::OnPreconnectsComplete(int result) {
preconnect_delegate_->OnPreconnectsComplete(this, result);
}
@@ -253,7 +259,7 @@ int HttpStreamRequest::RunLoop(int result) {
HttpProxyClientSocket* http_proxy_socket =
static_cast<HttpProxyClientSocket*>(connection_->socket());
const HttpResponseInfo* tunnel_auth_response =
- http_proxy_socket->GetResponseInfo();
+ http_proxy_socket->GetConnectResponseInfo();
next_state_ = STATE_WAITING_USER_ACTION;
MessageLoop::current()->PostTask(
@@ -273,6 +279,23 @@ int HttpStreamRequest::RunLoop(int result) {
connection_->ssl_error_response_info().cert_request_info));
return ERR_IO_PENDING;
+ case ERR_HTTPS_PROXY_TUNNEL_RESPONSE:
+ {
+ DCHECK(connection_.get());
+ DCHECK(connection_->socket());
+ DCHECK(establishing_tunnel_);
+
+ ProxyClientSocket* proxy_socket =
+ static_cast<ProxyClientSocket*>(connection_->socket());
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ method_factory_.NewRunnableMethod(
+ &HttpStreamRequest::OnHttpsProxyTunnelResponseCallback,
+ *proxy_socket->GetConnectResponseInfo(),
+ proxy_socket->CreateConnectResponseStream()));
+ return ERR_IO_PENDING;
+ }
+
case OK:
next_state_ = STATE_DONE;
MessageLoop::current()->PostTask(
@@ -662,13 +685,15 @@ int HttpStreamRequest::DoInitConnectionComplete(int result) {
if (!force_spdy_over_ssl_ && force_spdy_always_)
SwitchToSpdyMode();
- if (result == ERR_PROXY_AUTH_REQUESTED) {
+ if (result == ERR_PROXY_AUTH_REQUESTED ||
+ result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) {
DCHECK(!ssl_started);
// Other state (i.e. |using_ssl_|) suggests that |connection_| will have an
// SSL socket, but there was an error before that could happen. This
// puts the in progress HttpProxy socket into |connection_| in order to
- // complete the auth. The tunnel restart code is careful to remove it
- // before returning control to the rest of this class.
+ // complete the auth (or read the response body). The tunnel restart code
+ // is careful to remove it before returning control to the rest of this
+ // class.
connection_.reset(connection_->release_pending_http_proxy_connection());
return result;
}
@@ -744,7 +769,8 @@ int HttpStreamRequest::DoCreateStream() {
if (!using_spdy_) {
bool using_proxy = (proxy_info()->is_http() || proxy_info()->is_https()) &&
request_info().url.SchemeIs("http");
- stream_.reset(new HttpBasicStream(connection_.release(), using_proxy));
+ stream_.reset(new HttpBasicStream(connection_.release(), NULL,
+ using_proxy));
return OK;
}
@@ -792,8 +818,8 @@ int HttpStreamRequest::DoCreateStream() {
if (spdy_session->IsClosed())
return ERR_CONNECTION_CLOSED;
- bool useRelativeUrl = direct || request_info().url.SchemeIs("https");
- stream_.reset(new SpdyHttpStream(spdy_session, useRelativeUrl));
+ bool use_relative_url = direct || request_info().url.SchemeIs("https");
+ stream_.reset(new SpdyHttpStream(spdy_session, use_relative_url));
return OK;
}
diff --git a/net/http/http_stream_request.h b/net/http/http_stream_request.h
index 51559a7..f20b5f3 100644
--- a/net/http/http_stream_request.h
+++ b/net/http/http_stream_request.h
@@ -107,6 +107,8 @@ class HttpStreamRequest : public StreamRequest {
void OnNeedsProxyAuthCallback(const HttpResponseInfo& response_info,
HttpAuthController* auth_controller);
void OnNeedsClientAuthCallback(SSLCertRequestInfo* cert_info);
+ void OnHttpsProxyTunnelResponseCallback(const HttpResponseInfo& response_info,
+ HttpStream* stream);
void OnPreconnectsComplete(int result);
void OnIOComplete(int result);
diff --git a/net/http/proxy_client_socket.h b/net/http/proxy_client_socket.h
new file mode 100644
index 0000000..6557c883
--- /dev/null
+++ b/net/http/proxy_client_socket.h
@@ -0,0 +1,35 @@
+// 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_PROXY_CLIENT_SOCKET_H_
+#define NET_HTTP_PROXY_CLIENT_SOCKET_H_
+#pragma once
+
+#include "net/socket/client_socket.h"
+
+namespace net {
+
+class HttpStream;
+class HttpResponseInfo;
+
+class ProxyClientSocket : public ClientSocket {
+ public:
+ ProxyClientSocket() {}
+ virtual ~ProxyClientSocket() {}
+
+ // Returns the HttpResponseInfo (including HTTP Headers) from
+ // the response to the CONNECT request.
+ virtual const HttpResponseInfo* GetConnectResponseInfo() const = 0;
+
+ // Transfers ownership of a newly created HttpStream to the caller
+ // which can be used to read the response body.
+ virtual HttpStream* CreateConnectResponseStream() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProxyClientSocket);
+};
+
+} // namespace net
+
+#endif // NET_HTTP_PROXY_CLIENT_SOCKET_H_
diff --git a/net/http/stream_factory.h b/net/http/stream_factory.h
index 94b3cc8..1a7aa77 100644
--- a/net/http/stream_factory.h
+++ b/net/http/stream_factory.h
@@ -69,6 +69,13 @@ class StreamRequest {
// may take a reference if it needs the cert_info beyond the lifetime of
// this callback.
virtual void OnNeedsClientAuth(SSLCertRequestInfo* cert_info) = 0;
+
+ // This is the failure of the CONNECT request through an HTTPS proxy.
+ // Headers can be read from |response_info|, while the body can be read
+ // from |stream|.
+ // Ownership of |stream| is transferred to the delegate.
+ virtual void OnHttpsProxyTunnelResponse(
+ const HttpResponseInfo& response_info, HttpStream* stream) = 0;
};
virtual ~StreamRequest() {}