WebTransportParent.cpp (30505B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "WebTransportParent.h" 8 9 #include "Http3WebTransportSession.h" 10 #include "mozilla/StaticPrefs_network.h" 11 #include "mozilla/TimeStamp.h" 12 #include "mozilla/dom/ClientInfo.h" 13 #include "mozilla/dom/WebTransportBinding.h" 14 #include "mozilla/dom/WebTransportLog.h" 15 #include "mozilla/ipc/BackgroundParent.h" 16 #include "mozilla/net/WebTransportHash.h" 17 #include "nsIEventTarget.h" 18 #include "nsIOService.h" 19 #include "nsIPrincipal.h" 20 #include "nsIWebTransport.h" 21 #include "nsIWebTransportStream.h" 22 #include "nsStreamUtils.h" 23 24 using IPCResult = mozilla::ipc::IPCResult; 25 26 namespace mozilla::dom { 27 28 NS_IMPL_ISUPPORTS(WebTransportParent, WebTransportSessionEventListener); 29 30 using CreateWebTransportPromise = 31 MozPromise<WebTransportReliabilityMode, nsresult, true>; 32 WebTransportParent::~WebTransportParent() { 33 LOG(("Destroying WebTransportParent %p", this)); 34 } 35 36 void WebTransportParent::Create( 37 const nsAString& aURL, nsIPrincipal* aPrincipal, 38 const uint64_t& aBrowsingContextID, 39 const mozilla::Maybe<IPCClientInfo>& aClientInfo, const bool& aDedicated, 40 const bool& aRequireUnreliable, const uint32_t& aCongestionControl, 41 nsTArray<WebTransportHash>&& aServerCertHashes, 42 Endpoint<PWebTransportParent>&& aParentEndpoint, 43 std::function<void(std::tuple<const nsresult&, const uint8_t&>)>&& 44 aResolver) { 45 LOG(("Created WebTransportParent %p %s %s %s congestion=%s", this, 46 NS_ConvertUTF16toUTF8(aURL).get(), 47 aDedicated ? "Dedicated" : "AllowPooling", 48 aRequireUnreliable ? "RequireUnreliable" : "", 49 aCongestionControl == 50 (uint32_t)dom::WebTransportCongestionControl::Throughput 51 ? "ThroughPut" 52 : (aCongestionControl == 53 (uint32_t)dom::WebTransportCongestionControl::Low_latency 54 ? "Low-Latency" 55 : "Default"))); 56 57 if (!aParentEndpoint.IsValid()) { 58 aResolver(ResolveType( 59 NS_ERROR_INVALID_ARG, 60 static_cast<uint8_t>(WebTransportReliabilityMode::Pending))); 61 return; 62 } 63 64 MOZ_DIAGNOSTIC_ASSERT(mozilla::net::gIOService); 65 nsresult rv = 66 mozilla::net::gIOService->NewWebTransport(getter_AddRefs(mWebTransport)); 67 if (NS_FAILED(rv)) { 68 aResolver(ResolveType( 69 rv, static_cast<uint8_t>(WebTransportReliabilityMode::Pending))); 70 return; 71 } 72 73 mOwningEventTarget = GetCurrentSerialEventTarget(); 74 MOZ_ASSERT(aPrincipal); 75 nsCOMPtr<nsIURI> uri; 76 rv = NS_NewURI(getter_AddRefs(uri), aURL); 77 if (NS_FAILED(rv)) { 78 aResolver(ResolveType( 79 NS_ERROR_INVALID_ARG, 80 static_cast<uint8_t>(WebTransportReliabilityMode::Pending))); 81 return; 82 } 83 84 nsTArray<RefPtr<nsIWebTransportHash>> nsServerCertHashes; 85 for (const auto& hash : aServerCertHashes) { 86 nsCString alg = hash.algorithm(); 87 nsServerCertHashes.AppendElement( 88 new net::WebTransportHash(alg, hash.value())); 89 } 90 91 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 92 "WebTransport AsyncConnect", 93 [self = RefPtr{this}, uri = std::move(uri), 94 dedicated = true /* aDedicated, see BUG 1915735.*/, 95 nsServerCertHashes = std::move(nsServerCertHashes), 96 principal = RefPtr{aPrincipal}, browsingContextID = aBrowsingContextID, 97 flags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 98 clientInfo = aClientInfo] { 99 LOG(("WebTransport %p AsyncConnect", self.get())); 100 if (NS_FAILED(self->mWebTransport->AsyncConnectWithClient( 101 uri, dedicated, std::move(nsServerCertHashes), principal, 102 browsingContextID, flags, self, clientInfo, 103 nsIWebTransport::HTTPVersion::h3))) { 104 LOG(("AsyncConnect failure; we should get OnSessionClosed")); 105 } 106 }); 107 108 // Bind to SocketThread for IPC - connection creation/destruction must 109 // hit MainThread, but keep all other traffic on SocketThread. Note that 110 // we must call aResolver() on this (PBackground) thread. 111 mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); 112 MOZ_ASSERT(NS_SUCCEEDED(rv)); 113 114 InvokeAsync(mSocketThread, __func__, 115 [parentEndpoint = std::move(aParentEndpoint), runnable = r, 116 resolver = std::move(aResolver), p = RefPtr{this}]() mutable { 117 { 118 MutexAutoLock lock(p->mMutex); 119 p->mResolver = resolver; 120 } 121 122 LOG(("Binding parent endpoint")); 123 if (!parentEndpoint.Bind(p)) { 124 return CreateWebTransportPromise::CreateAndReject( 125 NS_ERROR_FAILURE, __func__); 126 } 127 // IPC now holds a ref to parent 128 // Send connection to the server via MainThread 129 NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); 130 131 return CreateWebTransportPromise::CreateAndResolve( 132 WebTransportReliabilityMode::Supports_unreliable, __func__); 133 }) 134 ->Then( 135 GetCurrentSerialEventTarget(), __func__, 136 [p = RefPtr{this}]( 137 const CreateWebTransportPromise::ResolveOrRejectValue& aValue) { 138 if (aValue.IsReject()) { 139 std::function<void(ResolveType)> resolver; 140 { 141 MutexAutoLock lock(p->mMutex); 142 resolver = std::move(p->mResolver); 143 } 144 if (resolver) { 145 resolver( 146 ResolveType(aValue.RejectValue(), 147 static_cast<uint8_t>( 148 WebTransportReliabilityMode::Pending))); 149 } 150 } 151 }); 152 } 153 154 void WebTransportParent::ActorDestroy(ActorDestroyReason aWhy) { 155 LOG(("ActorDestroy WebTransportParent %d", aWhy)); 156 } 157 158 // We may not receive this response if the child side is destroyed without 159 // `Close` or `Shutdown` being explicitly called. 160 IPCResult WebTransportParent::RecvClose(const uint32_t& aCode, 161 const nsACString& aReason) { 162 LOG(("Close for %p received, code = %u, reason = %s", this, aCode, 163 PromiseFlatCString(aReason).get())); 164 { 165 MutexAutoLock lock(mMutex); 166 MOZ_ASSERT(!mClosed); 167 mClosed.Flip(); 168 } 169 mWebTransport->CloseSession(aCode, aReason); 170 Close(); 171 return IPC_OK(); 172 } 173 174 class BidiReceiveStream : public nsIWebTransportStreamCallback { 175 public: 176 NS_DECL_THREADSAFE_ISUPPORTS 177 NS_DECL_NSIWEBTRANSPORTSTREAMCALLBACK 178 179 BidiReceiveStream( 180 WebTransportParent::CreateBidirectionalStreamResolver&& aResolver, 181 std::function< 182 void(uint64_t, WebTransportParent::OnResetOrStopSendingCallback&&, 183 nsIWebTransportBidirectionalStream* aStream)>&& aStreamCallback, 184 Maybe<int64_t> aSendOrder, nsCOMPtr<nsISerialEventTarget>& aSocketThread) 185 : mResolver(aResolver), 186 mStreamCallback(std::move(aStreamCallback)), 187 mSendOrder(aSendOrder), 188 mSocketThread(aSocketThread) {} 189 190 private: 191 virtual ~BidiReceiveStream() = default; 192 WebTransportParent::CreateBidirectionalStreamResolver mResolver; 193 std::function<void(uint64_t, 194 WebTransportParent::OnResetOrStopSendingCallback&&, 195 nsIWebTransportBidirectionalStream* aStream)> 196 mStreamCallback; 197 Maybe<int64_t> mSendOrder; 198 nsCOMPtr<nsISerialEventTarget> mSocketThread; 199 }; 200 201 class UniReceiveStream : public nsIWebTransportStreamCallback { 202 public: 203 NS_DECL_THREADSAFE_ISUPPORTS 204 NS_DECL_NSIWEBTRANSPORTSTREAMCALLBACK 205 206 UniReceiveStream( 207 WebTransportParent::CreateUnidirectionalStreamResolver&& aResolver, 208 std::function<void(uint64_t, 209 WebTransportParent::OnResetOrStopSendingCallback&&, 210 nsIWebTransportSendStream* aStream)>&& aStreamCallback, 211 Maybe<int64_t> aSendOrder, nsCOMPtr<nsISerialEventTarget>& aSocketThread) 212 : mResolver(aResolver), 213 mStreamCallback(std::move(aStreamCallback)), 214 mSendOrder(aSendOrder), 215 mSocketThread(aSocketThread) {} 216 217 private: 218 virtual ~UniReceiveStream() = default; 219 WebTransportParent::CreateUnidirectionalStreamResolver mResolver; 220 std::function<void(uint64_t, 221 WebTransportParent::OnResetOrStopSendingCallback&&, 222 nsIWebTransportSendStream* aStream)> 223 mStreamCallback; 224 Maybe<int64_t> mSendOrder; 225 nsCOMPtr<nsISerialEventTarget> mSocketThread; 226 }; 227 228 NS_IMPL_ISUPPORTS(BidiReceiveStream, nsIWebTransportStreamCallback) 229 NS_IMPL_ISUPPORTS(UniReceiveStream, nsIWebTransportStreamCallback) 230 231 // nsIWebTransportStreamCallback: 232 NS_IMETHODIMP BidiReceiveStream::OnBidirectionalStreamReady( 233 nsIWebTransportBidirectionalStream* aStream) { 234 LOG(("Bidirectional stream ready!")); 235 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 236 237 aStream->SetSendOrder(mSendOrder); 238 239 RefPtr<mozilla::ipc::DataPipeSender> inputsender; 240 RefPtr<mozilla::ipc::DataPipeReceiver> inputreceiver; 241 nsresult rv = 242 NewDataPipe(mozilla::ipc::kDefaultDataPipeCapacity, 243 getter_AddRefs(inputsender), getter_AddRefs(inputreceiver)); 244 if (NS_WARN_IF(NS_FAILED(rv))) { 245 mResolver(rv); 246 return rv; 247 } 248 249 uint64_t id; 250 (void)aStream->GetStreamId(&id); 251 nsCOMPtr<nsIAsyncInputStream> inputStream; 252 aStream->GetInputStream(getter_AddRefs(inputStream)); 253 MOZ_ASSERT(inputStream); 254 nsCOMPtr<nsISupports> inputCopyContext; 255 rv = NS_AsyncCopy(inputStream, inputsender, mSocketThread, 256 NS_ASYNCCOPY_VIA_WRITESEGMENTS, // can we use READSEGMENTS? 257 mozilla::ipc::kDefaultDataPipeCapacity, nullptr, nullptr, 258 true, true, getter_AddRefs(inputCopyContext)); 259 if (NS_WARN_IF(NS_FAILED(rv))) { 260 mResolver(rv); 261 return rv; 262 } 263 264 RefPtr<mozilla::ipc::DataPipeSender> outputsender; 265 RefPtr<mozilla::ipc::DataPipeReceiver> outputreceiver; 266 rv = 267 NewDataPipe(mozilla::ipc::kDefaultDataPipeCapacity, 268 getter_AddRefs(outputsender), getter_AddRefs(outputreceiver)); 269 if (NS_WARN_IF(NS_FAILED(rv))) { 270 mResolver(rv); 271 return rv; 272 } 273 274 nsCOMPtr<nsIAsyncOutputStream> outputStream; 275 aStream->GetOutputStream(getter_AddRefs(outputStream)); 276 MOZ_ASSERT(outputStream); 277 nsCOMPtr<nsISupports> outputCopyContext; 278 rv = NS_AsyncCopy(outputreceiver, outputStream, mSocketThread, 279 NS_ASYNCCOPY_VIA_READSEGMENTS, 280 mozilla::ipc::kDefaultDataPipeCapacity, nullptr, nullptr, 281 true, true, getter_AddRefs(outputCopyContext)); 282 if (NS_WARN_IF(NS_FAILED(rv))) { 283 mResolver(rv); 284 return rv; 285 } 286 287 LOG(("Returning BidirectionalStream pipe to content")); 288 mResolver(BidirectionalStream(id, inputreceiver, outputsender)); 289 290 auto onResetOrStopSending = 291 [inputCopyContext(inputCopyContext), outputCopyContext(outputCopyContext), 292 inputsender(inputsender), 293 outputreceiver(outputreceiver)](nsresult aError) { 294 LOG(("onResetOrStopSending err=%x", static_cast<uint32_t>(aError))); 295 NS_CancelAsyncCopy(inputCopyContext, aError); 296 inputsender->CloseWithStatus(aError); 297 NS_CancelAsyncCopy(outputCopyContext, aError); 298 outputreceiver->CloseWithStatus(aError); 299 }; 300 301 // Store onResetOrStopSending in WebTransportParent::mBidiStreamCallbackMap 302 // and onResetOrStopSending will be called when a stream receives STOP_SENDING 303 // or RESET. 304 mStreamCallback(id, 305 WebTransportParent::OnResetOrStopSendingCallback( 306 std::move(onResetOrStopSending)), 307 aStream); 308 return NS_OK; 309 } 310 311 NS_IMETHODIMP UniReceiveStream::OnBidirectionalStreamReady( 312 nsIWebTransportBidirectionalStream* aStream) { 313 return NS_ERROR_FAILURE; 314 } 315 316 NS_IMETHODIMP 317 UniReceiveStream::OnUnidirectionalStreamReady( 318 nsIWebTransportSendStream* aStream) { 319 LOG(("Unidirectional stream ready!")); 320 // We should be on the Socket Thread 321 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 322 323 aStream->SetSendOrder(mSendOrder); 324 325 RefPtr<::mozilla::ipc::DataPipeSender> sender; 326 RefPtr<::mozilla::ipc::DataPipeReceiver> receiver; 327 nsresult rv = NewDataPipe(mozilla::ipc::kDefaultDataPipeCapacity, 328 getter_AddRefs(sender), getter_AddRefs(receiver)); 329 if (NS_WARN_IF(NS_FAILED(rv))) { 330 mResolver(rv); 331 return rv; 332 } 333 334 uint64_t id; 335 (void)aStream->GetStreamId(&id); 336 nsCOMPtr<nsIAsyncOutputStream> outputStream; 337 aStream->GetOutputStream(getter_AddRefs(outputStream)); 338 MOZ_ASSERT(outputStream); 339 nsCOMPtr<nsISupports> copyContext; 340 rv = NS_AsyncCopy(receiver, outputStream, mSocketThread, 341 NS_ASYNCCOPY_VIA_READSEGMENTS, 342 mozilla::ipc::kDefaultDataPipeCapacity, nullptr, nullptr, 343 true, true, getter_AddRefs(copyContext)); 344 if (NS_WARN_IF(NS_FAILED(rv))) { 345 mResolver(rv); 346 return rv; 347 } 348 349 LOG(("Returning UnidirectionalStream pipe to content")); 350 // pass the DataPipeSender to the content process 351 mResolver(UnidirectionalStream(id, sender)); 352 353 auto onResetOrStopSending = [copyContext(copyContext), 354 receiver(receiver)](nsresult aError) { 355 LOG(("onResetOrStopSending err=%x", static_cast<uint32_t>(aError))); 356 NS_CancelAsyncCopy(copyContext, aError); 357 receiver->CloseWithStatus(aError); 358 }; 359 360 // Store onResetOrStopSending in WebTransportParent::mStreamCallbackMap and 361 // onResetOrStopSending will be called when a stream receives STOP_SENDING. 362 mStreamCallback(id, 363 WebTransportParent::OnResetOrStopSendingCallback( 364 std::move(onResetOrStopSending)), 365 aStream); 366 367 return NS_OK; 368 } 369 370 NS_IMETHODIMP 371 BidiReceiveStream::OnUnidirectionalStreamReady( 372 nsIWebTransportSendStream* aStream) { 373 return NS_ERROR_FAILURE; 374 } 375 376 JS_HAZ_CAN_RUN_SCRIPT NS_IMETHODIMP UniReceiveStream::OnError(uint8_t aError) { 377 nsresult rv = aError == nsIWebTransport::INVALID_STATE_ERROR 378 ? NS_ERROR_DOM_INVALID_STATE_ERR 379 : NS_ERROR_FAILURE; 380 LOG(("CreateStream OnError: %u", aError)); 381 mResolver(rv); 382 return NS_OK; 383 } 384 385 JS_HAZ_CAN_RUN_SCRIPT NS_IMETHODIMP BidiReceiveStream::OnError(uint8_t aError) { 386 nsresult rv = aError == nsIWebTransport::INVALID_STATE_ERROR 387 ? NS_ERROR_DOM_INVALID_STATE_ERR 388 : NS_ERROR_FAILURE; 389 LOG(("CreateStream OnError: %u", aError)); 390 mResolver(rv); 391 return NS_OK; 392 } 393 394 IPCResult WebTransportParent::RecvSetSendOrder(uint64_t aStreamId, 395 Maybe<int64_t> aSendOrder) { 396 if (aSendOrder) { 397 LOG(("Set sendOrder=%" PRIi64 " for streamId %" PRIu64, aSendOrder.value(), 398 aStreamId)); 399 } else { 400 LOG(("Set sendOrder=null for streamId %" PRIu64, aStreamId)); 401 } 402 if (auto entry = mUniStreamCallbackMap.Lookup(aStreamId)) { 403 entry->mStream->SetSendOrder(aSendOrder); 404 } else if (auto entry = mBidiStreamCallbackMap.Lookup(aStreamId)) { 405 entry->mStream->SetSendOrder(aSendOrder); 406 } 407 return IPC_OK(); 408 } 409 410 IPCResult WebTransportParent::RecvCreateUnidirectionalStream( 411 Maybe<int64_t> aSendOrder, CreateUnidirectionalStreamResolver&& aResolver) { 412 LOG(("%s for %p received, useSendOrder=%d, sendOrder=%" PRIi64, __func__, 413 this, aSendOrder.isSome(), 414 aSendOrder.isSome() ? aSendOrder.value() : 0)); 415 416 auto streamCb = 417 [self = RefPtr{this}]( 418 uint64_t aStreamId, 419 WebTransportParent::OnResetOrStopSendingCallback&& aCallback, 420 nsIWebTransportSendStream* aStream) { 421 self->mUniStreamCallbackMap.InsertOrUpdate( 422 aStreamId, StreamHash<nsIWebTransportSendStream>{ 423 std::move(aCallback), aStream}); 424 }; 425 RefPtr<UniReceiveStream> callback = new UniReceiveStream( 426 std::move(aResolver), std::move(streamCb), aSendOrder, mSocketThread); 427 nsresult rv; 428 rv = mWebTransport->CreateOutgoingUnidirectionalStream(callback); 429 if (NS_FAILED(rv)) { 430 callback->OnError(0); // XXX 431 } 432 return IPC_OK(); 433 } 434 435 IPCResult WebTransportParent::RecvCreateBidirectionalStream( 436 Maybe<int64_t> aSendOrder, CreateBidirectionalStreamResolver&& aResolver) { 437 LOG(("%s for %p received, useSendOrder=%d, sendOrder=%" PRIi64, __func__, 438 this, aSendOrder.isSome(), 439 aSendOrder.isSome() ? aSendOrder.value() : 0)); 440 441 auto streamCb = 442 [self = RefPtr{this}]( 443 uint64_t aStreamId, 444 WebTransportParent::OnResetOrStopSendingCallback&& aCallback, 445 nsIWebTransportBidirectionalStream* aStream) { 446 self->mBidiStreamCallbackMap.InsertOrUpdate( 447 aStreamId, StreamHash<nsIWebTransportBidirectionalStream>{ 448 std::move(aCallback), aStream}); 449 }; 450 RefPtr<BidiReceiveStream> callback = new BidiReceiveStream( 451 std::move(aResolver), std::move(streamCb), aSendOrder, mSocketThread); 452 nsresult rv; 453 rv = mWebTransport->CreateOutgoingBidirectionalStream(callback); 454 if (NS_FAILED(rv)) { 455 callback->OnError(0); // XXX 456 } 457 return IPC_OK(); 458 } 459 460 // We recieve this notification from the WebTransportSessionProxy if session was 461 // successfully created at the end of 462 // WebTransportSessionProxy::OnStopRequest 463 NS_IMETHODIMP 464 WebTransportParent::OnSessionReady(uint64_t aSessionId) { 465 MOZ_ASSERT(mOwningEventTarget); 466 MOZ_ASSERT(!mOwningEventTarget->IsOnCurrentThread()); 467 468 LOG(("Created web transport session, sessionID = %" PRIu64 ", for %p", 469 aSessionId, this)); 470 471 mSessionReady = true; 472 473 // Retarget to socket thread. After this, WebTransportParent and 474 // |mWebTransport| should be only accessed on the socket thread. 475 nsresult rv = mWebTransport->RetargetTo(mSocketThread); 476 if (NS_FAILED(rv)) { 477 mOwningEventTarget->Dispatch(NS_NewRunnableFunction( 478 "WebTransportParent::OnSessionReady Failed", 479 [self = RefPtr{this}, result = rv] { 480 MutexAutoLock lock(self->mMutex); 481 if (!self->mClosed && self->mResolver) { 482 self->mResolver(ResolveType( 483 result, static_cast<uint8_t>( 484 WebTransportReliabilityMode::Supports_unreliable))); 485 self->mResolver = nullptr; 486 } 487 })); 488 return NS_OK; 489 } 490 491 mOwningEventTarget->Dispatch(NS_NewRunnableFunction( 492 "WebTransportParent::OnSessionReady", [self = RefPtr{this}] { 493 MutexAutoLock lock(self->mMutex); 494 if (!self->mClosed && self->mResolver) { 495 self->mResolver(ResolveType( 496 NS_OK, static_cast<uint8_t>( 497 WebTransportReliabilityMode::Supports_unreliable))); 498 self->mResolver = nullptr; 499 if (self->mExecuteAfterResolverCallback) { 500 self->mExecuteAfterResolverCallback(); 501 self->mExecuteAfterResolverCallback = nullptr; 502 } 503 } else { 504 if (self->mClosed) { 505 LOG(("Session already closed at OnSessionReady %p", self.get())); 506 } else { 507 LOG(("No resolver at OnSessionReady %p", self.get())); 508 } 509 } 510 })); 511 512 return NS_OK; 513 } 514 515 // We receive this notification from the WebTransportSessionProxy if session 516 // creation was unsuccessful at the end of 517 // WebTransportSessionProxy::OnStopRequest 518 NS_IMETHODIMP 519 WebTransportParent::OnSessionClosed(const bool aCleanly, 520 const uint32_t aErrorCode, 521 const nsACString& aReason) { 522 nsresult rv = NS_OK; 523 524 MOZ_ASSERT(mOwningEventTarget); 525 MOZ_ASSERT(!mOwningEventTarget->IsOnCurrentThread()); 526 527 // currently we just know if session was closed gracefully or not. 528 // we need better error propagation from lower-levels of http3 529 // webtransport session and it's subsequent error mapping to DOM. 530 // XXX See Bug 1806834 531 if (!mSessionReady) { 532 // this is an unclean close (we never got to ready) 533 LOG(("webtransport %p session creation failed code= %u, reason= %s", this, 534 aErrorCode, PromiseFlatCString(aReason).get())); 535 // we know we haven't gone Ready yet 536 rv = NS_ERROR_FAILURE; 537 mOwningEventTarget->Dispatch(NS_NewRunnableFunction( 538 "WebTransportParent::OnSessionClosed", 539 [self = RefPtr{this}, result = rv] { 540 MutexAutoLock lock(self->mMutex); 541 if (!self->mClosed && self->mResolver) { 542 self->mResolver(ResolveType( 543 result, static_cast<uint8_t>( 544 WebTransportReliabilityMode::Supports_unreliable))); 545 self->mResolver = nullptr; 546 } 547 })); 548 } else { 549 { 550 MutexAutoLock lock(mMutex); 551 if (mResolver) { 552 LOG(("[%p] NotifyRemoteClosed to be called later", this)); 553 // NotifyRemoteClosed needs to wait until mResolver is invoked. 554 mExecuteAfterResolverCallback = [self = RefPtr{this}, aCleanly, 555 aErrorCode, 556 reason = nsCString{aReason}]() { 557 self->NotifyRemoteClosed(aCleanly, aErrorCode, reason); 558 }; 559 return NS_OK; 560 } 561 } 562 // https://w3c.github.io/webtransport/#web-transport-termination 563 // Step 1: Let cleanly be a boolean representing whether the HTTP/3 564 // stream associated with the CONNECT request that initiated 565 // transport.[[Session]] is in the "Data Recvd" state. [QUIC] 566 // XXX not calculated yet 567 NotifyRemoteClosed(aCleanly, aErrorCode, aReason); 568 } 569 570 return NS_OK; 571 } 572 573 NS_IMETHODIMP WebTransportParent::OnStopSending(uint64_t aStreamId, 574 nsresult aError) { 575 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 576 LOG(("WebTransportParent::OnStopSending %p stream id=%" PRIx64, this, 577 aStreamId)); 578 if (auto entry = mUniStreamCallbackMap.Lookup(aStreamId)) { 579 entry->mCallback.OnResetOrStopSending(aError); 580 mUniStreamCallbackMap.Remove(aStreamId); 581 } else if (auto entry = mBidiStreamCallbackMap.Lookup(aStreamId)) { 582 entry->mCallback.OnResetOrStopSending(aError); 583 mBidiStreamCallbackMap.Remove(aStreamId); 584 } 585 if (CanSend()) { 586 (void)SendOnStreamResetOrStopSending(aStreamId, StopSendingError(aError)); 587 } 588 return NS_OK; 589 } 590 591 NS_IMETHODIMP WebTransportParent::OnResetReceived(uint64_t aStreamId, 592 nsresult aError) { 593 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 594 LOG(("WebTransportParent::OnResetReceived %p stream id=%" PRIx64, this, 595 aStreamId)); 596 if (auto entry = mUniStreamCallbackMap.Lookup(aStreamId)) { 597 entry->mCallback.OnResetOrStopSending(aError); 598 mUniStreamCallbackMap.Remove(aStreamId); 599 } else if (auto entry = mBidiStreamCallbackMap.Lookup(aStreamId)) { 600 entry->mCallback.OnResetOrStopSending(aError); 601 mBidiStreamCallbackMap.Remove(aStreamId); 602 } 603 if (CanSend()) { 604 (void)SendOnStreamResetOrStopSending(aStreamId, ResetError(aError)); 605 } 606 return NS_OK; 607 } 608 609 void WebTransportParent::NotifyRemoteClosed(bool aCleanly, uint32_t aErrorCode, 610 const nsACString& aReason) { 611 LOG(("webtransport %p session remote closed cleanly=%d code= %u, reason= %s", 612 this, aCleanly, aErrorCode, PromiseFlatCString(aReason).get())); 613 mSocketThread->Dispatch(NS_NewRunnableFunction( 614 __func__, [self = RefPtr{this}, aErrorCode, reason = nsCString{aReason}, 615 aCleanly]() { 616 // Tell the content side we were closed by the server 617 (void)self->SendRemoteClosed(aCleanly, aErrorCode, reason); 618 // Let the other end shut down the IPC channel after RecvClose() 619 })); 620 } 621 622 NS_IMETHODIMP 623 WebTransportParent::OnIncomingUnidirectionalStreamAvailable( 624 nsIWebTransportReceiveStream* aStream) { 625 // Note: we need to hold a reference to the stream if we want to get stats, 626 // etc 627 LOG(("%p IncomingUnidirectonalStream available", this)); 628 629 // We must be on the Socket Thread 630 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 631 632 RefPtr<DataPipeSender> sender; 633 RefPtr<DataPipeReceiver> receiver; 634 nsresult rv = NewDataPipe(mozilla::ipc::kDefaultDataPipeCapacity, 635 getter_AddRefs(sender), getter_AddRefs(receiver)); 636 if (NS_WARN_IF(NS_FAILED(rv))) { 637 return rv; 638 } 639 640 nsCOMPtr<nsIAsyncInputStream> inputStream; 641 aStream->GetInputStream(getter_AddRefs(inputStream)); 642 MOZ_ASSERT(inputStream); 643 rv = NS_AsyncCopy(inputStream, sender, mSocketThread, 644 NS_ASYNCCOPY_VIA_WRITESEGMENTS, 645 mozilla::ipc::kDefaultDataPipeCapacity); 646 if (NS_WARN_IF(NS_FAILED(rv))) { 647 return rv; 648 } 649 650 LOG(("%p Sending UnidirectionalStream pipe to content", this)); 651 // pass the DataPipeReceiver to the content process 652 uint64_t id; 653 (void)aStream->GetStreamId(&id); 654 (void)SendIncomingUnidirectionalStream(id, receiver); 655 656 return NS_OK; 657 } 658 659 NS_IMETHODIMP 660 WebTransportParent::OnIncomingBidirectionalStreamAvailable( 661 nsIWebTransportBidirectionalStream* aStream) { 662 // Note: we need to hold a reference to the stream if we want to get stats, 663 // etc 664 LOG(("%p IncomingBidirectonalStream available", this)); 665 666 // We must be on the Socket Thread 667 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 668 669 RefPtr<DataPipeSender> inputSender; 670 RefPtr<DataPipeReceiver> inputReceiver; 671 nsresult rv = 672 NewDataPipe(mozilla::ipc::kDefaultDataPipeCapacity, 673 getter_AddRefs(inputSender), getter_AddRefs(inputReceiver)); 674 if (NS_WARN_IF(NS_FAILED(rv))) { 675 return rv; 676 } 677 678 nsCOMPtr<nsIAsyncInputStream> inputStream; 679 aStream->GetInputStream(getter_AddRefs(inputStream)); 680 MOZ_ASSERT(inputStream); 681 rv = NS_AsyncCopy(inputStream, inputSender, mSocketThread, 682 NS_ASYNCCOPY_VIA_WRITESEGMENTS, 683 mozilla::ipc::kDefaultDataPipeCapacity); 684 if (NS_WARN_IF(NS_FAILED(rv))) { 685 return rv; 686 } 687 688 RefPtr<DataPipeSender> outputSender; 689 RefPtr<DataPipeReceiver> outputReceiver; 690 rv = 691 NewDataPipe(mozilla::ipc::kDefaultDataPipeCapacity, 692 getter_AddRefs(outputSender), getter_AddRefs(outputReceiver)); 693 if (NS_WARN_IF(NS_FAILED(rv))) { 694 return rv; 695 } 696 697 nsCOMPtr<nsIAsyncOutputStream> outputStream; 698 aStream->GetOutputStream(getter_AddRefs(outputStream)); 699 MOZ_ASSERT(outputStream); 700 rv = NS_AsyncCopy(outputReceiver, outputStream, mSocketThread, 701 NS_ASYNCCOPY_VIA_READSEGMENTS, 702 mozilla::ipc::kDefaultDataPipeCapacity); 703 if (NS_WARN_IF(NS_FAILED(rv))) { 704 return rv; 705 } 706 707 LOG(("%p Sending BidirectionalStream pipe to content", this)); 708 // pass the DataPipeSender to the content process 709 uint64_t id; 710 (void)aStream->GetStreamId(&id); 711 (void)SendIncomingBidirectionalStream(id, inputReceiver, outputSender); 712 return NS_OK; 713 } 714 715 ::mozilla::ipc::IPCResult WebTransportParent::RecvGetMaxDatagramSize( 716 GetMaxDatagramSizeResolver&& aResolver) { 717 LOG(("WebTransportParent RecvGetMaxDatagramSize")); 718 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 719 MOZ_ASSERT(mWebTransport); 720 MOZ_ASSERT(!mMaxDatagramSizeResolver); 721 722 mMaxDatagramSizeResolver = std::move(aResolver); 723 // maximum datagram size for the session is returned from network stack 724 // synchronously via WebTransportSessionEventListener::OnMaxDatagramSize 725 // interface 726 mWebTransport->GetMaxDatagramSize(); 727 return IPC_OK(); 728 } 729 730 ::mozilla::ipc::IPCResult WebTransportParent::RecvGetHttpChannelID( 731 GetHttpChannelIDResolver&& aResolver) { 732 LOG(("WebTransportParent Channel ID")); 733 MOZ_ASSERT(mWebTransport); 734 uint64_t aHttpChannelId; 735 mWebTransport->GetHttpChannelID(&aHttpChannelId); 736 aResolver(aHttpChannelId); 737 return IPC_OK(); 738 } 739 740 // The promise sent in this request will be resolved 741 // in OnOutgoingDatagramOutCome which is called synchronously from 742 // WebTransportSessionProxy::SendDatagram 743 ::mozilla::ipc::IPCResult WebTransportParent::RecvOutgoingDatagram( 744 nsTArray<uint8_t>&& aData, const TimeStamp& aExpirationTime, 745 OutgoingDatagramResolver&& aResolver) { 746 LOG(("WebTransportParent sending datagram")); 747 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 748 MOZ_ASSERT(mWebTransport); 749 750 (void)aExpirationTime; 751 752 MOZ_ASSERT(!mOutgoingDatagramResolver); 753 mOutgoingDatagramResolver = std::move(aResolver); 754 // XXX we need to forward the timestamps to the necko stack 755 // timestamp should be checked in the necko for expiry 756 // See Bug 1818300 757 // currently this calls OnOutgoingDatagramOutCome synchronously 758 // Neqo won't call us back if the id == 0! 759 // We don't use the ID for anything currently; rework of the stack 760 // to implement proper HighWatermark buffering will require 761 // changes here anyways. 762 static uint64_t sDatagramId = 1; 763 LOG_VERBOSE(("Sending datagram %" PRIu64 ", length %zu", sDatagramId, 764 aData.Length())); 765 (void)mWebTransport->SendDatagram(aData, sDatagramId++); 766 767 return IPC_OK(); 768 } 769 770 NS_IMETHODIMP WebTransportParent::OnDatagramReceived( 771 const nsTArray<uint8_t>& aData) { 772 // We must be on the Socket Thread 773 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 774 775 LOG(("WebTransportParent received datagram length = %zu", aData.Length())); 776 777 TimeStamp ts = TimeStamp::Now(); 778 (void)SendIncomingDatagram(aData, ts); 779 780 return NS_OK; 781 } 782 783 NS_IMETHODIMP 784 WebTransportParent::OnOutgoingDatagramOutCome( 785 uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome) { 786 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 787 // XXX - do we need better error mappings for failures? 788 nsresult result = NS_ERROR_FAILURE; 789 (void)result; 790 (void)aId; 791 792 if (aOutCome == WebTransportSessionEventListener::DatagramOutcome::SENT) { 793 result = NS_OK; 794 LOG(("Sent datagram id= %" PRIu64, aId)); 795 } else { 796 LOG(("Didn't send datagram id= %" PRIu64, aId)); 797 } 798 799 // This assumes the stack is calling us back synchronously! 800 MOZ_ASSERT(mOutgoingDatagramResolver); 801 mOutgoingDatagramResolver(result); 802 mOutgoingDatagramResolver = nullptr; 803 804 return NS_OK; 805 } 806 807 NS_IMETHODIMP WebTransportParent::OnMaxDatagramSize(uint64_t aSize) { 808 LOG(("Max datagram size is %" PRIu64, aSize)); 809 MOZ_ASSERT(mSocketThread->IsOnCurrentThread()); 810 MOZ_ASSERT(mMaxDatagramSizeResolver); 811 mMaxDatagramSizeResolver(aSize); 812 mMaxDatagramSizeResolver = nullptr; 813 return NS_OK; 814 } 815 } // namespace mozilla::dom