Report QUIC_BUG if application tries to write data before encryption is established.
gfe-relnote: no behavior change in production. not protected.
PiperOrigin-RevId: 300639905
Change-Id: I0d6a6c6a73f95ded04da01a8ec8a0920f877399a
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index dc33775..06912c3 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -14,6 +14,7 @@
#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_socket_address.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
@@ -231,11 +232,10 @@
EXPECT_TRUE(session_->CreateOutgoingBidirectionalStream() == nullptr);
// Verify that no data may be send on existing streams.
char data[] = "hello world";
- QuicConsumedData consumed =
+ EXPECT_QUIC_BUG(
session_->WritevData(stream->id(), QUICHE_ARRAYSIZE(data), 0, NO_FIN,
- NOT_RETRANSMISSION, QuicheNullOpt);
- EXPECT_FALSE(consumed.fin_consumed);
- EXPECT_EQ(0u, consumed.bytes_consumed);
+ NOT_RETRANSMISSION, QuicheNullOpt),
+ "Client: Try to send data of stream");
}
TEST_P(QuicSpdyClientSessionTest, MaxNumStreamsWithNoFinOrRst) {
diff --git a/quic/core/http/quic_spdy_session_test.cc b/quic/core/http/quic_spdy_session_test.cc
index c0a1269..635b3c3 100644
--- a/quic/core/http/quic_spdy_session_test.cc
+++ b/quic/core/http/quic_spdy_session_test.cc
@@ -1033,6 +1033,15 @@
return;
}
connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+ if (connection_->version().HasHandshakeDone()) {
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ }
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
session_.SendHttp3GoAway();
EXPECT_TRUE(session_.http3_goaway_sent());
@@ -1108,13 +1117,20 @@
}
TEST_P(QuicSpdySessionTestServer, RstStreamBeforeHeadersDecompressed) {
+ connection_->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+ EXPECT_CALL(*connection_, SendControlFrame(_));
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
// Send two bytes of payload.
QuicStreamFrame data1(GetNthClientInitiatedBidirectionalId(0), false, 0,
quiche::QuicheStringPiece("HT"));
session_.OnStreamFrame(data1);
EXPECT_EQ(1u, session_.GetNumOpenIncomingStreams());
- EXPECT_CALL(*connection_, SendControlFrame(_));
if (!VersionHasIetfQuicFrames(transport_version())) {
// For version99, OnStreamReset gets called because of the STOP_SENDING,
// below. EXPECT the call there.
@@ -1384,6 +1400,15 @@
TEST_P(QuicSpdySessionTestServer,
ConnectionFlowControlAccountingRstOutOfOrder) {
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
// Test that when we receive an out of order stream RST we correctly adjust
// our connection level flow control receive window.
// On close, the stream should mark as consumed all bytes between the highest
@@ -1393,9 +1418,6 @@
const QuicStreamOffset kByteOffset =
1 + kInitialSessionFlowControlWindowForTest / 2;
- EXPECT_CALL(*connection_, SendControlFrame(_))
- .Times(2)
- .WillRepeatedly(Invoke(&ClearControlFrame));
if (!VersionHasIetfQuicFrames(transport_version())) {
// For version99 the call to OnStreamReset happens as a result of receiving
// the STOP_SENDING, so set up the EXPECT there.
@@ -1450,6 +1472,15 @@
}
TEST_P(QuicSpdySessionTestServer, ConnectionFlowControlAccountingFinAfterRst) {
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
// Test that when we RST the stream (and tear down stream state), and then
// receive a FIN from the peer, we correctly adjust our connection level flow
// control receive window.
@@ -1466,7 +1497,6 @@
// Reset our stream: this results in the stream being closed locally.
TestStream* stream = session_.CreateOutgoingBidirectionalStream();
- EXPECT_CALL(*connection_, SendControlFrame(_));
EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
stream->Reset(QUIC_STREAM_CANCELLED);
@@ -1489,6 +1519,15 @@
}
TEST_P(QuicSpdySessionTestServer, ConnectionFlowControlAccountingRstAfterRst) {
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
// Test that when we RST the stream (and tear down stream state), and then
// receive a RST from the peer, we correctly adjust our connection level flow
// control receive window.
@@ -1580,6 +1619,15 @@
}
TEST_P(QuicSpdySessionTestServer, FlowControlWithInvalidFinalOffset) {
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
// Test that if we receive a stream RST with a highest byte offset that
// violates flow control, that we close the connection.
const uint64_t kLargeOffset = kInitialSessionFlowControlWindowForTest + 1;
@@ -1589,7 +1637,6 @@
// Check that stream frame + FIN results in connection close.
TestStream* stream = session_.CreateOutgoingBidirectionalStream();
- EXPECT_CALL(*connection_, SendControlFrame(_));
EXPECT_CALL(*connection_, OnStreamReset(stream->id(), _));
stream->Reset(QUIC_STREAM_CANCELLED);
QuicStreamFrame frame(stream->id(), true, kLargeOffset,
@@ -1818,6 +1865,16 @@
return;
}
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
+
TestStream* stream = session_.CreateOutgoingBidirectionalStream();
// Write headers with FIN set to close write side of stream.
@@ -1871,14 +1928,22 @@
// IETF QUIC currently doesn't support PRIORITY.
return;
}
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
+
TestHeadersStream* headers_stream;
QuicSpdySessionPeer::SetHeadersStream(&session_, nullptr);
headers_stream = new TestHeadersStream(&session_);
QuicSpdySessionPeer::SetHeadersStream(&session_, headers_stream);
// Make packet writer blocked so |headers_stream| will buffer its write data.
- MockPacketWriter* writer = static_cast<MockPacketWriter*>(
- QuicConnectionPeer::GetWriter(session_.connection()));
EXPECT_CALL(*writer, IsWriteBlocked()).WillRepeatedly(Return(true));
const QuicStreamId id = 4;
@@ -2344,6 +2409,15 @@
if (!VersionUsesHttp3(transport_version())) {
return;
}
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
MockHttp3DebugVisitor debug_visitor;
// Use an arbitrary stream id.
QuicStreamId stream_id =
@@ -2726,6 +2800,15 @@
}
TEST_P(QuicSpdySessionTestServer, OnSetting) {
+ MockPacketWriter* writer = static_cast<MockPacketWriter*>(
+ QuicConnectionPeer::GetWriter(session_.connection()));
+ EXPECT_CALL(*writer, WritePacket(_, _, _, _, _))
+ .WillRepeatedly(Return(WriteResult(WRITE_STATUS_OK, 0)));
+
+ EXPECT_CALL(*connection_, SendControlFrame(_))
+ .WillRepeatedly(Invoke(&ClearControlFrame));
+ CryptoHandshakeMessage message;
+ session_.GetMutableCryptoStream()->OnHandshakeMessage(message);
if (VersionUsesHttp3(transport_version())) {
EXPECT_EQ(std::numeric_limits<size_t>::max(),
session_.max_outbound_header_list_size());
diff --git a/quic/core/quic_session.cc b/quic/core/quic_session.cc
index ed1cde5..ad16ce3 100644
--- a/quic/core/quic_session.cc
+++ b/quic/core/quic_session.cc
@@ -708,6 +708,8 @@
!QuicUtils::IsCryptoStreamId(transport_version(), id)) {
// Do not let streams write without encryption. The calling stream will end
// up write blocked until OnCanWrite is next called.
+ QUIC_BUG << ENDPOINT << "Try to send data of stream " << id
+ << " before encryption is established.";
return QuicConsumedData(0, false);
}