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;