Implement a QuicSession subclass for QuicTransport client.

gfe-relnote: n/a (code not used in production)
PiperOrigin-RevId: 272427421
Change-Id: I1a8ebe085ce26eaa9ec19c0be9b6f4bb7c2ad73e
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index 235d810..2fab3e1 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -165,7 +165,8 @@
           server_max_incoming_streams);
     }
     crypto_test_utils::HandshakeWithFakeServer(
-        &config, &helper_, &alarm_factory_, connection_, stream);
+        &config, &helper_, &alarm_factory_, connection_, stream,
+        AlpnForVersion(connection_->version()));
   }
 
   QuicCryptoClientConfig crypto_config_;
diff --git a/quic/core/quic_crypto_client_stream_test.cc b/quic/core/quic_crypto_client_stream_test.cc
index f0a60e0..9969baf 100644
--- a/quic/core/quic_crypto_client_stream_test.cc
+++ b/quic/core/quic_crypto_client_stream_test.cc
@@ -62,7 +62,8 @@
     stream()->CryptoConnect();
     QuicConfig config;
     crypto_test_utils::HandshakeWithFakeServer(
-        &config, &server_helper_, &alarm_factory_, connection_, stream());
+        &config, &server_helper_, &alarm_factory_, connection_, stream(),
+        AlpnForVersion(connection_->version()));
   }
 
   QuicCryptoClientStream* stream() {
diff --git a/quic/core/quic_crypto_server_stream_test.cc b/quic/core/quic_crypto_server_stream_test.cc
index e2ccfde..b73ea43 100644
--- a/quic/core/quic_crypto_server_stream_test.cc
+++ b/quic/core/quic_crypto_server_stream_test.cc
@@ -21,6 +21,7 @@
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/core/quic_session.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_flags.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
@@ -90,6 +91,12 @@
     server_session_.reset(server_session);
     EXPECT_CALL(*server_session_->helper(), CanAcceptClientHello(_, _, _, _, _))
         .Times(testing::AnyNumber());
+    EXPECT_CALL(*server_session_, SelectAlpn(_))
+        .WillRepeatedly([this](const std::vector<QuicStringPiece>& alpns) {
+          return std::find(
+              alpns.cbegin(), alpns.cend(),
+              AlpnForVersion(server_session_->connection()->version()));
+        });
     crypto_test_utils::SetupCryptoServerConfigForTest(
         server_connection_->clock(), server_connection_->random_generator(),
         &server_crypto_config_);
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 758106f..1805387 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -596,7 +596,7 @@
     return false;
   }
 
-  bool IsHandshakeConfirmed() { return is_handshake_confirmed_; }
+  bool IsHandshakeConfirmed() const { return is_handshake_confirmed_; }
 
  private:
   friend class test::QuicSessionPeer;
