Have separate uni- and bi-directional stream limits for IETF QUIC

IETF QUIC supports unidirectional and bidirectional streams, each
of which can have a different stream limit. The stream limit is first
negotiated using the transport parameters.  This change connects the
transport parameters to the stream-id-manager so that the former
can configure both the types of streams.

gfe-relnote: N/A all for version99 code.
PiperOrigin-RevId: 248325885
Change-Id: I4675c147dfda856b73337aac87a4290958599a18
diff --git a/quic/core/crypto/crypto_handshake_message.cc b/quic/core/crypto/crypto_handshake_message.cc
index 1bbed74..579bb37 100644
--- a/quic/core/crypto/crypto_handshake_message.cc
+++ b/quic/core/crypto/crypto_handshake_message.cc
@@ -274,7 +274,8 @@
       case kCFCW:
       case kSFCW:
       case kIRTT:
-      case kMIDS:
+      case kMIUS:
+      case kMIBS:
       case kSCLS:
       case kTCID:
         // uint32_t value
diff --git a/quic/core/crypto/crypto_protocol.h b/quic/core/crypto/crypto_protocol.h
index a3abb33..e8c8d84 100644
--- a/quic/core/crypto/crypto_protocol.h
+++ b/quic/core/crypto/crypto_protocol.h
@@ -218,7 +218,8 @@
 const QuicTag kCLOP = TAG('C', 'L', 'O', 'P');   // Client connection options
 const QuicTag kICSL = TAG('I', 'C', 'S', 'L');   // Idle network timeout
 const QuicTag kSCLS = TAG('S', 'C', 'L', 'S');   // Silently close on timeout
-const QuicTag kMIDS = TAG('M', 'I', 'D', 'S');   // Max incoming dynamic streams
+const QuicTag kMIBS = TAG('M', 'I', 'D', 'S');   // Max incoming bidi streams
+const QuicTag kMIUS = TAG('M', 'I', 'U', 'S');   // Max incoming unidi streams
 const QuicTag kIRTT = TAG('I', 'R', 'T', 'T');   // Estimated initial RTT in us.
 const QuicTag kSNI  = TAG('S', 'N', 'I', '\0');  // Server name
                                                  // indication
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc
index 2675428..209bc81 100644
--- a/quic/core/http/end_to_end_test.cc
+++ b/quic/core/http/end_to_end_test.cc
@@ -1389,7 +1389,7 @@
   // Set a limit on maximum number of incoming dynamic streams.
   // Make sure the limit is respected.
   const uint32_t kServerMaxIncomingDynamicStreams = 1;
-  server_config_.SetMaxIncomingDynamicStreamsToSend(
+  server_config_.SetMaxIncomingBidirectionalStreamsToSend(
       kServerMaxIncomingDynamicStreams);
   ASSERT_TRUE(Initialize());
   if (GetParam().negotiated_version.transport_version == QUIC_VERSION_99) {
@@ -1430,10 +1430,15 @@
   // Each endpoint can set max incoming dynamic streams independently.
   const uint32_t kClientMaxIncomingDynamicStreams = 2;
   const uint32_t kServerMaxIncomingDynamicStreams = 1;
-  client_config_.SetMaxIncomingDynamicStreamsToSend(
+  client_config_.SetMaxIncomingBidirectionalStreamsToSend(
       kClientMaxIncomingDynamicStreams);
-  server_config_.SetMaxIncomingDynamicStreamsToSend(
+  server_config_.SetMaxIncomingBidirectionalStreamsToSend(
       kServerMaxIncomingDynamicStreams);
+  client_config_.SetMaxIncomingUnidirectionalStreamsToSend(
+      kClientMaxIncomingDynamicStreams);
+  server_config_.SetMaxIncomingUnidirectionalStreamsToSend(
+      kServerMaxIncomingDynamicStreams);
+
   ASSERT_TRUE(Initialize());
   EXPECT_TRUE(client_->client()->WaitForCryptoHandshakeConfirmed());
 
@@ -2739,8 +2744,10 @@
   const size_t kNumMaxStreams = 10;
 
   EndToEndTestServerPush() : EndToEndTest() {
-    client_config_.SetMaxIncomingDynamicStreamsToSend(kNumMaxStreams);
-    server_config_.SetMaxIncomingDynamicStreamsToSend(kNumMaxStreams);
+    client_config_.SetMaxIncomingBidirectionalStreamsToSend(kNumMaxStreams);
+    server_config_.SetMaxIncomingBidirectionalStreamsToSend(kNumMaxStreams);
+    client_config_.SetMaxIncomingUnidirectionalStreamsToSend(kNumMaxStreams);
+    server_config_.SetMaxIncomingUnidirectionalStreamsToSend(kNumMaxStreams);
     support_server_push_ = true;
   }
 
diff --git a/quic/core/http/quic_server_session_base_test.cc b/quic/core/http/quic_server_session_base_test.cc
index 0527f0b..b532601 100644
--- a/quic/core/http/quic_server_session_base_test.cc
+++ b/quic/core/http/quic_server_session_base_test.cc
@@ -131,9 +131,12 @@
                        TlsServerHandshaker::CreateSslCtx()),
         compressed_certs_cache_(
             QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {
-    config_.SetMaxIncomingDynamicStreamsToSend(kMaxStreamsForTest);
-    QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(&config_,
-                                                         kMaxStreamsForTest);
+    config_.SetMaxIncomingBidirectionalStreamsToSend(kMaxStreamsForTest);
+    config_.SetMaxIncomingUnidirectionalStreamsToSend(kMaxStreamsForTest);
+    QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams(
+        &config_, kMaxStreamsForTest);
+    QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams(
+        &config_, kMaxStreamsForTest);
     config_.SetInitialStreamFlowControlWindowToSend(
         kInitialStreamFlowControlWindowForTest);
     config_.SetInitialSessionFlowControlWindowToSend(
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index 8460c7d..599f939 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -153,7 +153,15 @@
     QuicCryptoClientStream* stream = static_cast<QuicCryptoClientStream*>(
         session_->GetMutableCryptoStream());
     QuicConfig config = DefaultQuicConfig();
-    config.SetMaxIncomingDynamicStreamsToSend(server_max_incoming_streams);
+    if (connection_->transport_version() == QUIC_VERSION_99) {
+      config.SetMaxIncomingUnidirectionalStreamsToSend(
+          server_max_incoming_streams);
+      config.SetMaxIncomingBidirectionalStreamsToSend(
+          server_max_incoming_streams);
+    } else {
+      config.SetMaxIncomingBidirectionalStreamsToSend(
+          server_max_incoming_streams);
+    }
     crypto_test_utils::HandshakeWithFakeServer(
         &config, &helper_, &alarm_factory_, connection_, stream);
   }
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index 26ec153..c7ce057 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -69,7 +69,7 @@
         kInitialStreamFlowControlWindowForTest);
     session()->config()->SetInitialSessionFlowControlWindowToSend(
         kInitialSessionFlowControlWindowForTest);
-    session()->config()->ToHandshakeMessage(&msg);
+    session()->config()->ToHandshakeMessage(&msg, transport_version());
     const QuicErrorCode error =
         session()->config()->ProcessPeerHello(msg, CLIENT, &error_details);
     EXPECT_EQ(QUIC_NO_ERROR, error);
@@ -565,7 +565,11 @@
 TEST_P(QuicSpdySessionTestServer, ManyAvailableStreams) {
   // When max_open_streams_ is 200, should be able to create 200 streams
   // out-of-order, that is, creating the one with the largest stream ID first.
-  QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200);
+  if (IsVersion99()) {
+    QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_, 200);
+  } else {
+    QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200);
+  }
   QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
   // Create one stream.
   session_.GetOrCreateDynamicStream(stream_id);
@@ -1179,7 +1183,7 @@
     QuicStreamOffset offset = crypto_stream->stream_bytes_written();
     QuicConfig config;
     CryptoHandshakeMessage crypto_message;
-    config.ToHandshakeMessage(&crypto_message);
+    config.ToHandshakeMessage(&crypto_message, transport_version());
     crypto_stream->SendHandshakeMessage(crypto_message);
     char buf[1000];
     QuicDataWriter writer(1000, buf, NETWORK_BYTE_ORDER);
@@ -1500,7 +1504,12 @@
   // with a FIN or RST then we send an RST to refuse streams for versions other
   // than version 99. In version 99 the connection gets closed.
   const QuicStreamId kMaxStreams = 5;
-  QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+  if (IsVersion99()) {
+    QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_,
+                                                            kMaxStreams);
+  } else {
+    QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+  }
   // GetNth assumes that both the crypto and header streams have been
   // open, but the stream id manager, using GetFirstBidirectional... only
   // assumes that the crypto stream is open. This means that GetNth...(0)
@@ -1573,7 +1582,12 @@
   }
   EXPECT_CALL(*connection_, OnStreamReset(_, QUIC_REFUSED_STREAM)).Times(0);
   const QuicStreamId kMaxStreams = 5;
-  QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+  if (IsVersion99()) {
+    QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_,
+                                                            kMaxStreams);
+  } else {
+    QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+  }
 
   // Create kMaxStreams + 1 data streams, and mark them draining.
   const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0);
