Land Recent QUIC Changes.

Trying to merge as many of flow control changes as possible before the
branch (we have all changed until Fri 06/13 21:20 UTC).

Test tools for udp proxying.  This involves a few refactors of test
tools but nothing which affects the internal server.

minor refactors to allow quic udp proxy testing.

Merge internal change: 69157651
https://codereview.chromium.org/331963002/


Added FEC policy per stream, which is translated to an FEC protection
value on writes further on down.

Merge internal change: 69153464
https://codereview.chromium.org/338623002/


Pull out stream/session updates from OnConfigNegotiated. Preparation for
updating stream/session with different received windows.

QUIC refector: Pull out stream/session updates from OnConfigNegotiated

Merge internal change: 69106467
https://codereview.chromium.org/337723003/


Rather than passing initial_flow_control_window all the way down the
call stack, put it inside QuicConfig as intended: each member of
QuicConfig has a "value to send" field, so populate this at the top
level.

rtenneti: when porting to Chromium, you should add
config.SetInitialFlowControlWindowToSend(kInitialReceiveWindowSize)

in QuicStreamFactory::CreateSession, just above line 837: *session =
  new QuicClientSession(...)

Store initial flow control window for QUIC in QuicConfig. No behavior
change intended.

Added the following unit tests to EndToEndTest.cc
+ DoNotSetResumeWriteAlarmIfConnectionFlowControlBlocked
+ NegotiateMaxOpenStreams

Merge internal change: 69079363
https://codereview.chromium.org/333803007/


First version of a QUIC SendAlgorithmSimulator which is designed to
simulate BBR and TCP flows and changes.

Merge internal change: 69035927
https://codereview.chromium.org/330163003/

[email protected], [email protected]

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@277959 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/net.gypi b/net/net.gypi
index 748acc3..6571fb9a 100644
--- a/net/net.gypi
+++ b/net/net.gypi
@@ -1404,6 +1404,8 @@
       'quic/congestion_control/leaky_bucket_test.cc',
       'quic/congestion_control/pacing_sender_test.cc',
       'quic/congestion_control/rtt_stats_test.cc',
+      'quic/congestion_control/send_algorithm_simulator.cc',
+      'quic/congestion_control/send_algorithm_simulator.h',
       'quic/congestion_control/tcp_cubic_sender_test.cc',
       'quic/congestion_control/tcp_loss_algorithm_test.cc',
       'quic/congestion_control/tcp_receiver_test.cc',
