Land Recent QUIC Changes.

Introduce QUIC_VERSION_20: allowing endpoints to set different
stream/session flow control windows. Two new tags kSFCW, kCFCW for
stream flow control window and session (connection) flow control windows
respectively. kIFCW is still used for older versions <= QUIC_VERSION_19.
kIFCW has the same value as kCFCW.

Most of this CL is copy/paste code for setting/getting the new tags. New
values are set in quic_client_bin.cc (and quic_stream_factory.cc), and
processed in quic_session.cc and reliable_quic_stream.cc

rtenneti: When merging into Chromium, you should add something like:

config.SetInitialStreamFlowControlWindowToSend(kInitialReceiveWindow);
config.SetInitialSessionFlowControlWindowToSend(kInitialReceiveWindow);

just before *session = new QuicClientSession(...) in quic_stream_factory.cc

Introduce QUIC_VERSION_20: allowing endpoints to set different
stream/session flow control windows.

Merge internal change: 69557631
https://codereview.chromium.org/339803004/


Added SetUserAgentID method to QuicClient which calls crypto_config_'s
set_user_agent_id method.

The following is the internal CL description.

Add command line flags to allow enabling QUIC UDP proxying based on
Chrome version modifers (canary/dev/beta/stable) included in the CHLO
UAID.

Command line flags to allow QUIC UDP proxying for specific Chrome
versions.

Merge internal change: 69473619
https://codereview.chromium.org/346853003/


Refactoring QuicClient so it doesn't own the epoll server it uses.

We'll be using QuicClient to health check backends for UdpProxying, so
it'll need to use the siloed internal server's EpollServer.

Refactoring quic client to not own the epoll server

Merge internal change: 69460313
https://codereview.chromium.org/344053002/


Fix a bug where QUIC ack frames were not bundled with crypto stream
frames when there was a missing packet.

Moved the code to ensure Crypto stream frames weren't sent with other
retransmittable frames from CryptoStreamFrame into QuicPacketGenerator.

Merge internal change: 69415222
https://codereview.chromium.org/342983004/

[email protected]

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@278652 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/quic/crypto/crypto_handshake_message.cc b/net/quic/crypto/crypto_handshake_message.cc
index 9e08344..77f7c52 100644
--- a/net/quic/crypto/crypto_handshake_message.cc
+++ b/net/quic/crypto/crypto_handshake_message.cc
@@ -241,6 +241,8 @@
     switch (it->first) {
       case kICSL:
       case kIFCW:
+      case kCFCW:
+      case kSFCW:
       case kIRTT:
       case kKATO:
       case kMSPC:
diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h
index d1931d1..f2e7a16 100644
--- a/net/quic/crypto/crypto_protocol.h
+++ b/net/quic/crypto/crypto_protocol.h
@@ -91,11 +91,15 @@
 const QuicTag kCCS  = TAG('C', 'C', 'S', 0);     // Common certificate set
 const QuicTag kCCRT = TAG('C', 'C', 'R', 'T');   // Cached certificate
 const QuicTag kEXPY = TAG('E', 'X', 'P', 'Y');   // Expiry
+// TODO(rjshade): Remove kIFCW when removing QUIC_VERSION_19.
 const QuicTag kIFCW = TAG('I', 'F', 'C', 'W');   // Initial flow control receive
                                                  // window.
+const QuicTag kSFCW = TAG('S', 'F', 'C', 'W');   // Initial stream flow control
+                                                 // receive window.
+const QuicTag kCFCW = TAG('C', 'F', 'C', 'W');   // Initial session/connection
+                                                 // flow control receive window.
 const QuicTag kUAID = TAG('U', 'A', 'I', 'D');   // Client's User Agent ID.
 
-
 // Server hello tags
 const QuicTag kCADR = TAG('C', 'A', 'D', 'R');   // Client IP address and port
 
diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc
index f7b82501..f69aa379 100644
--- a/net/quic/quic_config.cc
+++ b/net/quic/quic_config.cc
@@ -436,7 +436,13 @@
       initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL),
       // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
       // QUIC_VERSION_17.
-      initial_flow_control_window_bytes_(kIFCW, PRESENCE_OPTIONAL) {
+      initial_flow_control_window_bytes_(kIFCW, PRESENCE_OPTIONAL),
+      // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+      // QUIC_VERSION_19.
+      initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL),
+      // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring
+      // QUIC_VERSION_19.
+      initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL) {
 }
 
 QuicConfig::~QuicConfig() {}
@@ -558,6 +564,50 @@
   return initial_flow_control_window_bytes_.GetReceivedValue();
 }
 
+void QuicConfig::SetInitialStreamFlowControlWindowToSend(uint32 window_bytes) {
+  if (window_bytes < kDefaultFlowControlSendWindow) {
+    LOG(DFATAL) << "Initial stream flow control receive window ("
+                << window_bytes << ") cannot be set lower than default ("
+                << kDefaultFlowControlSendWindow << ").";
+    window_bytes = kDefaultFlowControlSendWindow;
+  }
+  initial_stream_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32 QuicConfig::GetInitialStreamFlowControlWindowToSend() const {
+  return initial_stream_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialStreamFlowControlWindowBytes() const {
+  return initial_stream_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialStreamFlowControlWindowBytes() const {
+  return initial_stream_flow_control_window_bytes_.GetReceivedValue();
+}
+
+void QuicConfig::SetInitialSessionFlowControlWindowToSend(uint32 window_bytes) {
+  if (window_bytes < kDefaultFlowControlSendWindow) {
+    LOG(DFATAL) << "Initial session flow control receive window ("
+                << window_bytes << ") cannot be set lower than default ("
+                << kDefaultFlowControlSendWindow << ").";
+    window_bytes = kDefaultFlowControlSendWindow;
+  }
+  initial_session_flow_control_window_bytes_.SetSendValue(window_bytes);
+}
+
+uint32 QuicConfig::GetInitialSessionFlowControlWindowToSend() const {
+  return initial_session_flow_control_window_bytes_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedInitialSessionFlowControlWindowBytes() const {
+  return initial_session_flow_control_window_bytes_.HasReceivedValue();
+}
+
+uint32 QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const {
+  return initial_session_flow_control_window_bytes_.GetReceivedValue();
+}
+
 bool QuicConfig::negotiated() {
   // TODO(ianswett): Add the negotiated parameters once and iterate over all
   // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and
@@ -585,6 +635,8 @@
       kDefaultMaxTimeForCryptoHandshakeSecs);
 
   SetInitialFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+  SetInitialStreamFlowControlWindowToSend(kDefaultFlowControlSendWindow);
+  SetInitialSessionFlowControlWindowToSend(kDefaultFlowControlSendWindow);
 }
 
 void QuicConfig::EnablePacing(bool enable_pacing) {
@@ -605,6 +657,8 @@
   initial_round_trip_time_us_.ToHandshakeMessage(out);
   loss_detection_.ToHandshakeMessage(out);
   initial_flow_control_window_bytes_.ToHandshakeMessage(out);
+  initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out);
+  initial_session_flow_control_window_bytes_.ToHandshakeMessage(out);
   congestion_options_.ToHandshakeMessage(out);
 }
 
@@ -644,6 +698,14 @@
         peer_hello, hello_type, error_details);
   }
   if (error == QUIC_NO_ERROR) {
+    error = initial_stream_flow_control_window_bytes_.ProcessPeerHello(
+        peer_hello, hello_type, error_details);
+  }
+  if (error == QUIC_NO_ERROR) {
+    error = initial_session_flow_control_window_bytes_.ProcessPeerHello(
+        peer_hello, hello_type, error_details);
+  }
+  if (error == QUIC_NO_ERROR) {
     error = loss_detection_.ProcessPeerHello(
         peer_hello, hello_type, error_details);
   }
diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h
index ba681fc0..8f46917 100644
--- a/net/quic/quic_config.h
+++ b/net/quic/quic_config.h
@@ -307,7 +307,9 @@
 
   uint32 ReceivedInitialRoundTripTimeUs() const;
 
-  // Sets an initial flow control window size to transmit to the peer.
+  // TODO(rjshade): Remove all InitialFlowControlWindow methods when removing
+  // QUIC_VERSION_19.
+  // Sets an initial stream flow control window size to transmit to the peer.
   void SetInitialFlowControlWindowToSend(uint32 window_bytes);
 
   uint32 GetInitialFlowControlWindowToSend() const;
@@ -316,6 +318,24 @@
 
   uint32 ReceivedInitialFlowControlWindowBytes() const;
 
+  // Sets an initial stream flow control window size to transmit to the peer.
+  void SetInitialStreamFlowControlWindowToSend(uint32 window_bytes);
+
+  uint32 GetInitialStreamFlowControlWindowToSend() const;
+
+  bool HasReceivedInitialStreamFlowControlWindowBytes() const;
+
+  uint32 ReceivedInitialStreamFlowControlWindowBytes() const;
+
+  // Sets an initial session flow control window size to transmit to the peer.
+  void SetInitialSessionFlowControlWindowToSend(uint32 window_bytes);
+
+  uint32 GetInitialSessionFlowControlWindowToSend() const;
+
+  bool HasReceivedInitialSessionFlowControlWindowBytes() const;
+
+  uint32 ReceivedInitialSessionFlowControlWindowBytes() const;
+
   bool negotiated();
 
   // SetDefaults sets the members to sensible, default values.
