1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/modules/webtransport/quic_transport.h"
6 
7 #include <array>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/containers/span.h"
12 #include "base/memory/weak_ptr.h"
13 #include "base/test/mock_callback.h"
14 #include "mojo/public/cpp/bindings/receiver_set.h"
15 #include "services/network/public/mojom/quic_transport.mojom-blink.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/blink/public/common/browser_interface_broker_proxy.h"
19 #include "third_party/blink/public/mojom/webtransport/quic_transport_connector.mojom-blink.h"
20 #include "third_party/blink/renderer/bindings/core/v8/script_promise_tester.h"
21 #include "third_party/blink/renderer/bindings/core/v8/script_value.h"
22 #include "third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h"
23 #include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer.h"
24 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
25 #include "third_party/blink/renderer/bindings/core/v8/v8_dom_exception.h"
26 #include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
27 #include "third_party/blink/renderer/bindings/core/v8/v8_iterator_result_value.h"
28 #include "third_party/blink/renderer/bindings/core/v8/v8_uint8_array.h"
29 #include "third_party/blink/renderer/bindings/modules/v8/v8_bidirectional_stream.h"
30 #include "third_party/blink/renderer/bindings/modules/v8/v8_quic_transport_options.h"
31 #include "third_party/blink/renderer/bindings/modules/v8/v8_receive_stream.h"
32 #include "third_party/blink/renderer/bindings/modules/v8/v8_rtc_dtls_fingerprint.h"
33 #include "third_party/blink/renderer/bindings/modules/v8/v8_send_stream.h"
34 #include "third_party/blink/renderer/bindings/modules/v8/v8_web_transport_close_info.h"
35 #include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
36 #include "third_party/blink/renderer/core/streams/readable_stream.h"
37 #include "third_party/blink/renderer/core/streams/readable_stream_default_reader.h"
38 #include "third_party/blink/renderer/core/streams/writable_stream.h"
39 #include "third_party/blink/renderer/core/streams/writable_stream_default_writer.h"
40 #include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
41 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
42 #include "third_party/blink/renderer/modules/webtransport/receive_stream.h"
43 #include "third_party/blink/renderer/modules/webtransport/send_stream.h"
44 #include "third_party/blink/renderer/modules/webtransport/test_utils.h"
45 #include "third_party/blink/renderer/platform/bindings/exception_code.h"
46 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
47 #include "third_party/blink/renderer/platform/heap/persistent.h"
48 #include "third_party/blink/renderer/platform/testing/unit_test_helpers.h"
49 #include "third_party/blink/renderer/platform/wtf/deque.h"
50 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
51 #include "v8/include/v8.h"
52 
53 namespace blink {
54 
55 namespace {
56 
57 using ::testing::_;
58 using ::testing::ElementsAre;
59 using ::testing::Invoke;
60 using ::testing::Mock;
61 using ::testing::StrictMock;
62 using ::testing::Truly;
63 using ::testing::Unused;
64 
65 class QuicTransportConnector final
66     : public mojom::blink::QuicTransportConnector {
67  public:
68   struct ConnectArgs {
ConnectArgsblink::__anonba0457960111::QuicTransportConnector::ConnectArgs69     ConnectArgs(
70         const KURL& url,
71         Vector<network::mojom::blink::QuicTransportCertificateFingerprintPtr>
72             fingerprints,
73         mojo::PendingRemote<network::mojom::blink::QuicTransportHandshakeClient>
74             handshake_client)
75         : url(url),
76           fingerprints(std::move(fingerprints)),
77           handshake_client(std::move(handshake_client)) {}
78 
79     KURL url;
80     Vector<network::mojom::blink::QuicTransportCertificateFingerprintPtr>
81         fingerprints;
82     mojo::PendingRemote<network::mojom::blink::QuicTransportHandshakeClient>
83         handshake_client;
84   };
85 
Connect(const KURL & url,Vector<network::mojom::blink::QuicTransportCertificateFingerprintPtr> fingerprints,mojo::PendingRemote<network::mojom::blink::QuicTransportHandshakeClient> handshake_client)86   void Connect(
87       const KURL& url,
88       Vector<network::mojom::blink::QuicTransportCertificateFingerprintPtr>
89           fingerprints,
90       mojo::PendingRemote<network::mojom::blink::QuicTransportHandshakeClient>
91           handshake_client) override {
92     connect_args_.push_back(
93         ConnectArgs(url, std::move(fingerprints), std::move(handshake_client)));
94   }
95 
TakeConnectArgs()96   Vector<ConnectArgs> TakeConnectArgs() { return std::move(connect_args_); }
97 
Bind(mojo::PendingReceiver<mojom::blink::QuicTransportConnector> receiver)98   void Bind(
99       mojo::PendingReceiver<mojom::blink::QuicTransportConnector> receiver) {
100     receiver_set_.Add(this, std::move(receiver));
101   }
102 
103  private:
104   mojo::ReceiverSet<mojom::blink::QuicTransportConnector> receiver_set_;
105   Vector<ConnectArgs> connect_args_;
106 };
107 
108 class MockQuicTransport : public network::mojom::blink::QuicTransport {
109  public:
MockQuicTransport(mojo::PendingReceiver<network::mojom::blink::QuicTransport> pending_receiver)110   MockQuicTransport(mojo::PendingReceiver<network::mojom::blink::QuicTransport>
111                         pending_receiver)
112       : receiver_(this, std::move(pending_receiver)) {}
113 
114   MOCK_METHOD2(SendDatagram,
115                void(base::span<const uint8_t> data,
116                     base::OnceCallback<void(bool)> callback));
117 
118   MOCK_METHOD3(CreateStream,
119                void(mojo::ScopedDataPipeConsumerHandle readable,
120                     mojo::ScopedDataPipeProducerHandle writable,
121                     base::OnceCallback<void(bool, uint32_t)> callback));
122 
123   MOCK_METHOD1(
124       AcceptBidirectionalStream,
125       void(base::OnceCallback<void(uint32_t,
126                                    mojo::ScopedDataPipeConsumerHandle,
127                                    mojo::ScopedDataPipeProducerHandle)>));
128 
129   MOCK_METHOD1(AcceptUnidirectionalStream,
130                void(base::OnceCallback<
131                     void(uint32_t, mojo::ScopedDataPipeConsumerHandle)>));
132 
SendFin(uint32_t stream_id)133   void SendFin(uint32_t stream_id) override {}
AbortStream(uint32_t stream_id,uint64_t code)134   void AbortStream(uint32_t stream_id, uint64_t code) override {}
135 
136  private:
137   mojo::Receiver<network::mojom::blink::QuicTransport> receiver_;
138 };
139 
140 class QuicTransportTest : public ::testing::Test {
141  public:
142   using AcceptUnidirectionalStreamCallback =
143       base::OnceCallback<void(uint32_t, mojo::ScopedDataPipeConsumerHandle)>;
144   using AcceptBidirectionalStreamCallback =
145       base::OnceCallback<void(uint32_t,
146                               mojo::ScopedDataPipeConsumerHandle,
147                               mojo::ScopedDataPipeProducerHandle)>;
148 
AddBinder(const V8TestingScope & scope)149   void AddBinder(const V8TestingScope& scope) {
150     interface_broker_ =
151         &scope.GetExecutionContext()->GetBrowserInterfaceBroker();
152     interface_broker_->SetBinderForTesting(
153         mojom::blink::QuicTransportConnector::Name_,
154         base::BindRepeating(&QuicTransportTest::BindConnector,
155                             weak_ptr_factory_.GetWeakPtr()));
156   }
157 
EmptyOptions()158   static QuicTransportOptions* EmptyOptions() {
159     return MakeGarbageCollected<QuicTransportOptions>();
160   }
161 
162   // Creates a QuicTransport object with the given |url|.
Create(const V8TestingScope & scope,const String & url)163   QuicTransport* Create(const V8TestingScope& scope, const String& url) {
164     AddBinder(scope);
165     return QuicTransport::Create(scope.GetScriptState(), url, EmptyOptions(),
166                                  ASSERT_NO_EXCEPTION);
167   }
168 
169   // Connects a QuicTransport object. Runs the event loop.
ConnectSuccessfully(QuicTransport * quic_transport)170   void ConnectSuccessfully(QuicTransport* quic_transport) {
171     DCHECK(!mock_quic_transport_) << "Only one connection supported, sorry";
172 
173     test::RunPendingTasks();
174 
175     auto args = connector_.TakeConnectArgs();
176     if (args.size() != 1u) {
177       ADD_FAILURE() << "args.size() should be 1, but is " << args.size();
178       return;
179     }
180 
181     mojo::Remote<network::mojom::blink::QuicTransportHandshakeClient>
182         handshake_client(std::move(args[0].handshake_client));
183 
184     mojo::PendingRemote<network::mojom::blink::QuicTransport>
185         quic_transport_to_pass;
186     mojo::PendingRemote<network::mojom::blink::QuicTransportClient>
187         client_remote;
188 
189     mock_quic_transport_ = std::make_unique<StrictMock<MockQuicTransport>>(
190         quic_transport_to_pass.InitWithNewPipeAndPassReceiver());
191 
192     // These are called on every connection, so expect them in every test.
193     EXPECT_CALL(*mock_quic_transport_, AcceptUnidirectionalStream(_))
194         .WillRepeatedly([this](AcceptUnidirectionalStreamCallback callback) {
195           pending_unidirectional_accept_callbacks_.push_back(
196               std::move(callback));
197         });
198 
199     EXPECT_CALL(*mock_quic_transport_, AcceptBidirectionalStream(_))
200         .WillRepeatedly([this](AcceptBidirectionalStreamCallback callback) {
201           pending_bidirectional_accept_callbacks_.push_back(
202               std::move(callback));
203         });
204 
205     handshake_client->OnConnectionEstablished(
206         std::move(quic_transport_to_pass),
207         client_remote.InitWithNewPipeAndPassReceiver());
208     client_remote_.Bind(std::move(client_remote));
209 
210     test::RunPendingTasks();
211   }
212 
213   // Creates, connects and returns a QuicTransport object with the given |url|.
214   // Runs the event loop.
CreateAndConnectSuccessfully(const V8TestingScope & scope,const String & url)215   QuicTransport* CreateAndConnectSuccessfully(const V8TestingScope& scope,
216                                               const String& url) {
217     auto* quic_transport = Create(scope, url);
218     ConnectSuccessfully(quic_transport);
219     return quic_transport;
220   }
221 
CreateSendStreamSuccessfully(const V8TestingScope & scope,QuicTransport * quic_transport)222   SendStream* CreateSendStreamSuccessfully(const V8TestingScope& scope,
223                                            QuicTransport* quic_transport) {
224     EXPECT_CALL(*mock_quic_transport_, CreateStream(_, _, _))
225         .WillOnce([this](mojo::ScopedDataPipeConsumerHandle handle, Unused,
226                          base::OnceCallback<void(bool, uint32_t)> callback) {
227           send_stream_consumer_handle_ = std::move(handle);
228           std::move(callback).Run(true, next_stream_id_++);
229         });
230 
231     auto* script_state = scope.GetScriptState();
232     ScriptPromise send_stream_promise =
233         quic_transport->createSendStream(script_state, ASSERT_NO_EXCEPTION);
234     ScriptPromiseTester tester(script_state, send_stream_promise);
235 
236     tester.WaitUntilSettled();
237 
238     EXPECT_TRUE(tester.IsFulfilled());
239     auto* send_stream = V8SendStream::ToImplWithTypeCheck(
240         scope.GetIsolate(), tester.Value().V8Value());
241     EXPECT_TRUE(send_stream);
242     return send_stream;
243   }
244 
DoAcceptUnidirectionalStream()245   mojo::ScopedDataPipeProducerHandle DoAcceptUnidirectionalStream() {
246     mojo::ScopedDataPipeProducerHandle producer;
247     mojo::ScopedDataPipeConsumerHandle consumer;
248 
249     // There's no good way to handle failure to create the pipe, so just
250     // continue.
251     CreateDataPipeForWebTransportTests(&producer, &consumer);
252 
253     std::move(pending_unidirectional_accept_callbacks_.front())
254         .Run(next_stream_id_++, std::move(consumer));
255     pending_unidirectional_accept_callbacks_.pop_front();
256 
257     return producer;
258   }
259 
ReadReceiveStream(const V8TestingScope & scope,QuicTransport * quic_transport)260   ReceiveStream* ReadReceiveStream(const V8TestingScope& scope,
261                                    QuicTransport* quic_transport) {
262     ReadableStream* streams = quic_transport->receiveStreams();
263 
264     v8::Local<v8::Value> v8value = ReadValueFromStream(scope, streams);
265 
266     ReceiveStream* receive_stream =
267         V8ReceiveStream::ToImplWithTypeCheck(scope.GetIsolate(), v8value);
268     EXPECT_TRUE(receive_stream);
269 
270     return receive_stream;
271   }
272 
BindConnector(mojo::ScopedMessagePipeHandle handle)273   void BindConnector(mojo::ScopedMessagePipeHandle handle) {
274     connector_.Bind(mojo::PendingReceiver<mojom::blink::QuicTransportConnector>(
275         std::move(handle)));
276   }
277 
TearDown()278   void TearDown() override {
279     if (!interface_broker_)
280       return;
281     interface_broker_->SetBinderForTesting(
282         mojom::blink::QuicTransportConnector::Name_, {});
283   }
284 
285   BrowserInterfaceBrokerProxy* interface_broker_ = nullptr;
286   WTF::Deque<AcceptUnidirectionalStreamCallback>
287       pending_unidirectional_accept_callbacks_;
288   WTF::Deque<AcceptBidirectionalStreamCallback>
289       pending_bidirectional_accept_callbacks_;
290   QuicTransportConnector connector_;
291   std::unique_ptr<MockQuicTransport> mock_quic_transport_;
292   mojo::Remote<network::mojom::blink::QuicTransportClient> client_remote_;
293   uint32_t next_stream_id_ = 0;
294   mojo::ScopedDataPipeConsumerHandle send_stream_consumer_handle_;
295 
296   base::WeakPtrFactory<QuicTransportTest> weak_ptr_factory_{this};
297 };
298 
TEST_F(QuicTransportTest,FailWithNullURL)299 TEST_F(QuicTransportTest, FailWithNullURL) {
300   V8TestingScope scope;
301   auto& exception_state = scope.GetExceptionState();
302   QuicTransport::Create(scope.GetScriptState(), String(), EmptyOptions(),
303                         exception_state);
304   EXPECT_TRUE(exception_state.HadException());
305   EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
306             exception_state.Code());
307 }
308 
TEST_F(QuicTransportTest,FailWithEmptyURL)309 TEST_F(QuicTransportTest, FailWithEmptyURL) {
310   V8TestingScope scope;
311   auto& exception_state = scope.GetExceptionState();
312   QuicTransport::Create(scope.GetScriptState(), String(""), EmptyOptions(),
313                         exception_state);
314   EXPECT_TRUE(exception_state.HadException());
315   EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
316             exception_state.Code());
317   EXPECT_EQ("The URL '' is invalid.", exception_state.Message());
318 }
319 
TEST_F(QuicTransportTest,FailWithNoScheme)320 TEST_F(QuicTransportTest, FailWithNoScheme) {
321   V8TestingScope scope;
322   auto& exception_state = scope.GetExceptionState();
323   QuicTransport::Create(scope.GetScriptState(), String("no-scheme"),
324                         EmptyOptions(), exception_state);
325   EXPECT_TRUE(exception_state.HadException());
326   EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
327             exception_state.Code());
328   EXPECT_EQ("The URL 'no-scheme' is invalid.", exception_state.Message());
329 }
330 
TEST_F(QuicTransportTest,FailWithHttpsURL)331 TEST_F(QuicTransportTest, FailWithHttpsURL) {
332   V8TestingScope scope;
333   auto& exception_state = scope.GetExceptionState();
334   QuicTransport::Create(scope.GetScriptState(), String("https://example.com/"),
335                         EmptyOptions(), exception_state);
336   EXPECT_TRUE(exception_state.HadException());
337   EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
338             exception_state.Code());
339   EXPECT_EQ(
340       "The URL's scheme must be 'quic-transport'. 'https' is not allowed.",
341       exception_state.Message());
342 }
343 
TEST_F(QuicTransportTest,FailWithNoHost)344 TEST_F(QuicTransportTest, FailWithNoHost) {
345   V8TestingScope scope;
346   auto& exception_state = scope.GetExceptionState();
347   QuicTransport::Create(scope.GetScriptState(), String("quic-transport:///"),
348                         EmptyOptions(), exception_state);
349   EXPECT_TRUE(exception_state.HadException());
350   EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
351             exception_state.Code());
352   EXPECT_EQ("The URL 'quic-transport:///' is invalid.",
353             exception_state.Message());
354 }
355 
TEST_F(QuicTransportTest,FailWithURLFragment)356 TEST_F(QuicTransportTest, FailWithURLFragment) {
357   V8TestingScope scope;
358   auto& exception_state = scope.GetExceptionState();
359   QuicTransport::Create(scope.GetScriptState(),
360                         String("quic-transport://example.com/#failing"),
361                         EmptyOptions(), exception_state);
362   EXPECT_TRUE(exception_state.HadException());
363   EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSyntaxError),
364             exception_state.Code());
365   EXPECT_EQ(
366       "The URL contains a fragment identifier ('#failing'). Fragment "
367       "identifiers are not allowed in QuicTransport URLs.",
368       exception_state.Message());
369 }
370 
TEST_F(QuicTransportTest,FailByCSP)371 TEST_F(QuicTransportTest, FailByCSP) {
372   V8TestingScope scope;
373   auto& exception_state = scope.GetExceptionState();
374   scope.GetExecutionContext()
375       ->GetContentSecurityPolicyForCurrentWorld()
376       ->DidReceiveHeader("connect-src 'none'",
377                          network::mojom::ContentSecurityPolicyType::kEnforce,
378                          network::mojom::ContentSecurityPolicySource::kHTTP);
379   QuicTransport::Create(scope.GetScriptState(),
380                         String("quic-transport://example.com/"), EmptyOptions(),
381                         exception_state);
382   EXPECT_TRUE(exception_state.HadException());
383   EXPECT_EQ(static_cast<int>(DOMExceptionCode::kSecurityError),
384             exception_state.Code());
385   EXPECT_EQ("Failed to connect to 'quic-transport://example.com/'",
386             exception_state.Message());
387 }
388 
TEST_F(QuicTransportTest,PassCSP)389 TEST_F(QuicTransportTest, PassCSP) {
390   V8TestingScope scope;
391   // This doesn't work without the https:// prefix, even thought it should
392   // according to
393   // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src.
394   auto& exception_state = scope.GetExceptionState();
395   scope.GetExecutionContext()
396       ->GetContentSecurityPolicyForCurrentWorld()
397       ->DidReceiveHeader("connect-src quic-transport://example.com",
398                          network::mojom::ContentSecurityPolicyType::kEnforce,
399                          network::mojom::ContentSecurityPolicySource::kHTTP);
400   QuicTransport::Create(scope.GetScriptState(),
401                         String("quic-transport://example.com/"), EmptyOptions(),
402                         exception_state);
403   EXPECT_FALSE(exception_state.HadException());
404 }
405 
TEST_F(QuicTransportTest,SendConnect)406 TEST_F(QuicTransportTest, SendConnect) {
407   V8TestingScope scope;
408   AddBinder(scope);
409   auto* quic_transport = QuicTransport::Create(
410       scope.GetScriptState(), String("quic-transport://example.com/"),
411       EmptyOptions(), ASSERT_NO_EXCEPTION);
412 
413   test::RunPendingTasks();
414 
415   auto args = connector_.TakeConnectArgs();
416   ASSERT_EQ(1u, args.size());
417   EXPECT_EQ(KURL("quic-transport://example.com/"), args[0].url);
418   EXPECT_TRUE(args[0].fingerprints.IsEmpty());
419   EXPECT_TRUE(quic_transport->HasPendingActivity());
420 }
421 
TEST_F(QuicTransportTest,SuccessfulConnect)422 TEST_F(QuicTransportTest, SuccessfulConnect) {
423   V8TestingScope scope;
424   auto* quic_transport =
425       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
426   ScriptPromiseTester ready_tester(scope.GetScriptState(),
427                                    quic_transport->ready());
428 
429   EXPECT_TRUE(quic_transport->HasPendingActivity());
430 
431   ready_tester.WaitUntilSettled();
432   EXPECT_TRUE(ready_tester.IsFulfilled());
433 }
434 
TEST_F(QuicTransportTest,FailedConnect)435 TEST_F(QuicTransportTest, FailedConnect) {
436   V8TestingScope scope;
437   AddBinder(scope);
438   auto* quic_transport = QuicTransport::Create(
439       scope.GetScriptState(), String("quic-transport://example.com/"),
440       EmptyOptions(), ASSERT_NO_EXCEPTION);
441   ScriptPromiseTester ready_tester(scope.GetScriptState(),
442                                    quic_transport->ready());
443   ScriptPromiseTester closed_tester(scope.GetScriptState(),
444                                     quic_transport->closed());
445 
446   test::RunPendingTasks();
447 
448   auto args = connector_.TakeConnectArgs();
449   ASSERT_EQ(1u, args.size());
450 
451   mojo::Remote<network::mojom::blink::QuicTransportHandshakeClient>
452       handshake_client(std::move(args[0].handshake_client));
453 
454   handshake_client->OnHandshakeFailed(nullptr);
455 
456   test::RunPendingTasks();
457   EXPECT_FALSE(quic_transport->HasPendingActivity());
458   EXPECT_TRUE(ready_tester.IsRejected());
459   EXPECT_TRUE(closed_tester.IsRejected());
460 }
461 
TEST_F(QuicTransportTest,SendConnectWithFingerprint)462 TEST_F(QuicTransportTest, SendConnectWithFingerprint) {
463   V8TestingScope scope;
464   AddBinder(scope);
465   auto* fingerprints = MakeGarbageCollected<RTCDtlsFingerprint>();
466   fingerprints->setAlgorithm("sha-256");
467   fingerprints->setValue(
468       "ED:3D:D7:C3:67:10:94:68:D1:DC:D1:26:5C:B2:74:D7:1C:A2:63:3E:94:94:C0:84:"
469       "39:D6:64:FA:08:B9:77:37");
470   auto* options = MakeGarbageCollected<QuicTransportOptions>();
471   options->setServerCertificateFingerprints({fingerprints});
472   QuicTransport::Create(scope.GetScriptState(),
473                         String("quic-transport://example.com/"), options,
474                         ASSERT_NO_EXCEPTION);
475 
476   test::RunPendingTasks();
477 
478   auto args = connector_.TakeConnectArgs();
479   ASSERT_EQ(1u, args.size());
480   ASSERT_EQ(1u, args[0].fingerprints.size());
481   EXPECT_EQ(args[0].fingerprints[0]->algorithm, "sha-256");
482   EXPECT_EQ(args[0].fingerprints[0]->fingerprint,
483             "ED:3D:D7:C3:67:10:94:68:D1:DC:D1:26:5C:B2:74:D7:1C:A2:63:3E:94:94:"
484             "C0:84:39:D6:64:FA:08:B9:77:37");
485 }
486 
TEST_F(QuicTransportTest,CloseDuringConnect)487 TEST_F(QuicTransportTest, CloseDuringConnect) {
488   V8TestingScope scope;
489   AddBinder(scope);
490   auto* quic_transport = QuicTransport::Create(
491       scope.GetScriptState(), String("quic-transport://example.com/"),
492       EmptyOptions(), ASSERT_NO_EXCEPTION);
493   ScriptPromiseTester ready_tester(scope.GetScriptState(),
494                                    quic_transport->ready());
495   ScriptPromiseTester closed_tester(scope.GetScriptState(),
496                                     quic_transport->closed());
497 
498   test::RunPendingTasks();
499 
500   auto args = connector_.TakeConnectArgs();
501   ASSERT_EQ(1u, args.size());
502 
503   quic_transport->close(nullptr);
504 
505   test::RunPendingTasks();
506 
507   EXPECT_FALSE(quic_transport->HasPendingActivity());
508   EXPECT_TRUE(ready_tester.IsRejected());
509   EXPECT_TRUE(closed_tester.IsFulfilled());
510 }
511 
TEST_F(QuicTransportTest,CloseAfterConnection)512 TEST_F(QuicTransportTest, CloseAfterConnection) {
513   V8TestingScope scope;
514   auto* quic_transport =
515       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
516   ScriptPromiseTester ready_tester(scope.GetScriptState(),
517                                    quic_transport->ready());
518   ScriptPromiseTester closed_tester(scope.GetScriptState(),
519                                     quic_transport->closed());
520 
521   WebTransportCloseInfo close_info;
522   close_info.setErrorCode(42);
523   close_info.setReason("because");
524   quic_transport->close(&close_info);
525 
526   test::RunPendingTasks();
527 
528   // TODO(ricea): Check that the close info is sent through correctly, once we
529   // start sending it.
530 
531   EXPECT_FALSE(quic_transport->HasPendingActivity());
532   EXPECT_TRUE(ready_tester.IsFulfilled());
533   EXPECT_TRUE(closed_tester.IsFulfilled());
534 
535   // Calling close again does nothing.
536   quic_transport->close(nullptr);
537 }
538 
539 // A live connection will be kept alive even if there is no explicit reference.
540 // When the underlying connection is shut down, the connection will be swept.
TEST_F(QuicTransportTest,GarbageCollection)541 TEST_F(QuicTransportTest, GarbageCollection) {
542   V8TestingScope scope;
543 
544   WeakPersistent<QuicTransport> quic_transport;
545 
546   {
547     // The streams created when creating a QuicTransport create some v8 handles.
548     // To ensure these are collected, we need to create a handle scope. This is
549     // not a problem for garbage collection in normal operation.
550     v8::HandleScope handle_scope(scope.GetIsolate());
551     quic_transport =
552         CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
553   }
554 
555   // Pretend the stack is empty. This will avoid accidentally treating any
556   // copies of the |quic_transport| pointer as references.
557   ThreadState::Current()->CollectAllGarbageForTesting();
558 
559   EXPECT_TRUE(quic_transport);
560 
561   quic_transport->close(nullptr);
562 
563   test::RunPendingTasks();
564 
565   ThreadState::Current()->CollectAllGarbageForTesting();
566 
567   EXPECT_FALSE(quic_transport);
568 }
569 
TEST_F(QuicTransportTest,GarbageCollectMojoConnectionError)570 TEST_F(QuicTransportTest, GarbageCollectMojoConnectionError) {
571   V8TestingScope scope;
572 
573   WeakPersistent<QuicTransport> quic_transport;
574 
575   {
576     v8::HandleScope handle_scope(scope.GetIsolate());
577     quic_transport =
578         CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
579   }
580 
581   ScriptPromiseTester closed_tester(scope.GetScriptState(),
582                                     quic_transport->closed());
583 
584   // Closing the server-side of the pipe causes a mojo connection error.
585   client_remote_.reset();
586 
587   test::RunPendingTasks();
588 
589   ThreadState::Current()->CollectAllGarbageForTesting();
590 
591   EXPECT_FALSE(quic_transport);
592   EXPECT_TRUE(closed_tester.IsRejected());
593 }
594 
TEST_F(QuicTransportTest,SendDatagram)595 TEST_F(QuicTransportTest, SendDatagram) {
596   V8TestingScope scope;
597   auto* quic_transport =
598       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
599 
600   EXPECT_CALL(*mock_quic_transport_, SendDatagram(ElementsAre('A'), _))
601       .WillOnce(Invoke([](base::span<const uint8_t>,
602                           MockQuicTransport::SendDatagramCallback callback) {
603         std::move(callback).Run(true);
604       }));
605 
606   auto* writable = quic_transport->sendDatagrams();
607   auto* script_state = scope.GetScriptState();
608   auto* writer = writable->getWriter(script_state, ASSERT_NO_EXCEPTION);
609   auto* chunk = DOMUint8Array::Create(1);
610   *chunk->Data() = 'A';
611   ScriptPromise result =
612       writer->write(script_state, ScriptValue::From(script_state, chunk),
613                     ASSERT_NO_EXCEPTION);
614   ScriptPromiseTester tester(script_state, result);
615   tester.WaitUntilSettled();
616   EXPECT_TRUE(tester.IsFulfilled());
617   EXPECT_TRUE(tester.Value().IsUndefined());
618 }
619 
TEST_F(QuicTransportTest,SendDatagramBeforeConnect)620 TEST_F(QuicTransportTest, SendDatagramBeforeConnect) {
621   V8TestingScope scope;
622   auto* quic_transport = Create(scope, "quic-transport://example.com");
623 
624   auto* writable = quic_transport->sendDatagrams();
625   auto* script_state = scope.GetScriptState();
626   auto* writer = writable->getWriter(script_state, ASSERT_NO_EXCEPTION);
627   auto* chunk = DOMUint8Array::Create(1);
628   *chunk->Data() = 'A';
629   ScriptPromise result =
630       writer->write(script_state, ScriptValue::From(script_state, chunk),
631                     ASSERT_NO_EXCEPTION);
632 
633   ConnectSuccessfully(quic_transport);
634 
635   // No datagram is sent.
636 
637   ScriptPromiseTester tester(script_state, result);
638   tester.WaitUntilSettled();
639   EXPECT_TRUE(tester.IsFulfilled());
640   EXPECT_TRUE(tester.Value().IsUndefined());
641 }
642 
TEST_F(QuicTransportTest,SendDatagramAfterClose)643 TEST_F(QuicTransportTest, SendDatagramAfterClose) {
644   V8TestingScope scope;
645   auto* quic_transport =
646       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
647 
648   quic_transport->close(nullptr);
649   test::RunPendingTasks();
650 
651   auto* writable = quic_transport->sendDatagrams();
652   auto* script_state = scope.GetScriptState();
653   auto* writer = writable->getWriter(script_state, ASSERT_NO_EXCEPTION);
654 
655   auto* chunk = DOMUint8Array::Create(1);
656   *chunk->Data() = 'A';
657   ScriptPromise result =
658       writer->write(script_state, ScriptValue::From(script_state, chunk),
659                     ASSERT_NO_EXCEPTION);
660 
661   // No datagram is sent.
662 
663   ScriptPromiseTester tester(script_state, result);
664   tester.WaitUntilSettled();
665   EXPECT_TRUE(tester.IsRejected());
666 }
667 
GetValueAsVector(ScriptState * script_state,ScriptValue iterator_result)668 Vector<uint8_t> GetValueAsVector(ScriptState* script_state,
669                                  ScriptValue iterator_result) {
670   bool done = false;
671   v8::Local<v8::Value> value;
672   if (!V8UnpackIteratorResult(script_state,
673                               iterator_result.V8Value().As<v8::Object>(), &done)
674            .ToLocal(&value)) {
675     ADD_FAILURE() << "unable to unpack iterator_result";
676     return {};
677   }
678 
679   EXPECT_FALSE(done);
680   auto* array =
681       V8Uint8Array::ToImplWithTypeCheck(script_state->GetIsolate(), value);
682   if (!array) {
683     ADD_FAILURE() << "value was not a Uint8Array";
684     return {};
685   }
686 
687   Vector<uint8_t> result;
688   result.Append(array->Data(), array->length());
689   return result;
690 }
691 
TEST_F(QuicTransportTest,ReceiveDatagramBeforeRead)692 TEST_F(QuicTransportTest, ReceiveDatagramBeforeRead) {
693   V8TestingScope scope;
694   auto* quic_transport =
695       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
696 
697   const std::array<uint8_t, 1> chunk = {'A'};
698   client_remote_->OnDatagramReceived(chunk);
699 
700   test::RunPendingTasks();
701 
702   auto* readable = quic_transport->receiveDatagrams();
703   auto* script_state = scope.GetScriptState();
704   auto* reader =
705       readable->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
706   ScriptPromise result = reader->read(script_state, ASSERT_NO_EXCEPTION);
707   ScriptPromiseTester tester(script_state, result);
708   tester.WaitUntilSettled();
709   EXPECT_TRUE(tester.IsFulfilled());
710 
711   EXPECT_THAT(GetValueAsVector(script_state, tester.Value()), ElementsAre('A'));
712 }
713 
TEST_F(QuicTransportTest,ReceiveDatagramDuringRead)714 TEST_F(QuicTransportTest, ReceiveDatagramDuringRead) {
715   V8TestingScope scope;
716   auto* quic_transport =
717       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
718   auto* readable = quic_transport->receiveDatagrams();
719   auto* script_state = scope.GetScriptState();
720   auto* reader =
721       readable->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
722   ScriptPromise result = reader->read(script_state, ASSERT_NO_EXCEPTION);
723 
724   const std::array<uint8_t, 1> chunk = {'A'};
725   client_remote_->OnDatagramReceived(chunk);
726 
727   ScriptPromiseTester tester(script_state, result);
728   tester.WaitUntilSettled();
729   EXPECT_TRUE(tester.IsFulfilled());
730 
731   EXPECT_THAT(GetValueAsVector(script_state, tester.Value()), ElementsAre('A'));
732 }
733 
734 // This test documents the current behaviour. If you improve the behaviour,
735 // change the test!
TEST_F(QuicTransportTest,DatagramsAreDropped)736 TEST_F(QuicTransportTest, DatagramsAreDropped) {
737   V8TestingScope scope;
738   auto* quic_transport =
739       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
740 
741   // Chunk 'A' gets placed in the readable queue.
742   const std::array<uint8_t, 1> chunk1 = {'A'};
743   client_remote_->OnDatagramReceived(chunk1);
744 
745   // Chunk 'B' gets dropped, because there is no space in the readable queue.
746   const std::array<uint8_t, 1> chunk2 = {'B'};
747   client_remote_->OnDatagramReceived(chunk2);
748 
749   // Make sure that the calls have run.
750   test::RunPendingTasks();
751 
752   auto* readable = quic_transport->receiveDatagrams();
753   auto* script_state = scope.GetScriptState();
754   auto* reader =
755       readable->GetDefaultReaderForTesting(script_state, ASSERT_NO_EXCEPTION);
756   ScriptPromise result1 = reader->read(script_state, ASSERT_NO_EXCEPTION);
757   ScriptPromise result2 = reader->read(script_state, ASSERT_NO_EXCEPTION);
758 
759   ScriptPromiseTester tester1(script_state, result1);
760   ScriptPromiseTester tester2(script_state, result2);
761   tester1.WaitUntilSettled();
762   EXPECT_TRUE(tester1.IsFulfilled());
763   EXPECT_FALSE(tester2.IsFulfilled());
764 
765   EXPECT_THAT(GetValueAsVector(script_state, tester1.Value()),
766               ElementsAre('A'));
767 
768   // Chunk 'C' fulfills the pending read.
769   const std::array<uint8_t, 1> chunk3 = {'C'};
770   client_remote_->OnDatagramReceived(chunk3);
771 
772   tester2.WaitUntilSettled();
773   EXPECT_TRUE(tester2.IsFulfilled());
774 
775   EXPECT_THAT(GetValueAsVector(script_state, tester2.Value()),
776               ElementsAre('C'));
777 }
778 
ValidProducerHandle(const mojo::ScopedDataPipeProducerHandle & handle)779 bool ValidProducerHandle(const mojo::ScopedDataPipeProducerHandle& handle) {
780   return handle.is_valid();
781 }
782 
ValidConsumerHandle(const mojo::ScopedDataPipeConsumerHandle & handle)783 bool ValidConsumerHandle(const mojo::ScopedDataPipeConsumerHandle& handle) {
784   return handle.is_valid();
785 }
786 
TEST_F(QuicTransportTest,CreateSendStream)787 TEST_F(QuicTransportTest, CreateSendStream) {
788   V8TestingScope scope;
789   auto* quic_transport =
790       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
791 
792   EXPECT_CALL(*mock_quic_transport_,
793               CreateStream(Truly(ValidConsumerHandle),
794                            Not(Truly(ValidProducerHandle)), _))
795       .WillOnce([](Unused, Unused,
796                    base::OnceCallback<void(bool, uint32_t)> callback) {
797         std::move(callback).Run(true, 0);
798       });
799 
800   auto* script_state = scope.GetScriptState();
801   ScriptPromise send_stream_promise =
802       quic_transport->createSendStream(script_state, ASSERT_NO_EXCEPTION);
803   ScriptPromiseTester tester(script_state, send_stream_promise);
804 
805   tester.WaitUntilSettled();
806 
807   EXPECT_TRUE(tester.IsFulfilled());
808   auto* send_stream = V8SendStream::ToImplWithTypeCheck(
809       scope.GetIsolate(), tester.Value().V8Value());
810   EXPECT_TRUE(send_stream);
811 }
812 
TEST_F(QuicTransportTest,CreateSendStreamBeforeConnect)813 TEST_F(QuicTransportTest, CreateSendStreamBeforeConnect) {
814   V8TestingScope scope;
815 
816   auto* script_state = scope.GetScriptState();
817   auto* quic_transport =
818       QuicTransport::Create(script_state, "quic-transport://example.com",
819                             EmptyOptions(), ASSERT_NO_EXCEPTION);
820   auto& exception_state = scope.GetExceptionState();
821   ScriptPromise send_stream_promise =
822       quic_transport->createSendStream(script_state, exception_state);
823   EXPECT_TRUE(send_stream_promise.IsEmpty());
824   EXPECT_TRUE(exception_state.HadException());
825   EXPECT_EQ(static_cast<int>(DOMExceptionCode::kNetworkError),
826             exception_state.Code());
827 }
828 
TEST_F(QuicTransportTest,CreateSendStreamFailure)829 TEST_F(QuicTransportTest, CreateSendStreamFailure) {
830   V8TestingScope scope;
831   auto* quic_transport =
832       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
833 
834   EXPECT_CALL(*mock_quic_transport_, CreateStream(_, _, _))
835       .WillOnce([](Unused, Unused,
836                    base::OnceCallback<void(bool, uint32_t)> callback) {
837         std::move(callback).Run(false, 0);
838       });
839 
840   auto* script_state = scope.GetScriptState();
841   ScriptPromise send_stream_promise =
842       quic_transport->createSendStream(script_state, ASSERT_NO_EXCEPTION);
843   ScriptPromiseTester tester(script_state, send_stream_promise);
844 
845   tester.WaitUntilSettled();
846 
847   EXPECT_TRUE(tester.IsRejected());
848   DOMException* exception = V8DOMException::ToImplWithTypeCheck(
849       scope.GetIsolate(), tester.Value().V8Value());
850   EXPECT_EQ(exception->name(), "NetworkError");
851   EXPECT_EQ(exception->message(), "Failed to create send stream.");
852 }
853 
854 // Every active stream is kept alive by the QuicTransport object.
TEST_F(QuicTransportTest,SendStreamGarbageCollection)855 TEST_F(QuicTransportTest, SendStreamGarbageCollection) {
856   V8TestingScope scope;
857 
858   WeakPersistent<QuicTransport> quic_transport;
859   WeakPersistent<SendStream> send_stream;
860 
861   {
862     // The streams created when creating a QuicTransport or SendStream create
863     // some v8 handles. To ensure these are collected, we need to create a
864     // handle scope. This is not a problem for garbage collection in normal
865     // operation.
866     v8::HandleScope handle_scope(scope.GetIsolate());
867 
868     quic_transport =
869         CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
870     send_stream = CreateSendStreamSuccessfully(scope, quic_transport);
871   }
872 
873   ThreadState::Current()->CollectAllGarbageForTesting();
874 
875   EXPECT_TRUE(quic_transport);
876   EXPECT_TRUE(send_stream);
877 
878   quic_transport->close(nullptr);
879 
880   test::RunPendingTasks();
881 
882   ThreadState::Current()->CollectAllGarbageForTesting();
883 
884   EXPECT_FALSE(quic_transport);
885   EXPECT_FALSE(send_stream);
886 }
887 
888 // A live stream will be kept alive even if there is no explicit reference.
889 // When the underlying connection is shut down, the connection will be swept.
TEST_F(QuicTransportTest,SendStreamGarbageCollectionLocalClose)890 TEST_F(QuicTransportTest, SendStreamGarbageCollectionLocalClose) {
891   V8TestingScope scope;
892 
893   WeakPersistent<SendStream> send_stream;
894 
895   {
896     // The writable stream created when creating a SendStream creates some
897     // v8 handles. To ensure these are collected, we need to create a handle
898     // scope. This is not a problem for garbage collection in normal operation.
899     v8::HandleScope handle_scope(scope.GetIsolate());
900 
901     auto* quic_transport =
902         CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
903     send_stream = CreateSendStreamSuccessfully(scope, quic_transport);
904   }
905 
906   // Pretend the stack is empty. This will avoid accidentally treating any
907   // copies of the |send_stream| pointer as references.
908   ThreadState::Current()->CollectAllGarbageForTesting();
909 
910   ASSERT_TRUE(send_stream);
911 
912   auto* script_state = scope.GetScriptState();
913 
914   ScriptPromise close_promise =
915       send_stream->writable()->close(script_state, ASSERT_NO_EXCEPTION);
916   ScriptPromiseTester tester(script_state, close_promise);
917   tester.WaitUntilSettled();
918   EXPECT_TRUE(tester.IsFulfilled());
919 
920   ThreadState::Current()->CollectAllGarbageForTesting();
921 
922   EXPECT_FALSE(send_stream);
923 }
924 
TEST_F(QuicTransportTest,SendStreamGarbageCollectionRemoteClose)925 TEST_F(QuicTransportTest, SendStreamGarbageCollectionRemoteClose) {
926   V8TestingScope scope;
927 
928   WeakPersistent<SendStream> send_stream;
929 
930   {
931     v8::HandleScope handle_scope(scope.GetIsolate());
932 
933     auto* quic_transport =
934         CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
935     send_stream = CreateSendStreamSuccessfully(scope, quic_transport);
936   }
937 
938   ThreadState::Current()->CollectAllGarbageForTesting();
939 
940   ASSERT_TRUE(send_stream);
941 
942   // Close the other end of the pipe.
943   send_stream_consumer_handle_.reset();
944 
945   test::RunPendingTasks();
946 
947   ThreadState::Current()->CollectAllGarbageForTesting();
948 
949   EXPECT_FALSE(send_stream);
950 }
951 
952 // A live stream will be kept alive even if there is no explicit reference.
953 // When the underlying connection is shut down, the connection will be swept.
TEST_F(QuicTransportTest,ReceiveStreamGarbageCollectionCancel)954 TEST_F(QuicTransportTest, ReceiveStreamGarbageCollectionCancel) {
955   V8TestingScope scope;
956 
957   WeakPersistent<ReceiveStream> receive_stream;
958   mojo::ScopedDataPipeProducerHandle producer;
959 
960   {
961     // The readable stream created when creating a ReceiveStream creates some
962     // v8 handles. To ensure these are collected, we need to create a handle
963     // scope. This is not a problem for garbage collection in normal operation.
964     v8::HandleScope handle_scope(scope.GetIsolate());
965 
966     auto* quic_transport =
967         CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
968 
969     producer = DoAcceptUnidirectionalStream();
970     receive_stream = ReadReceiveStream(scope, quic_transport);
971   }
972 
973   // Pretend the stack is empty. This will avoid accidentally treating any
974   // copies of the |receive_stream| pointer as references.
975   ThreadState::Current()->CollectAllGarbageForTesting();
976 
977   ASSERT_TRUE(receive_stream);
978 
979   auto* script_state = scope.GetScriptState();
980 
981   ScriptPromise cancel_promise;
982   {
983     // Cancelling also creates v8 handles, so we need a new handle scope as
984     // above.
985     v8::HandleScope handle_scope(scope.GetIsolate());
986     cancel_promise =
987         receive_stream->readable()->cancel(script_state, ASSERT_NO_EXCEPTION);
988   }
989 
990   ScriptPromiseTester tester(script_state, cancel_promise);
991   tester.WaitUntilSettled();
992   EXPECT_TRUE(tester.IsFulfilled());
993 
994   ThreadState::Current()->CollectAllGarbageForTesting();
995 
996   EXPECT_FALSE(receive_stream);
997 }
998 
TEST_F(QuicTransportTest,ReceiveStreamGarbageCollectionRemoteClose)999 TEST_F(QuicTransportTest, ReceiveStreamGarbageCollectionRemoteClose) {
1000   V8TestingScope scope;
1001 
1002   WeakPersistent<ReceiveStream> receive_stream;
1003   mojo::ScopedDataPipeProducerHandle producer;
1004 
1005   {
1006     v8::HandleScope handle_scope(scope.GetIsolate());
1007 
1008     auto* quic_transport =
1009         CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
1010     producer = DoAcceptUnidirectionalStream();
1011     receive_stream = ReadReceiveStream(scope, quic_transport);
1012   }
1013 
1014   ThreadState::Current()->CollectAllGarbageForTesting();
1015 
1016   ASSERT_TRUE(receive_stream);
1017 
1018   // Close the other end of the pipe.
1019   producer.reset();
1020 
1021   test::RunPendingTasks();
1022 
1023   ThreadState::Current()->CollectAllGarbageForTesting();
1024 
1025   ASSERT_TRUE(receive_stream);
1026 
1027   receive_stream->OnIncomingStreamClosed(false);
1028 
1029   test::RunPendingTasks();
1030 
1031   ThreadState::Current()->CollectAllGarbageForTesting();
1032 
1033   EXPECT_FALSE(receive_stream);
1034 }
1035 
1036 // This is the same test as ReceiveStreamGarbageCollectionRemoteClose, except
1037 // that the order of the data pipe being reset and the OnIncomingStreamClosed
1038 // message is reversed. It is important that the object is not collected until
1039 // both events have happened.
TEST_F(QuicTransportTest,ReceiveStreamGarbageCollectionRemoteCloseReverse)1040 TEST_F(QuicTransportTest, ReceiveStreamGarbageCollectionRemoteCloseReverse) {
1041   V8TestingScope scope;
1042 
1043   WeakPersistent<ReceiveStream> receive_stream;
1044   mojo::ScopedDataPipeProducerHandle producer;
1045 
1046   {
1047     v8::HandleScope handle_scope(scope.GetIsolate());
1048 
1049     QuicTransport* quic_transport =
1050         CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
1051 
1052     producer = DoAcceptUnidirectionalStream();
1053     receive_stream = ReadReceiveStream(scope, quic_transport);
1054   }
1055 
1056   ThreadState::Current()->CollectAllGarbageForTesting();
1057 
1058   ASSERT_TRUE(receive_stream);
1059 
1060   receive_stream->OnIncomingStreamClosed(false);
1061 
1062   test::RunPendingTasks();
1063 
1064   ThreadState::Current()->CollectAllGarbageForTesting();
1065 
1066   ASSERT_TRUE(receive_stream);
1067 
1068   producer.reset();
1069 
1070   test::RunPendingTasks();
1071 
1072   ThreadState::Current()->CollectAllGarbageForTesting();
1073 
1074   EXPECT_FALSE(receive_stream);
1075 }
1076 
TEST_F(QuicTransportTest,CreateSendStreamAbortedByClose)1077 TEST_F(QuicTransportTest, CreateSendStreamAbortedByClose) {
1078   V8TestingScope scope;
1079 
1080   auto* script_state = scope.GetScriptState();
1081   auto* quic_transport =
1082       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
1083 
1084   base::OnceCallback<void(bool, uint32_t)> create_stream_callback;
1085   EXPECT_CALL(*mock_quic_transport_, CreateStream(_, _, _))
1086       .WillOnce([&](Unused, Unused,
1087                     base::OnceCallback<void(bool, uint32_t)> callback) {
1088         create_stream_callback = std::move(callback);
1089       });
1090 
1091   ScriptPromise send_stream_promise =
1092       quic_transport->createSendStream(script_state, ASSERT_NO_EXCEPTION);
1093   ScriptPromiseTester tester(script_state, send_stream_promise);
1094 
1095   test::RunPendingTasks();
1096 
1097   quic_transport->close(nullptr);
1098   std::move(create_stream_callback).Run(true, 0);
1099 
1100   tester.WaitUntilSettled();
1101 
1102   EXPECT_TRUE(tester.IsRejected());
1103 }
1104 
1105 // ReceiveStream functionality is thoroughly tested in incoming_stream_test.cc.
1106 // This test just verifies that the creation is done correctly.
TEST_F(QuicTransportTest,CreateReceiveStream)1107 TEST_F(QuicTransportTest, CreateReceiveStream) {
1108   V8TestingScope scope;
1109 
1110   auto* script_state = scope.GetScriptState();
1111   auto* quic_transport =
1112       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
1113 
1114   mojo::ScopedDataPipeProducerHandle producer = DoAcceptUnidirectionalStream();
1115 
1116   ReceiveStream* receive_stream = ReadReceiveStream(scope, quic_transport);
1117 
1118   const char data[] = "what";
1119   uint32_t num_bytes = 4u;
1120 
1121   EXPECT_EQ(
1122       producer->WriteData(data, &num_bytes, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE),
1123       MOJO_RESULT_OK);
1124   EXPECT_EQ(num_bytes, 4u);
1125 
1126   producer.reset();
1127   quic_transport->OnIncomingStreamClosed(/*stream_id=*/0, true);
1128 
1129   auto* reader = receive_stream->readable()->GetDefaultReaderForTesting(
1130       script_state, ASSERT_NO_EXCEPTION);
1131   ScriptPromise read_promise = reader->read(script_state, ASSERT_NO_EXCEPTION);
1132   ScriptPromiseTester read_tester(script_state, read_promise);
1133   read_tester.WaitUntilSettled();
1134   EXPECT_TRUE(read_tester.IsFulfilled());
1135   auto read_result = read_tester.Value().V8Value();
1136   ASSERT_TRUE(read_result->IsObject());
1137   v8::Local<v8::Value> value;
1138   bool done = false;
1139   ASSERT_TRUE(
1140       V8UnpackIteratorResult(script_state, read_result.As<v8::Object>(), &done)
1141           .ToLocal(&value));
1142   DOMUint8Array* u8array =
1143       V8Uint8Array::ToImplWithTypeCheck(scope.GetIsolate(), value);
1144   ASSERT_TRUE(u8array);
1145   EXPECT_THAT(base::make_span(static_cast<uint8_t*>(u8array->Data()),
1146                               u8array->byteLength()),
1147               ElementsAre('w', 'h', 'a', 't'));
1148 }
1149 
TEST_F(QuicTransportTest,CreateReceiveStreamThenClose)1150 TEST_F(QuicTransportTest, CreateReceiveStreamThenClose) {
1151   V8TestingScope scope;
1152 
1153   auto* script_state = scope.GetScriptState();
1154   auto* quic_transport =
1155       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
1156 
1157   mojo::ScopedDataPipeProducerHandle producer = DoAcceptUnidirectionalStream();
1158 
1159   ReceiveStream* receive_stream = ReadReceiveStream(scope, quic_transport);
1160 
1161   auto* reader = receive_stream->readable()->GetDefaultReaderForTesting(
1162       script_state, ASSERT_NO_EXCEPTION);
1163   ScriptPromise read_promise = reader->read(script_state, ASSERT_NO_EXCEPTION);
1164   ScriptPromiseTester read_tester(script_state, read_promise);
1165 
1166   quic_transport->close(nullptr);
1167 
1168   read_tester.WaitUntilSettled();
1169   EXPECT_TRUE(read_tester.IsRejected());
1170   DOMException* exception = V8DOMException::ToImplWithTypeCheck(
1171       scope.GetIsolate(), read_tester.Value().V8Value());
1172   ASSERT_TRUE(exception);
1173   EXPECT_EQ(exception->code(),
1174             static_cast<uint16_t>(DOMExceptionCode::kNetworkError));
1175 
1176   // TODO(ricea): Fix this message if possible.
1177   EXPECT_EQ(exception->message(),
1178             "The stream was aborted by the remote server");
1179 }
1180 
TEST_F(QuicTransportTest,CreateReceiveStreamThenRemoteClose)1181 TEST_F(QuicTransportTest, CreateReceiveStreamThenRemoteClose) {
1182   V8TestingScope scope;
1183 
1184   auto* script_state = scope.GetScriptState();
1185   auto* quic_transport =
1186       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
1187 
1188   mojo::ScopedDataPipeProducerHandle producer = DoAcceptUnidirectionalStream();
1189 
1190   ReceiveStream* receive_stream = ReadReceiveStream(scope, quic_transport);
1191 
1192   auto* reader = receive_stream->readable()->GetDefaultReaderForTesting(
1193       script_state, ASSERT_NO_EXCEPTION);
1194   ScriptPromise read_promise = reader->read(script_state, ASSERT_NO_EXCEPTION);
1195   ScriptPromiseTester read_tester(script_state, read_promise);
1196 
1197   client_remote_.reset();
1198 
1199   read_tester.WaitUntilSettled();
1200   EXPECT_TRUE(read_tester.IsRejected());
1201   DOMException* exception = V8DOMException::ToImplWithTypeCheck(
1202       scope.GetIsolate(), read_tester.Value().V8Value());
1203   ASSERT_TRUE(exception);
1204   EXPECT_EQ(exception->code(),
1205             static_cast<uint16_t>(DOMExceptionCode::kNetworkError));
1206 
1207   // TODO(ricea): Fix this message if possible.
1208   EXPECT_EQ(exception->message(),
1209             "The stream was aborted by the remote server");
1210 }
1211 
1212 // BidirectionalStreams are thoroughly tested in bidirectional_stream_test.cc.
1213 // Here we just test the QuicTransport APIs.
TEST_F(QuicTransportTest,CreateBidirectionalStream)1214 TEST_F(QuicTransportTest, CreateBidirectionalStream) {
1215   V8TestingScope scope;
1216 
1217   auto* quic_transport =
1218       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
1219 
1220   EXPECT_CALL(
1221       *mock_quic_transport_,
1222       CreateStream(Truly(ValidConsumerHandle), Truly(ValidProducerHandle), _))
1223       .WillOnce([](Unused, Unused,
1224                    base::OnceCallback<void(bool, uint32_t)> callback) {
1225         std::move(callback).Run(true, 0);
1226       });
1227 
1228   auto* script_state = scope.GetScriptState();
1229   ScriptPromise bidirectional_stream_promise =
1230       quic_transport->createBidirectionalStream(script_state,
1231                                                 ASSERT_NO_EXCEPTION);
1232   ScriptPromiseTester tester(script_state, bidirectional_stream_promise);
1233 
1234   tester.WaitUntilSettled();
1235 
1236   EXPECT_TRUE(tester.IsFulfilled());
1237   auto* bidirectional_stream = V8BidirectionalStream::ToImplWithTypeCheck(
1238       scope.GetIsolate(), tester.Value().V8Value());
1239   EXPECT_TRUE(bidirectional_stream);
1240 }
1241 
TEST_F(QuicTransportTest,ReceiveBidirectionalStream)1242 TEST_F(QuicTransportTest, ReceiveBidirectionalStream) {
1243   V8TestingScope scope;
1244 
1245   auto* quic_transport =
1246       CreateAndConnectSuccessfully(scope, "quic-transport://example.com");
1247 
1248   mojo::ScopedDataPipeProducerHandle outgoing_producer;
1249   mojo::ScopedDataPipeConsumerHandle outgoing_consumer;
1250   ASSERT_TRUE(CreateDataPipeForWebTransportTests(&outgoing_producer,
1251                                                  &outgoing_consumer));
1252 
1253   mojo::ScopedDataPipeProducerHandle incoming_producer;
1254   mojo::ScopedDataPipeConsumerHandle incoming_consumer;
1255   ASSERT_TRUE(CreateDataPipeForWebTransportTests(&incoming_producer,
1256                                                  &incoming_consumer));
1257 
1258   std::move(pending_bidirectional_accept_callbacks_.front())
1259       .Run(next_stream_id_++, std::move(incoming_consumer),
1260            std::move(outgoing_producer));
1261 
1262   ReadableStream* streams = quic_transport->receiveBidirectionalStreams();
1263 
1264   v8::Local<v8::Value> v8value = ReadValueFromStream(scope, streams);
1265 
1266   BidirectionalStream* bidirectional_stream =
1267       V8BidirectionalStream::ToImplWithTypeCheck(scope.GetIsolate(), v8value);
1268   EXPECT_TRUE(bidirectional_stream);
1269 }
1270 
1271 }  // namespace
1272 
1273 }  // namespace blink
1274