diff options
author | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-12 01:39:55 +0000 |
---|---|---|
committer | willchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-12 01:39:55 +0000 |
commit | 2d67286973ae47301854b37eb1f142ac25d5e132 (patch) | |
tree | 468c5dfd91b57d6192cf87c34bc197ea415eca2c /net | |
parent | c942d18826443fe7ebdde476265ab0cf026d7527 (diff) | |
download | chromium_src-2d67286973ae47301854b37eb1f142ac25d5e132.zip chromium_src-2d67286973ae47301854b37eb1f142ac25d5e132.tar.gz chromium_src-2d67286973ae47301854b37eb1f142ac25d5e132.tar.bz2 |
Reland rest of r77399.
I had temporarily reverted it so I could break it up into 2 commits, so the first could be merged to 696. This is part 2.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6684019
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77908 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_network_transaction.cc | 6 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 282 | ||||
-rw-r--r-- | net/http/http_response_info.cc | 12 | ||||
-rw-r--r-- | net/http/http_response_info.h | 4 | ||||
-rw-r--r-- | net/http/http_stream_factory.h | 7 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl.cc | 127 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl.h | 12 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_job.cc | 360 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_job.h | 106 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_request.cc | 34 | ||||
-rw-r--r-- | net/http/http_stream_factory_impl_request.h | 5 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base.cc | 5 | ||||
-rw-r--r-- | net/socket/client_socket_pool_base.h | 2 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 148 | ||||
-rw-r--r-- | net/url_request/url_request.h | 6 |
15 files changed, 659 insertions, 457 deletions
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 6b30fd6..4bd867d 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -20,6 +20,7 @@ #include "build/build_config.h" #include "googleurl/src/gurl.h" #include "net/base/auth.h" +#include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -371,8 +372,6 @@ void HttpNetworkTransaction::OnStreamReady(const SSLConfig& used_ssl_config, stream_.reset(stream); ssl_config_ = used_ssl_config; proxy_info_ = used_proxy_info; - response_.was_alternate_protocol_available = - stream_request_->was_alternate_protocol_available(); response_.was_npn_negotiated = stream_request_->was_npn_negotiated(); response_.was_fetched_via_spdy = stream_request_->using_spdy(); response_.was_fetched_via_proxy = !proxy_info_.is_direct(); @@ -1070,7 +1069,8 @@ int HttpNetworkTransaction::HandleSSLHandshakeError(int error) { // This could be a TLS-intolerant server, an SSL 3.0 server that // chose a TLS-only cipher suite or a server with buggy DEFLATE // support. Turn off TLS 1.0, DEFLATE support and retry. - session_->http_stream_factory()->AddTLSIntolerantServer(request_->url); + session_->http_stream_factory()->AddTLSIntolerantServer( + HostPortPair::FromURL(request_->url)); ResetConnectionAndRequestForResend(); error = OK; } diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index ecd6132..b238f57 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -6437,7 +6437,6 @@ TEST_F(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) { EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); EXPECT_FALSE(response->was_fetched_via_spdy); EXPECT_FALSE(response->was_npn_negotiated); - EXPECT_FALSE(response->was_alternate_protocol_available); std::string response_data; ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); @@ -6478,11 +6477,6 @@ TEST_F(HttpNetworkTransactionTest, MarkBrokenAlternateProtocol) { data_reads, arraysize(data_reads), NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&second_data); - // TODO(willchan): Delete this extra data provider. It's necessary due to a - // ClientSocketPoolBaseHelper bug that starts up too many ConnectJobs: - // http://crbug.com/37454. - session_deps.socket_factory.AddSocketDataProvider(&second_data); - TestCompletionCallback callback; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); @@ -6517,59 +6511,6 @@ TEST_F(HttpNetworkTransactionTest, MarkBrokenAlternateProtocol) { HttpStreamFactory::set_use_alternate_protocols(false); } -// TODO(willchan): Redo this test to use TLS/NPN=>SPDY. Currently, the code -// says that it does SPDY, but it just does the TLS handshake, but the NPN -// response does not indicate SPDY, so we just do standard HTTPS over the port. -// We should add code such that we don't fallback to HTTPS, but fallback to HTTP -// on the original port. -// TEST_F(HttpNetworkTransactionTest, UseAlternateProtocol) { -// SessionDependencies session_deps; -// -// HttpRequestInfo request; -// request.method = "GET"; -// request.url = GURL("http://www.google.com/"); -// request.load_flags = 0; -// -// MockRead data_reads[] = { -// MockRead("HTTP/1.1 200 OK\r\n\r\n"), -// MockRead("hello world"), -// MockRead(true, OK), -// }; -// StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); -// session_deps.socket_factory.AddSocketDataProvider(&data); -// -// SSLSocketDataProvider ssl(true, OK); -// session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); -// -// TestCompletionCallback callback; -// -// scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); -// -// HostPortPair http_host_port_pair; -// http_host_port_pair.host = "www.google.com"; -// http_host_port_pair.port = 80; -// HttpAlternateProtocols* alternate_protocols = -// session->mutable_alternate_protocols(); -// alternate_protocols->SetAlternateProtocolFor( -// http_host_port_pair, 1234 /* port is ignored */, -// HttpAlternateProtocols::NPN_SPDY_1); -// -// scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); -// -// int rv = trans->Start(&request, &callback, BoundNetLog()); -// EXPECT_EQ(ERR_IO_PENDING, rv); -// EXPECT_EQ(OK, callback.WaitForResult()); -// -// 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("hello world", response_data); -// } - TEST_F(HttpNetworkTransactionTest, FailNpnSpdyAndFallback) { HttpStreamFactory::set_use_alternate_protocols(true); HttpStreamFactory::set_next_protos(kExpectedNPNString); @@ -6669,6 +6610,14 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { spdy_writes, arraysize(spdy_writes))); session_deps.socket_factory.AddSocketDataProvider(spdy_data); + MockConnect never_finishing_connect(false, ERR_IO_PENDING); + StaticSocketDataProvider hanging_non_alternate_protocol_socket( + NULL, 0, NULL, 0); + hanging_non_alternate_protocol_socket.set_connect_data( + never_finishing_connect); + session_deps.socket_factory.AddSocketDataProvider( + &hanging_non_alternate_protocol_socket); + TestCompletionCallback callback; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); @@ -6699,7 +6648,6 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); EXPECT_TRUE(response->was_fetched_via_spdy); EXPECT_TRUE(response->was_npn_negotiated); - EXPECT_TRUE(response->was_alternate_protocol_available); ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); EXPECT_EQ("hello!", response_data); @@ -6708,6 +6656,200 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { HttpStreamFactory::set_use_alternate_protocols(false); } +TEST_F(HttpNetworkTransactionTest, AlternateProtocolWithSpdyLateBinding) { + HttpStreamFactory::set_use_alternate_protocols(true); + HttpStreamFactory::set_next_protos(kExpectedNPNString); + SessionDependencies session_deps; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(kAlternateProtocolHttpHeader), + MockRead("hello world"), + MockRead(true, OK), + }; + + StaticSocketDataProvider first_transaction( + data_reads, arraysize(data_reads), NULL, 0); + // Socket 1 is the HTTP transaction with the Alternate-Protocol header. + session_deps.socket_factory.AddSocketDataProvider(&first_transaction); + + MockConnect never_finishing_connect(false, ERR_IO_PENDING); + StaticSocketDataProvider hanging_socket( + NULL, 0, NULL, 0); + hanging_socket.set_connect_data(never_finishing_connect); + // Socket 2 and 3 are the hanging Alternate-Protocol and + // non-Alternate-Protocol jobs from the 2nd transaction. + session_deps.socket_factory.AddSocketDataProvider(&hanging_socket); + session_deps.socket_factory.AddSocketDataProvider(&hanging_socket); + + 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); + + scoped_ptr<spdy::SpdyFrame> req1(ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); + scoped_ptr<spdy::SpdyFrame> req2(ConstructSpdyGet(NULL, 0, false, 3, LOWEST)); + MockWrite spdy_writes[] = { + CreateMockWrite(*req1), + CreateMockWrite(*req2), + }; + scoped_ptr<spdy::SpdyFrame> resp1(ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<spdy::SpdyFrame> data1(ConstructSpdyBodyFrame(1, true)); + scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 3)); + scoped_ptr<spdy::SpdyFrame> data2(ConstructSpdyBodyFrame(3, true)); + MockRead spdy_reads[] = { + CreateMockRead(*resp1), + CreateMockRead(*data1), + CreateMockRead(*resp2), + CreateMockRead(*data2), + MockRead(true, 0, 0), + }; + + scoped_refptr<DelayedSocketData> spdy_data( + new DelayedSocketData( + 2, // wait for writes to finish before reading. + spdy_reads, arraysize(spdy_reads), + spdy_writes, arraysize(spdy_writes))); + // Socket 4 is the successful Alternate-Protocol for transaction 3. + session_deps.socket_factory.AddSocketDataProvider(spdy_data); + + // Socket 5 is the unsuccessful non-Alternate-Protocol for transaction 3. + session_deps.socket_factory.AddSocketDataProvider(&hanging_socket); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + TestCompletionCallback callback1; + HttpNetworkTransaction trans1(session); + + int rv = trans1.Start(&request, &callback1, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback1.WaitForResult()); + + const HttpResponseInfo* response = trans1.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(&trans1, &response_data)); + EXPECT_EQ("hello world", response_data); + + TestCompletionCallback callback2; + HttpNetworkTransaction trans2(session); + rv = trans2.Start(&request, &callback2, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + TestCompletionCallback callback3; + HttpNetworkTransaction trans3(session); + rv = trans3.Start(&request, &callback3, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + EXPECT_EQ(OK, callback2.WaitForResult()); + EXPECT_EQ(OK, callback3.WaitForResult()); + + response = trans2.GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + EXPECT_TRUE(response->was_fetched_via_spdy); + EXPECT_TRUE(response->was_npn_negotiated); + ASSERT_EQ(OK, ReadTransaction(&trans2, &response_data)); + EXPECT_EQ("hello!", response_data); + + response = trans3.GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + EXPECT_TRUE(response->was_fetched_via_spdy); + EXPECT_TRUE(response->was_npn_negotiated); + ASSERT_EQ(OK, ReadTransaction(&trans3, &response_data)); + EXPECT_EQ("hello!", response_data); + + HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_use_alternate_protocols(false); +} + +TEST_F(HttpNetworkTransactionTest, StallAlternateProtocolForNpnSpdy) { + HttpStreamFactory::set_use_alternate_protocols(true); + HttpStreamFactory::set_next_protos(kExpectedNPNString); + SessionDependencies session_deps; + + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead(kAlternateProtocolHttpHeader), + MockRead("hello world"), + MockRead(true, OK), + }; + + StaticSocketDataProvider first_transaction( + data_reads, arraysize(data_reads), NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&first_transaction); + + 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); + + MockConnect never_finishing_connect(false, ERR_IO_PENDING); + StaticSocketDataProvider hanging_alternate_protocol_socket( + NULL, 0, NULL, 0); + hanging_alternate_protocol_socket.set_connect_data( + never_finishing_connect); + session_deps.socket_factory.AddSocketDataProvider( + &hanging_alternate_protocol_socket); + + // 2nd request is just a copy of the first one, over HTTP again. + session_deps.socket_factory.AddSocketDataProvider(&first_transaction); + + TestCompletionCallback callback; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); + + int rv = trans->Start(&request, &callback, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + 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("hello world", response_data); + + trans.reset(new HttpNetworkTransaction(session)); + + rv = trans->Start(&request, &callback, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + EXPECT_EQ(OK, callback.WaitForResult()); + + response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + EXPECT_FALSE(response->was_fetched_via_spdy); + EXPECT_FALSE(response->was_npn_negotiated); + + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ("hello world", response_data); + + HttpStreamFactory::set_next_protos(""); + HttpStreamFactory::set_use_alternate_protocols(false); +} + class CapturingProxyResolver : public ProxyResolver { public: CapturingProxyResolver() : ProxyResolver(false /* expects_pac_bytes */) {} @@ -6807,6 +6949,14 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) { spdy_writes, arraysize(spdy_writes))); session_deps.socket_factory.AddSocketDataProvider(spdy_data); + MockConnect never_finishing_connect(false, ERR_IO_PENDING); + StaticSocketDataProvider hanging_non_alternate_protocol_socket( + NULL, 0, NULL, 0); + hanging_non_alternate_protocol_socket.set_connect_data( + never_finishing_connect); + session_deps.socket_factory.AddSocketDataProvider( + &hanging_non_alternate_protocol_socket); + TestCompletionCallback callback; scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); @@ -6842,7 +6992,7 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) { ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); EXPECT_EQ("hello!", response_data); - ASSERT_EQ(2u, capturing_proxy_resolver->resolved().size()); + ASSERT_EQ(3u, capturing_proxy_resolver->resolved().size()); EXPECT_EQ("http://www.google.com/", capturing_proxy_resolver->resolved()[0].spec()); EXPECT_EQ("https://www.google.com/", @@ -6958,7 +7108,6 @@ TEST_F(HttpNetworkTransactionTest, EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); EXPECT_TRUE(response->was_fetched_via_spdy); EXPECT_TRUE(response->was_npn_negotiated); - EXPECT_TRUE(response->was_alternate_protocol_available); ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); EXPECT_EQ("hello!", response_data); @@ -7729,7 +7878,6 @@ TEST_F(HttpNetworkTransactionTest, NpnWithHttpOverSSL) { EXPECT_FALSE(response->was_fetched_via_spdy); EXPECT_TRUE(response->was_npn_negotiated); - EXPECT_FALSE(response->was_alternate_protocol_available); HttpStreamFactory::set_next_protos(""); HttpStreamFactory::set_use_alternate_protocols(false); @@ -7885,9 +8033,17 @@ TEST_F(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { ssl.next_proto = "spdy/2"; ssl.was_npn_negotiated = true; + MockConnect never_finishing_connect(false, ERR_IO_PENDING); + StaticSocketDataProvider hanging_non_alternate_protocol_socket( + NULL, 0, NULL, 0); + hanging_non_alternate_protocol_socket.set_connect_data( + never_finishing_connect); + session_deps.socket_factory.AddSocketDataProvider(&data_1); session_deps.socket_factory.AddSocketDataProvider(data_2.get()); session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); + session_deps.socket_factory.AddSocketDataProvider( + &hanging_non_alternate_protocol_socket); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); // First round should work and provide the Alternate-Protocol state. diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc index 9b3444a..fd76462 100644 --- a/net/http/http_response_info.cc +++ b/net/http/http_response_info.cc @@ -52,10 +52,6 @@ enum { // This bit is set if the request was fetched via an explicit proxy. RESPONSE_INFO_WAS_PROXY = 1 << 15, - // This bit is set if response could use alternate protocol. However, browser - // will ingore the alternate protocol if spdy is not enabled. - RESPONSE_INFO_WAS_ALTERNATE_PROTOCOL_AVAILABLE = 1 << 16, - // TODO(darin): Add other bits to indicate alternate request methods. // For now, we don't support storing those. }; @@ -64,7 +60,6 @@ HttpResponseInfo::HttpResponseInfo() : was_cached(false), was_fetched_via_spdy(false), was_npn_negotiated(false), - was_alternate_protocol_available(false), was_fetched_via_proxy(false) { } @@ -72,7 +67,6 @@ HttpResponseInfo::HttpResponseInfo(const HttpResponseInfo& rhs) : was_cached(rhs.was_cached), was_fetched_via_spdy(rhs.was_fetched_via_spdy), was_npn_negotiated(rhs.was_npn_negotiated), - was_alternate_protocol_available(rhs.was_alternate_protocol_available), was_fetched_via_proxy(rhs.was_fetched_via_proxy), socket_address(rhs.socket_address), request_time(rhs.request_time), @@ -92,7 +86,6 @@ HttpResponseInfo& HttpResponseInfo::operator=(const HttpResponseInfo& rhs) { was_cached = rhs.was_cached; was_fetched_via_spdy = rhs.was_fetched_via_spdy; was_npn_negotiated = rhs.was_npn_negotiated; - was_alternate_protocol_available = rhs.was_alternate_protocol_available; was_fetched_via_proxy = rhs.was_fetched_via_proxy; socket_address = rhs.socket_address; request_time = rhs.request_time; @@ -176,9 +169,6 @@ bool HttpResponseInfo::InitFromPickle(const Pickle& pickle, was_npn_negotiated = (flags & RESPONSE_INFO_WAS_NPN) != 0; - was_alternate_protocol_available = - (flags & RESPONSE_INFO_WAS_ALTERNATE_PROTOCOL_AVAILABLE) != 0; - was_fetched_via_proxy = (flags & RESPONSE_INFO_WAS_PROXY) != 0; *response_truncated = (flags & RESPONSE_INFO_TRUNCATED) ? true : false; @@ -205,8 +195,6 @@ void HttpResponseInfo::Persist(Pickle* pickle, flags |= RESPONSE_INFO_WAS_SPDY; if (was_npn_negotiated) flags |= RESPONSE_INFO_WAS_NPN; - if (was_alternate_protocol_available) - flags |= RESPONSE_INFO_WAS_ALTERNATE_PROTOCOL_AVAILABLE; if (was_fetched_via_proxy) flags |= RESPONSE_INFO_WAS_PROXY; diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h index e13f288..c925aa9 100644 --- a/net/http/http_response_info.h +++ b/net/http/http_response_info.h @@ -52,10 +52,6 @@ class HttpResponseInfo { // True if the npn was negotiated for this request. bool was_npn_negotiated; - // True if response could use alternate protocol. However, browser - // will ingore the alternate protocol if spdy is not enabled. - bool was_alternate_protocol_available; - // True if the request was fetched via an explicit proxy. The proxy could // be any type of proxy, HTTP or SOCKS. Note, we do not know if a // transparent proxy may have been involved. diff --git a/net/http/http_stream_factory.h b/net/http/http_stream_factory.h index 3d3bada..9268746 100644 --- a/net/http/http_stream_factory.h +++ b/net/http/http_stream_factory.h @@ -135,9 +135,6 @@ class HttpStreamRequest { // Returns the LoadState for the request. virtual LoadState GetLoadState() const = 0; - // Returns true if an AlternateProtocol for this request was available. - virtual bool was_alternate_protocol_available() const = 0; - // Returns true if TLS/NPN was negotiated for this stream. virtual bool was_npn_negotiated() const = 0; @@ -171,8 +168,8 @@ class HttpStreamFactory { const SSLConfig& ssl_config, const BoundNetLog& net_log) = 0; - virtual void AddTLSIntolerantServer(const GURL& url) = 0; - virtual bool IsTLSIntolerantServer(const GURL& url) const = 0; + virtual void AddTLSIntolerantServer(const HostPortPair& server) = 0; + virtual bool IsTLSIntolerantServer(const HostPortPair& server) const = 0; // Static settings static GURL ApplyHostMappingRules(const GURL& url, HostPortPair* endpoint); diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc index c7b0c93..8a1ee60 100644 --- a/net/http/http_stream_factory_impl.cc +++ b/net/http/http_stream_factory_impl.cc @@ -4,6 +4,7 @@ #include "net/http/http_stream_factory_impl.h" +#include "base/string_number_conversions.h" #include "base/stl_util-inl.h" #include "googleurl/src/gurl.h" #include "net/base/net_log.h" @@ -15,6 +16,34 @@ namespace net { +namespace { + +bool HasSpdyExclusion(const HostPortPair& endpoint) { + std::list<HostPortPair>* exclusions = + HttpStreamFactory::forced_spdy_exclusions(); + if (!exclusions) + return false; + + std::list<HostPortPair>::const_iterator it; + for (it = exclusions->begin(); it != exclusions->end(); it++) + if (it->Equals(endpoint)) + return true; + return false; +} + +GURL UpgradeUrlToHttps(const GURL& original_url) { + GURL::Replacements replacements; + // new_sheme and new_port need to be in scope here because GURL::Replacements + // references the memory contained by them directly. + const std::string new_scheme = "https"; + const std::string new_port = base::IntToString(443); + replacements.SetSchemeStr(new_scheme); + replacements.SetPortStr(new_port); + return original_url.ReplaceComponents(replacements); +} + +} // namespace + HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session) : session_(session) {} @@ -23,6 +52,11 @@ HttpStreamFactoryImpl::~HttpStreamFactoryImpl() { DCHECK(spdy_session_request_map_.empty()); std::set<const Job*> tmp_job_set; + tmp_job_set.swap(orphaned_job_set_); + STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end()); + DCHECK(orphaned_job_set_.empty()); + + tmp_job_set.clear(); tmp_job_set.swap(preconnect_job_set_); STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end()); DCHECK(preconnect_job_set_.empty()); @@ -33,10 +67,34 @@ HttpStreamRequest* HttpStreamFactoryImpl::RequestStream( const SSLConfig& ssl_config, HttpStreamRequest::Delegate* delegate, const BoundNetLog& net_log) { - Job* job = new Job(this, session_); Request* request = new Request(request_info.url, this, delegate, net_log); + + GURL alternate_url; + bool has_alternate_protocol = + GetAlternateProtocolRequestFor(request_info.url, &alternate_url); + Job* alternate_job = NULL; + if (has_alternate_protocol) { + HttpRequestInfo alternate_request_info = request_info; + alternate_request_info.url = alternate_url; + alternate_job = + new Job(this, session_, alternate_request_info, ssl_config, net_log); + request->AttachJob(alternate_job); + alternate_job->MarkAsAlternate(request_info.url); + } + + Job* job = new Job(this, session_, request_info, ssl_config, net_log); request->AttachJob(job); - job->Start(request, request_info, ssl_config, net_log); + if (alternate_job) { + job->WaitFor(alternate_job); + // Make sure to wait until we call WaitFor(), before starting + // |alternate_job|, otherwise |alternate_job| will not notify |job| + // appropriately. + alternate_job->Start(request); + } + // Even if |alternate_job| has already finished, it won't have notified the + // request yet, since we defer that to the next iteration of the MessageLoop, + // so starting |job| is always safe. + job->Start(request); return request; } @@ -45,17 +103,66 @@ void HttpStreamFactoryImpl::PreconnectStreams( const HttpRequestInfo& request_info, const SSLConfig& ssl_config, const BoundNetLog& net_log) { - Job* job = new Job(this, session_); + GURL alternate_url; + bool has_alternate_protocol = + GetAlternateProtocolRequestFor(request_info.url, &alternate_url); + Job* job = NULL; + if (has_alternate_protocol) { + HttpRequestInfo alternate_request_info = request_info; + alternate_request_info.url = alternate_url; + job = new Job(this, session_, alternate_request_info, ssl_config, net_log); + job->MarkAsAlternate(request_info.url); + } else { + job = new Job(this, session_, request_info, ssl_config, net_log); + } preconnect_job_set_.insert(job); - job->Preconnect(num_streams, request_info, ssl_config, net_log); + job->Preconnect(num_streams); +} + +void HttpStreamFactoryImpl::AddTLSIntolerantServer(const HostPortPair& server) { + tls_intolerant_servers_.insert(server); } -void HttpStreamFactoryImpl::AddTLSIntolerantServer(const GURL& url) { - tls_intolerant_servers_.insert(GetHostAndPort(url)); +bool HttpStreamFactoryImpl::IsTLSIntolerantServer( + const HostPortPair& server) const { + return ContainsKey(tls_intolerant_servers_, server); } -bool HttpStreamFactoryImpl::IsTLSIntolerantServer(const GURL& url) const { - return ContainsKey(tls_intolerant_servers_, GetHostAndPort(url)); +bool HttpStreamFactoryImpl::GetAlternateProtocolRequestFor( + const GURL& original_url, + GURL* alternate_url) const { + if (!spdy_enabled()) + return false; + + if (!use_alternate_protocols()) + return false; + + HostPortPair origin = HostPortPair(original_url.HostNoBrackets(), + original_url.EffectiveIntPort()); + + const HttpAlternateProtocols& alternate_protocols = + session_->alternate_protocols(); + if (!alternate_protocols.HasAlternateProtocolFor(origin)) + return false; + + HttpAlternateProtocols::PortProtocolPair alternate = + alternate_protocols.GetAlternateProtocolFor(origin); + if (alternate.protocol == HttpAlternateProtocols::BROKEN) + return false; + + DCHECK_LE(HttpAlternateProtocols::NPN_SPDY_1, alternate.protocol); + DCHECK_GT(HttpAlternateProtocols::NUM_ALTERNATE_PROTOCOLS, + alternate.protocol); + + if (alternate.protocol != HttpAlternateProtocols::NPN_SPDY_2) + return false; + + origin.set_port(alternate.port); + if (HasSpdyExclusion(origin)) + return false; + + *alternate_url = UpgradeUrlToHttps(original_url); + return true; } void HttpStreamFactoryImpl::OrphanJob(Job* job, const Request* request) { @@ -74,7 +181,6 @@ void HttpStreamFactoryImpl::OnSpdySessionReady( bool direct, const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, - bool was_alternate_protocol_available, bool was_npn_negotiated, bool using_spdy, const NetLog::Source& source) { @@ -91,8 +197,7 @@ void HttpStreamFactoryImpl::OnSpdySessionReady( if (!ContainsKey(spdy_session_request_map_, spdy_session_key)) break; Request* request = *spdy_session_request_map_[spdy_session_key].begin(); - request->Complete(was_alternate_protocol_available, - was_npn_negotiated, + request->Complete(was_npn_negotiated, using_spdy, source); bool use_relative_url = direct || request->url().SchemeIs("https"); diff --git a/net/http/http_stream_factory_impl.h b/net/http/http_stream_factory_impl.h index 4bdbfca..b43bf13 100644 --- a/net/http/http_stream_factory_impl.h +++ b/net/http/http_stream_factory_impl.h @@ -7,9 +7,9 @@ #include <map> #include <set> -#include <string> #include "base/ref_counted.h" +#include "net/base/host_port_pair.h" #include "net/http/http_stream_factory.h" #include "net/base/net_log.h" #include "net/proxy/proxy_server.h" @@ -35,8 +35,8 @@ class HttpStreamFactoryImpl : public HttpStreamFactory { const HttpRequestInfo& info, const SSLConfig& ssl_config, const BoundNetLog& net_log); - virtual void AddTLSIntolerantServer(const GURL& url); - virtual bool IsTLSIntolerantServer(const GURL& url) const; + virtual void AddTLSIntolerantServer(const HostPortPair& server); + virtual bool IsTLSIntolerantServer(const HostPortPair& server) const; private: class Request; @@ -45,6 +45,9 @@ class HttpStreamFactoryImpl : public HttpStreamFactory { typedef std::set<Request*> RequestSet; typedef std::map<HostPortProxyPair, RequestSet> SpdySessionRequestMap; + bool GetAlternateProtocolRequestFor(const GURL& original_url, + GURL* alternate_url) const; + // Detaches |job| from |request|. void OrphanJob(Job* job, const Request* request); @@ -55,7 +58,6 @@ class HttpStreamFactoryImpl : public HttpStreamFactory { bool direct, const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, - bool was_alternate_protocol_available, bool was_npn_negotiated, bool using_spdy, const NetLog::Source& source); @@ -76,7 +78,7 @@ class HttpStreamFactoryImpl : public HttpStreamFactory { HttpNetworkSession* const session_; - std::set<std::string> tls_intolerant_servers_; + std::set<HostPortPair> tls_intolerant_servers_; // All Requests are handed out to clients. By the time HttpStreamFactoryImpl // is destroyed, all Requests should be deleted (which should remove them from diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc index f8d8a9e..5e65d31 100644 --- a/net/http/http_stream_factory_impl_job.cc +++ b/net/http/http_stream_factory_impl_job.cc @@ -6,7 +6,6 @@ #include "base/logging.h" #include "base/stl_util-inl.h" -#include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/stringprintf.h" #include "base/values.h" @@ -34,46 +33,38 @@ namespace net { namespace { -GURL UpgradeUrlToHttps(const GURL& original_url) { - GURL::Replacements replacements; - // new_sheme and new_port need to be in scope here because GURL::Replacements - // references the memory contained by them directly. - const std::string new_scheme = "https"; - const std::string new_port = base::IntToString(443); - replacements.SetSchemeStr(new_scheme); - replacements.SetPortStr(new_port); - return original_url.ReplaceComponents(replacements); -} - } // namespace HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl* stream_factory, - HttpNetworkSession* session) + HttpNetworkSession* session, + const HttpRequestInfo& request_info, + const SSLConfig& ssl_config, + const BoundNetLog& net_log) : request_(NULL), + request_info_(request_info), + ssl_config_(ssl_config), + net_log_(BoundNetLog::Make(net_log.net_log(), + NetLog::SOURCE_HTTP_STREAM_JOB)), ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(this, &Job::OnIOComplete)), connection_(new ClientSocketHandle), session_(session), stream_factory_(stream_factory), next_state_(STATE_NONE), pac_request_(NULL), + blocking_job_(NULL), + dependent_job_(NULL), using_ssl_(false), using_spdy_(false), force_spdy_always_(HttpStreamFactory::force_spdy_always()), force_spdy_over_ssl_(HttpStreamFactory::force_spdy_over_ssl()), spdy_certificate_error_(OK), - alternate_protocol_(HttpAlternateProtocols::UNINITIALIZED), establishing_tunnel_(false), - was_alternate_protocol_available_(false), was_npn_negotiated_(false), num_streams_(0), spdy_session_direct_(false), ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { DCHECK(stream_factory); DCHECK(session); - if (HttpStreamFactory::use_alternate_protocols()) - alternate_protocol_mode_ = kUnspecified; - else - alternate_protocol_mode_ = kDoNotUseAlternateProtocol; } HttpStreamFactoryImpl::Job::~Job() { @@ -95,22 +86,16 @@ HttpStreamFactoryImpl::Job::~Job() { stream_->Close(true /* not reusable */); } -void HttpStreamFactoryImpl::Job::Start(Request* request, - const HttpRequestInfo& request_info, - const SSLConfig& ssl_config, - const BoundNetLog& net_log) { +void HttpStreamFactoryImpl::Job::Start(Request* request) { DCHECK(request); request_ = request; - StartInternal(request_info, ssl_config, net_log); + StartInternal(); } -int HttpStreamFactoryImpl::Job::Preconnect(int num_streams, - const HttpRequestInfo& request_info, - const SSLConfig& ssl_config, - const BoundNetLog& net_log) { +int HttpStreamFactoryImpl::Job::Preconnect(int num_streams) { DCHECK_GT(num_streams, 0); num_streams_ = num_streams; - return StartInternal(request_info, ssl_config, net_log); + return StartInternal(); } int HttpStreamFactoryImpl::Job::RestartTunnelWithProxyAuth( @@ -134,13 +119,45 @@ LoadState HttpStreamFactoryImpl::Job::GetLoadState() const { } } +void HttpStreamFactoryImpl::Job::MarkAsAlternate(const GURL& original_url) { + DCHECK(!original_url_.get()); + original_url_.reset(new GURL(original_url)); +} + +void HttpStreamFactoryImpl::Job::WaitFor(Job* job) { + DCHECK_EQ(STATE_NONE, next_state_); + DCHECK_EQ(STATE_NONE, job->next_state_); + DCHECK(!blocking_job_); + DCHECK(!job->dependent_job_); + blocking_job_ = job; + job->dependent_job_ = this; +} + +void HttpStreamFactoryImpl::Job::Resume(Job* job) { + DCHECK_EQ(blocking_job_, job); + blocking_job_ = NULL; + + // We know we're blocked if the next_state_ is STATE_WAIT_FOR_JOB_COMPLETE. + // Unblock |this|. + if (next_state_ == STATE_WAIT_FOR_JOB_COMPLETE) { + MessageLoop::current()->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &HttpStreamFactoryImpl::Job::OnIOComplete, OK)); + } +} + void HttpStreamFactoryImpl::Job::Orphan(const Request* request) { DCHECK_EQ(request_, request); request_ = NULL; -} - -bool HttpStreamFactoryImpl::Job::was_alternate_protocol_available() const { - return was_alternate_protocol_available_; + // We've been orphaned, but there's a job we're blocked on. Don't bother + // racing, just cancel ourself. + if (blocking_job_) { + DCHECK(blocking_job_->dependent_job_); + blocking_job_->dependent_job_ = NULL; + blocking_job_ = NULL; + stream_factory_->OnOrphanedJobComplete(this); + } } bool HttpStreamFactoryImpl::Job::was_npn_negotiated() const { @@ -174,8 +191,7 @@ void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() { if (IsOrphaned()) { stream_factory_->OnOrphanedJobComplete(this); } else { - request_->Complete(was_alternate_protocol_available(), - was_npn_negotiated(), + request_->Complete(was_npn_negotiated(), using_spdy(), net_log_.source()); request_->OnStreamReady(this, ssl_config_, proxy_info_, stream_.release()); @@ -193,8 +209,7 @@ void HttpStreamFactoryImpl::Job::OnSpdySessionReadyCallback() { if (IsOrphaned()) { stream_factory_->OnSpdySessionReady( spdy_session, spdy_session_direct_, ssl_config_, proxy_info_, - was_alternate_protocol_available(), was_npn_negotiated(), - using_spdy(), net_log_.source()); + was_npn_negotiated(), using_spdy(), net_log_.source()); stream_factory_->OnOrphanedJobComplete(this); } else { request_->OnSpdySessionReady(this, spdy_session, spdy_session_direct_); @@ -260,8 +275,7 @@ void HttpStreamFactoryImpl::Job::OnPreconnectsComplete() { if (new_spdy_session_) { stream_factory_->OnSpdySessionReady( new_spdy_session_, spdy_session_direct_, ssl_config_, - proxy_info_, was_alternate_protocol_available(), - was_npn_negotiated(), using_spdy(), net_log_.source()); + proxy_info_, was_npn_negotiated(), using_spdy(), net_log_.source()); } stream_factory_->OnPreconnectsComplete(this); // |this| may be deleted after this call. @@ -385,6 +399,13 @@ int HttpStreamFactoryImpl::Job::DoLoop(int result) { case STATE_RESOLVE_PROXY_COMPLETE: rv = DoResolveProxyComplete(rv); break; + case STATE_WAIT_FOR_JOB: + DCHECK_EQ(OK, rv); + rv = DoWaitForJob(); + break; + case STATE_WAIT_FOR_JOB_COMPLETE: + rv = DoWaitForJobComplete(rv); + break; case STATE_INIT_CONNECTION: DCHECK_EQ(OK, rv); rv = DoInitConnection(); @@ -418,18 +439,11 @@ int HttpStreamFactoryImpl::Job::DoLoop(int result) { return rv; } -int HttpStreamFactoryImpl::Job::StartInternal( - const HttpRequestInfo& request_info, - const SSLConfig& ssl_config, - const BoundNetLog& net_log) { +int HttpStreamFactoryImpl::Job::StartInternal() { CHECK_EQ(STATE_NONE, next_state_); - request_info_ = request_info; - ssl_config_ = ssl_config; - net_log_ = BoundNetLog::Make(net_log.net_log(), - NetLog::SOURCE_HTTP_STREAM_JOB); net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_JOB, make_scoped_refptr(new NetLogStringParameter( - "url", request_info.url.GetOrigin().spec()))); + "url", request_info_.url.GetOrigin().spec()))); next_state_ = STATE_RESOLVE_PROXY; int rv = RunLoop(OK); DCHECK_EQ(ERR_IO_PENDING, rv); @@ -441,40 +455,8 @@ int HttpStreamFactoryImpl::Job::DoResolveProxy() { next_state_ = STATE_RESOLVE_PROXY_COMPLETE; - // |endpoint_| indicates the final destination endpoint. - endpoint_ = HostPortPair(request_info_.url.HostNoBrackets(), - request_info_.url.EffectiveIntPort()); - - // Extra URL we might be attempting to resolve to. - GURL alternate_endpoint_url = request_info_.url; - - // Tracks whether we are using |request_.url| or |alternate_endpoint_url|. - const GURL *curr_endpoint_url = &request_info_.url; - - alternate_endpoint_url = - HttpStreamFactory::ApplyHostMappingRules( - alternate_endpoint_url, &endpoint_); - - const HttpAlternateProtocols& alternate_protocols = - session_->alternate_protocols(); - if (HttpStreamFactory::spdy_enabled() && - alternate_protocols.HasAlternateProtocolFor(endpoint_)) { - was_alternate_protocol_available_ = true; - if (alternate_protocol_mode_ == kUnspecified) { - HttpAlternateProtocols::PortProtocolPair alternate = - alternate_protocols.GetAlternateProtocolFor(endpoint_); - if (alternate.protocol != HttpAlternateProtocols::BROKEN) { - DCHECK_LE(HttpAlternateProtocols::NPN_SPDY_1, alternate.protocol); - DCHECK_GT(HttpAlternateProtocols::NUM_ALTERNATE_PROTOCOLS, - alternate.protocol); - endpoint_.set_port(alternate.port); - alternate_protocol_ = alternate.protocol; - alternate_protocol_mode_ = kUsingAlternateProtocol; - alternate_endpoint_url = UpgradeUrlToHttps(*curr_endpoint_url); - curr_endpoint_url = &alternate_endpoint_url; - } - } - } + origin_ = HostPortPair(request_info_.url.HostNoBrackets(), + request_info_.url.EffectiveIntPort()); if (request_info_.load_flags & LOAD_BYPASS_PROXY) { proxy_info_.UseDirect(); @@ -482,101 +464,104 @@ int HttpStreamFactoryImpl::Job::DoResolveProxy() { } return session_->proxy_service()->ResolveProxy( - *curr_endpoint_url, &proxy_info_, &io_callback_, &pac_request_, + request_info_.url, &proxy_info_, &io_callback_, &pac_request_, net_log_); } int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) { pac_request_ = NULL; - if (result != OK) - return result; - - // TODO(mbelshe): consider retrying ResolveProxy if we came here via use of - // AlternateProtocol. - - // Remove unsupported proxies from the list. - proxy_info_.RemoveProxiesWithoutScheme( - ProxyServer::SCHEME_DIRECT | - ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS | - ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5); + if (result == OK) { + // Remove unsupported proxies from the list. + proxy_info_.RemoveProxiesWithoutScheme( + ProxyServer::SCHEME_DIRECT | + ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS | + ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5); + + if (proxy_info_.is_empty()) { + // No proxies/direct to choose from. This happens when we don't support + // any of the proxies in the returned list. + result = ERR_NO_SUPPORTED_PROXIES; + } + } - if (proxy_info_.is_empty()) { - // No proxies/direct to choose from. This happens when we don't support any - // of the proxies in the returned list. - return ERR_NO_SUPPORTED_PROXIES; + if (result != OK) { + if (dependent_job_) + dependent_job_->Resume(this); + return result; } - next_state_ = STATE_INIT_CONNECTION; + if (blocking_job_) + next_state_ = STATE_WAIT_FOR_JOB; + else + next_state_ = STATE_INIT_CONNECTION; return OK; } -static bool HasSpdyExclusion(const HostPortPair& endpoint) { - std::list<HostPortPair>* exclusions = - HttpStreamFactory::forced_spdy_exclusions(); - if (!exclusions) - return false; +bool HttpStreamFactoryImpl::Job::ShouldForceSpdySSL() const { + return force_spdy_always_ && force_spdy_over_ssl_; +} - std::list<HostPortPair>::const_iterator it; - for (it = exclusions->begin(); it != exclusions->end(); it++) - if (it->Equals(endpoint)) - return true; - return false; +bool HttpStreamFactoryImpl::Job::ShouldForceSpdyWithoutSSL() const { + return force_spdy_always_ && !force_spdy_over_ssl_; } -bool HttpStreamFactoryImpl::Job::ShouldForceSpdySSL() { - bool rv = force_spdy_always_ && force_spdy_over_ssl_; - return rv && !HasSpdyExclusion(endpoint_); +int HttpStreamFactoryImpl::Job::DoWaitForJob() { + DCHECK(blocking_job_); + next_state_ = STATE_WAIT_FOR_JOB_COMPLETE; + return ERR_IO_PENDING; } -bool HttpStreamFactoryImpl::Job::ShouldForceSpdyWithoutSSL() { - bool rv = force_spdy_always_ && !force_spdy_over_ssl_; - return rv && !HasSpdyExclusion(endpoint_); +int HttpStreamFactoryImpl::Job::DoWaitForJobComplete(int result) { + DCHECK(!blocking_job_); + DCHECK_EQ(OK, result); + next_state_ = STATE_INIT_CONNECTION; + return OK; } int HttpStreamFactoryImpl::Job::DoInitConnection() { + DCHECK(!blocking_job_); DCHECK(!connection_->is_initialized()); DCHECK(proxy_info_.proxy_server().is_valid()); next_state_ = STATE_INIT_CONNECTION_COMPLETE; - bool want_spdy_over_npn = - alternate_protocol_mode_ == kUsingAlternateProtocol && - alternate_protocol_ == HttpAlternateProtocols::NPN_SPDY_2; - using_ssl_ = request_info_.url.SchemeIs("https") || - ShouldForceSpdySSL() || want_spdy_over_npn; + using_ssl_ = request_info_.url.SchemeIs("https") || ShouldForceSpdySSL(); using_spdy_ = false; - // If spdy has been turned off on-the-fly, then there may be SpdySessions - // still active. But don't use them unless spdy is currently on. - if (HttpStreamFactory::spdy_enabled() && !HasSpdyExclusion(endpoint_)) { - // Check first if we have a spdy session for this group. If so, then go - // straight to using that. - HostPortProxyPair spdy_session_key; - if (IsHttpsProxyAndHttpUrl()) { - spdy_session_key = - HostPortProxyPair(proxy_info_.proxy_server().host_port_pair(), - ProxyServer::Direct()); - } else { - spdy_session_key = - HostPortProxyPair(endpoint_, proxy_info_.proxy_server()); - } - if (session_->spdy_session_pool()->HasSession(spdy_session_key)) { - // If we're preconnecting, but we already have a SpdySession, we don't - // actually need to preconnect any sockets, so we're done. - if (IsPreconnecting()) - return OK; - using_spdy_ = true; - next_state_ = STATE_CREATE_STREAM; + // Check first if we have a spdy session for this group. If so, then go + // straight to using that. + HostPortProxyPair spdy_session_key; + if (IsHttpsProxyAndHttpUrl()) { + spdy_session_key = + HostPortProxyPair(proxy_info_.proxy_server().host_port_pair(), + ProxyServer::Direct()); + } else { + spdy_session_key = HostPortProxyPair(origin_, proxy_info_.proxy_server()); + } + if (session_->spdy_session_pool()->HasSession(spdy_session_key)) { + // If we're preconnecting, but we already have a SpdySession, we don't + // actually need to preconnect any sockets, so we're done. + if (IsPreconnecting()) return OK; - } else if (request_) { - // Update the spdy session key for the request that launched this job. - request_->SetSpdySessionKey(spdy_session_key); - } + using_spdy_ = true; + next_state_ = STATE_CREATE_STREAM; + return OK; + } else if (request_ && (using_ssl_ || ShouldForceSpdyWithoutSSL())) { + // Update the spdy session key for the request that launched this job. + request_->SetSpdySessionKey(spdy_session_key); + } + + // OK, there's no available SPDY session. Let |dependent_job_| resume if it's + // paused. + + if (dependent_job_) { + dependent_job_->Resume(this); + dependent_job_ = NULL; } // Build the string used to uniquely identify connections of this type. // Determine the host and port to connect to. - std::string connection_group = endpoint_.ToString(); + std::string connection_group = origin_.ToString(); DCHECK(!connection_group.empty()); if (using_ssl_) @@ -595,7 +580,7 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { scoped_ptr<HostPortPair> proxy_host_port; if (proxy_info_.is_direct()) { - tcp_params = new TCPSocketParams(endpoint_, request_info_.priority, + tcp_params = new TCPSocketParams(origin_, request_info_.priority, request_info_.referrer, disable_resolver_cache); } else { @@ -606,19 +591,6 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { request_info_.referrer, disable_resolver_cache)); if (proxy_info_.is_http() || proxy_info_.is_https()) { - GURL authentication_url = request_info_.url; - if (using_ssl_ && !authentication_url.SchemeIs("https")) { - // If a proxy tunnel connection needs to be established due to - // an Alternate-Protocol, the URL needs to be changed to indicate - // https or digest authentication attempts will fail. - // For example, suppose the initial request was for - // "http://www.example.com/index.html". If this is an SSL - // upgrade due to alternate protocol, the digest authorization - // should have a uri="www.example.com:443" field rather than a - // "/index.html" entry, even though the original request URL has not - // changed. - authentication_url = UpgradeUrlToHttps(authentication_url); - } establishing_tunnel_ = using_ssl_; std::string user_agent; request_info_.extra_headers.GetHeader(HttpRequestHeaders::kUserAgent, @@ -629,16 +601,16 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { ssl_params = GenerateSSLParams(proxy_tcp_params, NULL, NULL, ProxyServer::SCHEME_DIRECT, *proxy_host_port.get(), - want_spdy_over_npn); + original_url_.get() ? true : false); proxy_tcp_params = NULL; } http_proxy_params = new HttpProxySocketParams(proxy_tcp_params, ssl_params, - authentication_url, + request_info_.url, user_agent, - endpoint_, + origin_, session_->http_auth_cache(), session_->http_auth_handler_factory(), session_->spdy_session_pool(), @@ -655,7 +627,7 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { socks_params = new SOCKSSocketParams(proxy_tcp_params, socks_version == '5', - endpoint_, + origin_, request_info_.priority, request_info_.referrer); } @@ -666,8 +638,7 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { scoped_refptr<SSLSocketParams> ssl_params = GenerateSSLParams(tcp_params, http_proxy_params, socks_params, proxy_info_.proxy_server().scheme(), - HostPortPair::FromURL(request_info_.url), - want_spdy_over_npn); + origin_, original_url_.get() ? true : false); SSLClientSocketPool* ssl_pool = NULL; if (proxy_info_.is_direct()) ssl_pool = session_->ssl_socket_pool(); @@ -734,6 +705,13 @@ int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { return OK; } + // TODO(willchan): Make this a bit more exact. Maybe there are recoverable + // errors, such as ignoring certificate errors for Alternate-Protocol. + if (result < 0 && dependent_job_) { + dependent_job_->Resume(this); + dependent_job_ = NULL; + } + // |result| may be the result of any of the stacked pools. The following // logic is used when determining how to interpret an error. // If |result| < 0: @@ -782,12 +760,11 @@ int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { return result; } - if ((!ssl_started && result < 0 && - alternate_protocol_mode_ == kUsingAlternateProtocol) || - result == ERR_NPN_NEGOTIATION_FAILED) { + if (!ssl_started && result < 0 && original_url_.get()) { // Mark the alternate protocol as broken and fallback. - MarkBrokenAlternateProtocolAndFallback(); - return OK; + session_->mutable_alternate_protocols()->MarkBrokenAlternateProtocolFor( + HostPortPair::FromURL(*original_url_)); + return result; } if (result < 0 && !ssl_started) @@ -808,7 +785,8 @@ int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { if (using_ssl_) { DCHECK(ssl_started); if (IsCertificateError(result)) { - if (using_spdy_ && request_info_.url.SchemeIs("http")) { + if (using_spdy_ && original_url_.get() && + original_url_->SchemeIs("http")) { // We ignore certificate errors for http over spdy. spdy_certificate_error_ = result; result = OK; @@ -862,7 +840,7 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() { SpdySessionPool* spdy_pool = session_->spdy_session_pool(); scoped_refptr<SpdySession> spdy_session; - HostPortProxyPair pair(endpoint_, proxy_server); + HostPortProxyPair pair(origin_, 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. @@ -966,7 +944,15 @@ void HttpStreamFactoryImpl::Job::SetSocketMotivation() { } bool HttpStreamFactoryImpl::Job::IsHttpsProxyAndHttpUrl() { - return proxy_info_.is_https() && request_info_.url.SchemeIs("http"); + if (!proxy_info_.is_https()) + return false; + if (original_url_.get()) { + // We currently only support Alternate-Protocol where the original scheme + // is http. + DCHECK(original_url_->SchemeIs("http")); + return original_url_->SchemeIs("http"); + } + return request_info_.url.SchemeIs("http"); } // Returns a newly create SSLSocketParams, and sets several @@ -976,12 +962,12 @@ scoped_refptr<SSLSocketParams> HttpStreamFactoryImpl::Job::GenerateSSLParams( scoped_refptr<HttpProxySocketParams> http_proxy_params, scoped_refptr<SOCKSSocketParams> socks_params, ProxyServer::Scheme proxy_scheme, - const HostPortPair& host_and_port, + const HostPortPair& origin_server, bool want_spdy_over_npn) { - if (stream_factory_->IsTLSIntolerantServer(request_info_.url)) { + if (stream_factory_->IsTLSIntolerantServer(origin_server)) { LOG(WARNING) << "Falling back to SSLv3 because host is TLS intolerant: " - << GetHostAndPort(request_info_.url); + << origin_server.ToString(); ssl_config_.ssl3_fallback = true; ssl_config_.tls1_enabled = false; } @@ -1014,7 +1000,7 @@ scoped_refptr<SSLSocketParams> HttpStreamFactoryImpl::Job::GenerateSSLParams( scoped_refptr<SSLSocketParams> ssl_params( new SSLSocketParams(tcp_params, socks_params, http_proxy_params, - proxy_scheme, host_and_port, + proxy_scheme, origin_server, ssl_config_, load_flags, ShouldForceSpdySSL(), want_spdy_over_npn)); @@ -1023,24 +1009,6 @@ scoped_refptr<SSLSocketParams> HttpStreamFactoryImpl::Job::GenerateSSLParams( } -void HttpStreamFactoryImpl::Job::MarkBrokenAlternateProtocolAndFallback() { - // We have to: - // * Reset the endpoint to be the unmodified URL specified destination. - // * Mark the endpoint as broken so we don't try again. - // * Set the alternate protocol mode to kDoNotUseAlternateProtocol so we - // ignore future Alternate-Protocol headers from the HostPortPair. - // * Reset the connection and go back to STATE_INIT_CONNECTION. - - endpoint_ = HostPortPair(request_info_.url.HostNoBrackets(), - request_info_.url.EffectiveIntPort()); - - session_->mutable_alternate_protocols()->MarkBrokenAlternateProtocolFor( - endpoint_); - - alternate_protocol_mode_ = kDoNotUseAlternateProtocol; - ReturnToStateInitConnection(false /* close connection */); -} - int HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError(int error) { DCHECK(!pac_request_); diff --git a/net/http/http_stream_factory_impl_job.h b/net/http/http_stream_factory_impl_job.h index b466640..213b590 100644 --- a/net/http/http_stream_factory_impl_job.h +++ b/net/http/http_stream_factory_impl_job.h @@ -35,31 +35,40 @@ class TCPSocketParams; class HttpStreamFactoryImpl::Job { public: Job(HttpStreamFactoryImpl* stream_factory, - HttpNetworkSession* session); + HttpNetworkSession* session, + const HttpRequestInfo& request_info, + const SSLConfig& ssl_config, + const BoundNetLog& net_log); ~Job(); - // Start initiates the process of creating a new HttpStream. - // 3 parameters are passed in by reference. The caller asserts that the - // lifecycle of these parameters will remain valid until the stream is - // created, failed, or destroyed. In the first two cases, the delegate will - // be called to notify completion of the request. - void Start(Request* request, - const HttpRequestInfo& request_info, - const SSLConfig& ssl_config, - const BoundNetLog& net_log); - - int Preconnect(int num_streams, - const HttpRequestInfo& request_info, - const SSLConfig& ssl_config, - const BoundNetLog& net_log); + // Start initiates the process of creating a new HttpStream. |request| will be + // notified upon completion if the Job has not been Orphan()'d. + void Start(Request* request); + + // Preconnect will attempt to request |num_streams| sockets from the + // appropriate ClientSocketPool. + int Preconnect(int num_streams); int RestartTunnelWithProxyAuth(const string16& username, const string16& password); LoadState GetLoadState() const; + // Marks this Job as the "alternate" job, from Alternate-Protocol. Tracks the + // original url so we can mark the Alternate-Protocol as broken if + // we fail to connect. + void MarkAsAlternate(const GURL& original_url); + + // Tells |this| to wait for |job| to resume it. + void WaitFor(Job* job); + + // Tells |this| that |job| has determined it still needs to continue + // connecting, so allow |this| to continue. If this is not called, then + // |request_| is expected to cancel |this| by deleting it. + void Resume(Job* job); + + // Used to detach the Job from |request|. void Orphan(const Request* request); - bool was_alternate_protocol_available() const; bool was_npn_negotiated() const; bool using_spdy() const; const BoundNetLog& net_log() const { return net_log_; } @@ -74,15 +83,28 @@ class HttpStreamFactoryImpl::Job { bool IsOrphaned() const; private: - enum AlternateProtocolMode { - kUnspecified, // Unspecified, check HttpAlternateProtocols - kUsingAlternateProtocol, // Using an alternate protocol - kDoNotUseAlternateProtocol, // Failed to connect once, do not try again. - }; - enum State { STATE_RESOLVE_PROXY, STATE_RESOLVE_PROXY_COMPLETE, + + // Note that when Alternate-Protocol says we can connect to an alternate + // port using a different protocol, we have the choice of communicating over + // the original protocol, or speaking the alternate protocol (currently, + // only npn-spdy) over an alternate port. For a cold page load, the http + // connection that delivers the http response that has the + // Alternate-Protocol header will already be warm. So, blocking the next + // http request on establishing a new npn-spdy connection would incur extra + // latency. Even if the http connection was not reused, establishing a new + // http connection is typically faster than npn-spdy, since npn-spdy + // requires a SSL handshake. Therefore, we start both the http and the + // npn-spdy jobs in parallel. In order not to unnecessarily waste sockets, + // we have the http job block on the npn-spdy job after proxy resolution. + // The npn-spdy job will Resume() the http job if, in + // STATE_INIT_CONNECTION_COMPLETE, it detects an error or does not find an + // existing SpdySession. In that case, the http and npn-spdy jobs will race. + STATE_WAIT_FOR_JOB, + STATE_WAIT_FOR_JOB_COMPLETE, + STATE_INIT_CONNECTION, STATE_INIT_CONNECTION_COMPLETE, STATE_WAITING_USER_ACTION, @@ -110,9 +132,7 @@ class HttpStreamFactoryImpl::Job { void OnIOComplete(int result); int RunLoop(int result); int DoLoop(int result); - int StartInternal(const HttpRequestInfo& request_info, - const SSLConfig& ssl_config, - const BoundNetLog& net_log); + int StartInternal(); // Each of these methods corresponds to a State value. Those with an input // argument receive the result from the previous state. If a method returns @@ -120,6 +140,8 @@ class HttpStreamFactoryImpl::Job { // next state method as the result arg. int DoResolveProxy(); int DoResolveProxyComplete(int result); + int DoWaitForJob(); + int DoWaitForJobComplete(int result); int DoInitConnection(); int DoInitConnectionComplete(int result); int DoWaitingUserAction(int result); @@ -174,31 +196,42 @@ class HttpStreamFactoryImpl::Job { void SwitchToSpdyMode(); // Should we force SPDY to run over SSL for this stream request. - bool ShouldForceSpdySSL(); + bool ShouldForceSpdySSL() const; // Should we force SPDY to run without SSL for this stream request. - bool ShouldForceSpdyWithoutSSL(); + bool ShouldForceSpdyWithoutSSL() const; // Record histograms of latency until Connect() completes. static void LogHttpConnectedMetrics(const ClientSocketHandle& handle); Request* request_; - HttpRequestInfo request_info_; + const HttpRequestInfo request_info_; ProxyInfo proxy_info_; SSLConfig ssl_config_; + const BoundNetLog net_log_; CompletionCallbackImpl<Job> io_callback_; scoped_ptr<ClientSocketHandle> connection_; HttpNetworkSession* const session_; HttpStreamFactoryImpl* const stream_factory_; - BoundNetLog net_log_; State next_state_; ProxyService::PacRequest* pac_request_; SSLInfo ssl_info_; - // 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. - HostPortPair endpoint_; + + // The origin server we're trying to reach. + HostPortPair origin_; + + // If this is a Job for an "Alternate-Protocol", then this will be non-NULL + // and will specify the original URL. + scoped_ptr<GURL> original_url_; + + // This is the Job we're dependent on. It will notify us if/when it's OK to + // proceed. + Job* blocking_job_; + + // |dependent_job_| is dependent on |this|. Notify it when it's ok to proceed. + Job* dependent_job_; // True if handling a HTTPS request, or using SPDY with SSL bool using_ssl_; @@ -218,21 +251,12 @@ class HttpStreamFactoryImpl::Job { scoped_refptr<HttpAuthController> auth_controllers_[HttpAuth::AUTH_NUM_TARGETS]; - AlternateProtocolMode alternate_protocol_mode_; - - // Only valid if |alternate_protocol_mode_| == kUsingAlternateProtocol. - HttpAlternateProtocols::Protocol alternate_protocol_; - // True when the tunnel is in the process of being established - we can't // read from the socket until the tunnel is done. bool establishing_tunnel_; scoped_ptr<HttpStream> stream_; - // True if finding the connection for this request found an alternate - // protocol was available. - bool was_alternate_protocol_available_; - // True if we negotiated NPN. bool was_npn_negotiated_; diff --git a/net/http/http_stream_factory_impl_request.cc b/net/http/http_stream_factory_impl_request.cc index b9e0cee..d8bb09f 100644 --- a/net/http/http_stream_factory_impl_request.cc +++ b/net/http/http_stream_factory_impl_request.cc @@ -21,7 +21,6 @@ HttpStreamFactoryImpl::Request::Request(const GURL& url, delegate_(delegate), net_log_(net_log), completed_(false), - was_alternate_protocol_available_(false), was_npn_negotiated_(false), using_spdy_(false) { DCHECK(factory_); @@ -63,13 +62,11 @@ void HttpStreamFactoryImpl::Request::AttachJob(Job* job) { } void HttpStreamFactoryImpl::Request::Complete( - bool was_alternate_protocol_available, bool was_npn_negotiated, bool using_spdy, const NetLog::Source& job_source) { DCHECK(!completed_); completed_ = true; - was_alternate_protocol_available_ = was_alternate_protocol_available; was_npn_negotiated_ = was_npn_negotiated; using_spdy_ = using_spdy; net_log_.AddEvent( @@ -114,10 +111,23 @@ void HttpStreamFactoryImpl::Request::OnStreamFailed( int status, const SSLConfig& used_ssl_config) { DCHECK_NE(OK, status); - if (!bound_job_.get()) - OrphanJobsExcept(job); - else + if (!bound_job_.get()) { + // Hey, we've got other jobs! Maybe one of them will succeed, let's just + // ignore this failure. + if (jobs_.size() > 1) { + jobs_.erase(job); + factory_->request_map_.erase(job); + delete job; + return; + } else { + bound_job_.reset(job); + jobs_.erase(job); + DCHECK(jobs_.empty()); + factory_->request_map_.erase(job); + } + } else { DCHECK(jobs_.empty()); + } delegate_->OnStreamFailed(status, used_ssl_config); } @@ -189,11 +199,6 @@ LoadState HttpStreamFactoryImpl::Request::GetLoadState() const { return (*jobs_.begin())->GetLoadState(); } -bool HttpStreamFactoryImpl::Request::was_alternate_protocol_available() const { - DCHECK(completed_); - return was_alternate_protocol_available_; -} - bool HttpStreamFactoryImpl::Request::was_npn_negotiated() const { DCHECK(completed_); return was_npn_negotiated_; @@ -238,14 +243,11 @@ void HttpStreamFactoryImpl::Request::OnSpdySessionReady( // Cache these values in case the job gets deleted. const SSLConfig used_ssl_config = job->ssl_config(); const ProxyInfo used_proxy_info = job->proxy_info(); - const bool was_alternate_protocol_available = - job->was_alternate_protocol_available(); const bool was_npn_negotiated = job->was_npn_negotiated(); const bool using_spdy = job->using_spdy(); const NetLog::Source source = job->net_log().source(); - Complete(was_alternate_protocol_available, - was_npn_negotiated, + Complete(was_npn_negotiated, using_spdy, source); @@ -260,7 +262,7 @@ void HttpStreamFactoryImpl::Request::OnSpdySessionReady( // |this| may be deleted after this point. factory->OnSpdySessionReady( spdy_session, direct, used_ssl_config, used_proxy_info, - was_alternate_protocol_available, was_npn_negotiated, using_spdy, source); + was_npn_negotiated, using_spdy, source); } void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) { diff --git a/net/http/http_stream_factory_impl_request.h b/net/http/http_stream_factory_impl_request.h index 3c1b996..248a316 100644 --- a/net/http/http_stream_factory_impl_request.h +++ b/net/http/http_stream_factory_impl_request.h @@ -37,8 +37,7 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { // Marks completion of the request. Must be called before OnStreamReady(). // |source| is the NetLog::Source generated by the Job that fulfilled this // request. - void Complete(bool was_alternate_protocol_available, - bool was_npn_negotiated, + void Complete(bool was_npn_negotiated, bool using_spdy, const NetLog::Source& source); @@ -83,7 +82,6 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { virtual int RestartTunnelWithProxyAuth(const string16& username, const string16& password); virtual LoadState GetLoadState() const; - virtual bool was_alternate_protocol_available() const; virtual bool was_npn_negotiated() const; virtual bool using_spdy() const; @@ -106,7 +104,6 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { scoped_ptr<const HostPortProxyPair> spdy_session_key_; bool completed_; - bool was_alternate_protocol_available_; bool was_npn_negotiated_; bool using_spdy_; diff --git a/net/socket/client_socket_pool_base.cc b/net/socket/client_socket_pool_base.cc index c25f6ab..6d5e410 100644 --- a/net/socket/client_socket_pool_base.cc +++ b/net/socket/client_socket_pool_base.cc @@ -636,6 +636,11 @@ void ClientSocketPoolBaseHelper::RemoveGroup(GroupMap::iterator it) { } // static +bool ClientSocketPoolBaseHelper::connect_backup_jobs_enabled() { + return g_connect_backup_jobs_enabled; +} + +// static void ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(bool enabled) { g_connect_backup_jobs_enabled = enabled; } diff --git a/net/socket/client_socket_pool_base.h b/net/socket/client_socket_pool_base.h index 53c8fbc..8fcb571 100644 --- a/net/socket/client_socket_pool_base.h +++ b/net/socket/client_socket_pool_base.h @@ -283,7 +283,9 @@ class ClientSocketPoolBaseHelper return connect_job_factory_->ConnectionTimeout(); } + static bool connect_backup_jobs_enabled(); static void set_connect_backup_jobs_enabled(bool enabled); + void EnableConnectBackupJobs(); // ConnectJob::Delegate methods: diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index acfb7ea..9f4e1fe 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -11,6 +11,7 @@ #include "net/base/net_log_unittest.h" #include "net/http/http_network_session_peer.h" #include "net/http/http_transaction_unittest.h" +#include "net/socket/client_socket_pool_base.h" #include "net/spdy/spdy_http_stream.h" #include "net/spdy/spdy_http_utils.h" #include "net/spdy/spdy_session.h" @@ -35,17 +36,25 @@ enum SpdyNetworkTransactionTestTypes { class SpdyNetworkTransactionTest : public ::testing::TestWithParam<SpdyNetworkTransactionTestTypes> { protected: + virtual void SetUp() { // By default, all tests turn off compression. EnableCompression(false); google_get_request_initialized_ = false; google_post_request_initialized_ = false; google_chunked_post_request_initialized_ = false; + + backup_jobs_prev_value_ = + internal::ClientSocketPoolBaseHelper::connect_backup_jobs_enabled(); + internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled( + false); } virtual void TearDown() { // Empty the current queue. MessageLoop::current()->RunAllPending(); + internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled( + backup_jobs_prev_value_); } struct TransactionHelperResult { @@ -167,10 +176,8 @@ class SpdyNetworkTransactionTest EXPECT_EQ(spdy_enabled_, response->was_fetched_via_spdy); if (test_type_ == SPDYNPN && spdy_enabled_) { EXPECT_TRUE(response->was_npn_negotiated); - EXPECT_TRUE(response->was_alternate_protocol_available); } else { EXPECT_TRUE(!response->was_npn_negotiated); - EXPECT_TRUE(!response->was_alternate_protocol_available); } // If SPDY is not enabled, a HTTP request should not be diverted // over a SSL session. @@ -237,9 +244,20 @@ class SpdyNetworkTransactionTest ssl_->was_npn_negotiated = true; } ssl_vector_.push_back(ssl_); - if(test_type_ == SPDYNPN || test_type_ == SPDYSSL) + if (test_type_ == SPDYNPN || test_type_ == SPDYSSL) session_deps_->socket_factory->AddSSLSocketDataProvider(ssl_.get()); session_deps_->socket_factory->AddSocketDataProvider(data); + if (test_type_ == SPDYNPN) { + MockConnect never_finishing_connect(false, ERR_IO_PENDING); + linked_ptr<StaticSocketDataProvider> + hanging_non_alternate_protocol_socket( + new StaticSocketDataProvider(NULL, 0, NULL, 0)); + hanging_non_alternate_protocol_socket->set_connect_data( + never_finishing_connect); + session_deps_->socket_factory->AddSocketDataProvider( + hanging_non_alternate_protocol_socket.get()); + alternate_vector_.push_back(hanging_non_alternate_protocol_socket); + } } void AddDeterministicData(DeterministicSocketData* data) { @@ -253,10 +271,23 @@ class SpdyNetworkTransactionTest ssl_->was_npn_negotiated = true; } ssl_vector_.push_back(ssl_); - if(test_type_ == SPDYNPN || test_type_ == SPDYSSL) + if (test_type_ == SPDYNPN || test_type_ == SPDYSSL) { session_deps_->deterministic_socket_factory-> AddSSLSocketDataProvider(ssl_.get()); + } session_deps_->deterministic_socket_factory->AddSocketDataProvider(data); + if (test_type_ == SPDYNPN) { + MockConnect never_finishing_connect(false, ERR_IO_PENDING); + scoped_refptr<DeterministicSocketData> + hanging_non_alternate_protocol_socket( + new DeterministicSocketData(NULL, 0, NULL, 0)); + hanging_non_alternate_protocol_socket->set_connect_data( + never_finishing_connect); + session_deps_->deterministic_socket_factory->AddSocketDataProvider( + hanging_non_alternate_protocol_socket); + alternate_deterministic_vector_.push_back( + hanging_non_alternate_protocol_socket); + } } // This can only be called after RunPreTestSetup. It adds a Data Provider, @@ -289,6 +320,9 @@ class SpdyNetworkTransactionTest private: typedef std::vector<StaticSocketDataProvider*> DataVector; typedef std::vector<linked_ptr<SSLSocketDataProvider> > SSLVector; + typedef std::vector<linked_ptr<StaticSocketDataProvider> > AlternateVector; + typedef std::vector<scoped_refptr<DeterministicSocketData> > + AlternateDeterministicVector; HttpRequestInfo request_; scoped_ptr<SpdySessionDependencies> session_deps_; scoped_refptr<HttpNetworkSession> session_; @@ -299,6 +333,8 @@ class SpdyNetworkTransactionTest scoped_ptr<HttpNetworkTransaction> trans_; scoped_ptr<HttpNetworkTransaction> trans_http_; DataVector data_vector_; + AlternateVector alternate_vector_; + AlternateDeterministicVector alternate_deterministic_vector_; const BoundNetLog& log_; SpdyNetworkTransactionTestTypes test_type_; int port_; @@ -475,6 +511,8 @@ class SpdyNetworkTransactionTest HttpRequestInfo google_post_request_; HttpRequestInfo google_chunked_post_request_; HttpRequestInfo google_get_push_request_; + + bool backup_jobs_prev_value_; }; //----------------------------------------------------------------------------- @@ -704,7 +742,7 @@ TEST_P(SpdyNetworkTransactionTest, TwoGetsLateBinding) { new OrderedSocketData(reads, arraysize(reads), writes, arraysize(writes))); - MockConnect never_finishing_connect(true, ERR_IO_PENDING); + MockConnect never_finishing_connect(false, ERR_IO_PENDING); scoped_refptr<OrderedSocketData> data_placeholder( new OrderedSocketData(NULL, 0, NULL, 0)); @@ -893,10 +931,12 @@ TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) { settings.push_back(spdy::SpdySetting(id, max_concurrent_streams)); scoped_ptr<spdy::SpdyFrame> settings_frame(ConstructSpdySettings(settings)); - MockWrite writes[] = { CreateMockWrite(*req), - CreateMockWrite(*req2), - CreateMockWrite(*req3), + MockWrite writes[] = { + CreateMockWrite(*req), + CreateMockWrite(*req2), + CreateMockWrite(*req3), }; + MockRead reads[] = { CreateMockRead(*settings_frame, 1), CreateMockRead(*resp), @@ -921,14 +961,14 @@ TEST_P(SpdyNetworkTransactionTest, ThreeGetsWithMaxConcurrent) { BoundNetLog log; TransactionHelperResult out; { - NormalSpdyTransactionHelper helper(CreateGetRequest(), - BoundNetLog(), GetParam()); - helper.RunPreTestSetup(); - helper.AddData(data.get()); - // We require placeholder data because three get requests are sent out, so - // there needs to be three sets of SSL connection data. - helper.AddData(data_placeholder.get()); - helper.AddData(data_placeholder.get()); + NormalSpdyTransactionHelper helper(CreateGetRequest(), + BoundNetLog(), GetParam()); + helper.RunPreTestSetup(); + helper.AddData(data.get()); + // We require placeholder data because three get requests are sent out, so + // there needs to be three sets of SSL connection data. + helper.AddData(data_placeholder.get()); + helper.AddData(data_placeholder.get()); scoped_ptr<HttpNetworkTransaction> trans1( new HttpNetworkTransaction(helper.session())); scoped_ptr<HttpNetworkTransaction> trans2( @@ -1716,7 +1756,7 @@ TEST_P(SpdyNetworkTransactionTest, SocketWriteReturnsZero) { scoped_refptr<DeterministicSocketData> data( new DeterministicSocketData(reads, arraysize(reads), - writes, arraysize(writes))); + writes, arraysize(writes))); NormalSpdyTransactionHelper helper(CreateGetRequest(), BoundNetLog(), GetParam()); helper.SetDeterministic(); @@ -3807,80 +3847,6 @@ TEST_P(SpdyNetworkTransactionTest, BufferFull) { EXPECT_EQ("goodbye world", out.response_data); } -TEST_P(SpdyNetworkTransactionTest, ConnectFailureFallbackToHttp) { - MockConnect connects[] = { - MockConnect(true, ERR_NAME_NOT_RESOLVED), - MockConnect(false, ERR_NAME_NOT_RESOLVED), - MockConnect(true, ERR_INTERNET_DISCONNECTED), - MockConnect(false, ERR_INTERNET_DISCONNECTED) - }; - - for (size_t index = 0; index < arraysize(connects); ++index) { - scoped_ptr<spdy::SpdyFrame> req( - ConstructSpdyGet(NULL, 0, false, 1, LOWEST)); - MockWrite writes[] = { - CreateMockWrite(*req), - MockWrite(true, 0, 0) // EOF - }; - - scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1)); - scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true)); - MockRead reads[] = { - CreateMockRead(*resp), - CreateMockRead(*body), - MockRead(true, 0, 0) // EOF - }; - - scoped_refptr<DelayedSocketData> data( - new DelayedSocketData(connects[index], 1, reads, arraysize(reads), - writes, arraysize(writes))); - NormalSpdyTransactionHelper helper(CreateGetRequest(), - BoundNetLog(), GetParam()); - helper.RunPreTestSetup(); - helper.AddData(data.get()); - - // Set up http fallback data. - MockRead http_fallback_data[] = { - MockRead("HTTP/1.1 200 OK\r\n\r\n"), - MockRead("hello world!!!"), - MockRead(true, OK), - }; - - scoped_ptr<StaticSocketDataProvider> http_fallback( - new StaticSocketDataProvider(http_fallback_data, - arraysize(http_fallback_data), - NULL, 0)); - helper.AddDataNoSSL(http_fallback.get()); - HttpNetworkTransaction* trans = helper.trans(); - TestCompletionCallback callback; - - int rv = trans->Start(&helper.request(), &callback, BoundNetLog()); - EXPECT_EQ(rv, ERR_IO_PENDING); - rv = callback.WaitForResult(); - const HttpResponseInfo* response = trans->GetResponseInfo(); - if (GetParam() == SPDYNOSSL || GetParam() == SPDYSSL) { - ASSERT_TRUE(response == NULL); - return; - } - if (GetParam() != SPDYNPN) - NOTREACHED(); - ASSERT_TRUE(response != NULL); - ASSERT_TRUE(response->headers != NULL); - EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); - std::string response_data; - rv = ReadTransaction(trans, &response_data); - EXPECT_EQ(OK, rv); - - EXPECT_TRUE(!response->was_fetched_via_spdy); - EXPECT_TRUE(!response->was_npn_negotiated); - EXPECT_TRUE(response->was_alternate_protocol_available); - EXPECT_TRUE(http_fallback->at_read_eof()); - EXPECT_EQ(0u, data->read_index()); - EXPECT_EQ(0u, data->write_index()); - EXPECT_EQ("hello world!!!", response_data); - } -} - // Verify that basic buffering works; when multiple data frames arrive // at the same time, ensure that we don't notify a read completion for // each data frame individually. diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index c573099..2c58db1 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h @@ -409,12 +409,6 @@ class URLRequest : public base::NonThreadSafe { return response_info_.was_npn_negotiated; } - // Returns true if the URLRequest was delivered when the alternate protocol - // is available. - bool was_alternate_protocol_available() const { - return response_info_.was_alternate_protocol_available; - } - // Returns true if the URLRequest was delivered through a proxy. bool was_fetched_via_proxy() const { return response_info_.was_fetched_via_proxy; |