diff options
author | bengr@google.com <bengr@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-03 04:09:29 +0000 |
---|---|---|
committer | bengr@google.com <bengr@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-03 04:09:29 +0000 |
commit | 7c6f7ba3979d71df4c3914746ef3ab4b1b9c88eb (patch) | |
tree | a7632309261661d8f50d113f952d4a02930d60c4 /net | |
parent | 8ecd3cdd1876ff6b6079385ba05c666fc9580ec2 (diff) | |
download | chromium_src-7c6f7ba3979d71df4c3914746ef3ab4b1b9c88eb.zip chromium_src-7c6f7ba3979d71df4c3914746ef3ab4b1b9c88eb.tar.gz chromium_src-7c6f7ba3979d71df4c3914746ef3ab4b1b9c88eb.tar.bz2 |
Enables a SPDY proxy to push resources from arbitrary origins.
This change will make it possible for Chromium to accept a resource
that is pushed by a SPDY proxy, even if the SYN control frame advertises
a URL whose origin is different from its associated request. The advantage
of this change is, potentially, better performance. This change is disabled
by default and activated by a new switch.
BUG=113427
TEST=
Review URL: http://codereview.chromium.org/9475037
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@130302 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_network_transaction_spdy21_unittest.cc | 107 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy2_unittest.cc | 107 | ||||
-rw-r--r-- | net/http/http_network_transaction_spdy3_unittest.cc | 107 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_spdy21_unittest.cc | 5 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_spdy2_unittest.cc | 5 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_spdy3_unittest.cc | 5 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 36 | ||||
-rw-r--r-- | net/spdy/spdy_session.h | 8 |
8 files changed, 370 insertions, 10 deletions
diff --git a/net/http/http_network_transaction_spdy21_unittest.cc b/net/http/http_network_transaction_spdy21_unittest.cc index 6a6e706..93b50a3 100644 --- a/net/http/http_network_transaction_spdy21_unittest.cc +++ b/net/http/http_network_transaction_spdy21_unittest.cc @@ -4883,6 +4883,113 @@ TEST_F(HttpNetworkTransactionSpdy21Test, BasicAuthSpdyProxy) { session->CloseAllConnections(); } +// Test that an explicitly trusted SPDY proxy can push a resource from an +// origin that is different from that of its associated resource. +TEST_F(HttpNetworkTransactionSpdy21Test, CrossOriginProxyPush) { + HttpRequestInfo request; + HttpRequestInfo push_request; + + static const unsigned char kPushBodyFrame[] = { + 0x00, 0x00, 0x00, 0x02, // header, ID + 0x01, 0x00, 0x00, 0x06, // FIN, length + 'p', 'u', 's', 'h', 'e', 'd' // "pushed" + }; + + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + push_request.method = "GET"; + push_request.url = GURL("http://www.another-origin.com/foo.dat"); + + // Enable cross-origin push. + net::SpdySession::set_allow_spdy_proxy_push_across_origins("myproxy:70"); + + // Configure against https proxy server "myproxy:70". + SessionDependencies session_deps( + ProxyService::CreateFixed("https://myproxy:70")); + CapturingBoundNetLog log(CapturingNetLog::kUnbounded); + session_deps.net_log = log.bound().net_log(); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + scoped_ptr<SpdyFrame> + stream1_syn(ConstructSpdyGet(NULL, 0, false, 1, LOWEST, false)); + + MockWrite spdy_writes[] = { + CreateMockWrite(*stream1_syn, 1, ASYNC) + }; + + scoped_ptr<SpdyFrame> + stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); + + scoped_ptr<SpdyFrame> + stream1_body(ConstructSpdyBodyFrame(1, true)); + + scoped_ptr<SpdyFrame> + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.another-origin.com/foo.dat")); + + MockRead spdy_reads[] = { + CreateMockRead(*stream1_reply, 2, ASYNC), + CreateMockRead(*stream2_syn, 3, ASYNC), + CreateMockRead(*stream1_body, 4, ASYNC), + MockRead(ASYNC, reinterpret_cast<const char*>(kPushBodyFrame), + arraysize(kPushBodyFrame), 5), + MockRead(ASYNC, ERR_IO_PENDING, 6), // Force a pause + }; + + scoped_ptr<OrderedSocketData> spdy_data( + new OrderedSocketData( + spdy_reads, arraysize(spdy_reads), + spdy_writes, arraysize(spdy_writes))); + session_deps.socket_factory.AddSocketDataProvider(spdy_data.get()); + // Negotiate SPDY to the proxy + SSLSocketDataProvider proxy(ASYNC, OK); + proxy.SetNextProto(kProtoSPDY21); + session_deps.socket_factory.AddSSLSocketDataProvider(&proxy); + + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + TestCompletionCallback callback; + int rv = trans->Start(&request, callback.callback(), log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + const HttpResponseInfo* response = trans->GetResponseInfo(); + + scoped_ptr<HttpTransaction> push_trans(new HttpNetworkTransaction(session)); + rv = push_trans->Start(&push_request, callback.callback(), log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + const HttpResponseInfo* push_response = push_trans->GetResponseInfo(); + + ASSERT_TRUE(response != NULL); + EXPECT_TRUE(response->headers->IsKeepAlive()); + + EXPECT_EQ(200, response->headers->response_code()); + EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); + + std::string response_data; + rv = ReadTransaction(trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("hello!", response_data); + + // Verify the pushed stream. + EXPECT_TRUE(push_response->headers != NULL); + EXPECT_EQ(200, push_response->headers->response_code()); + + rv = ReadTransaction(push_trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("pushed", response_data); + + trans.reset(); + push_trans.reset(); + session->CloseAllConnections(); +} + // Test HTTPS connections to a site with a bad certificate, going through an // HTTPS proxy TEST_F(HttpNetworkTransactionSpdy21Test, HTTPSBadCertificateViaHttpsProxy) { diff --git a/net/http/http_network_transaction_spdy2_unittest.cc b/net/http/http_network_transaction_spdy2_unittest.cc index 29f8316..0ed4629 100644 --- a/net/http/http_network_transaction_spdy2_unittest.cc +++ b/net/http/http_network_transaction_spdy2_unittest.cc @@ -4883,6 +4883,113 @@ TEST_F(HttpNetworkTransactionSpdy2Test, BasicAuthSpdyProxy) { session->CloseAllConnections(); } +// Test that an explicitly trusted SPDY proxy can push a resource from an +// origin that is different from that of its associated resource. +TEST_F(HttpNetworkTransactionSpdy2Test, CrossOriginProxyPush) { + HttpRequestInfo request; + HttpRequestInfo push_request; + + static const unsigned char kPushBodyFrame[] = { + 0x00, 0x00, 0x00, 0x02, // header, ID + 0x01, 0x00, 0x00, 0x06, // FIN, length + 'p', 'u', 's', 'h', 'e', 'd' // "pushed" + }; + + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + push_request.method = "GET"; + push_request.url = GURL("http://www.another-origin.com/foo.dat"); + + // Enable cross-origin push. + net::SpdySession::set_allow_spdy_proxy_push_across_origins("myproxy:70"); + + // Configure against https proxy server "myproxy:70". + SessionDependencies session_deps( + ProxyService::CreateFixed("https://myproxy:70")); + CapturingBoundNetLog log(CapturingNetLog::kUnbounded); + session_deps.net_log = log.bound().net_log(); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + scoped_ptr<SpdyFrame> + stream1_syn(ConstructSpdyGet(NULL, 0, false, 1, LOWEST, false)); + + MockWrite spdy_writes[] = { + CreateMockWrite(*stream1_syn, 1, ASYNC) + }; + + scoped_ptr<SpdyFrame> + stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); + + scoped_ptr<SpdyFrame> + stream1_body(ConstructSpdyBodyFrame(1, true)); + + scoped_ptr<SpdyFrame> + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.another-origin.com/foo.dat")); + + MockRead spdy_reads[] = { + CreateMockRead(*stream1_reply, 2, ASYNC), + CreateMockRead(*stream2_syn, 3, ASYNC), + CreateMockRead(*stream1_body, 4, ASYNC), + MockRead(ASYNC, reinterpret_cast<const char*>(kPushBodyFrame), + arraysize(kPushBodyFrame), 5), + MockRead(ASYNC, ERR_IO_PENDING, 6), // Force a pause + }; + + scoped_ptr<OrderedSocketData> spdy_data( + new OrderedSocketData( + spdy_reads, arraysize(spdy_reads), + spdy_writes, arraysize(spdy_writes))); + session_deps.socket_factory.AddSocketDataProvider(spdy_data.get()); + // Negotiate SPDY to the proxy + SSLSocketDataProvider proxy(ASYNC, OK); + proxy.SetNextProto(kProtoSPDY2); + session_deps.socket_factory.AddSSLSocketDataProvider(&proxy); + + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + TestCompletionCallback callback; + int rv = trans->Start(&request, callback.callback(), log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + const HttpResponseInfo* response = trans->GetResponseInfo(); + + scoped_ptr<HttpTransaction> push_trans(new HttpNetworkTransaction(session)); + rv = push_trans->Start(&push_request, callback.callback(), log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + const HttpResponseInfo* push_response = push_trans->GetResponseInfo(); + + ASSERT_TRUE(response != NULL); + EXPECT_TRUE(response->headers->IsKeepAlive()); + + EXPECT_EQ(200, response->headers->response_code()); + EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); + + std::string response_data; + rv = ReadTransaction(trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("hello!", response_data); + + // Verify the pushed stream. + EXPECT_TRUE(push_response->headers != NULL); + EXPECT_EQ(200, push_response->headers->response_code()); + + rv = ReadTransaction(push_trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("pushed", response_data); + + trans.reset(); + push_trans.reset(); + session->CloseAllConnections(); +} + // Test HTTPS connections to a site with a bad certificate, going through an // HTTPS proxy TEST_F(HttpNetworkTransactionSpdy2Test, HTTPSBadCertificateViaHttpsProxy) { diff --git a/net/http/http_network_transaction_spdy3_unittest.cc b/net/http/http_network_transaction_spdy3_unittest.cc index d3d6dab..b8779c8 100644 --- a/net/http/http_network_transaction_spdy3_unittest.cc +++ b/net/http/http_network_transaction_spdy3_unittest.cc @@ -4884,6 +4884,113 @@ TEST_F(HttpNetworkTransactionSpdy3Test, BasicAuthSpdyProxy) { session->CloseAllConnections(); } +// Test that an explicitly trusted SPDY proxy can push a resource from an +// origin that is different from that of its associated resource. +TEST_F(HttpNetworkTransactionSpdy3Test, CrossOriginProxyPush) { + HttpRequestInfo request; + HttpRequestInfo push_request; + + static const unsigned char kPushBodyFrame[] = { + 0x00, 0x00, 0x00, 0x02, // header, ID + 0x01, 0x00, 0x00, 0x06, // FIN, length + 'p', 'u', 's', 'h', 'e', 'd' // "pushed" + }; + + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + push_request.method = "GET"; + push_request.url = GURL("http://www.another-origin.com/foo.dat"); + + // Enable cross-origin push. + net::SpdySession::set_allow_spdy_proxy_push_across_origins("myproxy:70"); + + // Configure against https proxy server "myproxy:70". + SessionDependencies session_deps( + ProxyService::CreateFixed("https://myproxy:70")); + CapturingBoundNetLog log(CapturingNetLog::kUnbounded); + session_deps.net_log = log.bound().net_log(); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + scoped_ptr<SpdyFrame> + stream1_syn(ConstructSpdyGet(NULL, 0, false, 1, LOWEST, false)); + + MockWrite spdy_writes[] = { + CreateMockWrite(*stream1_syn, 1, ASYNC) + }; + + scoped_ptr<SpdyFrame> + stream1_reply(ConstructSpdyGetSynReply(NULL, 0, 1)); + + scoped_ptr<SpdyFrame> + stream1_body(ConstructSpdyBodyFrame(1, true)); + + scoped_ptr<SpdyFrame> + stream2_syn(ConstructSpdyPush(NULL, + 0, + 2, + 1, + "http://www.another-origin.com/foo.dat")); + + MockRead spdy_reads[] = { + CreateMockRead(*stream1_reply, 2, ASYNC), + CreateMockRead(*stream2_syn, 3, ASYNC), + CreateMockRead(*stream1_body, 4, ASYNC), + MockRead(ASYNC, reinterpret_cast<const char*>(kPushBodyFrame), + arraysize(kPushBodyFrame), 5), + MockRead(ASYNC, ERR_IO_PENDING, 6), // Force a pause + }; + + scoped_ptr<OrderedSocketData> spdy_data( + new OrderedSocketData( + spdy_reads, arraysize(spdy_reads), + spdy_writes, arraysize(spdy_writes))); + session_deps.socket_factory.AddSocketDataProvider(spdy_data.get()); + // Negotiate SPDY to the proxy + SSLSocketDataProvider proxy(ASYNC, OK); + proxy.SetNextProto(kProtoSPDY3); + session_deps.socket_factory.AddSSLSocketDataProvider(&proxy); + + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + TestCompletionCallback callback; + int rv = trans->Start(&request, callback.callback(), log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + const HttpResponseInfo* response = trans->GetResponseInfo(); + + scoped_ptr<HttpTransaction> push_trans(new HttpNetworkTransaction(session)); + rv = push_trans->Start(&push_request, callback.callback(), log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + const HttpResponseInfo* push_response = push_trans->GetResponseInfo(); + + ASSERT_TRUE(response != NULL); + EXPECT_TRUE(response->headers->IsKeepAlive()); + + EXPECT_EQ(200, response->headers->response_code()); + EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); + + std::string response_data; + rv = ReadTransaction(trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("hello!", response_data); + + // Verify the pushed stream. + EXPECT_TRUE(push_response->headers != NULL); + EXPECT_EQ(200, push_response->headers->response_code()); + + rv = ReadTransaction(push_trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("pushed", response_data); + + trans.reset(); + push_trans.reset(); + session->CloseAllConnections(); +} + // Test HTTPS connections to a site with a bad certificate, going through an // HTTPS proxy TEST_F(HttpNetworkTransactionSpdy3Test, HTTPSBadCertificateViaHttpsProxy) { diff --git a/net/spdy/spdy_network_transaction_spdy21_unittest.cc b/net/spdy/spdy_network_transaction_spdy21_unittest.cc index 16918fe..c81f30a 100644 --- a/net/spdy/spdy_network_transaction_spdy21_unittest.cc +++ b/net/spdy/spdy_network_transaction_spdy21_unittest.cc @@ -5847,6 +5847,11 @@ TEST_P(SpdyNetworkTransactionSpdy21Test, ServerPushCrossOriginCorrectness) { helper.RunPreTestSetup(); helper.AddData(data.get()); + // Enable cross-origin push. Since we are not using a proxy, this should + // not actually enable cross-origin SPDY push. + net::SpdySession::set_allow_spdy_proxy_push_across_origins( + "123.45.67.89:8080"); + HttpNetworkTransaction* trans = helper.trans(); // Start the transaction with basic parameters. diff --git a/net/spdy/spdy_network_transaction_spdy2_unittest.cc b/net/spdy/spdy_network_transaction_spdy2_unittest.cc index 47ea77ca..5e6cf17 100644 --- a/net/spdy/spdy_network_transaction_spdy2_unittest.cc +++ b/net/spdy/spdy_network_transaction_spdy2_unittest.cc @@ -5483,6 +5483,11 @@ TEST_P(SpdyNetworkTransactionSpdy2Test, ServerPushCrossOriginCorrectness) { helper.RunPreTestSetup(); helper.AddData(data.get()); + // Enable cross-origin push. Since we are not using a proxy, this should + // not actually enable cross-origin SPDY push. + net::SpdySession::set_allow_spdy_proxy_push_across_origins( + "123.45.67.89:8080"); + HttpNetworkTransaction* trans = helper.trans(); // Start the transaction with basic parameters. diff --git a/net/spdy/spdy_network_transaction_spdy3_unittest.cc b/net/spdy/spdy_network_transaction_spdy3_unittest.cc index 5c4aa67..cb80d00 100644 --- a/net/spdy/spdy_network_transaction_spdy3_unittest.cc +++ b/net/spdy/spdy_network_transaction_spdy3_unittest.cc @@ -5855,6 +5855,11 @@ TEST_P(SpdyNetworkTransactionSpdy3Test, ServerPushCrossOriginCorrectness) { helper.RunPreTestSetup(); helper.AddData(data.get()); + // Enable cross-origin push. Since we are not using a proxy, this should + // not actually enable cross-origin SPDY push. + net::SpdySession::set_allow_spdy_proxy_push_across_origins( + "123.45.67.89:8080"); + HttpNetworkTransaction* trans = helper.trans(); // Start the transaction with basic parameters. diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 8fce171..3815357 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -296,6 +296,7 @@ NextProto g_default_protocol = kProtoUnknown; size_t g_init_max_concurrent_streams = 10; size_t g_max_concurrent_stream_limit = 256; bool g_enable_ping_based_connection_checking = true; +const char* g_allow_spdy_proxy_push_across_origins = NULL; } // namespace @@ -321,12 +322,18 @@ void SpdySession::set_init_max_concurrent_streams(size_t value) { } // static +void SpdySession::set_allow_spdy_proxy_push_across_origins(const char* value) { + g_allow_spdy_proxy_push_across_origins = value; +} + +// static void SpdySession::ResetStaticSettingsToInit() { // WARNING: These must match the initializers above. g_default_protocol = kProtoUnknown; g_init_max_concurrent_streams = 10; g_max_concurrent_stream_limit = 256; g_enable_ping_based_connection_checking = true; + g_allow_spdy_proxy_push_across_origins = NULL; } SpdySession::SpdySession(const HostPortProxyPair& host_port_proxy_pair, @@ -374,7 +381,11 @@ SpdySession::SpdySession(const HostPortProxyPair& host_port_proxy_pair, trailing_ping_delay_time_( base::TimeDelta::FromSeconds(kTrailingPingDelayTimeSeconds)), hung_interval_( - base::TimeDelta::FromSeconds(kHungIntervalSeconds)) { + base::TimeDelta::FromSeconds(kHungIntervalSeconds)), + allow_spdy_proxy_push_across_origins_( + HostPortPair::FromString( + std::string(g_allow_spdy_proxy_push_across_origins == NULL ? + "" : g_allow_spdy_proxy_push_across_origins))) { DCHECK(HttpStreamFactory::spdy_enabled()); net_log_.BeginEvent( NetLog::TYPE_SPDY_SESSION, @@ -1444,15 +1455,20 @@ void SpdySession::OnSynStream( return; } - scoped_refptr<SpdyStream> associated_stream = - active_streams_[associated_stream_id]; - GURL associated_url(associated_stream->GetUrl()); - if (associated_url.GetOrigin() != gurl.GetOrigin()) { - ResetStream(stream_id, REFUSED_STREAM, - base::StringPrintf( - "Rejected Cross Origin Push Stream %d", - associated_stream_id)); - return; + // Check that the SYN advertises the same origin as its associated stream. + // Bypass this check if and only if this session is with a SPDY proxy that + // is trusted explicitly via the allow_spdy_proxy_push_across_origins switch. + if (!allow_spdy_proxy_push_across_origins_.Equals(host_port_pair())) { + scoped_refptr<SpdyStream> associated_stream = + active_streams_[associated_stream_id]; + GURL associated_url(associated_stream->GetUrl()); + if (associated_url.GetOrigin() != gurl.GetOrigin()) { + ResetStream(stream_id, REFUSED_STREAM, + base::StringPrintf( + "Rejected Cross Origin Push Stream %d", + associated_stream_id)); + return; + } } // There should not be an existing pushed stream with the same path. diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 2fbfd1e..5c7b11e 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -188,6 +188,10 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, // server via SETTINGS. static void set_init_max_concurrent_streams(size_t value); + // Allow a SPDY proxy to push resources from origins that are different from + // those of their associated streams. + static void set_allow_spdy_proxy_push_across_origins(const char* proxy); + // Send WINDOW_UPDATE frame, called by a stream whenever receive window // size is increased. void SendWindowUpdate(SpdyStreamId stream_id, int32 delta_window_size); @@ -639,6 +643,10 @@ class NET_EXPORT SpdySession : public base::RefCounted<SpdySession>, // (of any form), while there is a ping in flight, before we declare the // connection to be hung. base::TimeDelta hung_interval_; + + // Allows a proxy to push a resource that has an origin that is different + // from its associated url. + HostPortPair allow_spdy_proxy_push_across_origins_; }; class NetLogSpdySynParameter : public NetLog::EventParameters { |