summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-06 16:45:29 +0000
committerwillchan@chromium.org <willchan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-06 16:45:29 +0000
commit46e1edc4121dda51143becfffbc46674c770def2 (patch)
tree27593717ecc788e3f863e91fcef9ec11f8991697
parent7a44af79d362f76d2af41a440506eb7fad0792cb (diff)
downloadchromium_src-46e1edc4121dda51143becfffbc46674c770def2.zip
chromium_src-46e1edc4121dda51143becfffbc46674c770def2.tar.gz
chromium_src-46e1edc4121dda51143becfffbc46674c770def2.tar.bz2
Don't block on stream requests on Alternate-Protocol.
Basically, after we get an HTTP header giving an Alternate-Protocol, we'll end up in a state where we're probably going to issue more HTTP requests. We have the already warm single HTTP connection. We probably don't have a SPDY session open. Previously, once we notice Alternate-Protocol, we'd block on trying to set up the new SPDY session, even though we still have the HTTP connection. Change this so we keep using HTTP until the SPDY session on the Alternate-Protocol becomes available, then we switch over to it. BUG=69688,75000 TEST=Browse to belshe.com. Examine net-internals to make sure it works. Test over a variety of connections (direct / various proxy types). Review URL: http://codereview.chromium.org/6610034 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77075 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--content/browser/renderer_host/resource_dispatcher_host.cc2
-rw-r--r--net/http/http_basic_stream.cc4
-rw-r--r--net/http/http_basic_stream.h2
-rw-r--r--net/http/http_network_transaction.cc9
-rw-r--r--net/http/http_network_transaction_unittest.cc282
-rw-r--r--net/http/http_response_body_drainer_unittest.cc2
-rw-r--r--net/http/http_response_info.cc12
-rw-r--r--net/http/http_response_info.h4
-rw-r--r--net/http/http_stream.h4
-rw-r--r--net/http/http_stream_factory.h7
-rw-r--r--net/http/http_stream_factory_impl.cc127
-rw-r--r--net/http/http_stream_factory_impl.h12
-rw-r--r--net/http/http_stream_factory_impl_job.cc360
-rw-r--r--net/http/http_stream_factory_impl_job.h106
-rw-r--r--net/http/http_stream_factory_impl_request.cc34
-rw-r--r--net/http/http_stream_factory_impl_request.h5
-rw-r--r--net/spdy/spdy_http_stream.h1
-rw-r--r--net/spdy/spdy_network_transaction_unittest.cc108
-rw-r--r--net/url_request/url_request.h6
19 files changed, 680 insertions, 407 deletions
diff --git a/content/browser/renderer_host/resource_dispatcher_host.cc b/content/browser/renderer_host/resource_dispatcher_host.cc
index 6ed17f1..ea553b2d 100644
--- a/content/browser/renderer_host/resource_dispatcher_host.cc
+++ b/content/browser/renderer_host/resource_dispatcher_host.cc
@@ -177,8 +177,6 @@ void PopulateResourceResponse(net::URLRequest* request,
response->response_head.was_fetched_via_spdy =
request->was_fetched_via_spdy();
response->response_head.was_npn_negotiated = request->was_npn_negotiated();
- response->response_head.was_alternate_protocol_available =
- request->was_alternate_protocol_available();
response->response_head.was_fetched_via_proxy =
request->was_fetched_via_proxy();
response->response_head.socket_address = request->GetSocketAddress();
diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc
index 3e69d7a..ef4d777 100644
--- a/net/http/http_basic_stream.cc
+++ b/net/http/http_basic_stream.cc
@@ -111,4 +111,8 @@ void HttpBasicStream::GetSSLCertRequestInfo(
parser_->GetSSLCertRequestInfo(cert_request_info);
}
+bool HttpBasicStream::IsSpdyHttpStream() const {
+ return false;
+}
+
} // namespace net
diff --git a/net/http/http_basic_stream.h b/net/http/http_basic_stream.h
index 918596a..a706e8e 100644
--- a/net/http/http_basic_stream.h
+++ b/net/http/http_basic_stream.h
@@ -76,6 +76,8 @@ class HttpBasicStream : public HttpStream {
virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info);
+ virtual bool IsSpdyHttpStream() const;
+
private:
scoped_refptr<GrowableIOBuffer> read_buf_;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index d3ed391..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"
@@ -128,6 +129,9 @@ HttpNetworkTransaction::~HttpNetworkTransaction() {
if (stream_->IsResponseBodyComplete()) {
// If the response body is complete, we can just reuse the socket.
stream_->Close(false /* reusable */);
+ } else if (stream_->IsSpdyHttpStream()) {
+ // Doesn't really matter for SpdyHttpStream. Just close it.
+ stream_->Close(true /* not reusable */);
} else {
// Otherwise, we try to drain the response body.
// TODO(willchan): Consider moving this response body draining to the
@@ -368,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();
@@ -1067,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_body_drainer_unittest.cc b/net/http/http_response_body_drainer_unittest.cc
index 5745865..ea9d299 100644
--- a/net/http/http_response_body_drainer_unittest.cc
+++ b/net/http/http_response_body_drainer_unittest.cc
@@ -111,6 +111,8 @@ class MockHttpStream : public HttpStream {
virtual bool IsResponseBodyComplete() const { return is_complete_; }
+ virtual bool IsSpdyHttpStream() const { return false; }
+
// Methods to tweak/observer mock behavior:
void StallReadsForever() { stall_reads_forever_ = true; }
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.h b/net/http/http_stream.h
index 16f84cc..e262038 100644
--- a/net/http/http_stream.h
+++ b/net/http/http_stream.h
@@ -126,6 +126,10 @@ class HttpStream {
// behavior is undefined.
virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info) = 0;
+ // HACK(willchan): Really, we should move the HttpResponseDrainer logic into
+ // the HttpStream implementation. This is just a quick hack.
+ virtual bool IsSpdyHttpStream() const = 0;
+
private:
DISALLOW_COPY_AND_ASSIGN(HttpStream);
};
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/spdy/spdy_http_stream.h b/net/spdy/spdy_http_stream.h
index bdae342..c165b21 100644
--- a/net/spdy/spdy_http_stream.h
+++ b/net/spdy/spdy_http_stream.h
@@ -65,6 +65,7 @@ class SpdyHttpStream : public SpdyStream::Delegate, public HttpStream {
virtual void SetConnectionReused();
virtual void GetSSLInfo(SSLInfo* ssl_info);
virtual void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info);
+ virtual bool IsSpdyHttpStream() const { return true; }
// SpdyStream::Delegate methods:
virtual bool OnSendHeadersComplete(int status);
diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc
index 94ef61d..8fc0913 100644
--- a/net/spdy/spdy_network_transaction_unittest.cc
+++ b/net/spdy/spdy_network_transaction_unittest.cc
@@ -167,10 +167,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 +235,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 +262,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 +311,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 +324,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_;
@@ -704,7 +731,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 +920,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 +950,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 +1745,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();
@@ -2530,20 +2559,6 @@ TEST_P(SpdyNetworkTransactionTest, RedirectServerPush) {
"version",
"HTTP/1.1"
};
- const char* const kStandardGetHeaders2[] = {
- "host",
- "www.foo.com",
- "method",
- "GET",
- "scheme",
- "http",
- "url",
- "/index.php",
- "user-agent",
- "",
- "version",
- "HTTP/1.1"
- };
// Setup writes/reads to www.google.com
scoped_ptr<spdy::SpdyFrame> req(
@@ -2552,12 +2567,6 @@ TEST_P(SpdyNetworkTransactionTest, RedirectServerPush) {
arraysize(kExtraHeaders) / 2,
kStandardGetHeaders,
arraysize(kStandardGetHeaders) / 2));
- scoped_ptr<spdy::SpdyFrame> req2(
- ConstructSpdyPacket(kSynStartHeader,
- kExtraHeaders,
- arraysize(kExtraHeaders) / 2,
- kStandardGetHeaders2,
- arraysize(kStandardGetHeaders2) / 2));
scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0, 1));
scoped_ptr<spdy::SpdyFrame> rep(
ConstructSpdyPush(NULL,
@@ -2568,18 +2577,40 @@ TEST_P(SpdyNetworkTransactionTest, RedirectServerPush) {
"301 Moved Permanently",
"http://www.foo.com/index.php"));
scoped_ptr<spdy::SpdyFrame> body(ConstructSpdyBodyFrame(1, true));
+ scoped_ptr<spdy::SpdyFrame> rst(ConstructSpdyRstStream(2, spdy::CANCEL));
MockWrite writes[] = {
CreateMockWrite(*req, 1),
+ CreateMockWrite(*rst, 6),
};
MockRead reads[] = {
CreateMockRead(*resp, 2),
CreateMockRead(*rep, 3),
CreateMockRead(*body, 4),
MockRead(true, ERR_IO_PENDING, 5), // Force a pause
- MockRead(true, 0, 0, 6) // EOF
+ MockRead(true, 0, 0, 7) // EOF
};
// Setup writes/reads to www.foo.com
+ const char* const kStandardGetHeaders2[] = {
+ "host",
+ "www.foo.com",
+ "method",
+ "GET",
+ "scheme",
+ "http",
+ "url",
+ "/index.php",
+ "user-agent",
+ "",
+ "version",
+ "HTTP/1.1"
+ };
+ scoped_ptr<spdy::SpdyFrame> req2(
+ ConstructSpdyPacket(kSynStartHeader,
+ kExtraHeaders,
+ arraysize(kExtraHeaders) / 2,
+ kStandardGetHeaders2,
+ arraysize(kStandardGetHeaders2) / 2));
scoped_ptr<spdy::SpdyFrame> resp2(ConstructSpdyGetSynReply(NULL, 0, 1));
scoped_ptr<spdy::SpdyFrame> body2(ConstructSpdyBodyFrame(1, true));
MockWrite writes2[] = {
@@ -3871,7 +3902,6 @@ TEST_P(SpdyNetworkTransactionTest, ConnectFailureFallbackToHttp) {
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());
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 849a03b..b8ff569 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -405,12 +405,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;