diff --git a/quic/core/quic_config.cc b/quic/core/quic_config.cc
index 4880c6d..0914809 100644
--- a/quic/core/quic_config.cc
+++ b/quic/core/quic_config.cc
@@ -404,7 +404,7 @@
       client_connection_options_(kCLOP, PRESENCE_OPTIONAL),
       idle_network_timeout_seconds_(kICSL, PRESENCE_REQUIRED),
       silent_close_(kSCLS, PRESENCE_OPTIONAL),
-      max_incoming_dynamic_streams_(kMIDS, PRESENCE_REQUIRED),
+      max_incoming_bidirectional_streams_(kMIBS, PRESENCE_REQUIRED),
       bytes_for_connection_id_(kTCID, PRESENCE_OPTIONAL),
       initial_round_trip_time_us_(kIRTT, PRESENCE_OPTIONAL),
       initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL),
@@ -412,7 +412,8 @@
       connection_migration_disabled_(kNCMR, PRESENCE_OPTIONAL),
       alternate_server_address_(kASAD, PRESENCE_OPTIONAL),
       support_max_header_list_size_(kSMHL, PRESENCE_OPTIONAL),
-      stateless_reset_token_(kSRST, PRESENCE_OPTIONAL) {
+      stateless_reset_token_(kSRST, PRESENCE_OPTIONAL),
+      max_incoming_unidirectional_streams_(kMIUS, PRESENCE_OPTIONAL) {
   SetDefaults();
 }
 
@@ -505,21 +506,38 @@
   return silent_close_.GetUint32() > 0;
 }
 