@@ -356,8 +376,15 @@
   QuicFixedUint32 initial_congestion_window_;
   // Initial round trip time estimate in microseconds.
   QuicFixedUint32 initial_round_trip_time_us_;
+
+  // TODO(rjshade): Remove when removing QUIC_VERSION_19.
   // Initial flow control receive window in bytes.
   QuicFixedUint32 initial_flow_control_window_bytes_;
+
+  // Initial stream flow control receive window in bytes.
+  QuicFixedUint32 initial_stream_flow_control_window_bytes_;
+  // Initial session flow control receive window in bytes.
+  QuicFixedUint32 initial_session_flow_control_window_bytes_;
 };
 
 }  // namespace net
diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc
index f77c382..3cb2dd2 100644
--- a/net/quic/quic_config_test.cc
+++ b/net/quic/quic_config_test.cc
@@ -33,7 +33,12 @@
 TEST_F(QuicConfigTest, ToHandshakeMessage) {
   ValueRestore<bool> old_flag(&FLAGS_enable_quic_pacing, false);
   config_.SetDefaults();
-  config_.SetInitialFlowControlWindowToSend(kInitialFlowControlWindowForTest);
+  config_.SetInitialFlowControlWindowToSend(
+      kInitialSessionFlowControlWindowForTest);
+  config_.SetInitialStreamFlowControlWindowToSend(
+      kInitialStreamFlowControlWindowForTest);
+  config_.SetInitialSessionFlowControlWindowToSend(
+      kInitialSessionFlowControlWindowForTest);
   config_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5),
                                              QuicTime::Delta::FromSeconds(2));
   config_.set_max_streams_per_connection(4, 2);
@@ -51,7 +56,15 @@
 
   error = msg.GetUint32(kIFCW, &value);
   EXPECT_EQ(QUIC_NO_ERROR, error);
-  EXPECT_EQ(kInitialFlowControlWindowForTest, value);
+  EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
+
+  error = msg.GetUint32(kSFCW, &value);
+  EXPECT_EQ(QUIC_NO_ERROR, error);
+  EXPECT_EQ(kInitialStreamFlowControlWindowForTest, value);
+
+  error = msg.GetUint32(kCFCW, &value);
+  EXPECT_EQ(QUIC_NO_ERROR, error);
+  EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
 
   const QuicTag* out;
   size_t out_len;
@@ -89,7 +102,11 @@
   client_config.SetInitialRoundTripTimeUsToSend(
       10 * base::Time::kMicrosecondsPerMillisecond);
   client_config.SetInitialFlowControlWindowToSend(
-      2 * kInitialFlowControlWindowForTest);
+      2 * kInitialSessionFlowControlWindowForTest);
+  client_config.SetInitialStreamFlowControlWindowToSend(
+      2 * kInitialStreamFlowControlWindowForTest);
+  client_config.SetInitialSessionFlowControlWindowToSend(
+      2 * kInitialSessionFlowControlWindowForTest);
   QuicTagVector copt;
   copt.push_back(kTBBR);
   client_config.SetCongestionOptionsToSend(copt);
@@ -113,7 +130,11 @@
   EXPECT_EQ(1u, config_.ReceivedCongestionOptions().size());
   EXPECT_EQ(config_.ReceivedCongestionOptions()[0], kTBBR);
   EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
-            2 * kInitialFlowControlWindowForTest);
+            2 * kInitialSessionFlowControlWindowForTest);
+  EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+            2 * kInitialStreamFlowControlWindowForTest);
+  EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+            2 * kInitialSessionFlowControlWindowForTest);
 }
 
 TEST_F(QuicConfigTest, ProcessServerHello) {
@@ -131,7 +152,11 @@
   server_config.SetInitialRoundTripTimeUsToSend(
       10 * base::Time::kMicrosecondsPerMillisecond);
   server_config.SetInitialFlowControlWindowToSend(
-      2 * kInitialFlowControlWindowForTest);
+      2 * kInitialSessionFlowControlWindowForTest);
+  server_config.SetInitialStreamFlowControlWindowToSend(
+      2 * kInitialStreamFlowControlWindowForTest);
+  server_config.SetInitialSessionFlowControlWindowToSend(
+      2 * kInitialSessionFlowControlWindowForTest);
   CryptoHandshakeMessage msg;
   server_config.ToHandshakeMessage(&msg);
   string error_details;
@@ -151,7 +176,11 @@
             config_.ReceivedInitialRoundTripTimeUs());
   EXPECT_FALSE(config_.HasReceivedLossDetection());
   EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
-            2 * kInitialFlowControlWindowForTest);
+            2 * kInitialSessionFlowControlWindowForTest);
+  EXPECT_EQ(config_.ReceivedInitialStreamFlowControlWindowBytes(),
+            2 * kInitialStreamFlowControlWindowForTest);
+  EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(),
+            2 * kInitialSessionFlowControlWindowForTest);
 }
 
 TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) {
diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc
index c7fb7c04..a977b3f 100644
--- a/net/quic/quic_connection.cc
+++ b/net/quic/quic_connection.cc
@@ -1241,10 +1241,10 @@
     // Retransmitted data packets do not use FEC, even when it's enabled.
     // Retransmitted packets use the same sequence number length as the
     // original.
-    // Flush the packet creator before making a new packet.
+    // Flush the packet generator before making a new packet.
     // TODO(ianswett): Implement ReserializeAllFrames as a separate path that
     // does not require the creator to be flushed.
-    Flush();
+    packet_generator_.FlushAllQueuedFrames();
     SerializedPacket serialized_packet = packet_generator_.ReserializeAllFrames(
         pending.retransmittable_frames.frames(),
         pending.sequence_number_length);
@@ -1780,7 +1780,7 @@
   frame->error_code = error;
   frame->error_details = details;
   packet_generator_.AddControlFrame(QuicFrame(frame));
-  Flush();
+  packet_generator_.FlushAllQueuedFrames();
 }
 
 void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) {
@@ -1842,10 +1842,6 @@
   return packet_generator_.set_max_packet_length(length);
 }
 
-void QuicConnection::Flush() {
-  packet_generator_.FlushAllQueuedFrames();
-}
-
 bool QuicConnection::HasQueuedData() const {
   return pending_version_negotiation_packet_ ||
       !queued_packets_.empty() || packet_generator_.HasQueuedFrames();
diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h
index 689d595e..fec6c68 100644
--- a/net/quic/quic_connection.h
+++ b/net/quic/quic_connection.h
@@ -389,10 +389,6 @@
     return connection_close_packet_.release();
   }
 
-  // Flush any queued frames immediately.  Preserves the batch write mode and
-  // does nothing if there are no pending frames.
-  void Flush();
-
   // Returns true if the underlying UDP socket is writable, there is
   // no queued data and the connection is not congestion-control
   // blocked.
diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc
index 43c324b8..38ead0ca 100644
--- a/net/quic/quic_connection_test.cc
+++ b/net/quic/quic_connection_test.cc
@@ -520,11 +520,7 @@
   // split needlessly across packet boundaries).  As a result, we have separate
   // tests for some cases for this stream.
   QuicConsumedData SendCryptoStreamData() {
-    this->Flush();
-    QuicConsumedData consumed =
-        SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin, NULL);
-    this->Flush();
-    return consumed;
+    return SendStreamDataWithString(kCryptoStreamId, "chlo", 0, !kFin, NULL);
   }
 
   bool is_server() {
@@ -1669,7 +1665,7 @@
 TEST_P(QuicConnectionTest, FramePackingCryptoThenNonCrypto) {
   CongestionBlockWrites();
 
-  // Send an ack and two stream frames (one crypto, then one non-crypto) in 3
+  // Send an ack and two stream frames (one crypto, then one non-crypto) in 2
   // packets by queueing them.
   connection_.SendAck();
   EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(DoAll(
@@ -1678,7 +1674,7 @@
       IgnoreResult(InvokeWithoutArgs(&connection_,
                                      &TestConnection::SendStreamData3))));
 
-  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(3);
+  EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2);
   CongestionUnblockWrites();
   connection_.GetSendAlarm()->Fire();
   EXPECT_EQ(0u, connection_.NumQueuedPackets());
@@ -3062,6 +3058,28 @@
   EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
 }
 
+TEST_P(QuicConnectionTest, BundleAckForSecondCHLO) {
+  EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
+  EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+  EXPECT_CALL(visitor_, OnCanWrite()).WillOnce(
+      IgnoreResult(InvokeWithoutArgs(&connection_,
+                                     &TestConnection::SendCryptoStreamData)));
+  // Process a packet from the crypto stream, which is frame1_'s default.
+  // Receiving the CHLO as packet 2 first will cause the connection to
+  // immediately send an ack, due to the packet gap.
+  ProcessPacket(2);
+  // Check that ack is sent and that delayed ack alarm is reset.
+  if (version() > QUIC_VERSION_15) {
+    EXPECT_EQ(3u, writer_->frame_count());
+    EXPECT_FALSE(writer_->stop_waiting_frames().empty());
+  } else {
+    EXPECT_EQ(2u, writer_->frame_count());
+  }
+  EXPECT_EQ(1u, writer_->stream_frames().size());
+  EXPECT_FALSE(writer_->ack_frames().empty());
+  EXPECT_FALSE(connection_.GetAckAlarm()->IsSet());
+}
+
 TEST_P(QuicConnectionTest, BundleAckWithDataOnIncomingAck) {
   EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_));
   connection_.SendStreamDataWithString(kClientDataStreamId1, "foo", 0,
diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc
index 9005d5a..0e44712 100644
--- a/net/quic/quic_crypto_stream.cc
+++ b/net/quic/quic_crypto_stream.cc
@@ -57,12 +57,8 @@
     const CryptoHandshakeMessage& message) {
   session()->OnCryptoHandshakeMessageSent(message);
   const QuicData& data = message.GetSerialized();
-  // To make reasoning about crypto frames easier, we don't combine them with
-  // any other frames in a single packet.
-  session()->connection()->Flush();
   // TODO(wtc): check the return value.
   WriteOrBufferData(string(data.data(), data.length()), false, NULL);
-  session()->connection()->Flush();
 }
 
 const QuicCryptoNegotiatedParameters&
