Add a probability to Alternate-Protocol support. Can be enabled either via a field trial or a command line flag.

Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=281777

Review URL: https://codereview.chromium.org/339663010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281861 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc
index 0704de4..258555f 100644
--- a/net/http/http_network_layer.cc
+++ b/net/http/http_network_layer.cc
@@ -51,9 +51,7 @@
 
 // static
 void HttpNetworkLayer::ForceAlternateProtocol() {
-  PortAlternateProtocolPair pair;
-  pair.port = 443;
-  pair.protocol = NPN_SPDY_3;
+  AlternateProtocolInfo pair(443, NPN_SPDY_3, 1);
   HttpServerPropertiesImpl::ForceAlternateProtocol(pair);
 }
 
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index ff178da..43153f7 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -81,6 +81,7 @@
       force_spdy_over_ssl(true),
       force_spdy_always(false),
       use_alternate_protocols(false),
+      alternate_protocol_probability_threshold(0),
       enable_websocket_over_spdy(false),
       enable_quic(false),
       enable_quic_port_selection(true),
@@ -182,6 +183,9 @@
   if (HpackHuffmanAggregator::UseAggregator()) {
     huffman_aggregator_.reset(new HpackHuffmanAggregator());
   }
+
+  http_server_properties_->SetAlternateProtocolProbabilityThreshold(
+      params.alternate_protocol_probability_threshold);
 }
 
 HttpNetworkSession::~HttpNetworkSession() {
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 272f96e7..e265a914 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -104,6 +104,7 @@
     // Noe: Using this in the case of NPN for HTTP only results in the browser
     // trying SSL and then falling back to http.
     bool use_alternate_protocols;
+    double alternate_protocol_probability_threshold;
     bool enable_websocket_over_spdy;
 
     bool enable_quic;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 0a2c8c4..f4df073 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -79,17 +79,20 @@
     HttpNetworkSession* session,
     const HttpResponseHeaders& headers,
     const HostPortPair& http_host_port_pair) {
-  std::string alternate_protocol_str;
-
-  if (!headers.EnumerateHeader(NULL, kAlternateProtocolHeader,
-                               &alternate_protocol_str)) {
-    // Header is not present.
+  if (!headers.HasHeader(kAlternateProtocolHeader))
     return;
+
+  std::vector<std::string> alternate_protocol_values;
+  void* iter = NULL;
+  std::string alternate_protocol_str;
+  while (headers.EnumerateHeader(&iter, kAlternateProtocolHeader,
+                                 &alternate_protocol_str)) {
+    alternate_protocol_values.push_back(alternate_protocol_str);
   }
 
   session->http_stream_factory()->ProcessAlternateProtocol(
       session->http_server_properties(),
-      alternate_protocol_str,
+      alternate_protocol_values,
       http_host_port_pair,
       *session);
 }
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index ab64040..8c078c4 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -7536,7 +7536,7 @@
       session->http_server_properties();
   http_server_properties->SetAlternateProtocol(
       HostPortPair("host.with.alternate", 80), 443,
-      AlternateProtocolFromNextProto(next_proto));
+      AlternateProtocolFromNextProto(next_proto), 1);
 
   return session;
 }
@@ -8448,11 +8448,10 @@
   EXPECT_EQ("hello world", response_data);
 
   ASSERT_TRUE(http_server_properties.HasAlternateProtocol(http_host_port_pair));
-  const PortAlternateProtocolPair alternate =
+  const AlternateProtocolInfo alternate =
       http_server_properties.GetAlternateProtocol(http_host_port_pair);
-  PortAlternateProtocolPair expected_alternate;
-  expected_alternate.port = 443;
-  expected_alternate.protocol = AlternateProtocolFromNextProto(GetParam());
+  AlternateProtocolInfo expected_alternate(
+      443, AlternateProtocolFromNextProto(GetParam()), 1);
   EXPECT_TRUE(expected_alternate.Equals(alternate));
 }
 
@@ -8488,7 +8487,7 @@
   http_server_properties->SetAlternateProtocol(
       HostPortPair::FromURL(request.url),
       666 /* port is ignored by MockConnect anyway */,
-      AlternateProtocolFromNextProto(GetParam()));
+      AlternateProtocolFromNextProto(GetParam()), 1);
 
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
@@ -8509,7 +8508,7 @@
 
   ASSERT_TRUE(http_server_properties->HasAlternateProtocol(
       HostPortPair::FromURL(request.url)));
-  const PortAlternateProtocolPair alternate =
+  const AlternateProtocolInfo alternate =
       http_server_properties->GetAlternateProtocol(
           HostPortPair::FromURL(request.url));
   EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol);