-void QuicConfig::SetMaxIncomingDynamicStreamsToSend(
-    uint32_t max_incoming_dynamic_streams) {
-  max_incoming_dynamic_streams_.SetSendValue(max_incoming_dynamic_streams);
+void QuicConfig::SetMaxIncomingBidirectionalStreamsToSend(
+    uint32_t max_streams) {
+  max_incoming_bidirectional_streams_.SetSendValue(max_streams);
 }
 
-uint32_t QuicConfig::GetMaxIncomingDynamicStreamsToSend() {
-  return max_incoming_dynamic_streams_.GetSendValue();
+uint32_t QuicConfig::GetMaxIncomingBidirectionalStreamsToSend() {
+  return max_incoming_bidirectional_streams_.GetSendValue();
 }
 
-bool QuicConfig::HasReceivedMaxIncomingDynamicStreams() {
-  return max_incoming_dynamic_streams_.HasReceivedValue();
+bool QuicConfig::HasReceivedMaxIncomingBidirectionalStreams() {
+  return max_incoming_bidirectional_streams_.HasReceivedValue();
 }
 
-uint32_t QuicConfig::ReceivedMaxIncomingDynamicStreams() {
-  return max_incoming_dynamic_streams_.GetReceivedValue();
+uint32_t QuicConfig::ReceivedMaxIncomingBidirectionalStreams() {
+  return max_incoming_bidirectional_streams_.GetReceivedValue();
+}
+
+void QuicConfig::SetMaxIncomingUnidirectionalStreamsToSend(
+    uint32_t max_streams) {
+  max_incoming_unidirectional_streams_.SetSendValue(max_streams);
+}
+
+uint32_t QuicConfig::GetMaxIncomingUnidirectionalStreamsToSend() {
+  return max_incoming_unidirectional_streams_.GetSendValue();
+}
+
+bool QuicConfig::HasReceivedMaxIncomingUnidirectionalStreams() {
+  return max_incoming_unidirectional_streams_.HasReceivedValue();
+}
+
+uint32_t QuicConfig::ReceivedMaxIncomingUnidirectionalStreams() {
+  return max_incoming_unidirectional_streams_.GetReceivedValue();
 }
 
 bool QuicConfig::HasSetBytesForConnectionIdToSend() const {
@@ -664,7 +682,8 @@
   idle_network_timeout_seconds_.set(kMaximumIdleTimeoutSecs,
                                     kDefaultIdleTimeoutSecs);
   silent_close_.set(1, 0);
-  SetMaxIncomingDynamicStreamsToSend(kDefaultMaxStreamsPerConnection);
+  SetMaxIncomingBidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection);
+  SetMaxIncomingUnidirectionalStreamsToSend(kDefaultMaxStreamsPerConnection);
   max_time_before_crypto_handshake_ =
       QuicTime::Delta::FromSeconds(kMaxTimeForCryptoHandshakeSecs);
   max_idle_time_before_crypto_handshake_ =
@@ -676,10 +695,18 @@
   SetSupportMaxHeaderListSize();
 }
 
-void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const {
+void QuicConfig::ToHandshakeMessage(
+    CryptoHandshakeMessage* out,
+    QuicTransportVersion transport_version) const {
   idle_network_timeout_seconds_.ToHandshakeMessage(out);
   silent_close_.ToHandshakeMessage(out);
-  max_incoming_dynamic_streams_.ToHandshakeMessage(out);
+  // Do not need a version check here, max...bi... will encode
+  // as "MIDS" -- the max initial dynamic streams tag -- if
+  // doing some version other than IETF QUIC/V99.
+  max_incoming_bidirectional_streams_.ToHandshakeMessage(out);
+  if (transport_version == QUIC_VERSION_99) {
+    max_incoming_unidirectional_streams_.ToHandshakeMessage(out);
+  }
   bytes_for_connection_id_.ToHandshakeMessage(out);
   initial_round_trip_time_us_.ToHandshakeMessage(out);
   initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out);
@@ -707,7 +734,11 @@
         silent_close_.ProcessPeerHello(peer_hello, hello_type, error_details);
   }
   if (error == QUIC_NO_ERROR) {
-    error = max_incoming_dynamic_streams_.ProcessPeerHello(
+    error = max_incoming_bidirectional_streams_.ProcessPeerHello(
+        peer_hello, hello_type, error_details);
+  }
+  if (error == QUIC_NO_ERROR) {
+    error = max_incoming_unidirectional_streams_.ProcessPeerHello(
         peer_hello, hello_type, error_details);
   }
   if (error == QUIC_NO_ERROR) {
@@ -771,9 +802,9 @@
   params->initial_max_stream_data_uni.set_value(
       initial_stream_flow_control_window_bytes_.GetSendValue());
   params->initial_max_streams_bidi.set_value(
-      max_incoming_dynamic_streams_.GetSendValue());
+      max_incoming_bidirectional_streams_.GetSendValue());
   params->initial_max_streams_uni.set_value(
-      max_incoming_dynamic_streams_.GetSendValue());
+      max_incoming_unidirectional_streams_.GetSendValue());
   params->max_ack_delay.set_value(kDefaultDelayedAckTimeMs);
   params->disable_migration =
       connection_migration_disabled_.HasSendValue() &&
@@ -846,10 +877,12 @@
   initial_session_flow_control_window_bytes_.SetReceivedValue(
       std::min<uint64_t>(params.initial_max_data.value(),
                          std::numeric_limits<uint32_t>::max()));
-
-  max_incoming_dynamic_streams_.SetReceivedValue(
+  max_incoming_bidirectional_streams_.SetReceivedValue(
       std::min<uint64_t>(params.initial_max_streams_bidi.value(),
                          std::numeric_limits<uint32_t>::max()));
+  max_incoming_unidirectional_streams_.SetReceivedValue(
+      std::min<uint64_t>(params.initial_max_streams_uni.value(),
+                         std::numeric_limits<uint32_t>::max()));
 
   initial_stream_flow_control_window_bytes_.SetReceivedValue(
       std::min<uint64_t>(params.initial_max_stream_data_bidi_local.value(),
diff --git a/quic/core/quic_config.h b/quic/core/quic_config.h
index 8ad161d..ae15b6a 100644
--- a/quic/core/quic_config.h
+++ b/quic/core/quic_config.h
@@ -305,14 +305,24 @@
 
   bool SilentClose() const;
 
-  void SetMaxIncomingDynamicStreamsToSend(
-      uint32_t max_incoming_dynamic_streams);
+  // Configuration for the Google QUIC and IETF QUIC stream ID managers. Note
+  // that the naming is a bit  weird; it is from the perspective of the node
+  // generating (sending) the configuration and, thus, The "incoming" counts are
+  // the number of streams that the node sending the configuration is willing to
+  // accept and therefore the number that the node receiving the confguration
+  // can create .. the number of outbound streams that may be intiated..
+  // There are two sets, one for unidirectional streams and one for
+  // bidirectional. The bidirectional set also covers Google-QUICs
+  // dynamic stream count (which are bidirectional streams).
+  void SetMaxIncomingBidirectionalStreamsToSend(uint32_t max_streams);
+  uint32_t GetMaxIncomingBidirectionalStreamsToSend();
+  bool HasReceivedMaxIncomingBidirectionalStreams();
+  uint32_t ReceivedMaxIncomingBidirectionalStreams();
 
-  uint32_t GetMaxIncomingDynamicStreamsToSend();
-
-  bool HasReceivedMaxIncomingDynamicStreams();
-
-  uint32_t ReceivedMaxIncomingDynamicStreams();
+  void SetMaxIncomingUnidirectionalStreamsToSend(uint32_t max_streams);
+  uint32_t GetMaxIncomingUnidirectionalStreamsToSend();
+  bool HasReceivedMaxIncomingUnidirectionalStreams();
+  uint32_t ReceivedMaxIncomingUnidirectionalStreams();
 
   void set_max_time_before_crypto_handshake(
       QuicTime::Delta max_time_before_crypto_handshake) {
@@ -412,7 +422,8 @@
 
   // ToHandshakeMessage serialises the settings in this object as a series of
   // tags /value pairs and adds them to |out|.
-  void ToHandshakeMessage(CryptoHandshakeMessage* out) const;
+  void ToHandshakeMessage(CryptoHandshakeMessage* out,
+                          QuicTransportVersion transport_version) const;
 
   // Calls ProcessPeerHello on each negotiable parameter. On failure returns
   // the corresponding QuicErrorCode and sets detailed error in |error_details|.
@@ -456,8 +467,10 @@
   QuicNegotiableUint32 idle_network_timeout_seconds_;
   // Whether to use silent close.  Defaults to 0 (false) and is otherwise true.
   QuicNegotiableUint32 silent_close_;
-  // Maximum number of incoming dynamic streams that the connection can support.
-  QuicFixedUint32 max_incoming_dynamic_streams_;
+  // Maximum number of incoming dynamic streams that a Google QUIC connection
+  // can support or the maximum number of incoming bidirectional streams that
+  // an IETF QUIC connection can support.
+  QuicFixedUint32 max_incoming_bidirectional_streams_;
   // The number of bytes required for the connection ID.
   QuicFixedUint32 bytes_for_connection_id_;
   // Initial round trip time estimate in microseconds.
@@ -484,6 +497,10 @@
   // be created. This allows for CHLOs that are larger than a single
   // packet to be processed.
   QuicTagVector create_session_tag_indicators_;
+
+  // Maximum number of incoming unidirectional streams that the connection can
+  // support.
+  QuicFixedUint32 max_incoming_unidirectional_streams_;
 };
 
 }  // namespace quic
diff --git a/quic/core/quic_config_test.cc b/quic/core/quic_config_test.cc
index c18c520..84e89c1 100644
--- a/quic/core/quic_config_test.cc
+++ b/quic/core/quic_config_test.cc
@@ -21,12 +21,17 @@
 namespace test {
 namespace {
 
-class QuicConfigTest : public QuicTest {
+class QuicConfigTest : public QuicTestWithParam<QuicTransportVersion> {
  protected:
   QuicConfig config_;
 };
 
-TEST_F(QuicConfigTest, ToHandshakeMessage) {
+// Run all tests with all versions of QUIC.
+INSTANTIATE_TEST_SUITE_P(QuicConfigTests,
+                         QuicConfigTest,
+                         ::testing::ValuesIn(AllSupportedTransportVersions()));
+
+TEST_P(QuicConfigTest, ToHandshakeMessage) {
   config_.SetInitialStreamFlowControlWindowToSend(
       kInitialStreamFlowControlWindowForTest);
   config_.SetInitialSessionFlowControlWindowToSend(
@@ -34,7 +39,7 @@
   config_.SetIdleNetworkTimeout(QuicTime::Delta::FromSeconds(5),
                                 QuicTime::Delta::FromSeconds(2));
   CryptoHandshakeMessage msg;
-  config_.ToHandshakeMessage(&msg);
+  config_.ToHandshakeMessage(&msg, GetParam());
 
   uint32_t value;
   QuicErrorCode error = msg.GetUint32(kICSL, &value);
@@ -50,7 +55,7 @@
   EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value);
 }
 
-TEST_F(QuicConfigTest, ProcessClientHello) {
+TEST_P(QuicConfigTest, ProcessClientHello) {
   QuicConfig client_config;
   QuicTagVector cgst;
   cgst.push_back(kQBIC);
@@ -66,7 +71,7 @@
   copt.push_back(kTBBR);
   client_config.SetConnectionOptionsToSend(copt);
   CryptoHandshakeMessage msg;
-  client_config.ToHandshakeMessage(&msg);
+  client_config.ToHandshakeMessage(&msg, GetParam());
 
   std::string error_details;
   QuicTagVector initial_received_options;
@@ -96,7 +101,7 @@
             2 * kInitialSessionFlowControlWindowForTest);
 }
 
-TEST_F(QuicConfigTest, ProcessServerHello) {
+TEST_P(QuicConfigTest, ProcessServerHello) {
   QuicIpAddress host;
   host.FromString("127.0.3.1");
   const QuicSocketAddress kTestServerAddress = QuicSocketAddress(host, 1234);
@@ -115,7 +120,7 @@
   server_config.SetAlternateServerAddressToSend(kTestServerAddress);
   server_config.SetStatelessResetTokenToSend(kTestResetToken);
   CryptoHandshakeMessage msg;
-  server_config.ToHandshakeMessage(&msg);
+  server_config.ToHandshakeMessage(&msg, GetParam());
   std::string error_details;
   const QuicErrorCode error =
       config_.ProcessPeerHello(msg, SERVER, &error_details);
@@ -134,13 +139,13 @@
   EXPECT_EQ(kTestResetToken, config_.ReceivedStatelessResetToken());
 }
 
-TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) {
+TEST_P(QuicConfigTest, MissingOptionalValuesInCHLO) {
   CryptoHandshakeMessage msg;
   msg.SetValue(kICSL, 1);
 
   // Set all REQUIRED tags.
   msg.SetValue(kICSL, 1);
-  msg.SetValue(kMIDS, 1);
+  msg.SetValue(kMIBS, 1);
 
   // No error, as rest are optional.
   std::string error_details;
@@ -150,12 +155,12 @@
   EXPECT_TRUE(config_.negotiated());
 }
 
-TEST_F(QuicConfigTest, MissingOptionalValuesInSHLO) {
+TEST_P(QuicConfigTest, MissingOptionalValuesInSHLO) {
   CryptoHandshakeMessage msg;
 
   // Set all REQUIRED tags.
   msg.SetValue(kICSL, 1);
-  msg.SetValue(kMIDS, 1);
+  msg.SetValue(kMIBS, 1);
 
   // No error, as rest are optional.
   std::string error_details;
@@ -165,7 +170,7 @@
   EXPECT_TRUE(config_.negotiated());
 }
 
-TEST_F(QuicConfigTest, MissingValueInCHLO) {
+TEST_P(QuicConfigTest, MissingValueInCHLO) {
   // Server receives CHLO with missing kICSL.
   CryptoHandshakeMessage msg;
   std::string error_details;
@@ -174,7 +179,7 @@
   EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
 }
 
-TEST_F(QuicConfigTest, MissingValueInSHLO) {
+TEST_P(QuicConfigTest, MissingValueInSHLO) {
   // Client receives SHLO with missing kICSL.
   CryptoHandshakeMessage msg;
   std::string error_details;
@@ -183,21 +188,21 @@
   EXPECT_EQ(QUIC_CRYPTO_MESSAGE_PARAMETER_NOT_FOUND, error);
 }
 
-TEST_F(QuicConfigTest, OutOfBoundSHLO) {
+TEST_P(QuicConfigTest, OutOfBoundSHLO) {
   QuicConfig server_config;
   server_config.SetIdleNetworkTimeout(
       QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs),
       QuicTime::Delta::FromSeconds(2 * kMaximumIdleTimeoutSecs));
 
   CryptoHandshakeMessage msg;
-  server_config.ToHandshakeMessage(&msg);
+  server_config.ToHandshakeMessage(&msg, GetParam());
   std::string error_details;
   const QuicErrorCode error =
       config_.ProcessPeerHello(msg, SERVER, &error_details);
   EXPECT_EQ(QUIC_INVALID_NEGOTIATED_VALUE, error);
 }
 
-TEST_F(QuicConfigTest, InvalidFlowControlWindow) {
+TEST_P(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;
@@ -210,7 +215,7 @@
             config.GetInitialStreamFlowControlWindowToSend());
 }
 
-TEST_F(QuicConfigTest, HasClientSentConnectionOption) {
+TEST_P(QuicConfigTest, HasClientSentConnectionOption) {
   QuicConfig client_config;
   QuicTagVector copt;
   copt.push_back(kTBBR);
@@ -219,7 +224,7 @@
       kTBBR, Perspective::IS_CLIENT));
 
   CryptoHandshakeMessage msg;
-  client_config.ToHandshakeMessage(&msg);
+  client_config.ToHandshakeMessage(&msg, GetParam());
 
   std::string error_details;
   const QuicErrorCode error =
@@ -233,14 +238,14 @@
       config_.HasClientSentConnectionOption(kTBBR, Perspective::IS_SERVER));
 }
 
-TEST_F(QuicConfigTest, DontSendClientConnectionOptions) {
+TEST_P(QuicConfigTest, DontSendClientConnectionOptions) {
   QuicConfig client_config;
   QuicTagVector copt;
   copt.push_back(kTBBR);
   client_config.SetClientConnectionOptions(copt);
 
   CryptoHandshakeMessage msg;
-  client_config.ToHandshakeMessage(&msg);
+  client_config.ToHandshakeMessage(&msg, GetParam());
 
   std::string error_details;
   const QuicErrorCode error =
@@ -251,7 +256,7 @@
   EXPECT_FALSE(config_.HasReceivedConnectionOptions());
 }
 
-TEST_F(QuicConfigTest, HasClientRequestedIndependentOption) {
+TEST_P(QuicConfigTest, HasClientRequestedIndependentOption) {
   QuicConfig client_config;
   QuicTagVector client_opt;
   client_opt.push_back(kRENO);
@@ -267,7 +272,7 @@
       kTBBR, Perspective::IS_CLIENT));
 
   CryptoHandshakeMessage msg;
-  client_config.ToHandshakeMessage(&msg);
+  client_config.ToHandshakeMessage(&msg, GetParam());
 
   std::string error_details;
   const QuicErrorCode error =
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 0bae7d5..d80b55f 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -4988,7 +4988,7 @@
   client_config.SetIdleNetworkTimeout(
       QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs),
       QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs));
-  client_config.ToHandshakeMessage(&msg);
+  client_config.ToHandshakeMessage(&msg, connection_.transport_version());
   const QuicErrorCode error =
       config.ProcessPeerHello(msg, CLIENT, &error_details);
   EXPECT_EQ(QUIC_NO_ERROR, error);
@@ -5055,7 +5055,7 @@
   client_config.SetIdleNetworkTimeout(
       QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs),
       QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs));
-  client_config.ToHandshakeMessage(&msg);
+  client_config.ToHandshakeMessage(&msg, connection_.transport_version());
   const QuicErrorCode error =
       config.ProcessPeerHello(msg, CLIENT, &error_details);
   EXPECT_EQ(QUIC_NO_ERROR, error);
@@ -5111,7 +5111,7 @@
   client_config.SetIdleNetworkTimeout(
       QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs),
       QuicTime::Delta::FromSeconds(kDefaultIdleTimeoutSecs));
-  client_config.ToHandshakeMessage(&msg);
+  client_config.ToHandshakeMessage(&msg, connection_.transport_version());
   const QuicErrorCode error =
       config.ProcessPeerHello(msg, CLIENT, &error_details);
   EXPECT_EQ(QUIC_NO_ERROR, error);
diff --git a/quic/core/quic_crypto_client_handshaker.cc b/quic/core/quic_crypto_client_handshaker.cc
index 57d8c28..b0c26be 100644
--- a/quic/core/quic_crypto_client_handshaker.cc
+++ b/quic/core/quic_crypto_client_handshaker.cc
@@ -263,7 +263,7 @@
   DCHECK(session()->config() != nullptr);
   // Send all the options, regardless of whether we're sending an
   // inchoate or subsequent hello.
-  session()->config()->ToHandshakeMessage(&out);
+  session()->config()->ToHandshakeMessage(&out, session()->transport_version());
 
   if (!cached->IsComplete(session()->connection()->clock()->WallNow())) {
     crypto_config_->FillInchoateClientHello(
diff --git a/quic/core/quic_crypto_server_handshaker.cc b/quic/core/quic_crypto_server_handshaker.cc
index 9e46d6d..025e391 100644
--- a/quic/core/quic_crypto_server_handshaker.cc
+++ b/quic/core/quic_crypto_server_handshaker.cc
@@ -208,7 +208,7 @@
 
   session()->OnConfigNegotiated();
 
-  config->ToHandshakeMessage(reply.get());
+  config->ToHandshakeMessage(reply.get(), session()->transport_version());
 
   // Receiving a full CHLO implies the client is prepared to decrypt with
   // the new server write key.  We can start to encrypt with the new server
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index 0545fa6..d56689f 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -55,10 +55,13 @@
       config_(config),
       stream_id_manager_(this,
                          kDefaultMaxStreamsPerConnection,
-                         config_.GetMaxIncomingDynamicStreamsToSend()),
-      v99_streamid_manager_(this,
-                            kDefaultMaxStreamsPerConnection,
-                            config_.GetMaxIncomingDynamicStreamsToSend()),
+                         config_.GetMaxIncomingBidirectionalStreamsToSend()),
+      v99_streamid_manager_(
+          this,
+          kDefaultMaxStreamsPerConnection,
+          kDefaultMaxStreamsPerConnection,
+          config_.GetMaxIncomingBidirectionalStreamsToSend(),
+          config_.GetMaxIncomingUnidirectionalStreamsToSend()),
       num_dynamic_incoming_streams_(0),
       num_draining_incoming_streams_(0),
       num_outgoing_static_streams_(0),
@@ -973,23 +976,33 @@
 void QuicSession::OnConfigNegotiated() {
   connection_->SetFromConfig(config_);
 
-  uint32_t max_streams = 0;
-  if (config_.HasReceivedMaxIncomingDynamicStreams()) {
-    max_streams = config_.ReceivedMaxIncomingDynamicStreams();
-  }
-  QUIC_DVLOG(1) << "Setting max_open_outgoing_streams_ to " << max_streams;
   if (connection_->transport_version() == QUIC_VERSION_99) {
-    // TODO: When transport negotiation knows about bi- and uni- directional
-    // streams, this should be modified to indicate which one to the manager.
-    // Currently, BOTH are set to the same value.
-    // TODO(fkastenholz): AdjustMax is cognizant of the number of static streams
-    // and sets the maximum to be max_streams + number_of_statics. This should
-    // eventually be removed from IETF QUIC. -- Replace the call with
-    // ConfigureMaxOpen...
-    v99_streamid_manager_.AdjustMaxOpenOutgoingStreams(max_streams);
+    uint32_t max_streams = 0;
+    if (config_.HasReceivedMaxIncomingBidirectionalStreams()) {
+      max_streams = config_.ReceivedMaxIncomingBidirectionalStreams();
+    }
+    QUIC_DVLOG(1) << "Setting Bidirectional outgoing_max_streams_ to "
+                  << max_streams;
+    v99_streamid_manager_.AdjustMaxOpenOutgoingBidirectionalStreams(
+        max_streams);
+
+    max_streams = 0;
+    if (config_.HasReceivedMaxIncomingUnidirectionalStreams()) {
+      max_streams = config_.ReceivedMaxIncomingUnidirectionalStreams();
+    }
+    QUIC_DVLOG(1) << "Setting Unidirectional outgoing_max_streams_ to "
+                  << max_streams;
+    v99_streamid_manager_.AdjustMaxOpenOutgoingUnidirectionalStreams(
+        max_streams);
   } else {
+    uint32_t max_streams = 0;
+    if (config_.HasReceivedMaxIncomingBidirectionalStreams()) {
+      max_streams = config_.ReceivedMaxIncomingBidirectionalStreams();
+    }
+    QUIC_DVLOG(1) << "Setting max_open_outgoing_streams_ to " << max_streams;
     stream_id_manager_.set_max_open_outgoing_streams(max_streams);
   }
+
   if (perspective() == Perspective::IS_SERVER) {
     if (config_.HasReceivedConnectionOptions()) {
       // The following variations change the initial receive flow control
@@ -1014,17 +1027,19 @@
     config_.SetStatelessResetTokenToSend(GetStatelessResetToken());
   }
 
-  // A small number of additional incoming streams beyond the limit should be
-  // allowed. This helps avoid early connection termination when FIN/RSTs for
-  // old streams are lost or arrive out of order.
-  // Use a minimum number of additional streams, or a percentage increase,
-  // whichever is larger.
-  uint32_t max_incoming_streams_to_send =
-      config_.GetMaxIncomingDynamicStreamsToSend();
   if (connection_->transport_version() == QUIC_VERSION_99) {
-    v99_streamid_manager_.SetMaxOpenIncomingStreams(
-        max_incoming_streams_to_send);
+    v99_streamid_manager_.SetMaxOpenIncomingBidirectionalStreams(
+        config_.GetMaxIncomingBidirectionalStreamsToSend());
+    v99_streamid_manager_.SetMaxOpenIncomingUnidirectionalStreams(
+        config_.GetMaxIncomingUnidirectionalStreamsToSend());
   } else {
+    // A small number of additional incoming streams beyond the limit should be
+    // allowed. This helps avoid early connection termination when FIN/RSTs for
+    // old streams are lost or arrive out of order.
+    // Use a minimum number of additional streams, or a percentage increase,
+    // whichever is larger.
+    uint32_t max_incoming_streams_to_send =
+        config_.GetMaxIncomingBidirectionalStreamsToSend();
     uint32_t max_incoming_streams =
         std::max(max_incoming_streams_to_send + kMaxStreamsMinimumIncrement,
                  static_cast<uint32_t>(max_incoming_streams_to_send *
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index f16477c..7da8ec3 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -419,6 +419,10 @@
   static void RecordConnectionCloseAtServer(QuicErrorCode error,
                                             ConnectionCloseSource source);
 
+  inline QuicTransportVersion transport_version() const {
+    return connection_->transport_version();
+  }
+
  protected:
   using StaticStreamMap = QuicSmallMap<QuicStreamId, QuicStream*, 2>;
 
diff --git a/quic/core/quic_session_test.cc b/quic/core/quic_session_test.cc
index 8f418c0..95bb6d4 100644
--- a/quic/core/quic_session_test.cc
+++ b/quic/core/quic_session_test.cc
@@ -69,7 +69,8 @@
         kInitialStreamFlowControlWindowForTest);
     session()->config()->SetInitialSessionFlowControlWindowToSend(
         kInitialSessionFlowControlWindowForTest);
-    session()->config()->ToHandshakeMessage(&msg);
+    session()->config()->ToHandshakeMessage(
+        &msg, session()->connection()->transport_version());
     const QuicErrorCode error =
         session()->config()->ProcessPeerHello(msg, CLIENT, &error_details);
     EXPECT_EQ(QUIC_NO_ERROR, error);
@@ -707,31 +708,86 @@
 TEST_P(QuicSessionTestServer, ManyAvailableBidirectionalStreams) {
   // When max_open_streams_ is 200, should be able to create 200 streams
   // out-of-order, that is, creating the one with the largest stream ID first.
-  QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200);
+  if (transport_version() == QUIC_VERSION_99) {
+    QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_, 200);
+    // Smaller limit on unidirectional streams to help detect crossed wires.
+    QuicSessionPeer::SetMaxOpenIncomingUnidirectionalStreams(&session_, 50);
+  } else {
+    QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200);
+  }
+  // Create a stream at the start of the range.
   QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0);
-  // Create one stream.
   EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id));
-  EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
 
   // Create the largest stream ID of a threatened total of 200 streams.
   // GetNth... starts at 0, so for 200 streams, get the 199th.
+  EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
   EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(
                          GetNthClientInitiatedBidirectionalId(199)));
+
+  if (transport_version() == QUIC_VERSION_99) {
+    // If IETF QUIC, check to make sure that creating bidirectional
+    // streams does not mess up the unidirectional streams.
+    stream_id = GetNthClientInitiatedUnidirectionalId(0);
+    EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id));
+    // Now try to get the last possible unidirectional stream.
+    EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(
+                           GetNthClientInitiatedUnidirectionalId(49)));
+    // and this should fail because it exceeds the unidirectional limit
+    // (but not the bi-)
+    EXPECT_CALL(
+        *connection_,
+        CloseConnection(QUIC_INVALID_STREAM_ID,
+                        "Stream id 798 would exceed stream count limit 50",
+                        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET
+
+                        ))
+        .Times(1);
+    EXPECT_EQ(nullptr, session_.GetOrCreateDynamicStream(
+                           GetNthClientInitiatedUnidirectionalId(199)));
+  }
 }
 
 TEST_P(QuicSessionTestServer, ManyAvailableUnidirectionalStreams) {
   // When max_open_streams_ is 200, should be able to create 200 streams
   // out-of-order, that is, creating the one with the largest stream ID first.
-  QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200);
-  QuicStreamId stream_id = GetNthClientInitiatedUnidirectionalId(0);
+  if (transport_version() == QUIC_VERSION_99) {
+    QuicSessionPeer::SetMaxOpenIncomingUnidirectionalStreams(&session_, 200);
+    // Smaller limit on unidirectional streams to help detect crossed wires.
+    QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_, 50);
+  } else {
+    QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200);
+  }
   // Create one stream.
