QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 5 | #include "quic/core/http/quic_spdy_session.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 6 | |
| 7 | #include <cstdint> |
bnc | 92fa63a | 2020-02-04 05:13:43 -0800 | [diff] [blame] | 8 | #include <limits> |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 9 | #include <set> |
vasilvv | 872e7a3 | 2019-03-12 16:42:44 -0700 | [diff] [blame] | 10 | #include <string> |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 11 | #include <utility> |
| 12 | |
vasilvv | fe1c8f3 | 2020-10-19 14:10:59 -0700 | [diff] [blame] | 13 | #include "absl/base/macros.h" |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 14 | #include "absl/strings/escaping.h" |
vasilvv | a163b38 | 2020-12-04 11:47:46 -0800 | [diff] [blame] | 15 | #include "absl/strings/str_cat.h" |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 16 | #include "absl/strings/string_view.h" |
QUICHE team | 5be974e | 2020-12-29 18:35:24 -0500 | [diff] [blame] | 17 | #include "quic/core/crypto/crypto_protocol.h" |
| 18 | #include "quic/core/crypto/null_encrypter.h" |
| 19 | #include "quic/core/frames/quic_stream_frame.h" |
| 20 | #include "quic/core/frames/quic_streams_blocked_frame.h" |
| 21 | #include "quic/core/http/http_constants.h" |
| 22 | #include "quic/core/http/http_encoder.h" |
| 23 | #include "quic/core/qpack/qpack_header_table.h" |
| 24 | #include "quic/core/quic_config.h" |
| 25 | #include "quic/core/quic_crypto_stream.h" |
| 26 | #include "quic/core/quic_data_writer.h" |
| 27 | #include "quic/core/quic_error_codes.h" |
| 28 | #include "quic/core/quic_packets.h" |
| 29 | #include "quic/core/quic_stream.h" |
| 30 | #include "quic/core/quic_types.h" |
| 31 | #include "quic/core/quic_utils.h" |
| 32 | #include "quic/core/quic_versions.h" |
| 33 | #include "quic/platform/api/quic_expect_bug.h" |
| 34 | #include "quic/platform/api/quic_flags.h" |
| 35 | #include "quic/platform/api/quic_map_util.h" |
| 36 | #include "quic/platform/api/quic_ptr_util.h" |
| 37 | #include "quic/platform/api/quic_test.h" |
| 38 | #include "quic/test_tools/qpack/qpack_encoder_peer.h" |
| 39 | #include "quic/test_tools/qpack/qpack_header_table_peer.h" |
| 40 | #include "quic/test_tools/qpack/qpack_test_utils.h" |
| 41 | #include "quic/test_tools/quic_config_peer.h" |
| 42 | #include "quic/test_tools/quic_connection_peer.h" |
| 43 | #include "quic/test_tools/quic_flow_controller_peer.h" |
| 44 | #include "quic/test_tools/quic_session_peer.h" |
| 45 | #include "quic/test_tools/quic_spdy_session_peer.h" |
| 46 | #include "quic/test_tools/quic_stream_peer.h" |
| 47 | #include "quic/test_tools/quic_stream_send_buffer_peer.h" |
| 48 | #include "quic/test_tools/quic_test_utils.h" |
| 49 | #include "common/platform/api/quiche_text_utils.h" |
| 50 | #include "common/quiche_endian.h" |
| 51 | #include "spdy/core/spdy_framer.h" |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 52 | |
| 53 | using spdy::kV3HighestPriority; |
| 54 | using spdy::Spdy3PriorityToHttp2Weight; |
| 55 | using spdy::SpdyFramer; |
| 56 | using spdy::SpdyHeaderBlock; |
| 57 | using spdy::SpdyPriority; |
| 58 | using spdy::SpdyPriorityIR; |
| 59 | using spdy::SpdySerializedFrame; |
dschinazi | 101d2eb | 2020-07-06 19:42:34 -0700 | [diff] [blame] | 60 | using ::testing::_; |
| 61 | using ::testing::AnyNumber; |
| 62 | using ::testing::AtLeast; |
| 63 | using ::testing::InSequence; |
| 64 | using ::testing::Invoke; |
| 65 | using ::testing::Return; |
| 66 | using ::testing::StrictMock; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 67 | |
| 68 | namespace quic { |
| 69 | namespace test { |
| 70 | namespace { |
| 71 | |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 72 | bool VerifyAndClearStopSendingFrame(const QuicFrame& frame) { |
| 73 | EXPECT_EQ(STOP_SENDING_FRAME, frame.type); |
| 74 | return ClearControlFrame(frame); |
| 75 | } |
| 76 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 77 | class TestCryptoStream : public QuicCryptoStream, public QuicCryptoHandshaker { |
| 78 | public: |
| 79 | explicit TestCryptoStream(QuicSession* session) |
| 80 | : QuicCryptoStream(session), |
| 81 | QuicCryptoHandshaker(this, session), |
| 82 | encryption_established_(false), |
fayang | 685367a | 2020-01-14 10:40:15 -0800 | [diff] [blame] | 83 | one_rtt_keys_available_(false), |
rch | 9001a96 | 2019-12-17 10:44:04 -0800 | [diff] [blame] | 84 | params_(new QuicCryptoNegotiatedParameters) { |
| 85 | // Simulate a negotiated cipher_suite with a fake value. |
| 86 | params_->cipher_suite = 1; |
| 87 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 88 | |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 89 | void EstablishZeroRttEncryption() { |
| 90 | encryption_established_ = true; |
| 91 | session()->connection()->SetEncrypter( |
| 92 | ENCRYPTION_ZERO_RTT, |
| 93 | std::make_unique<NullEncrypter>(session()->perspective())); |
| 94 | } |
| 95 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 96 | void OnHandshakeMessage(const CryptoHandshakeMessage& /*message*/) override { |
| 97 | encryption_established_ = true; |
fayang | 685367a | 2020-01-14 10:40:15 -0800 | [diff] [blame] | 98 | one_rtt_keys_available_ = true; |
dschinazi | aab6818 | 2019-10-09 15:39:51 -0700 | [diff] [blame] | 99 | QuicErrorCode error; |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 100 | std::string error_details; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 101 | session()->config()->SetInitialStreamFlowControlWindowToSend( |
| 102 | kInitialStreamFlowControlWindowForTest); |
| 103 | session()->config()->SetInitialSessionFlowControlWindowToSend( |
| 104 | kInitialSessionFlowControlWindowForTest); |
dschinazi | 1f6aca8 | 2020-12-04 13:20:08 -0800 | [diff] [blame] | 105 | if (session()->version().UsesTls()) { |
dschinazi | e7c38a5 | 2020-05-29 15:25:45 -0700 | [diff] [blame] | 106 | if (session()->perspective() == Perspective::IS_CLIENT) { |
| 107 | session()->config()->SetOriginalConnectionIdToSend( |
| 108 | session()->connection()->connection_id()); |
| 109 | session()->config()->SetInitialSourceConnectionIdToSend( |
| 110 | session()->connection()->connection_id()); |
| 111 | } else { |
| 112 | session()->config()->SetInitialSourceConnectionIdToSend( |
| 113 | session()->connection()->client_connection_id()); |
| 114 | } |
dschinazi | aab6818 | 2019-10-09 15:39:51 -0700 | [diff] [blame] | 115 | TransportParameters transport_parameters; |
| 116 | EXPECT_TRUE( |
| 117 | session()->config()->FillTransportParameters(&transport_parameters)); |
| 118 | error = session()->config()->ProcessTransportParameters( |
dschinazi | 52c8f33 | 2020-08-17 13:30:01 -0700 | [diff] [blame] | 119 | transport_parameters, /* is_resumption = */ false, &error_details); |
dschinazi | aab6818 | 2019-10-09 15:39:51 -0700 | [diff] [blame] | 120 | } else { |
| 121 | CryptoHandshakeMessage msg; |
| 122 | session()->config()->ToHandshakeMessage(&msg, transport_version()); |
| 123 | error = |
| 124 | session()->config()->ProcessPeerHello(msg, CLIENT, &error_details); |
| 125 | } |
bnc | c576950 | 2019-11-27 10:01:44 -0800 | [diff] [blame] | 126 | EXPECT_THAT(error, IsQuicNoError()); |
fayang | a45ee8a | 2020-03-20 08:56:11 -0700 | [diff] [blame] | 127 | session()->OnNewEncryptionKeyAvailable( |
| 128 | ENCRYPTION_FORWARD_SECURE, |
| 129 | std::make_unique<NullEncrypter>(session()->perspective())); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 130 | session()->OnConfigNegotiated(); |
fayang | d18bfb9 | 2020-03-19 17:24:21 -0700 | [diff] [blame] | 131 | if (session()->connection()->version().handshake_protocol == |
| 132 | PROTOCOL_TLS1_3) { |
renjietang | c50cc4a | 2020-08-17 18:33:30 -0700 | [diff] [blame] | 133 | session()->OnTlsHandshakeComplete(); |
fayang | d18bfb9 | 2020-03-19 17:24:21 -0700 | [diff] [blame] | 134 | } else { |
| 135 | session()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| 136 | } |
fayang | d96ecda | 2020-02-03 08:45:18 -0800 | [diff] [blame] | 137 | session()->DiscardOldEncryptionKey(ENCRYPTION_INITIAL); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | // QuicCryptoStream implementation |
nharper | 6a6bd31 | 2020-09-17 13:20:53 -0700 | [diff] [blame] | 141 | ssl_early_data_reason_t EarlyDataReason() const override { |
| 142 | return ssl_early_data_unknown; |
| 143 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 144 | bool encryption_established() const override { |
| 145 | return encryption_established_; |
| 146 | } |
fayang | 685367a | 2020-01-14 10:40:15 -0800 | [diff] [blame] | 147 | bool one_rtt_keys_available() const override { |
| 148 | return one_rtt_keys_available_; |
| 149 | } |
fayang | 9a863cf | 2020-01-16 14:12:11 -0800 | [diff] [blame] | 150 | HandshakeState GetHandshakeState() const override { |
| 151 | return one_rtt_keys_available() ? HANDSHAKE_COMPLETE : HANDSHAKE_START; |
| 152 | } |
nharper | ac52a86 | 2020-06-08 12:41:06 -0700 | [diff] [blame] | 153 | void SetServerApplicationStateForResumption( |
| 154 | std::unique_ptr<ApplicationState> /*application_state*/) override {} |
mattm | 072a7e3 | 2020-10-09 16:16:56 -0700 | [diff] [blame] | 155 | bool KeyUpdateSupportedLocally() const override { return false; } |
| 156 | std::unique_ptr<QuicDecrypter> AdvanceKeysAndCreateCurrentOneRttDecrypter() |
| 157 | override { |
| 158 | return nullptr; |
| 159 | } |
| 160 | std::unique_ptr<QuicEncrypter> CreateCurrentOneRttEncrypter() override { |
| 161 | return nullptr; |
| 162 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 163 | const QuicCryptoNegotiatedParameters& crypto_negotiated_params() |
| 164 | const override { |
| 165 | return *params_; |
| 166 | } |
| 167 | CryptoMessageParser* crypto_message_parser() override { |
| 168 | return QuicCryptoHandshaker::crypto_message_parser(); |
| 169 | } |
fayang | d58736d | 2019-11-27 13:35:31 -0800 | [diff] [blame] | 170 | void OnPacketDecrypted(EncryptionLevel /*level*/) override {} |
fayang | 2f2915d | 2020-01-24 06:47:15 -0800 | [diff] [blame] | 171 | void OnOneRttPacketAcknowledged() override {} |
fayang | 44ae4e9 | 2020-04-28 13:09:42 -0700 | [diff] [blame] | 172 | void OnHandshakePacketSent() override {} |
fayang | 0106294 | 2020-01-22 07:23:23 -0800 | [diff] [blame] | 173 | void OnHandshakeDoneReceived() override {} |
fayang | 133b868 | 2020-12-08 05:50:33 -0800 | [diff] [blame] | 174 | void OnNewTokenReceived(absl::string_view /*token*/) override {} |
| 175 | std::string GetAddressToken() const override { return ""; } |
| 176 | bool ValidateAddressToken(absl::string_view /*token*/) const override { |
| 177 | return true; |
| 178 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 179 | |
wub | 713afae | 2020-04-27 07:48:31 -0700 | [diff] [blame] | 180 | MOCK_METHOD(void, OnCanWrite, (), (override)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 181 | |
nharper | 46833c3 | 2019-05-15 21:33:05 -0700 | [diff] [blame] | 182 | bool HasPendingCryptoRetransmission() const override { return false; } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 183 | |
wub | 713afae | 2020-04-27 07:48:31 -0700 | [diff] [blame] | 184 | MOCK_METHOD(bool, HasPendingRetransmission, (), (const, override)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 185 | |
renjietang | 70738bb | 2020-07-08 15:07:12 -0700 | [diff] [blame] | 186 | void OnConnectionClosed(QuicErrorCode /*error*/, |
| 187 | ConnectionCloseSource /*source*/) override {} |
| 188 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 189 | private: |
| 190 | using QuicCryptoStream::session; |
| 191 | |
| 192 | bool encryption_established_; |
fayang | 685367a | 2020-01-14 10:40:15 -0800 | [diff] [blame] | 193 | bool one_rtt_keys_available_; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 194 | QuicReferenceCountedPointer<QuicCryptoNegotiatedParameters> params_; |
| 195 | }; |
| 196 | |
| 197 | class TestHeadersStream : public QuicHeadersStream { |
| 198 | public: |
| 199 | explicit TestHeadersStream(QuicSpdySession* session) |
| 200 | : QuicHeadersStream(session) {} |
| 201 | |
wub | 713afae | 2020-04-27 07:48:31 -0700 | [diff] [blame] | 202 | MOCK_METHOD(void, OnCanWrite, (), (override)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 203 | }; |
| 204 | |
| 205 | class TestStream : public QuicSpdyStream { |
| 206 | public: |
| 207 | TestStream(QuicStreamId id, QuicSpdySession* session, StreamType type) |
| 208 | : QuicSpdyStream(id, session, type) {} |
| 209 | |
renjietang | baea59c | 2019-05-29 15:08:14 -0700 | [diff] [blame] | 210 | TestStream(PendingStream* pending, QuicSpdySession* session, StreamType type) |
| 211 | : QuicSpdyStream(pending, session, type) {} |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 212 | |
| 213 | using QuicStream::CloseWriteSide; |
| 214 | |
| 215 | void OnBodyAvailable() override {} |
| 216 | |
wub | 713afae | 2020-04-27 07:48:31 -0700 | [diff] [blame] | 217 | MOCK_METHOD(void, OnCanWrite, (), (override)); |
| 218 | MOCK_METHOD(bool, |
| 219 | RetransmitStreamData, |
| 220 | (QuicStreamOffset, QuicByteCount, bool, TransmissionType), |
| 221 | (override)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 222 | |
wub | 713afae | 2020-04-27 07:48:31 -0700 | [diff] [blame] | 223 | MOCK_METHOD(bool, HasPendingRetransmission, (), (const, override)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 224 | }; |
| 225 | |
| 226 | class TestSession : public QuicSpdySession { |
| 227 | public: |
| 228 | explicit TestSession(QuicConnection* connection) |
| 229 | : QuicSpdySession(connection, |
| 230 | nullptr, |
| 231 | DefaultQuicConfig(), |
| 232 | CurrentSupportedVersions()), |
| 233 | crypto_stream_(this), |
| 234 | writev_consumes_all_data_(false) { |
| 235 | Initialize(); |
| 236 | this->connection()->SetEncrypter( |
| 237 | ENCRYPTION_FORWARD_SECURE, |
vasilvv | 0fc587f | 2019-09-06 13:33:08 -0700 | [diff] [blame] | 238 | std::make_unique<NullEncrypter>(connection->perspective())); |
fayang | 6a25841 | 2020-05-28 08:57:12 -0700 | [diff] [blame] | 239 | if (this->connection()->version().SupportsAntiAmplificationLimit()) { |
| 240 | QuicConnectionPeer::SetAddressValidated(this->connection()); |
| 241 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 242 | } |
| 243 | |
ianswett | 6aefa0b | 2019-12-10 07:26:15 -0800 | [diff] [blame] | 244 | ~TestSession() override { DeleteConnection(); } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 245 | |
| 246 | TestCryptoStream* GetMutableCryptoStream() override { |
| 247 | return &crypto_stream_; |
| 248 | } |
| 249 | |
| 250 | const TestCryptoStream* GetCryptoStream() const override { |
| 251 | return &crypto_stream_; |
| 252 | } |
| 253 | |
| 254 | TestStream* CreateOutgoingBidirectionalStream() override { |
| 255 | TestStream* stream = new TestStream(GetNextOutgoingBidirectionalStreamId(), |
| 256 | this, BIDIRECTIONAL); |
| 257 | ActivateStream(QuicWrapUnique(stream)); |
| 258 | return stream; |
| 259 | } |
| 260 | |
| 261 | TestStream* CreateOutgoingUnidirectionalStream() override { |
| 262 | TestStream* stream = new TestStream(GetNextOutgoingUnidirectionalStreamId(), |
| 263 | this, WRITE_UNIDIRECTIONAL); |
| 264 | ActivateStream(QuicWrapUnique(stream)); |
| 265 | return stream; |
| 266 | } |
| 267 | |
| 268 | TestStream* CreateIncomingStream(QuicStreamId id) override { |
| 269 | // Enforce the limit on the number of open streams. |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 270 | if (!VersionHasIetfQuicFrames(connection()->transport_version()) && |
fayang | bd94f4c | 2020-06-01 14:36:37 -0700 | [diff] [blame] | 271 | stream_id_manager().num_open_incoming_streams() + 1 > |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 272 | max_open_incoming_bidirectional_streams()) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 273 | connection()->CloseConnection( |
| 274 | QUIC_TOO_MANY_OPEN_STREAMS, "Too many streams!", |
| 275 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 276 | return nullptr; |
| 277 | } else { |
| 278 | TestStream* stream = new TestStream( |
| 279 | id, this, |
renjietang | d262e25 | 2020-06-19 15:11:24 -0700 | [diff] [blame] | 280 | DetermineStreamType(id, connection()->version(), perspective(), |
| 281 | /*is_incoming=*/true, BIDIRECTIONAL)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 282 | ActivateStream(QuicWrapUnique(stream)); |
| 283 | return stream; |
| 284 | } |
| 285 | } |
| 286 | |
renjietang | baea59c | 2019-05-29 15:08:14 -0700 | [diff] [blame] | 287 | TestStream* CreateIncomingStream(PendingStream* pending) override { |
| 288 | QuicStreamId id = pending->id(); |
renjietang | d262e25 | 2020-06-19 15:11:24 -0700 | [diff] [blame] | 289 | TestStream* stream = new TestStream( |
| 290 | pending, this, |
| 291 | DetermineStreamType(id, connection()->version(), perspective(), |
| 292 | /*is_incoming=*/true, BIDIRECTIONAL)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 293 | ActivateStream(QuicWrapUnique(stream)); |
| 294 | return stream; |
| 295 | } |
| 296 | |
| 297 | bool ShouldCreateIncomingStream(QuicStreamId /*id*/) override { return true; } |
| 298 | |
| 299 | bool ShouldCreateOutgoingBidirectionalStream() override { return true; } |
| 300 | bool ShouldCreateOutgoingUnidirectionalStream() override { return true; } |
| 301 | |
| 302 | bool IsClosedStream(QuicStreamId id) { |
| 303 | return QuicSession::IsClosedStream(id); |
| 304 | } |
| 305 | |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 306 | QuicStream* GetOrCreateStream(QuicStreamId stream_id) { |
| 307 | return QuicSpdySession::GetOrCreateStream(stream_id); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 308 | } |
| 309 | |
vasilvv | 7df418b | 2020-10-13 13:47:09 -0700 | [diff] [blame] | 310 | QuicConsumedData WritevData(QuicStreamId id, |
| 311 | size_t write_length, |
| 312 | QuicStreamOffset offset, |
| 313 | StreamSendingState state, |
| 314 | TransmissionType type, |
| 315 | absl::optional<EncryptionLevel> level) override { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 316 | bool fin = state != NO_FIN; |
| 317 | QuicConsumedData consumed(write_length, fin); |
| 318 | if (!writev_consumes_all_data_) { |
renjietang | 4d992bf | 2020-03-03 13:01:55 -0800 | [diff] [blame] | 319 | consumed = |
| 320 | QuicSession::WritevData(id, write_length, offset, state, type, level); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 321 | } |
| 322 | QuicSessionPeer::GetWriteBlockedStreams(this)->UpdateBytesForStream( |
| 323 | id, consumed.bytes_consumed); |
| 324 | return consumed; |
| 325 | } |
| 326 | |
| 327 | void set_writev_consumes_all_data(bool val) { |
| 328 | writev_consumes_all_data_ = val; |
| 329 | } |
| 330 | |
| 331 | QuicConsumedData SendStreamData(QuicStream* stream) { |
| 332 | struct iovec iov; |
nharper | 46833c3 | 2019-05-15 21:33:05 -0700 | [diff] [blame] | 333 | if (!QuicUtils::IsCryptoStreamId(connection()->transport_version(), |
| 334 | stream->id()) && |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 335 | connection()->encryption_level() != ENCRYPTION_FORWARD_SECURE) { |
| 336 | this->connection()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
| 337 | } |
| 338 | MakeIOVector("not empty", &iov); |
| 339 | QuicStreamPeer::SendBuffer(stream).SaveStreamData(&iov, 1, 0, 9); |
renjietang | 41a1b41 | 2020-02-27 15:05:14 -0800 | [diff] [blame] | 340 | QuicConsumedData consumed = |
fayang | 9c41f8b | 2020-10-30 13:13:06 -0700 | [diff] [blame] | 341 | WritevData(stream->id(), 9, 0, FIN, NOT_RETRANSMISSION, |
| 342 | GetEncryptionLevelToSendApplicationData()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 343 | QuicStreamPeer::SendBuffer(stream).OnStreamDataConsumed( |
| 344 | consumed.bytes_consumed); |
| 345 | return consumed; |
| 346 | } |
| 347 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 348 | QuicConsumedData SendLargeFakeData(QuicStream* stream, int bytes) { |
| 349 | DCHECK(writev_consumes_all_data_); |
renjietang | 4d992bf | 2020-03-03 13:01:55 -0800 | [diff] [blame] | 350 | return WritevData(stream->id(), bytes, 0, FIN, NOT_RETRANSMISSION, |
fayang | 9c41f8b | 2020-10-30 13:13:06 -0700 | [diff] [blame] | 351 | GetEncryptionLevelToSendApplicationData()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 352 | } |
| 353 | |
bnc | 9be5c21 | 2020-12-11 12:38:20 -0800 | [diff] [blame] | 354 | MOCK_METHOD(void, OnAcceptChFrame, (const AcceptChFrame&), (override)); |
| 355 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 356 | using QuicSession::closed_streams; |
renjietang | 56d2ed2 | 2019-10-22 14:11:55 -0700 | [diff] [blame] | 357 | using QuicSession::ShouldKeepConnectionAlive; |
renjietang | e76b2da | 2019-05-13 14:50:23 -0700 | [diff] [blame] | 358 | using QuicSpdySession::ProcessPendingStream; |
| 359 | using QuicSpdySession::UsesPendingStreams; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 360 | |
| 361 | private: |
| 362 | StrictMock<TestCryptoStream> crypto_stream_; |
| 363 | |
| 364 | bool writev_consumes_all_data_; |
| 365 | }; |
| 366 | |
| 367 | class QuicSpdySessionTestBase : public QuicTestWithParam<ParsedQuicVersion> { |
| 368 | public: |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 369 | bool ClearMaxStreamsControlFrame(const QuicFrame& frame) { |
| 370 | if (frame.type == MAX_STREAMS_FRAME) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 371 | DeleteFrame(&const_cast<QuicFrame&>(frame)); |
| 372 | return true; |
| 373 | } |
| 374 | return false; |
| 375 | } |
| 376 | |
| 377 | protected: |
| 378 | explicit QuicSpdySessionTestBase(Perspective perspective) |
| 379 | : connection_( |
| 380 | new StrictMock<MockQuicConnection>(&helper_, |
| 381 | &alarm_factory_, |
| 382 | perspective, |
| 383 | SupportedVersions(GetParam()))), |
| 384 | session_(connection_) { |
| 385 | session_.config()->SetInitialStreamFlowControlWindowToSend( |
| 386 | kInitialStreamFlowControlWindowForTest); |
| 387 | session_.config()->SetInitialSessionFlowControlWindowToSend( |
| 388 | kInitialSessionFlowControlWindowForTest); |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 389 | if (VersionUsesHttp3(transport_version())) { |
renjietang | e6d9467 | 2020-01-07 10:30:10 -0800 | [diff] [blame] | 390 | QuicConfigPeer::SetReceivedMaxUnidirectionalStreams( |
renjietang | 241ba60 | 2020-04-29 13:29:46 -0700 | [diff] [blame] | 391 | session_.config(), kHttp3StaticUnidirectionalStreamCount); |
renjietang | 216dc01 | 2019-08-27 11:28:27 -0700 | [diff] [blame] | 392 | } |
renjietang | bb2e22a | 2019-09-12 15:46:39 -0700 | [diff] [blame] | 393 | QuicConfigPeer::SetReceivedInitialSessionFlowControlWindow( |
| 394 | session_.config(), kMinimumFlowControlSendWindow); |
dschinazi | 18cdf13 | 2019-10-09 16:08:18 -0700 | [diff] [blame] | 395 | QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesUnidirectional( |
| 396 | session_.config(), kMinimumFlowControlSendWindow); |
| 397 | QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesIncomingBidirectional( |
| 398 | session_.config(), kMinimumFlowControlSendWindow); |
| 399 | QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesOutgoingBidirectional( |
| 400 | session_.config(), kMinimumFlowControlSendWindow); |
renjietang | bb2e22a | 2019-09-12 15:46:39 -0700 | [diff] [blame] | 401 | session_.OnConfigNegotiated(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 402 | connection_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); |
| 403 | TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); |
| 404 | EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) |
| 405 | .Times(testing::AnyNumber()); |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 406 | writer_ = static_cast<MockPacketWriter*>( |
| 407 | QuicConnectionPeer::GetWriter(session_.connection())); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 408 | } |
| 409 | |
| 410 | void CheckClosedStreams() { |
QUICHE team | dc41bf1 | 2019-03-20 12:58:42 -0700 | [diff] [blame] | 411 | QuicStreamId first_stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 412 | transport_version(), Perspective::IS_CLIENT); |
| 413 | if (!QuicVersionUsesCryptoFrames(transport_version())) { |
| 414 | first_stream_id = QuicUtils::GetCryptoStreamId(transport_version()); |
QUICHE team | dc41bf1 | 2019-03-20 12:58:42 -0700 | [diff] [blame] | 415 | } |
| 416 | for (QuicStreamId i = first_stream_id; i < 100; i++) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 417 | if (!QuicContainsKey(closed_streams_, i)) { |
| 418 | EXPECT_FALSE(session_.IsClosedStream(i)) << " stream id: " << i; |
| 419 | } else { |
| 420 | EXPECT_TRUE(session_.IsClosedStream(i)) << " stream id: " << i; |
| 421 | } |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | void CloseStream(QuicStreamId id) { |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 426 | if (!VersionHasIetfQuicFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 427 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
bnc | 5b3c3be | 2019-06-25 10:37:09 -0700 | [diff] [blame] | 428 | .WillOnce(Invoke(&ClearControlFrame)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 429 | } else { |
renjietang | f8bde4b | 2020-10-02 13:25:08 -0700 | [diff] [blame] | 430 | // IETF QUIC has two frames, RST_STREAM and STOP_SENDING |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 431 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
| 432 | .Times(2) |
bnc | 5b3c3be | 2019-06-25 10:37:09 -0700 | [diff] [blame] | 433 | .WillRepeatedly(Invoke(&ClearControlFrame)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 434 | } |
| 435 | EXPECT_CALL(*connection_, OnStreamReset(id, _)); |
renjietang | fcf3f18 | 2020-06-30 15:06:56 -0700 | [diff] [blame] | 436 | |
| 437 | // QPACK streams might write data upon stream reset. Let the test session |
| 438 | // handle the data. |
| 439 | session_.set_writev_consumes_all_data(true); |
| 440 | |
renjietang | ff4240d | 2020-07-01 06:19:55 -0700 | [diff] [blame] | 441 | session_.ResetStream(id, QUIC_STREAM_CANCELLED); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 442 | closed_streams_.insert(id); |
| 443 | } |
| 444 | |
| 445 | QuicTransportVersion transport_version() const { |
| 446 | return connection_->transport_version(); |
| 447 | } |
| 448 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 449 | QuicStreamId GetNthClientInitiatedBidirectionalId(int n) { |
| 450 | return GetNthClientInitiatedBidirectionalStreamId(transport_version(), n); |
| 451 | } |
| 452 | |
| 453 | QuicStreamId GetNthServerInitiatedBidirectionalId(int n) { |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 454 | return GetNthServerInitiatedBidirectionalStreamId(transport_version(), n); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 455 | } |
| 456 | |
| 457 | QuicStreamId IdDelta() { |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 458 | return QuicUtils::StreamIdDelta(transport_version()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 459 | } |
| 460 | |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 461 | std::string EncodeSettings(const SettingsFrame& settings) { |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 462 | std::unique_ptr<char[]> buffer; |
bnc | 4694272 | 2019-10-29 11:56:21 -0700 | [diff] [blame] | 463 | auto header_length = HttpEncoder::SerializeSettingsFrame(settings, &buffer); |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 464 | return std::string(buffer.get(), header_length); |
| 465 | } |
| 466 | |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 467 | std::string SerializePriorityUpdateFrame( |
| 468 | const PriorityUpdateFrame& priority_update) { |
| 469 | std::unique_ptr<char[]> priority_buffer; |
| 470 | QuicByteCount priority_frame_length = |
| 471 | HttpEncoder::SerializePriorityUpdateFrame(priority_update, |
| 472 | &priority_buffer); |
| 473 | return std::string(priority_buffer.get(), priority_frame_length); |
| 474 | } |
| 475 | |
bnc | 9a7a673 | 2020-04-14 06:08:02 -0700 | [diff] [blame] | 476 | std::string SerializeMaxPushIdFrame(PushId push_id) { |
| 477 | MaxPushIdFrame max_push_id_frame; |
| 478 | max_push_id_frame.push_id = push_id; |
| 479 | std::unique_ptr<char[]> buffer; |
| 480 | QuicByteCount frame_length = |
| 481 | HttpEncoder::SerializeMaxPushIdFrame(max_push_id_frame, &buffer); |
| 482 | return std::string(buffer.get(), frame_length); |
| 483 | } |
| 484 | |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 485 | QuicStreamId StreamCountToId(QuicStreamCount stream_count, |
| 486 | Perspective perspective, |
| 487 | bool bidirectional) { |
| 488 | // Calculate and build up stream ID rather than use |
| 489 | // GetFirst... because the test that relies on this method |
| 490 | // needs to do the stream count where #1 is 0/1/2/3, and not |
| 491 | // take into account that stream 0 is special. |
| 492 | QuicStreamId id = |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 493 | ((stream_count - 1) * QuicUtils::StreamIdDelta(transport_version())); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 494 | if (!bidirectional) { |
| 495 | id |= 0x2; |
| 496 | } |
| 497 | if (perspective == Perspective::IS_SERVER) { |
| 498 | id |= 0x1; |
| 499 | } |
| 500 | return id; |
| 501 | } |
| 502 | |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 503 | void CompleteHandshake() { |
| 504 | if (VersionHasIetfQuicFrames(transport_version())) { |
| 505 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 506 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
| 507 | } |
dschinazi | 1f6aca8 | 2020-12-04 13:20:08 -0800 | [diff] [blame] | 508 | if (connection_->version().UsesTls() && |
bnc | e403126 | 2020-03-30 10:52:57 -0700 | [diff] [blame] | 509 | connection_->perspective() == Perspective::IS_SERVER) { |
dschinazi | 1f6aca8 | 2020-12-04 13:20:08 -0800 | [diff] [blame] | 510 | // HANDSHAKE_DONE frame. |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 511 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
| 512 | .WillOnce(Invoke(&ClearControlFrame)); |
| 513 | } |
| 514 | |
| 515 | CryptoHandshakeMessage message; |
| 516 | session_.GetMutableCryptoStream()->OnHandshakeMessage(message); |
| 517 | testing::Mock::VerifyAndClearExpectations(writer_); |
| 518 | testing::Mock::VerifyAndClearExpectations(connection_); |
| 519 | } |
| 520 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 521 | MockQuicConnectionHelper helper_; |
| 522 | MockAlarmFactory alarm_factory_; |
| 523 | StrictMock<MockQuicConnection>* connection_; |
| 524 | TestSession session_; |
| 525 | std::set<QuicStreamId> closed_streams_; |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 526 | MockPacketWriter* writer_; |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 527 | }; |
| 528 | |
| 529 | class QuicSpdySessionTestServer : public QuicSpdySessionTestBase { |
| 530 | protected: |
| 531 | QuicSpdySessionTestServer() |
| 532 | : QuicSpdySessionTestBase(Perspective::IS_SERVER) {} |
| 533 | }; |
| 534 | |
| 535 | INSTANTIATE_TEST_SUITE_P(Tests, |
| 536 | QuicSpdySessionTestServer, |
dschinazi | 142051a | 2019-09-18 18:17:29 -0700 | [diff] [blame] | 537 | ::testing::ValuesIn(AllSupportedVersions()), |
| 538 | ::testing::PrintToStringParamName()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 539 | |
renjietang | e76b2da | 2019-05-13 14:50:23 -0700 | [diff] [blame] | 540 | TEST_P(QuicSpdySessionTestServer, UsesPendingStreams) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 541 | if (!VersionUsesHttp3(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 542 | return; |
| 543 | } |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 544 | EXPECT_TRUE(session_.UsesPendingStreams()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 545 | } |
| 546 | |
| 547 | TEST_P(QuicSpdySessionTestServer, PeerAddress) { |
| 548 | EXPECT_EQ(QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort), |
| 549 | session_.peer_address()); |
| 550 | } |
| 551 | |
| 552 | TEST_P(QuicSpdySessionTestServer, SelfAddress) { |
| 553 | EXPECT_TRUE(session_.self_address().IsInitialized()); |
| 554 | } |
| 555 | |
fayang | a3d8df7 | 2020-01-14 11:54:39 -0800 | [diff] [blame] | 556 | TEST_P(QuicSpdySessionTestServer, OneRttKeysAvailable) { |
fayang | a3d8df7 | 2020-01-14 11:54:39 -0800 | [diff] [blame] | 557 | EXPECT_FALSE(session_.OneRttKeysAvailable()); |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 558 | CompleteHandshake(); |
fayang | a3d8df7 | 2020-01-14 11:54:39 -0800 | [diff] [blame] | 559 | EXPECT_TRUE(session_.OneRttKeysAvailable()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 560 | } |
| 561 | |
| 562 | TEST_P(QuicSpdySessionTestServer, IsClosedStreamDefault) { |
| 563 | // Ensure that no streams are initially closed. |
QUICHE team | dc41bf1 | 2019-03-20 12:58:42 -0700 | [diff] [blame] | 564 | QuicStreamId first_stream_id = QuicUtils::GetFirstBidirectionalStreamId( |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 565 | transport_version(), Perspective::IS_CLIENT); |
| 566 | if (!QuicVersionUsesCryptoFrames(transport_version())) { |
| 567 | first_stream_id = QuicUtils::GetCryptoStreamId(transport_version()); |
QUICHE team | dc41bf1 | 2019-03-20 12:58:42 -0700 | [diff] [blame] | 568 | } |
| 569 | for (QuicStreamId i = first_stream_id; i < 100; i++) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 570 | EXPECT_FALSE(session_.IsClosedStream(i)) << "stream id: " << i; |
| 571 | } |
| 572 | } |
| 573 | |
| 574 | TEST_P(QuicSpdySessionTestServer, AvailableStreams) { |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 575 | ASSERT_TRUE(session_.GetOrCreateStream( |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 576 | GetNthClientInitiatedBidirectionalId(2)) != nullptr); |
| 577 | // Both client initiated streams with smaller stream IDs are available. |
| 578 | EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( |
| 579 | &session_, GetNthClientInitiatedBidirectionalId(0))); |
| 580 | EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( |
| 581 | &session_, GetNthClientInitiatedBidirectionalId(1))); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 582 | ASSERT_TRUE(session_.GetOrCreateStream( |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 583 | GetNthClientInitiatedBidirectionalId(1)) != nullptr); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 584 | ASSERT_TRUE(session_.GetOrCreateStream( |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 585 | GetNthClientInitiatedBidirectionalId(0)) != nullptr); |
| 586 | } |
| 587 | |
| 588 | TEST_P(QuicSpdySessionTestServer, IsClosedStreamLocallyCreated) { |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 589 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 590 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 591 | EXPECT_EQ(GetNthServerInitiatedBidirectionalId(0), stream2->id()); |
| 592 | QuicSpdyStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 593 | EXPECT_EQ(GetNthServerInitiatedBidirectionalId(1), stream4->id()); |
| 594 | |
| 595 | CheckClosedStreams(); |
| 596 | CloseStream(GetNthServerInitiatedBidirectionalId(0)); |
| 597 | CheckClosedStreams(); |
| 598 | CloseStream(GetNthServerInitiatedBidirectionalId(1)); |
| 599 | CheckClosedStreams(); |
| 600 | } |
| 601 | |
| 602 | TEST_P(QuicSpdySessionTestServer, IsClosedStreamPeerCreated) { |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 603 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 604 | QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0); |
| 605 | QuicStreamId stream_id2 = GetNthClientInitiatedBidirectionalId(1); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 606 | session_.GetOrCreateStream(stream_id1); |
| 607 | session_.GetOrCreateStream(stream_id2); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 608 | |
| 609 | CheckClosedStreams(); |
| 610 | CloseStream(stream_id1); |
| 611 | CheckClosedStreams(); |
| 612 | CloseStream(stream_id2); |
| 613 | // Create a stream, and make another available. |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 614 | QuicStream* stream3 = session_.GetOrCreateStream(stream_id2 + 4); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 615 | CheckClosedStreams(); |
| 616 | // Close one, but make sure the other is still not closed |
| 617 | CloseStream(stream3->id()); |
| 618 | CheckClosedStreams(); |
| 619 | } |
| 620 | |
| 621 | TEST_P(QuicSpdySessionTestServer, MaximumAvailableOpenedStreams) { |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 622 | if (VersionHasIetfQuicFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 623 | // For IETF QUIC, we should be able to obtain the max allowed |
| 624 | // stream ID, the next ID should fail. Since the actual limit |
| 625 | // is not the number of open streams, we allocate the max and the max+2. |
| 626 | // Get the max allowed stream ID, this should succeed. |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 627 | QuicStreamId stream_id = StreamCountToId( |
renjietang | f8bde4b | 2020-10-02 13:25:08 -0700 | [diff] [blame] | 628 | QuicSessionPeer::ietf_streamid_manager(&session_) |
ianswett | d141453 | 2019-09-18 20:12:59 -0700 | [diff] [blame] | 629 | ->max_incoming_bidirectional_streams(), |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 630 | Perspective::IS_CLIENT, // Client initates stream, allocs stream id. |
| 631 | /*bidirectional=*/true); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 632 | EXPECT_NE(nullptr, session_.GetOrCreateStream(stream_id)); |
renjietang | f8bde4b | 2020-10-02 13:25:08 -0700 | [diff] [blame] | 633 | stream_id = |
| 634 | StreamCountToId(QuicSessionPeer::ietf_streamid_manager(&session_) |
| 635 | ->max_incoming_unidirectional_streams(), |
| 636 | Perspective::IS_CLIENT, |
| 637 | /*bidirectional=*/false); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 638 | EXPECT_NE(nullptr, session_.GetOrCreateStream(stream_id)); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 639 | EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(2); |
| 640 | // Get the (max allowed stream ID)++. These should all fail. |
renjietang | f8bde4b | 2020-10-02 13:25:08 -0700 | [diff] [blame] | 641 | stream_id = |
| 642 | StreamCountToId(QuicSessionPeer::ietf_streamid_manager(&session_) |
| 643 | ->max_incoming_bidirectional_streams() + |
| 644 | 1, |
| 645 | Perspective::IS_CLIENT, |
| 646 | /*bidirectional=*/true); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 647 | EXPECT_EQ(nullptr, session_.GetOrCreateStream(stream_id)); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 648 | |
ianswett | d141453 | 2019-09-18 20:12:59 -0700 | [diff] [blame] | 649 | stream_id = |
renjietang | f8bde4b | 2020-10-02 13:25:08 -0700 | [diff] [blame] | 650 | StreamCountToId(QuicSessionPeer::ietf_streamid_manager(&session_) |
ianswett | d141453 | 2019-09-18 20:12:59 -0700 | [diff] [blame] | 651 | ->max_incoming_unidirectional_streams() + |
| 652 | 1, |
| 653 | Perspective::IS_CLIENT, |
| 654 | /*bidirectional=*/false); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 655 | EXPECT_EQ(nullptr, session_.GetOrCreateStream(stream_id)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 656 | } else { |
| 657 | QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 658 | session_.GetOrCreateStream(stream_id); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 659 | EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); |
| 660 | EXPECT_NE( |
| 661 | nullptr, |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 662 | session_.GetOrCreateStream( |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 663 | stream_id + |
| 664 | IdDelta() * |
| 665 | (session_.max_open_incoming_bidirectional_streams() - 1))); |
| 666 | } |
| 667 | } |
| 668 | |
| 669 | TEST_P(QuicSpdySessionTestServer, TooManyAvailableStreams) { |
| 670 | QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0); |
| 671 | QuicStreamId stream_id2; |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 672 | EXPECT_NE(nullptr, session_.GetOrCreateStream(stream_id1)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 673 | // A stream ID which is too large to create. |
| 674 | stream_id2 = GetNthClientInitiatedBidirectionalId( |
| 675 | 2 * session_.MaxAvailableBidirectionalStreams() + 4); |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 676 | if (VersionHasIetfQuicFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 677 | EXPECT_CALL(*connection_, CloseConnection(QUIC_INVALID_STREAM_ID, _, _)); |
| 678 | } else { |
| 679 | EXPECT_CALL(*connection_, |
| 680 | CloseConnection(QUIC_TOO_MANY_AVAILABLE_STREAMS, _, _)); |
| 681 | } |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 682 | EXPECT_EQ(nullptr, session_.GetOrCreateStream(stream_id2)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 683 | } |
| 684 | |
| 685 | TEST_P(QuicSpdySessionTestServer, ManyAvailableStreams) { |
| 686 | // When max_open_streams_ is 200, should be able to create 200 streams |
| 687 | // out-of-order, that is, creating the one with the largest stream ID first. |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 688 | if (VersionHasIetfQuicFrames(transport_version())) { |
fkastenholz | d3a1de9 | 2019-05-15 07:00:07 -0700 | [diff] [blame] | 689 | QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_, 200); |
| 690 | } else { |
| 691 | QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, 200); |
| 692 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 693 | QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); |
| 694 | // Create one stream. |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 695 | session_.GetOrCreateStream(stream_id); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 696 | EXPECT_CALL(*connection_, CloseConnection(_, _, _)).Times(0); |
| 697 | // Stream count is 200, GetNth... starts counting at 0, so the 200'th stream |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 698 | // is 199. BUT actually we need to do 198 because the crypto stream (Stream |
| 699 | // ID 0) has not been registered, but GetNth... assumes that it has. |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 700 | EXPECT_NE(nullptr, session_.GetOrCreateStream( |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 701 | GetNthClientInitiatedBidirectionalId(198))); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 702 | } |
| 703 | |
| 704 | TEST_P(QuicSpdySessionTestServer, |
| 705 | DebugDFatalIfMarkingClosedStreamWriteBlocked) { |
dschinazi | 1e41d21 | 2020-03-31 15:55:25 -0700 | [diff] [blame] | 706 | CompleteHandshake(); |
| 707 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 708 | .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 709 | |
| 710 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 711 | QuicStreamId closed_stream_id = stream2->id(); |
| 712 | // Close the stream. |
| 713 | EXPECT_CALL(*connection_, SendControlFrame(_)); |
| 714 | EXPECT_CALL(*connection_, OnStreamReset(closed_stream_id, _)); |
| 715 | stream2->Reset(QUIC_BAD_APPLICATION_PAYLOAD); |
vasilvv | a163b38 | 2020-12-04 11:47:46 -0800 | [diff] [blame] | 716 | std::string msg = |
| 717 | absl::StrCat("Marking unknown stream ", closed_stream_id, " blocked."); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 718 | EXPECT_QUIC_BUG(session_.MarkConnectionLevelWriteBlocked(closed_stream_id), |
| 719 | msg); |
| 720 | } |
| 721 | |
| 722 | TEST_P(QuicSpdySessionTestServer, OnCanWrite) { |
fayang | a61d1ce | 2020-10-27 11:05:44 -0700 | [diff] [blame] | 723 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 724 | session_.set_writev_consumes_all_data(true); |
| 725 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 726 | TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 727 | TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); |
| 728 | |
| 729 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 730 | session_.MarkConnectionLevelWriteBlocked(stream6->id()); |
| 731 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 732 | |
| 733 | InSequence s; |
| 734 | |
| 735 | // Reregister, to test the loop limit. |
| 736 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 737 | session_.SendStreamData(stream2); |
| 738 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 739 | })); |
| 740 | // 2 will get called a second time as it didn't finish its block |
| 741 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 742 | session_.SendStreamData(stream2); |
| 743 | })); |
| 744 | EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { |
| 745 | session_.SendStreamData(stream6); |
| 746 | })); |
| 747 | // 4 will not get called, as we exceeded the loop limit. |
| 748 | session_.OnCanWrite(); |
| 749 | EXPECT_TRUE(session_.WillingAndAbleToWrite()); |
| 750 | } |
| 751 | |
renjietang | e358ac4 | 2020-03-23 17:26:54 -0700 | [diff] [blame] | 752 | TEST_P(QuicSpdySessionTestServer, TooLargeStreamBlocked) { |
| 753 | // STREAMS_BLOCKED frame is IETF QUIC only. |
| 754 | if (!VersionUsesHttp3(transport_version())) { |
| 755 | return; |
| 756 | } |
| 757 | |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 758 | CompleteHandshake(); |
renjietang | e358ac4 | 2020-03-23 17:26:54 -0700 | [diff] [blame] | 759 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 760 | session_.set_debug_visitor(&debug_visitor); |
renjietang | e358ac4 | 2020-03-23 17:26:54 -0700 | [diff] [blame] | 761 | |
| 762 | // Simualte the situation where the incoming stream count is at its limit and |
| 763 | // the peer is blocked. |
| 764 | QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams( |
| 765 | static_cast<QuicSession*>(&session_), QuicUtils::GetMaxStreamCount()); |
| 766 | QuicStreamsBlockedFrame frame; |
| 767 | frame.stream_count = QuicUtils::GetMaxStreamCount(); |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 768 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 769 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
renjietang | e358ac4 | 2020-03-23 17:26:54 -0700 | [diff] [blame] | 770 | EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)); |
| 771 | session_.OnStreamsBlockedFrame(frame); |
| 772 | } |
| 773 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 774 | TEST_P(QuicSpdySessionTestServer, TestBatchedWrites) { |
| 775 | session_.set_writev_consumes_all_data(true); |
| 776 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 777 | TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 778 | TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); |
| 779 | |
| 780 | session_.set_writev_consumes_all_data(true); |
| 781 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 782 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 783 | |
| 784 | // With two sessions blocked, we should get two write calls. They should both |
| 785 | // go to the first stream as it will only write 6k and mark itself blocked |
| 786 | // again. |
| 787 | InSequence s; |
| 788 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 789 | session_.SendLargeFakeData(stream2, 6000); |
| 790 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 791 | })); |
| 792 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 793 | session_.SendLargeFakeData(stream2, 6000); |
| 794 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 795 | })); |
| 796 | session_.OnCanWrite(); |
| 797 | |
| 798 | // We should get one more call for stream2, at which point it has used its |
| 799 | // write quota and we move over to stream 4. |
| 800 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 801 | session_.SendLargeFakeData(stream2, 6000); |
| 802 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 803 | })); |
| 804 | EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { |
| 805 | session_.SendLargeFakeData(stream4, 6000); |
| 806 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 807 | })); |
| 808 | session_.OnCanWrite(); |
| 809 | |
| 810 | // Now let stream 4 do the 2nd of its 3 writes, but add a block for a high |
| 811 | // priority stream 6. 4 should be preempted. 6 will write but *not* block so |
| 812 | // will cede back to 4. |
fayang | 476683a | 2019-07-25 12:42:16 -0700 | [diff] [blame] | 813 | stream6->SetPriority(spdy::SpdyStreamPrecedence(kV3HighestPriority)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 814 | EXPECT_CALL(*stream4, OnCanWrite()) |
| 815 | .WillOnce(Invoke([this, stream4, stream6]() { |
| 816 | session_.SendLargeFakeData(stream4, 6000); |
| 817 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 818 | session_.MarkConnectionLevelWriteBlocked(stream6->id()); |
| 819 | })); |
| 820 | EXPECT_CALL(*stream6, OnCanWrite()) |
| 821 | .WillOnce(Invoke([this, stream4, stream6]() { |
| 822 | session_.SendStreamData(stream6); |
| 823 | session_.SendLargeFakeData(stream4, 6000); |
| 824 | })); |
| 825 | session_.OnCanWrite(); |
| 826 | |
| 827 | // Stream4 alread did 6k worth of writes, so after doing another 12k it should |
| 828 | // cede and 2 should resume. |
| 829 | EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { |
| 830 | session_.SendLargeFakeData(stream4, 12000); |
| 831 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 832 | })); |
| 833 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 834 | session_.SendLargeFakeData(stream2, 6000); |
| 835 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 836 | })); |
| 837 | session_.OnCanWrite(); |
| 838 | } |
| 839 | |
| 840 | TEST_P(QuicSpdySessionTestServer, OnCanWriteBundlesStreams) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 841 | // Encryption needs to be established before data can be sent. |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 842 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 843 | |
| 844 | // Drive congestion control manually. |
| 845 | MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; |
| 846 | QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); |
| 847 | |
| 848 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 849 | TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 850 | TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); |
| 851 | |
| 852 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 853 | session_.MarkConnectionLevelWriteBlocked(stream6->id()); |
| 854 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 855 | |
| 856 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); |
| 857 | EXPECT_CALL(*send_algorithm, GetCongestionWindow()) |
dschinazi | 66dea07 | 2019-04-09 11:41:06 -0700 | [diff] [blame] | 858 | .WillRepeatedly(Return(kMaxOutgoingPacketSize * 10)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 859 | EXPECT_CALL(*send_algorithm, InRecovery()).WillRepeatedly(Return(false)); |
| 860 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 861 | session_.SendStreamData(stream2); |
| 862 | })); |
| 863 | EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { |
| 864 | session_.SendStreamData(stream4); |
| 865 | })); |
| 866 | EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { |
| 867 | session_.SendStreamData(stream6); |
| 868 | })); |
| 869 | |
| 870 | // Expect that we only send one packet, the writes from different streams |
| 871 | // should be bundled together. |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 872 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 873 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
| 874 | EXPECT_CALL(*send_algorithm, OnPacketSent(_, _, _, _, _)); |
| 875 | EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); |
| 876 | session_.OnCanWrite(); |
| 877 | EXPECT_FALSE(session_.WillingAndAbleToWrite()); |
| 878 | } |
| 879 | |
| 880 | TEST_P(QuicSpdySessionTestServer, OnCanWriteCongestionControlBlocks) { |
fayang | a61d1ce | 2020-10-27 11:05:44 -0700 | [diff] [blame] | 881 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 882 | session_.set_writev_consumes_all_data(true); |
| 883 | InSequence s; |
| 884 | |
| 885 | // Drive congestion control manually. |
| 886 | MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; |
| 887 | QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); |
| 888 | |
| 889 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 890 | TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 891 | TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); |
| 892 | |
| 893 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 894 | session_.MarkConnectionLevelWriteBlocked(stream6->id()); |
| 895 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 896 | |
| 897 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); |
| 898 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 899 | session_.SendStreamData(stream2); |
| 900 | })); |
dschinazi | 101d2eb | 2020-07-06 19:42:34 -0700 | [diff] [blame] | 901 | EXPECT_CALL(*send_algorithm, GetCongestionWindow()).Times(AnyNumber()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 902 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); |
| 903 | EXPECT_CALL(*stream6, OnCanWrite()).WillOnce(Invoke([this, stream6]() { |
| 904 | session_.SendStreamData(stream6); |
| 905 | })); |
| 906 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false)); |
| 907 | // stream4->OnCanWrite is not called. |
| 908 | |
| 909 | session_.OnCanWrite(); |
| 910 | EXPECT_TRUE(session_.WillingAndAbleToWrite()); |
| 911 | |
| 912 | // Still congestion-control blocked. |
| 913 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(false)); |
| 914 | session_.OnCanWrite(); |
| 915 | EXPECT_TRUE(session_.WillingAndAbleToWrite()); |
| 916 | |
| 917 | // stream4->OnCanWrite is called once the connection stops being |
| 918 | // congestion-control blocked. |
| 919 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); |
| 920 | EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { |
| 921 | session_.SendStreamData(stream4); |
| 922 | })); |
| 923 | EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); |
| 924 | session_.OnCanWrite(); |
| 925 | EXPECT_FALSE(session_.WillingAndAbleToWrite()); |
| 926 | } |
| 927 | |
| 928 | TEST_P(QuicSpdySessionTestServer, OnCanWriteWriterBlocks) { |
fayang | a61d1ce | 2020-10-27 11:05:44 -0700 | [diff] [blame] | 929 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 930 | // Drive congestion control manually in order to ensure that |
| 931 | // application-limited signaling is handled correctly. |
| 932 | MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; |
| 933 | QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); |
| 934 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); |
| 935 | |
| 936 | // Drive packet writer manually. |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 937 | EXPECT_CALL(*writer_, IsWriteBlocked()).WillRepeatedly(Return(true)); |
| 938 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)).Times(0); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 939 | |
| 940 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 941 | |
| 942 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 943 | |
| 944 | EXPECT_CALL(*stream2, OnCanWrite()).Times(0); |
| 945 | EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)).Times(0); |
| 946 | |
| 947 | session_.OnCanWrite(); |
| 948 | EXPECT_TRUE(session_.WillingAndAbleToWrite()); |
| 949 | } |
| 950 | |
| 951 | TEST_P(QuicSpdySessionTestServer, BufferedHandshake) { |
nharper | d5c4a93 | 2019-05-13 13:58:49 -0700 | [diff] [blame] | 952 | // This tests prioritization of the crypto stream when flow control limits are |
| 953 | // reached. When CRYPTO frames are in use, there is no flow control for the |
| 954 | // crypto handshake, so this test is irrelevant. |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 955 | if (QuicVersionUsesCryptoFrames(transport_version())) { |
nharper | d5c4a93 | 2019-05-13 13:58:49 -0700 | [diff] [blame] | 956 | return; |
| 957 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 958 | session_.set_writev_consumes_all_data(true); |
| 959 | EXPECT_FALSE(session_.HasPendingHandshake()); // Default value. |
| 960 | |
| 961 | // Test that blocking other streams does not change our status. |
| 962 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 963 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 964 | EXPECT_FALSE(session_.HasPendingHandshake()); |
| 965 | |
| 966 | TestStream* stream3 = session_.CreateOutgoingBidirectionalStream(); |
| 967 | session_.MarkConnectionLevelWriteBlocked(stream3->id()); |
| 968 | EXPECT_FALSE(session_.HasPendingHandshake()); |
| 969 | |
| 970 | // Blocking (due to buffering of) the Crypto stream is detected. |
| 971 | session_.MarkConnectionLevelWriteBlocked( |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 972 | QuicUtils::GetCryptoStreamId(transport_version())); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 973 | EXPECT_TRUE(session_.HasPendingHandshake()); |
| 974 | |
| 975 | TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 976 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 977 | EXPECT_TRUE(session_.HasPendingHandshake()); |
| 978 | |
| 979 | InSequence s; |
| 980 | // Force most streams to re-register, which is common scenario when we block |
| 981 | // the Crypto stream, and only the crypto stream can "really" write. |
| 982 | |
| 983 | // Due to prioritization, we *should* be asked to write the crypto stream |
| 984 | // first. |
| 985 | // Don't re-register the crypto stream (which signals complete writing). |
| 986 | TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); |
| 987 | EXPECT_CALL(*crypto_stream, OnCanWrite()); |
| 988 | |
| 989 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 990 | session_.SendStreamData(stream2); |
| 991 | })); |
| 992 | EXPECT_CALL(*stream3, OnCanWrite()).WillOnce(Invoke([this, stream3]() { |
| 993 | session_.SendStreamData(stream3); |
| 994 | })); |
| 995 | EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { |
| 996 | session_.SendStreamData(stream4); |
| 997 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 998 | })); |
| 999 | |
| 1000 | session_.OnCanWrite(); |
| 1001 | EXPECT_TRUE(session_.WillingAndAbleToWrite()); |
| 1002 | EXPECT_FALSE(session_.HasPendingHandshake()); // Crypto stream wrote. |
| 1003 | } |
| 1004 | |
| 1005 | TEST_P(QuicSpdySessionTestServer, OnCanWriteWithClosedStream) { |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1006 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1007 | session_.set_writev_consumes_all_data(true); |
| 1008 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 1009 | TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 1010 | TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); |
| 1011 | |
| 1012 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 1013 | session_.MarkConnectionLevelWriteBlocked(stream6->id()); |
| 1014 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 1015 | CloseStream(stream6->id()); |
| 1016 | |
| 1017 | InSequence s; |
| 1018 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
bnc | 5b3c3be | 2019-06-25 10:37:09 -0700 | [diff] [blame] | 1019 | .WillRepeatedly(Invoke(&ClearControlFrame)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1020 | EXPECT_CALL(*stream2, OnCanWrite()).WillOnce(Invoke([this, stream2]() { |
| 1021 | session_.SendStreamData(stream2); |
| 1022 | })); |
| 1023 | EXPECT_CALL(*stream4, OnCanWrite()).WillOnce(Invoke([this, stream4]() { |
| 1024 | session_.SendStreamData(stream4); |
| 1025 | })); |
| 1026 | session_.OnCanWrite(); |
| 1027 | EXPECT_FALSE(session_.WillingAndAbleToWrite()); |
| 1028 | } |
| 1029 | |
| 1030 | TEST_P(QuicSpdySessionTestServer, |
| 1031 | OnCanWriteLimitsNumWritesIfFlowControlBlocked) { |
fayang | a61d1ce | 2020-10-27 11:05:44 -0700 | [diff] [blame] | 1032 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1033 | // Drive congestion control manually in order to ensure that |
| 1034 | // application-limited signaling is handled correctly. |
| 1035 | MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; |
| 1036 | QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); |
| 1037 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(true)); |
| 1038 | |
| 1039 | // Ensure connection level flow control blockage. |
| 1040 | QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0); |
| 1041 | EXPECT_TRUE(session_.flow_controller()->IsBlocked()); |
| 1042 | EXPECT_TRUE(session_.IsConnectionFlowControlBlocked()); |
| 1043 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1044 | |
| 1045 | // Mark the crypto and headers streams as write blocked, we expect them to be |
| 1046 | // allowed to write later. |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1047 | if (!QuicVersionUsesCryptoFrames(transport_version())) { |
nharper | d5c4a93 | 2019-05-13 13:58:49 -0700 | [diff] [blame] | 1048 | session_.MarkConnectionLevelWriteBlocked( |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1049 | QuicUtils::GetCryptoStreamId(transport_version())); |
nharper | d5c4a93 | 2019-05-13 13:58:49 -0700 | [diff] [blame] | 1050 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1051 | |
| 1052 | // Create a data stream, and although it is write blocked we never expect it |
| 1053 | // to be allowed to write as we are connection level flow control blocked. |
| 1054 | TestStream* stream = session_.CreateOutgoingBidirectionalStream(); |
| 1055 | session_.MarkConnectionLevelWriteBlocked(stream->id()); |
| 1056 | EXPECT_CALL(*stream, OnCanWrite()).Times(0); |
| 1057 | |
| 1058 | // The crypto and headers streams should be called even though we are |
| 1059 | // connection flow control blocked. |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1060 | if (!QuicVersionUsesCryptoFrames(transport_version())) { |
nharper | d5c4a93 | 2019-05-13 13:58:49 -0700 | [diff] [blame] | 1061 | TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); |
| 1062 | EXPECT_CALL(*crypto_stream, OnCanWrite()); |
| 1063 | } |
renjietang | b663b86 | 2019-07-08 16:02:39 -0700 | [diff] [blame] | 1064 | |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1065 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1066 | TestHeadersStream* headers_stream; |
| 1067 | QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr); |
| 1068 | headers_stream = new TestHeadersStream(&session_); |
| 1069 | QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream); |
| 1070 | session_.MarkConnectionLevelWriteBlocked( |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1071 | QuicUtils::GetHeadersStreamId(transport_version())); |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1072 | EXPECT_CALL(*headers_stream, OnCanWrite()); |
| 1073 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1074 | |
| 1075 | // After the crypto and header streams perform a write, the connection will be |
| 1076 | // blocked by the flow control, hence it should become application-limited. |
| 1077 | EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); |
| 1078 | |
| 1079 | session_.OnCanWrite(); |
| 1080 | EXPECT_FALSE(session_.WillingAndAbleToWrite()); |
| 1081 | } |
| 1082 | |
| 1083 | TEST_P(QuicSpdySessionTestServer, SendGoAway) { |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1084 | CompleteHandshake(); |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1085 | if (VersionHasIetfQuicFrames(transport_version())) { |
renjietang | 686ce58 | 2019-10-17 14:28:16 -0700 | [diff] [blame] | 1086 | // HTTP/3 GOAWAY has different semantic and thus has its own test. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1087 | return; |
| 1088 | } |
fayang | 93cc53a | 2019-08-22 12:47:30 -0700 | [diff] [blame] | 1089 | connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1090 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1091 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
| 1092 | |
| 1093 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
| 1094 | .WillOnce( |
| 1095 | Invoke(connection_, &MockQuicConnection::ReallySendControlFrame)); |
| 1096 | session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); |
| 1097 | EXPECT_TRUE(session_.goaway_sent()); |
| 1098 | |
| 1099 | const QuicStreamId kTestStreamId = 5u; |
| 1100 | EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); |
| 1101 | EXPECT_CALL(*connection_, |
| 1102 | OnStreamReset(kTestStreamId, QUIC_STREAM_PEER_GOING_AWAY)) |
| 1103 | .Times(0); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 1104 | EXPECT_TRUE(session_.GetOrCreateStream(kTestStreamId)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1105 | } |
| 1106 | |
fayang | b6daf21 | 2020-12-08 12:00:40 -0800 | [diff] [blame] | 1107 | TEST_P(QuicSpdySessionTestServer, SendGoAwayWithoutEncryption) { |
| 1108 | SetQuicReloadableFlag(quic_encrypted_goaway, true); |
| 1109 | if (VersionHasIetfQuicFrames(transport_version())) { |
| 1110 | // HTTP/3 GOAWAY has different semantic and thus has its own test. |
| 1111 | return; |
| 1112 | } |
| 1113 | EXPECT_CALL( |
| 1114 | *connection_, |
| 1115 | CloseConnection(QUIC_PEER_GOING_AWAY, "Going Away.", |
| 1116 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); |
| 1117 | EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); |
| 1118 | session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); |
| 1119 | EXPECT_FALSE(session_.goaway_sent()); |
| 1120 | } |
| 1121 | |
renjietang | 686ce58 | 2019-10-17 14:28:16 -0700 | [diff] [blame] | 1122 | TEST_P(QuicSpdySessionTestServer, SendHttp3GoAway) { |
| 1123 | if (!VersionUsesHttp3(transport_version())) { |
| 1124 | return; |
| 1125 | } |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 1126 | |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1127 | CompleteHandshake(); |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 1128 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 1129 | session_.set_debug_visitor(&debug_visitor); |
| 1130 | |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1131 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 1132 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
bnc | 01aa36e | 2020-11-11 08:26:27 -0800 | [diff] [blame] | 1133 | if (GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { |
| 1134 | // Send max stream id (currently 32 bits). |
| 1135 | EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 0xfffffffc)); |
| 1136 | } else { |
| 1137 | // No client-initiated stream has been received, therefore a GOAWAY frame |
| 1138 | // with stream ID = 0 is sent to notify the client that all requests can be |
| 1139 | // retried on a different connection. |
| 1140 | EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 0)); |
| 1141 | } |
fayang | b6daf21 | 2020-12-08 12:00:40 -0800 | [diff] [blame] | 1142 | session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); |
bnc | 3988f57 | 2020-08-27 05:27:04 -0700 | [diff] [blame] | 1143 | EXPECT_TRUE(session_.goaway_sent()); |
renjietang | 686ce58 | 2019-10-17 14:28:16 -0700 | [diff] [blame] | 1144 | |
bnc | 01aa36e | 2020-11-11 08:26:27 -0800 | [diff] [blame] | 1145 | // New incoming stream is not reset. |
renjietang | 686ce58 | 2019-10-17 14:28:16 -0700 | [diff] [blame] | 1146 | const QuicStreamId kTestStreamId = |
| 1147 | GetNthClientInitiatedBidirectionalStreamId(transport_version(), 0); |
| 1148 | EXPECT_CALL(*connection_, OnStreamReset(kTestStreamId, _)).Times(0); |
| 1149 | EXPECT_TRUE(session_.GetOrCreateStream(kTestStreamId)); |
bnc | 01aa36e | 2020-11-11 08:26:27 -0800 | [diff] [blame] | 1150 | |
| 1151 | // No more GOAWAY frames are sent because they could not convey new |
| 1152 | // information to the client. |
fayang | b6daf21 | 2020-12-08 12:00:40 -0800 | [diff] [blame] | 1153 | session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); |
| 1154 | } |
| 1155 | |
| 1156 | TEST_P(QuicSpdySessionTestServer, SendHttp3GoAwayWithoutEncryption) { |
| 1157 | SetQuicReloadableFlag(quic_encrypted_goaway, true); |
| 1158 | if (!VersionUsesHttp3(transport_version())) { |
| 1159 | return; |
| 1160 | } |
| 1161 | EXPECT_CALL( |
| 1162 | *connection_, |
| 1163 | CloseConnection(QUIC_PEER_GOING_AWAY, "Goaway", |
| 1164 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); |
| 1165 | session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); |
| 1166 | EXPECT_FALSE(session_.goaway_sent()); |
renjietang | 686ce58 | 2019-10-17 14:28:16 -0700 | [diff] [blame] | 1167 | } |
| 1168 | |
bnc | f3d3241 | 2020-09-09 18:21:03 -0700 | [diff] [blame] | 1169 | TEST_P(QuicSpdySessionTestServer, SendHttp3GoAwayAfterStreamIsCreated) { |
| 1170 | if (!VersionUsesHttp3(transport_version())) { |
| 1171 | return; |
| 1172 | } |
| 1173 | |
bnc | f3d3241 | 2020-09-09 18:21:03 -0700 | [diff] [blame] | 1174 | CompleteHandshake(); |
| 1175 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 1176 | session_.set_debug_visitor(&debug_visitor); |
| 1177 | |
| 1178 | const QuicStreamId kTestStreamId = |
| 1179 | GetNthClientInitiatedBidirectionalStreamId(transport_version(), 0); |
| 1180 | EXPECT_TRUE(session_.GetOrCreateStream(kTestStreamId)); |
| 1181 | |
| 1182 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 1183 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
bnc | 01aa36e | 2020-11-11 08:26:27 -0800 | [diff] [blame] | 1184 | if (GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { |
| 1185 | // Send max stream id (currently 32 bits). |
| 1186 | EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 0xfffffffc)); |
| 1187 | } else { |
| 1188 | // The first stream, of kTestStreamId = 0, could already have been |
| 1189 | // processed. A GOAWAY frame is sent to notify the client that requests |
| 1190 | // starting with stream ID = 4 can be retried on a different connection. |
| 1191 | EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(/* stream_id = */ 4)); |
| 1192 | } |
fayang | b6daf21 | 2020-12-08 12:00:40 -0800 | [diff] [blame] | 1193 | session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); |
bnc | f3d3241 | 2020-09-09 18:21:03 -0700 | [diff] [blame] | 1194 | EXPECT_TRUE(session_.goaway_sent()); |
| 1195 | |
| 1196 | // No more GOAWAY frames are sent because they could not convey new |
| 1197 | // information to the client. |
fayang | b6daf21 | 2020-12-08 12:00:40 -0800 | [diff] [blame] | 1198 | session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); |
bnc | f3d3241 | 2020-09-09 18:21:03 -0700 | [diff] [blame] | 1199 | } |
| 1200 | |
QUICHE team | 63eb43a | 2020-09-02 15:12:29 -0700 | [diff] [blame] | 1201 | TEST_P(QuicSpdySessionTestServer, SendHttp3Shutdown) { |
bnc | 01aa36e | 2020-11-11 08:26:27 -0800 | [diff] [blame] | 1202 | if (GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { |
| 1203 | return; |
| 1204 | } |
| 1205 | |
QUICHE team | 63eb43a | 2020-09-02 15:12:29 -0700 | [diff] [blame] | 1206 | if (!VersionUsesHttp3(transport_version())) { |
| 1207 | return; |
| 1208 | } |
| 1209 | |
| 1210 | CompleteHandshake(); |
| 1211 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 1212 | session_.set_debug_visitor(&debug_visitor); |
| 1213 | |
| 1214 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 1215 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
| 1216 | EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)); |
| 1217 | session_.SendHttp3Shutdown(); |
| 1218 | EXPECT_TRUE(session_.goaway_sent()); |
| 1219 | |
| 1220 | const QuicStreamId kTestStreamId = |
| 1221 | GetNthClientInitiatedBidirectionalStreamId(transport_version(), 0); |
| 1222 | EXPECT_CALL(*connection_, OnStreamReset(kTestStreamId, _)).Times(0); |
| 1223 | EXPECT_TRUE(session_.GetOrCreateStream(kTestStreamId)); |
| 1224 | } |
| 1225 | |
| 1226 | TEST_P(QuicSpdySessionTestServer, SendHttp3GoAwayAfterShutdownNotice) { |
bnc | 01aa36e | 2020-11-11 08:26:27 -0800 | [diff] [blame] | 1227 | if (GetQuicReloadableFlag(quic_goaway_with_max_stream_id)) { |
| 1228 | return; |
| 1229 | } |
| 1230 | |
QUICHE team | 63eb43a | 2020-09-02 15:12:29 -0700 | [diff] [blame] | 1231 | if (!VersionUsesHttp3(transport_version())) { |
| 1232 | return; |
| 1233 | } |
| 1234 | |
| 1235 | CompleteHandshake(); |
| 1236 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 1237 | session_.set_debug_visitor(&debug_visitor); |
| 1238 | |
| 1239 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 1240 | .Times(2) |
| 1241 | .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); |
| 1242 | EXPECT_CALL(debug_visitor, OnGoAwayFrameSent(_)).Times(2); |
| 1243 | |
| 1244 | session_.SendHttp3Shutdown(); |
| 1245 | EXPECT_TRUE(session_.goaway_sent()); |
fayang | b6daf21 | 2020-12-08 12:00:40 -0800 | [diff] [blame] | 1246 | session_.SendHttp3GoAway(QUIC_PEER_GOING_AWAY, "Goaway"); |
QUICHE team | 63eb43a | 2020-09-02 15:12:29 -0700 | [diff] [blame] | 1247 | |
| 1248 | const QuicStreamId kTestStreamId = |
| 1249 | GetNthClientInitiatedBidirectionalStreamId(transport_version(), 0); |
| 1250 | EXPECT_CALL(*connection_, OnStreamReset(kTestStreamId, _)).Times(0); |
| 1251 | EXPECT_TRUE(session_.GetOrCreateStream(kTestStreamId)); |
| 1252 | } |
| 1253 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1254 | TEST_P(QuicSpdySessionTestServer, DoNotSendGoAwayTwice) { |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1255 | CompleteHandshake(); |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1256 | if (VersionHasIetfQuicFrames(transport_version())) { |
renjietang | 686ce58 | 2019-10-17 14:28:16 -0700 | [diff] [blame] | 1257 | // HTTP/3 GOAWAY doesn't have such restriction. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1258 | return; |
| 1259 | } |
| 1260 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
bnc | 5b3c3be | 2019-06-25 10:37:09 -0700 | [diff] [blame] | 1261 | .WillOnce(Invoke(&ClearControlFrame)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1262 | session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); |
| 1263 | EXPECT_TRUE(session_.goaway_sent()); |
| 1264 | session_.SendGoAway(QUIC_PEER_GOING_AWAY, "Going Away."); |
| 1265 | } |
| 1266 | |
| 1267 | TEST_P(QuicSpdySessionTestServer, InvalidGoAway) { |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1268 | if (VersionHasIetfQuicFrames(transport_version())) { |
renjietang | 686ce58 | 2019-10-17 14:28:16 -0700 | [diff] [blame] | 1269 | // HTTP/3 GOAWAY has different semantics and thus has its own test. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1270 | return; |
| 1271 | } |
| 1272 | QuicGoAwayFrame go_away(kInvalidControlFrameId, QUIC_PEER_GOING_AWAY, |
| 1273 | session_.next_outgoing_bidirectional_stream_id(), ""); |
| 1274 | session_.OnGoAway(go_away); |
| 1275 | } |
| 1276 | |
bnc | af8e330 | 2020-07-27 07:42:59 -0700 | [diff] [blame] | 1277 | TEST_P(QuicSpdySessionTestServer, Http3GoAwayLargerIdThanBefore) { |
| 1278 | if (!VersionUsesHttp3(transport_version())) { |
| 1279 | return; |
| 1280 | } |
bnc | af8e330 | 2020-07-27 07:42:59 -0700 | [diff] [blame] | 1281 | |
bnc | 3988f57 | 2020-08-27 05:27:04 -0700 | [diff] [blame] | 1282 | EXPECT_FALSE(session_.goaway_received()); |
bnc | af8e330 | 2020-07-27 07:42:59 -0700 | [diff] [blame] | 1283 | PushId push_id1 = 0; |
| 1284 | session_.OnHttp3GoAway(push_id1); |
bnc | 3988f57 | 2020-08-27 05:27:04 -0700 | [diff] [blame] | 1285 | EXPECT_TRUE(session_.goaway_received()); |
bnc | af8e330 | 2020-07-27 07:42:59 -0700 | [diff] [blame] | 1286 | |
| 1287 | EXPECT_CALL( |
| 1288 | *connection_, |
| 1289 | CloseConnection( |
| 1290 | QUIC_HTTP_GOAWAY_ID_LARGER_THAN_PREVIOUS, |
| 1291 | "GOAWAY received with ID 1 greater than previously received ID 0", |
| 1292 | _)); |
| 1293 | PushId push_id2 = 1; |
| 1294 | session_.OnHttp3GoAway(push_id2); |
| 1295 | } |
| 1296 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1297 | // Test that server session will send a connectivity probe in response to a |
| 1298 | // connectivity probe on the same path. |
| 1299 | TEST_P(QuicSpdySessionTestServer, ServerReplyToConnecitivityProbe) { |
danzh | 353c9f0 | 2021-01-13 09:36:04 -0800 | [diff] [blame] | 1300 | if (VersionHasIetfQuicFrames(transport_version()) && |
| 1301 | connection_->send_path_response()) { |
| 1302 | return; |
| 1303 | } |
danzh | 8a27a1a | 2020-09-02 10:26:28 -0700 | [diff] [blame] | 1304 | connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1305 | QuicSocketAddress old_peer_address = |
| 1306 | QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort); |
| 1307 | EXPECT_EQ(old_peer_address, session_.peer_address()); |
| 1308 | |
| 1309 | QuicSocketAddress new_peer_address = |
| 1310 | QuicSocketAddress(QuicIpAddress::Loopback4(), kTestPort + 1); |
| 1311 | |
danzh | 8a27a1a | 2020-09-02 10:26:28 -0700 | [diff] [blame] | 1312 | if (connection_->send_path_response()) { |
| 1313 | EXPECT_CALL(*connection_, |
| 1314 | SendConnectivityProbingPacket(nullptr, new_peer_address)); |
| 1315 | } else { |
| 1316 | EXPECT_CALL(*connection_, |
| 1317 | SendConnectivityProbingResponsePacket(new_peer_address)); |
| 1318 | } |
| 1319 | |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1320 | if (VersionHasIetfQuicFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1321 | // Need to explicitly do this to emulate the reception of a PathChallenge, |
| 1322 | // which stores its payload for use in generating the response. |
| 1323 | connection_->OnPathChallengeFrame( |
| 1324 | QuicPathChallengeFrame(0, {{0, 1, 2, 3, 4, 5, 6, 7}})); |
| 1325 | } |
zhongyi | 83161e4 | 2019-08-19 09:06:25 -0700 | [diff] [blame] | 1326 | session_.OnPacketReceived(session_.self_address(), new_peer_address, |
| 1327 | /*is_connectivity_probe=*/true); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1328 | EXPECT_EQ(old_peer_address, session_.peer_address()); |
| 1329 | } |
| 1330 | |
| 1331 | TEST_P(QuicSpdySessionTestServer, IncreasedTimeoutAfterCryptoHandshake) { |
| 1332 | EXPECT_EQ(kInitialIdleTimeoutSecs + 3, |
| 1333 | QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1334 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1335 | EXPECT_EQ(kMaximumIdleTimeoutSecs + 3, |
| 1336 | QuicConnectionPeer::GetNetworkTimeout(connection_).ToSeconds()); |
| 1337 | } |
| 1338 | |
| 1339 | TEST_P(QuicSpdySessionTestServer, RstStreamBeforeHeadersDecompressed) { |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1340 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1341 | // Send two bytes of payload. |
| 1342 | QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0, |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1343 | absl::string_view("HT")); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1344 | session_.OnStreamFrame(data1); |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 1345 | EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1346 | |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1347 | if (!VersionHasIetfQuicFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1348 | // For version99, OnStreamReset gets called because of the STOP_SENDING, |
| 1349 | // below. EXPECT the call there. |
| 1350 | EXPECT_CALL(*connection_, |
| 1351 | OnStreamReset(GetNthClientInitiatedBidirectionalId(0), _)); |
| 1352 | } |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1353 | |
| 1354 | // In HTTP/3, Qpack stream will send data on stream reset and cause packet to |
| 1355 | // be flushed. |
| 1356 | if (VersionUsesHttp3(transport_version())) { |
| 1357 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 1358 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
| 1359 | } |
| 1360 | EXPECT_CALL(*connection_, SendControlFrame(_)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1361 | QuicRstStreamFrame rst1(kInvalidControlFrameId, |
| 1362 | GetNthClientInitiatedBidirectionalId(0), |
| 1363 | QUIC_ERROR_PROCESSING_STREAM, 0); |
| 1364 | session_.OnRstStream(rst1); |
| 1365 | |
| 1366 | // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a |
| 1367 | // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a |
| 1368 | // one-way close. |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1369 | if (VersionHasIetfQuicFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1370 | // Only needed for version 99/IETF QUIC. |
bnc | 187eea3 | 2020-09-02 12:16:15 -0700 | [diff] [blame] | 1371 | QuicStopSendingFrame stop_sending(kInvalidControlFrameId, |
| 1372 | GetNthClientInitiatedBidirectionalId(0), |
| 1373 | QUIC_ERROR_PROCESSING_STREAM); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1374 | // Expect the RESET_STREAM that is generated in response to receiving a |
| 1375 | // STOP_SENDING. |
| 1376 | EXPECT_CALL(*connection_, |
| 1377 | OnStreamReset(GetNthClientInitiatedBidirectionalId(0), |
| 1378 | QUIC_ERROR_PROCESSING_STREAM)); |
| 1379 | session_.OnStopSendingFrame(stop_sending); |
| 1380 | } |
| 1381 | |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 1382 | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1383 | // Connection should remain alive. |
| 1384 | EXPECT_TRUE(connection_->connected()); |
| 1385 | } |
| 1386 | |
| 1387 | TEST_P(QuicSpdySessionTestServer, OnStreamFrameFinStaticStreamId) { |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1388 | QuicStreamId id; |
| 1389 | // Initialize HTTP/3 control stream. |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 1390 | if (VersionUsesHttp3(transport_version())) { |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1391 | id = GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); |
| 1392 | char type[] = {kControlStream}; |
| 1393 | |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1394 | QuicStreamFrame data1(id, false, 0, absl::string_view(type, 1)); |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1395 | session_.OnStreamFrame(data1); |
| 1396 | } else { |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1397 | id = QuicUtils::GetHeadersStreamId(transport_version()); |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1398 | } |
| 1399 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1400 | // Send two bytes of payload. |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1401 | QuicStreamFrame data1(id, true, 0, absl::string_view("HT")); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1402 | EXPECT_CALL(*connection_, |
| 1403 | CloseConnection( |
| 1404 | QUIC_INVALID_STREAM_ID, "Attempt to close a static stream", |
| 1405 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); |
| 1406 | session_.OnStreamFrame(data1); |
| 1407 | } |
| 1408 | |
| 1409 | TEST_P(QuicSpdySessionTestServer, OnRstStreamStaticStreamId) { |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1410 | QuicStreamId id; |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 1411 | QuicErrorCode expected_error; |
renjietang | 546c714 | 2020-03-05 14:12:10 -0800 | [diff] [blame] | 1412 | std::string error_message; |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1413 | // Initialize HTTP/3 control stream. |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 1414 | if (VersionUsesHttp3(transport_version())) { |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1415 | id = GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); |
| 1416 | char type[] = {kControlStream}; |
| 1417 | |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1418 | QuicStreamFrame data1(id, false, 0, absl::string_view(type, 1)); |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1419 | session_.OnStreamFrame(data1); |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 1420 | expected_error = QUIC_HTTP_CLOSED_CRITICAL_STREAM; |
| 1421 | error_message = "RESET_STREAM received for receive control stream"; |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1422 | } else { |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1423 | id = QuicUtils::GetHeadersStreamId(transport_version()); |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 1424 | expected_error = QUIC_INVALID_STREAM_ID; |
renjietang | 546c714 | 2020-03-05 14:12:10 -0800 | [diff] [blame] | 1425 | error_message = "Attempt to reset headers stream"; |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1426 | } |
| 1427 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1428 | // Send two bytes of payload. |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1429 | QuicRstStreamFrame rst1(kInvalidControlFrameId, id, |
| 1430 | QUIC_ERROR_PROCESSING_STREAM, 0); |
renjietang | 546c714 | 2020-03-05 14:12:10 -0800 | [diff] [blame] | 1431 | EXPECT_CALL( |
| 1432 | *connection_, |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 1433 | CloseConnection(expected_error, error_message, |
renjietang | 546c714 | 2020-03-05 14:12:10 -0800 | [diff] [blame] | 1434 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1435 | session_.OnRstStream(rst1); |
| 1436 | } |
| 1437 | |
| 1438 | TEST_P(QuicSpdySessionTestServer, OnStreamFrameInvalidStreamId) { |
| 1439 | // Send two bytes of payload. |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1440 | QuicStreamFrame data1(QuicUtils::GetInvalidStreamId(transport_version()), |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1441 | true, 0, absl::string_view("HT")); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1442 | EXPECT_CALL(*connection_, |
| 1443 | CloseConnection( |
bnc | e433f53 | 2019-04-16 13:05:27 -0700 | [diff] [blame] | 1444 | QUIC_INVALID_STREAM_ID, "Received data for an invalid stream", |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1445 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); |
| 1446 | session_.OnStreamFrame(data1); |
| 1447 | } |
| 1448 | |
| 1449 | TEST_P(QuicSpdySessionTestServer, OnRstStreamInvalidStreamId) { |
| 1450 | // Send two bytes of payload. |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1451 | QuicRstStreamFrame rst1(kInvalidControlFrameId, |
| 1452 | QuicUtils::GetInvalidStreamId(transport_version()), |
| 1453 | QUIC_ERROR_PROCESSING_STREAM, 0); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1454 | EXPECT_CALL(*connection_, |
| 1455 | CloseConnection( |
bnc | e433f53 | 2019-04-16 13:05:27 -0700 | [diff] [blame] | 1456 | QUIC_INVALID_STREAM_ID, "Received data for an invalid stream", |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1457 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); |
| 1458 | session_.OnRstStream(rst1); |
| 1459 | } |
| 1460 | |
| 1461 | TEST_P(QuicSpdySessionTestServer, HandshakeUnblocksFlowControlBlockedStream) { |
dschinazi | aab6818 | 2019-10-09 15:39:51 -0700 | [diff] [blame] | 1462 | if (connection_->version().handshake_protocol == PROTOCOL_TLS1_3) { |
| 1463 | // This test requires Google QUIC crypto because it assumes streams start |
| 1464 | // off unblocked. |
| 1465 | return; |
| 1466 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1467 | // Test that if a stream is flow control blocked, then on receipt of the SHLO |
| 1468 | // containing a suitable send window offset, the stream becomes unblocked. |
| 1469 | |
| 1470 | // Ensure that Writev consumes all the data it is given (simulate no socket |
| 1471 | // blocking). |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1472 | session_.GetMutableCryptoStream()->EstablishZeroRttEncryption(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1473 | session_.set_writev_consumes_all_data(true); |
| 1474 | |
| 1475 | // Create a stream, and send enough data to make it flow control blocked. |
| 1476 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
vasilvv | c48c871 | 2019-03-11 13:38:16 -0700 | [diff] [blame] | 1477 | std::string body(kMinimumFlowControlSendWindow, '.'); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1478 | EXPECT_FALSE(stream2->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1479 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1480 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1481 | EXPECT_CALL(*connection_, SendControlFrame(_)).Times(AtLeast(1)); |
| 1482 | stream2->WriteOrBufferBody(body, false); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1483 | EXPECT_TRUE(stream2->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1484 | EXPECT_TRUE(session_.IsConnectionFlowControlBlocked()); |
| 1485 | EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); |
| 1486 | |
| 1487 | // Now complete the crypto handshake, resulting in an increased flow control |
| 1488 | // send window. |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1489 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1490 | EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked(&session_, stream2->id())); |
| 1491 | // Stream is now unblocked. |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1492 | EXPECT_FALSE(stream2->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1493 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1494 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1495 | } |
| 1496 | |
| 1497 | TEST_P(QuicSpdySessionTestServer, |
| 1498 | HandshakeUnblocksFlowControlBlockedCryptoStream) { |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1499 | if (QuicVersionUsesCryptoFrames(transport_version()) || |
| 1500 | connection_->encrypted_control_frames()) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1501 | // QUIC version 47 onwards uses CRYPTO frames for the handshake, so this |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1502 | // test doesn't make sense for those versions. With |
| 1503 | // use_encryption_level_context, control frames can only be sent when |
| 1504 | // encryption gets established, do not send BLOCKED for crypto streams. |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1505 | return; |
| 1506 | } |
| 1507 | // Test that if the crypto stream is flow control blocked, then if the SHLO |
| 1508 | // contains a larger send window offset, the stream becomes unblocked. |
| 1509 | session_.set_writev_consumes_all_data(true); |
| 1510 | TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1511 | EXPECT_FALSE(crypto_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1512 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1513 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1514 | QuicHeadersStream* headers_stream = |
| 1515 | QuicSpdySessionPeer::GetHeadersStream(&session_); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1516 | EXPECT_FALSE(headers_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1517 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1518 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
renjietang | eab9285 | 2019-10-25 12:16:14 -0700 | [diff] [blame] | 1519 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
| 1520 | .WillOnce(Invoke(&ClearControlFrame)); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1521 | for (QuicStreamId i = 0; !crypto_stream->IsFlowControlBlocked() && i < 1000u; |
| 1522 | i++) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1523 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1524 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1525 | QuicStreamOffset offset = crypto_stream->stream_bytes_written(); |
| 1526 | QuicConfig config; |
| 1527 | CryptoHandshakeMessage crypto_message; |
fkastenholz | d3a1de9 | 2019-05-15 07:00:07 -0700 | [diff] [blame] | 1528 | config.ToHandshakeMessage(&crypto_message, transport_version()); |
fayang | 9c41f8b | 2020-10-30 13:13:06 -0700 | [diff] [blame] | 1529 | crypto_stream->SendHandshakeMessage(crypto_message, ENCRYPTION_INITIAL); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1530 | char buf[1000]; |
QUICHE team | 173c48f | 2019-11-19 16:34:44 -0800 | [diff] [blame] | 1531 | QuicDataWriter writer(1000, buf, quiche::NETWORK_BYTE_ORDER); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1532 | crypto_stream->WriteStreamData(offset, crypto_message.size(), &writer); |
| 1533 | } |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1534 | EXPECT_TRUE(crypto_stream->IsFlowControlBlocked()); |
| 1535 | EXPECT_FALSE(headers_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1536 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1537 | EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); |
| 1538 | EXPECT_FALSE(session_.HasDataToWrite()); |
| 1539 | EXPECT_TRUE(crypto_stream->HasBufferedData()); |
| 1540 | |
| 1541 | // Now complete the crypto handshake, resulting in an increased flow control |
| 1542 | // send window. |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1543 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1544 | EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked( |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1545 | &session_, QuicUtils::GetCryptoStreamId(transport_version()))); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1546 | // Stream is now unblocked and will no longer have buffered data. |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1547 | EXPECT_FALSE(crypto_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1548 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1549 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1550 | } |
| 1551 | |
| 1552 | #if !defined(OS_IOS) |
| 1553 | // This test is failing flakily for iOS bots. |
| 1554 | // http://crbug.com/425050 |
| 1555 | // NOTE: It's not possible to use the standard MAYBE_ convention to disable |
| 1556 | // this test on iOS because when this test gets instantiated it ends up with |
| 1557 | // various names that are dependent on the parameters passed. |
| 1558 | TEST_P(QuicSpdySessionTestServer, |
| 1559 | HandshakeUnblocksFlowControlBlockedHeadersStream) { |
nharper | d5c4a93 | 2019-05-13 13:58:49 -0700 | [diff] [blame] | 1560 | // This test depends on stream-level flow control for the crypto stream, which |
| 1561 | // doesn't exist when CRYPTO frames are used. |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1562 | if (QuicVersionUsesCryptoFrames(transport_version())) { |
nharper | d5c4a93 | 2019-05-13 13:58:49 -0700 | [diff] [blame] | 1563 | return; |
| 1564 | } |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1565 | |
| 1566 | // This test depends on the headers stream, which does not exist when QPACK is |
| 1567 | // used. |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 1568 | if (VersionUsesHttp3(transport_version())) { |
renjietang | 2abedac | 2019-05-20 14:04:50 -0700 | [diff] [blame] | 1569 | return; |
| 1570 | } |
| 1571 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1572 | // Test that if the header stream is flow control blocked, then if the SHLO |
| 1573 | // contains a larger send window offset, the stream becomes unblocked. |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1574 | session_.GetMutableCryptoStream()->EstablishZeroRttEncryption(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1575 | session_.set_writev_consumes_all_data(true); |
| 1576 | TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1577 | EXPECT_FALSE(crypto_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1578 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1579 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1580 | QuicHeadersStream* headers_stream = |
| 1581 | QuicSpdySessionPeer::GetHeadersStream(&session_); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1582 | EXPECT_FALSE(headers_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1583 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1584 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1585 | QuicStreamId stream_id = 5; |
| 1586 | // Write until the header stream is flow control blocked. |
| 1587 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
bnc | 5b3c3be | 2019-06-25 10:37:09 -0700 | [diff] [blame] | 1588 | .WillOnce(Invoke(&ClearControlFrame)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1589 | SpdyHeaderBlock headers; |
| 1590 | SimpleRandom random; |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1591 | while (!headers_stream->IsFlowControlBlocked() && stream_id < 2000) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1592 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1593 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
vasilvv | a163b38 | 2020-12-04 11:47:46 -0800 | [diff] [blame] | 1594 | headers["header"] = absl::StrCat(random.RandUint64(), random.RandUint64(), |
| 1595 | random.RandUint64()); |
fayang | 476683a | 2019-07-25 12:42:16 -0700 | [diff] [blame] | 1596 | session_.WriteHeadersOnHeadersStream(stream_id, headers.Clone(), true, |
| 1597 | spdy::SpdyStreamPrecedence(0), |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1598 | nullptr); |
| 1599 | stream_id += IdDelta(); |
| 1600 | } |
| 1601 | // Write once more to ensure that the headers stream has buffered data. The |
| 1602 | // random headers may have exactly filled the flow control window. |
fayang | 476683a | 2019-07-25 12:42:16 -0700 | [diff] [blame] | 1603 | session_.WriteHeadersOnHeadersStream(stream_id, std::move(headers), true, |
| 1604 | spdy::SpdyStreamPrecedence(0), nullptr); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1605 | EXPECT_TRUE(headers_stream->HasBufferedData()); |
| 1606 | |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1607 | EXPECT_TRUE(headers_stream->IsFlowControlBlocked()); |
| 1608 | EXPECT_FALSE(crypto_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1609 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1610 | EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); |
| 1611 | EXPECT_FALSE(session_.HasDataToWrite()); |
| 1612 | |
| 1613 | // Now complete the crypto handshake, resulting in an increased flow control |
| 1614 | // send window. |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1615 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1616 | |
| 1617 | // Stream is now unblocked and will no longer have buffered data. |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1618 | EXPECT_FALSE(headers_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1619 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1620 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1621 | EXPECT_TRUE(headers_stream->HasBufferedData()); |
| 1622 | EXPECT_TRUE(QuicSessionPeer::IsStreamWriteBlocked( |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1623 | &session_, QuicUtils::GetHeadersStreamId(transport_version()))); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1624 | } |
| 1625 | #endif // !defined(OS_IOS) |
| 1626 | |
| 1627 | TEST_P(QuicSpdySessionTestServer, |
| 1628 | ConnectionFlowControlAccountingRstOutOfOrder) { |
renjietang | 2be2a7e | 2020-03-12 15:47:29 -0700 | [diff] [blame] | 1629 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
| 1630 | .WillRepeatedly(Invoke(&ClearControlFrame)); |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1631 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1632 | // Test that when we receive an out of order stream RST we correctly adjust |
| 1633 | // our connection level flow control receive window. |
| 1634 | // On close, the stream should mark as consumed all bytes between the highest |
| 1635 | // byte consumed so far and the final byte offset from the RST frame. |
| 1636 | TestStream* stream = session_.CreateOutgoingBidirectionalStream(); |
| 1637 | |
| 1638 | const QuicStreamOffset kByteOffset = |
| 1639 | 1 + kInitialSessionFlowControlWindowForTest / 2; |
| 1640 | |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1641 | if (!VersionHasIetfQuicFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1642 | // For version99 the call to OnStreamReset happens as a result of receiving |
| 1643 | // the STOP_SENDING, so set up the EXPECT there. |
| 1644 | EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1645 | EXPECT_CALL(*connection_, SendControlFrame(_)); |
| 1646 | } else { |
| 1647 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 1648 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1649 | } |
| 1650 | QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream->id(), |
| 1651 | QUIC_STREAM_CANCELLED, kByteOffset); |
| 1652 | session_.OnRstStream(rst_frame); |
| 1653 | // Create and inject a STOP_SENDING frame. In GOOGLE QUIC, receiving a |
| 1654 | // RST_STREAM frame causes a two-way close. For IETF QUIC, RST_STREAM causes a |
| 1655 | // one-way close. |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1656 | if (VersionHasIetfQuicFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1657 | // Only needed for version 99/IETF QUIC. |
bnc | 187eea3 | 2020-09-02 12:16:15 -0700 | [diff] [blame] | 1658 | QuicStopSendingFrame stop_sending(kInvalidControlFrameId, stream->id(), |
| 1659 | QUIC_STREAM_CANCELLED); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1660 | // Expect the RESET_STREAM that is generated in response to receiving a |
| 1661 | // STOP_SENDING. |
| 1662 | EXPECT_CALL(*connection_, |
| 1663 | OnStreamReset(stream->id(), QUIC_STREAM_CANCELLED)); |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1664 | EXPECT_CALL(*connection_, SendControlFrame(_)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1665 | session_.OnStopSendingFrame(stop_sending); |
| 1666 | } |
| 1667 | |
| 1668 | EXPECT_EQ(kByteOffset, session_.flow_controller()->bytes_consumed()); |
| 1669 | } |
| 1670 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1671 | TEST_P(QuicSpdySessionTestServer, InvalidStreamFlowControlWindowInHandshake) { |
nharper | f5e6845 | 2019-05-29 17:24:18 -0700 | [diff] [blame] | 1672 | if (GetParam().handshake_protocol == PROTOCOL_TLS1_3) { |
renjietang | bb2e22a | 2019-09-12 15:46:39 -0700 | [diff] [blame] | 1673 | // IETF Quic doesn't require a minimum flow control window. |
nharper | f5e6845 | 2019-05-29 17:24:18 -0700 | [diff] [blame] | 1674 | return; |
| 1675 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1676 | // Test that receipt of an invalid (< default) stream flow control window from |
| 1677 | // the peer results in the connection being torn down. |
| 1678 | const uint32_t kInvalidWindow = kMinimumFlowControlSendWindow - 1; |
| 1679 | QuicConfigPeer::SetReceivedInitialStreamFlowControlWindow(session_.config(), |
| 1680 | kInvalidWindow); |
| 1681 | |
| 1682 | EXPECT_CALL(*connection_, |
| 1683 | CloseConnection(QUIC_FLOW_CONTROL_INVALID_WINDOW, _, _)); |
| 1684 | session_.OnConfigNegotiated(); |
| 1685 | } |
| 1686 | |
renjietang | 203926d | 2019-09-04 15:09:03 -0700 | [diff] [blame] | 1687 | TEST_P(QuicSpdySessionTestServer, TooLowUnidirectionalStreamLimitHttp3) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 1688 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 203926d | 2019-09-04 15:09:03 -0700 | [diff] [blame] | 1689 | return; |
| 1690 | } |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1691 | session_.GetMutableCryptoStream()->EstablishZeroRttEncryption(); |
renjietang | e6d9467 | 2020-01-07 10:30:10 -0800 | [diff] [blame] | 1692 | QuicConfigPeer::SetReceivedMaxUnidirectionalStreams(session_.config(), 2u); |
renjietang | c02d446 | 2020-06-08 14:25:16 -0700 | [diff] [blame] | 1693 | connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
renjietang | 203926d | 2019-09-04 15:09:03 -0700 | [diff] [blame] | 1694 | |
| 1695 | EXPECT_CALL( |
| 1696 | *connection_, |
renjietang | 241ba60 | 2020-04-29 13:29:46 -0700 | [diff] [blame] | 1697 | CloseConnection( |
| 1698 | _, "new unidirectional limit 2 decreases the current limit: 3", _)); |
renjietang | 203926d | 2019-09-04 15:09:03 -0700 | [diff] [blame] | 1699 | session_.OnConfigNegotiated(); |
| 1700 | } |
| 1701 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1702 | // Test negotiation of custom server initial flow control window. |
| 1703 | TEST_P(QuicSpdySessionTestServer, CustomFlowControlWindow) { |
| 1704 | QuicTagVector copt; |
| 1705 | copt.push_back(kIFW7); |
| 1706 | QuicConfigPeer::SetReceivedConnectionOptions(session_.config(), copt); |
renjietang | c02d446 | 2020-06-08 14:25:16 -0700 | [diff] [blame] | 1707 | connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1708 | session_.OnConfigNegotiated(); |
| 1709 | EXPECT_EQ(192 * 1024u, QuicFlowControllerPeer::ReceiveWindowSize( |
| 1710 | session_.flow_controller())); |
| 1711 | } |
| 1712 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1713 | TEST_P(QuicSpdySessionTestServer, WindowUpdateUnblocksHeadersStream) { |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1714 | if (VersionUsesHttp3(transport_version())) { |
renjietang | 67e5bcb | 2019-09-26 16:19:55 -0700 | [diff] [blame] | 1715 | // The test relies on headers stream, which no longer exists in IETF QUIC. |
nharper | f5e6845 | 2019-05-29 17:24:18 -0700 | [diff] [blame] | 1716 | return; |
| 1717 | } |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1718 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1719 | // Test that a flow control blocked headers stream gets unblocked on recipt of |
| 1720 | // a WINDOW_UPDATE frame. |
| 1721 | |
| 1722 | // Set the headers stream to be flow control blocked. |
| 1723 | QuicHeadersStream* headers_stream = |
| 1724 | QuicSpdySessionPeer::GetHeadersStream(&session_); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1725 | QuicStreamPeer::SetSendWindowOffset(headers_stream, 0); |
| 1726 | EXPECT_TRUE(headers_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1727 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1728 | EXPECT_TRUE(session_.IsStreamFlowControlBlocked()); |
| 1729 | |
| 1730 | // Unblock the headers stream by supplying a WINDOW_UPDATE. |
| 1731 | QuicWindowUpdateFrame window_update_frame(kInvalidControlFrameId, |
| 1732 | headers_stream->id(), |
| 1733 | 2 * kMinimumFlowControlSendWindow); |
| 1734 | session_.OnWindowUpdateFrame(window_update_frame); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 1735 | EXPECT_FALSE(headers_stream->IsFlowControlBlocked()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1736 | EXPECT_FALSE(session_.IsConnectionFlowControlBlocked()); |
| 1737 | EXPECT_FALSE(session_.IsStreamFlowControlBlocked()); |
| 1738 | } |
| 1739 | |
| 1740 | TEST_P(QuicSpdySessionTestServer, |
| 1741 | TooManyUnfinishedStreamsCauseServerRejectStream) { |
| 1742 | // If a buggy/malicious peer creates too many streams that are not ended |
| 1743 | // with a FIN or RST then we send an RST to refuse streams for versions other |
| 1744 | // than version 99. In version 99 the connection gets closed. |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1745 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1746 | const QuicStreamId kMaxStreams = 5; |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1747 | if (VersionHasIetfQuicFrames(transport_version())) { |
fkastenholz | d3a1de9 | 2019-05-15 07:00:07 -0700 | [diff] [blame] | 1748 | QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_, |
| 1749 | kMaxStreams); |
| 1750 | } else { |
| 1751 | QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams); |
| 1752 | } |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 1753 | // GetNth assumes that both the crypto and header streams have been |
| 1754 | // open, but the stream id manager, using GetFirstBidirectional... only |
| 1755 | // assumes that the crypto stream is open. This means that GetNth...(0) |
| 1756 | // Will return stream ID == 8 (with id ==0 for crypto and id==4 for headers). |
| 1757 | // It also means that GetNth(kMax..=5) returns 28 (streams 0/1/2/3/4 are ids |
| 1758 | // 8, 12, 16, 20, 24, respectively, so stream#5 is stream id 28). |
| 1759 | // However, the stream ID manager does not assume stream 4 is for headers. |
| 1760 | // The ID manager would assume that stream#5 is streamid 24. |
| 1761 | // In order to make this all work out properly, kFinalStreamId will |
renjietang | f8bde4b | 2020-10-02 13:25:08 -0700 | [diff] [blame] | 1762 | // be set to GetNth...(kMaxStreams-1)... but only for IETF QUIC |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1763 | const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0); |
| 1764 | const QuicStreamId kFinalStreamId = |
| 1765 | GetNthClientInitiatedBidirectionalId(kMaxStreams); |
| 1766 | // Create kMaxStreams data streams, and close them all without receiving a |
| 1767 | // FIN or a RST_STREAM from the client. |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 1768 | const QuicStreamId kNextId = QuicUtils::StreamIdDelta(transport_version()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1769 | for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; i += kNextId) { |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1770 | QuicStreamFrame data1(i, false, 0, absl::string_view("HT")); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1771 | session_.OnStreamFrame(data1); |
renjietang | fcf3f18 | 2020-06-30 15:06:56 -0700 | [diff] [blame] | 1772 | CloseStream(i); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1773 | } |
| 1774 | // Try and open a stream that exceeds the limit. |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1775 | if (!VersionHasIetfQuicFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1776 | // On versions other than 99, opening such a stream results in a |
| 1777 | // RST_STREAM. |
| 1778 | EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); |
| 1779 | EXPECT_CALL(*connection_, |
| 1780 | OnStreamReset(kFinalStreamId, QUIC_REFUSED_STREAM)) |
| 1781 | .Times(1); |
| 1782 | } else { |
| 1783 | // On version 99 opening such a stream results in a connection close. |
dschinazi | 7ae3d60 | 2019-06-19 11:04:29 -0700 | [diff] [blame] | 1784 | EXPECT_CALL( |
| 1785 | *connection_, |
| 1786 | CloseConnection(QUIC_INVALID_STREAM_ID, |
| 1787 | testing::MatchesRegex( |
renjietang | 118c8ac | 2019-07-30 11:43:59 -0700 | [diff] [blame] | 1788 | "Stream id \\d+ would exceed stream count limit 5"), |
dschinazi | 7ae3d60 | 2019-06-19 11:04:29 -0700 | [diff] [blame] | 1789 | _)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1790 | } |
| 1791 | // Create one more data streams to exceed limit of open stream. |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1792 | QuicStreamFrame data1(kFinalStreamId, false, 0, absl::string_view("HT")); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1793 | session_.OnStreamFrame(data1); |
| 1794 | } |
| 1795 | |
| 1796 | TEST_P(QuicSpdySessionTestServer, DrainingStreamsDoNotCountAsOpened) { |
| 1797 | // Verify that a draining stream (which has received a FIN but not consumed |
| 1798 | // it) does not count against the open quota (because it is closed from the |
| 1799 | // protocol point of view). |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1800 | CompleteHandshake(); |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1801 | if (VersionHasIetfQuicFrames(transport_version())) { |
fkastenholz | 56055be | 2019-09-17 11:17:37 -0700 | [diff] [blame] | 1802 | // Simulate receiving a config. so that MAX_STREAMS/etc frames may |
| 1803 | // be transmitted |
| 1804 | QuicSessionPeer::set_is_configured(&session_, true); |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 1805 | // Version 99 will result in a MAX_STREAMS frame as streams are consumed |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1806 | // (via the OnStreamFrame call) and then released (via |
| 1807 | // StreamDraining). Eventually this node will believe that the peer is |
fkastenholz | 3c4eabf | 2019-04-22 07:49:59 -0700 | [diff] [blame] | 1808 | // running low on available stream ids and then send a MAX_STREAMS frame, |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1809 | // caught by this EXPECT_CALL. |
| 1810 | EXPECT_CALL(*connection_, SendControlFrame(_)).Times(1); |
| 1811 | } else { |
| 1812 | EXPECT_CALL(*connection_, SendControlFrame(_)).Times(0); |
| 1813 | } |
| 1814 | EXPECT_CALL(*connection_, OnStreamReset(_, QUIC_REFUSED_STREAM)).Times(0); |
| 1815 | const QuicStreamId kMaxStreams = 5; |
fkastenholz | 305e173 | 2019-06-18 05:01:22 -0700 | [diff] [blame] | 1816 | if (VersionHasIetfQuicFrames(transport_version())) { |
fkastenholz | d3a1de9 | 2019-05-15 07:00:07 -0700 | [diff] [blame] | 1817 | QuicSessionPeer::SetMaxOpenIncomingBidirectionalStreams(&session_, |
| 1818 | kMaxStreams); |
| 1819 | } else { |
| 1820 | QuicSessionPeer::SetMaxOpenIncomingStreams(&session_, kMaxStreams); |
| 1821 | } |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1822 | |
| 1823 | // Create kMaxStreams + 1 data streams, and mark them draining. |
| 1824 | const QuicStreamId kFirstStreamId = GetNthClientInitiatedBidirectionalId(0); |
| 1825 | const QuicStreamId kFinalStreamId = |
| 1826 | GetNthClientInitiatedBidirectionalId(kMaxStreams + 1); |
| 1827 | for (QuicStreamId i = kFirstStreamId; i < kFinalStreamId; i += IdDelta()) { |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1828 | QuicStreamFrame data1(i, true, 0, absl::string_view("HT")); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1829 | session_.OnStreamFrame(data1); |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 1830 | EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
fayang | be6d664 | 2020-04-16 14:15:34 -0700 | [diff] [blame] | 1831 | session_.StreamDraining(i, /*unidirectional=*/false); |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 1832 | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1833 | } |
| 1834 | } |
| 1835 | |
bnc | 9a7a673 | 2020-04-14 06:08:02 -0700 | [diff] [blame] | 1836 | TEST_P(QuicSpdySessionTestServer, ReduceMaxPushId) { |
| 1837 | if (!VersionUsesHttp3(transport_version())) { |
| 1838 | return; |
| 1839 | } |
| 1840 | |
| 1841 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 1842 | session_.set_debug_visitor(&debug_visitor); |
| 1843 | |
| 1844 | // Use an arbitrary stream id for incoming control stream. |
| 1845 | QuicStreamId stream_id = |
| 1846 | GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); |
| 1847 | char type[] = {kControlStream}; |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1848 | absl::string_view stream_type(type, 1); |
bnc | 9a7a673 | 2020-04-14 06:08:02 -0700 | [diff] [blame] | 1849 | |
| 1850 | QuicStreamOffset offset = 0; |
| 1851 | QuicStreamFrame data1(stream_id, false, offset, stream_type); |
| 1852 | offset += stream_type.length(); |
| 1853 | EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(stream_id)); |
| 1854 | session_.OnStreamFrame(data1); |
| 1855 | EXPECT_EQ(stream_id, |
| 1856 | QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id()); |
| 1857 | |
| 1858 | SettingsFrame settings; |
| 1859 | std::string settings_frame = EncodeSettings(settings); |
| 1860 | QuicStreamFrame data2(stream_id, false, offset, settings_frame); |
| 1861 | offset += settings_frame.length(); |
| 1862 | |
| 1863 | EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(settings)); |
| 1864 | session_.OnStreamFrame(data2); |
| 1865 | |
| 1866 | std::string max_push_id_frame1 = SerializeMaxPushIdFrame(/* push_id = */ 3); |
| 1867 | QuicStreamFrame data3(stream_id, false, offset, max_push_id_frame1); |
| 1868 | offset += max_push_id_frame1.length(); |
| 1869 | |
| 1870 | EXPECT_CALL(debug_visitor, OnMaxPushIdFrameReceived(_)); |
| 1871 | session_.OnStreamFrame(data3); |
| 1872 | |
| 1873 | std::string max_push_id_frame2 = SerializeMaxPushIdFrame(/* push_id = */ 1); |
| 1874 | QuicStreamFrame data4(stream_id, false, offset, max_push_id_frame2); |
| 1875 | |
| 1876 | EXPECT_CALL(debug_visitor, OnMaxPushIdFrameReceived(_)); |
| 1877 | EXPECT_CALL(*connection_, |
| 1878 | CloseConnection(QUIC_HTTP_INVALID_MAX_PUSH_ID, |
| 1879 | "MAX_PUSH_ID received with value 1 which is " |
| 1880 | "smaller that previously received value 3", |
| 1881 | _)); |
| 1882 | session_.OnStreamFrame(data4); |
| 1883 | } |
| 1884 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1885 | class QuicSpdySessionTestClient : public QuicSpdySessionTestBase { |
| 1886 | protected: |
| 1887 | QuicSpdySessionTestClient() |
| 1888 | : QuicSpdySessionTestBase(Perspective::IS_CLIENT) {} |
| 1889 | }; |
| 1890 | |
| 1891 | INSTANTIATE_TEST_SUITE_P(Tests, |
| 1892 | QuicSpdySessionTestClient, |
dschinazi | 142051a | 2019-09-18 18:17:29 -0700 | [diff] [blame] | 1893 | ::testing::ValuesIn(AllSupportedVersions()), |
| 1894 | ::testing::PrintToStringParamName()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1895 | |
renjietang | e76b2da | 2019-05-13 14:50:23 -0700 | [diff] [blame] | 1896 | TEST_P(QuicSpdySessionTestClient, UsesPendingStreams) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 1897 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | e76b2da | 2019-05-13 14:50:23 -0700 | [diff] [blame] | 1898 | return; |
| 1899 | } |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 1900 | EXPECT_TRUE(session_.UsesPendingStreams()); |
renjietang | e76b2da | 2019-05-13 14:50:23 -0700 | [diff] [blame] | 1901 | } |
| 1902 | |
renjietang | a553da0 | 2019-06-24 11:57:04 -0700 | [diff] [blame] | 1903 | // Regression test for crbug.com/977581. |
| 1904 | TEST_P(QuicSpdySessionTestClient, BadStreamFramePendingStream) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 1905 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | a553da0 | 2019-06-24 11:57:04 -0700 | [diff] [blame] | 1906 | return; |
| 1907 | } |
| 1908 | |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 1909 | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
renjietang | a553da0 | 2019-06-24 11:57:04 -0700 | [diff] [blame] | 1910 | QuicStreamId stream_id1 = |
| 1911 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
| 1912 | // A bad stream frame with no data and no fin. |
| 1913 | QuicStreamFrame data1(stream_id1, false, 0, 0); |
fayang | 528c36b | 2020-12-23 07:02:09 -0800 | [diff] [blame] | 1914 | if (!GetQuicReloadableFlag(quic_accept_empty_stream_frame_with_no_fin)) { |
| 1915 | EXPECT_CALL(*connection_, CloseConnection(_, _, _)) |
| 1916 | .WillOnce( |
| 1917 | Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); |
mattm | 55006b0 | 2021-01-14 15:09:54 -0800 | [diff] [blame] | 1918 | EXPECT_CALL(*connection_, SendConnectionClosePacket(_, _, _)); |
fayang | 528c36b | 2020-12-23 07:02:09 -0800 | [diff] [blame] | 1919 | } |
renjietang | a553da0 | 2019-06-24 11:57:04 -0700 | [diff] [blame] | 1920 | session_.OnStreamFrame(data1); |
| 1921 | } |
| 1922 | |
renjietang | 56d2ed2 | 2019-10-22 14:11:55 -0700 | [diff] [blame] | 1923 | TEST_P(QuicSpdySessionTestClient, PendingStreamKeepsConnectionAlive) { |
| 1924 | if (!VersionUsesHttp3(transport_version())) { |
| 1925 | return; |
| 1926 | } |
| 1927 | |
| 1928 | QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( |
| 1929 | transport_version(), Perspective::IS_SERVER); |
| 1930 | |
| 1931 | QuicStreamFrame frame(stream_id, false, 1, "test"); |
| 1932 | EXPECT_FALSE(session_.ShouldKeepConnectionAlive()); |
| 1933 | session_.OnStreamFrame(frame); |
| 1934 | EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); |
| 1935 | EXPECT_TRUE(session_.ShouldKeepConnectionAlive()); |
| 1936 | } |
| 1937 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1938 | TEST_P(QuicSpdySessionTestClient, AvailableStreamsClient) { |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 1939 | ASSERT_TRUE(session_.GetOrCreateStream( |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1940 | GetNthServerInitiatedBidirectionalId(2)) != nullptr); |
| 1941 | // Both server initiated streams with smaller stream IDs should be available. |
| 1942 | EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( |
| 1943 | &session_, GetNthServerInitiatedBidirectionalId(0))); |
| 1944 | EXPECT_TRUE(QuicSessionPeer::IsStreamAvailable( |
| 1945 | &session_, GetNthServerInitiatedBidirectionalId(1))); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 1946 | ASSERT_TRUE(session_.GetOrCreateStream( |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1947 | GetNthServerInitiatedBidirectionalId(0)) != nullptr); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 1948 | ASSERT_TRUE(session_.GetOrCreateStream( |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1949 | GetNthServerInitiatedBidirectionalId(1)) != nullptr); |
| 1950 | // And client initiated stream ID should be not available. |
| 1951 | EXPECT_FALSE(QuicSessionPeer::IsStreamAvailable( |
| 1952 | &session_, GetNthClientInitiatedBidirectionalId(0))); |
| 1953 | } |
| 1954 | |
bnc | a980ce5 | 2019-06-14 10:49:15 -0700 | [diff] [blame] | 1955 | // Regression test for b/130740258 and https://crbug.com/971779. |
| 1956 | // If headers that are too large or empty are received (these cases are handled |
| 1957 | // the same way, as QuicHeaderList clears itself when headers exceed the limit), |
| 1958 | // then the stream is reset. No more frames must be sent in this case. |
| 1959 | TEST_P(QuicSpdySessionTestClient, TooLargeHeadersMustNotCauseWriteAfterReset) { |
| 1960 | // In IETF QUIC, HEADERS do not carry FIN flag, and OnStreamHeaderList() is |
| 1961 | // never called after an error, including too large headers. |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 1962 | if (VersionUsesHttp3(transport_version())) { |
bnc | a980ce5 | 2019-06-14 10:49:15 -0700 | [diff] [blame] | 1963 | return; |
| 1964 | } |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1965 | CompleteHandshake(); |
bnc | a980ce5 | 2019-06-14 10:49:15 -0700 | [diff] [blame] | 1966 | TestStream* stream = session_.CreateOutgoingBidirectionalStream(); |
| 1967 | |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 1968 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 1969 | .WillOnce(Return(WriteResult(WRITE_STATUS_OK, 0))); |
bnc | a980ce5 | 2019-06-14 10:49:15 -0700 | [diff] [blame] | 1970 | // Write headers with FIN set to close write side of stream. |
| 1971 | // Header block does not matter. |
| 1972 | stream->WriteHeaders(SpdyHeaderBlock(), /* fin = */ true, nullptr); |
| 1973 | |
| 1974 | // Receive headers that are too large or empty, with FIN set. |
| 1975 | // This causes the stream to be reset. No frames must be written after this. |
| 1976 | QuicHeaderList headers; |
| 1977 | EXPECT_CALL(*connection_, SendControlFrame(_)); |
| 1978 | EXPECT_CALL(*connection_, |
| 1979 | OnStreamReset(stream->id(), QUIC_HEADERS_TOO_LARGE)); |
| 1980 | stream->OnStreamHeaderList(/* fin = */ true, |
| 1981 | headers.uncompressed_header_bytes(), headers); |
| 1982 | } |
| 1983 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1984 | TEST_P(QuicSpdySessionTestClient, RecordFinAfterReadSideClosed) { |
| 1985 | // Verify that an incoming FIN is recorded in a stream object even if the read |
| 1986 | // side has been closed. This prevents an entry from being made in |
| 1987 | // locally_closed_streams_highest_offset_ (which will never be deleted). |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 1988 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1989 | TestStream* stream = session_.CreateOutgoingBidirectionalStream(); |
| 1990 | QuicStreamId stream_id = stream->id(); |
| 1991 | |
| 1992 | // Close the read side manually. |
| 1993 | QuicStreamPeer::CloseReadSide(stream); |
| 1994 | |
| 1995 | // Receive a stream data frame with FIN. |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 1996 | QuicStreamFrame frame(stream_id, true, 0, absl::string_view()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 1997 | session_.OnStreamFrame(frame); |
| 1998 | EXPECT_TRUE(stream->fin_received()); |
| 1999 | |
| 2000 | // Reset stream locally. |
| 2001 | EXPECT_CALL(*connection_, SendControlFrame(_)); |
| 2002 | EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _)); |
| 2003 | stream->Reset(QUIC_STREAM_CANCELLED); |
| 2004 | EXPECT_TRUE(QuicStreamPeer::read_side_closed(stream)); |
| 2005 | |
| 2006 | EXPECT_TRUE(connection_->connected()); |
| 2007 | EXPECT_TRUE(QuicSessionPeer::IsStreamClosed(&session_, stream_id)); |
| 2008 | EXPECT_FALSE(QuicSessionPeer::IsStreamCreated(&session_, stream_id)); |
| 2009 | |
| 2010 | // The stream is not waiting for the arrival of the peer's final offset as it |
| 2011 | // was received with the FIN earlier. |
| 2012 | EXPECT_EQ( |
| 2013 | 0u, |
| 2014 | QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(&session_).size()); |
| 2015 | } |
| 2016 | |
| 2017 | TEST_P(QuicSpdySessionTestClient, WritePriority) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2018 | if (VersionUsesHttp3(transport_version())) { |
renjietang | 67e5bcb | 2019-09-26 16:19:55 -0700 | [diff] [blame] | 2019 | // IETF QUIC currently doesn't support PRIORITY. |
renjietang | 7cedcbf | 2019-07-19 12:06:36 -0700 | [diff] [blame] | 2020 | return; |
| 2021 | } |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 2022 | CompleteHandshake(); |
renjietang | 2be2a7e | 2020-03-12 15:47:29 -0700 | [diff] [blame] | 2023 | |
renjietang | fbeb5bf | 2019-04-19 15:06:20 -0700 | [diff] [blame] | 2024 | TestHeadersStream* headers_stream; |
renjietang | 9818f8c | 2019-07-16 11:12:27 -0700 | [diff] [blame] | 2025 | QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr); |
renjietang | b663b86 | 2019-07-08 16:02:39 -0700 | [diff] [blame] | 2026 | headers_stream = new TestHeadersStream(&session_); |
renjietang | 9818f8c | 2019-07-16 11:12:27 -0700 | [diff] [blame] | 2027 | QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2028 | |
| 2029 | // Make packet writer blocked so |headers_stream| will buffer its write data. |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 2030 | EXPECT_CALL(*writer_, IsWriteBlocked()).WillRepeatedly(Return(true)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2031 | |
| 2032 | const QuicStreamId id = 4; |
| 2033 | const QuicStreamId parent_stream_id = 9; |
| 2034 | const SpdyPriority priority = kV3HighestPriority; |
| 2035 | const bool exclusive = true; |
| 2036 | session_.WritePriority(id, parent_stream_id, |
| 2037 | Spdy3PriorityToHttp2Weight(priority), exclusive); |
| 2038 | |
| 2039 | QuicStreamSendBuffer& send_buffer = |
| 2040 | QuicStreamPeer::SendBuffer(headers_stream); |
fayang | 8265a2a | 2019-10-16 11:23:51 -0700 | [diff] [blame] | 2041 | ASSERT_EQ(1u, send_buffer.size()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2042 | |
fayang | 8265a2a | 2019-10-16 11:23:51 -0700 | [diff] [blame] | 2043 | SpdyPriorityIR priority_frame( |
| 2044 | id, parent_stream_id, Spdy3PriorityToHttp2Weight(priority), exclusive); |
| 2045 | SpdyFramer spdy_framer(SpdyFramer::ENABLE_COMPRESSION); |
| 2046 | SpdySerializedFrame frame = spdy_framer.SerializeFrame(priority_frame); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2047 | |
fayang | 8265a2a | 2019-10-16 11:23:51 -0700 | [diff] [blame] | 2048 | const QuicMemSlice& slice = |
| 2049 | QuicStreamSendBufferPeer::CurrentWriteSlice(&send_buffer)->slice; |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2050 | EXPECT_EQ(absl::string_view(frame.data(), frame.size()), |
| 2051 | absl::string_view(slice.data(), slice.length())); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2052 | } |
| 2053 | |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2054 | TEST_P(QuicSpdySessionTestClient, Http3ServerPush) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2055 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2056 | return; |
| 2057 | } |
| 2058 | |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 2059 | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
bnc | a7ac847 | 2019-07-27 11:48:50 -0700 | [diff] [blame] | 2060 | |
| 2061 | // Push unidirectional stream is type 0x01. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2062 | std::string frame_type1 = absl::HexStringToBytes("01"); |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2063 | QuicStreamId stream_id1 = |
| 2064 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
bnc | a7ac847 | 2019-07-27 11:48:50 -0700 | [diff] [blame] | 2065 | session_.OnStreamFrame(QuicStreamFrame(stream_id1, /* fin = */ false, |
| 2066 | /* offset = */ 0, frame_type1)); |
| 2067 | |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 2068 | EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 2069 | QuicStream* stream = session_.GetOrCreateStream(stream_id1); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 2070 | EXPECT_EQ(1u, QuicStreamPeer::bytes_consumed(stream)); |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2071 | EXPECT_EQ(1u, session_.flow_controller()->bytes_consumed()); |
| 2072 | |
bnc | a7ac847 | 2019-07-27 11:48:50 -0700 | [diff] [blame] | 2073 | // The same stream type can be encoded differently. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2074 | std::string frame_type2 = absl::HexStringToBytes("80000001"); |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2075 | QuicStreamId stream_id2 = |
| 2076 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1); |
bnc | a7ac847 | 2019-07-27 11:48:50 -0700 | [diff] [blame] | 2077 | session_.OnStreamFrame(QuicStreamFrame(stream_id2, /* fin = */ false, |
| 2078 | /* offset = */ 0, frame_type2)); |
| 2079 | |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 2080 | EXPECT_EQ(2u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
renjietang | 880d243 | 2019-07-16 13:14:37 -0700 | [diff] [blame] | 2081 | stream = session_.GetOrCreateStream(stream_id2); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 2082 | EXPECT_EQ(4u, QuicStreamPeer::bytes_consumed(stream)); |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2083 | EXPECT_EQ(5u, session_.flow_controller()->bytes_consumed()); |
| 2084 | } |
| 2085 | |
| 2086 | TEST_P(QuicSpdySessionTestClient, Http3ServerPushOutofOrderFrame) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2087 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2088 | return; |
| 2089 | } |
| 2090 | |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 2091 | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2092 | |
bnc | a7ac847 | 2019-07-27 11:48:50 -0700 | [diff] [blame] | 2093 | // Push unidirectional stream is type 0x01. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2094 | std::string frame_type = absl::HexStringToBytes("01"); |
bnc | a7ac847 | 2019-07-27 11:48:50 -0700 | [diff] [blame] | 2095 | // The first field of a push stream is the Push ID. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2096 | std::string push_id = absl::HexStringToBytes("4000"); |
bnc | a7ac847 | 2019-07-27 11:48:50 -0700 | [diff] [blame] | 2097 | |
| 2098 | QuicStreamId stream_id = |
| 2099 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
| 2100 | |
| 2101 | QuicStreamFrame data1(stream_id, |
| 2102 | /* fin = */ false, /* offset = */ 0, frame_type); |
| 2103 | QuicStreamFrame data2(stream_id, |
| 2104 | /* fin = */ false, /* offset = */ frame_type.size(), |
| 2105 | push_id); |
| 2106 | |
| 2107 | // Receiving some stream data without stream type does not open the stream. |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2108 | session_.OnStreamFrame(data2); |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 2109 | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
bnc | a7ac847 | 2019-07-27 11:48:50 -0700 | [diff] [blame] | 2110 | |
| 2111 | session_.OnStreamFrame(data1); |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 2112 | EXPECT_EQ(1u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
bnc | a7ac847 | 2019-07-27 11:48:50 -0700 | [diff] [blame] | 2113 | QuicStream* stream = session_.GetOrCreateStream(stream_id); |
renjietang | 07b2e8d | 2020-08-10 15:18:24 -0700 | [diff] [blame] | 2114 | EXPECT_EQ(3u, stream->highest_received_byte_offset()); |
renjietang | bb1c489 | 2019-05-24 15:58:44 -0700 | [diff] [blame] | 2115 | } |
| 2116 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2117 | TEST_P(QuicSpdySessionTestServer, OnStreamFrameLost) { |
fayang | a61d1ce | 2020-10-27 11:05:44 -0700 | [diff] [blame] | 2118 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2119 | InSequence s; |
| 2120 | |
| 2121 | // Drive congestion control manually. |
| 2122 | MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; |
| 2123 | QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); |
| 2124 | |
| 2125 | TestCryptoStream* crypto_stream = session_.GetMutableCryptoStream(); |
| 2126 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 2127 | TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 2128 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2129 | QuicStreamFrame frame2(stream2->id(), false, 0, 9); |
| 2130 | QuicStreamFrame frame3(stream4->id(), false, 0, 9); |
| 2131 | |
| 2132 | // Lost data on cryption stream, streams 2 and 4. |
| 2133 | EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true)); |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 2134 | if (!QuicVersionUsesCryptoFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2135 | EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) |
| 2136 | .WillOnce(Return(true)); |
| 2137 | } |
| 2138 | EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true)); |
| 2139 | session_.OnFrameLost(QuicFrame(frame3)); |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 2140 | if (!QuicVersionUsesCryptoFrames(transport_version())) { |
| 2141 | QuicStreamFrame frame1(QuicUtils::GetCryptoStreamId(transport_version()), |
| 2142 | false, 0, 1300); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2143 | session_.OnFrameLost(QuicFrame(frame1)); |
| 2144 | } else { |
QUICHE team | 6987b4a | 2019-03-15 16:23:04 -0700 | [diff] [blame] | 2145 | QuicCryptoFrame crypto_frame(ENCRYPTION_INITIAL, 0, 1300); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2146 | session_.OnFrameLost(QuicFrame(&crypto_frame)); |
| 2147 | } |
| 2148 | session_.OnFrameLost(QuicFrame(frame2)); |
| 2149 | EXPECT_TRUE(session_.WillingAndAbleToWrite()); |
| 2150 | |
| 2151 | // Mark streams 2 and 4 write blocked. |
| 2152 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 2153 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 2154 | |
| 2155 | // Lost data is retransmitted before new data, and retransmissions for crypto |
| 2156 | // stream go first. |
| 2157 | // Do not check congestion window when crypto stream has lost data. |
| 2158 | EXPECT_CALL(*send_algorithm, CanSend(_)).Times(0); |
renjietang | e47e15f | 2019-10-29 14:19:30 -0700 | [diff] [blame] | 2159 | if (!QuicVersionUsesCryptoFrames(transport_version())) { |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2160 | EXPECT_CALL(*crypto_stream, OnCanWrite()); |
| 2161 | EXPECT_CALL(*crypto_stream, HasPendingRetransmission()) |
| 2162 | .WillOnce(Return(false)); |
| 2163 | } |
| 2164 | // Check congestion window for non crypto streams. |
| 2165 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); |
| 2166 | EXPECT_CALL(*stream4, OnCanWrite()); |
| 2167 | EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(false)); |
| 2168 | // Connection is blocked. |
| 2169 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillRepeatedly(Return(false)); |
| 2170 | |
| 2171 | session_.OnCanWrite(); |
| 2172 | EXPECT_TRUE(session_.WillingAndAbleToWrite()); |
| 2173 | |
| 2174 | // Unblock connection. |
| 2175 | // Stream 2 retransmits lost data. |
| 2176 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); |
| 2177 | EXPECT_CALL(*stream2, OnCanWrite()); |
| 2178 | EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false)); |
| 2179 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); |
| 2180 | // Stream 2 sends new data. |
| 2181 | EXPECT_CALL(*stream2, OnCanWrite()); |
| 2182 | EXPECT_CALL(*send_algorithm, CanSend(_)).WillOnce(Return(true)); |
| 2183 | EXPECT_CALL(*stream4, OnCanWrite()); |
| 2184 | EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); |
| 2185 | |
| 2186 | session_.OnCanWrite(); |
| 2187 | EXPECT_FALSE(session_.WillingAndAbleToWrite()); |
| 2188 | } |
| 2189 | |
| 2190 | TEST_P(QuicSpdySessionTestServer, DonotRetransmitDataOfClosedStreams) { |
bnc | 6f18a82 | 2019-11-27 17:50:38 -0800 | [diff] [blame] | 2191 | // Resetting a stream will send a QPACK Stream Cancellation instruction on the |
| 2192 | // decoder stream. For simplicity, ignore writes on this stream. |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 2193 | CompleteHandshake(); |
bnc | 6f18a82 | 2019-11-27 17:50:38 -0800 | [diff] [blame] | 2194 | NoopQpackStreamSenderDelegate qpack_stream_sender_delegate; |
| 2195 | if (VersionUsesHttp3(transport_version())) { |
| 2196 | session_.qpack_decoder()->set_qpack_stream_sender_delegate( |
| 2197 | &qpack_stream_sender_delegate); |
| 2198 | } |
| 2199 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2200 | InSequence s; |
| 2201 | |
| 2202 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 2203 | TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 2204 | TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); |
| 2205 | |
| 2206 | QuicStreamFrame frame1(stream2->id(), false, 0, 9); |
| 2207 | QuicStreamFrame frame2(stream4->id(), false, 0, 9); |
| 2208 | QuicStreamFrame frame3(stream6->id(), false, 0, 9); |
| 2209 | |
| 2210 | EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(true)); |
| 2211 | EXPECT_CALL(*stream4, HasPendingRetransmission()).WillOnce(Return(true)); |
| 2212 | EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(true)); |
| 2213 | session_.OnFrameLost(QuicFrame(frame3)); |
| 2214 | session_.OnFrameLost(QuicFrame(frame2)); |
| 2215 | session_.OnFrameLost(QuicFrame(frame1)); |
| 2216 | |
| 2217 | session_.MarkConnectionLevelWriteBlocked(stream2->id()); |
| 2218 | session_.MarkConnectionLevelWriteBlocked(stream4->id()); |
| 2219 | session_.MarkConnectionLevelWriteBlocked(stream6->id()); |
| 2220 | |
| 2221 | // Reset stream 4 locally. |
| 2222 | EXPECT_CALL(*connection_, SendControlFrame(_)); |
| 2223 | EXPECT_CALL(*connection_, OnStreamReset(stream4->id(), _)); |
| 2224 | stream4->Reset(QUIC_STREAM_CANCELLED); |
| 2225 | |
| 2226 | // Verify stream 4 is removed from streams with lost data list. |
| 2227 | EXPECT_CALL(*stream6, OnCanWrite()); |
| 2228 | EXPECT_CALL(*stream6, HasPendingRetransmission()).WillOnce(Return(false)); |
| 2229 | EXPECT_CALL(*stream2, OnCanWrite()); |
| 2230 | EXPECT_CALL(*stream2, HasPendingRetransmission()).WillOnce(Return(false)); |
| 2231 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
bnc | 5b3c3be | 2019-06-25 10:37:09 -0700 | [diff] [blame] | 2232 | .WillRepeatedly(Invoke(&ClearControlFrame)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2233 | EXPECT_CALL(*stream2, OnCanWrite()); |
| 2234 | EXPECT_CALL(*stream6, OnCanWrite()); |
| 2235 | session_.OnCanWrite(); |
| 2236 | } |
| 2237 | |
| 2238 | TEST_P(QuicSpdySessionTestServer, RetransmitFrames) { |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 2239 | CompleteHandshake(); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2240 | MockSendAlgorithm* send_algorithm = new StrictMock<MockSendAlgorithm>; |
| 2241 | QuicConnectionPeer::SetSendAlgorithm(session_.connection(), send_algorithm); |
| 2242 | InSequence s; |
| 2243 | |
| 2244 | TestStream* stream2 = session_.CreateOutgoingBidirectionalStream(); |
| 2245 | TestStream* stream4 = session_.CreateOutgoingBidirectionalStream(); |
| 2246 | TestStream* stream6 = session_.CreateOutgoingBidirectionalStream(); |
| 2247 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
bnc | 5b3c3be | 2019-06-25 10:37:09 -0700 | [diff] [blame] | 2248 | .WillOnce(Invoke(&ClearControlFrame)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2249 | session_.SendWindowUpdate(stream2->id(), 9); |
| 2250 | |
| 2251 | QuicStreamFrame frame1(stream2->id(), false, 0, 9); |
| 2252 | QuicStreamFrame frame2(stream4->id(), false, 0, 9); |
| 2253 | QuicStreamFrame frame3(stream6->id(), false, 0, 9); |
| 2254 | QuicWindowUpdateFrame window_update(1, stream2->id(), 9); |
| 2255 | QuicFrames frames; |
| 2256 | frames.push_back(QuicFrame(frame1)); |
| 2257 | frames.push_back(QuicFrame(&window_update)); |
| 2258 | frames.push_back(QuicFrame(frame2)); |
| 2259 | frames.push_back(QuicFrame(frame3)); |
| 2260 | EXPECT_FALSE(session_.WillingAndAbleToWrite()); |
| 2261 | |
renjietang | 4d992bf | 2020-03-03 13:01:55 -0800 | [diff] [blame] | 2262 | EXPECT_CALL(*stream2, RetransmitStreamData(_, _, _, _)) |
| 2263 | .WillOnce(Return(true)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2264 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
bnc | 5b3c3be | 2019-06-25 10:37:09 -0700 | [diff] [blame] | 2265 | .WillOnce(Invoke(&ClearControlFrame)); |
renjietang | 4d992bf | 2020-03-03 13:01:55 -0800 | [diff] [blame] | 2266 | EXPECT_CALL(*stream4, RetransmitStreamData(_, _, _, _)) |
| 2267 | .WillOnce(Return(true)); |
| 2268 | EXPECT_CALL(*stream6, RetransmitStreamData(_, _, _, _)) |
| 2269 | .WillOnce(Return(true)); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2270 | EXPECT_CALL(*send_algorithm, OnApplicationLimited(_)); |
| 2271 | session_.RetransmitFrames(frames, TLP_RETRANSMISSION); |
| 2272 | } |
| 2273 | |
| 2274 | TEST_P(QuicSpdySessionTestServer, OnPriorityFrame) { |
| 2275 | QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); |
| 2276 | TestStream* stream = session_.CreateIncomingStream(stream_id); |
fayang | 476683a | 2019-07-25 12:42:16 -0700 | [diff] [blame] | 2277 | session_.OnPriorityFrame(stream_id, |
| 2278 | spdy::SpdyStreamPrecedence(kV3HighestPriority)); |
| 2279 | EXPECT_EQ(spdy::SpdyStreamPrecedence(kV3HighestPriority), |
| 2280 | stream->precedence()); |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 2281 | } |
| 2282 | |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 2283 | TEST_P(QuicSpdySessionTestServer, OnPriorityUpdateFrame) { |
| 2284 | if (!VersionUsesHttp3(transport_version())) { |
| 2285 | return; |
| 2286 | } |
| 2287 | |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2288 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 2289 | session_.set_debug_visitor(&debug_visitor); |
| 2290 | |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 2291 | // Create control stream. |
| 2292 | QuicStreamId receive_control_stream_id = |
| 2293 | GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); |
| 2294 | char type[] = {kControlStream}; |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2295 | absl::string_view stream_type(type, 1); |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 2296 | QuicStreamOffset offset = 0; |
| 2297 | QuicStreamFrame data1(receive_control_stream_id, false, offset, stream_type); |
| 2298 | offset += stream_type.length(); |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2299 | EXPECT_CALL(debug_visitor, |
| 2300 | OnPeerControlStreamCreated(receive_control_stream_id)); |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 2301 | session_.OnStreamFrame(data1); |
| 2302 | EXPECT_EQ(receive_control_stream_id, |
| 2303 | QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id()); |
| 2304 | |
| 2305 | // Send SETTINGS frame. |
| 2306 | std::string serialized_settings = EncodeSettings({}); |
| 2307 | QuicStreamFrame data2(receive_control_stream_id, false, offset, |
| 2308 | serialized_settings); |
| 2309 | offset += serialized_settings.length(); |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2310 | EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(_)); |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 2311 | session_.OnStreamFrame(data2); |
| 2312 | |
| 2313 | // PRIORITY_UPDATE frame for first request stream. |
| 2314 | const QuicStreamId stream_id1 = GetNthClientInitiatedBidirectionalId(0); |
| 2315 | struct PriorityUpdateFrame priority_update1; |
| 2316 | priority_update1.prioritized_element_type = REQUEST_STREAM; |
| 2317 | priority_update1.prioritized_element_id = stream_id1; |
bnc | 5f20251 | 2020-02-01 18:43:02 -0800 | [diff] [blame] | 2318 | priority_update1.priority_field_value = "u=2"; |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 2319 | std::string serialized_priority_update1 = |
| 2320 | SerializePriorityUpdateFrame(priority_update1); |
| 2321 | QuicStreamFrame data3(receive_control_stream_id, |
| 2322 | /* fin = */ false, offset, serialized_priority_update1); |
| 2323 | offset += serialized_priority_update1.size(); |
| 2324 | |
| 2325 | // PRIORITY_UPDATE frame arrives after stream creation. |
| 2326 | TestStream* stream1 = session_.CreateIncomingStream(stream_id1); |
bnc | 9e7c08c | 2020-11-09 11:24:49 -0800 | [diff] [blame] | 2327 | EXPECT_EQ(QuicStream::kDefaultUrgency, |
bnc | 5f20251 | 2020-02-01 18:43:02 -0800 | [diff] [blame] | 2328 | stream1->precedence().spdy3_priority()); |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2329 | EXPECT_CALL(debug_visitor, OnPriorityUpdateFrameReceived(priority_update1)); |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 2330 | session_.OnStreamFrame(data3); |
bnc | 5f20251 | 2020-02-01 18:43:02 -0800 | [diff] [blame] | 2331 | EXPECT_EQ(2u, stream1->precedence().spdy3_priority()); |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 2332 | |
| 2333 | // PRIORITY_UPDATE frame for second request stream. |
| 2334 | const QuicStreamId stream_id2 = GetNthClientInitiatedBidirectionalId(1); |
| 2335 | struct PriorityUpdateFrame priority_update2; |
| 2336 | priority_update2.prioritized_element_type = REQUEST_STREAM; |
| 2337 | priority_update2.prioritized_element_id = stream_id2; |
| 2338 | priority_update2.priority_field_value = "u=2"; |
| 2339 | std::string serialized_priority_update2 = |
| 2340 | SerializePriorityUpdateFrame(priority_update2); |
| 2341 | QuicStreamFrame stream_frame3(receive_control_stream_id, |
| 2342 | /* fin = */ false, offset, |
| 2343 | serialized_priority_update2); |
| 2344 | |
| 2345 | // PRIORITY_UPDATE frame arrives before stream creation, |
| 2346 | // priority value is buffered. |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2347 | EXPECT_CALL(debug_visitor, OnPriorityUpdateFrameReceived(priority_update2)); |
bnc | f6b86b2 | 2020-01-26 11:08:46 -0800 | [diff] [blame] | 2348 | session_.OnStreamFrame(stream_frame3); |
| 2349 | // Priority is applied upon stream construction. |
| 2350 | TestStream* stream2 = session_.CreateIncomingStream(stream_id2); |
| 2351 | EXPECT_EQ(2u, stream2->precedence().spdy3_priority()); |
| 2352 | } |
| 2353 | |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2354 | TEST_P(QuicSpdySessionTestServer, SimplePendingStreamType) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2355 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2356 | return; |
| 2357 | } |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 2358 | CompleteHandshake(); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2359 | char input[] = {0x04, // type |
| 2360 | 'a', 'b', 'c'}; // data |
vasilvv | fe1c8f3 | 2020-10-19 14:10:59 -0700 | [diff] [blame] | 2361 | absl::string_view payload(input, ABSL_ARRAYSIZE(input)); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2362 | |
| 2363 | // This is a server test with a client-initiated unidirectional stream. |
| 2364 | QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( |
| 2365 | transport_version(), Perspective::IS_CLIENT); |
| 2366 | |
| 2367 | for (bool fin : {true, false}) { |
| 2368 | QuicStreamFrame frame(stream_id, fin, /* offset = */ 0, payload); |
| 2369 | |
| 2370 | // A STOP_SENDING frame is sent in response to the unknown stream type. |
| 2371 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
bnc | 731f447 | 2020-03-12 04:25:18 -0700 | [diff] [blame] | 2372 | .WillOnce(Invoke([stream_id](const QuicFrame& frame) { |
| 2373 | EXPECT_EQ(STOP_SENDING_FRAME, frame.type); |
| 2374 | |
| 2375 | QuicStopSendingFrame* stop_sending = frame.stop_sending_frame; |
| 2376 | EXPECT_EQ(stream_id, stop_sending->stream_id); |
bnc | ba7be05 | 2020-11-24 06:58:02 -0800 | [diff] [blame] | 2377 | EXPECT_EQ(QUIC_STREAM_STREAM_CREATION_ERROR, |
| 2378 | stop_sending->error_code); |
bnc | 4fd6e9b | 2020-09-10 13:06:54 -0700 | [diff] [blame] | 2379 | EXPECT_EQ( |
| 2380 | static_cast<uint64_t>(QuicHttp3ErrorCode::STREAM_CREATION_ERROR), |
| 2381 | stop_sending->ietf_error_code); |
bnc | 731f447 | 2020-03-12 04:25:18 -0700 | [diff] [blame] | 2382 | |
| 2383 | return ClearControlFrame(frame); |
| 2384 | })); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2385 | session_.OnStreamFrame(frame); |
| 2386 | |
| 2387 | PendingStream* pending = |
| 2388 | QuicSessionPeer::GetPendingStream(&session_, stream_id); |
| 2389 | if (fin) { |
| 2390 | // Stream is closed if FIN is received. |
| 2391 | EXPECT_FALSE(pending); |
| 2392 | } else { |
| 2393 | ASSERT_TRUE(pending); |
| 2394 | // The pending stream must ignore read data. |
| 2395 | EXPECT_TRUE(pending->sequencer()->ignore_read_data()); |
| 2396 | } |
| 2397 | |
| 2398 | stream_id += QuicUtils::StreamIdDelta(transport_version()); |
| 2399 | } |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2400 | } |
| 2401 | |
| 2402 | TEST_P(QuicSpdySessionTestServer, SimplePendingStreamTypeOutOfOrderDelivery) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2403 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2404 | return; |
| 2405 | } |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 2406 | CompleteHandshake(); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2407 | char input[] = {0x04, // type |
| 2408 | 'a', 'b', 'c'}; // data |
vasilvv | fe1c8f3 | 2020-10-19 14:10:59 -0700 | [diff] [blame] | 2409 | absl::string_view payload(input, ABSL_ARRAYSIZE(input)); |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2410 | |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2411 | // This is a server test with a client-initiated unidirectional stream. |
| 2412 | QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( |
| 2413 | transport_version(), Perspective::IS_CLIENT); |
| 2414 | |
| 2415 | for (bool fin : {true, false}) { |
| 2416 | QuicStreamFrame frame1(stream_id, /* fin = */ false, /* offset = */ 0, |
| 2417 | payload.substr(0, 1)); |
| 2418 | QuicStreamFrame frame2(stream_id, fin, /* offset = */ 1, payload.substr(1)); |
| 2419 | |
| 2420 | // Deliver frames out of order. |
| 2421 | session_.OnStreamFrame(frame2); |
| 2422 | // A STOP_SENDING frame is sent in response to the unknown stream type. |
| 2423 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
| 2424 | .WillOnce(Invoke(&VerifyAndClearStopSendingFrame)); |
| 2425 | session_.OnStreamFrame(frame1); |
| 2426 | |
| 2427 | PendingStream* pending = |
| 2428 | QuicSessionPeer::GetPendingStream(&session_, stream_id); |
| 2429 | if (fin) { |
| 2430 | // Stream is closed if FIN is received. |
| 2431 | EXPECT_FALSE(pending); |
| 2432 | } else { |
| 2433 | ASSERT_TRUE(pending); |
| 2434 | // The pending stream must ignore read data. |
| 2435 | EXPECT_TRUE(pending->sequencer()->ignore_read_data()); |
| 2436 | } |
| 2437 | |
| 2438 | stream_id += QuicUtils::StreamIdDelta(transport_version()); |
| 2439 | } |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2440 | } |
| 2441 | |
| 2442 | TEST_P(QuicSpdySessionTestServer, |
| 2443 | MultipleBytesPendingStreamTypeOutOfOrderDelivery) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2444 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2445 | return; |
| 2446 | } |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 2447 | CompleteHandshake(); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2448 | char input[] = {0x41, 0x00, // type (256) |
| 2449 | 'a', 'b', 'c'}; // data |
vasilvv | fe1c8f3 | 2020-10-19 14:10:59 -0700 | [diff] [blame] | 2450 | absl::string_view payload(input, ABSL_ARRAYSIZE(input)); |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2451 | |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2452 | // This is a server test with a client-initiated unidirectional stream. |
| 2453 | QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId( |
| 2454 | transport_version(), Perspective::IS_CLIENT); |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2455 | |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2456 | for (bool fin : {true, false}) { |
| 2457 | QuicStreamFrame frame1(stream_id, /* fin = */ false, /* offset = */ 0, |
| 2458 | payload.substr(0, 1)); |
| 2459 | QuicStreamFrame frame2(stream_id, /* fin = */ false, /* offset = */ 1, |
| 2460 | payload.substr(1, 1)); |
| 2461 | QuicStreamFrame frame3(stream_id, fin, /* offset = */ 2, payload.substr(2)); |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2462 | |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2463 | // Deliver frames out of order. |
| 2464 | session_.OnStreamFrame(frame3); |
| 2465 | // The first byte does not contain the entire type varint. |
| 2466 | session_.OnStreamFrame(frame1); |
| 2467 | // A STOP_SENDING frame is sent in response to the unknown stream type. |
| 2468 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
| 2469 | .WillOnce(Invoke(&VerifyAndClearStopSendingFrame)); |
| 2470 | session_.OnStreamFrame(frame2); |
| 2471 | |
| 2472 | PendingStream* pending = |
| 2473 | QuicSessionPeer::GetPendingStream(&session_, stream_id); |
| 2474 | if (fin) { |
| 2475 | // Stream is closed if FIN is received. |
| 2476 | EXPECT_FALSE(pending); |
| 2477 | } else { |
| 2478 | ASSERT_TRUE(pending); |
| 2479 | // The pending stream must ignore read data. |
| 2480 | EXPECT_TRUE(pending->sequencer()->ignore_read_data()); |
| 2481 | } |
| 2482 | |
| 2483 | stream_id += QuicUtils::StreamIdDelta(transport_version()); |
| 2484 | } |
renjietang | 0c55886 | 2019-05-08 13:26:23 -0700 | [diff] [blame] | 2485 | } |
| 2486 | |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2487 | TEST_P(QuicSpdySessionTestServer, ReceiveControlStream) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2488 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2489 | return; |
| 2490 | } |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2491 | |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 2492 | CompleteHandshake(); |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2493 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 2494 | session_.set_debug_visitor(&debug_visitor); |
| 2495 | |
bnc | a0c8f5a | 2019-08-04 11:49:03 -0700 | [diff] [blame] | 2496 | // Use an arbitrary stream id. |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2497 | QuicStreamId stream_id = |
| 2498 | GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); |
| 2499 | char type[] = {kControlStream}; |
| 2500 | |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2501 | QuicStreamFrame data1(stream_id, false, 0, absl::string_view(type, 1)); |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2502 | EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(stream_id)); |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2503 | session_.OnStreamFrame(data1); |
| 2504 | EXPECT_EQ(stream_id, |
| 2505 | QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id()); |
| 2506 | |
| 2507 | SettingsFrame settings; |
bnc | a0c8f5a | 2019-08-04 11:49:03 -0700 | [diff] [blame] | 2508 | settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 512; |
renjietang | 4e9714d | 2020-09-21 15:25:23 -0700 | [diff] [blame] | 2509 | settings.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = 5; |
bnc | a0c8f5a | 2019-08-04 11:49:03 -0700 | [diff] [blame] | 2510 | settings.values[SETTINGS_QPACK_BLOCKED_STREAMS] = 42; |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2511 | std::string data = EncodeSettings(settings); |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2512 | QuicStreamFrame frame(stream_id, false, 1, absl::string_view(data)); |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2513 | |
bnc | a0c8f5a | 2019-08-04 11:49:03 -0700 | [diff] [blame] | 2514 | QpackEncoder* qpack_encoder = session_.qpack_encoder(); |
| 2515 | QpackHeaderTable* header_table = |
| 2516 | QpackEncoderPeer::header_table(qpack_encoder); |
| 2517 | |
| 2518 | EXPECT_NE(512u, |
| 2519 | QpackHeaderTablePeer::maximum_dynamic_table_capacity(header_table)); |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2520 | EXPECT_NE(5u, session_.max_outbound_header_list_size()); |
bnc | a0c8f5a | 2019-08-04 11:49:03 -0700 | [diff] [blame] | 2521 | EXPECT_NE(42u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder)); |
| 2522 | |
renjietang | a6d4737 | 2019-09-27 14:17:56 -0700 | [diff] [blame] | 2523 | EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(settings)); |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2524 | session_.OnStreamFrame(frame); |
bnc | a0c8f5a | 2019-08-04 11:49:03 -0700 | [diff] [blame] | 2525 | |
| 2526 | EXPECT_EQ(512u, |
| 2527 | QpackHeaderTablePeer::maximum_dynamic_table_capacity(header_table)); |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2528 | EXPECT_EQ(5u, session_.max_outbound_header_list_size()); |
bnc | a0c8f5a | 2019-08-04 11:49:03 -0700 | [diff] [blame] | 2529 | EXPECT_EQ(42u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder)); |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2530 | } |
| 2531 | |
| 2532 | TEST_P(QuicSpdySessionTestServer, ReceiveControlStreamOutOfOrderDelivery) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2533 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2534 | return; |
| 2535 | } |
| 2536 | // Use an arbitrary stream id. |
| 2537 | QuicStreamId stream_id = |
| 2538 | GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); |
| 2539 | char type[] = {kControlStream}; |
| 2540 | SettingsFrame settings; |
renjietang | 21fc64c | 2020-09-22 10:59:50 -0700 | [diff] [blame] | 2541 | settings.values[10] = 2; |
renjietang | 4e9714d | 2020-09-21 15:25:23 -0700 | [diff] [blame] | 2542 | settings.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = 5; |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2543 | std::string data = EncodeSettings(settings); |
| 2544 | |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2545 | QuicStreamFrame data1(stream_id, false, 1, absl::string_view(data)); |
| 2546 | QuicStreamFrame data2(stream_id, false, 0, absl::string_view(type, 1)); |
renjietang | 3a1bb80 | 2019-06-11 10:42:41 -0700 | [diff] [blame] | 2547 | |
| 2548 | session_.OnStreamFrame(data1); |
| 2549 | EXPECT_NE(5u, session_.max_outbound_header_list_size()); |
| 2550 | session_.OnStreamFrame(data2); |
| 2551 | EXPECT_EQ(5u, session_.max_outbound_header_list_size()); |
| 2552 | } |
| 2553 | |
bnc | 2f2b742 | 2019-10-02 18:10:43 -0700 | [diff] [blame] | 2554 | // Regression test for https://crbug.com/1009551. |
| 2555 | TEST_P(QuicSpdySessionTestServer, StreamClosedWhileHeaderDecodingBlocked) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2556 | if (!VersionUsesHttp3(transport_version())) { |
bnc | 2f2b742 | 2019-10-02 18:10:43 -0700 | [diff] [blame] | 2557 | return; |
| 2558 | } |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 2559 | CompleteHandshake(); |
bnc | 2f2b742 | 2019-10-02 18:10:43 -0700 | [diff] [blame] | 2560 | session_.qpack_decoder()->OnSetDynamicTableCapacity(1024); |
| 2561 | |
| 2562 | QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); |
| 2563 | TestStream* stream = session_.CreateIncomingStream(stream_id); |
| 2564 | |
| 2565 | // HEADERS frame referencing first dynamic table entry. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2566 | std::string headers_payload = absl::HexStringToBytes("020080"); |
bnc | 2f2b742 | 2019-10-02 18:10:43 -0700 | [diff] [blame] | 2567 | std::unique_ptr<char[]> headers_buffer; |
bnc | 2f2b742 | 2019-10-02 18:10:43 -0700 | [diff] [blame] | 2568 | QuicByteCount headers_frame_header_length = |
bnc | 4694272 | 2019-10-29 11:56:21 -0700 | [diff] [blame] | 2569 | HttpEncoder::SerializeHeadersFrameHeader(headers_payload.length(), |
| 2570 | &headers_buffer); |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2571 | absl::string_view headers_frame_header(headers_buffer.get(), |
| 2572 | headers_frame_header_length); |
vasilvv | a163b38 | 2020-12-04 11:47:46 -0800 | [diff] [blame] | 2573 | std::string headers = absl::StrCat(headers_frame_header, headers_payload); |
bnc | 2f2b742 | 2019-10-02 18:10:43 -0700 | [diff] [blame] | 2574 | stream->OnStreamFrame(QuicStreamFrame(stream_id, false, 0, headers)); |
| 2575 | |
| 2576 | // Decoding is blocked because dynamic table entry has not been received yet. |
| 2577 | EXPECT_FALSE(stream->headers_decompressed()); |
| 2578 | |
| 2579 | // Stream is closed and destroyed. |
| 2580 | CloseStream(stream_id); |
| 2581 | session_.CleanUpClosedStreams(); |
| 2582 | |
| 2583 | // Dynamic table entry arrived on the decoder stream. |
| 2584 | // The destroyed stream object must not be referenced. |
| 2585 | session_.qpack_decoder()->OnInsertWithoutNameReference("foo", "bar"); |
| 2586 | } |
| 2587 | |
bnc | 45a573a | 2019-10-04 09:30:02 -0700 | [diff] [blame] | 2588 | // Regression test for https://crbug.com/1011294. |
| 2589 | TEST_P(QuicSpdySessionTestServer, SessionDestroyedWhileHeaderDecodingBlocked) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2590 | if (!VersionUsesHttp3(transport_version())) { |
bnc | 45a573a | 2019-10-04 09:30:02 -0700 | [diff] [blame] | 2591 | return; |
| 2592 | } |
| 2593 | |
| 2594 | session_.qpack_decoder()->OnSetDynamicTableCapacity(1024); |
| 2595 | |
| 2596 | QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); |
| 2597 | TestStream* stream = session_.CreateIncomingStream(stream_id); |
| 2598 | |
| 2599 | // HEADERS frame referencing first dynamic table entry. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2600 | std::string headers_payload = absl::HexStringToBytes("020080"); |
bnc | 45a573a | 2019-10-04 09:30:02 -0700 | [diff] [blame] | 2601 | std::unique_ptr<char[]> headers_buffer; |
bnc | 45a573a | 2019-10-04 09:30:02 -0700 | [diff] [blame] | 2602 | QuicByteCount headers_frame_header_length = |
bnc | 4694272 | 2019-10-29 11:56:21 -0700 | [diff] [blame] | 2603 | HttpEncoder::SerializeHeadersFrameHeader(headers_payload.length(), |
| 2604 | &headers_buffer); |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2605 | absl::string_view headers_frame_header(headers_buffer.get(), |
| 2606 | headers_frame_header_length); |
vasilvv | a163b38 | 2020-12-04 11:47:46 -0800 | [diff] [blame] | 2607 | std::string headers = absl::StrCat(headers_frame_header, headers_payload); |
bnc | 45a573a | 2019-10-04 09:30:02 -0700 | [diff] [blame] | 2608 | stream->OnStreamFrame(QuicStreamFrame(stream_id, false, 0, headers)); |
| 2609 | |
| 2610 | // Decoding is blocked because dynamic table entry has not been received yet. |
| 2611 | EXPECT_FALSE(stream->headers_decompressed()); |
| 2612 | |
| 2613 | // |session_| gets destoyed. That destroys QpackDecoder, a member of |
| 2614 | // QuicSpdySession (derived class), which destroys QpackHeaderTable. |
| 2615 | // Then |*stream|, owned by QuicSession (base class) get destroyed, which |
| 2616 | // destroys QpackProgessiveDecoder, a registered Observer of QpackHeaderTable. |
| 2617 | // This must not cause a crash. |
| 2618 | } |
| 2619 | |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2620 | TEST_P(QuicSpdySessionTestClient, ResetAfterInvalidIncomingStreamType) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2621 | if (!VersionUsesHttp3(transport_version())) { |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2622 | return; |
| 2623 | } |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 2624 | CompleteHandshake(); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2625 | ASSERT_TRUE(session_.UsesPendingStreams()); |
| 2626 | |
| 2627 | const QuicStreamId stream_id = |
| 2628 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
| 2629 | |
| 2630 | // Payload consists of two bytes. The first byte is an unknown unidirectional |
| 2631 | // stream type. The second one would be the type of a push stream, but it |
| 2632 | // must not be interpreted as stream type. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2633 | std::string payload = absl::HexStringToBytes("3f01"); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2634 | QuicStreamFrame frame(stream_id, /* fin = */ false, /* offset = */ 0, |
| 2635 | payload); |
| 2636 | |
| 2637 | // A STOP_SENDING frame is sent in response to the unknown stream type. |
| 2638 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
| 2639 | .WillOnce(Invoke(&VerifyAndClearStopSendingFrame)); |
| 2640 | session_.OnStreamFrame(frame); |
| 2641 | |
| 2642 | // There are no active streams. |
fayang | 01591ae | 2020-04-23 14:14:56 -0700 | [diff] [blame] | 2643 | EXPECT_EQ(0u, QuicSessionPeer::GetNumOpenDynamicStreams(&session_)); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2644 | |
| 2645 | // The pending stream is still around, because it did not receive a FIN. |
| 2646 | PendingStream* pending = |
| 2647 | QuicSessionPeer::GetPendingStream(&session_, stream_id); |
| 2648 | ASSERT_TRUE(pending); |
| 2649 | |
| 2650 | // The pending stream must ignore read data. |
| 2651 | EXPECT_TRUE(pending->sequencer()->ignore_read_data()); |
| 2652 | |
| 2653 | // If the stream frame is received again, it should be ignored. |
| 2654 | session_.OnStreamFrame(frame); |
| 2655 | |
| 2656 | // Receive RESET_STREAM. |
| 2657 | QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_id, |
| 2658 | QUIC_STREAM_CANCELLED, |
| 2659 | /* bytes_written = */ payload.size()); |
| 2660 | |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2661 | session_.OnRstStream(rst_frame); |
| 2662 | |
| 2663 | // The stream is closed. |
| 2664 | EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); |
| 2665 | } |
| 2666 | |
| 2667 | TEST_P(QuicSpdySessionTestClient, FinAfterInvalidIncomingStreamType) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2668 | if (!VersionUsesHttp3(transport_version())) { |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2669 | return; |
| 2670 | } |
fayang | 29ce0bd | 2020-10-29 08:28:44 -0700 | [diff] [blame] | 2671 | CompleteHandshake(); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2672 | ASSERT_TRUE(session_.UsesPendingStreams()); |
| 2673 | |
| 2674 | const QuicStreamId stream_id = |
| 2675 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
| 2676 | |
| 2677 | // Payload consists of two bytes. The first byte is an unknown unidirectional |
| 2678 | // stream type. The second one would be the type of a push stream, but it |
| 2679 | // must not be interpreted as stream type. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2680 | std::string payload = absl::HexStringToBytes("3f01"); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2681 | QuicStreamFrame frame(stream_id, /* fin = */ false, /* offset = */ 0, |
| 2682 | payload); |
| 2683 | |
| 2684 | // A STOP_SENDING frame is sent in response to the unknown stream type. |
| 2685 | EXPECT_CALL(*connection_, SendControlFrame(_)) |
| 2686 | .WillOnce(Invoke(&VerifyAndClearStopSendingFrame)); |
| 2687 | session_.OnStreamFrame(frame); |
| 2688 | |
| 2689 | // The pending stream is still around, because it did not receive a FIN. |
| 2690 | PendingStream* pending = |
| 2691 | QuicSessionPeer::GetPendingStream(&session_, stream_id); |
| 2692 | EXPECT_TRUE(pending); |
| 2693 | |
| 2694 | // The pending stream must ignore read data. |
| 2695 | EXPECT_TRUE(pending->sequencer()->ignore_read_data()); |
| 2696 | |
| 2697 | // If the stream frame is received again, it should be ignored. |
| 2698 | session_.OnStreamFrame(frame); |
| 2699 | |
| 2700 | // Receive FIN. |
| 2701 | session_.OnStreamFrame(QuicStreamFrame(stream_id, /* fin = */ true, |
| 2702 | /* offset = */ payload.size(), "")); |
| 2703 | |
| 2704 | EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); |
| 2705 | } |
| 2706 | |
| 2707 | TEST_P(QuicSpdySessionTestClient, ResetInMiddleOfStreamType) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2708 | if (!VersionUsesHttp3(transport_version())) { |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2709 | return; |
| 2710 | } |
| 2711 | ASSERT_TRUE(session_.UsesPendingStreams()); |
| 2712 | |
| 2713 | const QuicStreamId stream_id = |
| 2714 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
| 2715 | |
| 2716 | // Payload is the first byte of a two byte varint encoding. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2717 | std::string payload = absl::HexStringToBytes("40"); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2718 | QuicStreamFrame frame(stream_id, /* fin = */ false, /* offset = */ 0, |
| 2719 | payload); |
| 2720 | |
| 2721 | session_.OnStreamFrame(frame); |
| 2722 | EXPECT_TRUE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); |
| 2723 | |
| 2724 | // Receive RESET_STREAM. |
| 2725 | QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_id, |
| 2726 | QUIC_STREAM_CANCELLED, |
| 2727 | /* bytes_written = */ payload.size()); |
| 2728 | |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2729 | session_.OnRstStream(rst_frame); |
| 2730 | |
| 2731 | // The stream is closed. |
| 2732 | EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); |
| 2733 | } |
| 2734 | |
| 2735 | TEST_P(QuicSpdySessionTestClient, FinInMiddleOfStreamType) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2736 | if (!VersionUsesHttp3(transport_version())) { |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2737 | return; |
| 2738 | } |
| 2739 | ASSERT_TRUE(session_.UsesPendingStreams()); |
| 2740 | |
| 2741 | const QuicStreamId stream_id = |
| 2742 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
| 2743 | |
| 2744 | // Payload is the first byte of a two byte varint encoding with a FIN. |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2745 | std::string payload = absl::HexStringToBytes("40"); |
bnc | 4ff6062 | 2019-08-09 18:55:45 -0700 | [diff] [blame] | 2746 | QuicStreamFrame frame(stream_id, /* fin = */ true, /* offset = */ 0, payload); |
| 2747 | |
| 2748 | session_.OnStreamFrame(frame); |
| 2749 | EXPECT_FALSE(QuicSessionPeer::GetPendingStream(&session_, stream_id)); |
| 2750 | } |
| 2751 | |
renjietang | d6a3567 | 2019-08-15 12:30:08 -0700 | [diff] [blame] | 2752 | TEST_P(QuicSpdySessionTestClient, DuplicateHttp3UnidirectionalStreams) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2753 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | d6a3567 | 2019-08-15 12:30:08 -0700 | [diff] [blame] | 2754 | return; |
| 2755 | } |
renjietang | aaf85e4 | 2019-09-17 10:29:50 -0700 | [diff] [blame] | 2756 | |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2757 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
renjietang | aaf85e4 | 2019-09-17 10:29:50 -0700 | [diff] [blame] | 2758 | session_.set_debug_visitor(&debug_visitor); |
| 2759 | |
renjietang | d6a3567 | 2019-08-15 12:30:08 -0700 | [diff] [blame] | 2760 | QuicStreamId id1 = |
| 2761 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
renjietang | 309b7fb | 2019-08-21 12:26:26 -0700 | [diff] [blame] | 2762 | char type1[] = {kControlStream}; |
renjietang | d6a3567 | 2019-08-15 12:30:08 -0700 | [diff] [blame] | 2763 | |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2764 | QuicStreamFrame data1(id1, false, 0, absl::string_view(type1, 1)); |
renjietang | aaf85e4 | 2019-09-17 10:29:50 -0700 | [diff] [blame] | 2765 | EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(id1)); |
renjietang | d6a3567 | 2019-08-15 12:30:08 -0700 | [diff] [blame] | 2766 | session_.OnStreamFrame(data1); |
| 2767 | QuicStreamId id2 = |
| 2768 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 1); |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2769 | QuicStreamFrame data2(id2, false, 0, absl::string_view(type1, 1)); |
renjietang | aaf85e4 | 2019-09-17 10:29:50 -0700 | [diff] [blame] | 2770 | EXPECT_CALL(debug_visitor, OnPeerControlStreamCreated(id2)).Times(0); |
renjietang | d6a3567 | 2019-08-15 12:30:08 -0700 | [diff] [blame] | 2771 | EXPECT_CALL(*connection_, |
bnc | ec3602c | 2020-03-12 17:57:59 -0700 | [diff] [blame] | 2772 | CloseConnection(QUIC_HTTP_DUPLICATE_UNIDIRECTIONAL_STREAM, |
renjietang | d6a3567 | 2019-08-15 12:30:08 -0700 | [diff] [blame] | 2773 | "Control stream is received twice.", _)); |
| 2774 | EXPECT_QUIC_PEER_BUG( |
| 2775 | session_.OnStreamFrame(data2), |
renjietang | 309b7fb | 2019-08-21 12:26:26 -0700 | [diff] [blame] | 2776 | "Received a duplicate Control stream: Closing connection."); |
| 2777 | |
| 2778 | QuicStreamId id3 = |
| 2779 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 2); |
| 2780 | char type2[]{kQpackEncoderStream}; |
| 2781 | |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2782 | QuicStreamFrame data3(id3, false, 0, absl::string_view(type2, 1)); |
renjietang | aaf85e4 | 2019-09-17 10:29:50 -0700 | [diff] [blame] | 2783 | EXPECT_CALL(debug_visitor, OnPeerQpackEncoderStreamCreated(id3)); |
renjietang | 309b7fb | 2019-08-21 12:26:26 -0700 | [diff] [blame] | 2784 | session_.OnStreamFrame(data3); |
| 2785 | |
| 2786 | QuicStreamId id4 = |
| 2787 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2788 | QuicStreamFrame data4(id4, false, 0, absl::string_view(type2, 1)); |
renjietang | aaf85e4 | 2019-09-17 10:29:50 -0700 | [diff] [blame] | 2789 | EXPECT_CALL(debug_visitor, OnPeerQpackEncoderStreamCreated(id4)).Times(0); |
renjietang | 309b7fb | 2019-08-21 12:26:26 -0700 | [diff] [blame] | 2790 | EXPECT_CALL(*connection_, |
bnc | ec3602c | 2020-03-12 17:57:59 -0700 | [diff] [blame] | 2791 | CloseConnection(QUIC_HTTP_DUPLICATE_UNIDIRECTIONAL_STREAM, |
renjietang | 309b7fb | 2019-08-21 12:26:26 -0700 | [diff] [blame] | 2792 | "QPACK encoder stream is received twice.", _)); |
| 2793 | EXPECT_QUIC_PEER_BUG( |
| 2794 | session_.OnStreamFrame(data4), |
| 2795 | "Received a duplicate QPACK encoder stream: Closing connection."); |
| 2796 | |
| 2797 | QuicStreamId id5 = |
| 2798 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 4); |
| 2799 | char type3[]{kQpackDecoderStream}; |
| 2800 | |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2801 | QuicStreamFrame data5(id5, false, 0, absl::string_view(type3, 1)); |
renjietang | aaf85e4 | 2019-09-17 10:29:50 -0700 | [diff] [blame] | 2802 | EXPECT_CALL(debug_visitor, OnPeerQpackDecoderStreamCreated(id5)); |
renjietang | 309b7fb | 2019-08-21 12:26:26 -0700 | [diff] [blame] | 2803 | session_.OnStreamFrame(data5); |
| 2804 | |
| 2805 | QuicStreamId id6 = |
| 2806 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 5); |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2807 | QuicStreamFrame data6(id6, false, 0, absl::string_view(type3, 1)); |
renjietang | aaf85e4 | 2019-09-17 10:29:50 -0700 | [diff] [blame] | 2808 | EXPECT_CALL(debug_visitor, OnPeerQpackDecoderStreamCreated(id6)).Times(0); |
renjietang | 309b7fb | 2019-08-21 12:26:26 -0700 | [diff] [blame] | 2809 | EXPECT_CALL(*connection_, |
bnc | ec3602c | 2020-03-12 17:57:59 -0700 | [diff] [blame] | 2810 | CloseConnection(QUIC_HTTP_DUPLICATE_UNIDIRECTIONAL_STREAM, |
renjietang | 309b7fb | 2019-08-21 12:26:26 -0700 | [diff] [blame] | 2811 | "QPACK decoder stream is received twice.", _)); |
| 2812 | EXPECT_QUIC_PEER_BUG( |
| 2813 | session_.OnStreamFrame(data6), |
| 2814 | "Received a duplicate QPACK decoder stream: Closing connection."); |
renjietang | d6a3567 | 2019-08-15 12:30:08 -0700 | [diff] [blame] | 2815 | } |
| 2816 | |
bnc | 331385e | 2019-09-11 05:02:16 -0700 | [diff] [blame] | 2817 | TEST_P(QuicSpdySessionTestClient, EncoderStreamError) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2818 | if (!VersionUsesHttp3(transport_version())) { |
bnc | 331385e | 2019-09-11 05:02:16 -0700 | [diff] [blame] | 2819 | return; |
| 2820 | } |
| 2821 | |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2822 | std::string data = absl::HexStringToBytes( |
bnc | 331385e | 2019-09-11 05:02:16 -0700 | [diff] [blame] | 2823 | "02" // Encoder stream. |
| 2824 | "00"); // Duplicate entry 0, but no entries exist. |
| 2825 | |
| 2826 | QuicStreamId stream_id = |
| 2827 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
| 2828 | |
| 2829 | QuicStreamFrame frame(stream_id, /* fin = */ false, /* offset = */ 0, data); |
| 2830 | |
| 2831 | EXPECT_CALL( |
| 2832 | *connection_, |
bnc | 4e44010 | 2020-10-20 17:32:29 -0700 | [diff] [blame] | 2833 | CloseConnection( |
| 2834 | GetQuicReloadableFlag(quic_granular_qpack_error_codes) |
| 2835 | ? QUIC_QPACK_ENCODER_STREAM_DUPLICATE_INVALID_RELATIVE_INDEX |
| 2836 | : QUIC_QPACK_ENCODER_STREAM_ERROR, |
| 2837 | "Encoder stream error: Invalid relative index.", _)); |
bnc | 331385e | 2019-09-11 05:02:16 -0700 | [diff] [blame] | 2838 | session_.OnStreamFrame(frame); |
| 2839 | } |
| 2840 | |
| 2841 | TEST_P(QuicSpdySessionTestClient, DecoderStreamError) { |
renjietang | a29a96a | 2019-10-10 12:47:50 -0700 | [diff] [blame] | 2842 | if (!VersionUsesHttp3(transport_version())) { |
bnc | 331385e | 2019-09-11 05:02:16 -0700 | [diff] [blame] | 2843 | return; |
| 2844 | } |
| 2845 | |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 2846 | std::string data = absl::HexStringToBytes( |
bnc | 331385e | 2019-09-11 05:02:16 -0700 | [diff] [blame] | 2847 | "03" // Decoder stream. |
| 2848 | "00"); // Insert Count Increment with forbidden increment value of zero. |
| 2849 | |
| 2850 | QuicStreamId stream_id = |
| 2851 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
| 2852 | |
| 2853 | QuicStreamFrame frame(stream_id, /* fin = */ false, /* offset = */ 0, data); |
| 2854 | |
| 2855 | EXPECT_CALL( |
| 2856 | *connection_, |
bnc | 4e44010 | 2020-10-20 17:32:29 -0700 | [diff] [blame] | 2857 | CloseConnection(GetQuicReloadableFlag(quic_granular_qpack_error_codes) |
| 2858 | ? QUIC_QPACK_DECODER_STREAM_INVALID_ZERO_INCREMENT |
| 2859 | : QUIC_QPACK_DECODER_STREAM_ERROR, |
bnc | 331385e | 2019-09-11 05:02:16 -0700 | [diff] [blame] | 2860 | "Decoder stream error: Invalid increment value 0.", _)); |
| 2861 | session_.OnStreamFrame(frame); |
| 2862 | } |
| 2863 | |
renjietang | 686ce58 | 2019-10-17 14:28:16 -0700 | [diff] [blame] | 2864 | TEST_P(QuicSpdySessionTestClient, InvalidHttp3GoAway) { |
| 2865 | if (!VersionUsesHttp3(transport_version())) { |
| 2866 | return; |
| 2867 | } |
bnc | 5f7d0e8 | 2020-09-14 10:31:58 -0700 | [diff] [blame] | 2868 | EXPECT_CALL(*connection_, |
| 2869 | CloseConnection(QUIC_HTTP_GOAWAY_INVALID_STREAM_ID, |
| 2870 | "GOAWAY with invalid stream ID", _)); |
renjietang | 686ce58 | 2019-10-17 14:28:16 -0700 | [diff] [blame] | 2871 | QuicStreamId stream_id = |
| 2872 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 0); |
| 2873 | session_.OnHttp3GoAway(stream_id); |
| 2874 | } |
| 2875 | |
bnc | af8e330 | 2020-07-27 07:42:59 -0700 | [diff] [blame] | 2876 | TEST_P(QuicSpdySessionTestClient, Http3GoAwayLargerIdThanBefore) { |
| 2877 | if (!VersionUsesHttp3(transport_version())) { |
| 2878 | return; |
| 2879 | } |
bnc | af8e330 | 2020-07-27 07:42:59 -0700 | [diff] [blame] | 2880 | |
bnc | 3988f57 | 2020-08-27 05:27:04 -0700 | [diff] [blame] | 2881 | EXPECT_FALSE(session_.goaway_received()); |
bnc | af8e330 | 2020-07-27 07:42:59 -0700 | [diff] [blame] | 2882 | QuicStreamId stream_id1 = |
| 2883 | GetNthClientInitiatedBidirectionalStreamId(transport_version(), 0); |
| 2884 | session_.OnHttp3GoAway(stream_id1); |
bnc | 3988f57 | 2020-08-27 05:27:04 -0700 | [diff] [blame] | 2885 | EXPECT_TRUE(session_.goaway_received()); |
bnc | af8e330 | 2020-07-27 07:42:59 -0700 | [diff] [blame] | 2886 | |
| 2887 | EXPECT_CALL( |
| 2888 | *connection_, |
| 2889 | CloseConnection( |
| 2890 | QUIC_HTTP_GOAWAY_ID_LARGER_THAN_PREVIOUS, |
| 2891 | "GOAWAY received with ID 4 greater than previously received ID 0", |
| 2892 | _)); |
| 2893 | QuicStreamId stream_id2 = |
| 2894 | GetNthClientInitiatedBidirectionalStreamId(transport_version(), 1); |
| 2895 | session_.OnHttp3GoAway(stream_id2); |
| 2896 | } |
| 2897 | |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 2898 | // Test that receipt of CANCEL_PUSH frame does not result in closing the |
| 2899 | // connection. |
| 2900 | // TODO(b/151841240): Handle CANCEL_PUSH frames instead of ignoring them. |
| 2901 | TEST_P(QuicSpdySessionTestClient, IgnoreCancelPush) { |
| 2902 | if (!VersionUsesHttp3(transport_version())) { |
| 2903 | return; |
| 2904 | } |
| 2905 | |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2906 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 2907 | session_.set_debug_visitor(&debug_visitor); |
| 2908 | |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 2909 | // Create control stream. |
| 2910 | QuicStreamId receive_control_stream_id = |
| 2911 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); |
| 2912 | char type[] = {kControlStream}; |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2913 | absl::string_view stream_type(type, 1); |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 2914 | QuicStreamOffset offset = 0; |
| 2915 | QuicStreamFrame data1(receive_control_stream_id, /* fin = */ false, offset, |
| 2916 | stream_type); |
| 2917 | offset += stream_type.length(); |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2918 | EXPECT_CALL(debug_visitor, |
| 2919 | OnPeerControlStreamCreated(receive_control_stream_id)); |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 2920 | session_.OnStreamFrame(data1); |
| 2921 | EXPECT_EQ(receive_control_stream_id, |
| 2922 | QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id()); |
| 2923 | |
bnc | f72ef0b | 2020-04-02 06:59:32 -0700 | [diff] [blame] | 2924 | // First frame has to be SETTINGS. |
| 2925 | std::string serialized_settings = EncodeSettings({}); |
| 2926 | QuicStreamFrame data2(receive_control_stream_id, /* fin = */ false, offset, |
| 2927 | serialized_settings); |
| 2928 | offset += serialized_settings.length(); |
| 2929 | EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(_)); |
| 2930 | session_.OnStreamFrame(data2); |
| 2931 | |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 2932 | CancelPushFrame cancel_push{/* push_id = */ 0}; |
| 2933 | std::unique_ptr<char[]> buffer; |
| 2934 | auto frame_length = |
| 2935 | HttpEncoder::SerializeCancelPushFrame(cancel_push, &buffer); |
bnc | f72ef0b | 2020-04-02 06:59:32 -0700 | [diff] [blame] | 2936 | QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset, |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 2937 | absl::string_view(buffer.get(), frame_length)); |
bnc | b9d5e79 | 2020-03-20 17:43:41 -0700 | [diff] [blame] | 2938 | EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_)); |
bnc | f72ef0b | 2020-04-02 06:59:32 -0700 | [diff] [blame] | 2939 | session_.OnStreamFrame(data3); |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 2940 | } |
| 2941 | |
bnc | 7a69d55 | 2020-03-17 07:05:16 -0700 | [diff] [blame] | 2942 | TEST_P(QuicSpdySessionTestServer, ServerPushEnabledDefaultValue) { |
| 2943 | if (VersionUsesHttp3(transport_version())) { |
| 2944 | EXPECT_FALSE(session_.server_push_enabled()); |
| 2945 | } else { |
| 2946 | EXPECT_TRUE(session_.server_push_enabled()); |
| 2947 | } |
| 2948 | } |
| 2949 | |
bnc | 92fa63a | 2020-02-04 05:13:43 -0800 | [diff] [blame] | 2950 | TEST_P(QuicSpdySessionTestServer, OnSetting) { |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 2951 | CompleteHandshake(); |
bnc | 92fa63a | 2020-02-04 05:13:43 -0800 | [diff] [blame] | 2952 | if (VersionUsesHttp3(transport_version())) { |
| 2953 | EXPECT_EQ(std::numeric_limits<size_t>::max(), |
| 2954 | session_.max_outbound_header_list_size()); |
renjietang | 4e9714d | 2020-09-21 15:25:23 -0700 | [diff] [blame] | 2955 | session_.OnSetting(SETTINGS_MAX_FIELD_SECTION_SIZE, 5); |
bnc | 92fa63a | 2020-02-04 05:13:43 -0800 | [diff] [blame] | 2956 | EXPECT_EQ(5u, session_.max_outbound_header_list_size()); |
| 2957 | |
renjietang | 7712ed7 | 2020-03-25 14:07:30 -0700 | [diff] [blame] | 2958 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 2959 | .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); |
bnc | 92fa63a | 2020-02-04 05:13:43 -0800 | [diff] [blame] | 2960 | QpackEncoder* qpack_encoder = session_.qpack_encoder(); |
| 2961 | EXPECT_EQ(0u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder)); |
| 2962 | session_.OnSetting(SETTINGS_QPACK_BLOCKED_STREAMS, 12); |
| 2963 | EXPECT_EQ(12u, QpackEncoderPeer::maximum_blocked_streams(qpack_encoder)); |
| 2964 | |
| 2965 | QpackHeaderTable* header_table = |
| 2966 | QpackEncoderPeer::header_table(qpack_encoder); |
| 2967 | EXPECT_EQ(0u, header_table->maximum_dynamic_table_capacity()); |
| 2968 | session_.OnSetting(SETTINGS_QPACK_MAX_TABLE_CAPACITY, 37); |
| 2969 | EXPECT_EQ(37u, header_table->maximum_dynamic_table_capacity()); |
| 2970 | |
| 2971 | return; |
| 2972 | } |
| 2973 | |
| 2974 | EXPECT_EQ(std::numeric_limits<size_t>::max(), |
| 2975 | session_.max_outbound_header_list_size()); |
renjietang | 4e9714d | 2020-09-21 15:25:23 -0700 | [diff] [blame] | 2976 | session_.OnSetting(SETTINGS_MAX_FIELD_SECTION_SIZE, 5); |
bnc | 92fa63a | 2020-02-04 05:13:43 -0800 | [diff] [blame] | 2977 | EXPECT_EQ(5u, session_.max_outbound_header_list_size()); |
| 2978 | |
| 2979 | EXPECT_TRUE(session_.server_push_enabled()); |
| 2980 | session_.OnSetting(spdy::SETTINGS_ENABLE_PUSH, 0); |
| 2981 | EXPECT_FALSE(session_.server_push_enabled()); |
| 2982 | |
| 2983 | spdy::HpackEncoder* hpack_encoder = |
| 2984 | QuicSpdySessionPeer::GetSpdyFramer(&session_)->GetHpackEncoder(); |
| 2985 | EXPECT_EQ(4096u, hpack_encoder->CurrentHeaderTableSizeSetting()); |
| 2986 | session_.OnSetting(spdy::SETTINGS_HEADER_TABLE_SIZE, 59); |
| 2987 | EXPECT_EQ(59u, hpack_encoder->CurrentHeaderTableSizeSetting()); |
| 2988 | } |
| 2989 | |
bnc | 77b4641 | 2020-02-21 08:09:09 -0800 | [diff] [blame] | 2990 | TEST_P(QuicSpdySessionTestServer, FineGrainedHpackErrorCodes) { |
| 2991 | if (VersionUsesHttp3(transport_version())) { |
| 2992 | // HPACK is not used in HTTP/3. |
| 2993 | return; |
| 2994 | } |
| 2995 | |
bnc | 77b4641 | 2020-02-21 08:09:09 -0800 | [diff] [blame] | 2996 | QuicStreamId request_stream_id = 5; |
| 2997 | session_.CreateIncomingStream(request_stream_id); |
| 2998 | |
| 2999 | // Index 126 does not exist (static table has 61 entries and dynamic table is |
| 3000 | // empty). |
vasilvv | 72b789c | 2020-10-27 17:39:17 -0700 | [diff] [blame] | 3001 | std::string headers_frame = absl::HexStringToBytes( |
bnc | 77b4641 | 2020-02-21 08:09:09 -0800 | [diff] [blame] | 3002 | "000006" // length |
| 3003 | "01" // type |
| 3004 | "24" // flags: PRIORITY | END_HEADERS |
| 3005 | "00000005" // stream_id |
| 3006 | "00000000" // stream dependency |
| 3007 | "10" // weight |
| 3008 | "fe"); // payload: reference to index 126. |
| 3009 | QuicStreamId headers_stream_id = |
| 3010 | QuicUtils::GetHeadersStreamId(transport_version()); |
| 3011 | QuicStreamFrame data(headers_stream_id, false, 0, headers_frame); |
| 3012 | |
| 3013 | EXPECT_CALL( |
| 3014 | *connection_, |
| 3015 | CloseConnection(QUIC_HPACK_INVALID_INDEX, |
| 3016 | "SPDY framing error: HPACK_INVALID_INDEX", |
| 3017 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET)); |
| 3018 | session_.OnStreamFrame(data); |
| 3019 | } |
| 3020 | |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 3021 | TEST_P(QuicSpdySessionTestServer, PeerClosesCriticalReceiveStream) { |
| 3022 | if (!VersionUsesHttp3(transport_version())) { |
| 3023 | return; |
| 3024 | } |
| 3025 | |
| 3026 | struct { |
| 3027 | char type; |
| 3028 | const char* error_details; |
| 3029 | } kTestData[] = { |
| 3030 | {kControlStream, "RESET_STREAM received for receive control stream"}, |
| 3031 | {kQpackEncoderStream, "RESET_STREAM received for QPACK receive stream"}, |
| 3032 | {kQpackDecoderStream, "RESET_STREAM received for QPACK receive stream"}, |
| 3033 | }; |
vasilvv | fe1c8f3 | 2020-10-19 14:10:59 -0700 | [diff] [blame] | 3034 | for (size_t i = 0; i < ABSL_ARRAYSIZE(kTestData); ++i) { |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 3035 | QuicStreamId stream_id = |
| 3036 | GetNthClientInitiatedUnidirectionalStreamId(transport_version(), i + 1); |
| 3037 | const QuicByteCount data_length = 1; |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 3038 | QuicStreamFrame data(stream_id, false, 0, |
| 3039 | absl::string_view(&kTestData[i].type, data_length)); |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 3040 | session_.OnStreamFrame(data); |
| 3041 | |
| 3042 | EXPECT_CALL(*connection_, CloseConnection(QUIC_HTTP_CLOSED_CRITICAL_STREAM, |
| 3043 | kTestData[i].error_details, _)); |
| 3044 | |
| 3045 | QuicRstStreamFrame rst(kInvalidControlFrameId, stream_id, |
| 3046 | QUIC_STREAM_CANCELLED, data_length); |
| 3047 | session_.OnRstStream(rst); |
| 3048 | } |
| 3049 | } |
| 3050 | |
fayang | b4c1dac | 2020-05-29 14:46:25 -0700 | [diff] [blame] | 3051 | TEST_P(QuicSpdySessionTestServer, |
| 3052 | H3ControlStreamsLimitedByConnectionFlowControl) { |
| 3053 | if (!VersionUsesHttp3(transport_version())) { |
| 3054 | return; |
| 3055 | } |
| 3056 | // Ensure connection level flow control blockage. |
| 3057 | QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0); |
| 3058 | EXPECT_TRUE(session_.IsConnectionFlowControlBlocked()); |
| 3059 | |
| 3060 | QuicSendControlStream* send_control_stream = |
| 3061 | QuicSpdySessionPeer::GetSendControlStream(&session_); |
| 3062 | // Mark send_control stream write blocked. |
| 3063 | session_.MarkConnectionLevelWriteBlocked(send_control_stream->id()); |
fayang | faad93f | 2020-07-06 08:44:49 -0700 | [diff] [blame] | 3064 | EXPECT_FALSE(session_.WillingAndAbleToWrite()); |
fayang | b4c1dac | 2020-05-29 14:46:25 -0700 | [diff] [blame] | 3065 | } |
| 3066 | |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 3067 | TEST_P(QuicSpdySessionTestServer, PeerClosesCriticalSendStream) { |
| 3068 | if (!VersionUsesHttp3(transport_version())) { |
| 3069 | return; |
| 3070 | } |
| 3071 | |
| 3072 | QuicSendControlStream* control_stream = |
| 3073 | QuicSpdySessionPeer::GetSendControlStream(&session_); |
| 3074 | ASSERT_TRUE(control_stream); |
| 3075 | |
| 3076 | QuicStopSendingFrame stop_sending_control_stream( |
bnc | 187eea3 | 2020-09-02 12:16:15 -0700 | [diff] [blame] | 3077 | kInvalidControlFrameId, control_stream->id(), QUIC_STREAM_CANCELLED); |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 3078 | EXPECT_CALL( |
| 3079 | *connection_, |
| 3080 | CloseConnection(QUIC_HTTP_CLOSED_CRITICAL_STREAM, |
| 3081 | "STOP_SENDING received for send control stream", _)); |
| 3082 | session_.OnStopSendingFrame(stop_sending_control_stream); |
| 3083 | |
| 3084 | QpackSendStream* decoder_stream = |
| 3085 | QuicSpdySessionPeer::GetQpackDecoderSendStream(&session_); |
| 3086 | ASSERT_TRUE(decoder_stream); |
| 3087 | |
| 3088 | QuicStopSendingFrame stop_sending_decoder_stream( |
bnc | 187eea3 | 2020-09-02 12:16:15 -0700 | [diff] [blame] | 3089 | kInvalidControlFrameId, decoder_stream->id(), QUIC_STREAM_CANCELLED); |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 3090 | EXPECT_CALL( |
| 3091 | *connection_, |
| 3092 | CloseConnection(QUIC_HTTP_CLOSED_CRITICAL_STREAM, |
| 3093 | "STOP_SENDING received for QPACK send stream", _)); |
| 3094 | session_.OnStopSendingFrame(stop_sending_decoder_stream); |
| 3095 | |
| 3096 | QpackSendStream* encoder_stream = |
| 3097 | QuicSpdySessionPeer::GetQpackEncoderSendStream(&session_); |
| 3098 | ASSERT_TRUE(encoder_stream); |
| 3099 | |
| 3100 | QuicStopSendingFrame stop_sending_encoder_stream( |
bnc | 187eea3 | 2020-09-02 12:16:15 -0700 | [diff] [blame] | 3101 | kInvalidControlFrameId, encoder_stream->id(), QUIC_STREAM_CANCELLED); |
bnc | 09c1eda | 2020-03-13 04:52:38 -0700 | [diff] [blame] | 3102 | EXPECT_CALL( |
| 3103 | *connection_, |
| 3104 | CloseConnection(QUIC_HTTP_CLOSED_CRITICAL_STREAM, |
| 3105 | "STOP_SENDING received for QPACK send stream", _)); |
| 3106 | session_.OnStopSendingFrame(stop_sending_encoder_stream); |
| 3107 | } |
| 3108 | |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 3109 | // Test that receipt of CANCEL_PUSH frame does not result in closing the |
| 3110 | // connection. |
| 3111 | // TODO(b/151841240): Handle CANCEL_PUSH frames instead of ignoring them. |
| 3112 | TEST_P(QuicSpdySessionTestServer, IgnoreCancelPush) { |
| 3113 | if (!VersionUsesHttp3(transport_version())) { |
| 3114 | return; |
| 3115 | } |
| 3116 | |
bnc | f72ef0b | 2020-04-02 06:59:32 -0700 | [diff] [blame] | 3117 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 3118 | session_.set_debug_visitor(&debug_visitor); |
| 3119 | |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 3120 | // Create control stream. |
| 3121 | QuicStreamId receive_control_stream_id = |
| 3122 | GetNthClientInitiatedUnidirectionalStreamId(transport_version(), 3); |
| 3123 | char type[] = {kControlStream}; |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 3124 | absl::string_view stream_type(type, 1); |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 3125 | QuicStreamOffset offset = 0; |
| 3126 | QuicStreamFrame data1(receive_control_stream_id, /* fin = */ false, offset, |
| 3127 | stream_type); |
| 3128 | offset += stream_type.length(); |
bnc | f72ef0b | 2020-04-02 06:59:32 -0700 | [diff] [blame] | 3129 | EXPECT_CALL(debug_visitor, |
| 3130 | OnPeerControlStreamCreated(receive_control_stream_id)); |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 3131 | session_.OnStreamFrame(data1); |
| 3132 | EXPECT_EQ(receive_control_stream_id, |
| 3133 | QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id()); |
| 3134 | |
bnc | f72ef0b | 2020-04-02 06:59:32 -0700 | [diff] [blame] | 3135 | // First frame has to be SETTINGS. |
| 3136 | std::string serialized_settings = EncodeSettings({}); |
| 3137 | QuicStreamFrame data2(receive_control_stream_id, /* fin = */ false, offset, |
| 3138 | serialized_settings); |
| 3139 | offset += serialized_settings.length(); |
| 3140 | EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(_)); |
| 3141 | session_.OnStreamFrame(data2); |
| 3142 | |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 3143 | CancelPushFrame cancel_push{/* push_id = */ 0}; |
| 3144 | std::unique_ptr<char[]> buffer; |
| 3145 | auto frame_length = |
| 3146 | HttpEncoder::SerializeCancelPushFrame(cancel_push, &buffer); |
bnc | f72ef0b | 2020-04-02 06:59:32 -0700 | [diff] [blame] | 3147 | QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset, |
vasilvv | e2707e3 | 2020-10-08 12:27:46 -0700 | [diff] [blame] | 3148 | absl::string_view(buffer.get(), frame_length)); |
bnc | f72ef0b | 2020-04-02 06:59:32 -0700 | [diff] [blame] | 3149 | EXPECT_CALL(debug_visitor, OnCancelPushFrameReceived(_)); |
| 3150 | session_.OnStreamFrame(data3); |
bnc | 1618f0d | 2020-03-19 07:18:44 -0700 | [diff] [blame] | 3151 | } |
| 3152 | |
bnc | c03d2c2 | 2020-10-15 13:30:09 -0700 | [diff] [blame] | 3153 | TEST_P(QuicSpdySessionTestServer, Http3GoAwayWhenClosingConnection) { |
| 3154 | if (!VersionUsesHttp3(transport_version())) { |
| 3155 | return; |
| 3156 | } |
| 3157 | |
| 3158 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 3159 | session_.set_debug_visitor(&debug_visitor); |
| 3160 | |
| 3161 | EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); |
| 3162 | CompleteHandshake(); |
| 3163 | |
| 3164 | QuicStreamId stream_id = GetNthClientInitiatedBidirectionalId(0); |
| 3165 | |
| 3166 | // Create stream by receiving some data (CreateIncomingStream() would not |
| 3167 | // update the session's largest peer created stream ID). |
| 3168 | const size_t headers_payload_length = 10; |
| 3169 | std::unique_ptr<char[]> headers_buffer; |
| 3170 | QuicByteCount headers_frame_header_length = |
| 3171 | HttpEncoder::SerializeHeadersFrameHeader(headers_payload_length, |
| 3172 | &headers_buffer); |
| 3173 | absl::string_view headers_frame_header(headers_buffer.get(), |
| 3174 | headers_frame_header_length); |
| 3175 | EXPECT_CALL(debug_visitor, |
| 3176 | OnHeadersFrameReceived(stream_id, headers_payload_length)); |
| 3177 | session_.OnStreamFrame( |
| 3178 | QuicStreamFrame(stream_id, false, 0, headers_frame_header)); |
| 3179 | |
| 3180 | EXPECT_EQ(stream_id, QuicSessionPeer::GetLargestPeerCreatedStreamId( |
| 3181 | &session_, /*unidirectional = */ false)); |
| 3182 | |
| 3183 | if (GetQuicReloadableFlag(quic_send_goaway_with_connection_close)) { |
bnc | 1278630 | 2020-11-23 10:50:15 -0800 | [diff] [blame] | 3184 | // Stream with stream_id is already received and potentially processed, |
| 3185 | // therefore a GOAWAY frame is sent with the next stream ID. |
| 3186 | EXPECT_CALL(debug_visitor, |
| 3187 | OnGoAwayFrameSent( |
| 3188 | stream_id + QuicUtils::StreamIdDelta(transport_version()))); |
bnc | c03d2c2 | 2020-10-15 13:30:09 -0700 | [diff] [blame] | 3189 | } |
| 3190 | |
| 3191 | // Close connection. |
| 3192 | EXPECT_CALL(*writer_, WritePacket(_, _, _, _, _)) |
| 3193 | .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0))); |
| 3194 | EXPECT_CALL(*connection_, CloseConnection(QUIC_NO_ERROR, _, _)) |
| 3195 | .WillOnce( |
| 3196 | Invoke(connection_, &MockQuicConnection::ReallyCloseConnection)); |
mattm | 55006b0 | 2021-01-14 15:09:54 -0800 | [diff] [blame] | 3197 | EXPECT_CALL(*connection_, SendConnectionClosePacket(QUIC_NO_ERROR, _, _)) |
bnc | c03d2c2 | 2020-10-15 13:30:09 -0700 | [diff] [blame] | 3198 | .WillOnce(Invoke(connection_, |
| 3199 | &MockQuicConnection::ReallySendConnectionClosePacket)); |
| 3200 | connection_->CloseConnection( |
| 3201 | QUIC_NO_ERROR, "closing connection", |
| 3202 | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
| 3203 | } |
| 3204 | |
bnc | e403126 | 2020-03-30 10:52:57 -0700 | [diff] [blame] | 3205 | TEST_P(QuicSpdySessionTestClient, SendInitialMaxPushIdIfSet) { |
| 3206 | if (!VersionUsesHttp3(transport_version())) { |
| 3207 | return; |
| 3208 | } |
| 3209 | |
| 3210 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 3211 | session_.set_debug_visitor(&debug_visitor); |
| 3212 | |
bnc | 2517302 | 2020-04-10 07:05:15 -0700 | [diff] [blame] | 3213 | const PushId max_push_id = 5; |
bnc | e403126 | 2020-03-30 10:52:57 -0700 | [diff] [blame] | 3214 | session_.SetMaxPushId(max_push_id); |
| 3215 | |
| 3216 | InSequence s; |
| 3217 | EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); |
| 3218 | const MaxPushIdFrame max_push_id_frame{max_push_id}; |
| 3219 | EXPECT_CALL(debug_visitor, OnMaxPushIdFrameSent(max_push_id_frame)); |
| 3220 | |
| 3221 | CompleteHandshake(); |
| 3222 | } |
| 3223 | |
| 3224 | TEST_P(QuicSpdySessionTestClient, DoNotSendInitialMaxPushIdIfNotSet) { |
| 3225 | if (!VersionUsesHttp3(transport_version())) { |
| 3226 | return; |
| 3227 | } |
| 3228 | |
| 3229 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 3230 | session_.set_debug_visitor(&debug_visitor); |
| 3231 | |
| 3232 | InSequence s; |
| 3233 | EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); |
| 3234 | |
| 3235 | CompleteHandshake(); |
| 3236 | } |
| 3237 | |
renjietang | 6e173ef | 2020-08-13 18:34:09 -0700 | [diff] [blame] | 3238 | TEST_P(QuicSpdySessionTestClient, DoNotSendInitialMaxPushIdIfSetToDefaut) { |
| 3239 | if (!VersionUsesHttp3(transport_version())) { |
| 3240 | return; |
| 3241 | } |
| 3242 | |
| 3243 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 3244 | session_.set_debug_visitor(&debug_visitor); |
| 3245 | |
| 3246 | session_.SetMaxPushId(0); |
| 3247 | |
| 3248 | InSequence s; |
| 3249 | EXPECT_CALL(debug_visitor, OnSettingsFrameSent(_)); |
| 3250 | CompleteHandshake(); |
| 3251 | } |
| 3252 | |
renjietang | 21fc64c | 2020-09-22 10:59:50 -0700 | [diff] [blame] | 3253 | TEST_P(QuicSpdySessionTestClient, ReceiveSpdySettingInHttp3) { |
renjietang | d2b7cef | 2020-11-30 13:51:24 -0800 | [diff] [blame] | 3254 | if (!VersionUsesHttp3(transport_version())) { |
renjietang | 21fc64c | 2020-09-22 10:59:50 -0700 | [diff] [blame] | 3255 | return; |
| 3256 | } |
| 3257 | |
| 3258 | SettingsFrame frame; |
| 3259 | frame.values[SETTINGS_MAX_FIELD_SECTION_SIZE] = 5; |
renjietang | 15be59c | 2020-09-22 11:19:55 -0700 | [diff] [blame] | 3260 | // https://datatracker.ietf.org/doc/html/draft-ietf-quic-http-30#section-7.2.4.1 |
renjietang | 21fc64c | 2020-09-22 10:59:50 -0700 | [diff] [blame] | 3261 | // specifies the presence of HTTP/2 setting as error. |
| 3262 | frame.values[spdy::SETTINGS_INITIAL_WINDOW_SIZE] = 100; |
| 3263 | |
| 3264 | CompleteHandshake(); |
| 3265 | |
| 3266 | EXPECT_CALL(*connection_, |
| 3267 | CloseConnection(QUIC_HTTP_RECEIVE_SPDY_SETTING, _, _)); |
| 3268 | session_.OnSettingsFrame(frame); |
| 3269 | } |
| 3270 | |
bnc | 9be5c21 | 2020-12-11 12:38:20 -0800 | [diff] [blame] | 3271 | TEST_P(QuicSpdySessionTestClient, ReceiveAcceptChFrame) { |
| 3272 | if (!VersionUsesHttp3(transport_version())) { |
| 3273 | return; |
| 3274 | } |
| 3275 | |
| 3276 | if (!GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { |
| 3277 | return; |
| 3278 | } |
| 3279 | |
| 3280 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 3281 | session_.set_debug_visitor(&debug_visitor); |
| 3282 | |
| 3283 | // Create control stream. |
| 3284 | QuicStreamId receive_control_stream_id = |
| 3285 | GetNthServerInitiatedUnidirectionalStreamId(transport_version(), 3); |
| 3286 | char type[] = {kControlStream}; |
| 3287 | absl::string_view stream_type(type, 1); |
| 3288 | QuicStreamOffset offset = 0; |
| 3289 | QuicStreamFrame data1(receive_control_stream_id, /* fin = */ false, offset, |
| 3290 | stream_type); |
| 3291 | offset += stream_type.length(); |
| 3292 | EXPECT_CALL(debug_visitor, |
| 3293 | OnPeerControlStreamCreated(receive_control_stream_id)); |
| 3294 | |
| 3295 | session_.OnStreamFrame(data1); |
| 3296 | EXPECT_EQ(receive_control_stream_id, |
| 3297 | QuicSpdySessionPeer::GetReceiveControlStream(&session_)->id()); |
| 3298 | |
| 3299 | // First frame has to be SETTINGS. |
| 3300 | std::string serialized_settings = EncodeSettings({}); |
| 3301 | QuicStreamFrame data2(receive_control_stream_id, /* fin = */ false, offset, |
| 3302 | serialized_settings); |
| 3303 | offset += serialized_settings.length(); |
| 3304 | EXPECT_CALL(debug_visitor, OnSettingsFrameReceived(_)); |
| 3305 | |
| 3306 | session_.OnStreamFrame(data2); |
| 3307 | |
| 3308 | // Receive ACCEPT_CH frame. |
| 3309 | AcceptChFrame accept_ch; |
| 3310 | accept_ch.entries.push_back({"foo", "bar"}); |
| 3311 | std::unique_ptr<char[]> buffer; |
| 3312 | auto frame_length = HttpEncoder::SerializeAcceptChFrame(accept_ch, &buffer); |
| 3313 | QuicStreamFrame data3(receive_control_stream_id, /* fin = */ false, offset, |
| 3314 | absl::string_view(buffer.get(), frame_length)); |
| 3315 | |
| 3316 | EXPECT_CALL(debug_visitor, OnAcceptChFrameReceived(accept_ch)); |
| 3317 | EXPECT_CALL(session_, OnAcceptChFrame(accept_ch)); |
| 3318 | |
| 3319 | session_.OnStreamFrame(data3); |
| 3320 | } |
| 3321 | |
bnc | 4049461 | 2021-01-21 04:50:33 -0800 | [diff] [blame] | 3322 | TEST_P(QuicSpdySessionTestClient, OnAlpsData) { |
| 3323 | StrictMock<MockHttp3DebugVisitor> debug_visitor; |
| 3324 | session_.set_debug_visitor(&debug_visitor); |
| 3325 | |
| 3326 | AcceptChFrame accept_ch_frame{{{"foo", "bar"}}}; |
| 3327 | std::string serialized_accept_ch_frame = absl::HexStringToBytes( |
| 3328 | "4089" // type (ACCEPT_CH) |
| 3329 | "08" // length |
| 3330 | "03" // length of origin |
| 3331 | "666f6f" // origin "foo" |
| 3332 | "03" // length of value |
| 3333 | "626172"); // value "bar" |
| 3334 | |
| 3335 | if (GetQuicReloadableFlag(quic_parse_accept_ch_frame)) { |
| 3336 | EXPECT_CALL(debug_visitor, OnAcceptChFrameReceivedViaAlps(accept_ch_frame)); |
| 3337 | } |
| 3338 | |
| 3339 | session_.OnAlpsData( |
| 3340 | reinterpret_cast<const uint8_t*>(serialized_accept_ch_frame.data()), |
| 3341 | serialized_accept_ch_frame.size()); |
| 3342 | } |
| 3343 | |
QUICHE team | a6ef0a6 | 2019-03-07 20:34:33 -0500 | [diff] [blame] | 3344 | } // namespace |
| 3345 | } // namespace test |
| 3346 | } // namespace quic |