diff --git a/net/quic/quic_end_to_end_unittest.cc b/net/quic/quic_end_to_end_unittest.cc
index e7e2769..89622b0 100644
--- a/net/quic/quic_end_to_end_unittest.cc
+++ b/net/quic/quic_end_to_end_unittest.cc
@@ -136,7 +136,11 @@
     server_address_ = IPEndPoint(ip, 0);
     server_config_.SetDefaults();
     server_config_.SetInitialFlowControlWindowToSend(
-        kInitialFlowControlWindowForTest);
+        kInitialSessionFlowControlWindowForTest);
+    server_config_.SetInitialStreamFlowControlWindowToSend(
+        kInitialStreamFlowControlWindowForTest);
+    server_config_.SetInitialSessionFlowControlWindowToSend(
+        kInitialSessionFlowControlWindowForTest);
     server_thread_.reset(new ServerThread(
          new QuicServer(server_config_, QuicSupportedVersions()),
          server_address_,
diff --git a/net/quic/quic_flow_controller_test.cc b/net/quic/quic_flow_controller_test.cc
index 30fe990..26781ac 100644
--- a/net/quic/quic_flow_controller_test.cc
+++ b/net/quic/quic_flow_controller_test.cc
@@ -24,9 +24,9 @@
  public:
   QuicFlowControllerTest()
       : stream_id_(1234),
-        send_window_(kInitialFlowControlWindowForTest),
-        receive_window_(kInitialFlowControlWindowForTest),
-        max_receive_window_(kInitialFlowControlWindowForTest),
+        send_window_(kInitialSessionFlowControlWindowForTest),
+        receive_window_(kInitialSessionFlowControlWindowForTest),
+        max_receive_window_(kInitialSessionFlowControlWindowForTest),
         connection_(false),
         old_flag_(&FLAGS_enable_quic_stream_flow_control_2, true) {
   }
@@ -95,7 +95,7 @@
   EXPECT_TRUE(flow_controller_->IsEnabled());
   EXPECT_FALSE(flow_controller_->IsBlocked());
   EXPECT_FALSE(flow_controller_->FlowControlViolation());
-  EXPECT_EQ(kInitialFlowControlWindowForTest,
+  EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
             QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
 
   // Receive some bytes, updating highest received offset, but not enough to
@@ -113,7 +113,7 @@
 
   // Result is that once again we have a fully open receive window.
   EXPECT_FALSE(flow_controller_->FlowControlViolation());
-  EXPECT_EQ(kInitialFlowControlWindowForTest,
+  EXPECT_EQ(kInitialSessionFlowControlWindowForTest,
             QuicFlowControllerPeer::ReceiveWindowSize(flow_controller_.get()));
 }
 
diff --git a/net/quic/quic_packet_creator.cc b/net/quic/quic_packet_creator.cc
index 77b39fa..4eb0ea9 100644
--- a/net/quic/quic_packet_creator.cc
+++ b/net/quic/quic_packet_creator.cc
@@ -312,6 +312,11 @@
   return !queued_frames_.empty();
 }
 
+bool QuicPacketCreator::HasPendingRetransmittableFrames() const {
+  return queued_retransmittable_frames_.get() != NULL &&
+      !queued_retransmittable_frames_->frames().empty();
+}
+
 size_t QuicPacketCreator::ExpansionOnNewFrame() const {
   // If packet is FEC protected, there's no expansion.
   if (should_fec_protect_) {
diff --git a/net/quic/quic_packet_creator.h b/net/quic/quic_packet_creator.h
index d7c942f..10b8e88 100644
--- a/net/quic/quic_packet_creator.h
+++ b/net/quic/quic_packet_creator.h
@@ -116,6 +116,9 @@
   // Returns true if there are frames pending to be serialized.
   bool HasPendingFrames() const;
 
+  // Returns true if there are retransmittable frames pending to be serialized.
+  bool HasPendingRetransmittableFrames() const;
+
   // Returns whether FEC protection is currently enabled. Note: Enabled does not
   // mean that an FEC group is currently active; i.e., IsFecProtected() may
   // still return false.
diff --git a/net/quic/quic_packet_generator.cc b/net/quic/quic_packet_generator.cc
index 952bdc2..9e7473e 100644
--- a/net/quic/quic_packet_generator.cc
+++ b/net/quic/quic_packet_generator.cc
@@ -97,7 +97,11 @@
                                                   FecProtection fec_protection,
                                                   QuicAckNotifier* notifier) {
   IsHandshake handshake = id == kCryptoStreamId ? IS_HANDSHAKE : NOT_HANDSHAKE;
-  SendQueuedFrames(false);
+  // To make reasoning about crypto frames easier, we don't combine them with
+  // other retransmittable frames in a single packet.
+  const bool flush = handshake == IS_HANDSHAKE &&
+      packet_creator_.HasPendingRetransmittableFrames();
+  SendQueuedFrames(flush);
 
   size_t total_bytes_consumed = 0;
   bool fin_consumed = false;
@@ -155,6 +159,11 @@
     }
   }
 
+  // Don't allow the handshake to be bundled with other retransmittable frames.
+  if (handshake == IS_HANDSHAKE) {
+    SendQueuedFrames(true);
+  }
+
   // Try to close FEC group since we've either run out of data to send or we're
   // blocked. If not in batch mode, force close the group.
   MaybeSendFecPacketAndCloseGroup(!InBatchMode());
diff --git a/net/quic/quic_packet_generator_test.cc b/net/quic/quic_packet_generator_test.cc
index 7738a8c9..331b1834 100644
--- a/net/quic/quic_packet_generator_test.cc
+++ b/net/quic/quic_packet_generator_test.cc
@@ -377,7 +377,7 @@
   delegate_.SetCanNotWrite();
 
   QuicConsumedData consumed = generator_.ConsumeData(
-      1, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
+      kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
   EXPECT_EQ(0u, consumed.bytes_consumed);
   EXPECT_FALSE(consumed.fin_consumed);
   EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -388,7 +388,7 @@
   generator_.StartBatchOperations();
 
   QuicConsumedData consumed = generator_.ConsumeData(
-      1, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
+      kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -400,7 +400,7 @@
   EXPECT_CALL(delegate_, OnSerializedPacket(_)).WillOnce(
       DoAll(SaveArg<0>(&packet_), Return(true)));
   QuicConsumedData consumed = generator_.ConsumeData(
-      1, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
+      kHeadersStreamId, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT, NULL);
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_FALSE(generator_.HasQueuedFrames());
@@ -415,8 +415,8 @@
   delegate_.SetCanWriteAnything();
   generator_.StartBatchOperations();
 
-  generator_.ConsumeData(1, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT,
-                         NULL);
+  generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true,
+                         MAY_FEC_PROTECT, NULL);
   QuicConsumedData consumed = generator_.ConsumeData(
       3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, NULL);
   EXPECT_EQ(4u, consumed.bytes_consumed);
@@ -428,8 +428,8 @@
   delegate_.SetCanWriteAnything();
   generator_.StartBatchOperations();
 
-  generator_.ConsumeData(1, MakeIOVector("foo"), 2, true, MAY_FEC_PROTECT,
-                         NULL);
+  generator_.ConsumeData(kHeadersStreamId, MakeIOVector("foo"), 2, true,
+                         MAY_FEC_PROTECT, NULL);
   QuicConsumedData consumed = generator_.ConsumeData(
       3, MakeIOVector("quux"), 7, false, MAY_FEC_PROTECT, NULL);
   EXPECT_EQ(4u, consumed.bytes_consumed);
@@ -543,7 +543,7 @@
   // Queue enough data to prevent a stream frame with a non-zero offset from
   // fitting.
   QuicConsumedData consumed = generator_.ConsumeData(
-      1, MakeIOVector("foo"), 0, false, MAY_FEC_PROTECT, NULL);
+      kHeadersStreamId, MakeIOVector("foo"), 0, false, MAY_FEC_PROTECT, NULL);
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_FALSE(consumed.fin_consumed);
   EXPECT_TRUE(generator_.HasQueuedFrames());
@@ -551,8 +551,8 @@
   // This frame will not fit with the existing frame, causing the queued frame
   // to be serialized, and it will not fit with another frame like it, so it is
   // serialized by itself.
-  consumed = generator_.ConsumeData(1, MakeIOVector("bar"), 3, true,
-                                    MAY_FEC_PROTECT, NULL);
+  consumed = generator_.ConsumeData(kHeadersStreamId, MakeIOVector("bar"), 3,
+                                    true, MAY_FEC_PROTECT, NULL);
   EXPECT_EQ(3u, consumed.bytes_consumed);
   EXPECT_TRUE(consumed.fin_consumed);
   EXPECT_FALSE(generator_.HasQueuedFrames());
diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc
index 9ad7798..398c94d 100644
--- a/net/quic/quic_protocol.cc
+++ b/net/quic/quic_protocol.cc
@@ -169,6 +169,8 @@
       return MakeQuicTag('Q', '0', '1', '8');
     case QUIC_VERSION_19:
       return MakeQuicTag('Q', '0', '1', '9');
+    case QUIC_VERSION_20:
+      return MakeQuicTag('Q', '0', '2', '0');
     default:
       // This shold be an ERROR because we should never attempt to convert an
       // invalid QuicVersion to be written to the wire.
@@ -200,6 +202,7 @@
     RETURN_STRING_LITERAL(QUIC_VERSION_17);
     RETURN_STRING_LITERAL(QUIC_VERSION_18);
     RETURN_STRING_LITERAL(QUIC_VERSION_19);
+    RETURN_STRING_LITERAL(QUIC_VERSION_20);
     default:
       return "QUIC_VERSION_UNSUPPORTED";
   }
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
index 31aca44a..e84d338 100644
--- a/net/quic/quic_protocol.h
+++ b/net/quic/quic_protocol.h
@@ -64,7 +64,7 @@
 const size_t kDefaultInitialWindow = 10;
 const uint32 kMaxInitialWindow = 100;
 
-// Default size of initial flow control window.
+// Default size of initial flow control window, for both stream and session.
 const uint32 kDefaultFlowControlSendWindow = 16 * 1024;  // 16 KB
 
 // Maximum size of the congestion window, in packets, for TCP congestion control
@@ -279,7 +279,8 @@
   QUIC_VERSION_16 = 16,
   QUIC_VERSION_17 = 17,
   QUIC_VERSION_18 = 18,
-  QUIC_VERSION_19 = 19,  // Current version.
+  QUIC_VERSION_19 = 19,
+  QUIC_VERSION_20 = 20,  // Current version.
 };
 
 // This vector contains QUIC versions which we currently support.