+  QuicStreamId stream_id = GetNthClientInitiatedUnidirectionalId(0);
   EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id));
-  EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
 
   // Create the largest stream ID of a threatened total of 200 streams.
   // GetNth... starts at 0, so for 200 streams, get the 199th.
+  EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0);
   EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(
                          GetNthClientInitiatedUnidirectionalId(199)));
+  if (transport_version() == QUIC_VERSION_99) {
+    // If IETF QUIC, check to make sure that creating unidirectional
+    // streams does not mess up the bidirectional streams.
+    stream_id = GetNthClientInitiatedBidirectionalId(0);
+    EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(stream_id));
+    // Now try to get the last possible bidirectional stream.
+    EXPECT_NE(nullptr, session_.GetOrCreateDynamicStream(
+                           GetNthClientInitiatedBidirectionalId(49)));
+    // and this should fail because it exceeds the bnidirectional limit
+    // (but not the uni-)
+    EXPECT_CALL(
+        *connection_,
+        CloseConnection(QUIC_INVALID_STREAM_ID,
+                        "Stream id 800 would exceed stream count limit 51",
+                        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET
+
+                        ))
+        .Times(1);
+    EXPECT_EQ(nullptr, session_.GetOrCreateDynamicStream(
+                           GetNthClientInitiatedBidirectionalId(199)));
+  }
 }
 
 TEST_P(QuicSessionTestServer, DebugDFatalIfMarkingClosedStreamWriteBlocked) {
@@ -1304,7 +1360,7 @@
     QuicStreamOffset offset = crypto_stream->stream_bytes_written();
     QuicConfig config;
     CryptoHandshakeMessage crypto_message;
-    config.ToHandshakeMessage(&crypto_message);
+    config.ToHandshakeMessage(&crypto_message, transport_version());
     crypto_stream->SendHandshakeMessage(crypto_message);
     char buf[1000];
     QuicDataWriter writer(1000, buf, NETWORK_BYTE_ORDER);
@@ -1528,7 +1584,12 @@
   // with a FIN or RST then we send an RST to refuse streams. For V99 the
   // connection is closed.
   const QuicStreamId kMaxStreams = 5;
-  QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+  if (transport_version() == QUIC_VERSION_99) {
+    QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_,
+                                                            kMaxStreams);
+  } else {
+    QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+  }
   const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0);
   const QuicStreamId kFinalStreamId =
       GetNthClientInitiatedBidirectionalId(kMaxStreams);