@@ -8550,7 +8549,7 @@
   http_server_properties->SetAlternateProtocol(
       HostPortPair::FromURL(restricted_port_request.url),
       kUnrestrictedAlternatePort,
-      AlternateProtocolFromNextProto(GetParam()));
+      AlternateProtocolFromNextProto(GetParam()), 1);
 
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
@@ -8601,7 +8600,7 @@
   http_server_properties->SetAlternateProtocol(
       HostPortPair::FromURL(restricted_port_request.url),
       kUnrestrictedAlternatePort,
-      AlternateProtocolFromNextProto(GetParam()));
+      AlternateProtocolFromNextProto(GetParam()), 1);
 
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
@@ -8649,7 +8648,7 @@
   http_server_properties->SetAlternateProtocol(
       HostPortPair::FromURL(restricted_port_request.url),
       kRestrictedAlternatePort,
-      AlternateProtocolFromNextProto(GetParam()));
+      AlternateProtocolFromNextProto(GetParam()), 1);
 
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
@@ -8698,7 +8697,7 @@
   http_server_properties->SetAlternateProtocol(
       HostPortPair::FromURL(unrestricted_port_request.url),
       kRestrictedAlternatePort,
-      AlternateProtocolFromNextProto(GetParam()));
+      AlternateProtocolFromNextProto(GetParam()), 1);
 
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
@@ -8746,7 +8745,7 @@
   http_server_properties->SetAlternateProtocol(
       HostPortPair::FromURL(unrestricted_port_request.url),
       kUnrestrictedAlternatePort,
-      AlternateProtocolFromNextProto(GetParam()));
+      AlternateProtocolFromNextProto(GetParam()), 1);
 
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
@@ -8789,7 +8788,7 @@
   http_server_properties->SetAlternateProtocol(
       HostPortPair::FromURL(request.url),
       kUnsafePort,
-      AlternateProtocolFromNextProto(GetParam()));
+      AlternateProtocolFromNextProto(GetParam()), 1);
 
   scoped_ptr<HttpTransaction> trans(
       new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get()));
diff --git a/net/http/http_server_properties.cc b/net/http/http_server_properties.cc
index 1a9bfb6..abc5cb1 100644
--- a/net/http/http_server_properties.cc
+++ b/net/http/http_server_properties.cc
@@ -111,9 +111,10 @@
   return UNINITIALIZED_ALTERNATE_PROTOCOL;
 }
 
-std::string PortAlternateProtocolPair::ToString() const {
-  return base::StringPrintf("%d:%s", port,
-                            AlternateProtocolToString(protocol));
+std::string AlternateProtocolInfo::ToString() const {
+  return base::StringPrintf("%d:%s p=%f", port,
+                            AlternateProtocolToString(protocol),
+                            probability);
 }
 
 }  // namespace net
diff --git a/net/http/http_server_properties.h b/net/http/http_server_properties.h
index 88d1f3c..718f4c5 100644
--- a/net/http/http_server_properties.h
+++ b/net/http/http_server_properties.h
@@ -92,19 +92,29 @@
 NET_EXPORT_PRIVATE AlternateProtocol AlternateProtocolFromNextProto(
     NextProto next_proto);
 
