WebTransportSessionProxy.cpp (43837B)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 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 "WebTransportLog.h" 7 #include "Http3WebTransportSession.h" 8 #include "Http3WebTransportStream.h" 9 #include "ScopedNSSTypes.h" 10 #include "WebTransportSessionProxy.h" 11 #include "WebTransportStreamProxy.h" 12 #include "WebTransportEventService.h" 13 #include "nsIAsyncVerifyRedirectCallback.h" 14 #include "nsIHttpChannel.h" 15 #include "nsIHttpChannelInternal.h" 16 #include "nsIRequest.h" 17 #include "nsITransportSecurityInfo.h" 18 #include "nsIX509Cert.h" 19 #include "nsNetUtil.h" 20 #include "nsProxyRelease.h" 21 #include "nsILoadInfo.h" 22 #include "nsSocketTransportService2.h" 23 #include "mozilla/Logging.h" 24 #include "mozilla/ScopeExit.h" 25 #include "mozilla/StaticPrefs_network.h" 26 #include "mozilla/LoadInfo.h" 27 28 namespace mozilla::net { 29 30 LazyLogModule webTransportLog("nsWebTransport"); 31 32 NS_IMPL_ISUPPORTS(WebTransportSessionProxy, WebTransportSessionEventListener, 33 WebTransportSessionEventListenerInternal, 34 WebTransportConnectionSettings, nsIWebTransport, 35 nsIRedirectResultListener, nsIStreamListener, 36 nsIChannelEventSink, nsIInterfaceRequestor); 37 38 WebTransportSessionProxy::WebTransportSessionProxy() 39 : mMutex("WebTransportSessionProxy::mMutex"), 40 mTarget(GetMainThreadSerialEventTarget()) { 41 LOG(("WebTransportSessionProxy constructor")); 42 } 43 44 WebTransportSessionProxy::~WebTransportSessionProxy() { 45 if (OnSocketThread()) { 46 return; 47 } 48 49 MutexAutoLock lock(mMutex); 50 if ((mState != WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED) && 51 (mState != WebTransportSessionProxyState::ACTIVE) && 52 (mState != WebTransportSessionProxyState::SESSION_CLOSE_PENDING)) { 53 return; 54 } 55 56 MOZ_ASSERT(mState != WebTransportSessionProxyState::SESSION_CLOSE_PENDING, 57 "We can not be in the SESSION_CLOSE_PENDING state in destructor, " 58 "because should be a runnable that holds reference to this" 59 "object."); 60 61 (void)gSocketTransportService->Dispatch(NS_NewRunnableFunction( 62 "WebTransportSessionProxy::ProxyHttp3WebTransportSessionRelease", 63 [self{std::move(mWebTransportSession)}]() {})); 64 } 65 66 //----------------------------------------------------------------------------- 67 // WebTransportSessionProxy::nsIWebTransport 68 //----------------------------------------------------------------------------- 69 70 nsresult WebTransportSessionProxy::AsyncConnect( 71 nsIURI* aURI, bool aDedicated, 72 const nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes, 73 nsIPrincipal* aPrincipal, uint32_t aSecurityFlags, 74 WebTransportSessionEventListener* aListener, 75 nsIWebTransport::HTTPVersion aVersion) { 76 return AsyncConnectWithClient(aURI, aDedicated, std::move(aServerCertHashes), 77 aPrincipal, 0, aSecurityFlags, aListener, 78 Maybe<dom::ClientInfo>(), aVersion); 79 } 80 81 nsresult WebTransportSessionProxy::AsyncConnectWithClient( 82 nsIURI* aURI, bool aDedicated, 83 const nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes, 84 nsIPrincipal* aPrincipal, uint64_t aBrowsingContextID, 85 uint32_t aSecurityFlags, WebTransportSessionEventListener* aListener, 86 const Maybe<dom::ClientInfo>& aClientInfo, 87 nsIWebTransport::HTTPVersion aVersion) { 88 MOZ_ASSERT(NS_IsMainThread()); 89 90 if (aVersion == nsIWebTransport::HTTPVersion::h2) { 91 mHTTPVersion = nsIWebTransport::HTTPVersion::h2; 92 } 93 LOG(("WebTransportSessionProxy::AsyncConnect")); 94 { 95 MutexAutoLock lock(mMutex); 96 mListener = aListener; 97 } 98 auto cleanup = MakeScopeExit([self = RefPtr<WebTransportSessionProxy>(this)] { 99 MutexAutoLock lock(self->mMutex); 100 self->mListener->OnSessionClosed(false, 0, 101 ""_ns); // TODO: find a better error. 102 self->mChannel = nullptr; 103 self->mListener = nullptr; 104 self->ChangeState(WebTransportSessionProxyState::DONE); 105 }); 106 107 nsSecurityFlags flags = nsILoadInfo::SEC_COOKIES_OMIT | aSecurityFlags; 108 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | 109 nsIRequest::LOAD_BYPASS_CACHE | 110 nsIRequest::INHIBIT_CACHING; 111 nsresult rv = NS_ERROR_FAILURE; 112 113 if (aClientInfo.isSome()) { 114 rv = NS_NewChannel(getter_AddRefs(mChannel), aURI, aPrincipal, 115 aClientInfo.ref(), Maybe<dom::ServiceWorkerDescriptor>(), 116 flags, nsContentPolicyType::TYPE_WEB_TRANSPORT, 117 /* aCookieJarSettings */ nullptr, 118 /* aPerformanceStorage */ nullptr, 119 /* aLoadGroup */ nullptr, 120 /* aCallbacks */ this, loadFlags); 121 } else { 122 rv = NS_NewChannel(getter_AddRefs(mChannel), aURI, aPrincipal, flags, 123 nsContentPolicyType::TYPE_WEB_TRANSPORT, 124 /* aCookieJarSettings */ nullptr, 125 /* aPerformanceStorage */ nullptr, 126 /* aLoadGroup */ nullptr, 127 /* aCallbacks */ this, loadFlags); 128 } 129 130 NS_ENSURE_SUCCESS(rv, rv); 131 132 // configure HTTP specific stuff 133 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); 134 if (!httpChannel) { 135 mChannel = nullptr; 136 return NS_ERROR_ABORT; 137 } 138 139 mDedicatedConnection = aDedicated; 140 141 if (!aServerCertHashes.IsEmpty()) { 142 mServerCertHashes.Clear(); 143 mServerCertHashes.AppendElements(aServerCertHashes); 144 } 145 146 { 147 MutexAutoLock lock(mMutex); 148 ChangeState(WebTransportSessionProxyState::NEGOTIATING); 149 } 150 151 // https://www.ietf.org/archive/id/draft-ietf-webtrans-http3-04.html#section-6 152 rv = httpChannel->SetRequestHeader("Sec-Webtransport-Http3-Draft02"_ns, 153 "1"_ns, false); 154 if (NS_FAILED(rv)) { 155 return rv; 156 } 157 158 // To establish a WebTransport session with an origin origin, follow 159 // [WEB-TRANSPORT-HTTP3] section 3.3, with using origin, serialized and 160 // isomorphic encoded, as the `Origin` header of the request. 161 // https://www.w3.org/TR/webtransport/#protocol-concepts 162 nsAutoCString serializedOrigin; 163 if (NS_FAILED( 164 aPrincipal->GetWebExposedOriginSerialization(serializedOrigin))) { 165 // origin/URI will be missing for system principals 166 // assign null origin 167 serializedOrigin = "null"_ns; 168 } 169 170 rv = httpChannel->SetRequestHeader("Origin"_ns, serializedOrigin, false); 171 if (NS_FAILED(rv)) { 172 return rv; 173 } 174 175 nsCOMPtr<nsIHttpChannelInternal> internalChannel = 176 do_QueryInterface(mChannel); 177 if (!internalChannel) { 178 mChannel = nullptr; 179 return NS_ERROR_ABORT; 180 } 181 (void)internalChannel->SetWebTransportSessionEventListener(this); 182 183 rv = mChannel->AsyncOpen(this); 184 if (NS_SUCCEEDED(rv)) { 185 cleanup.release(); 186 } 187 188 mHttpChannelID = httpChannel->ChannelId(); 189 190 // Setting the BrowsingContextID here to let WebTransport requests show up in 191 // devtools. Normally that would automatically happen if we would pass the 192 // nsILoadGroup in ns_NewChannel above, but the nsILoadGroup is inaccessible 193 // here in the ParentProcess. The nsILoadGroup only exists in ContentProcess 194 // as part of the document and nsDocShell. It is also not yet determined which 195 // ContentProcess this load belongs to. 196 if (aBrowsingContextID != 0) { 197 nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo(); 198 static_cast<LoadInfo*>(loadInfo.get()) 199 ->UpdateBrowsingContextID(aBrowsingContextID); 200 } 201 202 return rv; 203 } 204 205 NS_IMETHODIMP 206 WebTransportSessionProxy::RetargetTo(nsIEventTarget* aTarget) { 207 if (!aTarget) { 208 return NS_ERROR_INVALID_ARG; 209 } 210 211 { 212 MutexAutoLock lock(mMutex); 213 LOG(("WebTransportSessionProxy::RetargetTo mState=%d", mState)); 214 // RetargetTo should be only called after the session is ready. 215 if (mState != WebTransportSessionProxyState::ACTIVE) { 216 return NS_ERROR_UNEXPECTED; 217 } 218 219 mTarget = aTarget; 220 } 221 222 return NS_OK; 223 } 224 225 NS_IMETHODIMP 226 WebTransportSessionProxy::GetStats() { return NS_ERROR_NOT_IMPLEMENTED; } 227 228 NS_IMETHODIMP 229 WebTransportSessionProxy::CloseSession(uint32_t status, 230 const nsACString& reason) { 231 MutexAutoLock lock(mMutex); 232 MOZ_ASSERT(mTarget->IsOnCurrentThread()); 233 mCloseStatus = status; 234 mReason = reason; 235 mListener = nullptr; 236 mPendingEvents.Clear(); 237 mServerCertHashes.Clear(); 238 switch (mState) { 239 case WebTransportSessionProxyState::INIT: 240 case WebTransportSessionProxyState::DONE: 241 return NS_ERROR_NOT_INITIALIZED; 242 case WebTransportSessionProxyState::NEGOTIATING: 243 mChannel->Cancel(NS_ERROR_ABORT); 244 mChannel = nullptr; 245 ChangeState(WebTransportSessionProxyState::DONE); 246 break; 247 case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: 248 mChannel->Cancel(NS_ERROR_ABORT); 249 mChannel = nullptr; 250 ChangeState(WebTransportSessionProxyState::SESSION_CLOSE_PENDING); 251 CloseSessionInternal(); 252 break; 253 case WebTransportSessionProxyState::ACTIVE: 254 ChangeState(WebTransportSessionProxyState::SESSION_CLOSE_PENDING); 255 CloseSessionInternal(); 256 break; 257 case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING: 258 ChangeState(WebTransportSessionProxyState::DONE); 259 break; 260 case SESSION_CLOSE_PENDING: 261 break; 262 } 263 return NS_OK; 264 } 265 266 NS_IMETHODIMP WebTransportSessionProxy::GetDedicated(bool* dedicated) { 267 *dedicated = mDedicatedConnection; 268 return NS_OK; 269 } 270 271 NS_IMETHODIMP WebTransportSessionProxy::GetServerCertificateHashes( 272 nsTArray<RefPtr<nsIWebTransportHash>>& aServerCertHashes) { 273 aServerCertHashes.Clear(); 274 aServerCertHashes.AppendElements(mServerCertHashes); 275 return NS_OK; 276 } 277 278 NS_IMETHODIMP WebTransportSessionProxy::GetHttpVersion( 279 nsIWebTransport::HTTPVersion* aVersion) { 280 *aVersion = mHTTPVersion; 281 return NS_OK; 282 } 283 284 void WebTransportSessionProxy::CloseSessionInternalLocked() { 285 MutexAutoLock lock(mMutex); 286 CloseSessionInternal(); 287 } 288 289 void WebTransportSessionProxy::CloseSessionInternal() MOZ_REQUIRES(mMutex) { 290 if (!OnSocketThread()) { 291 mMutex.AssertCurrentThreadOwns(); 292 RefPtr<WebTransportSessionProxy> self(this); 293 (void)gSocketTransportService->Dispatch(NS_NewRunnableFunction( 294 "WebTransportSessionProxy::CallCloseWebTransportSession", 295 [self{std::move(self)}]() { self->CloseSessionInternalLocked(); })); 296 return; 297 } 298 299 mMutex.AssertCurrentThreadOwns(); 300 301 RefPtr<WebTransportSessionBase> wt; 302 uint32_t closeStatus = 0; 303 nsCString reason; 304 305 if (mState == WebTransportSessionProxyState::SESSION_CLOSE_PENDING) { 306 MOZ_ASSERT(mWebTransportSession); 307 wt = mWebTransportSession; 308 mWebTransportSession = nullptr; 309 closeStatus = mCloseStatus; 310 reason = mReason; 311 ChangeState(WebTransportSessionProxyState::DONE); 312 } else { 313 MOZ_ASSERT(mState == WebTransportSessionProxyState::DONE); 314 } 315 316 if (wt) { 317 MutexAutoUnlock unlock(mMutex); 318 wt->CloseSession(closeStatus, reason); 319 } 320 } 321 322 class WebTransportStreamCallbackWrapper final { 323 public: 324 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebTransportStreamCallbackWrapper) 325 326 explicit WebTransportStreamCallbackWrapper( 327 nsIWebTransportStreamCallback* aCallback, bool aBidi) 328 : mCallback(aCallback), 329 mTarget(GetCurrentSerialEventTarget()), 330 mBidi(aBidi) {} 331 332 void CallOnError(nsresult aError) { 333 if (!mTarget->IsOnCurrentThread()) { 334 RefPtr<WebTransportStreamCallbackWrapper> self(this); 335 (void)mTarget->Dispatch(NS_NewRunnableFunction( 336 "WebTransportStreamCallbackWrapper::CallOnError", 337 [self{std::move(self)}, error{aError}]() { 338 self->CallOnError(error); 339 })); 340 return; 341 } 342 343 LOG(("WebTransportStreamCallbackWrapper::OnError aError=0x%" PRIx32, 344 static_cast<uint32_t>(aError))); 345 (void)mCallback->OnError(nsIWebTransport::INVALID_STATE_ERROR); 346 } 347 348 void CallOnStreamReady(WebTransportStreamProxy* aStream) { 349 if (!mTarget->IsOnCurrentThread()) { 350 RefPtr<WebTransportStreamCallbackWrapper> self(this); 351 RefPtr<WebTransportStreamProxy> stream = aStream; 352 (void)mTarget->Dispatch(NS_NewRunnableFunction( 353 "WebTransportStreamCallbackWrapper::CallOnStreamReady", 354 [self{std::move(self)}, stream{std::move(stream)}]() { 355 self->CallOnStreamReady(stream); 356 })); 357 return; 358 } 359 360 if (mBidi) { 361 (void)mCallback->OnBidirectionalStreamReady(aStream); 362 return; 363 } 364 365 (void)mCallback->OnUnidirectionalStreamReady(aStream); 366 } 367 368 private: 369 virtual ~WebTransportStreamCallbackWrapper() { 370 NS_ProxyRelease( 371 "WebTransportStreamCallbackWrapper::~WebTransportStreamCallbackWrapper", 372 mTarget, mCallback.forget()); 373 } 374 375 nsCOMPtr<nsIWebTransportStreamCallback> mCallback; 376 nsCOMPtr<nsIEventTarget> mTarget; 377 bool mBidi = false; 378 }; 379 380 void WebTransportSessionProxy::CreateStreamInternal( 381 nsIWebTransportStreamCallback* callback, bool aBidi) { 382 mMutex.AssertCurrentThreadOwns(); 383 LOG( 384 ("WebTransportSessionProxy::CreateStreamInternal %p " 385 "mState=%d, bidi=%d", 386 this, mState, aBidi)); 387 switch (mState) { 388 case WebTransportSessionProxyState::INIT: 389 case WebTransportSessionProxyState::NEGOTIATING: 390 case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: 391 case WebTransportSessionProxyState::ACTIVE: { 392 RefPtr<WebTransportStreamCallbackWrapper> wrapper = 393 new WebTransportStreamCallbackWrapper(callback, aBidi); 394 if (mState == WebTransportSessionProxyState::ACTIVE && 395 mWebTransportSession) { 396 DoCreateStream(wrapper, mWebTransportSession, aBidi); 397 } else { 398 LOG( 399 ("WebTransportSessionProxy::CreateStreamInternal %p " 400 " queue create stream event", 401 this)); 402 auto task = [self = RefPtr{this}, wrapper{std::move(wrapper)}, 403 bidi(aBidi)](nsresult aStatus) { 404 if (NS_FAILED(aStatus)) { 405 wrapper->CallOnError(aStatus); 406 return; 407 } 408 409 self->DoCreateStream(wrapper, nullptr, bidi); 410 }; 411 // TODO: we should do this properly in bug 1830362. 412 mPendingCreateStreamEvents.AppendElement(std::move(task)); 413 } 414 } break; 415 case WebTransportSessionProxyState::SESSION_CLOSE_PENDING: 416 case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING: 417 case WebTransportSessionProxyState::DONE: { 418 nsCOMPtr<nsIWebTransportStreamCallback> cb(callback); 419 NS_DispatchToCurrentThread(NS_NewRunnableFunction( 420 "WebTransportSessionProxy::CreateStreamInternal", 421 [cb{std::move(cb)}]() { 422 cb->OnError(nsIWebTransport::INVALID_STATE_ERROR); 423 })); 424 } break; 425 } 426 } 427 428 void WebTransportSessionProxy::DoCreateStream( 429 WebTransportStreamCallbackWrapper* aCallback, 430 WebTransportSessionBase* aSession, bool aBidi) { 431 if (!OnSocketThread()) { 432 RefPtr<WebTransportSessionProxy> self(this); 433 RefPtr<WebTransportStreamCallbackWrapper> wrapper(aCallback); 434 (void)gSocketTransportService->Dispatch(NS_NewRunnableFunction( 435 "WebTransportSessionProxy::DoCreateStream", 436 [self{std::move(self)}, wrapper{std::move(wrapper)}, bidi(aBidi)]() { 437 self->DoCreateStream(wrapper, nullptr, bidi); 438 })); 439 return; 440 } 441 442 LOG(("WebTransportSessionProxy::DoCreateStream %p bidi=%d", this, aBidi)); 443 444 RefPtr<WebTransportSessionBase> session = aSession; 445 // Having no session here means that this is called by dispatching tasks. 446 // The mState may be already changed, so we need to check it again. 447 if (!aSession) { 448 MutexAutoLock lock(mMutex); 449 switch (mState) { 450 case WebTransportSessionProxyState::INIT: 451 case WebTransportSessionProxyState::NEGOTIATING: 452 case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: 453 MOZ_ASSERT(false, "DoCreateStream called with invalid state"); 454 aCallback->CallOnError(NS_ERROR_UNEXPECTED); 455 return; 456 case WebTransportSessionProxyState::ACTIVE: { 457 session = mWebTransportSession; 458 } break; 459 case WebTransportSessionProxyState::SESSION_CLOSE_PENDING: 460 case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING: 461 case WebTransportSessionProxyState::DONE: 462 // Session is going to be closed. 463 aCallback->CallOnError(NS_ERROR_NOT_AVAILABLE); 464 return; 465 } 466 } 467 468 if (!session) { 469 MOZ_ASSERT_UNREACHABLE("This should not happen"); 470 aCallback->CallOnError(NS_ERROR_UNEXPECTED); 471 return; 472 } 473 474 RefPtr<WebTransportStreamCallbackWrapper> wrapper(aCallback); 475 auto callback = 476 [wrapper{std::move(wrapper)}]( 477 Result<RefPtr<WebTransportStreamBase>, nsresult>&& aResult) { 478 if (aResult.isErr()) { 479 wrapper->CallOnError(aResult.unwrapErr()); 480 return; 481 } 482 483 RefPtr<WebTransportStreamBase> stream = aResult.unwrap(); 484 RefPtr<WebTransportStreamProxy> streamProxy = 485 new WebTransportStreamProxy(stream); 486 wrapper->CallOnStreamReady(streamProxy); 487 }; 488 489 if (aBidi) { 490 session->CreateOutgoingBidirectionalStream(std::move(callback)); 491 } else { 492 session->CreateOutgoingUnidirectionalStream(std::move(callback)); 493 } 494 } 495 496 NS_IMETHODIMP 497 WebTransportSessionProxy::CreateOutgoingUnidirectionalStream( 498 nsIWebTransportStreamCallback* callback) { 499 if (!callback) { 500 return NS_ERROR_INVALID_ARG; 501 } 502 503 MutexAutoLock lock(mMutex); 504 CreateStreamInternal(callback, false); 505 return NS_OK; 506 } 507 508 NS_IMETHODIMP 509 WebTransportSessionProxy::CreateOutgoingBidirectionalStream( 510 nsIWebTransportStreamCallback* callback) { 511 if (!callback) { 512 return NS_ERROR_INVALID_ARG; 513 } 514 515 MutexAutoLock lock(mMutex); 516 CreateStreamInternal(callback, true); 517 return NS_OK; 518 } 519 520 void WebTransportSessionProxy::SendDatagramInternal( 521 const RefPtr<WebTransportSessionBase>& aSession, nsTArray<uint8_t>&& aData, 522 uint64_t aTrackingId) { 523 MOZ_ASSERT(OnSocketThread()); 524 525 aSession->SendDatagram(std::move(aData), aTrackingId); 526 } 527 528 NS_IMETHODIMP 529 WebTransportSessionProxy::SendDatagram(const nsTArray<uint8_t>& aData, 530 uint64_t aTrackingId) { 531 RefPtr<WebTransportSessionBase> session; 532 { 533 MutexAutoLock lock(mMutex); 534 if (mState != WebTransportSessionProxyState::ACTIVE || 535 !mWebTransportSession) { 536 return NS_ERROR_NOT_AVAILABLE; 537 } 538 session = mWebTransportSession; 539 } 540 541 nsTArray<uint8_t> copied; 542 copied.Assign(aData); 543 if (!OnSocketThread()) { 544 return gSocketTransportService->Dispatch(NS_NewRunnableFunction( 545 "WebTransportSessionProxy::SendDatagramInternal", 546 [self = RefPtr{this}, session{std::move(session)}, 547 data{std::move(copied)}, trackingId(aTrackingId)]() mutable { 548 self->SendDatagramInternal(session, std::move(data), trackingId); 549 })); 550 } 551 552 SendDatagramInternal(session, std::move(copied), aTrackingId); 553 return NS_OK; 554 } 555 556 void WebTransportSessionProxy::GetMaxDatagramSizeInternal( 557 const RefPtr<WebTransportSessionBase>& aSession) { 558 MOZ_ASSERT(OnSocketThread()); 559 560 aSession->GetMaxDatagramSize(); 561 } 562 563 NS_IMETHODIMP 564 WebTransportSessionProxy::GetMaxDatagramSize() { 565 RefPtr<WebTransportSessionBase> session; 566 { 567 MutexAutoLock lock(mMutex); 568 if (mState != WebTransportSessionProxyState::ACTIVE || 569 !mWebTransportSession) { 570 return NS_ERROR_NOT_AVAILABLE; 571 } 572 session = mWebTransportSession; 573 } 574 575 if (!OnSocketThread()) { 576 return gSocketTransportService->Dispatch(NS_NewRunnableFunction( 577 "WebTransportSessionProxy::GetMaxDatagramSizeInternal", 578 [self = RefPtr{this}, session{std::move(session)}]() { 579 self->GetMaxDatagramSizeInternal(session); 580 })); 581 } 582 583 GetMaxDatagramSizeInternal(session); 584 return NS_OK; 585 } 586 587 NS_IMETHODIMP 588 WebTransportSessionProxy::GetHttpChannelID(uint64_t* _retval) { 589 *_retval = mHttpChannelID; 590 return NS_OK; 591 } 592 593 //----------------------------------------------------------------------------- 594 // WebTransportSessionProxy::nsIStreamListener 595 //----------------------------------------------------------------------------- 596 597 NS_IMETHODIMP 598 WebTransportSessionProxy::OnStartRequest(nsIRequest* aRequest) { 599 MOZ_ASSERT(NS_IsMainThread()); 600 LOG(("WebTransportSessionProxy::OnStartRequest\n")); 601 nsCOMPtr<WebTransportSessionEventListener> listener; 602 nsAutoCString reason; 603 uint32_t closeStatus = 0; 604 { 605 MutexAutoLock lock(mMutex); 606 switch (mState) { 607 case WebTransportSessionProxyState::INIT: 608 case WebTransportSessionProxyState::DONE: 609 case WebTransportSessionProxyState::ACTIVE: 610 case WebTransportSessionProxyState::SESSION_CLOSE_PENDING: 611 MOZ_ASSERT(false, "OnStartRequest cannot be called in this state."); 612 break; 613 case WebTransportSessionProxyState::NEGOTIATING: 614 case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING: { 615 nsresult rv; 616 if (NS_SUCCEEDED(mChannel->GetStatus(&rv)) && 617 rv == NS_ERROR_WEBTRANSPORT_SESSION_LIMIT_EXCEEDED) { 618 mReason = "WebTransport session limit exceeded"_ns; 619 mCloseStatus = 0; 620 } 621 listener = mListener; 622 mListener = nullptr; 623 mChannel = nullptr; 624 reason = mReason; 625 closeStatus = mCloseStatus; 626 ChangeState(WebTransportSessionProxyState::DONE); 627 break; 628 } 629 case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: { 630 uint32_t status; 631 632 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel); 633 if (!httpChannel || 634 NS_FAILED(httpChannel->GetResponseStatus(&status)) || 635 !(status >= 200 && status < 300)) { 636 listener = mListener; 637 mListener = nullptr; 638 mChannel = nullptr; 639 mReason = ""_ns; 640 reason = ""_ns; 641 mCloseStatus = 642 0; // TODO: find a better error. Currently error code 0 is used 643 ChangeState(WebTransportSessionProxyState::SESSION_CLOSE_PENDING); 644 CloseSessionInternal(); // TODO: find a better error. Currently error 645 // code 0 is used. 646 } 647 // The success cases will be handled in OnStopRequest. 648 } break; 649 } 650 } 651 if (listener) { 652 listener->OnSessionClosed(false, closeStatus, reason); 653 } 654 return NS_OK; 655 } 656 657 NS_IMETHODIMP 658 WebTransportSessionProxy::OnDataAvailable(nsIRequest* aRequest, 659 nsIInputStream* aStream, 660 uint64_t aOffset, uint32_t aCount) { 661 MOZ_ASSERT(NS_IsMainThread()); 662 MOZ_RELEASE_ASSERT( 663 false, "WebTransportSessionProxy::OnDataAvailable should not be called"); 664 return NS_OK; 665 } 666 667 NS_IMETHODIMP 668 WebTransportSessionProxy::OnStopRequest(nsIRequest* aRequest, 669 nsresult aStatus) { 670 MOZ_ASSERT(NS_IsMainThread()); 671 mChannel = nullptr; 672 nsCOMPtr<WebTransportSessionEventListener> listener; 673 nsAutoCString reason; 674 uint32_t closeStatus = 0; 675 uint64_t sessionId; 676 bool succeeded = false; 677 nsTArray<std::function<void()>> pendingEvents; 678 nsTArray<std::function<void(nsresult)>> pendingCreateStreamEvents; 679 { 680 MutexAutoLock lock(mMutex); 681 switch (mState) { 682 case WebTransportSessionProxyState::INIT: 683 case WebTransportSessionProxyState::ACTIVE: 684 case WebTransportSessionProxyState::NEGOTIATING: 685 MOZ_ASSERT(false, "OnStopRequest cannot be called in this state."); 686 break; 687 case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING: 688 reason = mReason; 689 closeStatus = mCloseStatus; 690 listener = mListener; 691 mListener = nullptr; 692 ChangeState(WebTransportSessionProxyState::DONE); 693 break; 694 case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: 695 if (NS_FAILED(aStatus)) { 696 listener = mListener; 697 mListener = nullptr; 698 mReason = ""_ns; 699 reason = ""_ns; 700 mCloseStatus = 0; 701 ChangeState(WebTransportSessionProxyState::SESSION_CLOSE_PENDING); 702 CloseSessionInternal(); // TODO: find a better error. Currently error 703 // code 0 is used. 704 } else { 705 succeeded = true; 706 sessionId = mSessionId; 707 listener = mListener; 708 ChangeState(WebTransportSessionProxyState::ACTIVE); 709 } 710 break; 711 case WebTransportSessionProxyState::SESSION_CLOSE_PENDING: 712 case WebTransportSessionProxyState::DONE: 713 break; 714 } 715 pendingEvents = std::move(mPendingEvents); 716 pendingCreateStreamEvents = std::move(mPendingCreateStreamEvents); 717 if (!pendingCreateStreamEvents.IsEmpty()) { 718 if (NS_SUCCEEDED(aStatus) && 719 (mState == WebTransportSessionProxyState::DONE || 720 mState == WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING || 721 mState == WebTransportSessionProxyState::SESSION_CLOSE_PENDING)) { 722 aStatus = NS_ERROR_FAILURE; 723 } 724 } 725 726 mStopRequestCalled = true; 727 } 728 729 if (!pendingCreateStreamEvents.IsEmpty()) { 730 (void)gSocketTransportService->Dispatch(NS_NewRunnableFunction( 731 "WebTransportSessionProxy::DispatchPendingCreateStreamEvents", 732 [pendingCreateStreamEvents = std::move(pendingCreateStreamEvents), 733 status(aStatus)]() { 734 for (const auto& event : pendingCreateStreamEvents) { 735 event(status); 736 } 737 })); 738 } // otherwise let the CreateStreams just go away 739 740 if (listener) { 741 if (succeeded) { 742 listener->OnSessionReady(sessionId); 743 if (!pendingEvents.IsEmpty()) { 744 (void)gSocketTransportService->Dispatch(NS_NewRunnableFunction( 745 "WebTransportSessionProxy::DispatchPendingEvents", 746 [pendingEvents = std::move(pendingEvents)]() { 747 for (const auto& event : pendingEvents) { 748 event(); 749 } 750 })); 751 } 752 } else { 753 listener->OnSessionClosed(false, closeStatus, 754 reason); // TODO: find a better error. 755 // Currently error code 0 is used. 756 } 757 } 758 return NS_OK; 759 } 760 761 //----------------------------------------------------------------------------- 762 // WebTransportSessionProxy::nsIChannelEventSink 763 //----------------------------------------------------------------------------- 764 765 NS_IMETHODIMP 766 WebTransportSessionProxy::AsyncOnChannelRedirect( 767 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags, 768 nsIAsyncVerifyRedirectCallback* callback) { 769 // Currently implementation we do not reach this part of the code 770 // as location headers are not forwarded by the http3 stack to the applicaion. 771 // Hence, the channel is aborted due to the location header check in 772 // nsHttpChannel::AsyncProcessRedirection This comment must be removed after 773 // the following neqo bug is resolved 774 // https://github.com/mozilla/neqo/issues/1364 775 if (!StaticPrefs::network_webtransport_redirect_enabled()) { 776 LOG(("Channel Redirects are disabled for WebTransport sessions")); 777 return NS_ERROR_ABORT; 778 } 779 780 nsCOMPtr<nsIURI> newURI; 781 nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI)); 782 NS_ENSURE_SUCCESS(rv, rv); 783 784 rv = aNewChannel->GetURI(getter_AddRefs(newURI)); 785 if (NS_FAILED(rv)) { 786 callback->OnRedirectVerifyCallback(rv); 787 return NS_OK; 788 } 789 790 // abort the request if redirecting to insecure context 791 if (!newURI->SchemeIs("https")) { 792 callback->OnRedirectVerifyCallback(NS_ERROR_ABORT); 793 return NS_OK; 794 } 795 796 // Assign to mChannel after we get notification about success of the 797 // redirect in OnRedirectResult. 798 mRedirectChannel = aNewChannel; 799 800 callback->OnRedirectVerifyCallback(NS_OK); 801 return NS_OK; 802 } 803 804 //----------------------------------------------------------------------------- 805 // WebTransportSessionProxy::nsIRedirectResultListener 806 //----------------------------------------------------------------------------- 807 808 NS_IMETHODIMP 809 WebTransportSessionProxy::OnRedirectResult(nsresult aStatus) { 810 if (NS_SUCCEEDED(aStatus) && mRedirectChannel) { 811 mChannel = mRedirectChannel; 812 } 813 814 mRedirectChannel = nullptr; 815 816 return NS_OK; 817 } 818 819 //----------------------------------------------------------------------------- 820 // WebTransportSessionProxy::nsIInterfaceRequestor 821 //----------------------------------------------------------------------------- 822 823 NS_IMETHODIMP 824 WebTransportSessionProxy::GetInterface(const nsIID& aIID, void** aResult) { 825 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { 826 NS_ADDREF_THIS(); 827 *aResult = static_cast<nsIChannelEventSink*>(this); 828 return NS_OK; 829 } 830 831 if (aIID.Equals(NS_GET_IID(nsIRedirectResultListener))) { 832 NS_ADDREF_THIS(); 833 *aResult = static_cast<nsIRedirectResultListener*>(this); 834 return NS_OK; 835 } 836 837 return NS_ERROR_NO_INTERFACE; 838 } 839 840 //----------------------------------------------------------------------------- 841 // WebTransportSessionProxy::WebTransportSessionEventListener 842 //----------------------------------------------------------------------------- 843 844 // This function is called when the WebTransportSessionBase is ready. After 845 // this call WebTransportSessionProxy is responsible for the 846 // WebTransportSessionBase, i.e. it is responsible for closing it. 847 // The listener of the WebTransportSessionProxy will be informed during 848 // OnStopRequest call. 849 NS_IMETHODIMP 850 WebTransportSessionProxy::OnSessionReadyInternal( 851 WebTransportSessionBase* aSession) { 852 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 853 LOG(("WebTransportSessionProxy::OnSessionReadyInternal")); 854 MutexAutoLock lock(mMutex); 855 switch (mState) { 856 case WebTransportSessionProxyState::INIT: 857 case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING: 858 case WebTransportSessionProxyState::ACTIVE: 859 case WebTransportSessionProxyState::SESSION_CLOSE_PENDING: 860 case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: 861 MOZ_ASSERT(false, 862 "OnSessionReadyInternal cannot be called in this state."); 863 return NS_ERROR_ABORT; 864 case WebTransportSessionProxyState::NEGOTIATING: 865 mWebTransportSession = aSession; 866 mSessionId = aSession->GetStreamId(); 867 ChangeState(WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED); 868 mWebTransportSession->StartReading(); 869 break; 870 case WebTransportSessionProxyState::DONE: 871 // The session has been canceled. We do not need to set 872 // mWebTransportSession. 873 break; 874 } 875 return NS_OK; 876 } 877 878 NS_IMETHODIMP 879 WebTransportSessionProxy::OnIncomingStreamAvailableInternal( 880 WebTransportStreamBase* aStream) { 881 nsCOMPtr<WebTransportSessionEventListener> listener; 882 { 883 MutexAutoLock lock(mMutex); 884 885 LOG( 886 ("WebTransportSessionProxy::OnIncomingStreamAvailableInternal %p " 887 "mState=%d " 888 "mStopRequestCalled=%d", 889 this, mState, mStopRequestCalled)); 890 // Since OnSessionReady on the listener is called on the main thread, 891 // OnIncomingStreamAvailableInternal and OnSessionReady can be racy. If 892 // OnStopRequest is not called yet, OnIncomingStreamAvailableInternal needs 893 // to wait. 894 if (!mStopRequestCalled) { 895 mPendingEvents.AppendElement( 896 [self = RefPtr{this}, stream = RefPtr{aStream}]() { 897 self->OnIncomingStreamAvailableInternal(stream); 898 }); 899 return NS_OK; 900 } 901 902 if (!mTarget->IsOnCurrentThread()) { 903 RefPtr<WebTransportSessionProxy> self(this); 904 RefPtr<WebTransportStreamBase> stream = aStream; 905 (void)mTarget->Dispatch(NS_NewRunnableFunction( 906 "WebTransportSessionProxy::OnIncomingStreamAvailableInternal", 907 [self{std::move(self)}, stream{std::move(stream)}]() { 908 self->OnIncomingStreamAvailableInternal(stream); 909 })); 910 return NS_OK; 911 } 912 913 LOG( 914 ("WebTransportSessionProxy::OnIncomingStreamAvailableInternal %p " 915 "mState=%d mListener=%p", 916 this, mState, mListener.get())); 917 if (mState == WebTransportSessionProxyState::ACTIVE) { 918 listener = mListener; 919 } 920 } 921 922 if (!listener) { 923 // Session can be already closed. 924 return NS_OK; 925 } 926 927 RefPtr<WebTransportStreamProxy> streamProxy = 928 new WebTransportStreamProxy(aStream); 929 if (aStream->StreamType() == WebTransportStreamType::BiDi) { 930 (void)listener->OnIncomingBidirectionalStreamAvailable(streamProxy); 931 } else { 932 (void)listener->OnIncomingUnidirectionalStreamAvailable(streamProxy); 933 } 934 return NS_OK; 935 } 936 937 NS_IMETHODIMP 938 WebTransportSessionProxy::OnIncomingBidirectionalStreamAvailable( 939 nsIWebTransportBidirectionalStream* aStream) { 940 return NS_OK; 941 } 942 943 NS_IMETHODIMP 944 WebTransportSessionProxy::OnIncomingUnidirectionalStreamAvailable( 945 nsIWebTransportReceiveStream* aStream) { 946 return NS_OK; 947 } 948 949 NS_IMETHODIMP 950 WebTransportSessionProxy::OnSessionReady(uint64_t ready) { 951 MOZ_ASSERT(false, "Should not be called"); 952 return NS_OK; 953 } 954 955 NS_IMETHODIMP 956 WebTransportSessionProxy::OnSessionClosed(bool aCleanly, uint32_t aStatus, 957 const nsACString& aReason) { 958 MOZ_ASSERT(OnSocketThread(), "not on socket thread"); 959 MutexAutoLock lock(mMutex); 960 LOG( 961 ("WebTransportSessionProxy::OnSessionClosed %p mState=%d " 962 "mStopRequestCalled=%d", 963 this, mState, mStopRequestCalled)); 964 // Since OnSessionReady on the listener is called on the main thread, 965 // OnSessionClosed and OnSessionReady can be racy. If OnStopRequest is not 966 // called yet, OnSessionClosed needs to wait. 967 if (!mStopRequestCalled) { 968 nsCString closeReason(aReason); 969 mPendingEvents.AppendElement([self = RefPtr{this}, status(aStatus), 970 closeReason(std::move(closeReason)), 971 cleanly(aCleanly)]() { 972 (void)self->OnSessionClosed(cleanly, status, closeReason); 973 }); 974 return NS_OK; 975 } 976 977 switch (mState) { 978 case WebTransportSessionProxyState::INIT: 979 case WebTransportSessionProxyState::NEGOTIATING: 980 case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING: 981 MOZ_ASSERT(false, "OnSessionClosed cannot be called in this state."); 982 return NS_ERROR_ABORT; 983 case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: 984 case WebTransportSessionProxyState::ACTIVE: { 985 mCleanly = aCleanly; 986 mCloseStatus = aStatus; 987 mReason = aReason; 988 mWebTransportSession = nullptr; 989 ChangeState(WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING); 990 CallOnSessionClosed(); 991 } break; 992 case WebTransportSessionProxyState::SESSION_CLOSE_PENDING: 993 ChangeState(WebTransportSessionProxyState::DONE); 994 break; 995 case WebTransportSessionProxyState::DONE: 996 // The session has been canceled. We do not need to set 997 // mWebTransportSession. 998 break; 999 } 1000 return NS_OK; 1001 } 1002 1003 void WebTransportSessionProxy::CallOnSessionClosedLocked() { 1004 MutexAutoLock lock(mMutex); 1005 CallOnSessionClosed(); 1006 } 1007 1008 void WebTransportSessionProxy::CallOnSessionClosed() MOZ_REQUIRES(mMutex) { 1009 mMutex.AssertCurrentThreadOwns(); 1010 1011 if (!mTarget->IsOnCurrentThread()) { 1012 RefPtr<WebTransportSessionProxy> self(this); 1013 (void)mTarget->Dispatch(NS_NewRunnableFunction( 1014 "WebTransportSessionProxy::CallOnSessionClosed", 1015 [self{std::move(self)}]() { self->CallOnSessionClosedLocked(); })); 1016 return; 1017 } 1018 1019 MOZ_ASSERT(mTarget->IsOnCurrentThread()); 1020 nsCOMPtr<WebTransportSessionEventListener> listener; 1021 bool cleanly = false; 1022 nsAutoCString reason; 1023 uint32_t closeStatus = 0; 1024 1025 switch (mState) { 1026 case WebTransportSessionProxyState::INIT: 1027 case WebTransportSessionProxyState::NEGOTIATING: 1028 case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: 1029 case WebTransportSessionProxyState::ACTIVE: 1030 case WebTransportSessionProxyState::SESSION_CLOSE_PENDING: 1031 MOZ_ASSERT(false, "CallOnSessionClosed cannot be called in this state."); 1032 break; 1033 case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING: 1034 listener = mListener; 1035 mListener = nullptr; 1036 cleanly = mCleanly; 1037 reason = mReason; 1038 closeStatus = mCloseStatus; 1039 ChangeState(WebTransportSessionProxyState::DONE); 1040 break; 1041 case WebTransportSessionProxyState::DONE: 1042 break; 1043 } 1044 1045 if (listener) { 1046 // Don't invoke the callback under the lock. 1047 MutexAutoUnlock unlock(mMutex); 1048 listener->OnSessionClosed(cleanly, closeStatus, reason); 1049 } 1050 } 1051 1052 void WebTransportSessionProxy::ChangeState( 1053 WebTransportSessionProxyState newState) { 1054 mMutex.AssertCurrentThreadOwns(); 1055 LOG(("WebTransportSessionProxy::ChangeState %d -> %d [this=%p]", mState, 1056 newState, this)); 1057 switch (newState) { 1058 case WebTransportSessionProxyState::INIT: 1059 MOZ_ASSERT(false, "Cannot change into INIT sate."); 1060 break; 1061 case WebTransportSessionProxyState::NEGOTIATING: 1062 MOZ_ASSERT(mState == WebTransportSessionProxyState::INIT, 1063 "Only from INIT can be change into NEGOTIATING"); 1064 MOZ_ASSERT(mChannel); 1065 MOZ_ASSERT(mListener); 1066 break; 1067 case WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED: 1068 MOZ_ASSERT( 1069 mState == WebTransportSessionProxyState::NEGOTIATING, 1070 "Only from NEGOTIATING can be change into NEGOTIATING_SUCCEEDED"); 1071 MOZ_ASSERT(mChannel); 1072 MOZ_ASSERT(mWebTransportSession); 1073 MOZ_ASSERT(mListener); 1074 break; 1075 case WebTransportSessionProxyState::ACTIVE: 1076 MOZ_ASSERT(mState == WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED, 1077 "Only from NEGOTIATING_SUCCEEDED can be change into ACTIVE"); 1078 MOZ_ASSERT(!mChannel); 1079 MOZ_ASSERT(mWebTransportSession); 1080 MOZ_ASSERT(mListener); 1081 break; 1082 case WebTransportSessionProxyState::SESSION_CLOSE_PENDING: 1083 MOZ_ASSERT( 1084 (mState == WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED) || 1085 (mState == WebTransportSessionProxyState::ACTIVE), 1086 "Only from NEGOTIATING_SUCCEEDED and ACTIVE can be change into" 1087 " SESSION_CLOSE_PENDING"); 1088 MOZ_ASSERT(!mChannel); 1089 MOZ_ASSERT(mWebTransportSession); 1090 MOZ_ASSERT(!mListener); 1091 break; 1092 case WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING: 1093 MOZ_ASSERT( 1094 (mState == WebTransportSessionProxyState::NEGOTIATING_SUCCEEDED) || 1095 (mState == WebTransportSessionProxyState::ACTIVE), 1096 "Only from NEGOTIATING_SUCCEEDED and ACTIVE can be change into" 1097 " CLOSE_CALLBACK_PENDING"); 1098 MOZ_ASSERT(!mWebTransportSession); 1099 MOZ_ASSERT(mListener); 1100 break; 1101 case WebTransportSessionProxyState::DONE: 1102 MOZ_ASSERT( 1103 (mState == WebTransportSessionProxyState::NEGOTIATING) || 1104 (mState == 1105 WebTransportSessionProxyState::SESSION_CLOSE_PENDING) || 1106 (mState == WebTransportSessionProxyState::CLOSE_CALLBACK_PENDING), 1107 "Only from NEGOTIATING, SESSION_CLOSE_PENDING and " 1108 "CLOSE_CALLBACK_PENDING can be change into DONE"); 1109 MOZ_ASSERT(!mChannel); 1110 MOZ_ASSERT(!mWebTransportSession); 1111 MOZ_ASSERT(!mListener); 1112 break; 1113 } 1114 mState = newState; 1115 } 1116 1117 void WebTransportSessionProxy::NotifyDatagramReceived( 1118 nsTArray<uint8_t>&& aData) { 1119 nsCOMPtr<WebTransportSessionEventListener> listener; 1120 { 1121 MutexAutoLock lock(mMutex); 1122 MOZ_ASSERT(mTarget->IsOnCurrentThread()); 1123 1124 if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) { 1125 return; 1126 } 1127 listener = mListener; 1128 } 1129 1130 listener->OnDatagramReceived(aData); 1131 } 1132 1133 NS_IMETHODIMP WebTransportSessionProxy::OnDatagramReceivedInternal( 1134 nsTArray<uint8_t>&& aData) { 1135 MOZ_ASSERT(OnSocketThread()); 1136 1137 { 1138 MutexAutoLock lock(mMutex); 1139 if (!mStopRequestCalled) { 1140 CopyableTArray<uint8_t> copied(aData); 1141 mPendingEvents.AppendElement( 1142 [self = RefPtr{this}, data = std::move(copied)]() mutable { 1143 self->OnDatagramReceivedInternal(std::move(data)); 1144 }); 1145 return NS_OK; 1146 } 1147 1148 if (!mTarget->IsOnCurrentThread()) { 1149 return mTarget->Dispatch(NS_NewRunnableFunction( 1150 "WebTransportSessionProxy::OnDatagramReceived", 1151 [self = RefPtr{this}, data{std::move(aData)}]() mutable { 1152 self->NotifyDatagramReceived(std::move(data)); 1153 })); 1154 } 1155 } 1156 1157 NotifyDatagramReceived(std::move(aData)); 1158 return NS_OK; 1159 } 1160 1161 NS_IMETHODIMP WebTransportSessionProxy::OnDatagramReceived( 1162 const nsTArray<uint8_t>& aData) { 1163 return NS_ERROR_NOT_IMPLEMENTED; 1164 } 1165 1166 void WebTransportSessionProxy::OnMaxDatagramSizeInternal(uint64_t aSize) { 1167 nsCOMPtr<WebTransportSessionEventListener> listener; 1168 { 1169 MutexAutoLock lock(mMutex); 1170 MOZ_ASSERT(mTarget->IsOnCurrentThread()); 1171 1172 if (!mStopRequestCalled) { 1173 mPendingEvents.AppendElement([self = RefPtr{this}, size(aSize)]() { 1174 self->OnMaxDatagramSizeInternal(size); 1175 }); 1176 return; 1177 } 1178 1179 if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) { 1180 return; 1181 } 1182 listener = mListener; 1183 } 1184 1185 listener->OnMaxDatagramSize(aSize); 1186 } 1187 1188 NS_IMETHODIMP WebTransportSessionProxy::OnMaxDatagramSize(uint64_t aSize) { 1189 MOZ_ASSERT(OnSocketThread()); 1190 1191 { 1192 MutexAutoLock lock(mMutex); 1193 if (!mTarget->IsOnCurrentThread()) { 1194 return mTarget->Dispatch( 1195 NS_NewRunnableFunction("WebTransportSessionProxy::OnMaxDatagramSize", 1196 [self = RefPtr{this}, size(aSize)] { 1197 self->OnMaxDatagramSizeInternal(size); 1198 })); 1199 } 1200 } 1201 1202 OnMaxDatagramSizeInternal(aSize); 1203 return NS_OK; 1204 } 1205 1206 void WebTransportSessionProxy::OnOutgoingDatagramOutComeInternal( 1207 uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome) { 1208 nsCOMPtr<WebTransportSessionEventListener> listener; 1209 { 1210 MutexAutoLock lock(mMutex); 1211 MOZ_ASSERT(mTarget->IsOnCurrentThread()); 1212 if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) { 1213 return; 1214 } 1215 listener = mListener; 1216 } 1217 1218 listener->OnOutgoingDatagramOutCome(aId, aOutCome); 1219 } 1220 1221 NS_IMETHODIMP 1222 WebTransportSessionProxy::OnOutgoingDatagramOutCome( 1223 uint64_t aId, WebTransportSessionEventListener::DatagramOutcome aOutCome) { 1224 MOZ_ASSERT(OnSocketThread()); 1225 1226 { 1227 MutexAutoLock lock(mMutex); 1228 if (!mTarget->IsOnCurrentThread()) { 1229 return mTarget->Dispatch(NS_NewRunnableFunction( 1230 "WebTransportSessionProxy::OnOutgoingDatagramOutCome", 1231 [self = RefPtr{this}, id(aId), outcome(aOutCome)] { 1232 self->OnOutgoingDatagramOutComeInternal(id, outcome); 1233 })); 1234 } 1235 } 1236 1237 OnOutgoingDatagramOutComeInternal(aId, aOutCome); 1238 return NS_OK; 1239 } 1240 1241 NS_IMETHODIMP WebTransportSessionProxy::OnStopSending(uint64_t aStreamId, 1242 nsresult aError) { 1243 MOZ_ASSERT(OnSocketThread()); 1244 nsCOMPtr<WebTransportSessionEventListener> listener; 1245 { 1246 MutexAutoLock lock(mMutex); 1247 MOZ_ASSERT(mTarget->IsOnCurrentThread()); 1248 1249 if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) { 1250 return NS_OK; 1251 } 1252 listener = mListener; 1253 } 1254 1255 listener->OnStopSending(aStreamId, aError); 1256 return NS_OK; 1257 } 1258 1259 NS_IMETHODIMP WebTransportSessionProxy::OnResetReceived(uint64_t aStreamId, 1260 nsresult aError) { 1261 MOZ_ASSERT(OnSocketThread()); 1262 nsCOMPtr<WebTransportSessionEventListener> listener; 1263 { 1264 MutexAutoLock lock(mMutex); 1265 MOZ_ASSERT(mTarget->IsOnCurrentThread()); 1266 1267 if (mState != WebTransportSessionProxyState::ACTIVE || !mListener) { 1268 return NS_OK; 1269 } 1270 listener = mListener; 1271 } 1272 1273 listener->OnResetReceived(aStreamId, aError); 1274 return NS_OK; 1275 } 1276 1277 } // namespace mozilla::net