@@ -1653,7 +1714,12 @@
   }
   EXPECT_CALL(*connection_, OnStreamReset(_, QUIC_REFUSED_STREAM)).Times(0);
   const QuicStreamId kMaxStreams = 5;
-  QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+  if (transport_version() == QUIC_VERSION_99) {
+    QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_,
+                                                            kMaxStreams);
+  } else {
+    QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams);
+  }
 
   // Create kMaxStreams + 1 data streams, and mark them draining.
   const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0);
diff --git a/quic/core/quic_stream_id_manager.cc b/quic/core/quic_stream_id_manager.cc
index f68401a..94a8487 100644
--- a/quic/core/quic_stream_id_manager.cc
+++ b/quic/core/quic_stream_id_manager.cc
@@ -130,12 +130,13 @@
   outgoing_max_streams_ = std::min(
       static_cast<QuicStreamCount>(max_open_streams),
       QuicUtils::GetMaxStreamCount(unidirectional_, session_->perspective()));
-
   return true;
 }
 
 void QuicStreamIdManager::SetMaxOpenOutgoingStreams(size_t max_open_streams) {
   QUIC_BUG_IF(!using_default_max_streams_);
+  // TODO(fkastenholz): when static streams are removed from I-Quic, this
+  // should be revised to invoke ConfigureMaxOpen...
   AdjustMaxOpenOutgoingStreams(max_open_streams);
 }
 
@@ -143,8 +144,8 @@
 // including static streams. If the new stream limit wraps, will peg
 // the limit at the implementation max.
 // TODO(fkastenholz): AdjustMax is cognizant of the number of static streams and
