summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-01 20:55:17 +0000
committerrch@chromium.org <rch@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-09-01 20:55:17 +0000
commit7642b5aed675a2b99007a833d58b705b493b5ef4 (patch)
treeee9b7ce6cb84cc495eb26ac5ece10b89a7ff038a /net
parentf30394e222a0df68576c8564ecb4e1a8b4bf0d20 (diff)
downloadchromium_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.cc59
-rw-r--r--net/http/http_proxy_client_socket.cc11
-rw-r--r--net/http/http_proxy_client_socket.h9
-rw-r--r--net/http/http_proxy_client_socket_pool.cc11
-rw-r--r--net/http/http_proxy_client_socket_pool.h1
-rw-r--r--net/http/http_stream_request.cc43
-rw-r--r--net/spdy/spdy_http_stream.cc15
-rw-r--r--net/spdy/spdy_http_stream.h5
-rw-r--r--net/spdy/spdy_http_stream_unittest.cc5
-rw-r--r--net/spdy/spdy_test_util.cc18
-rw-r--r--net/spdy/spdy_test_util.h12
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.