diff --git a/net/quic/congestion_control/send_algorithm_simulator.cc b/net/quic/congestion_control/send_algorithm_simulator.cc
new file mode 100644
index 0000000..d539013
--- /dev/null
+++ b/net/quic/congestion_control/send_algorithm_simulator.cc
@@ -0,0 +1,273 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/quic/congestion_control/send_algorithm_simulator.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/rand_util.h"
+#include "net/quic/crypto/quic_random.h"
+
+using std::list;
+using std::max;
+using std::min;
+
+namespace net {
+
+namespace {
+
+const QuicByteCount kPacketSize = 1200;
+
+}  // namespace
+
+SendAlgorithmSimulator::SendAlgorithmSimulator(
+    SendAlgorithmInterface* send_algorithm,
+    MockClock* clock,
+    RttStats* rtt_stats,
+    QuicBandwidth bandwidth,
+    QuicTime::Delta rtt)
+    : send_algorithm_(send_algorithm),
+      clock_(clock),
+      rtt_stats_(rtt_stats),
+      next_sent_(1),
+      last_acked_(0),
+      next_acked_(1),
+      lose_next_ack_(false),
+      bytes_in_flight_(0),
+      forward_loss_rate_(0),
+      reverse_loss_rate_(0),
+      loss_correlation_(0),
+      bandwidth_(bandwidth),
+      rtt_(rtt),
+      buffer_size_(1000000),
+      max_cwnd_(0),
+      min_cwnd_(100000),
+      max_cwnd_drop_(0),
+      last_cwnd_(0) {
+  uint32 seed = base::RandInt(0, std::numeric_limits<int32>::max());
+  DVLOG(1) << "Seeding SendAlgorithmSimulator with " << seed;
+  simple_random_.set_seed(seed);
+}
+
+SendAlgorithmSimulator::~SendAlgorithmSimulator() {}
+
+// Sends the specified number of bytes as quickly as possible and returns the
+// average bandwidth in bytes per second.  The time elapsed is based on
+// waiting for all acks to arrive.
+QuicBandwidth SendAlgorithmSimulator::SendBytes(size_t num_bytes) {
+  const QuicTime start_time = clock_->Now();
+  size_t bytes_acked = 0;
+  while (bytes_acked < num_bytes) {
+    DVLOG(1) << "bytes_acked:" << bytes_acked << " bytes_in_flight_:"
+             << bytes_in_flight_ << " CWND(bytes):"
+             << send_algorithm_->GetCongestionWindow();
+    // Determine the times of next send and of the next ack arrival.
+    QuicTime::Delta send_delta = send_algorithm_->TimeUntilSend(
+        clock_->Now(), bytes_in_flight_, HAS_RETRANSMITTABLE_DATA);
+    // If we've already sent enough bytes, wait for them to be acked.
+    if (bytes_acked + bytes_in_flight_ >= num_bytes) {
+      send_delta = QuicTime::Delta::Infinite();
+    }
+    QuicTime::Delta ack_delta = NextAckDelta();
+    // If both times are infinite, fire a TLP.
+    if (ack_delta.IsInfinite() && send_delta.IsInfinite()) {
+      DVLOG(1) << "Both times are infinite, simulating a TLP.";
+      // TODO(ianswett): Use a more sophisticated TLP timer.
+      clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(100));
+      SendDataNow();
+    } else if (ack_delta < send_delta) {
+      DVLOG(1) << "Handling ack, advancing time:"
+               << ack_delta.ToMicroseconds() << "us";
+      // Ack data all the data up to ack time and lose any missing sequence
+      // numbers.
+      clock_->AdvanceTime(ack_delta);
+      bytes_acked += HandlePendingAck();
+    } else {
+      DVLOG(1) << "Sending, advancing time:"
+               << send_delta.ToMicroseconds() << "us";
+      clock_->AdvanceTime(send_delta);
+      SendDataNow();
+    }
+    RecordStats();
+  }
+  return QuicBandwidth::FromBytesAndTimeDelta(
+      num_bytes, clock_->Now().Subtract(start_time));
+}
+
+// NextAck takes into account packet loss in both forward and reverse
+// direction, as well as correlated losses.  And it assumes the receiver acks
+// every other packet when there is no loss.
+QuicTime::Delta SendAlgorithmSimulator::NextAckDelta() {
+  if (sent_packets_.empty() || AllPacketsLost()) {
+    DVLOG(1) << "No outstanding packets to cause acks. sent_packets_.size():"
+             << sent_packets_.size();
+    return QuicTime::Delta::Infinite();
+  }
+
+  // If necessary, determine next_acked_.
+  // This is only done once to ensure multiple calls return the same time.
+  FindNextAcked();
+
+  // If only one packet is acked, simulate a delayed ack.
+  if (next_acked_ - last_acked_ == 1) {
+    return sent_packets_.front().ack_time.Add(
+        QuicTime::Delta::FromMilliseconds(100)).Subtract(clock_->Now());
+  }
+  for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+       it != sent_packets_.end(); ++it) {
+    if (next_acked_ == it->sequence_number) {
+      return it->ack_time.Subtract(clock_->Now());
+    }
+  }
+  LOG(DFATAL) << "Error, next_acked_: " << next_acked_
+              << " should have been found in sent_packets_";
+  return QuicTime::Delta::Infinite();
+}
+
+bool SendAlgorithmSimulator::AllPacketsLost() {
+  for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+       it != sent_packets_.end(); ++it) {
+    if (it->ack_time.IsInitialized()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+void SendAlgorithmSimulator::FindNextAcked() {
+  // TODO(ianswett): Add a simpler mode which acks every packet.
+  bool packets_lost = false;
+  if (next_acked_ == last_acked_) {
+    // Determine if the next ack is lost only once, to ensure determinism.
+    lose_next_ack_ =
+        reverse_loss_rate_ * kuint64max > simple_random_.RandUint64();
+  }
+  bool two_acks_remaining = lose_next_ack_;
+  next_acked_ = last_acked_;
+  // Remove any packets that are simulated as lost.
+  for (list<SentPacket>::const_iterator it = sent_packets_.begin();
+      it != sent_packets_.end(); ++it) {
+    // Lost packets don't trigger an ack.
+    if (it->ack_time  == QuicTime::Zero()) {
+      packets_lost = true;
+      continue;
+    }
+    // Buffer dropped packets are skipped automatically, but still end up
+    // being lost and cause acks to be sent immediately.
+    if (next_acked_ < it->sequence_number - 1) {
+      packets_lost = true;
+    }
+    next_acked_ = it->sequence_number;
+    if (packets_lost || (next_acked_ - last_acked_) % 2 == 0) {
+      if (two_acks_remaining) {
+        two_acks_remaining = false;
+      } else {
+        break;
+      }
+    }
+  }
+  DVLOG(1) << "FindNextAcked found next_acked_:" << next_acked_
+           << " last_acked:" << last_acked_;
+}
+
+int SendAlgorithmSimulator::HandlePendingAck() {
+  DCHECK_LT(last_acked_, next_acked_);
+  SendAlgorithmInterface::CongestionMap acked_packets;
+  SendAlgorithmInterface::CongestionMap lost_packets;
+  // Some entries may be missing from the sent_packets_ array, if they were
+  // dropped due to buffer overruns.
+  SentPacket largest_observed = sent_packets_.front();
+  while (last_acked_ < next_acked_) {
+    ++last_acked_;
+    TransmissionInfo info = TransmissionInfo();
+    info.bytes_sent = kPacketSize;
+    info.in_flight = true;
+    // If it's missing from the array, it's a loss.
+    if (sent_packets_.front().sequence_number > last_acked_) {
+      DVLOG(1) << "Lost packet:" << last_acked_
+               << " dropped by buffer overflow.";
+      lost_packets[last_acked_] = info;
+      continue;
+    }
+    if (sent_packets_.front().ack_time.IsInitialized()) {
+      acked_packets[last_acked_] = info;
+    } else {
+      lost_packets[last_acked_] = info;
+    }
+    // Remove all packets from the front to next_acked_.
+    largest_observed = sent_packets_.front();
+    sent_packets_.pop_front();
+  }
+
+  DCHECK(largest_observed.ack_time.IsInitialized());
+  rtt_stats_->UpdateRtt(
+      largest_observed.ack_time.Subtract(largest_observed.send_time),
+      QuicTime::Delta::Zero(),
+      clock_->Now());
+  send_algorithm_->OnCongestionEvent(
+      true, bytes_in_flight_, acked_packets, lost_packets);
+  DCHECK_LE(kPacketSize * (acked_packets.size() + lost_packets.size()),
+            bytes_in_flight_);
+  bytes_in_flight_ -=
+      kPacketSize * (acked_packets.size() + lost_packets.size());
+  return acked_packets.size() * kPacketSize;
+}
+
+void SendAlgorithmSimulator::SendDataNow() {
+  DVLOG(1) << "Sending packet:" << next_sent_ << " bytes_in_flight:"
+           << bytes_in_flight_;
+  send_algorithm_->OnPacketSent(
+      clock_->Now(), bytes_in_flight_,
+      next_sent_, kPacketSize, HAS_RETRANSMITTABLE_DATA);
+  // Lose the packet immediately if the buffer is full.
+  if (sent_packets_.size() * kPacketSize < buffer_size_) {
+    // TODO(ianswett): This buffer simulation is an approximation.
+    // An ack time of zero means loss.
+    bool packet_lost =
+        forward_loss_rate_ * kuint64max > simple_random_.RandUint64();
+    // Handle correlated loss.
+    if (!sent_packets_.empty() &&
+        !sent_packets_.back().ack_time.IsInitialized() &&
+        loss_correlation_ * kuint64max > simple_random_.RandUint64()) {
+      packet_lost = true;
+    }
+
+    QuicTime ack_time = clock_->Now().Add(rtt_);
+    // If the number of bytes in flight are less than the bdp, there's
+    // no buffering delay.  Bytes lost from the buffer are not counted.
+    QuicByteCount bdp = bandwidth_.ToBytesPerPeriod(rtt_);
+    if (sent_packets_.size() * kPacketSize > bdp) {
+      QuicByteCount qsize = sent_packets_.size() * kPacketSize - bdp;
+      ack_time = ack_time.Add(bandwidth_.TransferTime(qsize));
+    }
+    // If the packet is lost, give it an ack time of Zero.
+    sent_packets_.push_back(SentPacket(
+        next_sent_, clock_->Now(), packet_lost ? QuicTime::Zero() : ack_time));
+  }
+  ++next_sent_;
+  bytes_in_flight_ += kPacketSize;
+}
+
+void SendAlgorithmSimulator::RecordStats() {
+  QuicByteCount cwnd = send_algorithm_->GetCongestionWindow();
+  max_cwnd_ = max(max_cwnd_, cwnd);
+  min_cwnd_ = min(min_cwnd_, cwnd);
+  if (last_cwnd_ > cwnd) {
+    max_cwnd_drop_ = max(max_cwnd_drop_, last_cwnd_ - cwnd);
+  }
+  last_cwnd_ = cwnd;
+}
+
+// Advance the time by |delta| without sending anything.
+void SendAlgorithmSimulator::AdvanceTime(QuicTime::Delta delta) {
+  clock_->AdvanceTime(delta);
+}
+
+// Elapsed time from the start of the connection.
+QuicTime SendAlgorithmSimulator::ElapsedTime() {
+  return clock_->Now();
+}
+
+}  // namespace net
diff --git a/net/quic/congestion_control/send_algorithm_simulator.h b/net/quic/congestion_control/send_algorithm_simulator.h
new file mode 100644
index 0000000..e9b80fa
--- /dev/null
+++ b/net/quic/congestion_control/send_algorithm_simulator.h
@@ -0,0 +1,133 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// A test only class to enable simulations of send algorithms.
+
+#ifndef NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
+#define NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "net/quic/congestion_control/send_algorithm_interface.h"
+#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_time.h"
+#include "net/quic/test_tools/mock_clock.h"
+#include "net/quic/test_tools/quic_test_utils.h"
+
+namespace net {
+
+class SendAlgorithmSimulator {
+ public:
+  struct SentPacket {
+    SentPacket(QuicPacketSequenceNumber sequence_number,
+               QuicTime send_time,
+               QuicTime ack_time) :
+        sequence_number(sequence_number),
+        send_time(send_time),
+        ack_time(ack_time) {}
+    QuicPacketSequenceNumber sequence_number;
+    QuicTime send_time;
+    QuicTime ack_time;
+  };
+
+  // |rtt_stats| should be the same RttStats used by the |send_algorithm|.
+  SendAlgorithmSimulator(SendAlgorithmInterface* send_algorithm,
+                         MockClock* clock_,
+                         RttStats* rtt_stats,
+                         QuicBandwidth bandwidth,
+                         QuicTime::Delta rtt);
+  ~SendAlgorithmSimulator();
+
+  void set_forward_loss_rate(float loss_rate) {
+    DCHECK_LT(loss_rate, 1.0f);
+    forward_loss_rate_ = loss_rate;
+  }
+
+  void set_reverse_loss_rate(float loss_rate) {
+    DCHECK_LT(loss_rate, 1.0f);
+    reverse_loss_rate_ = loss_rate;
+  }
+
+  void set_loss_correlation(float loss_correlation) {
+    DCHECK_LT(loss_correlation, 1.0f);
+    loss_correlation_ = loss_correlation;
+  }
+
+  void set_buffer_size(size_t buffer_size_bytes) {
+    buffer_size_ = buffer_size_bytes;
+  }
+
+  // Sends the specified number of bytes as quickly as possible and returns the
+  // average bandwidth in bytes per second.  The time elapsed is based on
+  // waiting for all acks to arrive.
+  QuicBandwidth SendBytes(size_t num_bytes);
+
+  const RttStats* rtt_stats() const { return rtt_stats_; }
+
+  QuicByteCount max_cwnd() const { return max_cwnd_; }
+  QuicByteCount min_cwnd() const { return min_cwnd_; }
+  QuicByteCount max_cwnd_drop() const { return max_cwnd_drop_; }
+  QuicByteCount last_cwnd() const { return last_cwnd_; }
+
+ private:
+  // NextAckTime takes into account packet loss in both forward and reverse
+  // direction, as well as delayed ack behavior.
+  QuicTime::Delta NextAckDelta();
+
+  // Whether all packets in sent_packets_ are lost.
+  bool AllPacketsLost();
+
+  // Sets the next acked.
+  void FindNextAcked();
+
+  // Process all the acks that should have arrived by the current time, and
+  // lose any packets that are missing.  Returns the number of bytes acked.
+  int HandlePendingAck();
+
+  void SendDataNow();
+  void RecordStats();
+
+  // Advance the time by |delta| without sending anything.
+  void AdvanceTime(QuicTime::Delta delta);
+
+  // Elapsed time from the start of the connection.
+  QuicTime ElapsedTime();
+
+  SendAlgorithmInterface* send_algorithm_;
+  MockClock* clock_;
+  RttStats* rtt_stats_;
+  // Next packet sequence number to send.
+  QuicPacketSequenceNumber next_sent_;
+  // Last packet sequence number acked.
+  QuicPacketSequenceNumber last_acked_;
+  // Packet sequence number to ack up to.
+  QuicPacketSequenceNumber next_acked_;
+  // Whether the next ack should be lost.
+  bool lose_next_ack_;
+  QuicByteCount bytes_in_flight_;
+  // The times acks are expected, assuming acks are not lost and every packet
+  // is acked.
+  std::list<SentPacket> sent_packets_;
+
+  test::SimpleRandom simple_random_;
+  float forward_loss_rate_;  // Loss rate on the forward path.
+  float reverse_loss_rate_;  // Loss rate on the reverse path.
+  float loss_correlation_;   // Likelihood the subsequent packet is lost.
+  QuicBandwidth bandwidth_;
+  QuicTime::Delta rtt_;
+  size_t buffer_size_;       // In bytes.
+
+  // Stats collected for understanding the congestion control.
+  QuicByteCount max_cwnd_;
+  QuicByteCount min_cwnd_;
+  QuicByteCount max_cwnd_drop_;
+  QuicByteCount last_cwnd_;
+
+  DISALLOW_COPY_AND_ASSIGN(SendAlgorithmSimulator);
+};
+
+}  // namespace net
+
+#endif  // NET_QUIC_CONGESTION_CONTROL_SEND_ALGORITHM_SIMULATOR_H_
diff --git a/net/quic/crypto/channel_id.h b/net/quic/crypto/channel_id.h
index cfa618f..7d29932 100644
--- a/net/quic/crypto/channel_id.h
+++ b/net/quic/crypto/channel_id.h
@@ -18,7 +18,7 @@
 // ChannelID key.
 class NET_EXPORT_PRIVATE ChannelIDKey {
  public:
-  virtual ~ChannelIDKey() { }
+  virtual ~ChannelIDKey() {}
 
   // Sign signs |signed_data| using the ChannelID private key and puts the
   // signature into |out_signature|. It returns true on success.
diff --git a/net/quic/crypto/crypto_server_test.cc b/net/quic/crypto/crypto_server_test.cc
index f3738ff..f1892d3 100644
--- a/net/quic/crypto/crypto_server_test.cc
+++ b/net/quic/crypto/crypto_server_test.cc
@@ -182,9 +182,8 @@
     string error_details;
     QuicErrorCode error = config_.ProcessClientHello(
         result, 1 /* ConnectionId */, client_address_,
-        supported_versions_.front(), supported_versions_,
-        kInitialFlowControlWindowForTest, &clock_, rand_, &params_, &out_,
-        &error_details);
+        supported_versions_.front(), supported_versions_, &clock_, rand_,
+        &params_, &out_, &error_details);
 
     if (should_succeed) {
       ASSERT_EQ(error, QUIC_NO_ERROR)
@@ -523,35 +522,5 @@
   EXPECT_EQ(kREJ, out_.tag());
 }
 
-TEST_F(CryptoServerTest, InitialFlowControlWindow) {
-  // Test that the SHLO contains a value for initial flow control window.
-  CryptoHandshakeMessage msg = CryptoTestUtils::Message(
-      "CHLO",
-      "AEAD", "AESG",
-      "KEXS", "C255",
-      "SCID", scid_hex_.c_str(),
-      "#004b5453", srct_hex_.c_str(),
-      "PUBS", pub_hex_.c_str(),
-      "NONC", nonce_hex_.c_str(),
-      "VER\0", client_version_.data(),
-      "$padding", static_cast<int>(kClientHelloMinimumSize),
-      NULL);
-  ShouldSucceed(msg);
-  // The message should be rejected because the strike-register is still
-  // quiescent.
-  ASSERT_EQ(kREJ, out_.tag());
-  config_.set_replay_protection(false);
-
-  // The message should be accepted now.
-  ShouldSucceed(msg);
-  ASSERT_EQ(kSHLO, out_.tag());
-  CheckServerHello(out_);
-
-  // Ensure that the kIFCW tag is populated correctly.
-  QuicTag ifcw;
-  EXPECT_EQ(QUIC_NO_ERROR, out_.GetUint32(kIFCW, &ifcw));
-  EXPECT_EQ(kInitialFlowControlWindowForTest, ifcw);
-}
-
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/crypto/quic_crypto_client_config.cc b/net/quic/crypto/quic_crypto_client_config.cc
index ae572483..8dc6a554 100644
--- a/net/quic/crypto/quic_crypto_client_config.cc
+++ b/net/quic/crypto/quic_crypto_client_config.cc
@@ -346,7 +346,6 @@
     const QuicServerId& server_id,
     QuicConnectionId connection_id,
     const QuicVersion preferred_version,
-    uint32 initial_flow_control_window_bytes,
     const CachedState* cached,
     QuicWallTime now,
     QuicRandom* rand,
@@ -359,9 +358,6 @@
   FillInchoateClientHello(server_id, preferred_version, cached,
                           out_params, out);
 
-  // Set initial receive window for flow control.
-  out->SetValue(kIFCW, initial_flow_control_window_bytes);
-
   const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
   if (!scfg) {
     // This should never happen as our caller should have checked
diff --git a/net/quic/crypto/quic_crypto_client_config.h b/net/quic/crypto/quic_crypto_client_config.h
index 14fa0d4..8aa4527 100644
--- a/net/quic/crypto/quic_crypto_client_config.h
+++ b/net/quic/crypto/quic_crypto_client_config.h
@@ -161,9 +161,6 @@
   // the server's hostname in order to perform a handshake. This can be checked
   // with the |IsComplete| member of |CachedState|.
   //
-  // |initial_flow_control_window_bytes| is the size of the initial flow
-  // control window this client will use for new streams.
-  //
   // |now| and |rand| are used to generate the nonce and |out_params| is
   // filled with the results of the handshake that the server is expected to
   // accept. |preferred_version| is the version of the QUIC protocol that this
@@ -176,7 +173,6 @@
   QuicErrorCode FillClientHello(const QuicServerId& server_id,
                                 QuicConnectionId connection_id,
                                 const QuicVersion preferred_version,
-                                uint32 initial_flow_control_window_bytes,
                                 const CachedState* cached,
                                 QuicWallTime now,
                                 QuicRandom* rand,
diff --git a/net/quic/crypto/quic_crypto_client_config_test.cc b/net/quic/crypto/quic_crypto_client_config_test.cc
index 75cf2397..fdce91c 100644
--- a/net/quic/crypto/quic_crypto_client_config_test.cc
+++ b/net/quic/crypto/quic_crypto_client_config_test.cc
@@ -110,7 +110,6 @@
   QuicCryptoClientConfig config;
   QuicCryptoNegotiatedParameters params;
   QuicConnectionId kConnectionId = 1234;
-  uint32 kInitialFlowControlWindow = 5678;
   string error_details;
   MockRandom rand;
   CryptoHandshakeMessage chlo;
@@ -118,7 +117,6 @@
   config.FillClientHello(server_id,
                          kConnectionId,
                          QuicVersionMax(),
-                         kInitialFlowControlWindow,
                          &state,
                          QuicWallTime::Zero(),
                          &rand,
@@ -131,10 +129,6 @@
   QuicTag cver;
   EXPECT_EQ(QUIC_NO_ERROR, chlo.GetUint32(kVER, &cver));
   EXPECT_EQ(QuicVersionToQuicTag(QuicVersionMax()), cver);
-
-  QuicTag ifcw;
-  EXPECT_EQ(QUIC_NO_ERROR, chlo.GetUint32(kIFCW, &ifcw));
-  EXPECT_EQ(kInitialFlowControlWindow, ifcw);
 }
 
 TEST(QuicCryptoClientConfigTest, ProcessServerDowngradeAttack) {
diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc
index 65db0f54..c8114ff 100644
--- a/net/quic/crypto/quic_crypto_server_config.cc
+++ b/net/quic/crypto/quic_crypto_server_config.cc
@@ -490,7 +490,6 @@
     IPEndPoint client_address,
     QuicVersion version,
     const QuicVersionVector& supported_versions,
-    uint32 initial_flow_control_window_bytes,
     const QuicClock* clock,
     QuicRandom* rand,
     QuicCryptoNegotiatedParameters *params,
@@ -739,9 +738,6 @@
   out->SetStringPiece(kCADR, address_coder.Encode());
   out->SetStringPiece(kPUBS, forward_secure_public_value);
 
-  // Set initial receive window for flow control.
-  out->SetValue(kIFCW, initial_flow_control_window_bytes);
-
   return QUIC_NO_ERROR;
 }
 
diff --git a/net/quic/crypto/quic_crypto_server_config.h b/net/quic/crypto/quic_crypto_server_config.h
index 216dbbf6..566e60d5 100644
--- a/net/quic/crypto/quic_crypto_server_config.h
+++ b/net/quic/crypto/quic_crypto_server_config.h
@@ -204,7 +204,6 @@
       IPEndPoint client_address,
       QuicVersion version,
       const QuicVersionVector& supported_versions,
-      uint32 initial_flow_control_window_bytes,
       const QuicClock* clock,
       QuicRandom* rand,
       QuicCryptoNegotiatedParameters* params,
diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc
index 2acaf86..ba95c7a 100644
--- a/net/quic/quic_client_session.cc
+++ b/net/quic/quic_client_session.cc
@@ -140,12 +140,10 @@
     scoped_ptr<QuicServerInfo> server_info,
     const QuicServerId& server_id,
     const QuicConfig& config,
-    uint32 max_flow_control_receive_window_bytes,
     QuicCryptoClientConfig* crypto_config,
     base::TaskRunner* task_runner,
     NetLog* net_log)
     : QuicClientSessionBase(connection,
-                            max_flow_control_receive_window_bytes,
                             config),
       require_confirmation_(false),
       stream_factory_(stream_factory),
diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h
index c64e908..7fbe3db 100644
--- a/net/quic/quic_client_session.h
+++ b/net/quic/quic_client_session.h
@@ -98,11 +98,9 @@
                     scoped_ptr<QuicServerInfo> server_info,
                     const QuicServerId& server_id,
                     const QuicConfig& config,
-                    uint32 max_flow_control_receive_window_bytes,
                     QuicCryptoClientConfig* crypto_config,
                     base::TaskRunner* task_runner,
                     NetLog* net_log);
-
   virtual ~QuicClientSession();
 
   void AddObserver(Observer* observer);
diff --git a/net/quic/quic_client_session_base.cc b/net/quic/quic_client_session_base.cc
index f735fac..4343011 100644
--- a/net/quic/quic_client_session_base.cc
+++ b/net/quic/quic_client_session_base.cc
@@ -8,11 +8,8 @@
 
 QuicClientSessionBase::QuicClientSessionBase(
     QuicConnection* connection,
-    uint32 max_flow_control_receive_window_bytes,
     const QuicConfig& config)
-    : QuicSession(connection,
-                  max_flow_control_receive_window_bytes,
-                  config) {}
+    : QuicSession(connection, config) {}
 
 QuicClientSessionBase::~QuicClientSessionBase() {}
 
diff --git a/net/quic/quic_client_session_base.h b/net/quic/quic_client_session_base.h
index 007d3ba..eab5d08 100644
--- a/net/quic/quic_client_session_base.h
+++ b/net/quic/quic_client_session_base.h
@@ -14,7 +14,6 @@
 class NET_EXPORT_PRIVATE QuicClientSessionBase : public QuicSession {
  public:
   QuicClientSessionBase(QuicConnection* connection,
-                        uint32 max_flow_control_receive_window_bytes,
                         const QuicConfig& config);
 
   virtual ~QuicClientSessionBase();
diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc
index 8706d7c..264fbfa 100644
--- a/net/quic/quic_client_session_test.cc
+++ b/net/quic/quic_client_session_test.cc
@@ -33,8 +33,7 @@
 
 class TestPacketWriter : public QuicDefaultPacketWriter {
  public:
-  TestPacketWriter(QuicVersion version) : version_(version) {
-  }
+  TestPacketWriter(QuicVersion version) : version_(version) {}
 
   // QuicPacketWriter
   virtual WriteResult WritePacket(
@@ -72,8 +71,7 @@
                  make_scoped_ptr((QuicServerInfo*)NULL),
                  QuicServerId(kServerHostname, kServerPort, false,
                               PRIVACY_MODE_DISABLED),
-                 DefaultQuicConfig(), kInitialFlowControlWindowForTest,
-                 &crypto_config_,
+                 DefaultQuicConfig(), &crypto_config_,
                  base::MessageLoop::current()->message_loop_proxy().get(),
                  &net_log_) {
     session_.config()->SetDefaults();
diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc
index 215c362..f7b82501 100644
--- a/net/quic/quic_config.cc
+++ b/net/quic/quic_config.cc
@@ -537,11 +537,21 @@
 }
 
 void QuicConfig::SetInitialFlowControlWindowToSend(uint32 window_bytes) {
+  if (window_bytes < kDefaultFlowControlSendWindow) {
+    LOG(DFATAL) << "Initial flow control receive window (" << window_bytes
+                << ") cannot be set lower than default ("
+                << kDefaultFlowControlSendWindow << ").";
+    window_bytes = kDefaultFlowControlSendWindow;
+  }
   initial_flow_control_window_bytes_.SetSendValue(window_bytes);
 }
 
+uint32 QuicConfig::GetInitialFlowControlWindowToSend() const {
+  return initial_flow_control_window_bytes_.GetSendValue();
+}
+
 bool QuicConfig::HasReceivedInitialFlowControlWindowBytes() const {
-  return initial_flow_control_window_bytes_.HasReceivedValue();;
+  return initial_flow_control_window_bytes_.HasReceivedValue();
 }
 
 uint32 QuicConfig::ReceivedInitialFlowControlWindowBytes() const {
@@ -573,6 +583,8 @@
                                   kDefaultMaxStreamsPerConnection);
   max_time_before_crypto_handshake_ = QuicTime::Delta::FromSeconds(
       kDefaultMaxTimeForCryptoHandshakeSecs);
+
+  SetInitialFlowControlWindowToSend(kDefaultFlowControlSendWindow);
 }
 
 void QuicConfig::EnablePacing(bool enable_pacing) {
diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h
index 4d65097..ba681fc0 100644
--- a/net/quic/quic_config.h
+++ b/net/quic/quic_config.h
@@ -310,6 +310,8 @@
   // Sets an initial flow control window size to transmit to the peer.
   void SetInitialFlowControlWindowToSend(uint32 window_bytes);
 
+  uint32 GetInitialFlowControlWindowToSend() const;
+
   bool HasReceivedInitialFlowControlWindowBytes() const;
 
   uint32 ReceivedInitialFlowControlWindowBytes() const;
diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc
index 63299e5b0..f77c382 100644
--- a/net/quic/quic_config_test.cc
+++ b/net/quic/quic_config_test.cc
@@ -12,6 +12,7 @@
 #include "net/quic/quic_time.h"
 #include "net/quic/quic_utils.h"
 #include "net/quic/test_tools/quic_test_utils.h"
+#include "net/test/gtest_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using std::string;
@@ -32,6 +33,7 @@
 TEST_F(QuicConfigTest, ToHandshakeMessage) {
   ValueRestore<bool> old_flag(&FLAGS_enable_quic_pacing, false);
   config_.SetDefaults();
+  config_.SetInitialFlowControlWindowToSend(kInitialFlowControlWindowForTest);
   config_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5),
                                              QuicTime::Delta::FromSeconds(2));
   config_.set_max_streams_per_connection(4, 2);
@@ -47,6 +49,10 @@
   EXPECT_EQ(QUIC_NO_ERROR, error);
   EXPECT_EQ(4u, value);
 
+  error = msg.GetUint32(kIFCW, &value);
+  EXPECT_EQ(QUIC_NO_ERROR, error);
+  EXPECT_EQ(kInitialFlowControlWindowForTest, value);
+
   const QuicTag* out;
   size_t out_len;
   error = msg.GetTaglist(kCGST, &out, &out_len);
@@ -82,6 +88,8 @@
       2 * kDefaultMaxStreamsPerConnection, kDefaultMaxStreamsPerConnection);
   client_config.SetInitialRoundTripTimeUsToSend(
       10 * base::Time::kMicrosecondsPerMillisecond);
+  client_config.SetInitialFlowControlWindowToSend(
+      2 * kInitialFlowControlWindowForTest);
   QuicTagVector copt;
   copt.push_back(kTBBR);
   client_config.SetCongestionOptionsToSend(copt);
@@ -104,6 +112,8 @@
   EXPECT_TRUE(config_.HasReceivedCongestionOptions());
   EXPECT_EQ(1u, config_.ReceivedCongestionOptions().size());
   EXPECT_EQ(config_.ReceivedCongestionOptions()[0], kTBBR);
+  EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
+            2 * kInitialFlowControlWindowForTest);
 }
 
 TEST_F(QuicConfigTest, ProcessServerHello) {
@@ -120,6 +130,8 @@
   server_config.SetInitialCongestionWindowToSend(kDefaultInitialWindow / 2);
   server_config.SetInitialRoundTripTimeUsToSend(
       10 * base::Time::kMicrosecondsPerMillisecond);
+  server_config.SetInitialFlowControlWindowToSend(
+      2 * kInitialFlowControlWindowForTest);
   CryptoHandshakeMessage msg;
   server_config.ToHandshakeMessage(&msg);
   string error_details;
@@ -138,6 +150,8 @@
   EXPECT_EQ(10 * base::Time::kMicrosecondsPerMillisecond,
             config_.ReceivedInitialRoundTripTimeUs());
   EXPECT_FALSE(config_.HasReceivedLossDetection());
+  EXPECT_EQ(config_.ReceivedInitialFlowControlWindowBytes(),
+            2 * kInitialFlowControlWindowForTest);
 }
 
 TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) {
@@ -243,6 +257,18 @@
   EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NO_OVERLAP, error);
 }
 