-struct NET_EXPORT PortAlternateProtocolPair {
-  bool Equals(const PortAlternateProtocolPair& other) const {
-    return port == other.port && protocol == other.protocol;
+struct NET_EXPORT AlternateProtocolInfo {
+  AlternateProtocolInfo(uint16 port,
+                        AlternateProtocol protocol,
+                        double probability)
+      : port(port),
+        protocol(protocol),
+        probability(probability) {}
+
+  bool Equals(const AlternateProtocolInfo& other) const {
+    return port == other.port &&
+        protocol == other.protocol &&
+        probability == other.probability;
   }
 
   std::string ToString() const;
 
   uint16 port;
   AlternateProtocol protocol;
+  double probability;
 };
 
 typedef base::MRUCache<
-    HostPortPair, PortAlternateProtocolPair> AlternateProtocolMap;
+    HostPortPair, AlternateProtocolInfo> AlternateProtocolMap;
 typedef base::MRUCache<HostPortPair, SettingsMap> SpdySettingsMap;
 
 extern const char kAlternateProtocolHeader[];
@@ -143,13 +153,14 @@
 
   // Returns the Alternate-Protocol and port for |server|.
   // HasAlternateProtocol(server) must be true.
-  virtual PortAlternateProtocolPair GetAlternateProtocol(
+  virtual AlternateProtocolInfo GetAlternateProtocol(
       const HostPortPair& server) = 0;
 
   // Sets the Alternate-Protocol for |server|.
   virtual void SetAlternateProtocol(const HostPortPair& server,
                                     uint16 alternate_port,
-                                    AlternateProtocol alternate_protocol) = 0;
+                                    AlternateProtocol alternate_protocol,
+                                    double probability) = 0;
 
   // Sets the Alternate-Protocol for |server| to be BROKEN.
   virtual void SetBrokenAlternateProtocol(const HostPortPair& server) = 0;
@@ -170,6 +181,9 @@
   virtual void SetAlternateProtocolExperiment(
       AlternateProtocolExperiment experiment) = 0;
 
+  virtual void SetAlternateProtocolProbabilityThreshold(
+      double threshold) = 0;
+
   virtual AlternateProtocolExperiment GetAlternateProtocolExperiment()
       const = 0;
 
diff --git a/net/http/http_server_properties_impl.cc b/net/http/http_server_properties_impl.cc
index 96f3a7d..4bb544d3 100644
--- a/net/http/http_server_properties_impl.cc
+++ b/net/http/http_server_properties_impl.cc
@@ -26,6 +26,7 @@
       alternate_protocol_experiment_(
           ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT),
       spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT),
+      alternate_protocol_probability_threshold_(0),
       weak_ptr_factory_(this) {
   canoncial_suffixes_.push_back(".c.youtube.com");
   canoncial_suffixes_.push_back(".googlevideo.com");
@@ -130,15 +131,15 @@
   return spdy_server;
 }
 
-static const PortAlternateProtocolPair* g_forced_alternate_protocol = NULL;
+static const AlternateProtocolInfo* g_forced_alternate_protocol = NULL;
 
 // static
 void HttpServerPropertiesImpl::ForceAlternateProtocol(
-    const PortAlternateProtocolPair& pair) {
+    const AlternateProtocolInfo& info) {
   // Note: we're going to leak this.
   if (g_forced_alternate_protocol)
     delete g_forced_alternate_protocol;
-  g_forced_alternate_protocol = new PortAlternateProtocolPair(pair);
+  g_forced_alternate_protocol = new AlternateProtocolInfo(info);
 }
 
 // static
@@ -193,9 +194,13 @@
 
 bool HttpServerPropertiesImpl::HasAlternateProtocol(
     const HostPortPair& server) {
-  if (alternate_protocol_map_.Get(server) != alternate_protocol_map_.end() ||
-      g_forced_alternate_protocol)
+  if (g_forced_alternate_protocol)
     return true;
+  AlternateProtocolMap::const_iterator it = alternate_protocol_map_.Get(server);
+  if (it != alternate_protocol_map_.end() &&
+      it->second.probability > alternate_protocol_probability_threshold_) {
+    return true;
+  }
 
   return GetCanonicalHost(server) != canonical_host_to_origin_map_.end();
 }
@@ -213,7 +218,7 @@
   return std::string();
 }
 
