TestHttp2WebTransport.cpp (36566B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include <utility> 7 8 #include "TestCommon.h" 9 #include "gtest/gtest.h" 10 #include "Http2WebTransportSession.h" 11 #include "Http2WebTransportStream.h" 12 #include "nsString.h" 13 #include "nsTArray.h" 14 #include "mozilla/gtest/MozAssertions.h" 15 #include "mozilla/Queue.h" 16 #include "mozilla/net/NeqoHttp3Conn.h" 17 #include "Capsule.h" 18 #include "CapsuleEncoder.h" 19 #include "CapsuleParser.h" 20 #include "nsIWebTransport.h" 21 #include "nsStreamUtils.h" 22 #include "nsThreadUtils.h" 23 24 using namespace mozilla; 25 using namespace mozilla::net; 26 27 class MockWebTransportClient : public CapsuleIOHandler { 28 public: 29 NS_INLINE_DECL_REFCOUNTING(MockWebTransportClient, override) 30 31 explicit MockWebTransportClient(Http2WebTransportInitialSettings aSettings) 32 : mSession(new Http2WebTransportSessionImpl(this, aSettings)), 33 mParser(MakeUnique<CapsuleParser>(mSession)) {} 34 35 Http2WebTransportSessionImpl* Session() { return mSession; } 36 37 void HasCapsuleToSend() override { 38 mSession->PrepareCapsulesToSend(mOutCapsules); 39 } 40 41 void SetSentFin() override { mSetSentFinCalled = true; } 42 43 void StartReading() override { mStartReadingCalled = true; } 44 45 void OnCapsuleParseFailure(nsresult aError) override { 46 mOnParseFailureCalled = true; 47 } 48 49 void ProcessInputCapsules( 50 mozilla::Queue<UniquePtr<CapsuleEncoder>>&& aCapsules) { 51 while (!aCapsules.IsEmpty()) { 52 UniquePtr<CapsuleEncoder> capsule = aCapsules.Pop(); 53 auto buffer = capsule->GetBuffer(); 54 mParser->ProcessCapsuleData(buffer.Elements(), buffer.Length()); 55 } 56 } 57 58 void ProcessOutput() { 59 mSession->PrepareCapsulesToSend(mOutCapsules); 60 mozilla::Queue<UniquePtr<CapsuleEncoder>> queue(std::move(mOutCapsules)); 61 while (!queue.IsEmpty()) { 62 UniquePtr<CapsuleEncoder> encoder = queue.Pop(); 63 auto metadata = encoder->GetStreamMetadata(); 64 if (metadata) { 65 mSession->OnStreamDataSent(StreamId(metadata->mID), 66 metadata->mDataSize); 67 } 68 mOutCapsules.Push(std::move(encoder)); 69 } 70 } 71 72 mozilla::Queue<UniquePtr<CapsuleEncoder>> GetOutCapsules() { 73 return std::move(mOutCapsules); 74 } 75 76 void Done() { 77 mParser = nullptr; 78 mSession->Close(NS_OK); 79 mSession = nullptr; 80 } 81 82 // State tracking 83 bool mSetSentFinCalled = false; 84 bool mStartReadingCalled = false; 85 bool mOnParseFailureCalled = false; 86 87 private: 88 ~MockWebTransportClient() = default; 89 90 RefPtr<Http2WebTransportSessionImpl> mSession; 91 UniquePtr<CapsuleParser> mParser; 92 mozilla::Queue<UniquePtr<CapsuleEncoder>> mOutCapsules; 93 }; 94 95 class MockWebTransportServer : public CapsuleParser::Listener { 96 public: 97 NS_INLINE_DECL_REFCOUNTING(MockWebTransportServer, override) 98 99 explicit MockWebTransportServer() 100 : mParser(MakeUnique<CapsuleParser>(this)) {} 101 102 bool OnCapsule(Capsule&& aCapsule) override { 103 mReceivedCapsules.AppendElement(std::move(aCapsule)); 104 return true; 105 } 106 void OnCapsuleParseFailure(nsresult aError) override { 107 MOZ_RELEASE_ASSERT(false); 108 } 109 110 nsTArray<Capsule> GetReceivedCapsules() { 111 return std::move(mReceivedCapsules); 112 } 113 114 void ProcessInputCapsules( 115 mozilla::Queue<UniquePtr<CapsuleEncoder>>&& aCapsules) { 116 while (!aCapsules.IsEmpty()) { 117 UniquePtr<CapsuleEncoder> capsule = aCapsules.Pop(); 118 auto buffer = capsule->GetBuffer(); 119 mParser->ProcessCapsuleData(buffer.Elements(), buffer.Length()); 120 } 121 } 122 123 void SendWebTransportMaxStreamsCapsule(uint64_t aLimit, bool aBidi) { 124 Capsule capsule = Capsule::WebTransportMaxStreams(aLimit, aBidi); 125 UniquePtr<CapsuleEncoder> encoder = MakeUnique<CapsuleEncoder>(); 126 encoder->EncodeCapsule(capsule); 127 mOutCapsules.Push(std::move(encoder)); 128 } 129 130 void SendWebTransportStreamDataCapsule(uint64_t aID, bool aFin, 131 nsTArray<uint8_t>&& aData) { 132 Capsule capsule = 133 Capsule::WebTransportStreamData(aID, aFin, std::move(aData)); 134 UniquePtr<CapsuleEncoder> encoder = MakeUnique<CapsuleEncoder>(); 135 encoder->EncodeCapsule(capsule); 136 mOutCapsules.Push(std::move(encoder)); 137 } 138 139 void SendWebTransportMaxStreamDataCapsule(uint64_t aLimit, uint64_t aID) { 140 Capsule capsule = Capsule::WebTransportMaxStreamData(aLimit, aID); 141 UniquePtr<CapsuleEncoder> encoder = MakeUnique<CapsuleEncoder>(); 142 encoder->EncodeCapsule(capsule); 143 mOutCapsules.Push(std::move(encoder)); 144 } 145 146 void SendWebTransportMaxDataCapsule(uint64_t aLimit) { 147 Capsule capsule = Capsule::WebTransportMaxData(aLimit); 148 UniquePtr<CapsuleEncoder> encoder = MakeUnique<CapsuleEncoder>(); 149 encoder->EncodeCapsule(capsule); 150 mOutCapsules.Push(std::move(encoder)); 151 } 152 153 void SendWebTransportStopSendingCapsule(uint64_t aError, uint64_t aID) { 154 Capsule capsule = Capsule::WebTransportStopSending(aError, aID); 155 UniquePtr<CapsuleEncoder> encoder = MakeUnique<CapsuleEncoder>(); 156 encoder->EncodeCapsule(capsule); 157 mOutCapsules.Push(std::move(encoder)); 158 } 159 160 void SendWebTransportResetStreamCapsule(uint64_t aError, uint64_t aSize, 161 uint64_t aID) { 162 Capsule capsule = Capsule::WebTransportResetStream(aError, aSize, aID); 163 UniquePtr<CapsuleEncoder> encoder = MakeUnique<CapsuleEncoder>(); 164 encoder->EncodeCapsule(capsule); 165 mOutCapsules.Push(std::move(encoder)); 166 } 167 168 void SendDatagramCapsule(nsTArray<uint8_t>&& aPayload) { 169 Capsule capsule = Capsule::WebTransportDatagram(std::move(aPayload)); 170 UniquePtr<CapsuleEncoder> encoder = MakeUnique<CapsuleEncoder>(); 171 encoder->EncodeCapsule(capsule); 172 mOutCapsules.Push(std::move(encoder)); 173 } 174 175 mozilla::Queue<UniquePtr<CapsuleEncoder>> GetOutCapsules() { 176 return std::move(mOutCapsules); 177 } 178 179 void Done() { mParser = nullptr; } 180 181 private: 182 ~MockWebTransportServer() = default; 183 184 UniquePtr<CapsuleParser> mParser; 185 nsTArray<Capsule> mReceivedCapsules; 186 mozilla::Queue<UniquePtr<CapsuleEncoder>> mOutCapsules; 187 }; 188 189 // TODO: will be used when testing incoming streams. 190 class MockWebTransportSessionEventListener 191 : public WebTransportSessionEventListener, 192 public WebTransportSessionEventListenerInternal { 193 public: 194 NS_DECL_THREADSAFE_ISUPPORTS 195 NS_DECL_WEBTRANSPORTSESSIONEVENTLISTENER 196 NS_DECL_WEBTRANSPORTSESSIONEVENTLISTENERINTERNAL 197 198 MockWebTransportSessionEventListener() {} 199 nsTArray<RefPtr<WebTransportStreamBase>> TakeIncomingStreams() { 200 return std::move(mIncomingStreams); 201 } 202 nsTArray<uint8_t> mReceivedDatagrams; 203 204 Maybe<std::pair<uint64_t, nsresult>> TakeStopSending() { 205 return std::move(mStopSending); 206 } 207 208 Maybe<std::pair<uint64_t, nsresult>> TakeReset() { return std::move(mReset); } 209 210 private: 211 virtual ~MockWebTransportSessionEventListener() = default; 212 213 nsTArray<RefPtr<WebTransportStreamBase>> mIncomingStreams; 214 Maybe<std::pair<uint64_t, nsresult>> mStopSending; 215 Maybe<std::pair<uint64_t, nsresult>> mReset; 216 }; 217 218 NS_IMPL_ISUPPORTS(MockWebTransportSessionEventListener, 219 WebTransportSessionEventListener, 220 WebTransportSessionEventListenerInternal) 221 222 NS_IMETHODIMP 223 MockWebTransportSessionEventListener::OnSessionReadyInternal( 224 WebTransportSessionBase* aSession) { 225 return NS_OK; 226 } 227 228 NS_IMETHODIMP 229 MockWebTransportSessionEventListener::OnIncomingStreamAvailableInternal( 230 WebTransportStreamBase* aStream) { 231 mIncomingStreams.AppendElement(RefPtr{aStream}); 232 return NS_OK; 233 } 234 235 NS_IMETHODIMP 236 MockWebTransportSessionEventListener::OnIncomingBidirectionalStreamAvailable( 237 nsIWebTransportBidirectionalStream* aStream) { 238 return NS_OK; 239 } 240 241 NS_IMETHODIMP 242 MockWebTransportSessionEventListener::OnIncomingUnidirectionalStreamAvailable( 243 nsIWebTransportReceiveStream* aStream) { 244 return NS_OK; 245 } 246 247 NS_IMETHODIMP 248 MockWebTransportSessionEventListener::OnSessionReady(uint64_t ready) { 249 return NS_OK; 250 } 251 252 NS_IMETHODIMP 253 MockWebTransportSessionEventListener::OnSessionClosed( 254 bool aCleanly, uint32_t aStatus, const nsACString& aReason) { 255 return NS_OK; 256 } 257 258 NS_IMETHODIMP MockWebTransportSessionEventListener::OnDatagramReceivedInternal( 259 nsTArray<uint8_t>&& aData) { 260 mReceivedDatagrams = std::move(aData); 261 return NS_OK; 262 } 263 264 NS_IMETHODIMP MockWebTransportSessionEventListener::OnDatagramReceived( 265 const nsTArray<uint8_t>& aData) { 266 return NS_OK; 267 } 268 269 NS_IMETHODIMP MockWebTransportSessionEventListener::OnMaxDatagramSize( 270 uint64_t aSize) { 271 return NS_OK; 272 } 273 274 NS_IMETHODIMP 275 MockWebTransportSessionEventListener::OnOutgoingDatagramOutCome( 276 uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome) { 277 return NS_OK; 278 } 279 280 NS_IMETHODIMP MockWebTransportSessionEventListener::OnStopSending( 281 uint64_t aStreamId, nsresult aError) { 282 mStopSending = Some(std::pair<uint64_t, nsresult>(aStreamId, aError)); 283 return NS_OK; 284 } 285 286 NS_IMETHODIMP MockWebTransportSessionEventListener::OnResetReceived( 287 uint64_t aStreamId, nsresult aError) { 288 mReset = Some(std::pair<uint64_t, nsresult>(aStreamId, aError)); 289 return NS_OK; 290 } 291 292 static void ServerProcessCapsules(MockWebTransportServer* aServer, 293 MockWebTransportClient* aClient) { 294 aClient->ProcessOutput(); 295 mozilla::Queue<UniquePtr<CapsuleEncoder>> outCapsules = 296 aClient->GetOutCapsules(); 297 aServer->ProcessInputCapsules(std::move(outCapsules)); 298 } 299 300 static void ClientProcessCapsules(MockWebTransportServer* aServer, 301 MockWebTransportClient* aClient) { 302 mozilla::Queue<UniquePtr<CapsuleEncoder>> outCapsules = 303 aServer->GetOutCapsules(); 304 aClient->ProcessInputCapsules(std::move(outCapsules)); 305 } 306 307 TEST(TestHttp2WebTransport, CloseSessionCapsule) 308 { 309 RefPtr<MockWebTransportClient> client = 310 new MockWebTransportClient(Http2WebTransportInitialSettings()); 311 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 312 313 nsCString reason("test"); 314 client->Session()->CloseSession(42, "test"_ns); 315 316 ServerProcessCapsules(server, client); 317 318 nsTArray<Capsule> received = server->GetReceivedCapsules(); 319 ASSERT_EQ(received.Length(), 1u); 320 321 CloseWebTransportSessionCapsule& parsedCapsule = 322 received[0].GetCloseWebTransportSessionCapsule(); 323 ASSERT_EQ(parsedCapsule.mStatus, 42u); 324 ASSERT_EQ(parsedCapsule.mReason, reason); 325 326 client->Done(); 327 server->Done(); 328 } 329 330 TEST(TestHttp2WebTransport, CreateOutgoingStream) 331 { 332 RefPtr<MockWebTransportClient> client = 333 new MockWebTransportClient(Http2WebTransportInitialSettings()); 334 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 335 336 RefPtr<WebTransportStreamBase> bidiStream; 337 auto callback = 338 [&](Result<RefPtr<WebTransportStreamBase>, nsresult>&& aResult) { 339 if (aResult.isErr()) { 340 return; 341 } 342 bidiStream = aResult.unwrap(); 343 }; 344 client->Session()->CreateOutgoingBidirectionalStream(std::move(callback)); 345 ASSERT_TRUE(bidiStream == nullptr); 346 347 ServerProcessCapsules(server, client); 348 349 // Creating a stream is blocked, we should see a 350 // WebTransportStreamsBlockedCapsule from the client. 351 nsTArray<Capsule> received = server->GetReceivedCapsules(); 352 ASSERT_EQ(received.Length(), 1u); 353 354 WebTransportStreamsBlockedCapsule& streamsBlocked = 355 received[0].GetWebTransportStreamsBlockedCapsule(); 356 ASSERT_EQ(streamsBlocked.mLimit, 0u); 357 ASSERT_EQ(streamsBlocked.mBidi, true); 358 359 server->SendWebTransportMaxStreamsCapsule(1, true); 360 ClientProcessCapsules(server, client); 361 ASSERT_TRUE(bidiStream != nullptr); 362 363 RefPtr<WebTransportStreamBase> unidiStream; 364 auto callback1 = 365 [&](Result<RefPtr<WebTransportStreamBase>, nsresult>&& aResult) { 366 if (aResult.isErr()) { 367 return; 368 } 369 unidiStream = aResult.unwrap(); 370 }; 371 client->Session()->CreateOutgoingUnidirectionalStream(std::move(callback1)); 372 ASSERT_TRUE(unidiStream == nullptr); 373 374 ServerProcessCapsules(server, client); 375 376 received = server->GetReceivedCapsules(); 377 ASSERT_EQ(received.Length(), 1u); 378 379 WebTransportStreamsBlockedCapsule& streamsBlocked1 = 380 received[0].GetWebTransportStreamsBlockedCapsule(); 381 ASSERT_EQ(streamsBlocked1.mLimit, 0u); 382 ASSERT_EQ(streamsBlocked1.mBidi, false); 383 384 server->SendWebTransportMaxStreamsCapsule(1, false); 385 ClientProcessCapsules(server, client); 386 ASSERT_TRUE(unidiStream != nullptr); 387 388 client->Done(); 389 server->Done(); 390 } 391 392 static void CreateTestData(uint32_t aNumBytes, nsTArray<uint8_t>& aDataOut) { 393 static constexpr const char kSampleText[] = 394 "{\"type\":\"message\",\"id\":42,\"payload\":\"The quick brown fox jumps " 395 "over the lazy dog.\"}"; 396 static constexpr uint32_t kSampleTextLen = sizeof(kSampleText) - 1; 397 398 aDataOut.SetCapacity(aNumBytes); 399 400 while (aNumBytes > 0) { 401 uint32_t chunkSize = std::min(kSampleTextLen, aNumBytes); 402 aDataOut.AppendElements(reinterpret_cast<const uint8_t*>(kSampleText), 403 chunkSize); 404 aNumBytes -= chunkSize; 405 } 406 } 407 408 static void ValidateData(nsTArray<uint8_t>& aInput, 409 nsTArray<uint8_t>& aExpectedData) { 410 ASSERT_EQ(aExpectedData.Length(), aInput.Length()); 411 for (size_t i = 0; i < aExpectedData.Length(); i++) { 412 ASSERT_EQ(aExpectedData[i], aInput[i]); 413 } 414 } 415 416 static void ValidateData(nsIInputStream* aStream, 417 nsTArray<uint8_t>& aExpectedData) { 418 nsTArray<uint8_t> outputData; 419 nsresult rv = NS_ConsumeStream(aStream, UINT32_MAX, outputData); 420 ASSERT_NS_SUCCEEDED(rv); 421 ValidateData(outputData, aExpectedData); 422 } 423 424 static std::pair<nsCOMPtr<nsIAsyncOutputStream>, nsCOMPtr<nsIAsyncInputStream>> 425 CreateStreamAndSendData(WebTransportStreamBase* aStream, 426 const nsTArray<uint8_t>& aData) { 427 nsCOMPtr<nsIAsyncOutputStream> writer; 428 nsCOMPtr<nsIAsyncInputStream> reader; 429 aStream->GetWriterAndReader(getter_AddRefs(writer), getter_AddRefs(reader)); 430 431 uint32_t numWritten = 0; 432 (void)writer->Write((const char*)aData.Elements(), aData.Length(), 433 &numWritten); 434 NS_ProcessPendingEvents(nullptr); 435 return std::make_pair(writer, reader); 436 } 437 438 static void ValidateStreamCapsule(MockWebTransportServer* aServer, 439 nsTArray<uint8_t>& aExpectedData, 440 bool aExpectBidi) { 441 nsTArray<Capsule> received = aServer->GetReceivedCapsules(); 442 ASSERT_EQ(received.Length(), 1u); 443 444 WebTransportStreamDataCapsule& streamData = 445 received[0].GetWebTransportStreamDataCapsule(); 446 StreamId id(streamData.mID); 447 ASSERT_TRUE(id.IsClientInitiated()); 448 ASSERT_EQ(id.IsBiDi(), aExpectBidi); 449 ValidateData(streamData.mData, aExpectedData); 450 } 451 452 static already_AddRefed<WebTransportStreamBase> CreateOutgoingStream( 453 MockWebTransportClient* aClient, bool aBidi = true) { 454 RefPtr<WebTransportStreamBase> stream; 455 auto callback = 456 [&](Result<RefPtr<WebTransportStreamBase>, nsresult>&& aResult) { 457 if (aResult.isErr()) { 458 return; 459 } 460 stream = aResult.unwrap(); 461 MOZ_RELEASE_ASSERT(stream); 462 }; 463 if (aBidi) { 464 aClient->Session()->CreateOutgoingBidirectionalStream(std::move(callback)); 465 } else { 466 aClient->Session()->CreateOutgoingUnidirectionalStream(std::move(callback)); 467 } 468 return stream.forget(); 469 } 470 471 TEST(TestHttp2WebTransport, OutgoingUniStream) 472 { 473 Http2WebTransportInitialSettings settings; 474 settings.mInitialMaxData = 1024; 475 settings.mInitialMaxStreamsUni = 1; 476 settings.mInitialMaxStreamDataUni = 512; 477 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 478 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 479 480 // Try to create bidi stream (should fail and trigger streams-blocked capsule) 481 RefPtr<WebTransportStreamBase> bidiStream; 482 client->Session()->CreateOutgoingBidirectionalStream([&](auto&& aResult) { 483 if (aResult.isOk()) { 484 bidiStream = aResult.unwrap(); 485 } 486 }); 487 ASSERT_TRUE(bidiStream == nullptr); 488 489 ServerProcessCapsules(server, client); 490 491 { 492 nsTArray<Capsule> received = server->GetReceivedCapsules(); 493 ASSERT_EQ(received.Length(), 1u); 494 auto& capsule = received[0].GetWebTransportStreamsBlockedCapsule(); 495 ASSERT_EQ(capsule.mLimit, 0u); 496 ASSERT_TRUE(capsule.mBidi); 497 } 498 499 // Create unidirectional stream and send data 500 RefPtr<WebTransportStreamBase> unidiStream = 501 CreateOutgoingStream(client, false); 502 ASSERT_TRUE(unidiStream != nullptr); 503 504 nsTArray<uint8_t> inputData; 505 CreateTestData(512, inputData); 506 CreateStreamAndSendData(unidiStream, inputData); 507 508 ServerProcessCapsules(server, client); 509 ValidateStreamCapsule(server, inputData, /* aExpectBidi = */ false); 510 511 client->Done(); 512 server->Done(); 513 } 514 515 TEST(TestHttp2WebTransport, OutgoingBidiStream) 516 { 517 Http2WebTransportInitialSettings settings; 518 settings.mInitialMaxData = 1024; 519 settings.mInitialMaxStreamsUni = 1; 520 settings.mInitialMaxStreamsBidi = 1; 521 settings.mInitialMaxStreamDataBidi = 512; 522 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 523 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 524 525 RefPtr<WebTransportStreamBase> bidiStream = CreateOutgoingStream(client); 526 ASSERT_TRUE(bidiStream != nullptr); 527 528 nsTArray<uint8_t> inputData; 529 CreateTestData(512, inputData); 530 CreateStreamAndSendData(bidiStream, inputData); 531 532 ServerProcessCapsules(server, client); 533 ValidateStreamCapsule(server, inputData, /* aExpectBidi = */ true); 534 535 // Echo back from server 536 nsTArray<uint8_t> echo; 537 echo.AppendElements(inputData.Elements(), inputData.Length()); 538 StreamId id = StreamId::From(0); 539 server->SendWebTransportStreamDataCapsule(id, false, std::move(echo)); 540 ClientProcessCapsules(server, client); 541 542 nsCOMPtr<nsIAsyncInputStream> reader; 543 nsCOMPtr<nsIAsyncOutputStream> writer; 544 bidiStream->GetWriterAndReader(getter_AddRefs(writer), 545 getter_AddRefs(reader)); 546 uint64_t available = 0; 547 (void)reader->Available(&available); 548 EXPECT_EQ(available, inputData.Length()); 549 550 ValidateData(reader, inputData); 551 552 client->Done(); 553 server->Done(); 554 } 555 556 TEST(TestHttp2WebTransport, IncomingBidiStream) 557 { 558 Http2WebTransportInitialSettings settings; 559 settings.mInitialLocalMaxStreamsBidi = 1; 560 settings.mInitialLocalMaxStreamDataBidi = 512; 561 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 562 RefPtr<MockWebTransportSessionEventListener> listener = 563 new MockWebTransportSessionEventListener(); 564 client->Session()->SetWebTransportSessionEventListener(listener); 565 566 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 567 568 nsTArray<uint8_t> inputData; 569 CreateTestData(512, inputData); 570 nsTArray<uint8_t> cloned(inputData.Clone()); 571 572 server->SendWebTransportStreamDataCapsule(1, false, std::move(cloned)); 573 574 ClientProcessCapsules(server, client); 575 576 nsTArray<RefPtr<WebTransportStreamBase>> streams = 577 listener->TakeIncomingStreams(); 578 ASSERT_EQ(streams.Length(), 1u); 579 580 nsCOMPtr<nsIAsyncOutputStream> writer; 581 nsCOMPtr<nsIAsyncInputStream> reader; 582 RefPtr<WebTransportStreamBase> stream = streams[0]; 583 stream->GetWriterAndReader(getter_AddRefs(writer), getter_AddRefs(reader)); 584 585 ValidateData(reader, inputData); 586 587 cloned = inputData.Clone(); 588 server->SendWebTransportStreamDataCapsule(5, false, std::move(cloned)); 589 590 ClientProcessCapsules(server, client); 591 streams = listener->TakeIncomingStreams(); 592 ASSERT_EQ(streams.Length(), 0u); 593 594 client->Session()->OnStreamClosed( 595 static_cast<Http2WebTransportStream*>(stream.get())); 596 597 ServerProcessCapsules(server, client); 598 599 cloned = inputData.Clone(); 600 server->SendWebTransportStreamDataCapsule(5, false, std::move(cloned)); 601 602 ClientProcessCapsules(server, client); 603 streams = listener->TakeIncomingStreams(); 604 ASSERT_EQ(streams.Length(), 1u); 605 606 client->Done(); 607 server->Done(); 608 } 609 610 TEST(TestHttp2WebTransport, StreamDataSenderFlowControl) 611 { 612 Http2WebTransportInitialSettings settings; 613 settings.mInitialMaxData = 1024; 614 settings.mInitialMaxStreamsBidi = 1; 615 settings.mInitialMaxStreamDataBidi = 100; 616 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 617 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 618 619 RefPtr<WebTransportStreamBase> bidiStream = CreateOutgoingStream(client); 620 ASSERT_TRUE(bidiStream != nullptr); 621 622 nsCOMPtr<nsIAsyncOutputStream> writer; 623 nsCOMPtr<nsIAsyncInputStream> reader; 624 bidiStream->GetWriterAndReader(getter_AddRefs(writer), 625 getter_AddRefs(reader)); 626 627 nsTArray<uint8_t> inputData; 628 CreateTestData(100, inputData); 629 uint32_t numWritten = 0; 630 (void)writer->Write((const char*)inputData.Elements(), inputData.Length(), 631 &numWritten); 632 633 NS_ProcessPendingEvents(nullptr); 634 635 ServerProcessCapsules(server, client); 636 637 nsTArray<Capsule> received = server->GetReceivedCapsules(); 638 ASSERT_EQ(received.Length(), 1u); 639 640 WebTransportStreamDataCapsule& streamData = 641 received[0].GetWebTransportStreamDataCapsule(); 642 643 StreamId id(streamData.mID); 644 ASSERT_TRUE(id.IsClientInitiated()); 645 ASSERT_TRUE(id.IsBiDi()); 646 ValidateData(streamData.mData, inputData); 647 648 numWritten = 0; 649 (void)writer->Write((const char*)inputData.Elements(), inputData.Length(), 650 &numWritten); 651 652 NS_ProcessPendingEvents(nullptr); 653 ServerProcessCapsules(server, client); 654 655 received = server->GetReceivedCapsules(); 656 ASSERT_EQ(received.Length(), 1u); 657 658 WebTransportStreamDataBlockedCapsule& blocked = 659 received[0].GetWebTransportStreamDataBlockedCapsule(); 660 StreamId receivedId(blocked.mID); 661 ASSERT_EQ(id, receivedId); 662 ASSERT_EQ(blocked.mLimit, 100u); 663 664 server->SendWebTransportMaxStreamDataCapsule(300, id); 665 ClientProcessCapsules(server, client); 666 667 (void)writer->Write((const char*)inputData.Elements(), inputData.Length(), 668 &numWritten); 669 670 NS_ProcessPendingEvents(nullptr); 671 ServerProcessCapsules(server, client); 672 673 received = server->GetReceivedCapsules(); 674 ASSERT_EQ(received.Length(), 1u); 675 676 WebTransportStreamDataCapsule& streamData1 = 677 received[0].GetWebTransportStreamDataCapsule(); 678 ASSERT_EQ(streamData1.mData.Length(), 200u); 679 680 client->Done(); 681 server->Done(); 682 } 683 684 TEST(TestHttp2WebTransport, StreamDataSenderFlowControlMaxData) 685 { 686 Http2WebTransportInitialSettings settings; 687 settings.mInitialMaxData = 100; 688 settings.mInitialMaxStreamsBidi = 1; 689 settings.mInitialMaxStreamDataBidi = 100; 690 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 691 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 692 693 RefPtr<WebTransportStreamBase> bidiStream = CreateOutgoingStream(client); 694 ASSERT_TRUE(bidiStream != nullptr); 695 696 nsCOMPtr<nsIAsyncOutputStream> writer; 697 nsCOMPtr<nsIAsyncInputStream> reader; 698 bidiStream->GetWriterAndReader(getter_AddRefs(writer), 699 getter_AddRefs(reader)); 700 701 nsTArray<uint8_t> inputData; 702 CreateTestData(100, inputData); 703 uint32_t numWritten = 0; 704 (void)writer->Write((const char*)inputData.Elements(), inputData.Length(), 705 &numWritten); 706 707 NS_ProcessPendingEvents(nullptr); 708 709 ServerProcessCapsules(server, client); 710 711 nsTArray<Capsule> received = server->GetReceivedCapsules(); 712 ASSERT_EQ(received.Length(), 1u); 713 714 WebTransportStreamDataCapsule& streamData = 715 received[0].GetWebTransportStreamDataCapsule(); 716 717 StreamId id(streamData.mID); 718 ASSERT_TRUE(id.IsClientInitiated()); 719 ASSERT_TRUE(id.IsBiDi()); 720 ValidateData(streamData.mData, inputData); 721 722 numWritten = 0; 723 (void)writer->Write((const char*)inputData.Elements(), inputData.Length(), 724 &numWritten); 725 726 NS_ProcessPendingEvents(nullptr); 727 ServerProcessCapsules(server, client); 728 729 received = server->GetReceivedCapsules(); 730 ASSERT_EQ(received.Length(), 2u); 731 732 WebTransportDataBlockedCapsule& sessionDataBlocked = 733 received[0].GetWebTransportDataBlockedCapsule(); 734 ASSERT_EQ(sessionDataBlocked.mLimit, 100u); 735 736 WebTransportStreamDataBlockedCapsule& blocked = 737 received[1].GetWebTransportStreamDataBlockedCapsule(); 738 StreamId receivedId(blocked.mID); 739 ASSERT_EQ(id, receivedId); 740 ASSERT_EQ(blocked.mLimit, 100u); 741 742 server->SendWebTransportMaxStreamDataCapsule(500, id); 743 ClientProcessCapsules(server, client); 744 745 (void)writer->Write((const char*)inputData.Elements(), inputData.Length(), 746 &numWritten); 747 748 NS_ProcessPendingEvents(nullptr); 749 ServerProcessCapsules(server, client); 750 751 received = server->GetReceivedCapsules(); 752 ASSERT_EQ(received.Length(), 0u); 753 754 server->SendWebTransportMaxDataCapsule(1024); 755 ClientProcessCapsules(server, client); 756 757 (void)writer->Write((const char*)inputData.Elements(), inputData.Length(), 758 &numWritten); 759 760 NS_ProcessPendingEvents(nullptr); 761 ServerProcessCapsules(server, client); 762 763 received = server->GetReceivedCapsules(); 764 ASSERT_EQ(received.Length(), 1u); 765 766 WebTransportStreamDataCapsule& streamData1 = 767 received[0].GetWebTransportStreamDataCapsule(); 768 ASSERT_EQ(streamData1.mData.Length(), 300u); 769 770 client->Done(); 771 server->Done(); 772 } 773 774 static void CheckFc(ReceiverFlowControlBase& aFc, uint64_t aConsumed, 775 uint64_t aRetired) { 776 MOZ_RELEASE_ASSERT(aFc.Consumed() == aConsumed); 777 MOZ_RELEASE_ASSERT(aFc.Retired() == aRetired); 778 } 779 780 TEST(TestHttp2WebTransport, ReceiverFlowControl) 781 { 782 const uint32_t FC_SIZE = 1024; 783 Http2WebTransportInitialSettings settings; 784 settings.mInitialMaxStreamsBidi = 2; 785 settings.mInitialLocalMaxStreamDataBidi = FC_SIZE * 3 / 4; 786 settings.mInitialLocalMaxData = FC_SIZE; 787 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 788 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 789 790 RefPtr<WebTransportStreamBase> s1 = CreateOutgoingStream(client); 791 ASSERT_TRUE(s1 != nullptr); 792 793 RefPtr<WebTransportStreamBase> s2 = CreateOutgoingStream(client); 794 ASSERT_TRUE(s2 != nullptr); 795 796 CheckFc(client->Session()->ReceiverFc(), 0, 0); 797 CheckFc(*s1->ReceiverFc(), 0, 0); 798 CheckFc(*s2->ReceiverFc(), 0, 0); 799 800 nsTArray<uint8_t> inputData; 801 CreateTestData(FC_SIZE / 4, inputData); 802 803 StreamId id = StreamId::From(0); 804 server->SendWebTransportStreamDataCapsule(id, false, std::move(inputData)); 805 806 CreateTestData(FC_SIZE / 4, inputData); 807 StreamId id1 = StreamId::From(4); 808 server->SendWebTransportStreamDataCapsule(id1, false, std::move(inputData)); 809 810 ClientProcessCapsules(server, client); 811 812 CheckFc(client->Session()->ReceiverFc(), FC_SIZE / 2, FC_SIZE / 2); 813 CheckFc(*s1->ReceiverFc(), FC_SIZE / 4, FC_SIZE / 4); 814 CheckFc(*s2->ReceiverFc(), FC_SIZE / 4, FC_SIZE / 4); 815 816 CreateTestData(FC_SIZE / 4, inputData); 817 server->SendWebTransportStreamDataCapsule(id, false, std::move(inputData)); 818 819 ClientProcessCapsules(server, client); 820 821 CheckFc(client->Session()->ReceiverFc(), FC_SIZE * 3 / 4, FC_SIZE * 3 / 4); 822 CheckFc(*s1->ReceiverFc(), FC_SIZE / 2, FC_SIZE / 2); 823 CheckFc(*s2->ReceiverFc(), FC_SIZE / 4, FC_SIZE / 4); 824 825 client->Done(); 826 server->Done(); 827 } 828 829 TEST(TestHttp2WebTransport, ReceiverFlowControl1) 830 { 831 const uint32_t FC_SIZE = 1024; 832 Http2WebTransportInitialSettings settings; 833 settings.mInitialMaxStreamsBidi = 1; 834 settings.mInitialLocalMaxStreamDataBidi = FC_SIZE / 2; 835 settings.mInitialLocalMaxData = FC_SIZE; 836 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 837 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 838 839 RefPtr<WebTransportStreamBase> bidiStream = CreateOutgoingStream(client); 840 ASSERT_TRUE(bidiStream != nullptr); 841 842 nsCOMPtr<nsIAsyncOutputStream> writer; 843 nsCOMPtr<nsIAsyncInputStream> reader; 844 bidiStream->GetWriterAndReader(getter_AddRefs(writer), 845 getter_AddRefs(reader)); 846 847 nsTArray<uint8_t> inputData; 848 CreateTestData(FC_SIZE / 4, inputData); 849 850 StreamId id = StreamId::From(0); 851 server->SendWebTransportStreamDataCapsule(id, false, std::move(inputData)); 852 ClientProcessCapsules(server, client); 853 854 uint64_t available = 0; 855 (void)reader->Available(&available); 856 EXPECT_EQ(available, FC_SIZE / 4); 857 858 nsTArray<uint8_t> outputData; 859 nsresult rv = NS_ConsumeStream(reader, UINT32_MAX, outputData); 860 ASSERT_NS_SUCCEEDED(rv); 861 862 CheckFc(client->Session()->ReceiverFc(), FC_SIZE / 4, FC_SIZE / 4); 863 CheckFc(*bidiStream->ReceiverFc(), FC_SIZE / 4, FC_SIZE / 4); 864 865 CreateTestData(1, inputData); 866 server->SendWebTransportStreamDataCapsule(id, false, std::move(inputData)); 867 ClientProcessCapsules(server, client); 868 869 CheckFc(client->Session()->ReceiverFc(), FC_SIZE / 4 + 1, FC_SIZE / 4 + 1); 870 CheckFc(*bidiStream->ReceiverFc(), FC_SIZE / 4 + 1, FC_SIZE / 4 + 1); 871 872 available = 0; 873 (void)reader->Available(&available); 874 EXPECT_EQ(available, 1u); 875 876 ServerProcessCapsules(server, client); 877 878 nsTArray<Capsule> received = server->GetReceivedCapsules(); 879 ASSERT_EQ(received.Length(), 1u); 880 881 WebTransportMaxStreamDataCapsule& capsule = 882 received[0].GetWebTransportMaxStreamDataCapsule(); 883 ASSERT_EQ(capsule.mID, 0u); 884 ASSERT_EQ(capsule.mLimit, FC_SIZE * 3 / 4 + 1); 885 886 CreateTestData(FC_SIZE / 4 - 1, inputData); 887 server->SendWebTransportStreamDataCapsule(id, false, std::move(inputData)); 888 ClientProcessCapsules(server, client); 889 890 CheckFc(client->Session()->ReceiverFc(), FC_SIZE / 2, FC_SIZE / 2); 891 CheckFc(*bidiStream->ReceiverFc(), FC_SIZE / 2, FC_SIZE / 2); 892 893 CreateTestData(1, inputData); 894 server->SendWebTransportStreamDataCapsule(id, false, std::move(inputData)); 895 ClientProcessCapsules(server, client); 896 897 CheckFc(client->Session()->ReceiverFc(), FC_SIZE / 2 + 1, FC_SIZE / 2 + 1); 898 CheckFc(*bidiStream->ReceiverFc(), FC_SIZE / 2 + 1, FC_SIZE / 2 + 1); 899 900 ServerProcessCapsules(server, client); 901 received = server->GetReceivedCapsules(); 902 ASSERT_EQ(received.Length(), 1u); 903 904 WebTransportMaxDataCapsule& maxData = 905 received[0].GetWebTransportMaxDataCapsule(); 906 ASSERT_EQ(maxData.mMaxDataSize, FC_SIZE * 3 / 2 + 1); 907 908 client->Done(); 909 server->Done(); 910 } 911 912 TEST(TestHttp2WebTransport, StreamStopSending) 913 { 914 Http2WebTransportInitialSettings settings; 915 settings.mInitialMaxStreamsUni = 1; 916 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 917 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 918 919 RefPtr<WebTransportStreamBase> uniStream = 920 CreateOutgoingStream(client, false); 921 ASSERT_TRUE(uniStream != nullptr); 922 923 uniStream->SendStopSending(0); 924 ServerProcessCapsules(server, client); 925 926 nsTArray<Capsule> received = server->GetReceivedCapsules(); 927 ASSERT_EQ(received.Length(), 1u); 928 929 WebTransportStopSendingCapsule& stopSending = 930 received[0].GetWebTransportStopSendingCapsule(); 931 ASSERT_EQ(stopSending.mID, uniStream->WebTransportStreamId()); 932 ASSERT_EQ(stopSending.mErrorCode, 0u); 933 934 client->Done(); 935 server->Done(); 936 } 937 938 TEST(TestHttp2WebTransport, StreamOnStopSending) 939 { 940 Http2WebTransportInitialSettings settings; 941 settings.mInitialMaxStreamsUni = 1; 942 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 943 RefPtr<MockWebTransportSessionEventListener> listener = 944 new MockWebTransportSessionEventListener(); 945 client->Session()->SetWebTransportSessionEventListener(listener); 946 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 947 948 RefPtr<WebTransportStreamBase> uniStream = 949 CreateOutgoingStream(client, false); 950 ASSERT_TRUE(uniStream != nullptr); 951 952 nsTArray<uint8_t> inputData; 953 CreateTestData(512, inputData); 954 CreateStreamAndSendData(uniStream, inputData); 955 956 ServerProcessCapsules(server, client); 957 958 server->SendWebTransportStopSendingCapsule(0, 959 uniStream->WebTransportStreamId()); 960 ClientProcessCapsules(server, client); 961 962 auto stopSending = listener->TakeStopSending(); 963 ASSERT_TRUE(stopSending); 964 ASSERT_EQ(stopSending->first, uniStream->WebTransportStreamId()); 965 ASSERT_EQ(stopSending->second, NS_ERROR_WEBTRANSPORT_CODE_BASE); 966 967 client->Done(); 968 server->Done(); 969 } 970 971 TEST(TestHttp2WebTransport, StreamReset) 972 { 973 const uint32_t TOTAL_SIZE = 1024; 974 Http2WebTransportInitialSettings settings; 975 settings.mInitialMaxStreamsBidi = 1; 976 settings.mInitialMaxStreamDataBidi = TOTAL_SIZE; 977 settings.mInitialMaxData = TOTAL_SIZE; 978 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 979 RefPtr<MockWebTransportSessionEventListener> listener = 980 new MockWebTransportSessionEventListener(); 981 client->Session()->SetWebTransportSessionEventListener(listener); 982 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 983 984 RefPtr<WebTransportStreamBase> stream = CreateOutgoingStream(client); 985 ASSERT_TRUE(stream != nullptr); 986 987 nsTArray<uint8_t> inputData; 988 CreateTestData(TOTAL_SIZE / 4, inputData); 989 CreateStreamAndSendData(stream, inputData); 990 991 ServerProcessCapsules(server, client); 992 993 inputData.Clear(); 994 CreateTestData(TOTAL_SIZE / 4, inputData); 995 CreateStreamAndSendData(stream, inputData); 996 997 ServerProcessCapsules(server, client); 998 999 nsTArray<Capsule> received = server->GetReceivedCapsules(); 1000 ASSERT_EQ(received.Length(), 2u); 1001 1002 stream->Reset(0); 1003 1004 ServerProcessCapsules(server, client); 1005 received = server->GetReceivedCapsules(); 1006 ASSERT_EQ(received.Length(), 1u); 1007 1008 WebTransportResetStreamCapsule& reset = 1009 received[0].GetWebTransportResetStreamCapsule(); 1010 ASSERT_EQ(reset.mID, stream->WebTransportStreamId()); 1011 ASSERT_EQ(reset.mErrorCode, 0u); 1012 ASSERT_EQ(reset.mReliableSize, TOTAL_SIZE / 2); 1013 1014 client->Done(); 1015 server->Done(); 1016 } 1017 1018 TEST(TestHttp2WebTransport, StreamResetReliableSize) 1019 { 1020 const uint32_t TOTAL_SIZE = 1024; 1021 Http2WebTransportInitialSettings settings; 1022 settings.mInitialMaxStreamsBidi = 1; 1023 settings.mInitialMaxStreamDataBidi = TOTAL_SIZE; 1024 settings.mInitialMaxData = TOTAL_SIZE; 1025 RefPtr<MockWebTransportClient> client = new MockWebTransportClient(settings); 1026 RefPtr<MockWebTransportSessionEventListener> listener = 1027 new MockWebTransportSessionEventListener(); 1028 client->Session()->SetWebTransportSessionEventListener(listener); 1029 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 1030 1031 RefPtr<WebTransportStreamBase> stream = CreateOutgoingStream(client); 1032 ASSERT_TRUE(stream != nullptr); 1033 1034 nsTArray<uint8_t> inputData; 1035 CreateTestData(TOTAL_SIZE / 4, inputData); 1036 auto streams = CreateStreamAndSendData(stream, inputData); 1037 1038 ServerProcessCapsules(server, client); 1039 1040 server->SendWebTransportStreamDataCapsule(stream->WebTransportStreamId(), 1041 false, std::move(inputData)); 1042 CreateTestData(TOTAL_SIZE / 4, inputData); 1043 server->SendWebTransportStreamDataCapsule(stream->WebTransportStreamId(), 1044 false, std::move(inputData)); 1045 1046 server->SendWebTransportResetStreamCapsule(0, TOTAL_SIZE / 2, 1047 stream->WebTransportStreamId()); 1048 ClientProcessCapsules(server, client); 1049 1050 auto reset = listener->TakeReset(); 1051 ASSERT_TRUE(reset); 1052 ASSERT_EQ(reset->first, stream->WebTransportStreamId()); 1053 ASSERT_EQ(reset->second, NS_ERROR_WEBTRANSPORT_CODE_BASE); 1054 1055 client->Done(); 1056 server->Done(); 1057 } 1058 1059 TEST(TestHttp2WebTransport, SendAndReceiveDatagram) 1060 { 1061 RefPtr<MockWebTransportClient> client = 1062 new MockWebTransportClient(Http2WebTransportInitialSettings()); 1063 RefPtr<MockWebTransportServer> server = new MockWebTransportServer(); 1064 RefPtr<MockWebTransportSessionEventListener> listener = 1065 new MockWebTransportSessionEventListener(); 1066 client->Session()->SetWebTransportSessionEventListener(listener); 1067 1068 nsTArray<uint8_t> mockData, expectedData; 1069 CreateTestData(512, mockData); 1070 expectedData.AppendElements(mockData); 1071 1072 // Send datagram from client to server 1073 client->Session()->SendDatagram(std::move(mockData), 1); 1074 ServerProcessCapsules(server, client); 1075 1076 // Verify the server received the correct datagram capsule 1077 nsTArray<Capsule> received = server->GetReceivedCapsules(); 1078 WebTransportDatagramCapsule& parsedCapsule = 1079 received[0].GetWebTransportDatagramCapsule(); 1080 ValidateData(parsedCapsule.mPayload, expectedData); 1081 1082 // Echo and verify the received datagram payload back to the client 1083 server->SendDatagramCapsule(std::move(parsedCapsule.mPayload)); 1084 ClientProcessCapsules(server, client); 1085 ValidateData(listener->mReceivedDatagrams, expectedData); 1086 1087 client->Done(); 1088 server->Done(); 1089 }