+TEST_F(QuicConfigTest, InvalidFlowControlWindow) {
+  // QuicConfig should not accept an invalid flow control window to send to the
+  // peer: the receive window must be at least the default of 16 Kb.
+  QuicConfig config;
+  const uint64 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
+  EXPECT_DFATAL(config.SetInitialFlowControlWindowToSend(kInvalidWindow),
+                "Initial flow control receive window");
+
+  EXPECT_EQ(kDefaultFlowControlSendWindow,
+            config.GetInitialFlowControlWindowToSend());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc
index 9cb1f7e..3993637 100644
--- a/net/quic/quic_crypto_client_stream.cc
+++ b/net/quic/quic_crypto_client_stream.cc
@@ -185,7 +185,6 @@
             server_id_,
             session()->connection()->connection_id(),
             session()->connection()->supported_versions().front(),
-            session()->max_flow_control_receive_window_bytes(),
             cached,
             session()->connection()->clock()->WallNow(),
             session()->connection()->random_generator(),
diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc
index 62b77953..7bd2c03 100644
--- a/net/quic/quic_crypto_server_stream.cc
+++ b/net/quic/quic_crypto_server_stream.cc
@@ -173,7 +173,6 @@
       session()->connection()->peer_address(),
       session()->connection()->version(),
       session()->connection()->supported_versions(),
-      session()->max_flow_control_receive_window_bytes(),
       session()->connection()->clock(),
       session()->connection()->random_generator(),
       &crypto_negotiated_params_, reply, error_details);
diff --git a/net/quic/quic_dispatcher.cc b/net/quic/quic_dispatcher.cc
index d1fe193..2d35df3 100644
--- a/net/quic/quic_dispatcher.cc
+++ b/net/quic/quic_dispatcher.cc
@@ -157,8 +157,7 @@
 QuicDispatcher::QuicDispatcher(const QuicConfig& config,
                                const QuicCryptoServerConfig& crypto_config,
                                const QuicVersionVector& supported_versions,
-                               QuicConnectionHelperInterface* helper,
-                               uint32 initial_flow_control_window_bytes)
+                               QuicConnectionHelperInterface* helper)
     : config_(config),
       crypto_config_(crypto_config),
       helper_(helper),
@@ -169,8 +168,7 @@
       supported_versions_no_connection_flow_control_(supported_versions),
       current_packet_(NULL),
       framer_(supported_versions, /*unused*/ QuicTime::Zero(), true),
-      framer_visitor_(new QuicFramerVisitor(this)),
-      initial_flow_control_window_bytes_(initial_flow_control_window_bytes) {
+      framer_visitor_(new QuicFramerVisitor(this)) {
   framer_.set_visitor(framer_visitor_.get());
 }
 
@@ -371,7 +369,6 @@
   QuicServerSession* session = new QuicServerSession(
       config_,
       CreateQuicConnection(connection_id, server_address, client_address),
-      initial_flow_control_window_bytes_,
       this);
   session->InitializeSession(crypto_config_);
   return session;
diff --git a/net/quic/quic_dispatcher.h b/net/quic/quic_dispatcher.h
index 423afe6..fa20bcc2d 100644
--- a/net/quic/quic_dispatcher.h
+++ b/net/quic/quic_dispatcher.h
@@ -57,8 +57,7 @@
   QuicDispatcher(const QuicConfig& config,
                  const QuicCryptoServerConfig& crypto_config,
                  const QuicVersionVector& supported_versions,
-                 QuicConnectionHelperInterface* helper,
-                 uint32 initial_flow_control_window_bytes);
+                 QuicConnectionHelperInterface* helper);
 
   virtual ~QuicDispatcher();
 
@@ -157,10 +156,6 @@
 
   QuicPacketWriter* writer() { return writer_.get(); }
 
-  uint32 initial_flow_control_window_bytes() const {
-    return initial_flow_control_window_bytes_;
-  }
-
  private:
   class QuicFramerVisitor;
   friend class net::test::QuicDispatcherPeer;
@@ -227,10 +222,6 @@
   QuicFramer framer_;
   scoped_ptr<QuicFramerVisitor> framer_visitor_;
 
-  // Initial flow control window size to advertize to peer on newly created
-  // connections.
-  const uint32 initial_flow_control_window_bytes_;
-
   DISALLOW_COPY_AND_ASSIGN(QuicDispatcher);
 };
 
diff --git a/net/quic/quic_end_to_end_unittest.cc b/net/quic/quic_end_to_end_unittest.cc
index 4777d8ae..e7e2769 100644
--- a/net/quic/quic_end_to_end_unittest.cc
+++ b/net/quic/quic_end_to_end_unittest.cc
@@ -23,6 +23,7 @@
 #include "net/quic/test_tools/quic_test_utils.h"
 #include "net/ssl/ssl_config_service_defaults.h"
 #include "net/tools/quic/quic_in_memory_cache.h"
+#include "net/tools/quic/quic_server.h"
 #include "net/tools/quic/test_tools/quic_in_memory_cache_peer.h"
 #include "net/tools/quic/test_tools/server_thread.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -30,6 +31,7 @@
 
 using base::StringPiece;
 using net::tools::QuicInMemoryCache;
+using net::tools::QuicServer;
 using net::tools::test::QuicInMemoryCachePeer;
 using net::tools::test::ServerThread;
 
@@ -133,10 +135,12 @@
     CHECK(net::ParseIPLiteralToNumber("127.0.0.1", &ip));
     server_address_ = IPEndPoint(ip, 0);
     server_config_.SetDefaults();
-    server_thread_.reset(new ServerThread(server_address_, server_config_,
-                                          QuicSupportedVersions(),
-                                          strike_register_no_startup_period_,
-                                          kInitialFlowControlWindowForTest));
+    server_config_.SetInitialFlowControlWindowToSend(
+        kInitialFlowControlWindowForTest);
+    server_thread_.reset(new ServerThread(
+         new QuicServer(server_config_, QuicSupportedVersions()),
+         server_address_,
+         strike_register_no_startup_period_));
     server_thread_->Initialize();
     server_address_ = IPEndPoint(server_address_.address(),
                                  server_thread_->GetPort());
diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc
index e44bd2d..fd077bc2 100644
--- a/net/quic/quic_framer.cc
+++ b/net/quic/quic_framer.cc
@@ -323,9 +323,9 @@
   return frame_len;
 }
 
-QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) { }
+QuicFramer::AckFrameInfo::AckFrameInfo() : max_delta(0) {}
 
-QuicFramer::AckFrameInfo::~AckFrameInfo() { }
+QuicFramer::AckFrameInfo::~AckFrameInfo() {}
 
 QuicPacketEntropyHash QuicFramer::GetPacketEntropyHash(
     const QuicPacketHeader& header) const {
diff --git a/net/quic/quic_headers_stream.cc b/net/quic/quic_headers_stream.cc
index aab85715..c158e0850 100644
--- a/net/quic/quic_headers_stream.cc
+++ b/net/quic/quic_headers_stream.cc
@@ -174,6 +174,7 @@
       spdy_framer_visitor_(new SpdyFramerVisitor(this)) {
   spdy_framer_.set_visitor(spdy_framer_visitor_.get());
   spdy_framer_.set_debug_visitor(spdy_framer_visitor_.get());
+  // TODO(jri): Set headers to be always FEC protected.
   DisableFlowControl();
 }
 
diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc
index 66261d4..8fafdd51 100644
--- a/net/quic/quic_http_stream_test.cc
+++ b/net/quic/quic_http_stream_test.cc
@@ -212,8 +212,7 @@
                               make_scoped_ptr((QuicServerInfo*)NULL),
                               QuicServerId(kServerHostname, kServerPort,
                                            false, PRIVACY_MODE_DISABLED),
-                              DefaultQuicConfig(),
-                              kInitialFlowControlWindowForTest, &crypto_config_,
+                              DefaultQuicConfig(), &crypto_config_,
                               base::MessageLoop::current()->
                                   message_loop_proxy().get(),
                               NULL));
diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc
index cd73694..9ad7798 100644
--- a/net/quic/quic_protocol.cc
+++ b/net/quic/quic_protocol.cc
@@ -753,7 +753,7 @@
       nack_count(0),
       transmission_type(NOT_RETRANSMISSION),
       all_transmissions(NULL),
