Commit 43d4baee authored by Adam Rice's avatar Adam Rice Committed by Commit Bot

Implement connection and close for QuicTransport

QuicTransport is a low-level JavaScript interface to permit
communication via the QUIC protocol. This CL implements only the
connection and close parts of the API, along with unit tests.

Bug: 1011392
Change-Id: I7383680e04c83a45f483e5d542e9227f8288ed65
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1899631Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Commit-Queue: Adam Rice <ricea@chromium.org>
Cr-Commit-Position: refs/heads/master@{#715349}
parent 4a910ce9
...@@ -877,6 +877,7 @@ modules_dictionary_idl_files = ...@@ -877,6 +877,7 @@ modules_dictionary_idl_files =
"websockets/websocket_close_info.idl", "websockets/websocket_close_info.idl",
"websockets/websocket_connection.idl", "websockets/websocket_connection.idl",
"websockets/websocket_stream_options.idl", "websockets/websocket_stream_options.idl",
"webtransport/web_transport_close_info.idl",
"webusb/usb_connection_event_init.idl", "webusb/usb_connection_event_init.idl",
"webusb/usb_control_transfer_parameters.idl", "webusb/usb_control_transfer_parameters.idl",
"webusb/usb_device_filter.idl", "webusb/usb_device_filter.idl",
......
...@@ -13,7 +13,9 @@ blink_modules_sources("webtransport") { ...@@ -13,7 +13,9 @@ blink_modules_sources("webtransport") {
jumbo_source_set("unit_tests") { jumbo_source_set("unit_tests") {
testonly = true testonly = true
sources = [] sources = [
"quic_transport_test.cc",
]
configs += [ configs += [
"//third_party/blink/renderer:config", "//third_party/blink/renderer:config",
......
...@@ -4,63 +4,177 @@ ...@@ -4,63 +4,177 @@
#include "third_party/blink/renderer/modules/webtransport/quic_transport.h" #include "third_party/blink/renderer/modules/webtransport/quic_transport.h"
#include <utility>
#include "mojo/public/cpp/bindings/remote.h" #include "mojo/public/cpp/bindings/remote.h"
#include "services/service_manager/public/cpp/interface_provider.h" #include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/mojom/webtransport/quic_transport_connector.mojom-blink.h" #include "third_party/blink/public/mojom/webtransport/quic_transport_connector.mojom-blink.h"
#include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink { namespace blink {
namespace {
KURL ReparseURLAsHTTPS(KURL url) {
url.SetProtocol("https");
return url;
}
} // namespace
QuicTransport* QuicTransport::Create(ScriptState* script_state, QuicTransport* QuicTransport::Create(ScriptState* script_state,
const String& url) { const String& url,
ExceptionState& exception_state) {
DVLOG(1) << "QuicTransport::Create() url=" << url;
auto* transport = auto* transport =
MakeGarbageCollected<QuicTransport>(PassKey(), script_state, url); MakeGarbageCollected<QuicTransport>(PassKey(), script_state, url);
transport->Init(); transport->Init(exception_state);
return transport; return transport;
} }
QuicTransport::QuicTransport(PassKey, QuicTransport::QuicTransport(PassKey,
ScriptState* script_state, ScriptState* script_state,
const String& url) const String& url)
: script_state_(script_state), : ContextLifecycleObserver(ExecutionContext::From(script_state)),
url_(url), url_(NullURL(), url) {}
handshake_client_receiver_(this) {}
void QuicTransport::close(const WebTransportCloseInfo* close_info) {
DVLOG(1) << "QuicTransport::close() this=" << this;
// TODO(ricea): Send |close_info| to the network service.
Dispose();
}
void QuicTransport::OnConnectionEstablished( void QuicTransport::OnConnectionEstablished(
mojo::PendingRemote<network::mojom::blink::QuicTransport>, mojo::PendingRemote<network::mojom::blink::QuicTransport> quic_transport,
mojo::PendingReceiver<network::mojom::blink::QuicTransportClient>) { mojo::PendingReceiver<network::mojom::blink::QuicTransportClient>
client_receiver) {
DVLOG(1) << "QuicTransport::OnConnectionEstablished() this=" << this;
handshake_client_receiver_.reset(); handshake_client_receiver_.reset();
// TODO(ricea): Report to devtools.
auto task_runner =
GetExecutionContext()->GetTaskRunner(TaskType::kNetworking);
client_receiver_.Bind(std::move(client_receiver), task_runner);
client_receiver_.set_disconnect_handler(
WTF::Bind(&QuicTransport::OnConnectionError, WrapWeakPersistent(this)));
DCHECK(!quic_transport_);
quic_transport_.Bind(std::move(quic_transport), task_runner);
} }
QuicTransport::~QuicTransport() = default;
void QuicTransport::OnHandshakeFailed() { void QuicTransport::OnHandshakeFailed() {
DVLOG(1) << "QuicTransport::OnHandshakeFailed() this=" << this;
handshake_client_receiver_.reset(); handshake_client_receiver_.reset();
} }
void QuicTransport::ContextDestroyed(ExecutionContext* execution_context) {
DVLOG(1) << "QuicTransport::ContextDestroyed() this=" << this;
Dispose();
}
bool QuicTransport::HasPendingActivity() const {
DVLOG(1) << "QuicTransport::HasPendingActivity() this=" << this;
return handshake_client_receiver_.is_bound() || client_receiver_.is_bound();
}
void QuicTransport::Trace(Visitor* visitor) { void QuicTransport::Trace(Visitor* visitor) {
visitor->Trace(script_state_); ContextLifecycleObserver::Trace(visitor);
ScriptWrappable::Trace(visitor); ScriptWrappable::Trace(visitor);
} }
void QuicTransport::Init() { void QuicTransport::Init(ExceptionState& exception_state) {
DVLOG(1) << "QuicTransport::Init() url=" << url_ << " this=" << this;
if (!url_.IsValid()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"The URL '" + url_.ElidedString() + "' is invalid.");
return;
}
if (!url_.ProtocolIs("quic-transport")) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"The URL's scheme must be 'quic-transport'. '" + url_.Protocol() +
"' is not allowed.");
return;
}
// TODO(ricea): Use the URL as-is once "quic-transport" it has been added to
// the "special" schemes list.
KURL url_as_https = ReparseURLAsHTTPS(url_);
if (!url_as_https.IsValid()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"The URL '" + url_.ElidedString() + "' is invalid.");
return;
}
if (url_as_https.HasFragmentIdentifier()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kSyntaxError,
"The URL contains a fragment identifier ('#" +
url_as_https.FragmentIdentifier() +
"'). Fragment identifiers are not allowed in QuicTransport URLs.");
return;
}
auto* execution_context = GetExecutionContext();
if (!execution_context->GetContentSecurityPolicyForWorld()
->AllowConnectToSource(url_as_https)) {
// TODO(ricea): This error should probably be asynchronous like it is for
// WebSockets and fetch.
exception_state.ThrowSecurityError(
"Failed to connect to '" + url_.ElidedString() + "'",
"Refused to connect to '" + url_.ElidedString() +
"' because it violates the document's Content Security Policy");
return;
}
// TODO(ricea): Register SchedulingPolicy so that we don't get throttled and
// to disable bfcache. Must be done before shipping.
// TODO(ricea): Check the SubresourceFilter and fail asynchronously if
// disallowed. Must be done before shipping.
mojo::Remote<mojom::blink::QuicTransportConnector> connector; mojo::Remote<mojom::blink::QuicTransportConnector> connector;
ExecutionContext* execution_context = ExecutionContext::From(script_state_); auto* interface_provider = execution_context->GetInterfaceProvider();
DCHECK(execution_context->GetInterfaceProvider()); DCHECK(interface_provider);
execution_context->GetInterfaceProvider()->GetInterface( interface_provider->GetInterface(connector.BindNewPipeAndPassReceiver(
connector.BindNewPipeAndPassReceiver(
execution_context->GetTaskRunner(TaskType::kNetworking))); execution_context->GetTaskRunner(TaskType::kNetworking)));
connector->Connect( connector->Connect(
url_, handshake_client_receiver_.BindNewPipeAndPassRemote( url_, handshake_client_receiver_.BindNewPipeAndPassRemote(
execution_context->GetTaskRunner(TaskType::kNetworking))); execution_context->GetTaskRunner(TaskType::kNetworking)));
// TODO(yhirano): Attach a disconnect handler for
// |handshake_client_receiver_|. handshake_client_receiver_.set_disconnect_handler(
WTF::Bind(&QuicTransport::OnConnectionError, WrapWeakPersistent(this)));
// TODO(ricea): Report something to devtools.
} }
void QuicTransport::Dispose() { void QuicTransport::Dispose() {
DVLOG(1) << "QuicTransport::Dispose() this=" << this;
quic_transport_.reset();
handshake_client_receiver_.reset(); handshake_client_receiver_.reset();
client_receiver_.reset();
}
void QuicTransport::OnConnectionError() {
DVLOG(1) << "QuicTransport::OnConnectionError() this=" << this;
Dispose();
} }
} // namespace blink } // namespace blink
...@@ -9,7 +9,10 @@ ...@@ -9,7 +9,10 @@
#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/network/public/mojom/quic_transport.mojom-blink.h" #include "services/network/public/mojom/quic_transport.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h"
#include "third_party/blink/renderer/core/execution_context/context_lifecycle_observer.h"
#include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h"
...@@ -17,20 +20,32 @@ ...@@ -17,20 +20,32 @@
namespace blink { namespace blink {
class ExceptionState;
class ExecutionContext;
class ScriptState; class ScriptState;
class WebTransportCloseInfo;
class MODULES_EXPORT QuicTransport final class MODULES_EXPORT QuicTransport final
: public ScriptWrappable, : public ScriptWrappable,
public network::mojom::blink::QuicTransportHandshakeClient { public ActiveScriptWrappable<QuicTransport>,
public ContextLifecycleObserver,
public network::mojom::blink::QuicTransportHandshakeClient,
public network::mojom::blink::QuicTransportClient {
DEFINE_WRAPPERTYPEINFO(); DEFINE_WRAPPERTYPEINFO();
USING_PRE_FINALIZER(QuicTransport, Dispose); USING_PRE_FINALIZER(QuicTransport, Dispose);
USING_GARBAGE_COLLECTED_MIXIN(QuicTransport);
public: public:
using PassKey = util::PassKey<QuicTransport>; using PassKey = util::PassKey<QuicTransport>;
static QuicTransport* Create(ScriptState* script_state, const String& url); static QuicTransport* Create(ScriptState* script_state,
const String& url,
ExceptionState&);
QuicTransport(PassKey, ScriptState*, const String& url); QuicTransport(PassKey, ScriptState*, const String& url);
~QuicTransport() override = default; ~QuicTransport() override;
// QuicTransport IDL implementation.
void close(const WebTransportCloseInfo*);
// QuicTransportHandshakeClient implementation // QuicTransportHandshakeClient implementation
void OnConnectionEstablished( void OnConnectionEstablished(
...@@ -39,19 +54,28 @@ class MODULES_EXPORT QuicTransport final ...@@ -39,19 +54,28 @@ class MODULES_EXPORT QuicTransport final
override; override;
void OnHandshakeFailed() override; void OnHandshakeFailed() override;
// Implementation of ContextLifecycleObserver
void ContextDestroyed(ExecutionContext*) final;
// Implementation of ActiveScriptWrappable
bool HasPendingActivity() const final;
// ScriptWrappable implementation // ScriptWrappable implementation
void Trace(Visitor* visitor) override; void Trace(Visitor* visitor) override;
private: private:
void Init(); void Init(ExceptionState&);
void Dispose(); void Dispose();
void OnConnectionError();
const Member<ScriptState> script_state_;
const KURL url_; const KURL url_;
mojo::Remote<network::mojom::blink::QuicTransport> quic_transport_;
mojo::Receiver<network::mojom::blink::QuicTransportHandshakeClient> mojo::Receiver<network::mojom::blink::QuicTransportHandshakeClient>
handshake_client_receiver_; handshake_client_receiver_{this};
mojo::Receiver<network::mojom::blink::QuicTransportClient> client_receiver_{
this};
}; };
} // namespace blink } // namespace blink
#endif #endif // THIRD_PARTY_BLINK_RENDERER_MODULES_WEBTRANSPORT_QUIC_TRANSPORT_H_
...@@ -4,13 +4,17 @@ ...@@ -4,13 +4,17 @@
// https://wicg.github.io/web-transport/#quic-transport // https://wicg.github.io/web-transport/#quic-transport
[ [
ActiveScriptWrappable,
Constructor(USVString url), Constructor(USVString url),
Exposed=(Window, Worker), Exposed=(Window,Worker),
RuntimeEnabled=QuicTransport, RuntimeEnabled=QuicTransport,
ConstructorCallWith=ScriptState ConstructorCallWith=ScriptState,
RaisesException=Constructor
] interface QuicTransport { ] interface QuicTransport {
// QuicTransport is the first, and at this moment only, transport which is // QuicTransport is the first, and at this moment only, transport which is
// implemented. In the (draft) spec there are many mix-in interfaces which // implemented. In the (draft) spec there are many mix-in interfaces which
// QuicTransport includes, but we define all their methods/attributes here // QuicTransport includes, but we define all their methods/attributes here
// for simplicity. // for simplicity.
void close(optional WebTransportCloseInfo closeInfo);
}; };
// Copyright 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 "third_party/blink/renderer/modules/webtransport/quic_transport.h"
#include <memory>
#include <utility>
#include "base/memory/weak_ptr.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "services/network/public/mojom/quic_transport.mojom-blink.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/mojom/webtransport/quic_transport_connector.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/modules/webtransport/web_transport_close_info.h"
#include "third_party/blink/renderer/platform/bindings/exception_code.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "v8/include/v8.h"
namespace blink {
namespace {
class QuicTransportConnector final
: public mojom::blink::QuicTransportConnector {
public:
struct ConnectArgs {
ConnectArgs(
const KURL& url,
mojo::PendingRemote<network::mojom::blink::QuicTransportHandshakeClient>
handshake_client)
: url(url), handshake_client(std::move(handshake_client)) {}
KURL url;
mojo::PendingRemote<network::mojom::blink::QuicTransportHandshakeClient>
handshake_client;
};
void Connect(
const KURL& url,
mojo::PendingRemote<network::mojom::blink::QuicTransportHandshakeClient>
handshake_client) override {
connect_args_.push_back(ConnectArgs(url, std::move(handshake_client)));
}
Vector<ConnectArgs> TakeConnectArgs() { return std::move(connect_args_); }
void Bind(
mojo::PendingReceiver<mojom::blink::QuicTransportConnector> receiver) {
receiver_set_.Add(this, std::move(receiver));
}
private:
mojo::ReceiverSet<mojom::blink::QuicTransportConnector> receiver_set_;
Vector<ConnectArgs> connect_args_;
};
class MockQuicTransport final : public network::mojom::blink::QuicTransport {
public:
MockQuicTransport(mojo::PendingReceiver<network::mojom::blink::QuicTransport>
pending_receiver)
: receiver_(this, std::move(pending_receiver)) {}
// TODO(ricea): Add methods when there are some.
private:
mojo::Receiver<network::mojom::blink::QuicTransport> receiver_;
};
class QuicTransportTest : public ::testing::Test {
public:
void AddBinder(const V8TestingScope& scope) {
service_manager::InterfaceProvider::TestApi(
scope.GetExecutionContext()->GetInterfaceProvider())
.SetBinderForName(mojom::blink::QuicTransportConnector::Name_,
base::BindRepeating(&QuicTransportTest::BindConnector,
weak_ptr_factory_.GetWeakPtr()));
}
// Creates, connects and returns a QuicTransport object with the given |url|.
// Runs the event loop.
QuicTransport* ConnectSuccessfully(const V8TestingScope& scope,
const String& url) {
AddBinder(scope);
auto* quic_transport =
QuicTransport::Create(scope.GetScriptState(), url, ASSERT_NO_EXCEPTION);
test::RunPendingTasks();
auto args = connector_.TakeConnectArgs();
if (args.size() != 1u) {
ADD_FAILURE() << "args.size() should be 1, but is " << args.size();
return nullptr;
}
mojo::Remote<network::mojom::blink::QuicTransportHandshakeClient>
handshake_client(std::move(args[0].handshake_client));
mojo::PendingRemote<network::mojom::blink::QuicTransport>
quic_transport_to_pass;
mojo::PendingRemote<network::mojom::blink::QuicTransportClient>
client_remote;
mock_quic_transport_ = std::make_unique<MockQuicTransport>(
quic_transport_to_pass.InitWithNewPipeAndPassReceiver());
handshake_client->OnConnectionEstablished(
std::move(quic_transport_to_pass),
client_remote.InitWithNewPipeAndPassReceiver());
test::RunPendingTasks();
return quic_transport;
}
void BindConnector(mojo::ScopedMessagePipeHandle handle) {
connector_.Bind(mojo::PendingReceiver<mojom::blink::QuicTransportConnector>(
std::move(handle)));
}
QuicTransportConnector connector_;
std::unique_ptr<MockQuicTransport> mock_quic_transport_;
base::WeakPtrFactory<QuicTransportTest> weak_ptr_factory_{this};
};
TEST_F(QuicTransportTest, FailWithNullURL) {
V8TestingScope scope;
auto& exception_state = scope.GetExceptionState();
QuicTransport::Create(scope.GetScriptState(), String(), exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
exception_state.Code());
}
TEST_F(QuicTransportTest, FailWithEmptyURL) {
V8TestingScope scope;
auto& exception_state = scope.GetExceptionState();
QuicTransport::Create(scope.GetScriptState(), String(""), exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
exception_state.Code());
EXPECT_EQ("The URL '' is invalid.", exception_state.Message());
}
TEST_F(QuicTransportTest, FailWithHttpsURL) {
V8TestingScope scope;
auto& exception_state = scope.GetExceptionState();
QuicTransport::Create(scope.GetScriptState(), String("https://example.com/"),
exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
exception_state.Code());
EXPECT_EQ(
"The URL's scheme must be 'quic-transport'. 'https' is not allowed.",
exception_state.Message());
}
TEST_F(QuicTransportTest, FailWithNoHost) {
V8TestingScope scope;
auto& exception_state = scope.GetExceptionState();
QuicTransport::Create(scope.GetScriptState(), String("quic-transport:///"),
exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
exception_state.Code());
EXPECT_EQ("The URL 'quic-transport:///' is invalid.",
exception_state.Message());
}
TEST_F(QuicTransportTest, FailWithURLFragment) {
V8TestingScope scope;
auto& exception_state = scope.GetExceptionState();
QuicTransport::Create(scope.GetScriptState(),
String("quic-transport://example.com/#failing"),
exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
exception_state.Code());
EXPECT_EQ(
"The URL contains a fragment identifier ('#failing'). Fragment "
"identifiers are not allowed in QuicTransport URLs.",
exception_state.Message());
}
TEST_F(QuicTransportTest, FailByCSP) {
V8TestingScope scope;
auto& exception_state = scope.GetExceptionState();
scope.GetExecutionContext()
->GetContentSecurityPolicyForWorld()
->DidReceiveHeader("connect-src 'none'",
kContentSecurityPolicyHeaderTypeEnforce,
kContentSecurityPolicyHeaderSourceHTTP);
QuicTransport::Create(scope.GetScriptState(),
String("quic-transport://example.com/"),
exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSecurityError),
exception_state.Code());
EXPECT_EQ("Failed to connect to 'quic-transport://example.com/'",
exception_state.Message());
}
TEST_F(QuicTransportTest, PassCSP) {
V8TestingScope scope;
// This doesn't work without the https:// prefix, even thought it should
// according to
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src.
// TODO(ricea): Make it work with the quic-transport: scheme.
auto& exception_state = scope.GetExceptionState();
scope.GetExecutionContext()
->GetContentSecurityPolicyForWorld()
->DidReceiveHeader("connect-src https://example.com:443",
kContentSecurityPolicyHeaderTypeEnforce,
kContentSecurityPolicyHeaderSourceHTTP);
QuicTransport::Create(scope.GetScriptState(),
String("quic-transport://example.com/"),
exception_state);
EXPECT_FALSE(exception_state.HadException());
}
TEST_F(QuicTransportTest, SendConnect) {
V8TestingScope scope;
AddBinder(scope);
auto* quic_transport = QuicTransport::Create(
scope.GetScriptState(), String("quic-transport://example.com/"),
ASSERT_NO_EXCEPTION);
test::RunPendingTasks();
auto args = connector_.TakeConnectArgs();
ASSERT_EQ(1u, args.size());
EXPECT_EQ(KURL("quic-transport://example.com/"), args[0].url);
EXPECT_TRUE(quic_transport->HasPendingActivity());
}
TEST_F(QuicTransportTest, SuccessfulConnect) {
V8TestingScope scope;
auto* quic_transport =
ConnectSuccessfully(scope, "quic-transport://example.com");
EXPECT_TRUE(quic_transport->HasPendingActivity());
}
TEST_F(QuicTransportTest, FailedConnect) {
V8TestingScope scope;
AddBinder(scope);
auto* quic_transport = QuicTransport::Create(
scope.GetScriptState(), String("quic-transport://example.com/"),
ASSERT_NO_EXCEPTION);
test::RunPendingTasks();
auto args = connector_.TakeConnectArgs();
ASSERT_EQ(1u, args.size());
mojo::Remote<network::mojom::blink::QuicTransportHandshakeClient>
handshake_client(std::move(args[0].handshake_client));
handshake_client->OnHandshakeFailed();
test::RunPendingTasks();
EXPECT_FALSE(quic_transport->HasPendingActivity());
}
TEST_F(QuicTransportTest, CloseDuringConnect) {
V8TestingScope scope;
AddBinder(scope);
auto* quic_transport = QuicTransport::Create(
scope.GetScriptState(), String("quic-transport://example.com/"),
ASSERT_NO_EXCEPTION);
test::RunPendingTasks();
auto args = connector_.TakeConnectArgs();
ASSERT_EQ(1u, args.size());
quic_transport->close(nullptr);
test::RunPendingTasks();
EXPECT_FALSE(quic_transport->HasPendingActivity());
}
TEST_F(QuicTransportTest, CloseAfterConnection) {
V8TestingScope scope;
auto* quic_transport =
ConnectSuccessfully(scope, "quic-transport://example.com");
WebTransportCloseInfo close_info;
close_info.setErrorCode(42);
close_info.setReason("because");
quic_transport->close(&close_info);
test::RunPendingTasks();
// TODO(ricea): Check that the close info is sent through correctly, once we
// start sending it.
EXPECT_FALSE(quic_transport->HasPendingActivity());
// Calling close again does nothing.
quic_transport->close(nullptr);
}
// A live connection will be kept alive even if there is no explicit reference.
// When the underlying connection is shut down, the connection will be swept.
TEST_F(QuicTransportTest, GarbageCollection) {
V8TestingScope scope;
WeakPersistent<QuicTransport> quic_transport =
ConnectSuccessfully(scope, "quic-transport://example.com");
// Pretend the stack is empty. This will avoid accidentally treating any
// copies of the |quic_transport| pointer as references.
V8GCController::CollectAllGarbageForTesting(
scope.GetIsolate(), v8::EmbedderHeapTracer::EmbedderStackState::kEmpty);
EXPECT_TRUE(quic_transport);
quic_transport->close(nullptr);
test::RunPendingTasks();
V8GCController::CollectAllGarbageForTesting(
scope.GetIsolate(), v8::EmbedderHeapTracer::EmbedderStackState::kEmpty);
EXPECT_FALSE(quic_transport);
}
TEST_F(QuicTransportTest, GarbageCollectMojoConnectionError) {
V8TestingScope scope;
WeakPersistent<QuicTransport> quic_transport =
ConnectSuccessfully(scope, "quic-transport://example.com");
// Deleting the server-side object causes a mojo connection error.
mock_quic_transport_ = nullptr;
test::RunPendingTasks();
V8GCController::CollectAllGarbageForTesting(
scope.GetIsolate(), v8::EmbedderHeapTracer::EmbedderStackState::kEmpty);
EXPECT_FALSE(quic_transport);
}
} // namespace
} // namespace blink
// Copyright 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.
// https://wicg.github.io/web-transport/#dictdef-webtransportcloseinfo
dictionary WebTransportCloseInfo {
unsigned short errorCode = 0;
DOMString reason = "";
};
...@@ -1221,6 +1221,7 @@ interface PushSubscriptionOptions ...@@ -1221,6 +1221,7 @@ interface PushSubscriptionOptions
method constructor method constructor
interface QuicTransport interface QuicTransport
attribute @@toStringTag attribute @@toStringTag
method close
method constructor method constructor
interface ReadableStream interface ReadableStream
attribute @@toStringTag attribute @@toStringTag
......
...@@ -1155,6 +1155,7 @@ Starting worker: resources/global-interface-listing-worker.js ...@@ -1155,6 +1155,7 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] method constructor [Worker] method constructor
[Worker] interface QuicTransport [Worker] interface QuicTransport
[Worker] attribute @@toStringTag [Worker] attribute @@toStringTag
[Worker] method close
[Worker] method constructor [Worker] method constructor
[Worker] interface ReadableStream [Worker] interface ReadableStream
[Worker] attribute @@toStringTag [Worker] attribute @@toStringTag
......
...@@ -6144,6 +6144,7 @@ interface PushSubscriptionOptions ...@@ -6144,6 +6144,7 @@ interface PushSubscriptionOptions
method constructor method constructor
interface QuicTransport interface QuicTransport
attribute @@toStringTag attribute @@toStringTag
method close
method constructor method constructor
interface RTCCertificate interface RTCCertificate
attribute @@toStringTag attribute @@toStringTag
......
...@@ -1137,6 +1137,7 @@ Starting worker: resources/global-interface-listing-worker.js ...@@ -1137,6 +1137,7 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] method constructor [Worker] method constructor
[Worker] interface QuicTransport [Worker] interface QuicTransport
[Worker] attribute @@toStringTag [Worker] attribute @@toStringTag
[Worker] method close
[Worker] method constructor [Worker] method constructor
[Worker] interface ReadableStream [Worker] interface ReadableStream
[Worker] attribute @@toStringTag [Worker] attribute @@toStringTag
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment