diff options
-rw-r--r-- | chrome/browser/browser_init.cc | 7 | ||||
-rw-r--r-- | chrome/browser/browser_main.cc | 49 | ||||
-rw-r--r-- | chrome/browser/renderer_host/resource_dispatcher_host.cc | 1 | ||||
-rw-r--r-- | chrome/common/render_messages.h | 6 | ||||
-rw-r--r-- | chrome/renderer/loadtimes_extension_bindings.cc | 3 | ||||
-rw-r--r-- | chrome/renderer/navigation_state.h | 6 | ||||
-rw-r--r-- | chrome/renderer/render_view.cc | 34 | ||||
-rw-r--r-- | net/http/http_network_layer.cc | 40 | ||||
-rw-r--r-- | net/http/http_network_transaction.cc | 19 | ||||
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 75 | ||||
-rw-r--r-- | net/http/http_response_info.cc | 10 | ||||
-rw-r--r-- | net/http/http_response_info.h | 3 | ||||
-rw-r--r-- | net/socket/socket_test_util.cc | 4 | ||||
-rw-r--r-- | net/socket/socket_test_util.h | 5 | ||||
-rw-r--r-- | net/socket/ssl_client_socket.h | 14 | ||||
-rw-r--r-- | net/spdy/spdy_session.cc | 5 | ||||
-rw-r--r-- | net/url_request/url_request.h | 6 | ||||
-rw-r--r-- | webkit/glue/resource_loader_bridge.cc | 1 | ||||
-rw-r--r-- | webkit/glue/resource_loader_bridge.h | 3 | ||||
-rw-r--r-- | webkit/glue/weburlloader_impl.cc | 1 |
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) |