-      in_flight(false) { }
+      in_flight(false) {}
 
 TransmissionInfo::TransmissionInfo(
     RetransmittableFrames* retransmittable_frames,
diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h
index 23bdd3e1..eed756b 100644
--- a/net/quic/quic_protocol.h
+++ b/net/quic/quic_protocol.h
@@ -158,7 +158,13 @@
 // Indicates FEC protection level for data being written.
 enum FecProtection {
   MUST_FEC_PROTECT,  // Callee must FEC protect this data.
-  MAY_FEC_PROTECT  // Callee does not have to but may FEC protect this data.
+  MAY_FEC_PROTECT    // Callee does not have to but may FEC protect this data.
+};
+
+// Indicates FEC policy
+enum FecPolicy {
+  FEC_PROTECT_ALWAYS,   // All data in the stream should be FEC protected.
+  FEC_PROTECT_OPTIONAL  // Data in the stream does not need FEC protection.
 };
 
 enum QuicFrameType {
diff --git a/net/quic/quic_received_packet_manager_test.cc b/net/quic/quic_received_packet_manager_test.cc
index 30c84c5f..b579309 100644
--- a/net/quic/quic_received_packet_manager_test.cc
+++ b/net/quic/quic_received_packet_manager_test.cc
@@ -186,7 +186,7 @@
 
 class QuicReceivedPacketManagerTest : public ::testing::Test {
  protected:
-  QuicReceivedPacketManagerTest() : received_manager_(kTCP, &stats_) { }
+  QuicReceivedPacketManagerTest() : received_manager_(kTCP, &stats_) {}
 
   void RecordPacketReceipt(QuicPacketSequenceNumber sequence_number,
                            QuicPacketEntropyHash entropy_hash) {
diff --git a/net/quic/quic_server_session.cc b/net/quic/quic_server_session.cc
index 3944acd..1de91a4 100644
--- a/net/quic/quic_server_session.cc
+++ b/net/quic/quic_server_session.cc
@@ -13,9 +13,8 @@
 
 QuicServerSession::QuicServerSession(const QuicConfig& config,
                                      QuicConnection* connection,
-                                     uint32 max_flow_control_window_bytes,
                                      QuicServerSessionVisitor* visitor)
-    : QuicSession(connection, max_flow_control_window_bytes, config),
+    : QuicSession(connection, config),
       visitor_(visitor) {}
 
 QuicServerSession::~QuicServerSession() {}
diff --git a/net/quic/quic_server_session.h b/net/quic/quic_server_session.h
index b413098..30ac08c8 100644
--- a/net/quic/quic_server_session.h
+++ b/net/quic/quic_server_session.h
@@ -45,7 +45,6 @@
  public:
   QuicServerSession(const QuicConfig& config,
                     QuicConnection* connection,
-                    uint32 max_flow_control_window_bytes,
                     QuicServerSessionVisitor* visitor);
 
   // Override the base class to notify the owner of the connection close.
diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc
index 8a2f8b4..f16434d 100644
--- a/net/quic/quic_session.cc
+++ b/net/quic/quic_session.cc
@@ -95,9 +95,7 @@
   QuicSession* session_;
 };
 
-QuicSession::QuicSession(QuicConnection* connection,
-                         uint32 max_flow_control_receive_window_bytes,
-                         const QuicConfig& config)
+QuicSession::QuicSession(QuicConnection* connection, const QuicConfig& config)
     : connection_(connection),
       visitor_shim_(new VisitorShim(this)),
       config_(config),
@@ -107,20 +105,11 @@
       error_(QUIC_NO_ERROR),
       goaway_received_(false),
       goaway_sent_(false),
-      has_pending_handshake_(false),
-      max_flow_control_receive_window_bytes_(
-          max_flow_control_receive_window_bytes) {
-  if (max_flow_control_receive_window_bytes_ < kDefaultFlowControlSendWindow) {
-    LOG(ERROR) << "Initial receive window ("
-               << max_flow_control_receive_window_bytes_
-               << ") cannot be set lower than default ("
-               << kDefaultFlowControlSendWindow << ").";
-    max_flow_control_receive_window_bytes_ = kDefaultFlowControlSendWindow;
-  }
+      has_pending_handshake_(false) {
   flow_controller_.reset(new QuicFlowController(
       connection_.get(), 0, is_server(), kDefaultFlowControlSendWindow,
-      max_flow_control_receive_window_bytes_,
-      max_flow_control_receive_window_bytes_));
+      config_.GetInitialFlowControlWindowToSend(),
+      config_.GetInitialFlowControlWindowToSend()));
 
   connection_->set_visitor(visitor_shim_.get());
   connection_->SetFromConfig(config_);
@@ -477,28 +466,43 @@
       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_flow_control_send_window =
-        config_.ReceivedInitialFlowControlWindowBytes();
-    if (new_flow_control_send_window < kDefaultFlowControlSendWindow) {
-      LOG(ERROR)
-          << "Peer sent us an invalid flow control send window: "
-          << new_flow_control_send_window
-          << ", below default: " << kDefaultFlowControlSendWindow;
-      connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW);
-      return;
-    }
-    DataStreamMap::iterator it = stream_map_.begin();
-    while (it != stream_map_.end()) {
-      it->second->flow_controller()->UpdateSendWindowOffset(
-          new_flow_control_send_window);
-      it++;
-    }
-
-    // Update connection level window.
-    flow_controller_->UpdateSendWindowOffset(new_flow_control_send_window);
+    uint32 new_window = config_.ReceivedInitialFlowControlWindowBytes();
+    OnNewStreamFlowControlWindow(new_window);
+    OnNewSessionFlowControlWindow(new_window);
   }
 }
 
+void QuicSession::OnNewStreamFlowControlWindow(uint32 new_window) {
+  if (new_window < kDefaultFlowControlSendWindow) {
+    LOG(ERROR)
+        << "Peer sent us an invalid stream flow control send window: "
+        << new_window << ", below default: " << kDefaultFlowControlSendWindow;
+    if (connection_->connected()) {
+      connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+    }
+    return;
+  }
+
+  for (DataStreamMap::iterator it = stream_map_.begin();
+       it != stream_map_.end(); ++it) {
+    it->second->flow_controller()->UpdateSendWindowOffset(new_window);
+  }
+}
+
+void QuicSession::OnNewSessionFlowControlWindow(uint32 new_window) {
+  if (new_window < kDefaultFlowControlSendWindow) {
+    LOG(ERROR)
+        << "Peer sent us an invalid session flow control send window: "
+        << new_window << ", below default: " << kDefaultFlowControlSendWindow;
+    if (connection_->connected()) {
+      connection_->SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW);
+    }
+    return;
+  }
+
+  flow_controller_->UpdateSendWindowOffset(new_window);
+}
+
 void QuicSession::OnCryptoHandshakeEvent(CryptoHandshakeEvent event) {
   switch (event) {
     // TODO(satyamshekhar): Move the logic of setting the encrypter/decrypter
diff --git a/net/quic/quic_session.h b/net/quic/quic_session.h
index 4cc3215..1b312800 100644
--- a/net/quic/quic_session.h
+++ b/net/quic/quic_session.h
@@ -53,9 +53,7 @@
     HANDSHAKE_CONFIRMED,
   };
 
-  QuicSession(QuicConnection* connection,
-              uint32 max_flow_control_receive_window_bytes,
-              const QuicConfig& config);
+  QuicSession(QuicConnection* connection, const QuicConfig& config);
 
   virtual ~QuicSession();
 
@@ -205,10 +203,6 @@
 
   bool is_server() const { return connection_->is_server(); }
 
-  uint32 max_flow_control_receive_window_bytes() {
-    return max_flow_control_receive_window_bytes_;
-  }
-
   QuicFlowController* flow_controller() { return flow_controller_.get(); }
 
  protected:
@@ -273,6 +267,14 @@
   void UpdateFlowControlOnFinalReceivedByteOffset(
       QuicStreamId id, QuicStreamOffset final_byte_offset);
 
+  // Called in OnConfigNegotiated when we receive a new stream level flow
+  // control window in a negotiated config. Closes the connection if invalid.
+  void OnNewStreamFlowControlWindow(uint32 new_window);
+
+  // Called in OnConfigNegotiated when we receive a new session level flow
+  // control window in a negotiated config. Closes the connection if invalid.
+  void OnNewSessionFlowControlWindow(uint32 new_window);
+
   // Keep track of highest received byte offset of locally closed streams, while
   // waiting for a definitive final highest offset from the peer.
   std::map<QuicStreamId, QuicStreamOffset>
@@ -320,9 +322,6 @@
   // Used for session level flow control.
   scoped_ptr<QuicFlowController> flow_controller_;
 
-  // Initial flow control receive window size for new streams.
-  uint32 max_flow_control_receive_window_bytes_;
-
   DISALLOW_COPY_AND_ASSIGN(QuicSession);
 };
 
diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc
index d5468e7f..b3250a7 100644
--- a/net/quic/quic_session_test.cc
+++ b/net/quic/quic_session_test.cc
@@ -15,6 +15,7 @@
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_utils.h"
 #include "net/quic/reliable_quic_stream.h"
+#include "net/quic/test_tools/quic_config_peer.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
 #include "net/quic/test_tools/quic_data_stream_peer.h"
 #include "net/quic/test_tools/quic_flow_controller_peer.h"
@@ -116,9 +117,8 @@
 
 class TestSession : public QuicSession {
  public:
-  TestSession(QuicConnection* connection,
-              uint32 max_initial_flow_control_window)
-      : QuicSession(connection, max_initial_flow_control_window,
+  explicit TestSession(QuicConnection* connection)
+      : QuicSession(connection,
                     DefaultQuicConfig()),
         crypto_stream_(this),
         writev_consumes_all_data_(false) {}
@@ -181,7 +181,9 @@
  protected:
   QuicSessionTest()
       : connection_(new MockConnection(true, SupportedVersions(GetParam()))),
-        session_(connection_, kInitialFlowControlWindowForTest) {
+        session_(connection_) {
+    session_.config()->SetInitialFlowControlWindowToSend(
+        kInitialFlowControlWindowForTest);
     headers_[":host"] = "www.google.com";
     headers_[":path"] = "/index.hml";
     headers_[":scheme"] = "http";
@@ -515,11 +517,11 @@
 }
 
 TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) {
-  ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control, true);
   if (version() < QUIC_VERSION_19) {
     return;
   }
 
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control, true);
   // Ensure connection level flow control blockage.
   QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0);
   EXPECT_TRUE(session_.flow_controller()->IsBlocked());
@@ -654,41 +656,21 @@
   ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
 
   uint32 kInvalidWindow = kDefaultFlowControlSendWindow - 1;
-
-  CryptoHandshakeMessage msg;
-  string error_details;
-  session_.config()->SetInitialFlowControlWindowToSend(kInvalidWindow);
-  session_.config()->ToHandshakeMessage(&msg);
-  const QuicErrorCode error =
-      session_.config()->ProcessPeerHello(msg, CLIENT, &error_details);
-  EXPECT_EQ(QUIC_NO_ERROR, error);
+  QuicConfigPeer::SetReceivedInitialFlowControlWindow(session_.config(),
+                                                      kInvalidWindow);
 
   EXPECT_CALL(*connection_,
-              SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW));
+              SendConnectionClose(QUIC_FLOW_CONTROL_INVALID_WINDOW)).Times(2);
   session_.OnConfigNegotiated();
 }
 
-
-TEST_P(QuicSessionTest, InvalidFlowControlWindow) {
-  // Test that an attempt to create a QuicSession with an invalid (< default)
-  // flow control window results in a QuicSession using the default.
-  QuicConnection* connection =
-      new MockConnection(true, SupportedVersions(GetParam()));
-
-  const uint32 kSmallerFlowControlWindow = kDefaultFlowControlSendWindow - 1;
-  TestSession session(connection, kSmallerFlowControlWindow);
-
-  EXPECT_EQ(kDefaultFlowControlSendWindow,
-            session.max_flow_control_receive_window_bytes());
-}
-
 TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) {
-  FLAGS_enable_quic_stream_flow_control_2 = true;
-  FLAGS_enable_quic_connection_flow_control = true;
   if (version() < QUIC_VERSION_19) {
     return;
   }
 
+  ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control, true);
   // Test that when we receive an out of order stream RST we correctly adjust
   // our connection level flow control receive window.
   // On close, the stream should mark as consumed all bytes between the highest
@@ -711,12 +693,12 @@
 }
 
 TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) {
-  FLAGS_enable_quic_stream_flow_control_2 = true;
-  FLAGS_enable_quic_connection_flow_control = true;
   if (version() < QUIC_VERSION_19) {
     return;
   }
 
+  ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control, true);
   // Test the situation where we receive a FIN on a stream, and before we fully
   // consume all the data from the sequencer buffer we locally RST the stream.
   // The bytes between highest consumed byte, and the final byte offset that we
@@ -752,11 +734,12 @@
   // Test that when we RST the stream (and tear down stream state), and then
   // receive a FIN from the peer, we correctly adjust our connection level flow
   // control receive window.
-  FLAGS_enable_quic_connection_flow_control = true;
   if (version() < QUIC_VERSION_19) {
     return;
   }
 
+  ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control, true);
   // Connection starts with some non-zero highest received byte offset,
   // due to other active streams.
   const uint64 kInitialConnectionBytesConsumed = 567;
@@ -795,11 +778,12 @@
   // Test that when we RST the stream (and tear down stream state), and then
   // receive a RST from the peer, we correctly adjust our connection level flow
   // control receive window.
-  FLAGS_enable_quic_connection_flow_control = true;
   if (version() < QUIC_VERSION_19) {
     return;
   }
 
+  ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control, true);
   // Connection starts with some non-zero highest received byte offset,
   // due to other active streams.
   const uint64 kInitialConnectionBytesConsumed = 567;
@@ -834,8 +818,8 @@
   if (version() < QUIC_VERSION_17) {
     return;
   }