diff --git a/quic/quic_transport/README.md b/quic/quic_transport/README.md
new file mode 100644
index 0000000..e2f0849
--- /dev/null
+++ b/quic/quic_transport/README.md
@@ -0,0 +1,7 @@
+# QuicTransport
+
+The files in this directory implement QuicTransport protocol as described in
+<https://tools.ietf.org/html/draft-vvv-webtransport-quic>.
+
+Design doc:
+https://docs.google.com/document/d/1UgviRBnZkMUq4OKcsAJvIQFX6UCXeCbOtX_wMgwD_es/edit#
diff --git a/quic/quic_transport/quic_transport_client_session.cc b/quic/quic_transport/quic_transport_client_session.cc
new file mode 100644
index 0000000..35d7a82
--- /dev/null
+++ b/quic/quic_transport/quic_transport_client_session.cc
@@ -0,0 +1,111 @@
+// 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_client_session.h"
+
+#include <memory>
+
+#include "url/gurl.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/core/quic_versions.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+const char* kQuicTransportAlpn = "wq-draft01";
+
+namespace {
+// ProofHandler is primarily used by QUIC crypto to persist QUIC server configs
+// and perform some of related debug logging.  QuicTransport does not support
+// QUIC crypto, so those methods are not called.
+class DummyProofHandler : public QuicCryptoClientStream::ProofHandler {
+ public:
+  void OnProofValid(
+      const QuicCryptoClientConfig::CachedState& /*cached*/) override {}
+  void OnProofVerifyDetailsAvailable(
+      const ProofVerifyDetails& /*verify_details*/) override {}
+};
+}  // namespace
+
+QuicTransportClientSession::QuicTransportClientSession(
+    QuicConnection* connection,
+    Visitor* owner,
+    const QuicConfig& config,
+    const ParsedQuicVersionVector& supported_versions,
+    const QuicServerId& server_id,
+    QuicCryptoClientConfig* crypto_config,
+    url::Origin origin)
+    : QuicSession(connection,
+                  owner,
+                  config,
+                  supported_versions,
+                  /*num_expected_unidirectional_static_streams*/ 0),
+      origin_(origin) {
+  for (const ParsedQuicVersion& version : supported_versions) {
+    QUIC_BUG_IF(version.handshake_protocol != PROTOCOL_TLS1_3)
+        << "QuicTransport requires TLS 1.3 handshake";
+  }
+  // ProofHandler API is not used by TLS 1.3.
+  static DummyProofHandler* proof_handler = new DummyProofHandler();
+  crypto_stream_ = std::make_unique<QuicCryptoClientStream>(
+      server_id, this, crypto_config->proof_verifier()->CreateDefaultContext(),
+      crypto_config, proof_handler);
+}
+
+void QuicTransportClientSession::OnCryptoHandshakeEvent(
+    CryptoHandshakeEvent event) {
+  QuicSession::OnCryptoHandshakeEvent(event);
+  if (event != HANDSHAKE_CONFIRMED) {
+    return;
+  }
+
+  auto it = config()->received_custom_transport_parameters().find(
+      WebAcceptedOriginsParameter());
+  if (it == config()->received_custom_transport_parameters().end()) {
+    connection()->CloseConnection(
+        QUIC_HANDSHAKE_FAILED,
+        "QuicTransport requires web_accepted_origins transport parameter",
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+    return;
+  }
+
+  QUIC_DLOG(INFO) << "QuicTransport using origin: " << origin_.Serialize();
+  QUIC_DLOG(INFO) << "QuicTransport origins offered: " << it->second;
+
+  if (CheckOrigin(it->second)) {
+    is_origin_valid_ = true;
+  } else {
+    QUIC_DLOG(ERROR) << "Origin check failed for " << origin_
+                     << ", allowed origin list: " << it->second;
+    connection()->CloseConnection(
+        QUIC_HANDSHAKE_FAILED, "QuicTransport origin check failed",
+        ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+  }
+}
+
+bool QuicTransportClientSession::CheckOrigin(
+    QuicStringPiece raw_accepted_origins) {
+  if (raw_accepted_origins == "*") {
+    return true;
+  }
+
+  std::vector<QuicStringPiece> accepted_origins =
+      QuicTextUtils::Split(raw_accepted_origins, ',');
+  for (QuicStringPiece raw_origin : accepted_origins) {
+    url::Origin accepted_origin =
+        url::Origin::Create(GURL(std::string(raw_origin)));
+    QUIC_DVLOG(1) << "QuicTransport offered origin normalized: "
+                  << accepted_origin.Serialize();
+    if (accepted_origin.IsSameOriginWith(origin_)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace quic
diff --git a/quic/quic_transport/quic_transport_client_session.h b/quic/quic_transport/quic_transport_client_session.h
new file mode 100644
index 0000000..7e1cf73
--- /dev/null
+++ b/quic/quic_transport/quic_transport_client_session.h
@@ -0,0 +1,78 @@
+// 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_SESSION_H_
+#define QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SESSION_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "url/origin.h"
+#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_config.h"
+#include "net/third_party/quiche/src/quic/core/quic_connection.h"
+#include "net/third_party/quiche/src/quic/core/quic_crypto_client_stream.h"
+#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_session.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"
+
+namespace quic {
+
+// The web_accepted_origins transport parameter ID.
+constexpr TransportParameters::TransportParameterId
+WebAcceptedOriginsParameter() {
+  return static_cast<TransportParameters::TransportParameterId>(0xffc8);
+}
+
+// The ALPN used by QuicTransport.
+QUIC_EXPORT extern const char* kQuicTransportAlpn;
+
+// A client session for the QuicTransport protocol.
+class QUIC_EXPORT QuicTransportClientSession : public QuicSession {
+ public:
+  QuicTransportClientSession(QuicConnection* connection,
+                             Visitor* owner,
+                             const QuicConfig& config,
+                             const ParsedQuicVersionVector& supported_versions,
+                             const QuicServerId& server_id,
+                             QuicCryptoClientConfig* crypto_config,
+                             url::Origin origin);
+
+  std::vector<std::string> GetAlpnsToOffer() const override {
+    return std::vector<std::string>({kQuicTransportAlpn});
+  }
+
+  void CryptoConnect() { crypto_stream_->CryptoConnect(); }
+
+  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() && is_origin_valid_;
+  }
+
+  void OnCryptoHandshakeEvent(CryptoHandshakeEvent event) override;
+
+ protected:
+  // Accepts the list of accepted origins in a format specified in
+  // <https://tools.ietf.org/html/draft-vvv-webtransport-quic-00#section-3.2>,
+  // and verifies that at least one of them matches |origin_|.
+  bool CheckOrigin(QuicStringPiece raw_accepted_origins);
+
+  std::unique_ptr<QuicCryptoClientStream> crypto_stream_;
+  url::Origin origin_;
+  bool is_origin_valid_ = false;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_QUIC_TRANSPORT_QUIC_TRANSPORT_SESSION_H_
diff --git a/quic/quic_transport/quic_transport_client_session_test.cc b/quic/quic_transport/quic_transport_client_session_test.cc
new file mode 100644
index 0000000..c500e2d
--- /dev/null
+++ b/quic/quic_transport/quic_transport_client_session_test.cc
@@ -0,0 +1,157 @@
+// 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_client_session.h"
+
+#include <memory>
+
+#include "url/gurl.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/platform/api/quic_str_cat.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.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::ElementsAre;
+
+const char* kTestOrigin = "https://test-origin.test";
+const char* kTestOriginInsecure = "http://test-origin.test";
+url::Origin GetTestOrigin() {
+  GURL origin_url(kTestOrigin);
+  return url::Origin::Create(origin_url);
+}
+
+ParsedQuicVersionVector GetVersions() {
+  return {ParsedQuicVersion{PROTOCOL_TLS1_3, QUIC_VERSION_99}};
+}
+
+class TestClientSession : public QuicTransportClientSession {
+ public:
+  using QuicTransportClientSession::QuicTransportClientSession;
+
+  class Stream : public QuicStream {
+   public:
+    using QuicStream::QuicStream;
+    void OnDataAvailable() override {}
+  };
+
+  QuicStream* CreateIncomingStream(QuicStreamId id) override {
+    auto stream = std::make_unique<Stream>(
+        id, this, /*is_static=*/false,
+        QuicUtils::GetStreamType(id, connection()->perspective(),
+                                 /*peer_initiated=*/true));
+    QuicStream* result = stream.get();
+    ActivateStream(std::move(stream));
+    return result;
+  }
+
+  QuicStream* CreateIncomingStream(PendingStream* /*pending*/) override {
+    QUIC_NOTREACHED();
+    return nullptr;
+  }
+};
+
+class QuicTransportClientSessionTest : public QuicTest {
+ protected:
+  QuicTransportClientSessionTest()
+      : connection_(&helper_,
+                    &alarm_factory_,
+                    Perspective::IS_CLIENT,
+                    GetVersions()),
+        server_id_("test.example.com", 443),
+        crypto_config_(crypto_test_utils::ProofVerifierForTesting()) {
+    SetQuicReloadableFlag(quic_supports_tls_handshake, true);
+    session_ = std::make_unique<TestClientSession>(
+        &connection_, nullptr, DefaultQuicConfig(), GetVersions(), server_id_,
+        &crypto_config_, GetTestOrigin());
+    session_->Initialize();
+    crypto_stream_ = static_cast<QuicCryptoClientStream*>(
+        session_->GetMutableCryptoStream());
+  }
+
+  void ConnectWithOriginList(std::string accepted_origins) {
+    session_->CryptoConnect();
+    QuicConfig server_config = DefaultQuicConfig();
+    server_config
+        .custom_transport_parameters_to_send()[WebAcceptedOriginsParameter()] =
+        accepted_origins;
+    crypto_test_utils::HandshakeWithFakeServer(
+        &server_config, &helper_, &alarm_factory_, &connection_, crypto_stream_,
+        kQuicTransportAlpn);
+  }
+
+  MockAlarmFactory alarm_factory_;
+  MockQuicConnectionHelper helper_;
+
+  PacketSavingConnection connection_;
+  QuicServerId server_id_;
+  QuicCryptoClientConfig crypto_config_;
+  std::unique_ptr<TestClientSession> session_;
+  QuicCryptoClientStream* crypto_stream_;
+};
+
+TEST_F(QuicTransportClientSessionTest, HasValidAlpn) {
+  EXPECT_THAT(session_->GetAlpnsToOffer(), ElementsAre(kQuicTransportAlpn));
+}
+
+TEST_F(QuicTransportClientSessionTest, SuccessfulConnection) {
+  ConnectWithOriginList(GetTestOrigin().Serialize());
+  EXPECT_TRUE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, SuccessfulConnectionManyOrigins) {
+  ConnectWithOriginList(
+      QuicStrCat("http://example.org,", kTestOrigin, ",https://example.com"));
+  EXPECT_TRUE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, SuccessfulConnectionWildcardOrigin) {
+  ConnectWithOriginList("*");
+  EXPECT_TRUE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, OriginMismatch) {
+  EXPECT_CALL(connection_,
+              CloseConnection(_, "QuicTransport origin check failed", _));
+  ConnectWithOriginList("https://obviously-wrong-website.test");
+  EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, OriginSchemaMismatch) {
+  EXPECT_CALL(connection_,
+              CloseConnection(_, "QuicTransport origin check failed", _));
+  ConnectWithOriginList(kTestOriginInsecure);
+  EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, OriginListMissing) {
+  EXPECT_CALL(
+      connection_,
+      CloseConnection(
+          _, "QuicTransport requires web_accepted_origins transport parameter",
+          _));
+  session_->CryptoConnect();
+  QuicConfig server_config = DefaultQuicConfig();
+  crypto_test_utils::HandshakeWithFakeServer(
+      &server_config, &helper_, &alarm_factory_, &connection_, crypto_stream_,
+      kQuicTransportAlpn);
+  EXPECT_FALSE(session_->IsSessionReady());
+}
+
+TEST_F(QuicTransportClientSessionTest, OriginListEmpty) {
+  EXPECT_CALL(connection_,
+              CloseConnection(_, "QuicTransport origin check failed", _));
+  ConnectWithOriginList("");
+  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 ce0cb6e..7e9b882 100644
--- a/quic/test_tools/crypto_test_utils.cc
+++ b/quic/test_tools/crypto_test_utils.cc
@@ -45,6 +45,8 @@
 
 namespace {
 
+using testing::_;
+
 // CryptoFramerVisitor is a framer visitor that records handshake messages.
 class CryptoFramerVisitor : public CryptoFramerVisitorInterface {
  public:
@@ -210,7 +212,8 @@
                             MockQuicConnectionHelper* helper,
                             MockAlarmFactory* alarm_factory,
                             PacketSavingConnection* client_conn,
-                            QuicCryptoClientStream* client) {
+                            QuicCryptoClientStream* client,
+                            std::string alpn) {
   PacketSavingConnection* server_conn = new PacketSavingConnection(
       helper, alarm_factory, Perspective::IS_SERVER,
       ParsedVersionOfIndex(client_conn->supported_versions(), 0));
@@ -234,6 +237,10 @@
       .Times(testing::AnyNumber());
   EXPECT_CALL(*server_conn, OnCanWrite()).Times(testing::AnyNumber());
   EXPECT_CALL(*client_conn, OnCanWrite()).Times(testing::AnyNumber());
+  EXPECT_CALL(server_session, SelectAlpn(_))
+      .WillRepeatedly([alpn](const std::vector<QuicStringPiece>& alpns) {
+        return std::find(alpns.cbegin(), alpns.cend(), alpn);
+      });
 
   // The client's handshake must have been started already.
   CHECK_NE(0u, client_conn->encrypted_packets_.size());
diff --git a/quic/test_tools/crypto_test_utils.h b/quic/test_tools/crypto_test_utils.h
index 421c0f3..4cee641 100644
--- a/quic/test_tools/crypto_test_utils.h
+++ b/quic/test_tools/crypto_test_utils.h
@@ -69,7 +69,8 @@
                             MockQuicConnectionHelper* helper,
                             MockAlarmFactory* alarm_factory,
                             PacketSavingConnection* client_conn,
-                            QuicCryptoClientStream* client);
+                            QuicCryptoClientStream* client,
+                            std::string alpn);
 
 // returns: the number of client hellos that the client sent.
 int HandshakeWithFakeClient(MockQuicConnectionHelper* helper,
diff --git a/quic/test_tools/quic_test_utils.h b/quic/test_tools/quic_test_utils.h
index c12587d..4246cfa 100644
--- a/quic/test_tools/quic_test_utils.h
+++ b/quic/test_tools/quic_test_utils.h
@@ -794,6 +794,9 @@
   MOCK_METHOD1(CreateIncomingStream, QuicSpdyStream*(PendingStream* stream));
   MOCK_METHOD0(CreateOutgoingBidirectionalStream, QuicSpdyStream*());
   MOCK_METHOD0(CreateOutgoingUnidirectionalStream, QuicSpdyStream*());
+  MOCK_CONST_METHOD1(SelectAlpn,
+                     std::vector<QuicStringPiece>::const_iterator(
+                         const std::vector<QuicStringPiece>&));
   QuicCryptoServerStreamBase* CreateQuicCryptoServerStream(
       const QuicCryptoServerConfig* crypto_config,
       QuicCompressedCertsCache* compressed_certs_cache) override;