Integrate QUIC info into net-internals.
Review URL: https://chromiumcodereview.appspot.com/11696010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@175239 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/resources/net_internals/browser_bridge.js b/chrome/browser/resources/net_internals/browser_bridge.js
index d86aae0a..f0fa6e4 100644
--- a/chrome/browser/resources/net_internals/browser_bridge.js
+++ b/chrome/browser/resources/net_internals/browser_bridge.js
@@ -54,6 +54,9 @@
this.pollableDataHelpers_.historicNetworkStats =
new PollableDataHelper('onHistoricNetworkStatsChanged',
this.sendGetHistoricNetworkStats.bind(this));
+ this.pollableDataHelpers_.quicInfo =
+ new PollableDataHelper('onQuicInfoChanged',
+ this.sendGetQuicInfo.bind(this));
this.pollableDataHelpers_.spdySessionInfo =
new PollableDataHelper('onSpdySessionInfoChanged',
this.sendGetSpdySessionInfo.bind(this));
@@ -212,6 +215,10 @@
this.send('flushSocketPools');
},
+ sendGetQuicInfo: function() {
+ this.send('getQuicInfo');
+ },
+
sendGetSpdySessionInfo: function() {
this.send('getSpdySessionInfo');
},
@@ -327,6 +334,10 @@
historicNetworkStats);
},
+ receivedQuicInfo: function(quicInfo) {
+ this.pollableDataHelpers_.quicInfo.update(quicInfo);
+ },
+
receivedSpdySessionInfo: function(spdySessionInfo) {
this.pollableDataHelpers_.spdySessionInfo.update(spdySessionInfo);
},
@@ -505,6 +516,17 @@
},
/**
+ * Adds a listener of the QUIC info. |observer| will be called back
+ * when data is received, through:
+ *
+ * observer.onQuicInfoChanged(quicInfo)
+ */
+ addQuicInfoObserver: function(observer, ignoreWhenUnchanged) {
+ this.pollableDataHelpers_.quicInfo.addObserver(
+ observer, ignoreWhenUnchanged);
+ },
+
+ /**
* Adds a listener of the SPDY info. |observer| will be called back
* when data is received, through:
*
diff --git a/chrome/browser/resources/net_internals/category_tabs.html b/chrome/browser/resources/net_internals/category_tabs.html
index f0653f8a..9183543 100644
--- a/chrome/browser/resources/net_internals/category_tabs.html
+++ b/chrome/browser/resources/net_internals/category_tabs.html
@@ -9,6 +9,7 @@
<a href="#dns" id=tab-handle-dns>DNS</a>
<a href="#sockets" id=tab-handle-sockets>Sockets</a>
<a href="#spdy" id=tab-handle-spdy>SPDY</a>
+ <a href="#quic" id=tab-handle-quic>QUIC</a>
<a href="#httpPipeline" id=tab-handle-http-pipeline>Pipelining</a>
<a href="#httpCache" id=tab-handle-http-cache>Cache</a>
<!-- Tab is only shown on Windows -->
diff --git a/chrome/browser/resources/net_internals/index.html b/chrome/browser/resources/net_internals/index.html
index 4c37b75..0184b165 100644
--- a/chrome/browser/resources/net_internals/index.html
+++ b/chrome/browser/resources/net_internals/index.html
@@ -28,6 +28,7 @@
<include src="dns_view.html"/>
<include src="sockets_view.html"/>
<include src="spdy_view.html"/>
+ <include src="quic_view.html"/>
<include src="http_pipeline_view.html"/>
<include src="http_cache_view.html"/>
<include src="bandwidth_view.html"/>
diff --git a/chrome/browser/resources/net_internals/index.js b/chrome/browser/resources/net_internals/index.js
index 269cb670..39f36b5 100644
--- a/chrome/browser/resources/net_internals/index.js
+++ b/chrome/browser/resources/net_internals/index.js
@@ -37,6 +37,7 @@
<include src="log_view_painter.js"/>
<include src="log_grouper.js"/>
<include src="proxy_view.js"/>
+<include src="quic_view.js"/>
<include src="socket_pool_wrapper.js"/>
<include src="sockets_view.js"/>
<include src="spdy_view.js"/>
diff --git a/chrome/browser/resources/net_internals/main.js b/chrome/browser/resources/net_internals/main.js
index db434815..b055ca9 100644
--- a/chrome/browser/resources/net_internals/main.js
+++ b/chrome/browser/resources/net_internals/main.js
@@ -90,6 +90,7 @@
tabs.addTab(SocketsView.TAB_HANDLE_ID, SocketsView.getInstance(),
false, true);
tabs.addTab(SpdyView.TAB_HANDLE_ID, SpdyView.getInstance(), false, true);
+ tabs.addTab(QuicView.TAB_HANDLE_ID, QuicView.getInstance(), false, true);
tabs.addTab(HttpPipelineView.TAB_HANDLE_ID, HttpPipelineView.getInstance(),
false, true);
tabs.addTab(HttpCacheView.TAB_HANDLE_ID, HttpCacheView.getInstance(),
diff --git a/chrome/browser/resources/net_internals/quic_view.html b/chrome/browser/resources/net_internals/quic_view.html
new file mode 100644
index 0000000..a84bebc
--- /dev/null
+++ b/chrome/browser/resources/net_internals/quic_view.html
@@ -0,0 +1,24 @@
+<div id=quic-view-tab-content class=content-box>
+ <h4>QUIC Status</h4>
+ <ul>
+ <li>QUIC Enabled: <span id=quic-view-enabled-span>????</span></li>
+ <li>Origin Port To Force QUIC On: <span id=quic-view-force-port-span>????</span></li>
+ </ul>
+
+ <h4>QUIC sessions</h4>
+ <!-- Only one of these two are shown -->
+ <span id=quic-view-session-none-span>None</span>
+ <span id=quic-view-session-link-span style="display: none;">
+ <a href="#events&q=type:QUIC_SESSION%20is:active">View live QUIC sessions</a>
+ </span>
+ <p>
+ <div id=quic-view-session-div>
+ </div>
+ </p>
+
+ <h4>Alternate Protocol Mappings</h4>
+ <p>
+ <div id=quic-view-alternate-protocol-mappings-div>
+ </div>
+ </p>
+</div>
diff --git a/chrome/browser/resources/net_internals/quic_view.js b/chrome/browser/resources/net_internals/quic_view.js
new file mode 100644
index 0000000..a365ca3bd
--- /dev/null
+++ b/chrome/browser/resources/net_internals/quic_view.js
@@ -0,0 +1,113 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * This view displays a summary of the state of each QUIC session, and
+ * has links to display them in the events tab.
+ */
+var QuicView = (function() {
+ 'use strict';
+
+ // We inherit from DivView.
+ var superClass = DivView;
+
+ /**
+ * @constructor
+ */
+ function QuicView() {
+ assertFirstConstructorCall(QuicView);
+
+ // Call superclass's constructor.
+ superClass.call(this, QuicView.MAIN_BOX_ID);
+
+ g_browser.addQuicInfoObserver(this, true);
+
+ this.quicEnabledSpan_ = $(QuicView.ENABLED_SPAN_ID);
+ this.quicForcePortSpan_ = $(QuicView.FORCE_PORT_SPAN_ID);
+
+ this.quicSessionNoneSpan_ = $(QuicView.SESSION_NONE_SPAN_ID);
+ this.quicSessionLinkSpan_ = $(QuicView.SESSION_LINK_SPAN_ID);
+ this.quicSessionDiv_ = $(QuicView.SESSION_DIV_ID);
+ }
+
+ // ID for special HTML element in category_tabs.html
+ QuicView.TAB_HANDLE_ID = 'tab-handle-quic';
+
+ // IDs for special HTML elements in quic_view.html
+ QuicView.MAIN_BOX_ID = 'quic-view-tab-content';
+ QuicView.ENABLED_SPAN_ID = 'quic-view-enabled-span';
+ QuicView.FORCE_PORT_SPAN_ID = 'quic-view-force-port-span';
+ QuicView.SESSION_NONE_SPAN_ID = 'quic-view-session-none-span';
+ QuicView.SESSION_LINK_SPAN_ID = 'quic-view-session-link-span';
+ QuicView.SESSION_DIV_ID = 'quic-view-session-div';
+
+ cr.addSingletonGetter(QuicView);
+
+ QuicView.prototype = {
+ // Inherit the superclass's methods.
+ __proto__: superClass.prototype,
+
+ onLoadLogFinish: function(data) {
+ return this.onQuicInfoChanged(data.quicInfo);
+ },
+
+ /**
+ * If there are any sessions, display a single table with
+ * information on each QUIC session. Otherwise, displays "None".
+ */
+ onQuicInfoChanged: function(quicInfo) {
+ this.quicSessionDiv_.innerHTML = '';
+
+ var hasNoSession =
+ (!quicInfo || !quicInfo.sessions || quicInfo.sessions.length == 0);
+ setNodeDisplay(this.quicSessionNoneSpan_, hasNoSession);
+ setNodeDisplay(this.quicSessionLinkSpan_, !hasNoSession);
+
+ // Only want to be hide the tab if there's no data. In the case of having
+ // data but no sessions, still show the tab.
+ if (!quicInfo)
+ return false;
+
+ this.quicEnabledSpan_.textContent = quicInfo.quic_enabled;
+ this.quicForcePortSpan_.textContent =
+ quicInfo.origin_port_to_force_quic_on;
+
+ if (!hasNoSession) {
+ var tablePrinter = createSessionTablePrinter(quicInfo.sessions);
+ tablePrinter.toHTML(this.quicSessionDiv_, 'styled-table');
+ }
+
+ return true;
+ },
+ };
+
+ /**
+ * Creates a table printer to print out the state of list of QUIC sessions.
+ */
+ function createSessionTablePrinter(quicSessions) {
+ var tablePrinter = new TablePrinter();
+
+ tablePrinter.addHeaderCell('Host');
+ tablePrinter.addHeaderCell('Peer address');
+ tablePrinter.addHeaderCell('GUID');
+ tablePrinter.addHeaderCell('Active streams');
+
+ for (var i = 0; i < quicSessions.length; i++) {
+ var session = quicSessions[i];
+ tablePrinter.addRow();
+
+ var host = session.host_port_pair;
+ if (session.aliases)
+ host += ' ' + session.aliases.join(' ');
+ tablePrinter.addCell(host);
+
+ tablePrinter.addCell(session.peer_address);
+ tablePrinter.addCell(session.guid);
+ tablePrinter.addCell(session.open_streams);
+ }
+ return tablePrinter;
+ }
+
+ return QuicView;
+})();
diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
index 2af5d8c5..2cb52ad4 100644
--- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
+++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc
@@ -514,6 +514,7 @@
void OnGetSpdySessionInfo(const ListValue* list);
void OnGetSpdyStatus(const ListValue* list);
void OnGetSpdyAlternateProtocolMappings(const ListValue* list);
+ void OnGetQuicInfo(const ListValue* list);
#if defined(OS_WIN)
void OnGetServiceProviders(const ListValue* list);
#endif
@@ -722,6 +723,10 @@
"getSpdyAlternateProtocolMappings",
base::Bind(&IOThreadImpl::CallbackHelper,
&IOThreadImpl::OnGetSpdyAlternateProtocolMappings, proxy_));
+ web_ui()->RegisterMessageCallback(
+ "getQuicInfo",
+ base::Bind(&IOThreadImpl::CallbackHelper,
+ &IOThreadImpl::OnGetQuicInfo, proxy_));
#if defined(OS_WIN)
web_ui()->RegisterMessageCallback(
"getServiceProviders",
@@ -1425,6 +1430,17 @@
SendJavascriptCommand("receivedSpdyAlternateProtocolMappings", dict_list);
}
+void NetInternalsMessageHandler::IOThreadImpl::OnGetQuicInfo(
+ const ListValue* list) {
+ DCHECK(!list);
+ net::HttpNetworkSession* http_network_session =
+ GetHttpNetworkSession(GetMainContext());
+
+ Value* quic_info = http_network_session ?
+ http_network_session->QuicInfoToValue() : NULL;
+ SendJavascriptCommand("receivedQuicInfo", quic_info);
+}
+
#if defined(OS_WIN)
void NetInternalsMessageHandler::IOThreadImpl::OnGetServiceProviders(
const ListValue* list) {
diff --git a/chrome/test/data/webui/net_internals/log_util.js b/chrome/test/data/webui/net_internals/log_util.js
index d005f17..01c94667 100644
--- a/chrome/test/data/webui/net_internals/log_util.js
+++ b/chrome/test/data/webui/net_internals/log_util.js
@@ -113,6 +113,7 @@
dns: true,
sockets: true,
spdy: true,
+ quic: true,
httpPipeline: false,
httpCache: true,
serviceProviders: cr.isWindows,
diff --git a/chrome/test/data/webui/net_internals/main.js b/chrome/test/data/webui/net_internals/main.js
index 7e651a1..d589b87 100644
--- a/chrome/test/data/webui/net_internals/main.js
+++ b/chrome/test/data/webui/net_internals/main.js
@@ -30,6 +30,7 @@
dns: true,
sockets: true,
spdy: true,
+ quic: true,
httpPipeline: true,
httpCache: true,
serviceProviders: cr.isWindows,
diff --git a/chrome/test/data/webui/net_internals/net_internals_test.js b/chrome/test/data/webui/net_internals/net_internals_test.js
index 12445b5..b6451ee 100644
--- a/chrome/test/data/webui/net_internals/net_internals_test.js
+++ b/chrome/test/data/webui/net_internals/net_internals_test.js
@@ -247,6 +247,7 @@
dns: DnsView.TAB_HANDLE_ID,
sockets: SocketsView.TAB_HANDLE_ID,
spdy: SpdyView.TAB_HANDLE_ID,
+ quic: QuicView.TAB_HANDLE_ID,
httpPipeline: HttpPipelineView.TAB_HANDLE_ID,
httpCache: HttpCacheView.TAB_HANDLE_ID,
serviceProviders: ServiceProvidersView.TAB_HANDLE_ID,
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 9128047c..b12a1eed1 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -179,6 +179,16 @@
return spdy_session_pool_.SpdySessionPoolInfoToValue();
}
+Value* HttpNetworkSession::QuicInfoToValue() const {
+ base::DictionaryValue* dict = new base::DictionaryValue();
+ dict->Set("sessions", quic_stream_factory_.QuicStreamFactoryInfoToValue());
+ dict->SetBoolean("quic_enabled", params_.origin_port_to_force_quic_on != 0);
+ dict->SetInteger("origin_port_to_force_quic_on",
+ params_.origin_port_to_force_quic_on);
+
+ return dict;
+}
+
void HttpNetworkSession::CloseAllConnections() {
normal_socket_pool_manager_->FlushSocketPoolsWithError(ERR_ABORTED);
websocket_socket_pool_manager_->FlushSocketPoolsWithError(ERR_ABORTED);
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index c7f7783..8a15c9c8 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -141,6 +141,10 @@
// responsible for deleting the returned value.
base::Value* SpdySessionPoolInfoToValue() const;
+ // Creates a Value summary of the state of the QUIC sessions and
+ // configuration. The caller is responsible for deleting the returned value.
+ base::Value* QuicInfoToValue() const;
+
void CloseAllConnections();
void CloseIdleConnections();
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc
index 19684af..3beba72 100644
--- a/net/quic/quic_client_session.cc
+++ b/net/quic/quic_client_session.cc
@@ -6,6 +6,8 @@
#include "base/message_loop.h"
#include "base/stl_util.h"
+#include "base/string_number_conversions.h"
+#include "base/values.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/quic/quic_connection_helper.h"
@@ -113,6 +115,15 @@
stream_factory_->OnSessionClose(this);
}
+Value* QuicClientSession::GetInfoAsValue(const HostPortPair& pair) const {
+ DictionaryValue* dict = new DictionaryValue();
+ dict->SetString("host_port_pair", pair.ToString());
+ dict->SetInteger("open_streams", GetNumOpenStreams());
+ dict->SetString("peer_address", peer_address().ToString());
+ dict->SetString("guid", base::Uint64ToString(guid()));
+ return dict;
+}
+
void QuicClientSession::OnReadComplete(int result) {
read_pending_ = false;
// TODO(rch): Inform the connection about the result.
diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h
index c17f632..c2044fe 100644
--- a/net/quic/quic_client_session.h
+++ b/net/quic/quic_client_session.h
@@ -48,6 +48,8 @@
// Close the session because of |error|.
void CloseSessionOnError(int error);
+ base::Value* GetInfoAsValue(const HostPortPair& pair) const;
+
protected:
// QuicSession methods:
virtual ReliableQuicStream* CreateIncomingReliableStream(
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index c21682f4..02d2a6c 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -267,7 +267,7 @@
implicitly_created_streams_.count(id) == 0;
}
-size_t QuicSession::GetNumOpenStreams() {
+size_t QuicSession::GetNumOpenStreams() const {
return stream_map_.size() + implicitly_created_streams_.size();
}
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index b124226..2bc06d2 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -76,12 +76,13 @@
const IPEndPoint& peer_address() const {
return connection_->peer_address();
}
+ QuicGuid guid() const { return connection_->guid(); }
QuicPacketCreator::Options* options() { return connection()->options(); }
// Returns the number of currently open streams, including those which have
// been implicitly created.
- virtual size_t GetNumOpenStreams();
+ virtual size_t GetNumOpenStreams() const;
void MarkWriteBlocked(QuicStreamId id);
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 47a5242..c9191c9 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -10,6 +10,7 @@
#include "base/message_loop_proxy.h"
#include "base/rand_util.h"
#include "base/stl_util.h"
+#include "base/values.h"
#include "net/base/host_resolver.h"
#include "net/base/net_errors.h"
#include "net/base/single_request_host_resolver.h"
@@ -333,6 +334,19 @@
DCHECK(all_sessions_.empty());
}
+base::Value* QuicStreamFactory::QuicStreamFactoryInfoToValue() const {
+ base::ListValue* list = new base::ListValue();
+
+ for (SessionMap::const_iterator it = active_sessions_.begin();
+ it != active_sessions_.end(); ++it) {
+ const HostPortProxyPair& pair = it->first;
+ const QuicClientSession* session = it->second;
+
+ list->Append(session->GetInfoAsValue(pair.first));
+ }
+ return list;
+}
+
bool QuicStreamFactory::HasActiveSession(
const HostPortProxyPair& host_port_proxy_pair) {
return ContainsKey(active_sessions_, host_port_proxy_pair);
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index 52a6ae6..077835d 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -93,6 +93,8 @@
// Closes all current sessions.
void CloseAllSessions(int error);
+ base::Value* QuicStreamFactoryInfoToValue() const;
+
private:
class Job;