@@ -289,7 +290,8 @@
 //
 // IMPORTANT: if you are addding to this list, follow the instructions at
 // http://sites/quic/adding-and-removing-versions
-static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_19,
+static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_20,
+                                                     QUIC_VERSION_19,
                                                      QUIC_VERSION_18,
                                                      QUIC_VERSION_17,
                                                      QUIC_VERSION_16,
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index d5f257d7..4cd9a33 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -106,10 +106,17 @@
       goaway_received_(false),
       goaway_sent_(false),
       has_pending_handshake_(false) {
-  flow_controller_.reset(new QuicFlowController(
-      connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
-      config_.GetInitialFlowControlWindowToSend(),
-      config_.GetInitialFlowControlWindowToSend()));
+  if (connection_->version() <= QUIC_VERSION_19) {
+    flow_controller_.reset(new QuicFlowController(
+        connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
+        config_.GetInitialFlowControlWindowToSend(),
+        config_.GetInitialFlowControlWindowToSend()));
+  } else {
+    flow_controller_.reset(new QuicFlowController(
+        connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
+        config_.GetInitialSessionFlowControlWindowToSend(),
+        config_.GetInitialSessionFlowControlWindowToSend()));
+  }
 
   connection_->set_visitor(visitor_shim_.get());
   connection_->SetFromConfig(config_);
@@ -461,14 +468,36 @@
 
 void QuicSession::OnConfigNegotiated() {
   connection_->SetFromConfig(config_);
-  // Tell all streams about the newly received peer receive window.
-  if (connection()->version() >= QUIC_VERSION_17 &&
-      config_.HasReceivedInitialFlowControlWindowBytes()) {
-    // Streams which were created before the SHLO was received (0RTT requests)
-    // are now informed of the peer's initial flow control window.
-    uint32 new_window = config_.ReceivedInitialFlowControlWindowBytes();
-    OnNewStreamFlowControlWindow(new_window);
-    OnNewSessionFlowControlWindow(new_window);
+  QuicVersion version = connection()->version();
+  if (version < QUIC_VERSION_17) {
+    return;
+  }
+
+  if (version <= QUIC_VERSION_19) {
+    // QUIC_VERSION_17,18,19 don't support independent stream/session flow
+    // control windows.
+    if (config_.HasReceivedInitialFlowControlWindowBytes()) {
+      // Streams which were created before the SHLO was received (0-RTT
+      // requests) are now informed of the peer's initial flow control window.
+      uint32 new_window = config_.ReceivedInitialFlowControlWindowBytes();
+      OnNewStreamFlowControlWindow(new_window);
+      OnNewSessionFlowControlWindow(new_window);
+    }
+
+    return;
+  }
+
+  // QUIC_VERSION_20 and higher can have independent stream and session flow
+  // control windows.
+  if (config_.HasReceivedInitialStreamFlowControlWindowBytes()) {
+    // Streams which were created before the SHLO was received (0-RTT
+    // requests) are now informed of the peer's initial flow control window.
+    OnNewStreamFlowControlWindow(
+        config_.ReceivedInitialStreamFlowControlWindowBytes());
+  }
+  if (config_.HasReceivedInitialSessionFlowControlWindowBytes()) {
+    OnNewSessionFlowControlWindow(
+        config_.ReceivedInitialSessionFlowControlWindowBytes());
   }
 }
 
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index d463401..aeb4103 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -58,7 +58,11 @@
     CryptoHandshakeMessage msg;
     string error_details;
     session()->config()->SetInitialFlowControlWindowToSend(
-        kInitialFlowControlWindowForTest);
+        kInitialSessionFlowControlWindowForTest);
+    session()->config()->SetInitialStreamFlowControlWindowToSend(
+        kInitialStreamFlowControlWindowForTest);
+    session()->config()->SetInitialSessionFlowControlWindowToSend(
+        kInitialSessionFlowControlWindowForTest);
     session()->config()->ToHandshakeMessage(&msg);
     const QuicErrorCode error = session()->config()->ProcessPeerHello(
         msg, CLIENT, &error_details);
@@ -183,7 +187,11 @@
       : connection_(new MockConnection(true, SupportedVersions(GetParam()))),
         session_(connection_) {
     session_.config()->SetInitialFlowControlWindowToSend(
-        kInitialFlowControlWindowForTest);
+        kInitialSessionFlowControlWindowForTest);
+    session_.config()->SetInitialStreamFlowControlWindowToSend(
+        kInitialStreamFlowControlWindowForTest);
+    session_.config()->SetInitialSessionFlowControlWindowToSend(
+        kInitialSessionFlowControlWindowForTest);
     headers_[":host"] = "www.google.com";
     headers_[":path"] = "/index.hml";
     headers_[":scheme"] = "http";
@@ -649,9 +657,10 @@
 }
 
 TEST_P(QuicSessionTest, InvalidFlowControlWindowInHandshake) {
-  // Test that receipt of an invalid (< default) flow control window from peer
-  // results in the connection being torn down.
-  if (version() < QUIC_VERSION_17) {
+  // TODO(rjshade): Remove this test when removing QUIC_VERSION_19.
+  // Test that receipt of an invalid (< default) flow control window from
+  // the peer results in the connection being torn down.
+  if (version() <= QUIC_VERSION_16 || version() > QUIC_VERSION_19) {
     return;
   }
   ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
@@ -665,6 +674,40 @@
   session_.OnConfigNegotiated();
 }
 
+TEST_P(QuicSessionTest, InvalidStreamFlowControlWindowInHandshake) {
+  // Test that receipt of an invalid (< default) stream flow control window from
+  // the peer results in the connection being torn down.
+  if (version() <= QUIC_VERSION_19) {
+    return;
+  }
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+  uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+  QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(),
+                                                            kInvalidWindow);
+
+  EXPECT_CALL(*connection_,
+              SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW));
+  session_.OnConfigNegotiated();
+}
+
+TEST_P(QuicSessionTest, InvalidSessionFlowControlWindowInHandshake) {
+  // Test that receipt of an invalid (< default) session flow control window
+  // from the peer results in the connection being torn down.
+  if (version() <= QUIC_VERSION_19) {
+    return;
+  }
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
+
+  uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+  QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(session_.config(),
+                                                             kInvalidWindow);
+
+  EXPECT_CALL(*connection_,
+              SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW));
+  session_.OnConfigNegotiated();
+}
+
 TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) {
   if (version() < QUIC_VERSION_19) {
     return;
@@ -679,13 +722,15 @@
   // byte consumed so far and the final byte offset from the RST frame.
   TestStream* stream = session_.CreateOutgoingDataStream();
 
-  const QuicStreamOffset kByteOffset = 1 + kInitialFlowControlWindowForTest / 2;
+  const QuicStreamOffset kByteOffset =
+      1 + kInitialSessionFlowControlWindowForTest / 2;
+
   // Expect no stream WINDOW_UPDATE frames, as stream read side closed.
   EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0);
   // We do expect a connection level WINDOW_UPDATE when the stream is reset.
   EXPECT_CALL(*connection_,
-              SendWindowUpdate(
-                  0, kInitialFlowControlWindowForTest + kByteOffset)).Times(1);
+              SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest +
+                                      kByteOffset)).Times(1);
 
   QuicRstStreamFrame rst_frame(stream->id(), QUIC_STREAM_CANCELLED,
                                kByteOffset);