-// sets the maximum to be max_streams + number_of_statics. This should
-// eventually be removed from IETF QUIC.
+// sets the maximum to be max_streams + number_of_statics. This should be
+// removed from IETF QUIC when static streams are gone.
 void QuicStreamIdManager::AdjustMaxOpenOutgoingStreams(
     size_t max_open_streams) {
   if ((outgoing_static_stream_count_ + max_open_streams) < max_open_streams) {
diff --git a/quic/core/uber_quic_stream_id_manager.cc b/quic/core/uber_quic_stream_id_manager.cc
index e2261a0..75f7c6a 100644
--- a/quic/core/uber_quic_stream_id_manager.cc
+++ b/quic/core/uber_quic_stream_id_manager.cc
@@ -11,17 +11,19 @@
 
 UberQuicStreamIdManager::UberQuicStreamIdManager(
     QuicSession* session,
-    QuicStreamCount max_open_outgoing_streams,
-    QuicStreamCount max_open_incoming_streams)
+    QuicStreamCount max_open_outgoing_bidirectional_streams,
+    QuicStreamCount max_open_outgoing_unidirectional_streams,
+    QuicStreamCount max_open_incoming_bidirectional_streams,
+    QuicStreamCount max_open_incoming_unidirectional_streams)
     : bidirectional_stream_id_manager_(session,
                                        /*unidirectional=*/false,
-                                       max_open_outgoing_streams,
-                                       max_open_incoming_streams),
-      unidirectional_stream_id_manager_(session,
-                                        /*unidirectional=*/true,
-                                        max_open_outgoing_streams,
-                                        max_open_incoming_streams) {}
-
+                                       max_open_outgoing_bidirectional_streams,
+                                       max_open_incoming_bidirectional_streams),
+      unidirectional_stream_id_manager_(
+          session,
+          /*unidirectional=*/true,
+          max_open_outgoing_unidirectional_streams,
+          max_open_incoming_unidirectional_streams) {}
 void UberQuicStreamIdManager::RegisterStaticStream(QuicStreamId id) {
   if (QuicUtils::IsBidirectionalStreamId(id)) {
     bidirectional_stream_id_manager_.RegisterStaticStream(id);
@@ -30,35 +32,42 @@
   unidirectional_stream_id_manager_.RegisterStaticStream(id);
 }
 
-void UberQuicStreamIdManager::ConfigureMaxOpenOutgoingStreams(
+void UberQuicStreamIdManager::AdjustMaxOpenOutgoingUnidirectionalStreams(
     size_t max_streams) {
-  // TODO(fkastenholz): When transport configuration negotiation knows uni- vs
-  // bi- directionality, this method needs modifying to select the correct
-  // manager to configure.
-  bidirectional_stream_id_manager_.ConfigureMaxOpenOutgoingStreams(max_streams);
-  unidirectional_stream_id_manager_.ConfigureMaxOpenOutgoingStreams(
-      max_streams);
+  unidirectional_stream_id_manager_.AdjustMaxOpenOutgoingStreams(max_streams);
+}
+void UberQuicStreamIdManager::AdjustMaxOpenOutgoingBidirectionalStreams(
+    size_t max_streams) {
+  bidirectional_stream_id_manager_.AdjustMaxOpenOutgoingStreams(max_streams);
 }
 
-void UberQuicStreamIdManager::AdjustMaxOpenOutgoingStreams(size_t max_streams) {
-  // TODO(fkastenholz): When transport configuration negotiation knows uni- vs
-  // bi- directionality, this method needs modifying to select the correct
-  // manager to configure.
-  bidirectional_stream_id_manager_.AdjustMaxOpenOutgoingStreams(max_streams);
-  unidirectional_stream_id_manager_.AdjustMaxOpenOutgoingStreams(max_streams);
+void UberQuicStreamIdManager::ConfigureMaxOpenOutgoingBidirectionalStreams(
+    size_t max_streams) {
+  bidirectional_stream_id_manager_.ConfigureMaxOpenOutgoingStreams(max_streams);
+}
+void UberQuicStreamIdManager::ConfigureMaxOpenOutgoingUnidirectionalStreams(
+    size_t max_streams) {
+  unidirectional_stream_id_manager_.ConfigureMaxOpenOutgoingStreams(
+      max_streams);
 }
 
 // TODO(fkastenholz): SetMax is cognizant of the number of static streams and
 // sets the maximum to be max_streams + number_of_statics. This should
 // eventually be removed from IETF QUIC.
-void UberQuicStreamIdManager::SetMaxOpenOutgoingStreams(
+void UberQuicStreamIdManager::SetMaxOpenOutgoingBidirectionalStreams(
     size_t max_open_streams) {
   bidirectional_stream_id_manager_.SetMaxOpenOutgoingStreams(max_open_streams);
+}
+void UberQuicStreamIdManager::SetMaxOpenOutgoingUnidirectionalStreams(
+    size_t max_open_streams) {
   unidirectional_stream_id_manager_.SetMaxOpenOutgoingStreams(max_open_streams);
 }
-void UberQuicStreamIdManager::SetMaxOpenIncomingStreams(
+void UberQuicStreamIdManager::SetMaxOpenIncomingBidirectionalStreams(
     size_t max_open_streams) {
   bidirectional_stream_id_manager_.SetMaxOpenIncomingStreams(max_open_streams);
+}
+void UberQuicStreamIdManager::SetMaxOpenIncomingUnidirectionalStreams(
+    size_t max_open_streams) {
   unidirectional_stream_id_manager_.SetMaxOpenIncomingStreams(max_open_streams);
 }
 
diff --git a/quic/core/uber_quic_stream_id_manager.h b/quic/core/uber_quic_stream_id_manager.h
index 41fa12b..288f0c0 100644
--- a/quic/core/uber_quic_stream_id_manager.h
+++ b/quic/core/uber_quic_stream_id_manager.h
@@ -20,9 +20,12 @@
 // unidirectional stream IDs, respectively.
 class QUIC_EXPORT_PRIVATE UberQuicStreamIdManager {
  public:
-  UberQuicStreamIdManager(QuicSession* session,
-                          QuicStreamCount max_open_outgoing_streams,
-                          QuicStreamCount max_open_incoming_streams);
+  UberQuicStreamIdManager(
+      QuicSession* session,
+      QuicStreamCount max_open_outgoing_bidirectional_streams,
+      QuicStreamCount max_open_outgoing_unidirectional_streams,
+      QuicStreamCount max_open_incoming_bidirectional_streams,
+      QuicStreamCount max_open_incoming_unidirectional_streams);
 
   // Called when a stream with |stream_id| is registered as a static stream.
   void RegisterStaticStream(QuicStreamId id);
@@ -30,21 +33,26 @@
   // Sets the maximum outgoing stream count as a result of doing the transport
   // configuration negotiation. Forces the limit to max_streams, regardless of
   // static streams.
-  void ConfigureMaxOpenOutgoingStreams(size_t max_streams);
+  void ConfigureMaxOpenOutgoingBidirectionalStreams(size_t max_streams);
+  void ConfigureMaxOpenOutgoingUnidirectionalStreams(size_t max_streams);
 
   // Sets the limits to max_open_streams + number of static streams
   // in existence. SetMaxOpenOutgoingStreams will QUIC_BUG if it is called
-  // after getting the first MAX_STREAMS frame.
+  // after getting the first MAX_STREAMS frame or the transport configuration
+  // was done.
   // TODO(fkastenholz): SetMax is cognizant of the number of static streams and
   // sets the maximum to be max_streams + number_of_statics. This should
   // eventually be removed from IETF QUIC.
-  void SetMaxOpenOutgoingStreams(size_t max_open_streams);
-  void SetMaxOpenIncomingStreams(size_t max_open_streams);
+  void SetMaxOpenOutgoingBidirectionalStreams(size_t max_open_streams);
+  void SetMaxOpenOutgoingUnidirectionalStreams(size_t max_open_streams);
+  void SetMaxOpenIncomingBidirectionalStreams(size_t max_open_streams);
+  void SetMaxOpenIncomingUnidirectionalStreams(size_t max_open_streams);
 
   // Sets the outgoing stream count to the number of static streams + max
   // outgoing streams.  Unlike SetMaxOpenOutgoingStreams, this method will
   // not QUIC_BUG if called after getting  the first MAX_STREAMS frame.
-  void AdjustMaxOpenOutgoingStreams(size_t max_streams);
+  void AdjustMaxOpenOutgoingBidirectionalStreams(size_t max_streams);
+  void AdjustMaxOpenOutgoingUnidirectionalStreams(size_t max_streams);
 
   // Returns true if next outgoing bidirectional stream ID can be allocated.
   bool CanOpenNextOutgoingBidirectionalStream();
@@ -58,7 +66,7 @@
   // Returns the next outgoing unidirectional stream id.
   QuicStreamId GetNextOutgoingUnidirectionalStreamId();
 
-  // Returns true if allow to open the incoming |id|.
+  // Returns true if the incoming |id| is within the limit.
   bool MaybeIncreaseLargestPeerStreamId(QuicStreamId id);
 
   // Called when |id| is released.
diff --git a/quic/core/uber_quic_stream_id_manager_test.cc b/quic/core/uber_quic_stream_id_manager_test.cc
index ed08d8f..b9420bf 100644
--- a/quic/core/uber_quic_stream_id_manager_test.cc
+++ b/quic/core/uber_quic_stream_id_manager_test.cc
@@ -60,6 +60,28 @@
            kV99StreamIdIncrement * n;
   }
 
+  // TODO(fkastenholz): Existing tests can use these helper functions.
+  QuicStreamId GetNthPeerInitiatedBidirectionalStreamId(int n) {
+    return ((GetParam() == Perspective::IS_SERVER)
+                ? GetNthClientInitiatedBidirectionalId(n)
+                : GetNthServerInitiatedBidirectionalId(n));
+  }
+  QuicStreamId GetNthPeerInitiatedUnidirectionalStreamId(int n) {
+    return ((GetParam() == Perspective::IS_SERVER)
+                ? GetNthClientInitiatedUnidirectionalId(n)
+                : GetNthServerInitiatedUnidirectionalId(n));
+  }
+  QuicStreamId GetNthSelfInitiatedBidirectionalStreamId(int n) {
+    return ((GetParam() == Perspective::IS_CLIENT)
+                ? GetNthClientInitiatedBidirectionalId(n)
+                : GetNthServerInitiatedBidirectionalId(n));
+  }
+  QuicStreamId GetNthSelfInitiatedUnidirectionalStreamId(int n) {
+    return ((GetParam() == Perspective::IS_CLIENT)
+                ? GetNthClientInitiatedUnidirectionalId(n)
+                : GetNthServerInitiatedUnidirectionalId(n));
+  }
+
   QuicStreamId StreamCountToId(QuicStreamCount stream_count,
                                Perspective perspective,
                                bool bidirectional) {
@@ -129,17 +151,39 @@
 
 TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreams) {
   const size_t kNumMaxOutgoingStream = 123;
-  manager_->SetMaxOpenOutgoingStreams(kNumMaxOutgoingStream);
+  // Set the uni- and bi- directional limits to different values to ensure
+  // that they are managed separately.
+  manager_->SetMaxOpenOutgoingBidirectionalStreams(kNumMaxOutgoingStream);
+  manager_->SetMaxOpenOutgoingUnidirectionalStreams(kNumMaxOutgoingStream + 1);
   EXPECT_EQ(kNumMaxOutgoingStream,
             manager_->max_allowed_outgoing_bidirectional_streams());
-  EXPECT_EQ(kNumMaxOutgoingStream,
+  EXPECT_EQ(kNumMaxOutgoingStream + 1,
             manager_->max_allowed_outgoing_unidirectional_streams());
+  // Check that, for each directionality, we can open the correct number of
+  // streams.
+  int i = kNumMaxOutgoingStream;
+  while (i) {
+    EXPECT_TRUE(manager_->CanOpenNextOutgoingBidirectionalStream());
+    manager_->GetNextOutgoingBidirectionalStreamId();
+    EXPECT_TRUE(manager_->CanOpenNextOutgoingUnidirectionalStream());
+    manager_->GetNextOutgoingUnidirectionalStreamId();
+    i--;
+  }
+  // One more unidirectional
+  EXPECT_TRUE(manager_->CanOpenNextOutgoingUnidirectionalStream());
+  manager_->GetNextOutgoingUnidirectionalStreamId();
+
+  // Both should be exhausted...
+  EXPECT_FALSE(manager_->CanOpenNextOutgoingUnidirectionalStream());
+  EXPECT_FALSE(manager_->CanOpenNextOutgoingBidirectionalStream());
 }
 
 TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenIncomingStreams) {
   const size_t kNumMaxIncomingStreams = 456;
-  manager_->SetMaxOpenIncomingStreams(kNumMaxIncomingStreams);
-  EXPECT_EQ(kNumMaxIncomingStreams,
+  manager_->SetMaxOpenIncomingUnidirectionalStreams(kNumMaxIncomingStreams);
+  // Do +1 for bidirectional to ensure that uni- and bi- get properly set.
+  manager_->SetMaxOpenIncomingBidirectionalStreams(kNumMaxIncomingStreams + 1);
+  EXPECT_EQ(kNumMaxIncomingStreams + 1,
             manager_->GetMaxAllowdIncomingBidirectionalStreams());
   EXPECT_EQ(kNumMaxIncomingStreams,
             manager_->GetMaxAllowdIncomingUnidirectionalStreams());
@@ -147,6 +191,24 @@
             manager_->advertised_max_allowed_incoming_bidirectional_streams());
   EXPECT_EQ(manager_->actual_max_allowed_incoming_unidirectional_streams(),
             manager_->advertised_max_allowed_incoming_unidirectional_streams());
+  // Make sure that we can create kNumMaxIncomingStreams incoming unidirectional
+  // streams and kNumMaxIncomingStreams+1 incoming bidirectional streams.
+  size_t i;
+  for (i = 0; i < kNumMaxIncomingStreams; i++) {
+    EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+        GetNthPeerInitiatedUnidirectionalStreamId(i)));
+    EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+        GetNthPeerInitiatedBidirectionalStreamId(i)));
+  }
+  // Should be able to open the next bidirectional stream
+  EXPECT_TRUE(manager_->MaybeIncreaseLargestPeerStreamId(
+      GetNthPeerInitiatedBidirectionalStreamId(i)));
+
+  // We should have exhausted the counts, the next streams should fail
+  EXPECT_FALSE(manager_->MaybeIncreaseLargestPeerStreamId(
+      GetNthPeerInitiatedUnidirectionalStreamId(i)));
+  EXPECT_FALSE(manager_->MaybeIncreaseLargestPeerStreamId(
+      GetNthPeerInitiatedBidirectionalStreamId(i + 1)));
 }
 
 TEST_P(UberQuicStreamIdManagerTest, GetNextOutgoingStreamId) {
@@ -339,6 +401,44 @@
   }
 }
 