-PortAlternateProtocolPair
+AlternateProtocolInfo
 HttpServerPropertiesImpl::GetAlternateProtocol(
     const HostPortPair& server) {
   DCHECK(HasAlternateProtocol(server));
@@ -236,17 +241,18 @@
 void HttpServerPropertiesImpl::SetAlternateProtocol(
     const HostPortPair& server,
     uint16 alternate_port,
-    AlternateProtocol alternate_protocol) {
+    AlternateProtocol alternate_protocol,
+    double alternate_probability) {
   if (alternate_protocol == ALTERNATE_PROTOCOL_BROKEN) {
     LOG(DFATAL) << "Call SetBrokenAlternateProtocol() instead.";
     return;
   }
 
-  PortAlternateProtocolPair alternate;
-  alternate.port = alternate_port;
-  alternate.protocol = alternate_protocol;
+  AlternateProtocolInfo alternate(alternate_port,
+                                  alternate_protocol,
+                                  alternate_probability);
   if (HasAlternateProtocol(server)) {
-    const PortAlternateProtocolPair existing_alternate =
+    const AlternateProtocolInfo existing_alternate =
         GetAlternateProtocol(server);
 
     if (existing_alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) {
@@ -260,8 +266,10 @@
                    << server.ToString()
                    << " from [Port: " << existing_alternate.port
                    << ", Protocol: " << existing_alternate.protocol
+                   << ", Probability: " << existing_alternate.probability
                    << "] to [Port: " << alternate_port
                    << ", Protocol: " << alternate_protocol
+                   << ", Probability: " << alternate_probability
                    << "].";
     }
   } else {
@@ -292,8 +300,9 @@
   if (it != alternate_protocol_map_.end()) {
     it->second.protocol = ALTERNATE_PROTOCOL_BROKEN;
   } else {
-    PortAlternateProtocolPair alternate;
-    alternate.protocol = ALTERNATE_PROTOCOL_BROKEN;
+    AlternateProtocolInfo alternate(server.port(),
+                                    ALTERNATE_PROTOCOL_BROKEN,
+                                    1);
     alternate_protocol_map_.Put(server, alternate);
   }
   int count = ++broken_alternate_protocol_map_[server];
@@ -414,6 +423,11 @@
   return &it->second;
 }
 
+void HttpServerPropertiesImpl::SetAlternateProtocolProbabilityThreshold(
+    double threshold) {
+  alternate_protocol_probability_threshold_ = threshold;
+}
+
 HttpServerPropertiesImpl::CanonicalHostMap::const_iterator
 HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const {
   for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) {
diff --git a/net/http/http_server_properties_impl.h b/net/http/http_server_properties_impl.h
index c65d5e3c..7d7ec11 100644
--- a/net/http/http_server_properties_impl.h
+++ b/net/http/http_server_properties_impl.h
@@ -55,7 +55,7 @@
   // Debugging to simulate presence of an AlternateProtocol.
   // If we don't have an alternate protocol in the map for any given host/port
   // pair, force this ProtocolPortPair.
-  static void ForceAlternateProtocol(const PortAlternateProtocolPair& pair);
+  static void ForceAlternateProtocol(const AlternateProtocolInfo& pair);
   static void DisableForcedAlternateProtocol();
 
   // Returns the canonical host suffix for |server|, or std::string() if none
@@ -84,14 +84,15 @@
 
   // Returns the Alternate-Protocol and port for |server|.
   // HasAlternateProtocol(server) must be true.
-  virtual PortAlternateProtocolPair GetAlternateProtocol(
+  virtual AlternateProtocolInfo GetAlternateProtocol(
       const HostPortPair& server) OVERRIDE;
 
   // Sets the Alternate-Protocol for |server|.
   virtual void SetAlternateProtocol(
       const HostPortPair& server,
       uint16 alternate_port,
-      AlternateProtocol alternate_protocol) OVERRIDE;
+      AlternateProtocol alternate_protocol,
+      double probability) OVERRIDE;
 
   // Sets the Alternate-Protocol for |server| to be BROKEN.
   virtual void SetBrokenAlternateProtocol(const HostPortPair& server) OVERRIDE;
@@ -112,6 +113,9 @@
   virtual void SetAlternateProtocolExperiment(
       AlternateProtocolExperiment experiment) OVERRIDE;
 
+  virtual void SetAlternateProtocolProbabilityThreshold(
+      double threshold) OVERRIDE;
+
   virtual AlternateProtocolExperiment GetAlternateProtocolExperiment()
       const OVERRIDE;
 
@@ -184,6 +188,8 @@
   // ".googlevideo.com", ".googleusercontent.com") of canoncial hostnames.
   CanonicalSufficList canoncial_suffixes_;
 
+  double alternate_protocol_probability_threshold_;
+
   base::WeakPtrFactory<HttpServerPropertiesImpl> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(HttpServerPropertiesImpl);
diff --git a/net/http/http_server_properties_impl_unittest.cc b/net/http/http_server_properties_impl_unittest.cc
index e3b08d46..f21c9692 100644
--- a/net/http/http_server_properties_impl_unittest.cc
+++ b/net/http/http_server_properties_impl_unittest.cc
@@ -243,9 +243,9 @@
 TEST_F(AlternateProtocolServerPropertiesTest, Basic) {
   HostPortPair test_host_port_pair("foo", 80);
   EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair));
-  impl_.SetAlternateProtocol(test_host_port_pair, 443, NPN_SPDY_3);
+  impl_.SetAlternateProtocol(test_host_port_pair, 443, NPN_SPDY_3, 1);
   ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair));
-  const PortAlternateProtocolPair alternate =
+  const AlternateProtocolInfo alternate =
       impl_.GetAlternateProtocol(test_host_port_pair);
   EXPECT_EQ(443, alternate.port);
   EXPECT_EQ(NPN_SPDY_3, alternate.protocol);
@@ -254,17 +254,38 @@
   EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair));
 }
 
