tor-browser

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

WebTransportParent.cpp (30505B)


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