-  FLAGS_enable_quic_stream_flow_control_2 = true;
-  FLAGS_enable_quic_connection_flow_control = true;
+  ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control, true);
 
   const uint64 kLargeOffset = kInitialFlowControlWindowForTest + 1;
   EXPECT_CALL(*connection_,
@@ -857,14 +841,12 @@
 }
 
 TEST_P(QuicSessionTest, VersionNegotiationDisablesFlowControl) {
-  ValueRestore<bool> old_stream_flag(
-      &FLAGS_enable_quic_stream_flow_control_2, true);
-  ValueRestore<bool> old_connection_flag(
-      &FLAGS_enable_quic_connection_flow_control, true);
   if (version() < QUIC_VERSION_19) {
     return;
   }
 
+  ValueRestore<bool> old_flag2(&FLAGS_enable_quic_stream_flow_control_2, true);
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_connection_flow_control, true);
   // Test that after successful version negotiation, flow control is disabled
   // appropriately at both the connection and stream level.
 
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 9acedcb..dd17924 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -834,6 +834,7 @@
   config.SetInitialCongestionWindowToSend(
       server_id.is_https() ? kServerSecureInitialCongestionWindow
                            : kServerInecureInitialCongestionWindow);
+  config.SetInitialFlowControlWindowToSend(kInitialReceiveWindowSize);
   if (http_server_properties_) {
     const HttpServerProperties::NetworkStats* stats =
         http_server_properties_->GetServerNetworkStats(
@@ -846,7 +847,7 @@
   *session = new QuicClientSession(
       connection, socket.Pass(), writer.Pass(), this,
       quic_crypto_client_stream_factory_, server_info.Pass(), server_id,
-      config, kInitialReceiveWindowSize, &crypto_config_,
+      config, &crypto_config_,
       base::MessageLoop::current()->message_loop_proxy().get(),
       net_log.net_log());
   all_sessions_[*session] = server_id;  // owning pointer
diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc
index 32ef6d3..a98e22d 100644
--- a/net/quic/reliable_quic_stream.cc
+++ b/net/quic/reliable_quic_stream.cc
@@ -124,6 +124,7 @@
       fin_received_(false),
       rst_sent_(false),
       rst_received_(false),
+      fec_policy_(FEC_PROTECT_OPTIONAL),
       is_server_(session_->is_server()),
       flow_controller_(
           session_->connection(),
@@ -132,8 +133,8 @@
           session_->config()->HasReceivedInitialFlowControlWindowBytes() ?
               session_->config()->ReceivedInitialFlowControlWindowBytes() :
               kDefaultFlowControlSendWindow,
-          session_->max_flow_control_receive_window_bytes(),
-          session_->max_flow_control_receive_window_bytes()),
+          session_->config()->GetInitialFlowControlWindowToSend(),
+          session_->config()->GetInitialFlowControlWindowToSend()),
       connection_flow_controller_(session_->flow_controller()) {
 }
 
@@ -360,9 +361,8 @@
   IOVector data;
   data.AppendIovecAtMostBytes(iov, iov_count, write_length);
 
-  // TODO(jri): Use the correct FecProtection based on FecPolicy on stream.
   QuicConsumedData consumed_data = session()->WritevData(
-      id(), data, stream_bytes_written_, fin, MAY_FEC_PROTECT,
+      id(), data, stream_bytes_written_, fin, GetFecProtection(),
       ack_notifier_delegate);
   stream_bytes_written_ += consumed_data.bytes_consumed;
 
@@ -384,6 +384,10 @@
   return consumed_data;
 }
 
+FecProtection ReliableQuicStream::GetFecProtection() {
+  return fec_policy_ == FEC_PROTECT_ALWAYS ? MUST_FEC_PROTECT : MAY_FEC_PROTECT;
+}
+
 void ReliableQuicStream::CloseReadSide() {
   if (read_side_closed_) {
     return;
diff --git a/net/quic/reliable_quic_stream.h b/net/quic/reliable_quic_stream.h
index 0774c93b..be89a284 100644
--- a/net/quic/reliable_quic_stream.h
+++ b/net/quic/reliable_quic_stream.h
@@ -140,6 +140,9 @@
       bool fin,
       QuicAckNotifier::DelegateInterface* ack_notifier_delegate);
 
+  // Helper method that returns FecProtection to use for writes to the session.
+  FecProtection GetFecProtection();
+
   // Close the read side of the socket.  Further frames will not be accepted.
   virtual void CloseReadSide();
 
@@ -150,6 +153,8 @@
 
   bool fin_buffered() const { return fin_buffered_; }
 
+  void set_fec_policy(FecPolicy fec_policy) { fec_policy_ = fec_policy; }
+
   const QuicSession* session() const { return session_; }
   QuicSession* session() { return session_; }
 
@@ -218,6 +223,9 @@
   // True if this stream has received a RST stream frame.
   bool rst_received_;
 
+  // FEC policy to be used for this stream.
+  FecPolicy fec_policy_;
+
   // True if the session this stream is running under is a server session.
   bool is_server_;
 
diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc
index df8e6301..13f9c7d 100644
--- a/net/quic/reliable_quic_stream_test.cc
+++ b/net/quic/reliable_quic_stream_test.cc
@@ -239,6 +239,78 @@
   stream_->OnCanWrite();
 }
 
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectAlways) {
+  Initialize(kShouldProcessData);
+
+  // Set FEC policy on stream.
+  ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_ALWAYS);
+
+  EXPECT_FALSE(HasWriteBlockedStreams());
+  size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+      connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+      PACKET_6BYTE_SEQUENCE_NUMBER, 0u, IN_FEC_GROUP);
+  QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+      length);
+
+  // Write first data onto stream, which will cause one session write.
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).WillOnce(
+      Return(QuicConsumedData(kDataLen - 1, false)));
+  stream_->WriteOrBufferData(kData1, false, NULL);
+  EXPECT_TRUE(HasWriteBlockedStreams());
+
+  // Queue a bytes_consumed write.
+  stream_->WriteOrBufferData(kData2, false, NULL);
+
+  // Make sure we get the tail of the first write followed by the bytes_consumed
+  InSequence s;
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+      WillOnce(Return(QuicConsumedData(1, false)));
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+      WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
+  stream_->OnCanWrite();
+
+  // And finally the end of the bytes_consumed.
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, MUST_FEC_PROTECT, _)).
+      WillOnce(Return(QuicConsumedData(2, true)));
+  stream_->OnCanWrite();
+}
+
+TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithFecProtectOptional) {
+  Initialize(kShouldProcessData);
+
+  // Set FEC policy on stream.
+  ReliableQuicStreamPeer::SetFecPolicy(stream_.get(), FEC_PROTECT_OPTIONAL);
+
+  EXPECT_FALSE(HasWriteBlockedStreams());
+  size_t length = 1 + QuicPacketCreator::StreamFramePacketOverhead(
+      connection_->version(), PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion,
+      PACKET_6BYTE_SEQUENCE_NUMBER, 0u, NOT_IN_FEC_GROUP);
+  QuicConnectionPeer::GetPacketCreator(connection_)->set_max_packet_length(
+      length);
+
+  // Write first data onto stream, which will cause one session write.
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).WillOnce(
+      Return(QuicConsumedData(kDataLen - 1, false)));
+  stream_->WriteOrBufferData(kData1, false, NULL);
+  EXPECT_TRUE(HasWriteBlockedStreams());
+
+  // Queue a bytes_consumed write.
+  stream_->WriteOrBufferData(kData2, false, NULL);
+
+  // Make sure we get the tail of the first write followed by the bytes_consumed
+  InSequence s;
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+      WillOnce(Return(QuicConsumedData(1, false)));
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+      WillOnce(Return(QuicConsumedData(kDataLen - 2, false)));
+  stream_->OnCanWrite();
+
+  // And finally the end of the bytes_consumed.
+  EXPECT_CALL(*session_, WritevData(_, _, _, _, MAY_FEC_PROTECT, _)).
+      WillOnce(Return(QuicConsumedData(2, true)));
+  stream_->OnCanWrite();
+}
+
 TEST_F(ReliableQuicStreamTest, ConnectionCloseAfterStreamClose) {
   Initialize(kShouldProcessData);
 
diff --git a/net/quic/test_tools/crypto_test_utils_chromium.cc b/net/quic/test_tools/crypto_test_utils_chromium.cc
index eb4fec03..9deee83 100644
--- a/net/quic/test_tools/crypto_test_utils_chromium.cc
+++ b/net/quic/test_tools/crypto_test_utils_chromium.cc
@@ -29,7 +29,7 @@
         ImportCertFromFile(GetTestCertsDirectory(), cert_file);
     scoped_root_.Reset(root_cert.get());
   }
-  virtual ~TestProofVerifierChromium() { }
+  virtual ~TestProofVerifierChromium() {}
 
  private:
   ScopedTestRoot scoped_root_;
diff --git a/net/quic/test_tools/mock_quic_dispatcher.cc b/net/quic/test_tools/mock_quic_dispatcher.cc
index 73a14e9..82c9dbf 100644
--- a/net/quic/test_tools/mock_quic_dispatcher.cc
+++ b/net/quic/test_tools/mock_quic_dispatcher.cc
@@ -15,11 +15,7 @@
     const QuicConfig& config,
     const QuicCryptoServerConfig& crypto_config,
     QuicConnectionHelperInterface* helper)
-    : QuicDispatcher(config,
-                     crypto_config,
-                     QuicSupportedVersions(),
-                     helper,
-                     kInitialFlowControlWindowForTest) {
+    : QuicDispatcher(config, crypto_config, QuicSupportedVersions(), helper) {
 }
 
 MockQuicDispatcher::~MockQuicDispatcher() {
diff --git a/net/quic/test_tools/quic_test_utils.cc b/net/quic/test_tools/quic_test_utils.cc
index 727c544..e61cc30a 100644
--- a/net/quic/test_tools/quic_test_utils.cc
+++ b/net/quic/test_tools/quic_test_utils.cc
@@ -4,6 +4,7 @@
 
 #include "net/quic/test_tools/quic_test_utils.h"
 
+#include "base/sha1.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "net/quic/crypto/crypto_framer.h"
@@ -82,6 +83,14 @@
   return framer->BuildDataPacket(header, frames, packet_size);
 }
 
+uint64 SimpleRandom::RandUint64() {
+  unsigned char hash[base::kSHA1Length];
+  base::SHA1HashBytes(reinterpret_cast<unsigned char*>(&seed_), sizeof(seed_),
+                      hash);
+  memcpy(&seed_, hash, sizeof(seed_));
+  return seed_;
+}
+
 MockFramerVisitor::MockFramerVisitor() {
   // By default, we want to accept packets.
   ON_CALL(*this, OnProtocolVersionMismatch(_))
@@ -292,8 +301,7 @@
 }
 
 MockSession::MockSession(QuicConnection* connection)
-    : QuicSession(connection, kInitialFlowControlWindowForTest,
-                  DefaultQuicConfig()) {
+    : QuicSession(connection, DefaultQuicConfig()) {
   ON_CALL(*this, WritevData(_, _, _, _, _, _))
       .WillByDefault(testing::Return(QuicConsumedData(0, false)));
 }
@@ -302,7 +310,7 @@
 }
 
 TestSession::TestSession(QuicConnection* connection, const QuicConfig& config)
-    : QuicSession(connection, kInitialFlowControlWindowForTest, config),
+    : QuicSession(connection, config),
       crypto_stream_(NULL) {}
 
 TestSession::~TestSession() {}
@@ -317,7 +325,7 @@
 
 TestClientSession::TestClientSession(QuicConnection* connection,
                                      const QuicConfig& config)
-    : QuicClientSessionBase(connection, kInitialFlowControlWindowForTest,
+    : QuicClientSessionBase(connection,
                             config),
       crypto_stream_(NULL) {
     EXPECT_CALL(*this, OnProofValid(_)).Times(AnyNumber());
@@ -562,22 +570,23 @@
           sequence_number_length, 0u, is_in_fec_group);
 }
 
-TestEntropyCalculator::TestEntropyCalculator() { }
+TestEntropyCalculator::TestEntropyCalculator() {}
 
-TestEntropyCalculator::~TestEntropyCalculator() { }
+TestEntropyCalculator::~TestEntropyCalculator() {}
 
 QuicPacketEntropyHash TestEntropyCalculator::EntropyHash(
     QuicPacketSequenceNumber sequence_number) const {
   return 1u;
 }
 
-MockEntropyCalculator::MockEntropyCalculator() { }
+MockEntropyCalculator::MockEntropyCalculator() {}
 
-MockEntropyCalculator::~MockEntropyCalculator() { }
+MockEntropyCalculator::~MockEntropyCalculator() {}
 
 QuicConfig DefaultQuicConfig() {
   QuicConfig config;
   config.SetDefaults();
+  config.SetInitialFlowControlWindowToSend(kInitialFlowControlWindowForTest);
   return config;
 }
 
diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h
index 4c6fcf1..6d1de91 100644
--- a/net/quic/test_tools/quic_test_utils.h
+++ b/net/quic/test_tools/quic_test_utils.h
@@ -122,6 +122,25 @@
   DISALLOW_COPY_AND_ASSIGN(ValueRestore);
 };
 
+// Simple random number generator used to compute random numbers suitable
+// for pseudo-randomly dropping packets in tests.  It works by computing
+// the sha1 hash of the current seed, and using the first 64 bits as
+// the next random number, and the next seed.
+class SimpleRandom {
+ public:
+  SimpleRandom() : seed_(0) {}
+
+  // Returns a random number in the range [0, kuint64max].
+  uint64 RandUint64();
+
+  void set_seed(uint64 seed) { seed_ = seed; }
+
+ private:
+  uint64 seed_;
+
+  DISALLOW_COPY_AND_ASSIGN(SimpleRandom);
+};
+
 class MockFramerVisitor : public QuicFramerVisitorInterface {
  public:
   MockFramerVisitor();
diff --git a/net/quic/test_tools/reliable_quic_stream_peer.cc b/net/quic/test_tools/reliable_quic_stream_peer.cc
index 6d1f1129..914609e 100644
--- a/net/quic/test_tools/reliable_quic_stream_peer.cc
+++ b/net/quic/test_tools/reliable_quic_stream_peer.cc
@@ -39,8 +39,6 @@
   return stream->rst_sent_;
 }
 
-
-
 // static
 uint32 ReliableQuicStreamPeer::SizeOfQueuedData(ReliableQuicStream* stream) {
   uint32 total = 0;
@@ -53,5 +51,11 @@
   return total;
 }
 
+// static
+void ReliableQuicStreamPeer::SetFecPolicy(ReliableQuicStream* stream,
+                                          FecPolicy fec_policy) {
+  stream->set_fec_policy(fec_policy);
+}
+
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/test_tools/reliable_quic_stream_peer.h b/net/quic/test_tools/reliable_quic_stream_peer.h
index c23bf50..1f5467d 100644
--- a/net/quic/test_tools/reliable_quic_stream_peer.h
+++ b/net/quic/test_tools/reliable_quic_stream_peer.h
@@ -26,6 +26,8 @@
 
   static uint32 SizeOfQueuedData(ReliableQuicStream* stream);
 
