diff options
author | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-01 20:55:17 +0000 |
---|---|---|
committer | rch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-01 20:55:17 +0000 |
commit | 7642b5aed675a2b99007a833d58b705b493b5ef4 (patch) | |
tree | ee9b7ce6cb84cc495eb26ac5ece10b89a7ff038a /net | |
parent | f30394e222a0df68576c8564ecb4e1a8b4bf0d20 (diff) | |
download | chromium_src-7642b5aed675a2b99007a833d58b705b493b5ef4.zip chromium_src-7642b5aed675a2b99007a833d58b705b493b5ef4.tar.gz chromium_src-7642b5aed675a2b99007a833d58b705b493b5ef4.tar.bz2 |
Add support for speaking SPDY to an HTTPS proxy.
Currently only http urls are supported.
BUG=29625
TEST=HttpNetworkTransactionTest.HttpsProxySpdyGet
Review URL: http://codereview.chromium.org/3259006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@58236 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 59 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket.cc | 11 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket.h | 9 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket_pool.cc | 11 | ||||
-rw-r--r-- | net/http/http_proxy_client_socket_pool.h | 1 | ||||
-rw-r--r-- | net/http/http_stream_request.cc | 43 | ||||
-rw-r--r-- | net/spdy/spdy_http_stream.cc | 15 | ||||
-rw-r--r-- | net/spdy/spdy_http_stream.h | 5 | ||||
-rw-r--r-- | net/spdy/spdy_http_stream_unittest.cc | 5 | ||||
-rw-r--r-- | net/spdy/spdy_test_util.cc | 18 | ||||
-rw-r--r-- | net/spdy/spdy_test_util.h | 12 |
11 files changed, 169 insertions, 20 deletions
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 5d21eba..d0ed59b 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -1722,6 +1722,65 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxyGet) { EXPECT_TRUE(response->auth_challenge.get() == NULL); } +// Test a SPDY get through an HTTPS Proxy. +TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGet) { + // Configure against https proxy server "proxy:70". + SessionDependencies session_deps(CreateFixedProxyService("https://proxy: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)); + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + // fetch http://www.google.com/ via SPDY + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST, + false)); + MockWrite spdy_writes[] = { CreateMockWrite(*req) }; + + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> data(ConstructSpdyBodyFrame(1, true)); + MockRead spdy_reads[] = { + CreateMockRead(*resp), + CreateMockRead(*data), + MockRead(true, 0, 0), + }; + + scoped_refptr<DelayedSocketData> spdy_data( + new DelayedSocketData( + 1, // wait for one write to finish before reading. + spdy_reads, arraysize(spdy_reads), + spdy_writes, arraysize(spdy_writes))); + session_deps.socket_factory.AddSocketDataProvider(spdy_data); + + SSLSocketDataProvider ssl(true, OK); + ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated; + ssl.next_proto = "spdy/2"; + ssl.was_npn_negotiated = true; + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); + + TestCompletionCallback callback1; + + int rv = trans->Start(&request, &callback1, log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback1.WaitForResult(); + EXPECT_EQ(OK, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + + std::string response_data; + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ(net::kUploadData, response_data); +} + // Test the challenge-response-retry sequence through an HTTPS Proxy TEST_F(HttpNetworkTransactionTest, HttpsProxyAuthRetry) { // Configure against https proxy server "proxy:70". diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc index 7ed80a5..eccd652 100644 --- a/net/http/http_proxy_client_socket.cc +++ b/net/http/http_proxy_client_socket.cc @@ -53,7 +53,8 @@ HttpProxyClientSocket::HttpProxyClientSocket( ClientSocketHandle* transport_socket, const GURL& request_url, const std::string& user_agent, const HostPortPair& endpoint, const HostPortPair& proxy_server, - const scoped_refptr<HttpNetworkSession>& session, bool tunnel) + const scoped_refptr<HttpNetworkSession>& session, bool tunnel, + bool using_spdy) : ALLOW_THIS_IN_INITIALIZER_LIST( io_callback_(this, &HttpProxyClientSocket::OnIOComplete)), next_state_(STATE_NONE), @@ -65,6 +66,7 @@ HttpProxyClientSocket::HttpProxyClientSocket( GURL("http://" + proxy_server.ToString()), session) : NULL), tunnel_(tunnel), + using_spdy_(using_spdy), net_log_(transport_socket->socket()->NetLog()) { // Synthesize the bits of a request that we actually use. request_.url = request_url; @@ -83,7 +85,12 @@ int HttpProxyClientSocket::Connect(CompletionCallback* callback) { DCHECK(transport_->socket()); DCHECK(!user_callback_); - if (!tunnel_) + // TODO(rch): figure out the right way to set up a tunnel with SPDY. + // This approach sends the complete HTTPS request to the proxy + // which allows the proxy to see "private" data. Instead, we should + // create an SSL tunnel to the origin server using the CONNECT method + // inside a single SPDY stream. + if (using_spdy_ || !tunnel_) next_state_ = STATE_DONE; if (next_state_ == STATE_DONE) return OK; diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h index 488fbdc..adfcc11 100644 --- a/net/http/http_proxy_client_socket.h +++ b/net/http/http_proxy_client_socket.h @@ -39,7 +39,8 @@ class HttpProxyClientSocket : public ClientSocket { const HostPortPair& endpoint, const HostPortPair& proxy_server, const scoped_refptr<HttpNetworkSession>& session, - bool tunnel); + bool tunnel, + bool using_spdy); // On destruction Disconnect() is called. virtual ~HttpProxyClientSocket(); @@ -57,6 +58,10 @@ class HttpProxyClientSocket : public ClientSocket { return auth_; } + bool using_spdy() { + return using_spdy_; + } + // ClientSocket methods: // Authenticates to the Http Proxy and then passes data freely. @@ -140,6 +145,8 @@ class HttpProxyClientSocket : public ClientSocket { const HostPortPair endpoint_; scoped_refptr<HttpAuthController> auth_; const bool tunnel_; + // If true, then the connection to the proxy is a SPDY connection. + const bool using_spdy_; std::string request_headers_; diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc index 34bc0bb..f11ad30 100644 --- a/net/http/http_proxy_client_socket_pool.cc +++ b/net/http/http_proxy_client_socket_pool.cc @@ -16,6 +16,7 @@ #include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool_base.h" #include "net/socket/tcp_client_socket_pool.h" +#include "net/socket/ssl_client_socket.h" namespace net { @@ -67,7 +68,8 @@ HttpProxyConnectJob::HttpProxyConnectJob( ssl_pool_(ssl_pool), resolver_(host_resolver), ALLOW_THIS_IN_INITIALIZER_LIST( - callback_(this, &HttpProxyConnectJob::OnIOComplete)) { + callback_(this, &HttpProxyConnectJob::OnIOComplete)), + using_spdy_(false) { } HttpProxyConnectJob::~HttpProxyConnectJob() {} @@ -182,6 +184,10 @@ int HttpProxyConnectJob::DoSSLConnectComplete(int result) { return result; } + SSLClientSocket* ssl = + static_cast<SSLClientSocket*>(transport_socket_handle_->socket()); + using_spdy_ = ssl->was_spdy_negotiated(); + // Reset the timer to just the length of time allowed for HttpProxy handshake // so that a fast SSL connection plus a slow HttpProxy failure doesn't take // longer to timeout than it should. @@ -204,7 +210,8 @@ int HttpProxyConnectJob::DoHttpProxyConnect() { params_->user_agent(), params_->endpoint(), proxy_server, params_->session(), - params_->tunnel())); + params_->tunnel(), + using_spdy_)); int result = transport_socket_->Connect(&callback_); // Clear the circular reference to HttpNetworkSession (|params_| reference diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h index 8a16e22..20c0f26 100644 --- a/net/http/http_proxy_client_socket_pool.h +++ b/net/http/http_proxy_client_socket_pool.h @@ -132,6 +132,7 @@ class HttpProxyConnectJob : public ConnectJob { CompletionCallbackImpl<HttpProxyConnectJob> callback_; scoped_ptr<ClientSocketHandle> transport_socket_handle_; scoped_ptr<ClientSocket> transport_socket_; + bool using_spdy_; DISALLOW_COPY_AND_ASSIGN(HttpProxyConnectJob); }; diff --git a/net/http/http_stream_request.cc b/net/http/http_stream_request.cc index 6a53a72..4d6895c 100644 --- a/net/http/http_stream_request.cc +++ b/net/http/http_stream_request.cc @@ -427,6 +427,17 @@ int HttpStreamRequest::DoInitConnection() { next_state_ = STATE_CREATE_STREAM; return OK; } + // Check next if we have a spdy session for this proxy. If so, then go + // straight to using that. + if (proxy_info()->is_https()) { + HostPortProxyPair proxy(proxy_info()->proxy_server().host_port_pair(), + proxy_info()->proxy_server()); + if (session_->spdy_session_pool()->HasSession(proxy)) { + using_spdy_ = true; + next_state_ = STATE_CREATE_STREAM; + return OK; + } + } // Build the string used to uniquely identify connections of this type. // Determine the host and port to connect to. @@ -569,6 +580,14 @@ int HttpStreamRequest::DoInitConnectionComplete(int result) { } if (force_spdy_over_ssl_ && force_spdy_always_) using_spdy_ = true; + } else if (proxy_info()->is_https() && connection_->socket() && + result == OK) { + HttpProxyClientSocket* proxy_socket = + static_cast<HttpProxyClientSocket*>(connection_->socket()); + if (proxy_socket->using_spdy()) { + was_npn_negotiated_ = true; + using_spdy_ = true; + } } // We may be using spdy without SSL @@ -654,15 +673,29 @@ int HttpStreamRequest::DoCreateStream() { CHECK(!stream_.get()); + bool direct = true; const scoped_refptr<SpdySessionPool> spdy_pool = session_->spdy_session_pool(); scoped_refptr<SpdySession> spdy_session; - HostPortProxyPair pair(endpoint_, proxy_info()->proxy_server()); - if (session_->spdy_session_pool()->HasSession(pair)) { + const ProxyServer& proxy_server = proxy_info()->proxy_server(); + HostPortProxyPair pair(endpoint_, proxy_server); + if (spdy_pool->HasSession(pair)) { + // We have a SPDY session to the origin server. This might be a direct + // connection, or it might be a SPDY session through an HTTP or HTTPS proxy. spdy_session = - session_->spdy_session_pool()->Get(pair, session_, net_log_); - } else { + spdy_pool->Get(pair, session_, net_log_); + } else if (proxy_info()->is_https()) { + // If we don't have a direct SPDY session, and we're using an HTTPS + // proxy, then we might have a SPDY session to the proxy + pair = HostPortProxyPair(proxy_server.host_port_pair(), proxy_server); + if (spdy_pool->HasSession(pair)) { + spdy_session = spdy_pool->Get(pair, session_, net_log_); + } + direct = false; + } + + if (!spdy_session.get()) { // SPDY can be negotiated using the TLS next protocol negotiation (NPN) // extension, or just directly using SSL. Either way, |connection_| must // contain an SSLClientSocket. @@ -677,7 +710,7 @@ int HttpStreamRequest::DoCreateStream() { if (spdy_session->IsClosed()) return ERR_CONNECTION_CLOSED; - SpdyHttpStream* stream = new SpdyHttpStream(spdy_session); + SpdyHttpStream* stream = new SpdyHttpStream(spdy_session, direct); stream_.reset(new HttpStreamHandle(NULL, stream)); return OK; } diff --git a/net/spdy/spdy_http_stream.cc b/net/spdy/spdy_http_stream.cc index 8072d53..2880b07 100644 --- a/net/spdy/spdy_http_stream.cc +++ b/net/spdy/spdy_http_stream.cc @@ -91,7 +91,8 @@ bool SpdyHeadersToHttpResponse(const spdy::SpdyHeaderBlock& headers, // Create a SpdyHeaderBlock for a Spdy SYN_STREAM Frame from // a HttpRequestInfo block. void CreateSpdyHeadersFromHttpRequest( - const net::HttpRequestInfo& info, spdy::SpdyHeaderBlock* headers) { + const net::HttpRequestInfo& info, spdy::SpdyHeaderBlock* headers, + bool direct) { // TODO(willchan): It's not really necessary to convert from // HttpRequestHeaders to spdy::SpdyHeaderBlock. @@ -131,7 +132,10 @@ void CreateSpdyHeadersFromHttpRequest( (*headers)["content-length"] = "0"; } - (*headers)["url"] = net::HttpUtil::PathForRequest(info.url); + if (direct) + (*headers)["url"] = net::HttpUtil::PathForRequest(info.url); + else + (*headers)["url"] = net::HttpUtil::SpecForRequest(info.url); (*headers)["host"] = net::GetHostAndOptionalPort(info.url); (*headers)["scheme"] = info.url.scheme(); (*headers)["version"] = kHttpProtocolVersion; @@ -151,7 +155,7 @@ void CreateSpdyHeadersFromHttpRequest( namespace net { -SpdyHttpStream::SpdyHttpStream(SpdySession* spdy_session) +SpdyHttpStream::SpdyHttpStream(SpdySession* spdy_session, bool direct) : ALLOW_THIS_IN_INITIALIZER_LIST(read_callback_factory_(this)), stream_(NULL), spdy_session_(spdy_session), @@ -160,7 +164,8 @@ SpdyHttpStream::SpdyHttpStream(SpdySession* spdy_session) user_callback_(NULL), user_buffer_len_(0), buffered_read_callback_pending_(false), - more_read_data_pending_(false) { } + more_read_data_pending_(false), + direct_(direct) { } SpdyHttpStream::~SpdyHttpStream() { if (stream_) @@ -279,7 +284,7 @@ int SpdyHttpStream::SendRequest(const std::string& /*headers_string*/, stream_->SetDelegate(this); linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock); - CreateSpdyHeadersFromHttpRequest(*request_info_, headers.get()); + CreateSpdyHeadersFromHttpRequest(*request_info_, headers.get(), direct_); stream_->set_spdy_headers(headers); stream_->SetRequestTime(request_time); diff --git a/net/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h index 2fe15c8..262c488 100644 --- a/net/spdy/spdy_http_stream.h +++ b/net/spdy/spdy_http_stream.h @@ -32,7 +32,7 @@ class UploadDataStream; class SpdyHttpStream : public SpdyStream::Delegate, public HttpStream { public: // SpdyHttpStream constructor - explicit SpdyHttpStream(SpdySession* spdy_session); + SpdyHttpStream(SpdySession* spdy_session, bool direct); virtual ~SpdyHttpStream(); SpdyStream* stream() { return stream_.get(); } @@ -174,6 +174,9 @@ class SpdyHttpStream : public SpdyStream::Delegate, public HttpStream { // scheduled read callback. bool more_read_data_pending_; + // Is this spdy stream direct to the origin server (or to a proxy). + bool direct_; + DISALLOW_COPY_AND_ASSIGN(SpdyHttpStream); }; diff --git a/net/spdy/spdy_http_stream_unittest.cc b/net/spdy/spdy_http_stream_unittest.cc index 22244c4..fbc2a8b 100644 --- a/net/spdy/spdy_http_stream_unittest.cc +++ b/net/spdy/spdy_http_stream_unittest.cc @@ -68,7 +68,8 @@ TEST_F(SpdyHttpStreamTest, SendRequest) { TestCompletionCallback callback; HttpResponseInfo response; BoundNetLog net_log; - scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_.get())); + scoped_ptr<SpdyHttpStream> http_stream( + new SpdyHttpStream(session_.get(), true)); ASSERT_EQ( OK, http_stream->InitializeStream(&request, net_log, NULL)); @@ -118,7 +119,7 @@ TEST_F(SpdyHttpStreamTest, SpdyURLTest) { TestCompletionCallback callback; HttpResponseInfo response; BoundNetLog net_log; - scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_)); + scoped_ptr<SpdyHttpStream> http_stream(new SpdyHttpStream(session_, true)); ASSERT_EQ( OK, http_stream->InitializeStream(&request, net_log, NULL)); diff --git a/net/spdy/spdy_test_util.cc b/net/spdy/spdy_test_util.cc index 5a0b884..1727717 100644 --- a/net/spdy/spdy_test_util.cc +++ b/net/spdy/spdy_test_util.cc @@ -370,11 +370,25 @@ spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], bool compressed, int stream_id, RequestPriority request_priority) { - static const char* const kStandardGetHeaders[] = { + return ConstructSpdyGet(extra_headers, extra_header_count, compressed, + stream_id, request_priority, true); +} + +// Constructs a standard SPDY GET SYN packet, optionally compressed. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. +// Returns a SpdyFrame. +spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority, + bool direct) { + const char* const kStandardGetHeaders[] = { "method", "GET", "url", - "/", + (direct ? "/" : "http://www.google.com/"), "host", "www.google.com", "scheme", diff --git a/net/spdy/spdy_test_util.h b/net/spdy/spdy_test_util.h index c2e15e0..34c1df80 100644 --- a/net/spdy/spdy_test_util.h +++ b/net/spdy/spdy_test_util.h @@ -200,6 +200,18 @@ spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], int stream_id, RequestPriority request_priority); +// Constructs a standard SPDY GET SYN packet, optionally compressed. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. If |direct| is false, the +// the full url will be used instead of simply the path. +// Returns a SpdyFrame. +spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], + int extra_header_count, + bool compressed, + int stream_id, + RequestPriority request_priority, + bool direct); + // Constructs a standard SPDY push SYN packet. // |extra_headers| are the extra header-value pairs, which typically // will vary the most between calls. |