summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/browser_init.cc7
-rw-r--r--chrome/browser/browser_main.cc49
-rw-r--r--chrome/browser/renderer_host/resource_dispatcher_host.cc1
-rw-r--r--chrome/common/render_messages.h6
-rw-r--r--chrome/renderer/loadtimes_extension_bindings.cc3
-rw-r--r--chrome/renderer/navigation_state.h6
-rw-r--r--chrome/renderer/render_view.cc34
-rw-r--r--net/http/http_network_layer.cc40
-rw-r--r--net/http/http_network_transaction.cc19
-rw-r--r--net/http/http_network_transaction_unittest.cc75
-rw-r--r--net/http/http_response_info.cc10
-rw-r--r--net/http/http_response_info.h3
-rw-r--r--net/socket/socket_test_util.cc4
-rw-r--r--net/socket/socket_test_util.h5
-rw-r--r--net/socket/ssl_client_socket.h14
-rw-r--r--net/spdy/spdy_session.cc5
-rw-r--r--net/url_request/url_request.h6
-rw-r--r--webkit/glue/resource_loader_bridge.cc1
-rw-r--r--webkit/glue/resource_loader_bridge.h3
-rw-r--r--webkit/glue/weburlloader_impl.cc1
20 files changed, 257 insertions, 35 deletions
diff --git a/chrome/browser/browser_init.cc b/chrome/browser/browser_init.cc
index e20dfa2..7e5b2fd 100644
--- a/chrome/browser/browser_init.cc
+++ b/chrome/browser/browser_init.cc
@@ -50,7 +50,6 @@
#include "grit/locale_settings.h"
#include "grit/theme_resources.h"
#include "net/base/net_util.h"
-#include "net/http/http_network_layer.h"
#include "net/url_request/url_request.h"
#include "webkit/glue/webkit_glue.h"
@@ -1008,12 +1007,6 @@ bool BrowserInit::ProcessCmdLineImpl(const CommandLine& command_line,
}
}
- if (command_line.HasSwitch(switches::kUseSpdy)) {
- std::string spdy_mode =
- command_line.GetSwitchValueASCII(switches::kUseSpdy);
- net::HttpNetworkLayer::EnableSpdy(spdy_mode);
- }
-
if (command_line.HasSwitch(switches::kExplicitlyAllowedPorts)) {
std::wstring allowed_ports =
command_line.GetSwitchValue(switches::kExplicitlyAllowedPorts);
diff --git a/chrome/browser/browser_main.cc b/chrome/browser/browser_main.cc
index 1ccb00f..4f69109 100644
--- a/chrome/browser/browser_main.cc
+++ b/chrome/browser/browser_main.cc
@@ -73,6 +73,7 @@
#include "grit/generated_resources.h"
#include "net/base/cookie_monster.h"
#include "net/base/net_module.h"
+#include "net/http/http_network_layer.h"
#include "net/http/http_network_session.h"
#include "net/http/http_network_transaction.h"
#include "net/socket/client_socket_pool_base.h"
@@ -707,9 +708,52 @@ int BrowserMain(const MainFunctionParams& parameters) {
net::EnsureWinsockInit();
#endif // defined(OS_WIN)
+ // Initialize statistical testing infrastructure for entire browser.
+ FieldTrialList field_trial;
+
+ // When --use-spdy not set, users will be in A/B test for spdy.
+ // group A (_npn_with_spdy): this means npn and spdy are enabled. In
+ // case server supports spdy, browser will use spdy.
+ // group B (_npn_with_http): this means npn is enabled but spdy
+ // won't be used. Http is still used for all requests.
+ // default group: no npn or spdy is involved. The "old" non-spdy chrome
+ // behavior.
+ bool is_spdy_trial = false;
+ if (parsed_command_line.HasSwitch(switches::kUseSpdy)) {
+ std::string spdy_mode =
+ parsed_command_line.GetSwitchValueASCII(switches::kUseSpdy);
+ net::HttpNetworkLayer::EnableSpdy(spdy_mode);
+ } else {
+ const FieldTrial::Probability kSpdyDivisor = 1000;
+ // TODO(lzheng): Increase these values to enable more spdy tests.
+ // To enable 100% npn_with_spdy, set kNpnHttpProbability = 0 and set
+ // kNpnSpdyProbability = FieldTrial::kAllRemainingProbability.
+ const FieldTrial::Probability kNpnHttpProbability = 0; // 0.0%
+ const FieldTrial::Probability kNpnSpdyProbability = 0; // 0.0%
+ scoped_refptr<FieldTrial> trial =
+ new FieldTrial("SpdyImpact", kSpdyDivisor);
+ // npn with only http support, no spdy.
+ int npn_http_grp =
+ trial->AppendGroup("_npn_with_http", kNpnHttpProbability);
+ // npn with spdy support.
+ int npn_spdy_grp =
+ trial->AppendGroup("_npn_with_spdy", kNpnSpdyProbability);
+ int trial_grp = trial->group();
+ if (trial_grp == npn_http_grp) {
+ is_spdy_trial = true;
+ net::HttpNetworkLayer::EnableSpdy("npn-http");
+ } else if (trial_grp == npn_spdy_grp) {
+ is_spdy_trial = true;
+ net::HttpNetworkLayer::EnableSpdy("npn");
+ } else {
+ CHECK(!is_spdy_trial);
+ }
+ }
+
#if defined(OS_WIN)
if (!parsed_command_line.HasSwitch(switches::kUseSChannel) ||
- parsed_command_line.HasSwitch(switches::kUseSpdy)) {
+ parsed_command_line.HasSwitch(switches::kUseSpdy) ||
+ is_spdy_trial) {
net::ClientSocketFactory::SetSSLClientSocketFactory(
net::SSLClientSocketNSSFactory);
// We want to be sure to init NSPR on the main thread.
@@ -727,9 +771,6 @@ int BrowserMain(const MainFunctionParams& parameters) {
SystemMonitor system_monitor;
HighResolutionTimerManager hi_res_timer_manager;
- // Initialize statistical testing infrastructure for entire browser.
- FieldTrialList field_trial;
-
std::wstring app_name = chrome::kBrowserAppName;
std::string thread_name_string = WideToASCII(app_name + L"_BrowserMain");
diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc
index 17bdf4e..580c2c2 100644
--- a/chrome/browser/renderer_host/resource_dispatcher_host.cc
+++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc
@@ -163,6 +163,7 @@ void PopulateResourceResponse(URLRequest* request,
request->GetMimeType(&response->response_head.mime_type);
response->response_head.was_fetched_via_spdy =
request->was_fetched_via_spdy();
+ response->response_head.was_npn_negotiated = request->was_npn_negotiated();
appcache::AppCacheInterceptor::GetExtraResponseInfo(
request,
&response->response_head.appcache_id,
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index e1f8791..ab0dcc1 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -1327,6 +1327,7 @@ struct ParamTraits<webkit_glue::ResourceLoaderBridge::ResponseInfo> {
WriteParam(m, p.appcache_id);
WriteParam(m, p.appcache_manifest_url);
WriteParam(m, p.was_fetched_via_spdy);
+ WriteParam(m, p.was_npn_negotiated);
}
static bool Read(const Message* m, void** iter, param_type* r) {
return
@@ -1339,7 +1340,8 @@ struct ParamTraits<webkit_glue::ResourceLoaderBridge::ResponseInfo> {
ReadParam(m, iter, &r->content_length) &&
ReadParam(m, iter, &r->appcache_id) &&
ReadParam(m, iter, &r->appcache_manifest_url) &&
- ReadParam(m, iter, &r->was_fetched_via_spdy);
+ ReadParam(m, iter, &r->was_fetched_via_spdy) &&
+ ReadParam(m, iter, &r->was_npn_negotiated);
}
static void Log(const param_type& p, std::wstring* l) {
l->append(L"(");
@@ -1362,6 +1364,8 @@ struct ParamTraits<webkit_glue::ResourceLoaderBridge::ResponseInfo> {
LogParam(p.appcache_manifest_url, l);
l->append(L", ");
LogParam(p.was_fetched_via_spdy, l);
+ l->append(L",");
+ LogParam(p.was_npn_negotiated, l);
l->append(L")");
}
};
diff --git a/chrome/renderer/loadtimes_extension_bindings.cc b/chrome/renderer/loadtimes_extension_bindings.cc
index c43cd95..151e78d 100644
--- a/chrome/renderer/loadtimes_extension_bindings.cc
+++ b/chrome/renderer/loadtimes_extension_bindings.cc
@@ -131,6 +131,9 @@ class LoadTimesExtensionWrapper : public v8::Extension {
load_times->Set(
v8::String::New("wasFetchedViaSpdy"),
v8::Boolean::New(navigation_state->was_fetched_via_spdy()));
+ load_times->Set(
+ v8::String::New("wasNpnNegotiated"),
+ v8::Boolean::New(navigation_state->was_npn_negotiated()));
return load_times;
}
}
diff --git a/chrome/renderer/navigation_state.h b/chrome/renderer/navigation_state.h
index 36a8441..8d762aa 100644
--- a/chrome/renderer/navigation_state.h
+++ b/chrome/renderer/navigation_state.h
@@ -220,6 +220,9 @@ class NavigationState : public WebKit::WebDataSource::ExtraData {
void set_was_fetched_via_spdy(bool value) { was_fetched_via_spdy_ = value; }
bool was_fetched_via_spdy() const { return was_fetched_via_spdy_; }
+ void set_was_npn_negotiated(bool value) { was_npn_negotiated_ = value; }
+ bool was_npn_negotiated() const { return was_npn_negotiated_; }
+
// Whether the frame text contents was translated to a different language.
void set_was_translated(bool value) { was_translated_ = value; }
bool was_translated() const { return was_translated_; }
@@ -243,6 +246,7 @@ class NavigationState : public WebKit::WebDataSource::ExtraData {
cache_policy_override_(WebKit::WebURLRequest::UseProtocolCachePolicy),
user_script_idle_scheduler_(NULL),
was_fetched_via_spdy_(false),
+ was_npn_negotiated_(false),
was_translated_(false) {
}
@@ -275,6 +279,8 @@ class NavigationState : public WebKit::WebDataSource::ExtraData {
bool was_fetched_via_spdy_;
+ bool was_npn_negotiated_;
+
bool was_translated_;
DISALLOW_COPY_AND_ASSIGN(NavigationState);
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 09c3436..45e2c37 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -2875,13 +2875,20 @@ void RenderView::didReceiveResponse(
if (frame->isViewSourceModeEnabled())
return;
+ NavigationState* navigation_state =
+ NavigationState::FromDataSource(frame->provisionalDataSource());
+ CHECK(navigation_state);
+
// Record that this was a page loaded over SPDY.
if (response.wasFetchedViaSPDY()) {
- NavigationState* navigation_state =
- NavigationState::FromDataSource(frame->provisionalDataSource());
navigation_state->set_was_fetched_via_spdy(true);
}
+ // Record that npn protocol was negotiated when fetching this page.
+ if (response.wasNpnNegotiated()) {
+ navigation_state->set_was_npn_negotiated(true);
+ }
+
// Consider loading an alternate error page for 404 responses.
if (response.httpStatusCode() != 404)
return;
@@ -2890,8 +2897,6 @@ void RenderView::didReceiveResponse(
if (!GetAlternateErrorPageURL(response.url(), HTTP_404).is_valid())
return;
- NavigationState* navigation_state =
- NavigationState::FromDataSource(frame->provisionalDataSource());
navigation_state->set_postpone_loading_data(true);
navigation_state->clear_postponed_data();
}
@@ -4556,6 +4561,27 @@ void RenderView::DumpLoadHistograms() const {
begin_to_finish_doc, kBeginToFinishDocMin, kBeginToFinishDocMax,
kBeginToFinishDocBucketCount);
+ static bool use_spdy_histogram(FieldTrialList::Find("SpdyImpact") &&
+ !FieldTrialList::Find("SpdyImpact")->group_name().empty());
+ if (use_spdy_histogram) {
+ switch (load_type) {
+ case NavigationState::LINK_LOAD_NORMAL:
+ if (navigation_state->was_npn_negotiated()) {
+ DCHECK(FieldTrialList::Find("SpdyImpact")->group_name() ==
+ "_npn_with_http" ||
+ FieldTrialList::Find("SpdyImpact")->group_name() ==
+ "_npn_with_spdy");
+ UMA_HISTOGRAM_CUSTOM_TIMES(FieldTrial::MakeName(
+ "Renderer4.BeginToFinish_LinkLoadNormal_SpdyTrial", "SpdyImpact"),
+ begin_to_finish, kBeginToFinishMin, kBeginToFinishMax,
+ kBeginToFinishBucketCount);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
UMA_HISTOGRAM_CUSTOM_TIMES("Renderer4.StartToFinish",
finish - start, kBeginToFinishMin,
kBeginToFinishMax, kBeginToFinishBucketCount);
diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc
index 09ee5cd..87422d4 100644
--- a/net/http/http_network_layer.cc
+++ b/net/http/http_network_layer.cc
@@ -4,6 +4,7 @@
#include "net/http/http_network_layer.h"
+#include "base/field_trial.h"
#include "base/logging.h"
#include "base/string_util.h"
#include "net/http/http_network_session.h"
@@ -122,7 +123,27 @@ HttpNetworkSession* HttpNetworkLayer::GetSession() {
void HttpNetworkLayer::EnableSpdy(const std::string& mode) {
static const char kDisableSSL[] = "no-ssl";
static const char kDisableCompression[] = "no-compress";
+
+ // We want an A/B experiment between SPDY enabled and SPDY disabled,
+ // but only for pages where SPDY *could have been* negotiated. To do
+ // this, we use NPN, but prevent it from negotiating SPDY. If the
+ // server negotiates HTTP, rather than SPDY, today that will only happen
+ // on servers that installed NPN (and could have done SPDY). But this is
+ // a bit of a hack, as this correlation between NPN and SPDY is not
+ // really guaranteed.
static const char kEnableNPN[] = "npn";
+ static const char kEnableNpnHttpOnly[] = "npn-http";
+
+ // Except for the first element, the order is irrelevant. First element
+ // specifies the fallback in case nothing matches
+ // (SSLClientSocket::kNextProtoNoOverlap). Otherwise, the SSL library
+ // will choose the first overlapping protocol in the server's list, since
+ // it presumedly has a better understanding of which protocol we should
+ // use, therefore the rest of the ordering here is not important.
+ static const char kNpnProtosFull[] =
+ "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy";
+ // No spdy specified.
+ static const char kNpnProtosHttpOnly[] = "\x08http/1.1\x07http1.1";
std::vector<std::string> spdy_options;
SplitString(mode, ',', &spdy_options);
@@ -133,22 +154,17 @@ void HttpNetworkLayer::EnableSpdy(const std::string& mode) {
for (std::vector<std::string>::iterator it = spdy_options.begin();
it != spdy_options.end(); ++it) {
const std::string& option = *it;
-
- // Disable SSL
if (option == kDisableSSL) {
- SpdySession::SetSSLMode(false);
+ SpdySession::SetSSLMode(false); // Disable SSL
} else if (option == kDisableCompression) {
spdy::SpdyFramer::set_enable_compression_default(false);
} else if (option == kEnableNPN) {
- net::HttpNetworkTransaction::SetUseAlternateProtocols(true);
- // Except for the first element, the order is irrelevant. First element
- // specifies the fallback in case nothing matches
- // (SSLClientSocket::kNextProtoNoOverlap). Otherwise, the SSL library
- // will choose the first overlapping protocol in the server's list, since
- // it presumedly has a better understanding of which protocol we should
- // use, therefore the rest of the ordering here is not important.
- HttpNetworkTransaction::SetNextProtos(
- "\x08http/1.1\x07http1.1\x06spdy/1\x04spdy");
+ HttpNetworkTransaction::SetUseAlternateProtocols(true);
+ HttpNetworkTransaction::SetNextProtos(kNpnProtosFull);
+ force_spdy_ = false;
+ } else if (option == kEnableNpnHttpOnly) {
+ HttpNetworkTransaction::SetUseAlternateProtocols(true);
+ HttpNetworkTransaction::SetNextProtos(kNpnProtosHttpOnly);
force_spdy_ = false;
} else if (option.empty() && it == spdy_options.begin()) {
continue;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 0d32ccf..36717c6f 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -911,6 +911,11 @@ int HttpNetworkTransaction::DoInitConnectionComplete(int result) {
// trying to reuse a keep-alive connection.
reused_socket_ = connection_->is_reused();
if (reused_socket_) {
+ if (using_ssl_) {
+ SSLClientSocket* ssl_socket =
+ reinterpret_cast<SSLClientSocket*>(connection_->socket());
+ response_.was_npn_negotiated = ssl_socket->wasNpnNegotiated();
+ }
next_state_ = STATE_SEND_REQUEST;
} else {
// Now we have a TCP connected socket. Perform other connection setup as
@@ -962,9 +967,15 @@ int HttpNetworkTransaction::DoSSLConnectComplete(int result) {
// here, then we know that we called SSL_ImportFD.
if (result == OK || IsCertificateError(result))
status = ssl_socket->GetNextProto(&proto);
- using_spdy_ = (status == SSLClientSocket::kNextProtoNegotiated &&
- SSLClientSocket::NextProtoFromString(proto) ==
- SSLClientSocket::kProtoSPDY1);
+
+ if (status == SSLClientSocket::kNextProtoNegotiated) {
+ ssl_socket->setWasNpnNegotiated(true);
+ response_.was_npn_negotiated = true;
+ if (SSLClientSocket::NextProtoFromString(proto) ==
+ SSLClientSocket::kProtoSPDY1) {
+ using_spdy_ = true;
+ }
+ }
if (alternate_protocol_mode_ == kUsingAlternateProtocol &&
alternate_protocol_ == HttpAlternateProtocols::NPN_SPDY_1 &&
@@ -1130,7 +1141,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) {
if (result == OK)
return result;
} else if ((result == ERR_SSL_DECOMPRESSION_FAILURE_ALERT ||
- result == ERR_SSL_BAD_RECORD_MAC_ALERT ) &&
+ result == ERR_SSL_BAD_RECORD_MAC_ALERT) &&
ssl_config_.tls1_enabled) {
// Some buggy servers select DEFLATE compression when offered and then
// fail to ever decompress anything. They will send a fatal alert telling
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 079f3ae..b427814 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -4868,6 +4868,8 @@ TEST_F(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) {
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);
std::string response_data;
ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
@@ -5084,6 +5086,7 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) {
SSLSocketDataProvider ssl(true, OK);
ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
ssl.next_proto = "spdy/1";
+ ssl.was_npn_negotiated = true;
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
MockWrite spdy_writes[] = {
@@ -5134,6 +5137,8 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) {
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(trans.get(), &response_data));
EXPECT_EQ("hello!", response_data);
@@ -5213,6 +5218,7 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) {
SSLSocketDataProvider ssl(true, OK);
ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
ssl.next_proto = "spdy/1";
+ ssl.was_npn_negotiated = true;
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
MockWrite spdy_writes[] = {
@@ -5253,6 +5259,8 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) {
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);
std::string response_data;
ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data));
@@ -5268,6 +5276,8 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) {
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(trans.get(), &response_data));
EXPECT_EQ("hello!", response_data);
@@ -5307,7 +5317,10 @@ TEST_F(HttpNetworkTransactionTest,
SSLSocketDataProvider ssl(true, OK);
ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
ssl.next_proto = "spdy/1";
+ ssl.was_npn_negotiated = true;
session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+ // Make sure we use ssl for spdy here.
+ SpdySession::SetSSLMode(true);
MockWrite spdy_writes[] = {
MockWrite(true, reinterpret_cast<const char*>(kGetSyn),
@@ -5354,7 +5367,6 @@ TEST_F(HttpNetworkTransactionTest,
session, BoundNetLog());
TCPSocketParams tcp_params("www.google.com", 443, MEDIUM, GURL(), false);
spdy_session->Connect("www.google.com:443", tcp_params, MEDIUM);
-
trans.reset(new HttpNetworkTransaction(session));
rv = trans->Start(&request, &callback, BoundNetLog());
@@ -5365,6 +5377,8 @@ TEST_F(HttpNetworkTransactionTest,
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(trans.get(), &response_data));
EXPECT_EQ("hello!", response_data);
@@ -5681,4 +5695,63 @@ TEST_F(HttpNetworkTransactionTest,
EXPECT_EQ("ok.", response_data);
}
+// This tests the case that a request is issued via http instead of spdy after
+// npn is negotiated.
+TEST_F(HttpNetworkTransactionTest, NpnWithHttpOverSSL) {
+ HttpNetworkTransaction::SetUseAlternateProtocols(true);
+ HttpNetworkTransaction::SetNextProtos("\x08http/1.1\x07http1.1");
+ SessionDependencies session_deps;
+ HttpRequestInfo request;
+ request.method = "GET";
+ request.url = GURL("https://www.google.com/");
+ request.load_flags = 0;
+
+ MockWrite data_writes[] = {
+ MockWrite("GET / HTTP/1.1\r\n"
+ "Host: www.google.com\r\n"
+ "Connection: keep-alive\r\n\r\n"),
+ };
+
+ MockRead data_reads[] = {
+ MockRead("HTTP/1.1 200 OK\r\n"),
+ MockRead("Alternate-Protocol: 443:npn-spdy/1\r\n\r\n"),
+ MockRead("hello world"),
+ MockRead(false, OK),
+ };
+
+ SSLSocketDataProvider ssl(true, OK);
+ ssl.next_proto_status = SSLClientSocket::kNextProtoNegotiated;
+ ssl.next_proto = "http/1.1";
+
+ session_deps.socket_factory.AddSSLSocketDataProvider(&ssl);
+
+ StaticSocketDataProvider data(data_reads, arraysize(data_reads),
+ data_writes, arraysize(data_writes));
+ session_deps.socket_factory.AddSocketDataProvider(&data);
+
+ TestCompletionCallback callback;
+
+ scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps));
+ 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);
+
+ EXPECT_FALSE(response->was_fetched_via_spdy);
+ EXPECT_TRUE(response->was_npn_negotiated);
+
+ HttpNetworkTransaction::SetNextProtos("");
+ HttpNetworkTransaction::SetUseAlternateProtocols(false);
+}
} // namespace net
diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc
index 3ce8cdd..d1e7911 100644
--- a/net/http/http_response_info.cc
+++ b/net/http/http_response_info.cc
@@ -41,7 +41,10 @@ enum {
// This bit is set if the response was received via SPDY.
RESPONSE_INFO_WAS_SPDY = 1 << 13,
- // This bit is set if the response was received via SPDY.
+ // This bit is set if the request has NPN negotiated.
+ RESPONSE_INFO_WAS_NPN = 1 << 14,
+
+ // This bit is set if the request was fetched via an explicit proxy.
RESPONSE_INFO_WAS_PROXY = 1 << 15,
// TODO(darin): Add other bits to indicate alternate request methods.
@@ -51,6 +54,7 @@ enum {
HttpResponseInfo::HttpResponseInfo()
: was_cached(false),
was_fetched_via_spdy(false),
+ was_npn_negotiated(false),
was_fetched_via_proxy(false) {
}
@@ -113,6 +117,8 @@ bool HttpResponseInfo::InitFromPickle(const Pickle& pickle,
was_fetched_via_spdy = (flags & RESPONSE_INFO_WAS_SPDY) != 0;
+ was_npn_negotiated = (flags & RESPONSE_INFO_WAS_NPN) != 0;
+
was_fetched_via_proxy = (flags & RESPONSE_INFO_WAS_PROXY) != 0;
*response_truncated = (flags & RESPONSE_INFO_TRUNCATED) ? true : false;
@@ -136,6 +142,8 @@ void HttpResponseInfo::Persist(Pickle* pickle,
flags |= RESPONSE_INFO_TRUNCATED;
if (was_fetched_via_spdy)
flags |= RESPONSE_INFO_WAS_SPDY;
+ if (was_npn_negotiated)
+ flags |= RESPONSE_INFO_WAS_NPN;
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 a61c299..518d8ac3 100644
--- a/net/http/http_response_info.h
+++ b/net/http/http_response_info.h
@@ -35,6 +35,9 @@ class HttpResponseInfo {
// True if the request was fetched over a SPDY channel.
bool was_fetched_via_spdy;
+ // True if the npn was negotiated for this request.
+ bool was_npn_negotiated;
+
// 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/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index 13a30dc..8c64e5f 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -389,6 +389,10 @@ SSLClientSocket::NextProtoStatus MockSSLClientSocket::GetNextProto(
return data_->next_proto_status;
}
+bool MockSSLClientSocket::wasNpnNegotiated() const {
+ return data_->was_npn_negotiated;
+}
+
MockRead StaticSocketDataProvider::GetNextRead() {
DCHECK(!at_read_eof());
reads_[read_index_].time_stamp = base::Time::Now();
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 6e0698f..dca6307 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -235,11 +235,13 @@ class DynamicSocketDataProvider : public SocketDataProvider {
struct SSLSocketDataProvider {
SSLSocketDataProvider(bool async, int result)
: connect(async, result),
- next_proto_status(SSLClientSocket::kNextProtoUnsupported) { }
+ next_proto_status(SSLClientSocket::kNextProtoUnsupported),
+ was_npn_negotiated(false) { }
MockConnect connect;
SSLClientSocket::NextProtoStatus next_proto_status;
std::string next_proto;
+ bool was_npn_negotiated;
};
// A DataProvider where the client must write a request before the reads (e.g.
@@ -512,6 +514,7 @@ class MockSSLClientSocket : public MockClientSocket {
// SSLClientSocket methods:
virtual void GetSSLInfo(net::SSLInfo* ssl_info);
virtual NextProtoStatus GetNextProto(std::string* proto);
+ virtual bool wasNpnNegotiated() const;
// This MockSocket does not implement the manual async IO feature.
virtual void OnReadComplete(const MockRead& data) { NOTIMPLEMENTED(); }
diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h
index 77537ea..961447b 100644
--- a/net/socket/ssl_client_socket.h
+++ b/net/socket/ssl_client_socket.h
@@ -22,6 +22,8 @@ class SSLInfo;
//
class SSLClientSocket : public ClientSocket {
public:
+ SSLClientSocket() : was_npn_negotiated_(false) {
+ }
// Next Protocol Negotiation (NPN) allows a TLS client and server to come to
// an agreement about the application level protocol to speak over a
// connection.
@@ -68,6 +70,18 @@ class SSLClientSocket : public ClientSocket {
return kProtoUnknown;
}
}
+
+ virtual bool wasNpnNegotiated() const {
+ return was_npn_negotiated_;
+ }
+
+ virtual bool setWasNpnNegotiated(bool negotiated) {
+ return was_npn_negotiated_ = negotiated;
+ }
+
+ private:
+ // True if NPN was responded to, independent of selecting SPDY or HTTP.
+ bool was_npn_negotiated_;
};
} // namespace net
diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc
index 5c1f133..25cafec 100644
--- a/net/spdy/spdy_session.cc
+++ b/net/spdy/spdy_session.cc
@@ -967,6 +967,11 @@ bool SpdySession::Respond(const spdy::SpdyHeaderBlock& headers,
GetSSLInfo(&response.ssl_info);
response.request_time = stream->GetRequestTime();
response.vary_data.Init(*stream->GetRequestInfo(), *response.headers);
+ if (is_secure_) {
+ SSLClientSocket* ssl_socket =
+ reinterpret_cast<SSLClientSocket*>(connection_->socket());
+ response.was_npn_negotiated = ssl_socket->wasNpnNegotiated();
+ }
rv = stream->OnResponseReceived(response);
} else {
rv = ERR_INVALID_RESPONSE;
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 250e4a2..2a7331a 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -376,6 +376,12 @@ class URLRequest {
return response_info_.was_fetched_via_spdy;
}
+ // Returns true if the URLRequest was delivered after NPN is negotiated,
+ // using either SPDY or HTTP.
+ bool was_npn_negotiated() const {
+ return response_info_.was_npn_negotiated;
+ }
+
// Get all response headers, as a HttpResponseHeaders object. See comments
// in HttpResponseHeaders class as to the format of the data.
net::HttpResponseHeaders* response_headers() const;
diff --git a/webkit/glue/resource_loader_bridge.cc b/webkit/glue/resource_loader_bridge.cc
index 2dccfac..124fd61 100644
--- a/webkit/glue/resource_loader_bridge.cc
+++ b/webkit/glue/resource_loader_bridge.cc
@@ -25,6 +25,7 @@ ResourceLoaderBridge::ResponseInfo::ResponseInfo() {
content_length = -1;
appcache_id = appcache::kNoCacheId;
was_fetched_via_spdy = false;
+ was_npn_negotiated = false;
}
ResourceLoaderBridge::ResponseInfo::~ResponseInfo() {
diff --git a/webkit/glue/resource_loader_bridge.h b/webkit/glue/resource_loader_bridge.h
index a1773e9..b78e3a5 100644
--- a/webkit/glue/resource_loader_bridge.h
+++ b/webkit/glue/resource_loader_bridge.h
@@ -124,6 +124,9 @@ class ResourceLoaderBridge {
// True if the response was delivered using SPDY.
bool was_fetched_via_spdy;
+
+ // True if the response was delivered after NPN is negotiated.
+ bool was_npn_negotiated;
};
// See the SyncLoad method declared below. (The name of this struct is not
diff --git a/webkit/glue/weburlloader_impl.cc b/webkit/glue/weburlloader_impl.cc
index db97cf3..b2d936c 100644
--- a/webkit/glue/weburlloader_impl.cc
+++ b/webkit/glue/weburlloader_impl.cc
@@ -171,6 +171,7 @@ void PopulateURLResponse(
response->setAppCacheID(info.appcache_id);
response->setAppCacheManifestURL(info.appcache_manifest_url);
response->setWasFetchedViaSPDY(info.was_fetched_via_spdy);
+ response->setWasNpnNegotiated(info.was_npn_negotiated);
const net::HttpResponseHeaders* headers = info.headers;
if (!headers)