+  static void SetFecPolicy(ReliableQuicStream* stream, FecPolicy fec_policy);
+
  private:
   DISALLOW_COPY_AND_ASSIGN(ReliableQuicStreamPeer);
 };
diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc
index 5676fc1..46df4fe5 100644
--- a/net/tools/quic/end_to_end_test.cc
+++ b/net/tools/quic/end_to_end_test.cc
@@ -24,6 +24,7 @@
 #include "net/quic/quic_sent_packet_manager.h"
 #include "net/quic/quic_server_id.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_flow_controller_peer.h"
 #include "net/quic/test_tools/quic_session_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
 #include "net/quic/test_tools/reliable_quic_stream_peer.h"
@@ -50,8 +51,10 @@
 using net::EpollServer;
 using net::test::GenerateBody;
 using net::test::QuicConnectionPeer;
+using net::test::QuicFlowControllerPeer;
 using net::test::QuicSessionPeer;
 using net::test::ReliableQuicStreamPeer;
+using net::test::ValueRestore;
 using net::test::kClientDataStreamId1;
 using net::tools::test::PacketDroppingTestWriter;
 using net::tools::test::QuicDispatcherPeer;
@@ -178,10 +181,10 @@
     server_config_.SetDefaults();
 
     // Use different flow control windows for client/server.
-    client_initial_flow_control_receive_window_ =
-        2 * kInitialFlowControlWindowForTest;
-    server_initial_flow_control_receive_window_ =
-        3 * kInitialFlowControlWindowForTest;
+    client_config_.SetInitialFlowControlWindowToSend(
+        2 * kInitialFlowControlWindowForTest);
+    server_config_.SetInitialFlowControlWindowToSend(
+        3 * kInitialFlowControlWindowForTest);
 
     QuicInMemoryCachePeer::ResetForTests();
     AddToCache("GET", "https://www.google.com/foo",
@@ -202,8 +205,7 @@
         server_hostname_,
         false,  // not secure
         client_config_,
-        client_supported_versions_,
-        client_initial_flow_control_receive_window_);
+        client_supported_versions_);
     client->UseWriter(writer);
     client->Connect();
     return client;
@@ -212,13 +214,13 @@
   void set_client_initial_flow_control_receive_window(uint32 window) {
     CHECK(client_.get() == NULL);
     DVLOG(1) << "Setting client initial flow control window: " << window;
-    client_initial_flow_control_receive_window_ = window;
+    client_config_.SetInitialFlowControlWindowToSend(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_initial_flow_control_receive_window_ = window;
+    server_config_.SetInitialFlowControlWindowToSend(window);
   }
 
   bool Initialize() {
@@ -248,11 +250,10 @@
 
   void StartServer() {
     server_thread_.reset(
-        new ServerThread(server_address_,
-                         server_config_,
-                         server_supported_versions_,
-                         strike_register_no_startup_period_,
-                         server_initial_flow_control_receive_window_));
+        new ServerThread(
+            new QuicServer(server_config_, server_supported_versions_),
+            server_address_,
+            strike_register_no_startup_period_));
     server_thread_->Initialize();
     server_address_ = IPEndPoint(server_address_.address(),
                                  server_thread_->GetPort());
@@ -351,8 +352,6 @@
   QuicVersionVector server_supported_versions_;
   QuicVersion negotiated_version_;
   bool strike_register_no_startup_period_;
-  uint32 client_initial_flow_control_receive_window_;
-  uint32 server_initial_flow_control_receive_window_;
 };
 
 // Run all end to end tests with all supported versions.
@@ -637,9 +636,6 @@
 }
 
 TEST_P(EndToEndTest, LargePostFEC) {
-  // TODO(jri): Set FecPolicy to always protect on client_->stream_.
-  // This test currently does not do any FEC protection.
-
   // Connect without packet loss to avoid issues with losing handshake packets,
   // and then up the packet loss rate (b/10126687).
   ASSERT_TRUE(Initialize());
@@ -651,7 +647,9 @@
   // Enable FEC protection.
   QuicPacketCreator* creator = QuicConnectionPeer::GetPacketCreator(
       client_->client()->session()->connection());
-  creator->set_max_packets_per_fec_group(6);
+  creator->set_max_packets_per_fec_group(3);
+  // Set FecPolicy to always protect data on all streams.
+  client_->SetFecPolicy(FEC_PROTECT_ALWAYS);
 
   string body;
   GenerateBody(&body, 10240);
@@ -689,6 +687,49 @@
   VerifyCleanConnection(false);
 }
 
+TEST_P(EndToEndTest, DoNotSetResumeWriteAlarmIfConnectionFlowControlBlocked) {
+  // Regression test for b/14677858.
+  // Test that the resume write alarm is not set in QuicConnection::OnCanWrite
+  // if currently connection level flow control blocked. If set, this results in
+  // an infinite loop in the EpollServer, as the alarm fires and is immediately
+  // rescheduled.
+  ASSERT_TRUE(Initialize());
+  if (negotiated_version_ < QUIC_VERSION_19) {
+    return;
+  }
+  client_->client()->WaitForCryptoHandshakeConfirmed();
+
+  // Ensure both stream and connection level are flow control blocked by setting
+  // the send window offset to 0.
+  const uint64 kFlowControlWindow =
+      server_config_.GetInitialFlowControlWindowToSend();
+  QuicSpdyClientStream* stream = client_->GetOrCreateStream();
+  QuicSession* session = client_->client()->session();
+  QuicFlowControllerPeer::SetSendWindowOffset(stream->flow_controller(), 0);
+  QuicFlowControllerPeer::SetSendWindowOffset(session->flow_controller(), 0);
+  EXPECT_TRUE(stream->flow_controller()->IsBlocked());
+  EXPECT_TRUE(session->flow_controller()->IsBlocked());
+
+  // Make sure that the stream has data pending so that it will be marked as
+  // write blocked when it receives a stream level WINDOW_UPDATE.
+  stream->SendBody("hello", false);
+
+  // The stream now attempts to write, fails because it is still connection
+  // level flow control blocked, and is added to the write blocked list.
+  QuicWindowUpdateFrame window_update(stream->id(), 2 * kFlowControlWindow);
+  stream->OnWindowUpdateFrame(window_update);
+
+  // Prior to fixing b/14677858 this call would result in an infinite loop in
+  // Chromium. As a proxy for detecting this, we now check whether the
+  // resume_writes_alarm is set after OnCanWrite. It should not be, as the
+  // connection is still flow control blocked.
+  session->connection()->OnCanWrite();
+
+  QuicAlarm* resume_writes_alarm =
+      QuicConnectionPeer::GetResumeWritesAlarm(session->connection());
+  EXPECT_FALSE(resume_writes_alarm->IsSet());
+}
+
 TEST_P(EndToEndTest, InvalidStream) {
   ASSERT_TRUE(Initialize());
   client_->client()->WaitForCryptoHandshakeConfirmed();
@@ -747,6 +788,30 @@
   }
 }
 
+TEST_P(EndToEndTest, NegotiateMaxOpenStreams) {
+  // Negotiate 1 max open stream.
+  client_config_.set_max_streams_per_connection(1, 1);
+  ASSERT_TRUE(Initialize());
+  client_->client()->WaitForCryptoHandshakeConfirmed();
+
+  // Make the client misbehave after negotiation.
+  QuicSessionPeer::SetMaxOpenStreams(client_->client()->session(), 10);
+
+  HTTPMessage request(HttpConstants::HTTP_1_1,
+                      HttpConstants::POST, "/foo");
+  request.AddHeader("content-length", "3");
+  request.set_has_complete_message(false);
+
+  // Open two simultaneous streams.
+  client_->SendMessage(request);
+  client_->SendMessage(request);
+  client_->WaitForResponse();
+
+  EXPECT_FALSE(client_->connected());
+  EXPECT_EQ(QUIC_STREAM_CONNECTION_ERROR, client_->stream_error());
+  EXPECT_EQ(QUIC_TOO_MANY_OPEN_STREAMS, client_->connection_error());
+}
+
 TEST_P(EndToEndTest, LimitMaxOpenStreams) {
   // Server limits the number of max streams to 2.
   server_config_.set_max_streams_per_connection(2, 2);
@@ -823,6 +888,7 @@
   client_->client()->WaitForCryptoHandshakeConfirmed();
   server_thread_->WaitForCryptoHandshakeConfirmed();
 
+  // Pause the server so we can access the server's internals without races.
   server_thread_->Pause();
   QuicDispatcher* dispatcher =
       QuicServerPeer::GetDispatcher(server_thread_->server());
diff --git a/net/tools/quic/quic_client.cc b/net/tools/quic/quic_client.cc
index f2802a2..1bee994 100644
--- a/net/tools/quic/quic_client.cc
+++ b/net/tools/quic/quic_client.cc
@@ -35,8 +35,7 @@
 QuicClient::QuicClient(IPEndPoint server_address,
                        const QuicServerId& server_id,
                        const QuicVersionVector& supported_versions,
-                       bool print_response,
-                       uint32 initial_flow_control_window)
+                       bool print_response)
     : server_address_(server_address),
       server_id_(server_id),
       local_port_(0),
@@ -46,16 +45,15 @@
       packets_dropped_(0),
       overflow_supported_(false),
       supported_versions_(supported_versions),
-      print_response_(print_response),
-      initial_flow_control_window_(initial_flow_control_window) {
+      print_response_(print_response) {
   config_.SetDefaults();
 }
 
 QuicClient::QuicClient(IPEndPoint server_address,
                        const QuicServerId& server_id,
-                       const QuicConfig& config,
                        const QuicVersionVector& supported_versions,
-                       uint32 initial_flow_control_window)
+                       bool print_response,
+                       const QuicConfig& config)
     : server_address_(server_address),
       server_id_(server_id),
       config_(config),
@@ -66,8 +64,7 @@
       packets_dropped_(0),
       overflow_supported_(false),
       supported_versions_(supported_versions),
-      print_response_(false),
-      initial_flow_control_window_(initial_flow_control_window) {
+      print_response_(print_response) {
 }
 
 QuicClient::~QuicClient() {
@@ -190,7 +187,6 @@
       config_,
       new QuicConnection(GenerateConnectionId(), server_address_, helper_.get(),
                          writer_.get(), false, supported_versions_),
-      initial_flow_control_window_,
       &crypto_config_));
   return session_->CryptoConnect();
 }
@@ -223,7 +219,7 @@
     stream->set_visitor(this);
   }
 
-  while (WaitForEvents()) { }
+  while (WaitForEvents()) {}
 }
 
 QuicSpdyClientStream* QuicClient::CreateReliableClientStream() {
diff --git a/net/tools/quic/quic_client.h b/net/tools/quic/quic_client.h
index 33dfeaa..da934ff7 100644
--- a/net/tools/quic/quic_client.h
+++ b/net/tools/quic/quic_client.h
@@ -50,13 +50,12 @@
   QuicClient(IPEndPoint server_address,
              const QuicServerId& server_id,
              const QuicVersionVector& supported_versions,
-             bool print_response,
-             uint32 initial_flow_control_window);
+             bool print_response);
   QuicClient(IPEndPoint server_address,
              const QuicServerId& server_id,
-             const QuicConfig& config,
              const QuicVersionVector& supported_versions,
-             uint32 initial_flow_control_window);
+             bool print_response,
+             const QuicConfig& config);
 
   virtual ~QuicClient();
 
@@ -245,9 +244,6 @@
   // when the stream is closed (in OnClose).
   bool print_response_;
 
-  // Size of initial flow control receive window to advertise to server.
-  uint32 initial_flow_control_window_;
-
   DISALLOW_COPY_AND_ASSIGN(QuicClient);
 };
 
diff --git a/net/tools/quic/quic_client_bin.cc b/net/tools/quic/quic_client_bin.cc
index ebbc51e..fefdfc9 100644
--- a/net/tools/quic/quic_client_bin.cc
+++ b/net/tools/quic/quic_client_bin.cc
@@ -79,12 +79,16 @@
 
   net::IPAddressNumber addr;
   CHECK(net::ParseIPLiteralToNumber(FLAGS_address, &addr));
+
+  net::QuicConfig config;
+  config.SetInitialFlowControlWindowToSend(FLAGS_initial_flow_control_window);
+
   // TODO(rjshade): Set version on command line.
   net::tools::QuicClient client(
       net::IPEndPoint(addr, FLAGS_port),
       net::QuicServerId(FLAGS_hostname, FLAGS_port, FLAGS_secure,
                         net::PRIVACY_MODE_DISABLED),
-      net::QuicSupportedVersions(), true, FLAGS_initial_flow_control_window);
+      net::QuicSupportedVersions(), true, config);
 
   client.Initialize();
 
diff --git a/net/tools/quic/quic_client_session.cc b/net/tools/quic/quic_client_session.cc
index c8e28db..aca5418a 100644
--- a/net/tools/quic/quic_client_session.cc
+++ b/net/tools/quic/quic_client_session.cc
@@ -18,11 +18,8 @@
     const QuicServerId& server_id,
     const QuicConfig& config,
     QuicConnection* connection,
-    uint32 max_flow_control_receive_window_bytes,
     QuicCryptoClientConfig* crypto_config)
-    : QuicClientSessionBase(connection,
-                            max_flow_control_receive_window_bytes,
-                            config),
+    : QuicClientSessionBase(connection, config),
       crypto_stream_(server_id, this, NULL, crypto_config) {
 }
 
diff --git a/net/tools/quic/quic_client_session.h b/net/tools/quic/quic_client_session.h
index d04b420..3aad445 100644
--- a/net/tools/quic/quic_client_session.h
+++ b/net/tools/quic/quic_client_session.h
@@ -28,7 +28,6 @@
   QuicClientSession(const QuicServerId& server_id,
                     const QuicConfig& config,
                     QuicConnection* connection,
-                    uint32 max_flow_control_receive_window_bytes,
                     QuicCryptoClientConfig* crypto_config);
   virtual ~QuicClientSession();
 