@@ -709,7 +754,8 @@
   // connection level flow controller when the stream is reset.
   TestStream* stream = session_.CreateOutgoingDataStream();
 
-  const QuicStreamOffset kByteOffset = 1 + kInitialFlowControlWindowForTest / 2;
+  const QuicStreamOffset kByteOffset =
+      1 + kInitialSessionFlowControlWindowForTest / 2;
   QuicStreamFrame frame(stream->id(), true, kByteOffset, IOVector());
   vector<QuicStreamFrame> frames;
   frames.push_back(frame);
@@ -720,16 +766,22 @@
   EXPECT_EQ(kByteOffset,
             stream->flow_controller()->highest_received_byte_offset());
 
-  // Expect no stream WINDOW_UPDATE frames, as stream read side closed.
-  EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0);
-  // We do expect a connection level WINDOW_UPDATE when the stream is reset.
-  EXPECT_CALL(*connection_,
-              SendWindowUpdate(
-                  0, kInitialFlowControlWindowForTest + kByteOffset)).Times(1);
+  // We only expect to see a connection WINDOW_UPDATE when talking
+  // QUIC_VERSION_19, as in this case both stream and session flow control
+  // windows are the same size. In later versions we will not see a connection
+  // level WINDOW_UPDATE when exhausting a stream, as the stream flow control
+  // limit is much lower than the connection flow control limit.
+  if (version() == QUIC_VERSION_19) {
+    // Expect no stream WINDOW_UPDATE frames, as stream read side closed.
+    EXPECT_CALL(*connection_, SendWindowUpdate(stream->id(), _)).Times(0);
+    // We do expect a connection level WINDOW_UPDATE when the stream is reset.
+    EXPECT_CALL(*connection_,
+                SendWindowUpdate(0, kInitialSessionFlowControlWindowForTest +
+                                        kByteOffset)).Times(1);
+  }
 
   // Reset stream locally.
   stream->Reset(QUIC_STREAM_CANCELLED);
-
   EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed());
 }
 
@@ -827,7 +879,7 @@
   ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control_2,
                               true);
 
-  const uint64 kLargeOffset = kInitialFlowControlWindowForTest + 1;
+  const uint64 kLargeOffset = kInitialSessionFlowControlWindowForTest + 1;
   EXPECT_CALL(*connection_,
               SendConnectionClose(QUIC_FLOW_CONTROL_RECEIVED_TOO_MUCH_DATA))
       .Times(2);
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index dd17924..2fc1b9e3 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -835,6 +835,8 @@
       server_id.is_https() ? kServerSecureInitialCongestionWindow
                            : kServerInecureInitialCongestionWindow);
   config.SetInitialFlowControlWindowToSend(kInitialReceiveWindowSize);
+  config.SetInitialStreamFlowControlWindowToSend(kInitialReceiveWindowSize);
+  config.SetInitialSessionFlowControlWindowToSend(kInitialReceiveWindowSize);
   if (http_server_properties_) {
     const HttpServerProperties::NetworkStats* stats =
         http_server_properties_->GetServerNetworkStats(
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index a98e22d..44d3a03 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -25,6 +25,34 @@
   return iov;
 }
 
+size_t GetInitialStreamFlowControlWindowToSend(QuicSession* session) {
+  QuicVersion version = session->connection()->version();
+  if (version <= QUIC_VERSION_19) {
+    return session->config()->GetInitialFlowControlWindowToSend();
+  }
+
+  return session->config()->GetInitialStreamFlowControlWindowToSend();
+}
+
+size_t GetReceivedFlowControlWindow(QuicSession* session) {
+  QuicVersion version = session->connection()->version();
+  if (version <= QUIC_VERSION_19) {
+    if (session->config()->HasReceivedInitialFlowControlWindowBytes()) {
+      return session->config()->ReceivedInitialFlowControlWindowBytes();
+    }
+
+    return kDefaultFlowControlSendWindow;
+  }
+
+  // Version must be >= QUIC_VERSION_20, so we check for stream specific flow
+  // control window.
+  if (session->config()->HasReceivedInitialStreamFlowControlWindowBytes()) {
+    return session->config()->ReceivedInitialStreamFlowControlWindowBytes();
+  }
+
+  return kDefaultFlowControlSendWindow;
+}
+
 }  // namespace
 
 // Wrapper that aggregates OnAckNotifications for packets sent using
@@ -127,14 +155,10 @@
       fec_policy_(FEC_PROTECT_OPTIONAL),
       is_server_(session_->is_server()),
       flow_controller_(
-          session_->connection(),
-          id_,
-          is_server_,
-          session_->config()->HasReceivedInitialFlowControlWindowBytes() ?
-              session_->config()->ReceivedInitialFlowControlWindowBytes() :
-              kDefaultFlowControlSendWindow,
-          session_->config()->GetInitialFlowControlWindowToSend(),
-          session_->config()->GetInitialFlowControlWindowToSend()),
+          session_->connection(), id_, is_server_,
+          GetReceivedFlowControlWindow(session),
+          GetInitialStreamFlowControlWindowToSend(session),
+          GetInitialStreamFlowControlWindowToSend(session)),
       connection_flow_controller_(session_->flow_controller()) {
 }
 
diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc
index 7b4c311..b673a4e 100644
--- a/net/quic/reliable_quic_stream_test.cc
+++ b/net/quic/reliable_quic_stream_test.cc
@@ -118,6 +118,8 @@
     // negotiated in the config.
     QuicConfigPeer::SetReceivedInitialFlowControlWindow(
         session_->config(), initial_flow_control_window_bytes_);
+    QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+        session_->config(), initial_flow_control_window_bytes_);
 
     stream_.reset(new TestStream(kHeadersStreamId, session_.get(),
                                  stream_should_process_data));
@@ -644,7 +646,7 @@
   // Receive a stream frame that violates flow control: the byte offset is
   // higher than the receive window offset.
   QuicStreamFrame frame(stream_->id(), false,
-                        kInitialFlowControlWindowForTest + 1,
+                        kInitialSessionFlowControlWindowForTest + 1,
                         MakeIOVector("."));
   EXPECT_GT(frame.offset, QuicFlowControllerPeer::ReceiveWindowOffset(
                               stream_->flow_controller()));
diff --git a/net/quic/test_tools/mock_quic_dispatcher.cc b/net/quic/test_tools/mock_quic_dispatcher.cc
index 82c9dbf..f3f41a5ae 100644
--- a/net/quic/test_tools/mock_quic_dispatcher.cc
+++ b/net/quic/test_tools/mock_quic_dispatcher.cc
@@ -6,8 +6,6 @@
 
 #include "net/quic/test_tools/quic_test_utils.h"
 
-using net::test::kInitialFlowControlWindowForTest;
-
 namespace net {
 namespace test {
 
diff --git a/net/quic/test_tools/quic_config_peer.cc b/net/quic/test_tools/quic_config_peer.cc
index 414f152..798209c 100644
--- a/net/quic/test_tools/quic_config_peer.cc
+++ b/net/quic/test_tools/quic_config_peer.cc
@@ -27,5 +27,19 @@
   config->initial_flow_control_window_bytes_.SetReceivedValue(window_bytes);
 }
 
+// static
+void QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(
+    QuicConfig* config, uint32 window_bytes) {
+  config->initial_stream_flow_control_window_bytes_.SetReceivedValue(
+      window_bytes);
+}
+
+// static
+void QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow(
+    QuicConfig* config, uint32 window_bytes) {
+  config->initial_session_flow_control_window_bytes_.SetReceivedValue(
+      window_bytes);
+}
+
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/test_tools/quic_config_peer.h b/net/quic/test_tools/quic_config_peer.h
index f471638f..53abb98 100644
--- a/net/quic/test_tools/quic_config_peer.h
+++ b/net/quic/test_tools/quic_config_peer.h
@@ -21,9 +21,16 @@
   static void SetReceivedLossDetection(QuicConfig* config,
                                        QuicTag loss_detection);
 
+  // TODO(rjshade): Remove when removing QUIC_VERSION_19.
   static void SetReceivedInitialFlowControlWindow(QuicConfig* config,
                                                   uint32 window_bytes);
 
+  static void SetReceivedInitialStreamFlowControlWindow(QuicConfig* config,
+                                                        uint32 window_bytes);
+
+  static void SetReceivedInitialSessionFlowControlWindow(QuicConfig* config,
+                                                         uint32 window_bytes);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(QuicConfigPeer);
 };
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index dac9799..b6a0744 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -586,7 +586,12 @@
 QuicConfig DefaultQuicConfig() {
   QuicConfig config;
   config.SetDefaults();
-  config.SetInitialFlowControlWindowToSend(kInitialFlowControlWindowForTest);
+  config.SetInitialFlowControlWindowToSend(
+      kInitialSessionFlowControlWindowForTest);
+  config.SetInitialStreamFlowControlWindowToSend(
+      kInitialStreamFlowControlWindowForTest);
+  config.SetInitialSessionFlowControlWindowToSend(
+      kInitialSessionFlowControlWindowForTest);
   return config;
 }
 
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 6d1de91..574e7d15c 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -29,7 +29,10 @@
 
 static const QuicConnectionId kTestConnectionId = 42;
 static const int kTestPort = 123;
