Implement the QuicTransport server session subclass.
This currently does not handle incoming streams, as those require special logic to prevent access to application data before the indication is received.
gfe-relnote: n/a (not used in production)
PiperOrigin-RevId: 274228824
Change-Id: Ie1cd37ecfb739d1242a3cdc40186bca00f8373fd
diff --git a/quic/core/quic_crypto_client_stream_test.cc b/quic/core/quic_crypto_client_stream_test.cc
index 1001301..8e1ef25 100644
--- a/quic/core/quic_crypto_client_stream_test.cc
+++ b/quic/core/quic_crypto_client_stream_test.cc
@@ -51,6 +51,9 @@
session_ = std::make_unique<TestQuicSpdyClientSession>(
connection_, DefaultQuicConfig(), supported_versions_, server_id_,
&crypto_config_);
+ EXPECT_CALL(*session_, GetAlpnsToOffer())
+ .WillRepeatedly(testing::Return(std::vector<std::string>(
+ {AlpnForVersion(connection_->version())})));
}
void CompleteCryptoHandshake() {
diff --git a/quic/core/quic_crypto_server_stream_test.cc b/quic/core/quic_crypto_server_stream_test.cc
index 8d71f26..360c268 100644
--- a/quic/core/quic_crypto_server_stream_test.cc
+++ b/quic/core/quic_crypto_server_stream_test.cc
@@ -132,7 +132,8 @@
return crypto_test_utils::HandshakeWithFakeClient(
helpers_.back().get(), alarm_factories_.back().get(),
- server_connection_, server_stream(), server_id_, client_options_);
+ server_connection_, server_stream(), server_id_, client_options_,
+ /*alpn=*/"");
}
// Performs a single round of handshake message-exchange between the
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index e69cd60..b1e8ed2 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -159,6 +159,7 @@
RETURN_STRING_LITERAL(
QUIC_WINDOW_UPDATE_RECEIVED_ON_READ_UNIDIRECTIONAL_STREAM);
RETURN_STRING_LITERAL(QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES);
+ RETURN_STRING_LITERAL(QUIC_TRANSPORT_INVALID_CLIENT_INDICATION);
RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
// Intentionally have no default case, so we'll break the build
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index 298029f..ce5c721 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -339,8 +339,11 @@
// There are too many buffered control frames in control frame manager.
QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES = 124,
+ // QuicTransport received invalid client indication.
+ QUIC_TRANSPORT_INVALID_CLIENT_INDICATION = 125,
+
// No error. Used as bound while iterating.
- QUIC_LAST_ERROR = 125,
+ QUIC_LAST_ERROR = 126,
};
// QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC,
// or a varint62 when doing IETF QUIC. Ensure that its value does not exceed
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc
index 499bc66..db48c5c 100644
--- a/quic/core/quic_types.cc
+++ b/quic/core/quic_types.cc
@@ -411,6 +411,8 @@
case QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES:
return {true,
{static_cast<uint64_t>(QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES)}};
+ case QUIC_TRANSPORT_INVALID_CLIENT_INDICATION:
+ return {false, {0u}};
case QUIC_LAST_ERROR:
return {false, {static_cast<uint64_t>(QUIC_LAST_ERROR)}};
}
diff --git a/quic/quic_transport/quic_transport_protocol.h b/quic/quic_transport/quic_transport_protocol.h
index bef5f8d..307354f 100644
--- a/quic/quic_transport/quic_transport_protocol.h
+++ b/quic/quic_transport/quic_transport_protocol.h
@@ -21,6 +21,11 @@
return 2;
}
+// The maximum allowed size of the client indication.
+QUIC_EXPORT constexpr QuicByteCount ClientIndicationMaxSize() {
+ return 65536;
+}
+
// The keys of the fields in the client indication.
enum class QuicTransportClientIndicationKeys : uint16_t {
kOrigin = 0x0000,
diff --git a/quic/quic_transport/quic_transport_server_session.cc b/quic/quic_transport/quic_transport_server_session.cc
new file mode 100644
index 0000000..de4db0f
--- /dev/null
+++ b/quic/quic_transport/quic_transport_server_session.cc
@@ -0,0 +1,164 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h"
+
+#include <memory>
+
+#include "url/gurl.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
+#include "net/third_party/quiche/src/quic/core/quic_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
+
+namespace quic {
+
+namespace {
+class QuicTransportServerCryptoHelper : public QuicCryptoServerStream::Helper {
+ public:
+ bool CanAcceptClientHello(const CryptoHandshakeMessage& /*message*/,
+ const QuicSocketAddress& /*client_address*/,
+ const QuicSocketAddress& /*peer_address*/,
+ const QuicSocketAddress& /*self_address*/,
+ std::string* /*error_details*/) const override {
+ return true;
+ }
+};
+} // namespace
+
+QuicTransportServerSession::QuicTransportServerSession(
+ QuicConnection* connection,
+ Visitor* owner,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ ServerVisitor* visitor)
+ : QuicSession(connection,
+ owner,
+ config,
+ supported_versions,
+ /*num_expected_unidirectional_static_streams*/ 0),
+ visitor_(visitor) {
+ for (const ParsedQuicVersion& version : supported_versions) {
+ QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3)
+ << "QuicTransport requires TLS 1.3 handshake";
+ }
+
+ static QuicTransportServerCryptoHelper* helper =
+ new QuicTransportServerCryptoHelper();
+ crypto_stream_ = std::make_unique<QuicCryptoServerStream>(
+ crypto_config, compressed_certs_cache, this, helper);
+}
+
+QuicStream* QuicTransportServerSession::CreateIncomingStream(QuicStreamId id) {
+ if (id == ClientIndicationStream()) {
+ auto indication = std::make_unique<ClientIndication>(this);
+ ClientIndication* indication_ptr = indication.get();
+ ActivateStream(std::move(indication));
+ return indication_ptr;
+ }
+
+ // TODO(vasilvv): implement incoming data streams.
+ QUIC_BUG << "Not implemented";
+ return nullptr;
+}
+
+QuicTransportServerSession::ClientIndication::ClientIndication(
+ QuicTransportServerSession* session)
+ : QuicStream(ClientIndicationStream(),
+ session,
+ /* is_static= */ false,
+ StreamType::READ_UNIDIRECTIONAL),
+ session_(session) {}
+
+void QuicTransportServerSession::ClientIndication::OnDataAvailable() {
+ sequencer()->Read(&buffer_);
+ if (buffer_.size() > ClientIndicationMaxSize()) {
+ session_->connection()->CloseConnection(
+ QUIC_TRANSPORT_INVALID_CLIENT_INDICATION,
+ QuicStrCat("Client indication size exceeds ", ClientIndicationMaxSize(),
+ " bytes"),
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ if (sequencer()->IsClosed()) {
+ session_->ProcessClientIndication(buffer_);
+ OnFinRead();
+ }
+}
+
+bool QuicTransportServerSession::ClientIndicationParser::Parse() {
+ bool origin_received = false;
+ while (!reader_.IsDoneReading()) {
+ uint16_t key;
+ if (!reader_.ReadUInt16(&key)) {
+ ParseError("Expected 16-bit key");
+ return false;
+ }
+
+ QuicStringPiece value;
+ if (!reader_.ReadStringPiece16(&value)) {
+ ParseError(QuicStrCat("Failed to read value for key ", key));
+ return false;
+ }
+
+ switch (static_cast<QuicTransportClientIndicationKeys>(key)) {
+ case QuicTransportClientIndicationKeys::kOrigin: {
+ GURL origin_url{std::string(value)};
+ if (!origin_url.is_valid()) {
+ Error("Unable to parse the specified origin");
+ return false;
+ }
+
+ url::Origin origin = url::Origin::Create(origin_url);
+ QUIC_DLOG(INFO) << "QuicTransport server received origin " << origin;
+ if (!session_->visitor_->CheckOrigin(origin)) {
+ Error("Origin check failed");
+ return false;
+ }
+ origin_received = true;
+ break;
+ }
+
+ default:
+ QUIC_DLOG(INFO) << "Unknown client indication key: " << key;
+ break;
+ }
+ }
+
+ if (!origin_received) {
+ Error("No origin received");
+ return false;
+ }
+
+ return true;
+}
+
+void QuicTransportServerSession::ClientIndicationParser::Error(
+ const std::string& error_message) {
+ session_->connection()->CloseConnection(
+ QUIC_TRANSPORT_INVALID_CLIENT_INDICATION, error_message,
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+}
+
+void QuicTransportServerSession::ClientIndicationParser::ParseError(
+ QuicStringPiece error_message) {
+ Error(QuicStrCat("Failed to parse the client indication stream: ",
+ error_message, reader_.DebugString()));
+}
+
+void QuicTransportServerSession::ProcessClientIndication(
+ QuicStringPiece indication) {
+ ClientIndicationParser parser(this, indication);
+ if (!parser.Parse()) {
+ return;
+ }
+ client_indication_processed_ = true;
+}
+
+} // namespace quic
diff --git a/quic/quic_transport/quic_transport_server_session.h b/quic/quic_transport/quic_transport_server_session.h
new file mode 100644
index 0000000..ee5cf69
--- /dev/null
+++ b/quic/quic_transport/quic_transport_server_session.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2019 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.
+
+#ifndef QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SERVER_SESSION_H_
+#define QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SERVER_SESSION_H_
+
+#include "url/origin.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_server_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
+
+namespace quic {
+
+// A server session for the QuicTransport protocol.
+class QUIC_EXPORT QuicTransportServerSession : public QuicSession {
+ public:
+ class ServerVisitor {
+ public:
+ virtual ~ServerVisitor() {}
+
+ virtual bool CheckOrigin(url::Origin origin) = 0;
+ };
+
+ QuicTransportServerSession(QuicConnection* connection,
+ Visitor* owner,
+ const QuicConfig& config,
+ const ParsedQuicVersionVector& supported_versions,
+ const QuicCryptoServerConfig* crypto_config,
+ QuicCompressedCertsCache* compressed_certs_cache,
+ ServerVisitor* visitor);
+
+ std::vector<QuicStringPiece>::const_iterator SelectAlpn(
+ const std::vector<QuicStringPiece>& alpns) const override {
+ return std::find(alpns.cbegin(), alpns.cend(), QuicTransportAlpn());
+ }
+
+ bool ShouldKeepConnectionAlive() const override { return true; }
+
+ QuicCryptoStream* GetMutableCryptoStream() override {
+ return crypto_stream_.get();
+ }
+ const QuicCryptoStream* GetCryptoStream() const override {
+ return crypto_stream_.get();
+ }
+
+ bool IsSessionReady() const {
+ return IsCryptoHandshakeConfirmed() && client_indication_processed_ &&
+ connection()->connected();
+ }
+
+ QuicStream* CreateIncomingStream(QuicStreamId id) override;
+ QuicStream* CreateIncomingStream(PendingStream* /*pending*/) override {
+ QUIC_BUG << "QuicTransportServerSession::CreateIncomingStream("
+ "PendingStream) not implemented";
+ return nullptr;
+ }
+
+ protected:
+ class ClientIndication : public QuicStream {
+ public:
+ explicit ClientIndication(QuicTransportServerSession* session);
+ void OnDataAvailable() override;
+
+ private:
+ QuicTransportServerSession* session_;
+ std::string buffer_;
+ };
+
+ // Utility class for parsing the client indication.
+ class ClientIndicationParser {
+ public:
+ ClientIndicationParser(QuicTransportServerSession* session,
+ QuicStringPiece indication)
+ : session_(session), reader_(indication) {}
+
+ // Parses the specified indication. Automatically closes the connection
+ // with detailed error if parsing fails. Returns true on success, false on
+ // failure.
+ bool Parse();
+
+ private:
+ void Error(const std::string& error_message);
+ void ParseError(QuicStringPiece error_message);
+
+ QuicTransportServerSession* session_;
+ QuicDataReader reader_;
+ };
+
+ // Parses and processes the client indication as described in
+ // https://vasilvv.github.io/webtransport/draft-vvv-webtransport-quic.html#rfc.section.3.2
+ void ProcessClientIndication(QuicStringPiece indication);
+
+ std::unique_ptr<QuicCryptoServerStream> crypto_stream_;
+ bool client_indication_processed_ = false;
+ ServerVisitor* visitor_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SERVER_SESSION_H_
diff --git a/quic/quic_transport/quic_transport_server_session_test.cc b/quic/quic_transport/quic_transport_server_session_test.cc
new file mode 100644
index 0000000..818c08d
--- /dev/null
+++ b/quic/quic_transport/quic_transport_server_session_test.cc
@@ -0,0 +1,238 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_server_session.h"
+
+#include <cstddef>
+#include <memory>
+#include <string>
+
+#include "url/gurl.h"
+#include "url/origin.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_compressed_certs_cache.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
+#include "net/third_party/quiche/src/quic/core/frames/quic_stream_frame.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_writer.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
+#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
+#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+using testing::_;
+using testing::AnyNumber;
+using testing::DoAll;
+using testing::HasSubstr;
+using testing::Return;
+using testing::SaveArg;
+
+constexpr char kTestOrigin[] = "https://test-origin.test";
+constexpr char kTestOriginClientIndication[] =
+ "\0\0\0\x18https://test-origin.test";
+const url::Origin GetTestOrigin() {
+ return url::Origin::Create(GURL(kTestOrigin));
+}
+const std::string GetTestOriginClientIndication() {
+ return std::string(kTestOriginClientIndication,
+ sizeof(kTestOriginClientIndication) - 1);
+}
+
+ParsedQuicVersionVector GetVersions() {
+ return {ParsedQuicVersion{PROTOCOL_TLS1_3, QUIC_VERSION_99}};
+}
+
+class MockVisitor : public QuicTransportServerSession::ServerVisitor {
+ public:
+ MOCK_METHOD1(CheckOrigin, bool(url::Origin));
+};
+
+class QuicTransportServerSessionTest : public QuicTest {
+ public:
+ QuicTransportServerSessionTest()
+ : connection_(&helper_,
+ &alarm_factory_,
+ Perspective::IS_SERVER,
+ GetVersions()),
+ crypto_config_(QuicCryptoServerConfig::TESTING,
+ QuicRandom::GetInstance(),
+ crypto_test_utils::ProofSourceForTesting(),
+ KeyExchangeSource::Default()),
+ compressed_certs_cache_(
+ QuicCompressedCertsCache::kQuicCompressedCertsCacheSize) {
+ SetQuicReloadableFlag(quic_supports_tls_handshake, true);
+ connection_.AdvanceTime(QuicTime::Delta::FromSeconds(100000));
+ crypto_test_utils::SetupCryptoServerConfigForTest(
+ helper_.GetClock(), helper_.GetRandomGenerator(), &crypto_config_);
+ session_ = std::make_unique<QuicTransportServerSession>(
+ &connection_, nullptr, DefaultQuicConfig(), GetVersions(),
+ &crypto_config_, &compressed_certs_cache_, &visitor_);
+ session_->Initialize();
+ crypto_stream_ = static_cast<QuicCryptoServerStream*>(
+ session_->GetMutableCryptoStream());
+ crypto_stream_->OnSuccessfulVersionNegotiation(GetVersions()[0]);
+ }
+
+ void Connect() {
+ crypto_test_utils::FakeClientOptions options;
+ options.only_tls_versions = true;
+ crypto_test_utils::HandshakeWithFakeClient(
+ &helper_, &alarm_factory_, &connection_, crypto_stream_,
+ QuicServerId("test.example.com", 443), options, QuicTransportAlpn());
+ }
+
+ void ReceiveIndication(QuicStringPiece indication) {
+ QUIC_LOG(INFO) << "Receiving indication: "
+ << QuicTextUtils::HexDump(indication);
+ constexpr size_t kChunkSize = 1024;
+ // Shard the indication, since some of the tests cause it to not fit into a
+ // single frame.
+ for (size_t i = 0; i < indication.size(); i += kChunkSize) {
+ QuicStreamFrame frame(ClientIndicationStream(), /*fin=*/false, i,
+ indication.substr(i, i + kChunkSize));
+ session_->OnStreamFrame(frame);
+ }
+ session_->OnStreamFrame(QuicStreamFrame(ClientIndicationStream(),
+ /*fin=*/true, indication.size(),
+ QuicStringPiece()));
+ }
+
+ protected:
+ MockAlarmFactory alarm_factory_;
+ MockQuicConnectionHelper helper_;
+
+ PacketSavingConnection connection_;
+ QuicCryptoServerConfig crypto_config_;
+ std::unique_ptr<QuicTransportServerSession> session_;
+ QuicCompressedCertsCache compressed_certs_cache_;
+ testing::StrictMock<MockVisitor> visitor_;
+ QuicCryptoServerStream* crypto_stream_;
+};
+
+TEST_F(QuicTransportServerSessionTest, SuccessfulHandshake) {
+ Connect();
+
+ url::Origin origin;
+ EXPECT_CALL(visitor_, CheckOrigin(_))
+ .WillOnce(DoAll(SaveArg<0>(&origin), Return(true)));
+ ReceiveIndication(GetTestOriginClientIndication());
+ EXPECT_TRUE(session_->IsSessionReady());
+ EXPECT_EQ(origin, GetTestOrigin());
+}
+
+TEST_F(QuicTransportServerSessionTest, PiecewiseClientIndication) {
+ Connect();
+ size_t i = 0;
+ for (; i < sizeof(kTestOriginClientIndication) - 2; i++) {
+ QuicStreamFrame frame(ClientIndicationStream(), false, i,
+ QuicStringPiece(&kTestOriginClientIndication[i], 1));
+ session_->OnStreamFrame(frame);
+ }
+
+ EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
+ QuicStreamFrame last_frame(
+ ClientIndicationStream(), true, i,
+ QuicStringPiece(&kTestOriginClientIndication[i], 1));
+ session_->OnStreamFrame(last_frame);
+ EXPECT_TRUE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportServerSessionTest, OriginRejected) {
+ Connect();
+ EXPECT_CALL(connection_,
+ CloseConnection(_, HasSubstr("Origin check failed"), _));
+ EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(false));
+ ReceiveIndication(GetTestOriginClientIndication());
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+std::string MakeUnknownField(QuicStringPiece payload) {
+ std::string buffer;
+ buffer.resize(payload.size() + 4);
+ QuicDataWriter writer(buffer.size(), &buffer[0]);
+ EXPECT_TRUE(writer.WriteUInt16(0xffff));
+ EXPECT_TRUE(writer.WriteUInt16(payload.size()));
+ EXPECT_TRUE(writer.WriteStringPiece(payload));
+ EXPECT_EQ(writer.remaining(), 0u);
+ return buffer;
+}
+
+TEST_F(QuicTransportServerSessionTest, SkipUnusedFields) {
+ Connect();
+ EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
+ ReceiveIndication(GetTestOriginClientIndication() +
+ MakeUnknownField("foobar"));
+ EXPECT_TRUE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportServerSessionTest, SkipLongUnusedFields) {
+ const size_t bytes =
+ ClientIndicationMaxSize() - GetTestOriginClientIndication().size() - 4;
+ Connect();
+ EXPECT_CALL(visitor_, CheckOrigin(_)).WillOnce(Return(true));
+ ReceiveIndication(GetTestOriginClientIndication() +
+ MakeUnknownField(std::string(bytes, 'a')));
+ EXPECT_TRUE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportServerSessionTest, ClientIndicationTooLong) {
+ Connect();
+ EXPECT_CALL(
+ connection_,
+ CloseConnection(_, HasSubstr("Client indication size exceeds"), _))
+ .Times(AnyNumber());
+ ReceiveIndication(GetTestOriginClientIndication() +
+ MakeUnknownField(std::string(65534, 'a')));
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportServerSessionTest, NoOrigin) {
+ Connect();
+ EXPECT_CALL(connection_, CloseConnection(_, HasSubstr("No origin"), _));
+ ReceiveIndication(MakeUnknownField("foobar"));
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportServerSessionTest, EmptyClientIndication) {
+ Connect();
+ EXPECT_CALL(connection_, CloseConnection(_, HasSubstr("No origin"), _));
+ ReceiveIndication("");
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportServerSessionTest, MalformedIndicationHeader) {
+ Connect();
+ EXPECT_CALL(connection_,
+ CloseConnection(_, HasSubstr("Expected 16-bit key"), _));
+ ReceiveIndication("\xff");
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportServerSessionTest, FieldTooShort) {
+ Connect();
+ EXPECT_CALL(
+ connection_,
+ CloseConnection(_, HasSubstr("Failed to read value for key 257"), _));
+ ReceiveIndication("\x01\x01\x01\x01");
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportServerSessionTest, InvalidOrigin) {
+ const std::string kEmptyOriginIndication(4, '\0');
+ Connect();
+ EXPECT_CALL(
+ connection_,
+ CloseConnection(_, HasSubstr("Unable to parse the specified origin"), _));
+ ReceiveIndication(kEmptyOriginIndication);
+ EXPECT_FALSE(session_->IsSessionReady());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/quic/test_tools/crypto_test_utils.cc b/quic/test_tools/crypto_test_utils.cc
index 45872d2..98d65e8 100644
--- a/quic/test_tools/crypto_test_utils.cc
+++ b/quic/test_tools/crypto_test_utils.cc
@@ -26,6 +26,7 @@
#include "net/third_party/quiche/src/quic/core/quic_crypto_stream.h"
#include "net/third_party/quiche/src/quic/core/quic_server_id.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_clock.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
@@ -257,7 +258,8 @@
PacketSavingConnection* server_conn,
QuicCryptoServerStream* server,
const QuicServerId& server_id,
- const FakeClientOptions& options) {
+ const FakeClientOptions& options,
+ std::string alpn) {
ParsedQuicVersionVector supported_versions = AllSupportedVersions();
if (options.only_tls_versions) {
supported_versions.clear();
@@ -282,6 +284,14 @@
EXPECT_CALL(client_session, OnProofVerifyDetailsAvailable(testing::_))
.Times(testing::AnyNumber());
EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber());
+ if (!alpn.empty()) {
+ EXPECT_CALL(client_session, GetAlpnsToOffer())
+ .WillRepeatedly(testing::Return(std::vector<std::string>({alpn})));
+ } else {
+ EXPECT_CALL(client_session, GetAlpnsToOffer())
+ .WillRepeatedly(testing::Return(std::vector<std::string>(
+ {AlpnForVersion(client_conn->version())})));
+ }
client_session.GetMutableCryptoStream()->CryptoConnect();
CHECK_EQ(1u, client_conn->encrypted_packets_.size());
diff --git a/quic/test_tools/crypto_test_utils.h b/quic/test_tools/crypto_test_utils.h
index 4cee641..6f87e90 100644
--- a/quic/test_tools/crypto_test_utils.h
+++ b/quic/test_tools/crypto_test_utils.h
@@ -78,7 +78,8 @@
PacketSavingConnection* server_conn,
QuicCryptoServerStream* server,
const QuicServerId& server_id,
- const FakeClientOptions& options);
+ const FakeClientOptions& options,
+ std::string alpn);
// SetupCryptoServerConfigForTest configures |crypto_config|
// with sensible defaults for testing.
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index 2fc1b4a..fbadf13 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -864,6 +864,7 @@
MOCK_METHOD1(ShouldCreateIncomingStream, bool(QuicStreamId id));
MOCK_METHOD0(ShouldCreateOutgoingBidirectionalStream, bool());
MOCK_METHOD0(ShouldCreateOutgoingUnidirectionalStream, bool());
+ MOCK_CONST_METHOD0(GetAlpnsToOffer, std::vector<std::string>());
// Override to not send max header list size.
void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;