diff --git a/net/tools/quic/quic_client_session_test.cc b/net/tools/quic/quic_client_session_test.cc
index 50f4c13..e2d39c1 100644
--- a/net/tools/quic/quic_client_session_test.cc
+++ b/net/tools/quic/quic_client_session_test.cc
@@ -39,7 +39,6 @@
         QuicServerId(kServerHostname, kPort, false, PRIVACY_MODE_DISABLED),
         DefaultQuicConfig(),
         connection_,
-        kInitialFlowControlWindowForTest,
         &crypto_config_));
     session_->config()->SetDefaults();
   }
diff --git a/net/tools/quic/quic_dispatcher.cc b/net/tools/quic/quic_dispatcher.cc
index 310450f..715e408 100644
--- a/net/tools/quic/quic_dispatcher.cc
+++ b/net/tools/quic/quic_dispatcher.cc
@@ -162,8 +162,7 @@
 QuicDispatcher::QuicDispatcher(const QuicConfig& config,
                                const QuicCryptoServerConfig& crypto_config,
                                const QuicVersionVector& supported_versions,
-                               EpollServer* epoll_server,
-                               uint32 initial_flow_control_window_bytes)
+                               EpollServer* epoll_server)
     : config_(config),
       crypto_config_(crypto_config),
       delete_sessions_alarm_(new DeleteSessionsAlarm(this)),
@@ -174,8 +173,7 @@
       supported_versions_no_connection_flow_control_(supported_versions),
       current_packet_(NULL),
       framer_(supported_versions, /*unused*/ QuicTime::Zero(), true),
-      framer_visitor_(new QuicFramerVisitor(this)),
-      initial_flow_control_window_bytes_(initial_flow_control_window_bytes) {
+      framer_visitor_(new QuicFramerVisitor(this)) {
   framer_.set_visitor(framer_visitor_.get());
 }
 
@@ -382,7 +380,6 @@
   QuicServerSession* session = new QuicServerSession(
       config_,
       CreateQuicConnection(connection_id, server_address, client_address),
-      initial_flow_control_window_bytes_,
       this);
   session->InitializeSession(crypto_config_);
   return session;
diff --git a/net/tools/quic/quic_dispatcher.h b/net/tools/quic/quic_dispatcher.h
index 4ec5449f..2468f1c 100644
--- a/net/tools/quic/quic_dispatcher.h
+++ b/net/tools/quic/quic_dispatcher.h
@@ -61,8 +61,7 @@
   QuicDispatcher(const QuicConfig& config,
                  const QuicCryptoServerConfig& crypto_config,
                  const QuicVersionVector& supported_versions,
-                 EpollServer* epoll_server,
-                 uint32 initial_flow_control_window_bytes);
+                 EpollServer* epoll_server);
 
   virtual ~QuicDispatcher();
 
@@ -166,10 +165,6 @@
 
   QuicPacketWriter* writer() { return writer_.get(); }
 
-  const uint32 initial_flow_control_window_bytes() const {
-    return initial_flow_control_window_bytes_;
-  }
-
  private:
   class QuicFramerVisitor;
   friend class net::tools::test::QuicDispatcherPeer;
@@ -238,10 +233,6 @@
   QuicFramer framer_;
   scoped_ptr<QuicFramerVisitor> framer_visitor_;
 
-  // Initial flow control window size to advertize to peer on newly created
-  // connections.
-  const uint32 initial_flow_control_window_bytes_;
-
   DISALLOW_COPY_AND_ASSIGN(QuicDispatcher);
 };
 
diff --git a/net/tools/quic/quic_dispatcher_test.cc b/net/tools/quic/quic_dispatcher_test.cc
index 67d4d63..41c3c27 100644
--- a/net/tools/quic/quic_dispatcher_test.cc
+++ b/net/tools/quic/quic_dispatcher_test.cc
@@ -24,15 +24,16 @@
 
 using base::StringPiece;
 using net::EpollServer;
-using net::test::MockSession;
 using net::test::ConstructEncryptedPacket;
+using net::test::MockSession;
+using net::test::ValueRestore;
 using net::tools::test::MockConnection;
 using std::make_pair;
-using testing::_;
 using testing::DoAll;
-using testing::Invoke;
 using testing::InSequence;
+using testing::Invoke;
 using testing::WithoutArgs;
+using testing::_;
 
 namespace net {
 namespace tools {
@@ -47,8 +48,7 @@
       : QuicDispatcher(config,
                        crypto_config,
                        QuicSupportedVersions(),
-                       eps,
-                       kInitialFlowControlWindowForTest) {
+                       eps) {
   }
 
   MOCK_METHOD3(CreateQuicSession, QuicSession*(
@@ -283,12 +283,11 @@
     kTestVersions.push_back(kTestQuicVersions[i]);
   }
 
-  QuicDispatcher dispatcher(config, server_config, kTestVersions, &eps,
-                            kInitialFlowControlWindowForTest);
+  QuicDispatcher dispatcher(config, server_config, kTestVersions, &eps);
   dispatcher.Initialize(0);
 
   // When flag is enabled, new connections should support QUIC_VERSION_17.
-  FLAGS_enable_quic_stream_flow_control_2 = true;
+  ValueRestore<bool> old_flag(&FLAGS_enable_quic_stream_flow_control_2, true);
   scoped_ptr<QuicConnection> connection_1(
       QuicDispatcherPeer::CreateQuicConnection(&dispatcher, kCID, client,
                                                server));
diff --git a/net/tools/quic/quic_server.cc b/net/tools/quic/quic_server.cc
index 2d1a52d..be3d9fb 100644
--- a/net/tools/quic/quic_server.cc
+++ b/net/tools/quic/quic_server.cc
@@ -42,17 +42,14 @@
       overflow_supported_(false),
       use_recvmmsg_(false),
       crypto_config_(kSourceAddressTokenSecret, QuicRandom::GetInstance()),
-      supported_versions_(QuicSupportedVersions()),
-      server_initial_flow_control_receive_window_(
-          kServerInitialFlowControlWindow) {
+      supported_versions_(QuicSupportedVersions()) {
   // Use hardcoded crypto parameters for now.
   config_.SetDefaults();
   Initialize();
 }
 
 QuicServer::QuicServer(const QuicConfig& config,
-                       const QuicVersionVector& supported_versions,
-                       uint32 server_initial_flow_control_receive_window)
+                       const QuicVersionVector& supported_versions)
     : port_(0),
       fd_(-1),
       packets_dropped_(0),
@@ -60,9 +57,7 @@
       use_recvmmsg_(false),
       config_(config),
       crypto_config_(kSourceAddressTokenSecret, QuicRandom::GetInstance()),
-      supported_versions_(supported_versions),
-      server_initial_flow_control_receive_window_(
-          server_initial_flow_control_receive_window) {
+      supported_versions_(supported_versions) {
   Initialize();
 }
 
@@ -80,6 +75,9 @@
       crypto_config_.AddDefaultConfig(
           QuicRandom::GetInstance(), &clock,
           QuicCryptoServerConfig::ConfigOptions()));
+
+  // Set flow control options in the config.
+  config_.SetInitialCongestionWindowToSend(kServerInitialFlowControlWindow);
 }
 
 QuicServer::~QuicServer() {
@@ -164,17 +162,20 @@
   }
 
   epoll_server_.RegisterFD(fd_, this, kEpollFlags);
-  dispatcher_.reset(new QuicDispatcher(
-      config_,
-      crypto_config_,
-      supported_versions_,
-      &epoll_server_,
-      server_initial_flow_control_receive_window_));
+  dispatcher_.reset(CreateQuicDispatcher());
   dispatcher_->Initialize(fd_);
 
   return true;
 }
 
+QuicDispatcher* QuicServer::CreateQuicDispatcher() {
+  return new QuicDispatcher(
+      config_,
+      crypto_config_,
+      supported_versions_,
+      &epoll_server_);
+}
+
 void QuicServer::WaitForEvents() {
   epoll_server_.WaitForEventsAndExecuteCallbacks();
 }
diff --git a/net/tools/quic/quic_server.h b/net/tools/quic/quic_server.h
index 119fa78..6285fd76 100644
--- a/net/tools/quic/quic_server.h
+++ b/net/tools/quic/quic_server.h
@@ -31,8 +31,7 @@
  public:
   QuicServer();
   QuicServer(const QuicConfig& config,
-             const QuicVersionVector& supported_versions,
-             uint32 server_initial_flow_control_receive_window);
+             const QuicVersionVector& supported_versions);
 
   virtual ~QuicServer();
 
@@ -81,6 +80,18 @@
 
   int port() { return port_; }
 
+ protected:
+  virtual QuicDispatcher* CreateQuicDispatcher();
+
+  const QuicConfig& config() const { return config_; }
+  const QuicCryptoServerConfig& crypto_config() const {
+    return crypto_config_;
+  }
+  const QuicVersionVector& supported_versions() const {
+    return supported_versions_;
+  }
+  EpollServer* epoll_server() { return &epoll_server_; }
+
  private:
   friend class net::tools::test::QuicServerPeer;
 
diff --git a/net/tools/quic/quic_server_session.cc b/net/tools/quic/quic_server_session.cc
index 7c31ef0..d7bc3b8 100644
--- a/net/tools/quic/quic_server_session.cc
+++ b/net/tools/quic/quic_server_session.cc
@@ -14,9 +14,8 @@
 
 QuicServerSession::QuicServerSession(const QuicConfig& config,
                                      QuicConnection* connection,
-                                     uint32 max_flow_control_window_bytes,
                                      QuicServerSessionVisitor* visitor)
-    : QuicSession(connection, max_flow_control_window_bytes, config),
+    : QuicSession(connection, config),
       visitor_(visitor) {}
 
 QuicServerSession::~QuicServerSession() {}
diff --git a/net/tools/quic/quic_server_session.h b/net/tools/quic/quic_server_session.h
index 9fc4611e..e05ba16 100644
--- a/net/tools/quic/quic_server_session.h
+++ b/net/tools/quic/quic_server_session.h
@@ -47,7 +47,6 @@
  public:
   QuicServerSession(const QuicConfig& config,
                     QuicConnection* connection,
-                    uint32 max_flow_control_window_bytes,
                     QuicServerSessionVisitor* visitor);
 
   // Override the base class to notify the owner of the connection close.
diff --git a/net/tools/quic/quic_server_session_test.cc b/net/tools/quic/quic_server_session_test.cc
index 728b2bc..34c0066 100644
--- a/net/tools/quic/quic_server_session_test.cc
+++ b/net/tools/quic/quic_server_session_test.cc
@@ -52,11 +52,11 @@
                        QuicRandom::GetInstance()) {
     config_.SetDefaults();
     config_.set_max_streams_per_connection(3, 3);
+    config_.SetInitialFlowControlWindowToSend(kInitialFlowControlWindowForTest);
 
     connection_ =
         new StrictMock<MockConnection>(true, SupportedVersions(GetParam()));
-    session_.reset(new QuicServerSession(
-        config_, connection_, kInitialFlowControlWindowForTest, &owner_));
+    session_.reset(new QuicServerSession(config_, connection_, &owner_));
     session_->InitializeSession(crypto_config_);
     visitor_ = QuicConnectionPeer::GetVisitor(connection_);
   }
diff --git a/net/tools/quic/quic_spdy_client_stream_test.cc b/net/tools/quic/quic_spdy_client_stream_test.cc
index 4de5c9b..db38924c 100644
--- a/net/tools/quic/quic_spdy_client_stream_test.cc
+++ b/net/tools/quic/quic_spdy_client_stream_test.cc
@@ -32,7 +32,6 @@
         session_(QuicServerId("example.com", 80, false, PRIVACY_MODE_DISABLED),
                  DefaultQuicConfig(),
                  connection_,
-                 kInitialFlowControlWindowForTest,
                  &crypto_config_),
         body_("hello world") {
     crypto_config_.SetDefaults();
diff --git a/net/tools/quic/quic_spdy_server_stream_test.cc b/net/tools/quic/quic_spdy_server_stream_test.cc
index 954f0a4..a6a015d 100644
--- a/net/tools/quic/quic_spdy_server_stream_test.cc
+++ b/net/tools/quic/quic_spdy_server_stream_test.cc
@@ -84,9 +84,8 @@
 
     // New streams rely on having the peer's flow control receive window
     // negotiated in the config.
-    const uint32 kInitialWindow = 10 * kMaxPacketSize;
     session_.config()->SetInitialFlowControlWindowToSend(
-        kInitialWindow);
+        kInitialFlowControlWindowForTest);
     stream_.reset(new QuicSpdyServerStreamPeer(3, &session_));
   }
 
diff --git a/net/tools/quic/test_tools/mock_epoll_server.h b/net/tools/quic/test_tools/mock_epoll_server.h
index abead02e..cdb6a36 100644
--- a/net/tools/quic/test_tools/mock_epoll_server.h
+++ b/net/tools/quic/test_tools/mock_epoll_server.h
@@ -85,16 +85,16 @@
  protected:  // functions
   // These functions do nothing here, as we're not actually
   // using the epoll_* syscalls.
-  virtual void DelFD(int fd) const OVERRIDE { }
-  virtual void AddFD(int fd, int event_mask) const OVERRIDE { }
-  virtual void ModFD(int fd, int event_mask) const OVERRIDE { }
+  virtual void DelFD(int fd) const OVERRIDE {}
+  virtual void AddFD(int fd, int event_mask) const OVERRIDE {}
+  virtual void ModFD(int fd, int event_mask) const OVERRIDE {}
 
   // Replaces the epoll_server's epoll_wait_impl.
   virtual int epoll_wait_impl(int epfd,
                               struct epoll_event* events,
                               int max_events,
                               int timeout_in_ms) OVERRIDE;
-  virtual void SetNonblocking (int fd) OVERRIDE { }
+  virtual void SetNonblocking (int fd) OVERRIDE {}
 
  private:  // members
   EventQueue event_queue_;
diff --git a/net/tools/quic/test_tools/mock_quic_dispatcher.cc b/net/tools/quic/test_tools/mock_quic_dispatcher.cc
index 21195bc..dc7f0d3f 100644
--- a/net/tools/quic/test_tools/mock_quic_dispatcher.cc
+++ b/net/tools/quic/test_tools/mock_quic_dispatcher.cc
@@ -19,8 +19,7 @@
     : QuicDispatcher(config,
                      crypto_config,
                      QuicSupportedVersions(),
-                     eps,
-                     kInitialFlowControlWindowForTest) {}
+                     eps) {}
 
 MockQuicDispatcher::~MockQuicDispatcher() {}
 