+TEST_P(UberQuicStreamIdManagerTest, SetMaxOpenOutgoingStreamsPlusFrame) {
+  const size_t kNumMaxOutgoingStream = 123;
+  // Set the uni- and bi- directional limits to different values to ensure
+  // that they are managed separately.
+  manager_->SetMaxOpenOutgoingBidirectionalStreams(kNumMaxOutgoingStream);
+  manager_->SetMaxOpenOutgoingUnidirectionalStreams(kNumMaxOutgoingStream + 1);
+  EXPECT_EQ(kNumMaxOutgoingStream,
+            manager_->max_allowed_outgoing_bidirectional_streams());
+  EXPECT_EQ(kNumMaxOutgoingStream + 1,
+            manager_->max_allowed_outgoing_unidirectional_streams());
+  // Check that, for each directionality, we can open the correct number of
+  // streams.
+  int i = kNumMaxOutgoingStream;
+  while (i) {
+    EXPECT_TRUE(manager_->CanOpenNextOutgoingBidirectionalStream());
+    manager_->GetNextOutgoingBidirectionalStreamId();
+    EXPECT_TRUE(manager_->CanOpenNextOutgoingUnidirectionalStream());
+    manager_->GetNextOutgoingUnidirectionalStreamId();
+    i--;
+  }
+  // One more unidirectional
+  EXPECT_TRUE(manager_->CanOpenNextOutgoingUnidirectionalStream());
+  manager_->GetNextOutgoingUnidirectionalStreamId();
+
+  // Both should be exhausted...
+  EXPECT_FALSE(manager_->CanOpenNextOutgoingUnidirectionalStream());
+  EXPECT_FALSE(manager_->CanOpenNextOutgoingBidirectionalStream());
+
+  // Now cons a MAX STREAMS frame for unidirectional streams to raise
+  // the limit.
+  QuicMaxStreamsFrame frame(1, kNumMaxOutgoingStream + 10,
+                            /*unidirectional=*/true);
+  manager_->OnMaxStreamsFrame(frame);
+  // We now should be able to get another uni- stream, but not a bi.
+  EXPECT_TRUE(manager_->CanOpenNextOutgoingUnidirectionalStream());
+  EXPECT_FALSE(manager_->CanOpenNextOutgoingBidirectionalStream());
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic
diff --git a/quic/quartc/quartc_factory.cc b/quic/quartc/quartc_factory.cc
index cb457ff..5b83a46 100644
--- a/quic/quartc/quartc_factory.cc
+++ b/quic/quartc/quartc_factory.cc
@@ -190,7 +190,7 @@
   // incomplete streams, but targets 1 second for recovery. Increasing the
   // number of open streams gives sufficient headroom to recover before QUIC
   // refuses new streams.
-  quic_config.SetMaxIncomingDynamicStreamsToSend(1000);
+  quic_config.SetMaxIncomingBidirectionalStreamsToSend(1000);
 
   return quic_config;
 }
diff --git a/quic/test_tools/quic_config_peer.cc b/quic/test_tools/quic_config_peer.cc
index f5a5b31..f9ce04f 100644
--- a/quic/test_tools/quic_config_peer.cc
+++ b/quic/test_tools/quic_config_peer.cc
@@ -45,10 +45,16 @@
 }
 
 // static