-static const uint32 kInitialFlowControlWindowForTest = 32 * 1024;  // 32 KB
+static const uint32 kInitialStreamFlowControlWindowForTest =
+    32 * 1024;  // 32 KB
+static const uint32 kInitialSessionFlowControlWindowForTest =
+    64 * 1024;  // 64 KB
 
 // Data stream IDs start at 5: the crypto stream is 1, headers stream is 3.
 static const QuicStreamId kClientDataStreamId1 = 5;
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index 2ad7a391..a21dc40 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -182,9 +182,17 @@
 
     // Use different flow control windows for client/server.
     client_config_.SetInitialFlowControlWindowToSend(
-        2 * kInitialFlowControlWindowForTest);
+        2 * kInitialSessionFlowControlWindowForTest);
+    client_config_.SetInitialStreamFlowControlWindowToSend(
+        2 * kInitialStreamFlowControlWindowForTest);
+    client_config_.SetInitialSessionFlowControlWindowToSend(
+        2 * kInitialSessionFlowControlWindowForTest);
     server_config_.SetInitialFlowControlWindowToSend(
-        3 * kInitialFlowControlWindowForTest);
+        3 * kInitialSessionFlowControlWindowForTest);
+    server_config_.SetInitialStreamFlowControlWindowToSend(
+        3 * kInitialStreamFlowControlWindowForTest);
+    server_config_.SetInitialSessionFlowControlWindowToSend(
+        3 * kInitialSessionFlowControlWindowForTest);
 
     QuicInMemoryCachePeer::ResetForTests();
     AddToCache("GET", "https://www.google.com/foo",
@@ -217,12 +225,40 @@
     client_config_.SetInitialFlowControlWindowToSend(window);
   }
 
+  void set_client_initial_stream_flow_control_receive_window(uint32 window) {
+    CHECK(client_.get() == NULL);
+    DLOG(INFO) << "Setting client initial stream flow control window: "
+               << window;
+    client_config_.SetInitialStreamFlowControlWindowToSend(window);
+  }
+
+  void set_client_initial_session_flow_control_receive_window(uint32 window) {
+    CHECK(client_.get() == NULL);
+    DLOG(INFO) << "Setting client initial session flow control window: "
+               << window;
+    client_config_.SetInitialSessionFlowControlWindowToSend(window);
+  }
+
   void set_server_initial_flow_control_receive_window(uint32 window) {
     CHECK(server_thread_.get() == NULL);
     DVLOG(1) << "Setting server initial flow control window: " << window;
     server_config_.SetInitialFlowControlWindowToSend(window);
   }
 
+  void set_server_initial_stream_flow_control_receive_window(uint32 window) {
+    CHECK(server_thread_.get() == NULL);
+    DLOG(INFO) << "Setting server initial stream flow control window: "
+               << window;
+    server_config_.SetInitialStreamFlowControlWindowToSend(window);
+  }
+
+  void set_server_initial_session_flow_control_receive_window(uint32 window) {
+    CHECK(server_thread_.get() == NULL);
+    DLOG(INFO) << "Setting server initial session flow control window: "
+               << window;
+    server_config_.SetInitialSessionFlowControlWindowToSend(window);
+  }
+
   bool Initialize() {
     // Start the server first, because CreateQuicClient() attempts
     // to connect to the server.
@@ -1065,7 +1101,7 @@
   IPEndPoint old_address = client_->client()->client_address();
 
   // Stop listening on the old FD.
-  EpollServer* eps = client_->client()->epoll_server();
+  EpollServer* eps = client_->epoll_server();
   int old_fd = client_->client()->fd();
   eps->UnregisterFD(old_fd);
   // Create a new socket before closing the old one, which will result in a new
@@ -1103,7 +1139,9 @@
   EXPECT_NE(old_address.port(), new_address.port());
 }
 
-TEST_P(EndToEndTest, DifferentFlowControlWindows) {
+
+TEST_P(EndToEndTest, DifferentFlowControlWindowsQ019) {
+  // TODO(rjshade): Remove this test when removing QUIC_VERSION_19.
   // Client and server can set different initial flow control receive windows.
   // These are sent in CHLO/SHLO. Tests that these values are exchanged properly
   // in the crypto handshake.
@@ -1115,6 +1153,9 @@
   set_server_initial_flow_control_receive_window(kServerIFCW);
 
   ASSERT_TRUE(Initialize());
+  if (negotiated_version_ > QUIC_VERSION_19) {
+    return;
+  }
 
   // Values are exchanged during crypto handshake, so wait for that to finish.
   client_->client()->WaitForCryptoHandshakeConfirmed();
@@ -1136,6 +1177,66 @@
   server_thread_->Resume();
 }
 
+TEST_P(EndToEndTest, DifferentFlowControlWindowsQ020) {
+  // TODO(rjshade): Rename to DifferentFlowControlWindows when removing
+  // QUIC_VERSION_19.
+  // Client and server can set different initial flow control receive windows.
+  // These are sent in CHLO/SHLO. Tests that these values are exchanged properly
+  // in the crypto handshake.
+  const uint32 kClientStreamIFCW = 123456;
+  const uint32 kClientSessionIFCW = 234567;
+  set_client_initial_stream_flow_control_receive_window(kClientStreamIFCW);
+  set_client_initial_session_flow_control_receive_window(kClientSessionIFCW);
+
+  const uint32 kServerStreamIFCW = 654321;
+  const uint32 kServerSessionIFCW = 765432;
+  set_server_initial_stream_flow_control_receive_window(kServerStreamIFCW);
+  set_server_initial_session_flow_control_receive_window(kServerSessionIFCW);
+
+  ASSERT_TRUE(Initialize());
+  if (negotiated_version_ <= QUIC_VERSION_19) {
+    return;
+  }
+
+  // Values are exchanged during crypto handshake, so wait for that to finish.
+  client_->client()->WaitForCryptoHandshakeConfirmed();
+  server_thread_->WaitForCryptoHandshakeConfirmed();
+
+  // Open a data stream to make sure the stream level flow control is updated.
+  QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+  stream->SendBody("hello", false);
+
+  // Client should have the right values for server's receive window.
+  EXPECT_EQ(kServerStreamIFCW,
+            client_->client()
+                ->session()
+                ->config()
+                ->ReceivedInitialStreamFlowControlWindowBytes());
+  EXPECT_EQ(kServerSessionIFCW,
+            client_->client()
+                ->session()
+                ->config()
+                ->ReceivedInitialSessionFlowControlWindowBytes());
+  EXPECT_EQ(kServerStreamIFCW, QuicFlowControllerPeer::SendWindowOffset(
+                                   stream->flow_controller()));
+  EXPECT_EQ(kServerSessionIFCW,
+            QuicFlowControllerPeer::SendWindowOffset(
+                client_->client()->session()->flow_controller()));
+
+  // Server should have the right values for client's receive window.
+  server_thread_->Pause();
+  QuicDispatcher* dispatcher =
+      QuicServerPeer::GetDispatcher(server_thread_->server());
+  QuicSession* session = dispatcher->session_map().begin()->second;
+  EXPECT_EQ(kClientStreamIFCW,
+            session->config()->ReceivedInitialStreamFlowControlWindowBytes());
+  EXPECT_EQ(kClientSessionIFCW,
+            session->config()->ReceivedInitialSessionFlowControlWindowBytes());
+  EXPECT_EQ(kClientSessionIFCW, QuicFlowControllerPeer::SendWindowOffset(
+                                    session->flow_controller()));
+  server_thread_->Resume();
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace tools
diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc
index 1bee994..19e9801 100644
--- a/net/tools/quic/quic_client.cc
+++ b/net/tools/quic/quic_client.cc
@@ -19,6 +19,7 @@
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_server_id.h"
 #include "net/tools/balsa/balsa_headers.h"
+#include "net/tools/epoll_server/epoll_server.h"
 #include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/quic_socket_utils.h"
 #include "net/tools/quic/quic_spdy_client_stream.h"
@@ -35,10 +36,12 @@
 QuicClient::QuicClient(IPEndPoint server_address,
                        const QuicServerId& server_id,
                        const QuicVersionVector& supported_versions,
-                       bool print_response)
+                       bool print_response,
+                       EpollServer* epoll_server)
     : server_address_(server_address),
       server_id_(server_id),
       local_port_(0),
+      epoll_server_(epoll_server),
       fd_(-1),
       helper_(CreateQuicConnectionHelper()),
       initialized_(false),
@@ -53,11 +56,13 @@
                        const QuicServerId& server_id,
                        const QuicVersionVector& supported_versions,
                        bool print_response,
-                       const QuicConfig& config)
+                       const QuicConfig& config,
+                       EpollServer* epoll_server)
     : server_address_(server_address),
       server_id_(server_id),
       config_(config),
       local_port_(0),
+      epoll_server_(epoll_server),
       fd_(-1),
       helper_(CreateQuicConnectionHelper()),
       initialized_(false),
@@ -72,19 +77,22 @@
     session()->connection()->SendConnectionClosePacket(
         QUIC_PEER_GOING_AWAY, "");
   }
