tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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