-void QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(
+void QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams(
     QuicConfig* config,
     uint32_t max_streams) {
-  config->max_incoming_dynamic_streams_.SetReceivedValue(max_streams);
+  config->max_incoming_bidirectional_streams_.SetReceivedValue(max_streams);
+}
+// static
+void QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams(
+    QuicConfig* config,
+    uint32_t max_streams) {
+  config->max_incoming_unidirectional_streams_.SetReceivedValue(max_streams);
 }
 
 // static
diff --git a/quic/test_tools/quic_config_peer.h b/quic/test_tools/quic_config_peer.h
index 8869d27..7faee7e 100644
--- a/quic/test_tools/quic_config_peer.h
+++ b/quic/test_tools/quic_config_peer.h
@@ -33,8 +33,10 @@
 
   static void SetReceivedDisableConnectionMigration(QuicConfig* config);
 
-  static void SetReceivedMaxIncomingDynamicStreams(QuicConfig* config,
-                                                   uint32_t max_streams);
+  static void SetReceivedMaxIncomingBidirectionalStreams(QuicConfig* config,
+                                                         uint32_t max_streams);
+  static void SetReceivedMaxIncomingUnidirectionalStreams(QuicConfig* config,
+                                                          uint32_t max_streams);
 
   static void SetConnectionOptionsToSend(QuicConfig* config,
                                          const QuicTagVector& options);
diff --git a/quic/test_tools/quic_session_peer.cc b/quic/test_tools/quic_session_peer.cc
index 176e22a..0683fd7 100644
--- a/quic/test_tools/quic_session_peer.cc
+++ b/quic/test_tools/quic_session_peer.cc
@@ -39,23 +39,73 @@
 void QuicSessionPeer::SetMaxOpenIncomingStreams(QuicSession* session,
                                                 uint32_t max_streams) {
   if (session->connection()->transport_version() == QUIC_VERSION_99) {
-    session->v99_streamid_manager_.SetMaxOpenIncomingStreams(max_streams);
+    QUIC_BUG << "SetmaxOpenIncomingStreams deprecated for IETF QUIC/V99";
+    session->v99_streamid_manager_.SetMaxOpenIncomingUnidirectionalStreams(
+        max_streams);
+    session->v99_streamid_manager_.SetMaxOpenIncomingBidirectionalStreams(
+        max_streams);
     return;
   }
   session->stream_id_manager_.set_max_open_incoming_streams(max_streams);
 }
 
 // static
+void QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(
+    QuicSession* session,
+    uint32_t max_streams) {
+  DCHECK_EQ(QUIC_VERSION_99, session->connection()->transport_version())
+      << "SetmaxOpenIncomingBidirectionalStreams not supported for Google "
+         "QUIC/not-V99";
+  session->v99_streamid_manager_.SetMaxOpenIncomingBidirectionalStreams(
+      max_streams);
+}
+// static
+void QuicSessionPeer::SetMaxOpenIncomingUnidirectionalStreams(
+    QuicSession* session,
+    uint32_t max_streams) {
+  DCHECK_EQ(QUIC_VERSION_99, session->connection()->transport_version())
+      << "SetmaxOpenIncomingUnidirectionalStreams not supported for Google "
+         "QUIC/not-V99";
+  session->v99_streamid_manager_.SetMaxOpenIncomingUnidirectionalStreams(
+      max_streams);
+}
+
+// static
 void QuicSessionPeer::SetMaxOpenOutgoingStreams(QuicSession* session,
                                                 uint32_t max_streams) {
   if (session->connection()->transport_version() == QUIC_VERSION_99) {
-    session->v99_streamid_manager_.SetMaxOpenOutgoingStreams(max_streams);
+    QUIC_BUG << "SetmaxOpenOutgoingStreams deprecated for IETF QUIC/V99";
+    session->v99_streamid_manager_.SetMaxOpenOutgoingUnidirectionalStreams(
+        max_streams);
+    session->v99_streamid_manager_.SetMaxOpenOutgoingBidirectionalStreams(
+        max_streams);
     return;
   }
   session->stream_id_manager_.set_max_open_outgoing_streams(max_streams);
 }
 
 // static
+void QuicSessionPeer::SetMaxOpenOutgoingBidirectionalStreams(
+    QuicSession* session,
+    uint32_t max_streams) {
+  DCHECK_EQ(QUIC_VERSION_99, session->connection()->transport_version())
+      << "SetmaxOpenOutgoingBidirectionalStreams not supported for Google "
+         "QUIC/not-V99";
+  session->v99_streamid_manager_.SetMaxOpenOutgoingBidirectionalStreams(
+      max_streams);
+}
+// static
+void QuicSessionPeer::SetMaxOpenOutgoingUnidirectionalStreams(
+    QuicSession* session,
+    uint32_t max_streams) {
+  DCHECK_EQ(QUIC_VERSION_99, session->connection()->transport_version())
+      << "SetmaxOpenOutgoingUnidirectionalStreams not supported for Google "
+         "QUIC/not-V99";
+  session->v99_streamid_manager_.SetMaxOpenOutgoingUnidirectionalStreams(
+      max_streams);
+}
+
+// static
 QuicCryptoStream* QuicSessionPeer::GetMutableCryptoStream(
     QuicSession* session) {
   return session->GetMutableCryptoStream();
diff --git a/quic/test_tools/quic_session_peer.h b/quic/test_tools/quic_session_peer.h
index 30e358f..994a36c 100644
--- a/quic/test_tools/quic_session_peer.h
+++ b/quic/test_tools/quic_session_peer.h
@@ -32,10 +32,24 @@
       QuicSession* session);
   static void SetNextOutgoingBidirectionalStreamId(QuicSession* session,
                                                    QuicStreamId id);
+  // Following is only for Google-QUIC, will QUIC_BUG if called for IETF
+  // QUIC.
   static void SetMaxOpenIncomingStreams(QuicSession* session,
                                         uint32_t max_streams);
+  // Following two are only for IETF-QUIC, will QUIC_BUG if called for Google
+  // QUIC.
+  static void SetMaxOpenIncomingBidirectionalStreams(QuicSession* session,
+                                                     uint32_t max_streams);
+  static void SetMaxOpenIncomingUnidirectionalStreams(QuicSession* session,
+                                                      uint32_t max_streams);
+
   static void SetMaxOpenOutgoingStreams(QuicSession* session,
                                         uint32_t max_streams);
+  static void SetMaxOpenOutgoingBidirectionalStreams(QuicSession* session,
+                                                     uint32_t max_streams);
+  static void SetMaxOpenOutgoingUnidirectionalStreams(QuicSession* session,
+                                                      uint32_t max_streams);
+
   static QuicCryptoStream* GetMutableCryptoStream(QuicSession* session);
   static QuicWriteBlockedList* GetWriteBlockedStreams(QuicSession* session);
   static QuicStream* GetOrCreateDynamicStream(QuicSession* session,
diff --git a/quic/test_tools/quic_test_utils.cc b/quic/test_tools/quic_test_utils.cc
index d74e0ba..00cd34a 100644
--- a/quic/test_tools/quic_test_utils.cc
+++ b/quic/test_tools/quic_test_utils.cc
@@ -1051,7 +1051,7 @@
       kInitialStreamFlowControlWindowForTest);
   config.SetInitialSessionFlowControlWindowToSend(
       kInitialSessionFlowControlWindowForTest);
-  QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(
+  QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams(
       &config, kDefaultMaxStreamsPerConnection);
   // Default enable NSTP.
   // This is unnecessary for versions > 44
diff --git a/quic/test_tools/simulator/quic_endpoint.cc b/quic/test_tools/simulator/quic_endpoint.cc
index 61d253f..043eae9 100644
--- a/quic/test_tools/simulator/quic_endpoint.cc
+++ b/quic/test_tools/simulator/quic_endpoint.cc
@@ -113,7 +113,7 @@
   CryptoHandshakeMessage peer_hello;
   peer_hello.SetValue(kICSL,
                       static_cast<uint32_t>(kMaximumIdleTimeoutSecs - 1));
-  peer_hello.SetValue(kMIDS,
+  peer_hello.SetValue(kMIBS,
                       static_cast<uint32_t>(kDefaultMaxStreamsPerConnection));
   QuicConfig config;
   QuicErrorCode error_code = config.ProcessPeerHello(
diff --git a/quic/tools/quic_simple_server_session_test.cc b/quic/tools/quic_simple_server_session_test.cc
index 330862a..577ff26 100644
--- a/quic/tools/quic_simple_server_session_test.cc
+++ b/quic/tools/quic_simple_server_session_test.cc
@@ -200,9 +200,13 @@
                        TlsServerHandshaker::CreateSslCtx()),
         compressed_certs_cache_(
             QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {
-    config_.SetMaxIncomingDynamicStreamsToSend(kMaxStreamsForTest);
-    QuicConfigPeer::SetReceivedMaxIncomingDynamicStreams(&config_,
-                                                         kMaxStreamsForTest);
+    config_.SetMaxIncomingBidirectionalStreamsToSend(kMaxStreamsForTest);
+    QuicConfigPeer::SetReceivedMaxIncomingBidirectionalStreams(
+        &config_, kMaxStreamsForTest);
+    config_.SetMaxIncomingUnidirectionalStreamsToSend(kMaxStreamsForTest);
+    QuicConfigPeer::SetReceivedMaxIncomingUnidirectionalStreams(
+        &config_, kMaxStreamsForTest);
+
     config_.SetInitialStreamFlowControlWindowToSend(
         kInitialStreamFlowControlWindowForTest);
     config_.SetInitialSessionFlowControlWindowToSend(
diff --git a/quic/tools/quic_simple_server_stream_test.cc b/quic/tools/quic_simple_server_stream_test.cc
index 7572a75..8aaaf6b 100644
--- a/quic/tools/quic_simple_server_stream_test.cc
+++ b/quic/tools/quic_simple_server_stream_test.cc
@@ -100,8 +100,15 @@
                                 crypto_config,
                                 compressed_certs_cache,
                                 quic_simple_server_backend) {
-    QuicSessionPeer::SetMaxOpenIncomingStreams(this, kMaxStreamsForTest);
-    QuicSessionPeer::SetMaxOpenOutgoingStreams(this, kMaxStreamsForTest);
+    if (connection->transport_version() == QUIC_VERSION_99) {
+      QuicSessionPeer::SetMaxOpenIncomingUnidirectionalStreams(
+          this, kMaxStreamsForTest);
+      QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(
+          this, kMaxStreamsForTest);
+    } else {
+      QuicSessionPeer::SetMaxOpenIncomingStreams(this, kMaxStreamsForTest);
+      QuicSessionPeer::SetMaxOpenOutgoingStreams(this, kMaxStreamsForTest);
+    }
     ON_CALL(*this, WritevData(_, _, _, _, _))
         .WillByDefault(Invoke(MockQuicSession::ConsumeData));
   }