+  if (fd_ > 0) {
+    epoll_server_->UnregisterFD(fd_);
+  }
 }
 
 bool QuicClient::Initialize() {
   DCHECK(!initialized_);
 
-  epoll_server_.set_timeout_in_us(50 * 1000);
+  epoll_server_->set_timeout_in_us(50 * 1000);
   crypto_config_.SetDefaults();
 
   if (!CreateUDPSocket()) {
     return false;
   }
 
-  epoll_server_.RegisterFD(fd_, this, kEpollFlags);
+  epoll_server_->RegisterFD(fd_, this, kEpollFlags);
   initialized_ = true;
   return true;
 }
@@ -202,7 +210,7 @@
   if (connected()) {
     session()->connection()->SendConnectionClose(QUIC_PEER_GOING_AWAY);
   }
-  epoll_server_.UnregisterFD(fd_);
+  epoll_server_->UnregisterFD(fd_);
   close(fd_);
   fd_ = -1;
   initialized_ = false;
@@ -234,7 +242,7 @@
   DCHECK(connected());
 
   while (connected() && !session_->IsClosedStream(id)) {
-    epoll_server_.WaitForEventsAndExecuteCallbacks();
+    epoll_server_->WaitForEventsAndExecuteCallbacks();
   }
 }
 
@@ -242,14 +250,14 @@
   DCHECK(connected());
 
   while (connected() && !session_->IsCryptoHandshakeConfirmed()) {
-    epoll_server_.WaitForEventsAndExecuteCallbacks();
+    epoll_server_->WaitForEventsAndExecuteCallbacks();
   }
 }
 
 bool QuicClient::WaitForEvents() {
   DCHECK(connected());
 
-  epoll_server_.WaitForEventsAndExecuteCallbacks();
+  epoll_server_->WaitForEventsAndExecuteCallbacks();
   return session_->num_active_requests() != 0;
 }
 
@@ -302,7 +310,7 @@
 }
 
 QuicEpollConnectionHelper* QuicClient::CreateQuicConnectionHelper() {
-  return new QuicEpollConnectionHelper(&epoll_server_);
+  return new QuicEpollConnectionHelper(epoll_server_);
 }
 
 QuicPacketWriter* QuicClient::CreateQuicPacketWriter() {
diff --git a/net/tools/quic/quic_client.h b/net/tools/quic/quic_client.h
index da934ff7..13aff63 100644
--- a/net/tools/quic/quic_client.h
+++ b/net/tools/quic/quic_client.h
@@ -47,15 +47,19 @@
                                     const string& response_body) = 0;
   };
 
-  QuicClient(IPEndPoint server_address,
-             const QuicServerId& server_id,
-             const QuicVersionVector& supported_versions,
-             bool print_response);
+  // Create a quic client, which will have events managed by an externally owned
+  // EpollServer.
   QuicClient(IPEndPoint server_address,
              const QuicServerId& server_id,
              const QuicVersionVector& supported_versions,
              bool print_response,
-             const QuicConfig& config);
+             EpollServer* epoll_server);
+  QuicClient(IPEndPoint server_address,
+             const QuicServerId& server_id,
+             const QuicVersionVector& supported_versions,
+             bool print_response,
+             const QuicConfig& config,
+             EpollServer* epoll_server);
 
   virtual ~QuicClient();
 
@@ -131,7 +135,7 @@
 
   const IPEndPoint& client_address() const { return client_address_; }
 
-  EpollServer* epoll_server() { return &epoll_server_; }
+  EpollServer* epoll_server() { return epoll_server_; }
 
   int fd() { return fd_; }
 
@@ -142,6 +146,10 @@
     server_id_ = server_id;
   }
 
+  void SetUserAgentID(const string& user_agent_id) {
+    crypto_config_.set_user_agent_id(user_agent_id);
+  }
+
   // SetProofVerifier sets the ProofVerifier that will be used to verify the
   // server's certificate and takes ownership of |verifier|.
   void SetProofVerifier(ProofVerifier* verifier) {
@@ -208,7 +216,7 @@
   // Session which manages streams.
   scoped_ptr<QuicClientSession> session_;
   // Listens for events on the client socket.
-  EpollServer epoll_server_;
+  EpollServer* epoll_server_;
   // UDP socket.
   int fd_;
 
diff --git a/net/tools/quic/quic_client_bin.cc b/net/tools/quic/quic_client_bin.cc
index ac622cb..f743a7d 100644
--- a/net/tools/quic/quic_client_bin.cc
+++ b/net/tools/quic/quic_client_bin.cc
@@ -5,8 +5,10 @@
 // A binary wrapper for QuicClient.  Connects to --hostname via --address
 // on --port and requests URLs specified on the command line.
 // Pass --secure to check the certificates using proof verifier.
-// Pass --initial_flow_control_window to specify the size of the initial flow
-// control receive window to advertise to server.
+// Pass --initial_stream_flow_control_window to specify the size of the initial
+// stream flow control receive window to advertise to server.
+// Pass --initial_session_flow_control_window to specify the size of the initial
+// session flow control receive window to advertise to server.
 //
 // For example:
 //  quic_client --address=127.0.0.1 --port=6122 --hostname=www.google.com
@@ -22,6 +24,7 @@
 #include "net/base/privacy_mode.h"
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_server_id.h"
+#include "net/tools/epoll_server/epoll_server.h"
 #include "net/tools/quic/quic_client.h"
 
 // The port the quic client will connect to.
@@ -29,8 +32,12 @@
 std::string FLAGS_address = "127.0.0.1";
 // The hostname the quic client will connect to.
 std::string FLAGS_hostname = "localhost";
-// Size of the initial flow control receive window to advertise to server.
-int32 FLAGS_initial_flow_control_window = 100 * net::kMaxPacketSize;
+// Size of the initial stream flow control receive window to advertise to
+// server.
+int32 FLAGS_initial_stream_flow_control_window = 100 * net::kMaxPacketSize;
+// Size of the initial session flow control receive window to advertise to
+// server.
+int32 FLAGS_initial_session_flow_control_window = 200 * net::kMaxPacketSize;
 // Check the certificates using proof verifier.
 bool FLAGS_secure = false;
 
@@ -82,14 +89,20 @@
 
   net::QuicConfig config;
   config.SetDefaults();
-  config.SetInitialFlowControlWindowToSend(FLAGS_initial_flow_control_window);
+  config.SetInitialFlowControlWindowToSend(
+      FLAGS_initial_session_flow_control_window);
+  config.SetInitialStreamFlowControlWindowToSend(
+      FLAGS_initial_stream_flow_control_window);
+  config.SetInitialSessionFlowControlWindowToSend(
+      FLAGS_initial_session_flow_control_window);
 
   // TODO(rjshade): Set version on command line.
+  net::EpollServer epoll_server;
   net::tools::QuicClient client(
       net::IPEndPoint(addr, FLAGS_port),
       net::QuicServerId(FLAGS_hostname, FLAGS_port, FLAGS_secure,
                         net::PRIVACY_MODE_DISABLED),
-      net::QuicSupportedVersions(), true, config);
+      net::QuicSupportedVersions(), true, config, &epoll_server);
 
   client.Initialize();
 
diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_client_session_test.cc
index e2d39c1..7b50f42a 100644
--- a/net/tools/quic/quic_client_session_test.cc
+++ b/net/tools/quic/quic_client_session_test.cc
@@ -17,7 +17,6 @@
 using net::test::DefaultQuicConfig;
 using net::test::PacketSavingConnection;
 using net::test::SupportedVersions;
-using net::test::kInitialFlowControlWindowForTest;
 using testing::_;
 
 namespace net {
diff --git a/net/tools/quic/quic_server_session_test.cc b/net/tools/quic/quic_server_session_test.cc
index 34c0066..1e04a7f 100644
--- a/net/tools/quic/quic_server_session_test.cc
+++ b/net/tools/quic/quic_server_session_test.cc
@@ -52,7 +52,12 @@
                        QuicRandom::GetInstance()) {
     config_.SetDefaults();
     config_.set_max_streams_per_connection(3, 3);
-    config_.SetInitialFlowControlWindowToSend(kInitialFlowControlWindowForTest);
+    config_.SetInitialFlowControlWindowToSend(
+        kInitialSessionFlowControlWindowForTest);
+    config_.SetInitialStreamFlowControlWindowToSend(
+        kInitialStreamFlowControlWindowForTest);
+    config_.SetInitialSessionFlowControlWindowToSend(
+        kInitialSessionFlowControlWindowForTest);
 
     connection_ =
         new StrictMock<MockConnection>(true, SupportedVersions(GetParam()));
diff --git a/net/tools/quic/quic_spdy_client_stream_test.cc b/net/tools/quic/quic_spdy_client_stream_test.cc
index db38924c..475f832 100644
--- a/net/tools/quic/quic_spdy_client_stream_test.cc
+++ b/net/tools/quic/quic_spdy_client_stream_test.cc
@@ -44,7 +44,11 @@
     // New streams rely on having the peer's flow control receive window
     // negotiated in the config.
     session_.config()->SetInitialFlowControlWindowToSend(
-        kInitialFlowControlWindowForTest);
+        kInitialSessionFlowControlWindowForTest);
+    session_.config()->SetInitialStreamFlowControlWindowToSend(
+        kInitialStreamFlowControlWindowForTest);
+    session_.config()->SetInitialSessionFlowControlWindowToSend(
+        kInitialSessionFlowControlWindowForTest);
     stream_.reset(new QuicSpdyClientStream(3, &session_));
   }
 