+TEST_F(AlternateProtocolServerPropertiesTest, Probability) {
+  impl_.SetAlternateProtocolProbabilityThreshold(.25);
+
+  HostPortPair test_host_port_pair("foo", 80);
+  impl_.SetAlternateProtocol(test_host_port_pair, 443, NPN_SPDY_3, .5);
+
+  ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair));
+  const AlternateProtocolInfo alternate =
+      impl_.GetAlternateProtocol(test_host_port_pair);
+  EXPECT_EQ(443, alternate.port);
+  EXPECT_EQ(NPN_SPDY_3, alternate.protocol);
+  EXPECT_EQ(.5, alternate.probability);
+}
+
+TEST_F(AlternateProtocolServerPropertiesTest, ProbabilityExcluded) {
+  impl_.SetAlternateProtocolProbabilityThreshold(.75);
+
+  HostPortPair test_host_port_pair("foo", 80);
+
+  impl_.SetAlternateProtocol(test_host_port_pair, 443, NPN_SPDY_3, .5);
+  EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair));
+}
+
 TEST_F(AlternateProtocolServerPropertiesTest, Initialize) {
   HostPortPair test_host_port_pair1("foo1", 80);
   impl_.SetBrokenAlternateProtocol(test_host_port_pair1);
   HostPortPair test_host_port_pair2("foo2", 80);
-  impl_.SetAlternateProtocol(test_host_port_pair2, 443, NPN_SPDY_3);
+  impl_.SetAlternateProtocol(test_host_port_pair2, 443, NPN_SPDY_3, 1);
 
   AlternateProtocolMap alternate_protocol_map(
       AlternateProtocolMap::NO_AUTO_EVICT);
-  PortAlternateProtocolPair port_alternate_protocol_pair;
-  port_alternate_protocol_pair.port = 123;
-  port_alternate_protocol_pair.protocol = NPN_SPDY_3;
+  AlternateProtocolInfo port_alternate_protocol_pair(123, NPN_SPDY_3, 1);
   alternate_protocol_map.Put(test_host_port_pair2,
                              port_alternate_protocol_pair);
   HostPortPair test_host_port_pair3("foo3", 80);
@@ -294,9 +315,9 @@
 
 TEST_F(AlternateProtocolServerPropertiesTest, MRUOfHasAlternateProtocol) {
   HostPortPair test_host_port_pair1("foo1", 80);
-  impl_.SetAlternateProtocol(test_host_port_pair1, 443, NPN_SPDY_3);
+  impl_.SetAlternateProtocol(test_host_port_pair1, 443, NPN_SPDY_3, 1);
   HostPortPair test_host_port_pair2("foo2", 80);
-  impl_.SetAlternateProtocol(test_host_port_pair2, 1234, NPN_SPDY_3);
+  impl_.SetAlternateProtocol(test_host_port_pair2, 1234, NPN_SPDY_3, 1);
 
   const net::AlternateProtocolMap& map = impl_.alternate_protocol_map();
   net::AlternateProtocolMap::const_iterator it = map.begin();
@@ -314,9 +335,9 @@
 
 TEST_F(AlternateProtocolServerPropertiesTest, MRUOfGetAlternateProtocol) {
   HostPortPair test_host_port_pair1("foo1", 80);
-  impl_.SetAlternateProtocol(test_host_port_pair1, 443, NPN_SPDY_3);
+  impl_.SetAlternateProtocol(test_host_port_pair1, 443, NPN_SPDY_3, 1);
   HostPortPair test_host_port_pair2("foo2", 80);
-  impl_.SetAlternateProtocol(test_host_port_pair2, 1234, NPN_SPDY_3);
+  impl_.SetAlternateProtocol(test_host_port_pair2, 1234, NPN_SPDY_3, 1);
 
   const net::AlternateProtocolMap& map = impl_.alternate_protocol_map();
   net::AlternateProtocolMap::const_iterator it = map.begin();
@@ -325,7 +346,7 @@
   EXPECT_EQ(NPN_SPDY_3, it->second.protocol);
 
   // GetAlternateProtocol should reoder the AlternateProtocol map.
-  PortAlternateProtocolPair alternate =
+  AlternateProtocolInfo alternate =
       impl_.GetAlternateProtocol(test_host_port_pair1);
   EXPECT_EQ(443, alternate.port);
   EXPECT_EQ(NPN_SPDY_3, alternate.protocol);
@@ -339,14 +360,15 @@
   HostPortPair test_host_port_pair("foo", 80);
   impl_.SetBrokenAlternateProtocol(test_host_port_pair);
   ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair));
-  PortAlternateProtocolPair alternate =
+  AlternateProtocolInfo alternate =
       impl_.GetAlternateProtocol(test_host_port_pair);
   EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol);
 
   impl_.SetAlternateProtocol(
       test_host_port_pair,
       1234,
-      NPN_SPDY_3);
+      NPN_SPDY_3,
+      1);
   alternate = impl_.GetAlternateProtocol(test_host_port_pair);
   EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol)
       << "Second attempt should be ignored.";
@@ -356,7 +378,7 @@
   HostPortPair test_host_port_pair("foo", 80);
   impl_.SetBrokenAlternateProtocol(test_host_port_pair);
   ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair));
-  PortAlternateProtocolPair alternate =
+  AlternateProtocolInfo alternate =
       impl_.GetAlternateProtocol(test_host_port_pair);
   EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol);
   impl_.ClearAlternateProtocol(test_host_port_pair);