diff --git a/net/tools/quic/test_tools/packet_dropping_test_writer.cc b/net/tools/quic/test_tools/packet_dropping_test_writer.cc
index 148591d0c..f438eb0 100644
--- a/net/tools/quic/test_tools/packet_dropping_test_writer.cc
+++ b/net/tools/quic/test_tools/packet_dropping_test_writer.cc
@@ -19,7 +19,7 @@
 class WriteUnblockedAlarm : public QuicAlarm::Delegate {
  public:
   explicit WriteUnblockedAlarm(PacketDroppingTestWriter* writer)
-      : writer_(writer) { }
+      : writer_(writer) {}
 
   virtual QuicTime OnAlarm() OVERRIDE {
     DVLOG(1) << "Unblocking socket.";
@@ -36,7 +36,7 @@
 class DelayAlarm : public QuicAlarm::Delegate {
  public:
   explicit DelayAlarm(PacketDroppingTestWriter* writer)
-      : writer_(writer) { }
+      : writer_(writer) {}
 
   virtual QuicTime OnAlarm() OVERRIDE {
     return writer_->ReleaseOldPackets();
diff --git a/net/tools/quic/test_tools/packet_dropping_test_writer.h b/net/tools/quic/test_tools/packet_dropping_test_writer.h
index 78f0a40..3509722 100644
--- a/net/tools/quic/test_tools/packet_dropping_test_writer.h
+++ b/net/tools/quic/test_tools/packet_dropping_test_writer.h
@@ -12,6 +12,7 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/synchronization/lock.h"
 #include "net/quic/quic_alarm.h"
+#include "net/quic/test_tools/quic_test_utils.h"
 #include "net/tools/quic/quic_epoll_clock.h"
 #include "net/tools/quic/quic_packet_writer_wrapper.h"
 #include "net/tools/quic/test_tools/quic_test_client.h"
@@ -134,7 +135,7 @@
   scoped_ptr<QuicAlarm> write_unblocked_alarm_;
   scoped_ptr<QuicAlarm> delay_alarm_;
   scoped_ptr<Delegate> on_can_write_;
-  SimpleRandom simple_random_;
+  net::test::SimpleRandom simple_random_;
   // Stored packets delayed by fake packet delay or bandwidth restrictions.
   DelayedPacketList delayed_packets_;
   QuicByteCount cur_buffer_size_;
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index 51f599d..d0cfe68 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -12,7 +12,9 @@
 #include "net/quic/crypto/proof_verifier.h"
 #include "net/quic/quic_server_id.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
+#include "net/quic/test_tools/quic_session_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
+#include "net/quic/test_tools/reliable_quic_stream_peer.h"
 #include "net/tools/balsa/balsa_headers.h"
 #include "net/tools/quic/quic_epoll_connection_helper.h"
 #include "net/tools/quic/quic_packet_writer_wrapper.h"
@@ -23,8 +25,10 @@
 
 using base::StringPiece;
 using net::QuicServerId;
-using net::test::kInitialFlowControlWindowForTest;
 using net::test::QuicConnectionPeer;
+using net::test::QuicSessionPeer;
+using net::test::ReliableQuicStreamPeer;
+using net::test::kInitialFlowControlWindowForTest;
 using std::string;
 using std::vector;
 
@@ -100,13 +104,11 @@
 MockableQuicClient::MockableQuicClient(
     IPEndPoint server_address,
     const QuicServerId& server_id,
-    const QuicVersionVector& supported_versions,
-    uint32 initial_flow_control_window)
+    const QuicVersionVector& supported_versions)
     : QuicClient(server_address,
                  server_id,
                  supported_versions,
-                 false,
-                 initial_flow_control_window),
+                 false),
       override_connection_id_(0),
       test_writer_(NULL) {}
 
@@ -114,13 +116,12 @@
     IPEndPoint server_address,
     const QuicServerId& server_id,
     const QuicConfig& config,
-    const QuicVersionVector& supported_versions,
-    uint32 initial_flow_control_window)
+    const QuicVersionVector& supported_versions)
     : QuicClient(server_address,
                  server_id,
-                 config,
                  supported_versions,
-                 initial_flow_control_window),
+                 false,
+                 config),
       override_connection_id_(0),
       test_writer_(NULL) {}
 
@@ -162,8 +163,7 @@
                                                   server_address.port(),
                                                   false,
                                                   PRIVACY_MODE_DISABLED),
-                                     supported_versions,
-                                     kInitialFlowControlWindowForTest)) {
+                                     supported_versions)) {
   Initialize(true);
 }
 
@@ -176,8 +176,7 @@
                                                   server_address.port(),
                                                   secure,
                                                   PRIVACY_MODE_DISABLED),
-                                     supported_versions,
-                                     kInitialFlowControlWindowForTest)) {
+                                     supported_versions)) {
   Initialize(secure);
 }
 
@@ -186,8 +185,7 @@
     const string& server_hostname,
     bool secure,
     const QuicConfig& config,
-    const QuicVersionVector& supported_versions,
-    uint32 client_initial_flow_control_receive_window)
+    const QuicVersionVector& supported_versions)
     : client_(
           new MockableQuicClient(server_address,
                                  QuicServerId(server_hostname,
@@ -195,8 +193,7 @@
                                               secure,
                                               PRIVACY_MODE_DISABLED),
                                  config,
-                                 supported_versions,
-                                 client_initial_flow_control_receive_window)) {
+                                 supported_versions)) {
   Initialize(secure);
 }
 
@@ -215,6 +212,7 @@
   secure_ = secure;
   auto_reconnect_ = false;
   buffer_body_ = true;
+  fec_policy_ = FEC_PROTECT_OPTIONAL;
   proof_verifier_ = NULL;
   ClearPerRequestState();
   ExpectCertificates(secure_);
@@ -333,6 +331,8 @@
     }
     stream_->set_visitor(this);
     reinterpret_cast<QuicSpdyClientStream*>(stream_)->set_priority(priority_);
+    // Set FEC policy on stream.
+    ReliableQuicStreamPeer::SetFecPolicy(stream_, fec_policy_);
   }
 
   return stream_;
@@ -545,6 +545,15 @@
   }
 }
 
+void QuicTestClient::SetFecPolicy(FecPolicy fec_policy) {
+  fec_policy_ = fec_policy;
+  // Set policy for headers and crypto streams.
+  ReliableQuicStreamPeer::SetFecPolicy(
+      QuicSessionPeer::GetHeadersStream(client()->session()), fec_policy);
+  ReliableQuicStreamPeer::SetFecPolicy(client()->session()->GetCryptoStream(),
+                                       fec_policy);
+}
+
 }  // namespace test
 }  // namespace tools
 }  // namespace net
diff --git a/net/tools/quic/test_tools/quic_test_client.h b/net/tools/quic/test_tools/quic_test_client.h
index 975ce5a..1c70c33 100644
--- a/net/tools/quic/test_tools/quic_test_client.h
+++ b/net/tools/quic/test_tools/quic_test_client.h
@@ -35,14 +35,12 @@
  public:
   MockableQuicClient(IPEndPoint server_address,
                      const QuicServerId& server_id,
-                     const QuicVersionVector& supported_versions,
-                     uint32 initial_flow_control_window);
+                     const QuicVersionVector& supported_versions);
 
   MockableQuicClient(IPEndPoint server_address,
                      const QuicServerId& server_id,
                      const QuicConfig& config,
-                     const QuicVersionVector& supported_versions,
-                     uint32 initial_flow_control_window);
+                     const QuicVersionVector& supported_versions);
 
   virtual ~MockableQuicClient() OVERRIDE;
   virtual QuicPacketWriter* CreateQuicPacketWriter() OVERRIDE;
@@ -72,8 +70,7 @@
                  const string& server_hostname,
                  bool secure,
                  const QuicConfig& config,
-                 const QuicVersionVector& supported_versions,
-                 uint32 client_initial_flow_control_receive_window);
+                 const QuicVersionVector& supported_versions);
 
   virtual ~QuicTestClient();
 
@@ -154,6 +151,10 @@
 
   void set_priority(QuicPriority priority) { priority_ = priority; }
 
+  // Sets client's FEC policy. This policy applies to the data stream(s), and
+  // also to the headers and crypto streams.
+  void SetFecPolicy(FecPolicy fec_policy);
+
   void WaitForWriteToFlush();
 
  protected:
@@ -189,7 +190,8 @@
   bool auto_reconnect_;
   // Should we buffer the response body? Defaults to true.
   bool buffer_body_;
-
+  // FEC policy for data sent by this client.
+  FecPolicy fec_policy_;
   // proof_verifier_ points to a RecordingProofVerifier that is owned by
   // client_.
   ProofVerifier* proof_verifier_;
diff --git a/net/tools/quic/test_tools/quic_test_utils.cc b/net/tools/quic/test_tools/quic_test_utils.cc
index 18a6a62..e552c24b 100644
--- a/net/tools/quic/test_tools/quic_test_utils.cc
+++ b/net/tools/quic/test_tools/quic_test_utils.cc
@@ -4,7 +4,6 @@
 
 #include "net/tools/quic/test_tools/quic_test_utils.h"
 
-#include "base/sha1.h"
 #include "net/quic/quic_connection.h"
 #include "net/quic/test_tools/quic_connection_peer.h"
 #include "net/quic/test_tools/quic_test_utils.h"
@@ -80,18 +79,10 @@
   return ack;
 }
 
-uint64 SimpleRandom::RandUint64() {
-  unsigned char hash[base::kSHA1Length];
-  base::SHA1HashBytes(reinterpret_cast<unsigned char*>(&seed_), sizeof(seed_),
-                      hash);
-  memcpy(&seed_, hash, sizeof(seed_));
-  return seed_;
-}
-
 TestSession::TestSession(QuicConnection* connection,
                          const QuicConfig& config)
-  : QuicSession(connection, kInitialFlowControlWindowForTest, config),
-      crypto_stream_(NULL) {
+  : QuicSession(connection, config),
+    crypto_stream_(NULL) {
 }
 
 TestSession::~TestSession() {}
diff --git a/net/tools/quic/test_tools/quic_test_utils.h b/net/tools/quic/test_tools/quic_test_utils.h
index 44dbd2bd..2ff8c84 100644
--- a/net/tools/quic/test_tools/quic_test_utils.h
+++ b/net/tools/quic/test_tools/quic_test_utils.h
@@ -34,25 +34,6 @@
 QuicAckFrame MakeAckFrameWithNackRanges(size_t num_nack_ranges,
                                         QuicPacketSequenceNumber least_unacked);
 
-// Simple random number generator used to compute random numbers suitable
-// for pseudo-randomly dropping packets in tests.  It works by computing
-// the sha1 hash of the current seed, and using the first 64 bits as
-// the next random number, and the next seed.
-class SimpleRandom {
- public:
-  SimpleRandom() : seed_(0) {}
-
-  // Returns a random number in the range [0, kuint64max].
-  uint64 RandUint64();
-
-  void set_seed(uint64 seed) { seed_ = seed; }
-
- private:
-  uint64 seed_;
-
-  DISALLOW_COPY_AND_ASSIGN(SimpleRandom);
-};
-
 class MockConnection : public QuicConnection {
  public:
   // Uses a MockHelper, ConnectionId of 42, and 127.0.0.1:123.
diff --git a/net/tools/quic/test_tools/server_thread.cc b/net/tools/quic/test_tools/server_thread.cc
index 839e137..337cc64 100644
--- a/net/tools/quic/test_tools/server_thread.cc
+++ b/net/tools/quic/test_tools/server_thread.cc
@@ -10,25 +10,21 @@
 namespace tools {
 namespace test {
 
-ServerThread::ServerThread(IPEndPoint address,
-                           const QuicConfig& config,
-                           const QuicVersionVector& supported_versions,
-                           bool strike_register_no_startup_period,
-                           uint32 server_initial_flow_control_receive_window)
+ServerThread::ServerThread(QuicServer* server,
+                           IPEndPoint address,
+                           bool strike_register_no_startup_period)
     : SimpleThread("server_thread"),
       confirmed_(true, false),
       pause_(true, false),
       paused_(true, false),
       resume_(true, false),
       quit_(true, false),
-      server_(config,
-              supported_versions,
-              server_initial_flow_control_receive_window),
+      server_(server),
       address_(address),
       port_(0),
       initialized_(false) {
   if (strike_register_no_startup_period) {
-    server_.SetStrikeRegisterNoStartupPeriod();
+    server_->SetStrikeRegisterNoStartupPeriod();
   }
 }
 
@@ -39,10 +35,10 @@
     return;
   }
 
-  server_.Listen(address_);
+  server_->Listen(address_);
 
   port_lock_.Acquire();
-  port_ = server_.port();
+  port_ = server_->port();
   port_lock_.Release();
 
   initialized_ = true;
@@ -58,11 +54,11 @@
       paused_.Signal();
       resume_.Wait();
     }
-    server_.WaitForEvents();
+    server_->WaitForEvents();
     MaybeNotifyOfHandshakeConfirmation();
   }
 
-  server_.Shutdown();
+  server_->Shutdown();
 }
 
 int ServerThread::GetPort() {
diff --git a/net/tools/quic/test_tools/server_thread.h b/net/tools/quic/test_tools/server_thread.h
index 9473784..6066d974 100644
--- a/net/tools/quic/test_tools/server_thread.h
+++ b/net/tools/quic/test_tools/server_thread.h
@@ -17,11 +17,9 @@
 // Simple wrapper class to run server in a thread.
 class ServerThread : public base::SimpleThread {
  public:
-  ServerThread(IPEndPoint address,
-               const QuicConfig& config,
-               const QuicVersionVector& supported_versions,
-               bool strike_register_no_startup_period,
-               uint32 server_initial_flow_control_receive_window);
+  ServerThread(QuicServer* server,
+               IPEndPoint address,
+               bool strike_register_no_startup_period);
 
   virtual ~ServerThread();
 
@@ -50,7 +48,7 @@
   // Returns the underlying server.  Care must be taken to avoid data races
   // when accessing the server.  It is always safe to access the server
   // after calling Pause() and before calling Resume().
-  QuicServer* server() { return &server_; }
+  QuicServer* server() { return server_.get(); }
 
   // Returns the port that the server is listening on.
   int GetPort();
@@ -65,7 +63,7 @@
   base::WaitableEvent resume_;     // Notified when the server should resume.
   base::WaitableEvent quit_;       // Notified when the server should quit.
 
-  tools::QuicServer server_;
+  scoped_ptr<QuicServer> server_;
   IPEndPoint address_;
   base::Lock port_lock_;
   int port_;