diff --git a/net/tools/quic/quic_spdy_server_stream_test.cc b/net/tools/quic/quic_spdy_server_stream_test.cc
index a6a015d..e01569e 100644
--- a/net/tools/quic/quic_spdy_server_stream_test.cc
+++ b/net/tools/quic/quic_spdy_server_stream_test.cc
@@ -85,7 +85,11 @@
     // New streams rely on having the peer's flow control receive window
     // negotiated in the config.
     session_.config()->SetInitialFlowControlWindowToSend(
-        kInitialFlowControlWindowForTest);
+        kInitialSessionFlowControlWindowForTest);
+    session_.config()->SetInitialStreamFlowControlWindowToSend(
+        kInitialStreamFlowControlWindowForTest);
+    session_.config()->SetInitialSessionFlowControlWindowToSend(
+        kInitialSessionFlowControlWindowForTest);
     stream_.reset(new QuicSpdyServerStreamPeer(3, &session_));
   }
 
diff --git a/net/tools/quic/test_tools/mock_quic_dispatcher.cc b/net/tools/quic/test_tools/mock_quic_dispatcher.cc
index dc7f0d3f..13271ca 100644
--- a/net/tools/quic/test_tools/mock_quic_dispatcher.cc
+++ b/net/tools/quic/test_tools/mock_quic_dispatcher.cc
@@ -6,8 +6,6 @@
 
 #include "net/quic/test_tools/quic_test_utils.h"
 
-using net::test::kInitialFlowControlWindowForTest;
-
 namespace net {
 namespace tools {
 namespace test {
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index d0cfe687..3e77bb5 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -28,7 +28,6 @@
 using net::test::QuicConnectionPeer;
 using net::test::QuicSessionPeer;
 using net::test::ReliableQuicStreamPeer;
-using net::test::kInitialFlowControlWindowForTest;
 using std::string;
 using std::vector;
 
@@ -104,11 +103,13 @@
 MockableQuicClient::MockableQuicClient(
     IPEndPoint server_address,
     const QuicServerId& server_id,
-    const QuicVersionVector& supported_versions)
+    const QuicVersionVector& supported_versions,
+    EpollServer* epoll_server)
     : QuicClient(server_address,
                  server_id,
                  supported_versions,
-                 false),
+                 false,
+                 epoll_server),
       override_connection_id_(0),
       test_writer_(NULL) {}
 
@@ -116,12 +117,14 @@
     IPEndPoint server_address,
     const QuicServerId& server_id,
     const QuicConfig& config,
-    const QuicVersionVector& supported_versions)
+    const QuicVersionVector& supported_versions,
+    EpollServer* epoll_server)
     : QuicClient(server_address,
                  server_id,
                  supported_versions,
                  false,
-                 config),
+                 config,
+                 epoll_server),
       override_connection_id_(0),
       test_writer_(NULL) {}
 
@@ -163,7 +166,8 @@
                                                   server_address.port(),
                                                   false,
                                                   PRIVACY_MODE_DISABLED),
-                                     supported_versions)) {
+                                     supported_versions,
+                                     &epoll_server_)) {
   Initialize(true);
 }
 
@@ -176,7 +180,8 @@
                                                   server_address.port(),
                                                   secure,
                                                   PRIVACY_MODE_DISABLED),
-                                     supported_versions)) {
+                                     supported_versions,
+                                     &epoll_server_)) {
   Initialize(secure);
 }
 
@@ -193,7 +198,8 @@
                                               secure,
                                               PRIVACY_MODE_DISABLED),
                                  config,
-                                 supported_versions)) {
+                                 supported_versions,
+                                 &epoll_server_)) {
   Initialize(secure);
 }
 
@@ -228,6 +234,10 @@
   }
 }
 
+void QuicTestClient::SetUserAgentID(const string& user_agent_id) {
+  client_->SetUserAgentID(user_agent_id);
+}
+
 ssize_t QuicTestClient::SendRequest(const string& uri) {
   HTTPMessage message(HttpConstants::HTTP_1_1,
                       HttpConstants::GET,
@@ -404,9 +414,9 @@
 
 void QuicTestClient::WaitForResponseForMs(int timeout_ms) {
   int64 timeout_us = timeout_ms * base::Time::kMicrosecondsPerMillisecond;
-  int64 old_timeout_us = client()->epoll_server()->timeout_in_us();
+  int64 old_timeout_us = epoll_server()->timeout_in_us();
   if (timeout_us > 0) {
-    client()->epoll_server()->set_timeout_in_us(timeout_us);
+    epoll_server()->set_timeout_in_us(timeout_us);
   }
   const QuicClock* clock =
       QuicConnectionPeer::GetHelper(client()->session()->connection())->
@@ -419,15 +429,15 @@
     client_->WaitForEvents();
   }
   if (timeout_us > 0) {
-    client()->epoll_server()->set_timeout_in_us(old_timeout_us);
+    epoll_server()->set_timeout_in_us(old_timeout_us);
   }
 }
 
 void QuicTestClient::WaitForInitialResponseForMs(int timeout_ms) {
   int64 timeout_us = timeout_ms * base::Time::kMicrosecondsPerMillisecond;
-  int64 old_timeout_us = client()->epoll_server()->timeout_in_us();
+  int64 old_timeout_us = epoll_server()->timeout_in_us();
   if (timeout_us > 0) {
-    client()->epoll_server()->set_timeout_in_us(timeout_us);
+    epoll_server()->set_timeout_in_us(timeout_us);
   }
   const QuicClock* clock =
       QuicConnectionPeer::GetHelper(client()->session()->connection())->
@@ -441,7 +451,7 @@
     client_->WaitForEvents();
   }
   if (timeout_us > 0) {
-    client()->epoll_server()->set_timeout_in_us(old_timeout_us);
+    epoll_server()->set_timeout_in_us(old_timeout_us);
   }
 }
 
diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h
index 1c70c33..93c0352bc 100644
--- a/net/tools/quic/test_tools/quic_test_client.h
+++ b/net/tools/quic/test_tools/quic_test_client.h
@@ -14,6 +14,7 @@
 #include "net/quic/quic_packet_creator.h"
 #include "net/quic/quic_protocol.h"
 #include "net/tools/balsa/balsa_frame.h"
+#include "net/tools/epoll_server/epoll_server.h"
 #include "net/tools/quic/quic_client.h"
 #include "net/tools/quic/test_tools/simple_client.h"
 
@@ -35,12 +36,14 @@
  public:
   MockableQuicClient(IPEndPoint server_address,
                      const QuicServerId& server_id,
-                     const QuicVersionVector& supported_versions);
+                     const QuicVersionVector& supported_versions,
+                     EpollServer* epoll_server);
 
   MockableQuicClient(IPEndPoint server_address,
                      const QuicServerId& server_id,
                      const QuicConfig& config,
-                     const QuicVersionVector& supported_versions);
+                     const QuicVersionVector& supported_versions,
+                     EpollServer* epoll_server);
 
   virtual ~MockableQuicClient() OVERRIDE;
   virtual QuicPacketWriter* CreateQuicPacketWriter() OVERRIDE;
@@ -79,6 +82,9 @@
   // name is recorded and available with |cert_common_name()|.
   void ExpectCertificates(bool on);
 
+  // Sets the |user_agent_id| of the |client_|.
+  void SetUserAgentID(const string& user_agent_id);
+
   // Wraps data in a quic packet and sends it.
   ssize_t SendData(string data, bool last_data);
 
@@ -157,6 +163,8 @@
 
   void WaitForWriteToFlush();
 
+  EpollServer* epoll_server() { return &epoll_server_; }
+
  protected:
   QuicTestClient();
 
@@ -165,6 +173,7 @@
   void set_client(MockableQuicClient* client) { client_.reset(client); }
 
  private:
+  EpollServer epoll_server_;
   scoped_ptr<MockableQuicClient> client_;  // The actual client
   QuicSpdyClientStream* stream_;
 
diff --git a/net/tools/quic/test_tools/quic_test_utils.cc b/net/tools/quic/test_tools/quic_test_utils.cc
index d50ee49..d46eae5 100644
--- a/net/tools/quic/test_tools/quic_test_utils.cc
+++ b/net/tools/quic/test_tools/quic_test_utils.cc
@@ -13,7 +13,6 @@
 using net::test::MakeAckFrame;
 using net::test::MockHelper;
 using net::test::QuicConnectionPeer;
-using net::test::kInitialFlowControlWindowForTest;
 
 namespace net {
 namespace tools {
diff --git a/net/tools/quic/test_tools/quic_test_utils.h b/net/tools/quic/test_tools/quic_test_utils.h
index 2ff8c84..a889a42e 100644
--- a/net/tools/quic/test_tools/quic_test_utils.h
+++ b/net/tools/quic/test_tools/quic_test_utils.h
@@ -27,7 +27,10 @@
 
 static const QuicConnectionId kTestConnectionId = 42;
 static const int kTestPort = 123;
-static const uint32 kInitialFlowControlWindowForTest = 32 * 1024;  // 32 KB
+static const uint32 kInitialStreamFlowControlWindowForTest =
+    32 * 1024;  // 32 KB
+static const uint32 kInitialSessionFlowControlWindowForTest =
+    64 * 1024;  // 64 KB
 
 // Testing convenience method to construct a QuicAckFrame with |num_nack_ranges|
 // nack ranges of width 1 packet, starting from |least_unacked|.