@@ -366,21 +388,19 @@
 TEST_F(AlternateProtocolServerPropertiesTest, Forced) {
   // Test forced alternate protocols.
 
-  PortAlternateProtocolPair default_protocol;
-  default_protocol.port = 1234;
-  default_protocol.protocol = NPN_SPDY_3;
+  AlternateProtocolInfo default_protocol(1234, NPN_SPDY_3, 1);
   HttpServerPropertiesImpl::ForceAlternateProtocol(default_protocol);
 
   // Verify the forced protocol.
   HostPortPair test_host_port_pair("foo", 80);
   EXPECT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair));
-  PortAlternateProtocolPair alternate =
+  AlternateProtocolInfo alternate =
       impl_.GetAlternateProtocol(test_host_port_pair);
   EXPECT_EQ(default_protocol.port, alternate.port);
   EXPECT_EQ(default_protocol.protocol, alternate.protocol);
 
   // Verify the real protocol overrides the forced protocol.
-  impl_.SetAlternateProtocol(test_host_port_pair, 443, NPN_SPDY_3);
+  impl_.SetAlternateProtocol(test_host_port_pair, 443, NPN_SPDY_3, 1);
   ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair));
   alternate = impl_.GetAlternateProtocol(test_host_port_pair);
   EXPECT_EQ(443, alternate.port);
@@ -402,16 +422,15 @@
   HostPortPair canonical_port_pair("bar.c.youtube.com", 80);
   EXPECT_FALSE(impl_.HasAlternateProtocol(canonical_port_pair));
 
-  PortAlternateProtocolPair canonical_protocol;
-  canonical_protocol.port = 1234;
-  canonical_protocol.protocol = QUIC;
+  AlternateProtocolInfo canonical_protocol(1234, QUIC, 1);
 
   impl_.SetAlternateProtocol(canonical_port_pair,
                              canonical_protocol.port,
-                             canonical_protocol.protocol);
+                             canonical_protocol.protocol,
+                             1);
   // Verify the forced protocol.
   ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair));
-  PortAlternateProtocolPair alternate =
+  AlternateProtocolInfo alternate =
       impl_.GetAlternateProtocol(test_host_port_pair);
   EXPECT_EQ(canonical_protocol.port, alternate.port);
   EXPECT_EQ(canonical_protocol.protocol, alternate.protocol);
@@ -425,13 +444,12 @@
   HostPortPair test_host_port_pair("foo.c.youtube.com", 80);
   HostPortPair canonical_port_pair("bar.c.youtube.com", 80);
 
-  PortAlternateProtocolPair canonical_protocol;
-  canonical_protocol.port = 1234;
-  canonical_protocol.protocol = QUIC;
+  AlternateProtocolInfo canonical_protocol(1234, QUIC, 1);
 
   impl_.SetAlternateProtocol(canonical_port_pair,
                              canonical_protocol.port,
-                             canonical_protocol.protocol);
+                             canonical_protocol.protocol,
+                             canonical_protocol.probability);
 
   impl_.ClearAlternateProtocol(canonical_port_pair);
   EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair));
@@ -441,13 +459,12 @@
   HostPortPair test_host_port_pair("foo.c.youtube.com", 80);
   HostPortPair canonical_port_pair("bar.c.youtube.com", 80);
 
-  PortAlternateProtocolPair canonical_protocol;
-  canonical_protocol.port = 1234;
-  canonical_protocol.protocol = QUIC;
+  AlternateProtocolInfo canonical_protocol(1234, QUIC, 1);
 
   impl_.SetAlternateProtocol(canonical_port_pair,
                              canonical_protocol.port,
-                             canonical_protocol.protocol);
+                             canonical_protocol.protocol,
+                             canonical_protocol.probability);
 
   impl_.SetBrokenAlternateProtocol(canonical_port_pair);
   EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair));
@@ -457,13 +474,12 @@
   HostPortPair test_host_port_pair("foo.c.youtube.com", 80);
   HostPortPair canonical_port_pair("bar.c.youtube.com", 80);
 
-  PortAlternateProtocolPair canonical_protocol;
-  canonical_protocol.port = 1234;
-  canonical_protocol.protocol = QUIC;
+  AlternateProtocolInfo canonical_protocol(1234, QUIC, 1);
 
   impl_.SetAlternateProtocol(canonical_port_pair,
                              canonical_protocol.port,
-                             canonical_protocol.protocol);
+                             canonical_protocol.protocol,
+                             canonical_protocol.probability);
 
   impl_.Clear();
   EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair));
diff --git a/net/http/http_stream_factory.cc b/net/http/http_stream_factory.cc
index b60df85e..e86d8f8 100644
--- a/net/http/http_stream_factory.cc
+++ b/net/http/http_stream_factory.cc
@@ -7,6 +7,7 @@
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
 #include "net/base/host_mapping_rules.h"
 #include "net/base/host_port_pair.h"
 #include "net/http/http_network_session.h"
