blob: 8ebf712d62e8cedc9dfaa79c550d472a2f3ef890 [file] [log] [blame]
// Copyright (c) 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quiche/quic/core/http/quic_spdy_server_stream_base.h"
#include "absl/memory/memory.h"
#include "quiche/quic/core/crypto/null_encrypter.h"
#include "quiche/quic/platform/api/quic_flags.h"
#include "quiche/quic/platform/api/quic_test.h"
#include "quiche/quic/test_tools/qpack/qpack_test_utils.h"
#include "quiche/quic/test_tools/quic_spdy_session_peer.h"
#include "quiche/quic/test_tools/quic_stream_peer.h"
#include "quiche/quic/test_tools/quic_test_utils.h"
#include "quiche/spdy/core/http2_header_block.h"
using testing::_;
namespace quic {
namespace test {
namespace {
class TestQuicSpdyServerStream : public QuicSpdyServerStreamBase {
public:
TestQuicSpdyServerStream(QuicStreamId id, QuicSpdySession* session,
StreamType type)
: QuicSpdyServerStreamBase(id, session, type) {}
void OnBodyAvailable() override {}
};
class QuicSpdyServerStreamBaseTest : public QuicTest {
protected:
QuicSpdyServerStreamBaseTest()
: session_(new MockQuicConnection(&helper_, &alarm_factory_,
Perspective::IS_SERVER)) {
session_.Initialize();
session_.connection()->SetEncrypter(
ENCRYPTION_FORWARD_SECURE,
std::make_unique<NullEncrypter>(session_.perspective()));
stream_ =
new TestQuicSpdyServerStream(GetNthClientInitiatedBidirectionalStreamId(
session_.transport_version(), 0),
&session_, BIDIRECTIONAL);
session_.ActivateStream(absl::WrapUnique(stream_));
helper_.AdvanceTime(QuicTime::Delta::FromSeconds(1));
}
QuicSpdyServerStreamBase* stream_ = nullptr;
MockQuicConnectionHelper helper_;
MockAlarmFactory alarm_factory_;
MockQuicSpdySession session_;
};
TEST_F(QuicSpdyServerStreamBaseTest,
SendQuicRstStreamNoErrorWithEarlyResponse) {
stream_->StopReading();
if (session_.version().UsesHttp3()) {
EXPECT_CALL(session_,
MaybeSendStopSendingFrame(_, QuicResetStreamError::FromInternal(
QUIC_STREAM_NO_ERROR)))
.Times(1);
} else {
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_STREAM_NO_ERROR), _))
.Times(1);
}
QuicStreamPeer::SetFinSent(stream_);
stream_->CloseWriteSide();
}
TEST_F(QuicSpdyServerStreamBaseTest,
DoNotSendQuicRstStreamNoErrorWithRstReceived) {
EXPECT_FALSE(stream_->reading_stopped());
EXPECT_CALL(session_,
MaybeSendRstStreamFrame(
_,
QuicResetStreamError::FromInternal(
VersionHasIetfQuicFrames(session_.transport_version())
? QUIC_STREAM_CANCELLED
: QUIC_RST_ACKNOWLEDGEMENT),
_))
.Times(1);
QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
QUIC_STREAM_CANCELLED, 1234);
stream_->OnStreamReset(rst_frame);
if (VersionHasIetfQuicFrames(session_.transport_version())) {
// Create and inject a STOP SENDING frame to complete the close
// of the stream. This is only needed for version 99/IETF QUIC.
QuicStopSendingFrame stop_sending(kInvalidControlFrameId, stream_->id(),
QUIC_STREAM_CANCELLED);
session_.OnStopSendingFrame(stop_sending);
}
EXPECT_TRUE(stream_->reading_stopped());
EXPECT_TRUE(stream_->write_side_closed());
}
TEST_F(QuicSpdyServerStreamBaseTest, AllowExtendedConnect) {
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":method", "CONNECT");
header_list.OnHeader(":protocol", "webtransport");
header_list.OnHeader(":path", "/path");
header_list.OnHeader(":scheme", "http");
header_list.OnHeaderBlockEnd(128, 128);
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_EQ(GetQuicReloadableFlag(quic_verify_request_headers_2) &&
GetQuicReloadableFlag(quic_act_upon_invalid_header) &&
!session_.allow_extended_connect(),
stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, AllowExtendedConnectProtocolFirst) {
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":protocol", "webtransport");
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":method", "CONNECT");
header_list.OnHeader(":path", "/path");
header_list.OnHeader(":scheme", "http");
header_list.OnHeaderBlockEnd(128, 128);
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_EQ(GetQuicReloadableFlag(quic_verify_request_headers_2) &&
GetQuicReloadableFlag(quic_act_upon_invalid_header) &&
!session_.allow_extended_connect(),
stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidExtendedConnect) {
if (!session_.version().UsesHttp3()) {
return;
}
SetQuicReloadableFlag(quic_verify_request_headers_2, true);
SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":method", "CONNECT");
header_list.OnHeader(":protocol", "webtransport");
header_list.OnHeader(":scheme", "http");
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD),
_));
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_TRUE(stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, VanillaConnectAllowed) {
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":method", "CONNECT");
header_list.OnHeaderBlockEnd(128, 128);
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_FALSE(stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidVanillaConnect) {
SetQuicReloadableFlag(quic_verify_request_headers_2, true);
SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":method", "CONNECT");
header_list.OnHeader(":scheme", "http");
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD),
_));
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_TRUE(stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidNonConnectWithProtocol) {
SetQuicReloadableFlag(quic_verify_request_headers_2, true);
SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":method", "GET");
header_list.OnHeader(":scheme", "http");
header_list.OnHeader(":path", "/path");
header_list.OnHeader(":protocol", "webtransport");
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD),
_));
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_TRUE(stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestWithoutScheme) {
SetQuicReloadableFlag(quic_verify_request_headers_2, true);
SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :scheme should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":method", "GET");
header_list.OnHeader(":path", "/path");
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD),
_));
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_TRUE(stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestWithoutAuthority) {
SetQuicReloadableFlag(quic_verify_request_headers_2, true);
SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :authority should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":scheme", "http");
header_list.OnHeader(":method", "GET");
header_list.OnHeader(":path", "/path");
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD),
_));
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_TRUE(stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestWithoutMethod) {
SetQuicReloadableFlag(quic_verify_request_headers_2, true);
SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :method should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":scheme", "http");
header_list.OnHeader(":path", "/path");
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD),
_));
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_TRUE(stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestWithoutPath) {
SetQuicReloadableFlag(quic_verify_request_headers_2, true);
SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :path should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":scheme", "http");
header_list.OnHeader(":method", "POST");
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD),
_));
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_TRUE(stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, InvalidRequestHeader) {
SetQuicReloadableFlag(quic_verify_request_headers_2, true);
SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
// A request without :path should be rejected.
QuicHeaderList header_list;
header_list.OnHeaderBlockStart();
header_list.OnHeader(":authority", "www.google.com:4433");
header_list.OnHeader(":scheme", "http");
header_list.OnHeader(":method", "POST");
header_list.OnHeader("invalid:header", "value");
header_list.OnHeaderBlockEnd(128, 128);
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD),
_));
stream_->OnStreamHeaderList(/*fin=*/false, 0, header_list);
EXPECT_TRUE(stream_->rst_sent());
}
TEST_F(QuicSpdyServerStreamBaseTest, EmptyHeaders) {
SetQuicReloadableFlag(quic_verify_request_headers_2, true);
SetQuicReloadableFlag(quic_act_upon_invalid_header, true);
spdy::Http2HeaderBlock empty_header;
quic::test::NoopQpackStreamSenderDelegate encoder_stream_sender_delegate;
NoopDecoderStreamErrorDelegate decoder_stream_error_delegate;
auto qpack_encoder =
std::make_unique<quic::QpackEncoder>(&decoder_stream_error_delegate);
qpack_encoder->set_qpack_stream_sender_delegate(
&encoder_stream_sender_delegate);
std::string payload =
qpack_encoder->EncodeHeaderList(stream_->id(), empty_header, nullptr);
std::string headers_frame_header =
quic::HttpEncoder::SerializeHeadersFrameHeader(payload.length());
EXPECT_CALL(
session_,
MaybeSendRstStreamFrame(
_, QuicResetStreamError::FromInternal(QUIC_BAD_APPLICATION_PAYLOAD),
_));
stream_->OnStreamFrame(QuicStreamFrame(
stream_->id(), true, 0, absl::StrCat(headers_frame_header, payload)));
EXPECT_TRUE(stream_->rst_sent());
}
} // namespace
} // namespace test
} // namespace quic