@@ -29,40 +30,60 @@
 
 void HttpStreamFactory::ProcessAlternateProtocol(
     const base::WeakPtr<HttpServerProperties>& http_server_properties,
-    const std::string& alternate_protocol_str,
+    const std::vector<std::string>& alternate_protocol_values,
     const HostPortPair& http_host_port_pair,
     const HttpNetworkSession& session) {
-  std::vector<std::string> port_protocol_vector;
-  base::SplitString(alternate_protocol_str, ':', &port_protocol_vector);
-  if (port_protocol_vector.size() != 2) {
-    DVLOG(1) << kAlternateProtocolHeader
-             << " header has too many tokens: "
-             << alternate_protocol_str;
-    return;
+  AlternateProtocol protocol = UNINITIALIZED_ALTERNATE_PROTOCOL;
+  int port = 0;
+  double probability = 1;
+  for (size_t i = 0; i < alternate_protocol_values.size(); ++i) {
+    const std::string& alternate_protocol_str = alternate_protocol_values[i];
+    if (StartsWithASCII(alternate_protocol_str, "p=", true)) {
+      if (!base::StringToDouble(alternate_protocol_str.substr(2),
+                                &probability) ||
+          probability < 0 || probability > 1) {
+        DVLOG(1) << kAlternateProtocolHeader
+                 << " header has unrecognizable probability: "
+                 << alternate_protocol_values[i];
+        return;
+      }
+      continue;
+    }
+
+    std::vector<std::string> port_protocol_vector;
+    base::SplitString(alternate_protocol_str, ':', &port_protocol_vector);
+    if (port_protocol_vector.size() != 2) {
+      DVLOG(1) << kAlternateProtocolHeader
+               << " header has too many tokens: "
+               << alternate_protocol_str;
+      return;
+    }
+
+    if (!base::StringToInt(port_protocol_vector[0], &port) ||
+        port <= 0 || port >= 1 << 16) {
+      DVLOG(1) << kAlternateProtocolHeader
+               << " header has unrecognizable port: "
+               << port_protocol_vector[0];
+      return;
+    }
+
+    protocol =
+        AlternateProtocolFromString(port_protocol_vector[1]);
+    if (IsAlternateProtocolValid(protocol) &&
+        !session.IsProtocolEnabled(protocol)) {
+      protocol = ALTERNATE_PROTOCOL_BROKEN;
+    }
+
+    if (protocol == ALTERNATE_PROTOCOL_BROKEN) {
+      DVLOG(1) << kAlternateProtocolHeader
+               << " header has unrecognized protocol: "
+               << port_protocol_vector[1];
+      return;
+    }
   }
 
-  int port;
-  if (!base::StringToInt(port_protocol_vector[0], &port) ||
-      port <= 0 || port >= 1 << 16) {
-    DVLOG(1) << kAlternateProtocolHeader
-             << " header has unrecognizable port: "
-             << port_protocol_vector[0];
+  if (protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
     return;
-  }
-
-  AlternateProtocol protocol =
-      AlternateProtocolFromString(port_protocol_vector[1]);
-  if (IsAlternateProtocolValid(protocol) &&
-      !session.IsProtocolEnabled(protocol)) {
-    protocol = ALTERNATE_PROTOCOL_BROKEN;
-  }
-
-  if (protocol == ALTERNATE_PROTOCOL_BROKEN) {
-    DVLOG(1) << kAlternateProtocolHeader
-             << " header has unrecognized protocol: "
-             << port_protocol_vector[1];
-    return;
-  }
 
   HostPortPair host_port(http_host_port_pair);
   const HostMappingRules* mapping_rules = GetHostMappingRules();
@@ -70,14 +91,15 @@
     mapping_rules->RewriteHost(&host_port);
 
   if (http_server_properties->HasAlternateProtocol(host_port)) {
-    const PortAlternateProtocolPair existing_alternate =
+    const AlternateProtocolInfo existing_alternate =
         http_server_properties->GetAlternateProtocol(host_port);
     // If we think the alternate protocol is broken, don't change it.
     if (existing_alternate.protocol == ALTERNATE_PROTOCOL_BROKEN)
       return;
   }
 
-  http_server_properties->SetAlternateProtocol(host_port, port, protocol);
+  http_server_properties->SetAlternateProtocol(host_port, port, protocol,
+                                               probability);
 }
 
 GURL HttpStreamFactory::ApplyHostMappingRules(const GURL& url,
diff --git a/net/http/http_stream_factory.h b/net/http/http_stream_factory.h
index f3b0203..8f6d892 100644
--- a/net/http/http_stream_factory.h
+++ b/net/http/http_stream_factory.h
@@ -179,7 +179,7 @@
 
   void ProcessAlternateProtocol(
       const base::WeakPtr<HttpServerProperties>& http_server_properties,
-      const std::string& alternate_protocol_str,
+      const std::vector<std::string>& alternate_protocol_values,
       const HostPortPair& http_host_port_pair,
       const HttpNetworkSession& session);
 
diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc
index ad12091..ced5f2b 100644
--- a/net/http/http_stream_factory_impl.cc
+++ b/net/http/http_stream_factory_impl.cc
@@ -22,10 +22,6 @@
 
 namespace {
 
-const PortAlternateProtocolPair kNoAlternateProtocol = {
-  0,  UNINITIALIZED_ALTERNATE_PROTOCOL
-};
-
 GURL UpgradeUrlToHttps(const GURL& original_url, int port) {
   GURL::Replacements replacements;
   // new_sheme and new_port need to be in scope here because GURL::Replacements
@@ -111,7 +107,7 @@
                                  net_log);
 
   GURL alternate_url;
-  PortAlternateProtocolPair alternate =
+  AlternateProtocolInfo alternate =
       GetAlternateProtocolRequestFor(request_info.url, &alternate_url);
   Job* alternate_job = NULL;
   if (alternate.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
@@ -155,7 +151,7 @@
     const SSLConfig& proxy_ssl_config) {
   DCHECK(!for_websockets_);
   GURL alternate_url;
-  PortAlternateProtocolPair alternate =
+  AlternateProtocolInfo alternate =
       GetAlternateProtocolRequestFor(request_info.url, &alternate_url);
   Job* job = NULL;
   if (alternate.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
@@ -176,9 +172,12 @@
   return session_->params().host_mapping_rules;
 }
 
-PortAlternateProtocolPair HttpStreamFactoryImpl::GetAlternateProtocolRequestFor(
+AlternateProtocolInfo HttpStreamFactoryImpl::GetAlternateProtocolRequestFor(
     const GURL& original_url,
     GURL* alternate_url) {
+  const AlternateProtocolInfo kNoAlternateProtocol =
+      AlternateProtocolInfo(0,  UNINITIALIZED_ALTERNATE_PROTOCOL, 0);
+
   if (!session_->params().use_alternate_protocols)
     return kNoAlternateProtocol;
 
@@ -193,7 +192,7 @@
   if (!http_server_properties.HasAlternateProtocol(origin))
     return kNoAlternateProtocol;
 
-  PortAlternateProtocolPair alternate =
+  AlternateProtocolInfo alternate =
       http_server_properties.GetAlternateProtocol(origin);
   if (alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) {
     HistogramAlternateProtocolUsage(
diff --git a/net/http/http_stream_factory_impl.h b/net/http/http_stream_factory_impl.h
index 8a6fdaf4..7df0a67 100644
--- a/net/http/http_stream_factory_impl.h
+++ b/net/http/http_stream_factory_impl.h
@@ -76,7 +76,7 @@
       WebSocketHandshakeStreamBase::CreateHelper* create_helper,
       const BoundNetLog& net_log);
 
-  PortAlternateProtocolPair GetAlternateProtocolRequestFor(
+  AlternateProtocolInfo GetAlternateProtocolRequestFor(
       const GURL& original_url,
       GURL* alternate_url);
 
diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc
index 79a9536..54147d6 100644
--- a/net/http/http_stream_factory_impl_job.cc
+++ b/net/http/http_stream_factory_impl_job.cc
@@ -171,7 +171,7 @@
 
 void HttpStreamFactoryImpl::Job::MarkAsAlternate(
     const GURL& original_url,
-    PortAlternateProtocolPair alternate) {
+    AlternateProtocolInfo alternate) {
   DCHECK(!original_url_.get());
   original_url_.reset(new GURL(original_url));
   if (alternate.protocol == QUIC) {
diff --git a/net/http/http_stream_factory_impl_job.h b/net/http/http_stream_factory_impl_job.h
index 9659d450..c2212110 100644
--- a/net/http/http_stream_factory_impl_job.h
+++ b/net/http/http_stream_factory_impl_job.h
@@ -60,7 +60,7 @@
   // we fail to connect.  |alternate| specifies the alternate protocol to use
   // and alternate port to connect to.
   void MarkAsAlternate(const GURL& original_url,
-                       PortAlternateProtocolPair alternate);
+                       AlternateProtocolInfo alternate);
 
   // Tells |this| to wait for |job| to resume it.
   void WaitFor(Job* job);
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc
index 6406957..3bf73964 100644
--- a/net/http/http_stream_factory_impl_unittest.cc
+++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -1269,7 +1269,8 @@
   session->http_server_properties()->SetAlternateProtocol(
       HostPortPair("www.google.com", 8888),
       9999,
-      NPN_SPDY_3);
+      NPN_SPDY_3,
+      1);
 
   SSLConfig ssl_config;
   StreamRequestWaiter waiter;