tor-browser

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

Http3Session.cpp (108918B)


      1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=4 sw=2 et cindent: */
      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 "ASpdySession.h"  // because of SoftStreamError()
      8 #include "Http3Session.h"
      9 #include "Http3Stream.h"
     10 #include "Http3StreamBase.h"
     11 #include "Http3WebTransportSession.h"
     12 #include "Http3ConnectUDPStream.h"
     13 #include "Http3StreamTunnel.h"
     14 #include "Http3WebTransportStream.h"
     15 #include "HttpConnectionUDP.h"
     16 #include "HttpLog.h"
     17 #include "QuicSocketControl.h"
     18 #include "SSLServerCertVerification.h"
     19 #include "SSLTokensCache.h"
     20 #include "ScopedNSSTypes.h"
     21 #include "mozilla/RandomNum.h"
     22 #include "mozilla/RefPtr.h"
     23 #include "mozilla/ScopeExit.h"
     24 #include "mozilla/glean/NetwerkMetrics.h"
     25 #include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
     26 #include "mozilla/net/DNS.h"
     27 #include "nsHttpHandler.h"
     28 #include "nsIHttpActivityObserver.h"
     29 #include "nsIOService.h"
     30 #include "nsITLSSocketControl.h"
     31 #include "nsNetAddr.h"
     32 #include "nsQueryObject.h"
     33 #include "nsSocketTransportService2.h"
     34 #include "nsThreadUtils.h"
     35 #include "sslerr.h"
     36 #include "WebTransportCertificateVerifier.h"
     37 
     38 namespace mozilla::net {
     39 
     40 extern const nsCString& TRRProviderKey();
     41 
     42 const uint64_t HTTP3_APP_ERROR_NO_ERROR = 0x100;
     43 // const uint64_t HTTP3_APP_ERROR_GENERAL_PROTOCOL_ERROR = 0x101;
     44 // const uint64_t HTTP3_APP_ERROR_INTERNAL_ERROR = 0x102;
     45 // const uint64_t HTTP3_APP_ERROR_STREAM_CREATION_ERROR = 0x103;
     46 // const uint64_t HTTP3_APP_ERROR_CLOSED_CRITICAL_STREAM = 0x104;
     47 // const uint64_t HTTP3_APP_ERROR_FRAME_UNEXPECTED = 0x105;
     48 // const uint64_t HTTP3_APP_ERROR_FRAME_ERROR = 0x106;
     49 // const uint64_t HTTP3_APP_ERROR_EXCESSIVE_LOAD = 0x107;
     50 // const uint64_t HTTP3_APP_ERROR_ID_ERROR = 0x108;
     51 // const uint64_t HTTP3_APP_ERROR_SETTINGS_ERROR = 0x109;
     52 // const uint64_t HTTP3_APP_ERROR_MISSING_SETTINGS = 0x10a;
     53 const uint64_t HTTP3_APP_ERROR_REQUEST_REJECTED = 0x10b;
     54 const uint64_t HTTP3_APP_ERROR_REQUEST_CANCELLED = 0x10c;
     55 // const uint64_t HTTP3_APP_ERROR_REQUEST_INCOMPLETE = 0x10d;
     56 // const uint64_t HTTP3_APP_ERROR_EARLY_RESPONSE = 0x10e;
     57 // const uint64_t HTTP3_APP_ERROR_CONNECT_ERROR = 0x10f;
     58 const uint64_t HTTP3_APP_ERROR_VERSION_FALLBACK = 0x110;
     59 
     60 // const uint32_t UDP_MAX_PACKET_SIZE = 4096;
     61 const uint32_t MAX_PTO_COUNTS = 16;
     62 
     63 const uint32_t TRANSPORT_ERROR_STATELESS_RESET = 20;
     64 
     65 NS_IMPL_ADDREF_INHERITED(Http3Session, nsAHttpConnection)
     66 NS_IMPL_RELEASE_INHERITED(Http3Session, nsAHttpConnection)
     67 NS_INTERFACE_MAP_BEGIN(Http3Session)
     68  NS_INTERFACE_MAP_ENTRY(nsAHttpConnection)
     69  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     70  NS_INTERFACE_MAP_ENTRY_CONCRETE(Http3Session)
     71 NS_INTERFACE_MAP_END
     72 
     73 Http3Session::Http3Session() {
     74  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
     75  LOG(("Http3Session::Http3Session [this=%p]", this));
     76 
     77  mCurrentBrowserId = gHttpHandler->ConnMgr()->CurrentBrowserId();
     78 }
     79 
     80 static nsresult RawBytesToNetAddr(uint16_t aFamily, const uint8_t* aRemoteAddr,
     81                                  uint16_t remotePort, NetAddr* netAddr) {
     82  if (aFamily == AF_INET) {
     83    netAddr->inet.family = AF_INET;
     84    netAddr->inet.port = htons(remotePort);
     85    memcpy(&netAddr->inet.ip, aRemoteAddr, 4);
     86  } else if (aFamily == AF_INET6) {
     87    netAddr->inet6.family = AF_INET6;
     88    netAddr->inet6.port = htons(remotePort);
     89    memcpy(&netAddr->inet6.ip.u8, aRemoteAddr, 16);
     90  } else {
     91    return NS_ERROR_UNEXPECTED;
     92  }
     93 
     94  return NS_OK;
     95 }
     96 
     97 nsresult Http3Session::Init(const nsHttpConnectionInfo* aConnInfo,
     98                            nsINetAddr* aSelfAddr, nsINetAddr* aPeerAddr,
     99                            HttpConnectionUDP* udpConn, uint32_t aProviderFlags,
    100                            nsIInterfaceRequestor* callbacks,
    101                            nsIUDPSocket* socket, bool aIsTunnel) {
    102  LOG3(("Http3Session::Init %p", this));
    103 
    104  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    105  MOZ_ASSERT(udpConn);
    106 
    107  mConnInfo = aConnInfo->Clone();
    108  mNetAddr = aPeerAddr;
    109 
    110  // When `isOuterConnection` is true, this Http3Session represents the *outer*
    111  // connection between Firefox and the proxy (e.g., when using CONNECT-UDP).
    112  //
    113  // We track this flag for two main reasons:
    114  // 1. To select the correct hostname during TLS negotiation on the outer
    115  //    connection.
    116  // 2. To explicitly enable Path MTU Discovery (PMTUD) on the outer connection,
    117  //    since the outer path’s MTU must be at least as large as the inner one.
    118  bool isOuterConnection = false;
    119  if (!aIsTunnel) {
    120    if (auto* proxyInfo = aConnInfo->ProxyInfo()) {
    121      isOuterConnection = proxyInfo->IsHttp3Proxy();
    122    }
    123  }
    124 
    125  // Create security control and info object for quic.
    126  mSocketControl =
    127      new QuicSocketControl(isOuterConnection ? aConnInfo->ProxyInfo()->Host()
    128                                              : aConnInfo->GetOrigin(),
    129                            isOuterConnection ? aConnInfo->ProxyInfo()->Port()
    130                                              : aConnInfo->OriginPort(),
    131                            aProviderFlags, this);
    132  const nsCString& alpn = isOuterConnection ? aConnInfo->GetProxyNPNToken()
    133                                            : aConnInfo->GetNPNToken();
    134 
    135  NetAddr selfAddr;
    136  MOZ_ALWAYS_SUCCEEDS(aSelfAddr->GetNetAddr(&selfAddr));
    137  NetAddr peerAddr;
    138  MOZ_ALWAYS_SUCCEEDS(aPeerAddr->GetNetAddr(&peerAddr));
    139 
    140  LOG3(
    141      ("Http3Session::Init origin=%s, alpn=%s, selfAddr=%s, peerAddr=%s,"
    142       " qpack table size=%u, max blocked streams=%u webtransport=%d "
    143       "[this=%p]",
    144       PromiseFlatCString(mSocketControl->GetHostName()).get(),
    145       PromiseFlatCString(alpn).get(), selfAddr.ToString().get(),
    146       peerAddr.ToString().get(), gHttpHandler->DefaultQpackTableSize(),
    147       gHttpHandler->DefaultHttp3MaxBlockedStreams(),
    148       mConnInfo->GetWebTransport(), this));
    149 
    150  if (mConnInfo->GetWebTransport()) {
    151    ExtState(ExtendedConnectKind::WebTransport).mStatus = NEGOTIATING;
    152  }
    153  if (isOuterConnection) {
    154    ExtState(ExtendedConnectKind::ConnectUDP).mStatus = NEGOTIATING;
    155  }
    156 
    157  mUseNSPRForIO =
    158      StaticPrefs::network_http_http3_use_nspr_for_io() || aIsTunnel;
    159 
    160  uint32_t idleTimeout =
    161      mConnInfo->GetIsTrrServiceChannel()
    162          ? StaticPrefs::network_trr_idle_timeout_for_http3_conn()
    163          : StaticPrefs::network_http_http3_idle_timeout();
    164 
    165  nsresult rv;
    166  if (mUseNSPRForIO) {
    167    rv = NeqoHttp3Conn::InitUseNSPRForIO(
    168        mSocketControl->GetHostName(), alpn, selfAddr, peerAddr,
    169        gHttpHandler->DefaultQpackTableSize(),
    170        gHttpHandler->DefaultHttp3MaxBlockedStreams(),
    171        StaticPrefs::network_http_http3_max_data(),
    172        StaticPrefs::network_http_http3_max_stream_data(),
    173        StaticPrefs::network_http_http3_version_negotiation_enabled(),
    174        mConnInfo->GetWebTransport(), gHttpHandler->Http3QlogDir(),
    175        aProviderFlags, idleTimeout, getter_AddRefs(mHttp3Connection));
    176  } else {
    177    rv = NeqoHttp3Conn::Init(
    178        mSocketControl->GetHostName(), alpn, selfAddr, peerAddr,
    179        gHttpHandler->DefaultQpackTableSize(),
    180        gHttpHandler->DefaultHttp3MaxBlockedStreams(),
    181        StaticPrefs::network_http_http3_max_data(),
    182        StaticPrefs::network_http_http3_max_stream_data(),
    183        StaticPrefs::network_http_http3_version_negotiation_enabled(),
    184        mConnInfo->GetWebTransport(), gHttpHandler->Http3QlogDir(),
    185        aProviderFlags, idleTimeout, socket->GetFileDescriptor(),
    186        isOuterConnection, getter_AddRefs(mHttp3Connection));
    187  }
    188  if (NS_FAILED(rv)) {
    189    return rv;
    190  }
    191 
    192  nsAutoCString peerId;
    193  mSocketControl->GetPeerId(peerId);
    194  nsTArray<uint8_t> token;
    195  SessionCacheInfo info;
    196  udpConn->ChangeConnectionState(ConnectionState::TLS_HANDSHAKING);
    197 
    198  auto hasServCertHashes = [&]() -> bool {
    199    if (!mConnInfo->GetWebTransport()) {
    200      return false;
    201    }
    202    const nsTArray<RefPtr<nsIWebTransportHash>>* servCertHashes =
    203        gHttpHandler->ConnMgr()->GetServerCertHashes(mConnInfo);
    204    return servCertHashes && !servCertHashes->IsEmpty();
    205  };
    206 
    207  // See https://github.com/mozilla/neqo/issues/2442.
    208  // We need to set ECH first before set resumption token.
    209  auto config = mConnInfo->GetEchConfig();
    210  if (config.IsEmpty()) {
    211    if (StaticPrefs::security_tls_ech_grease_http3() && config.IsEmpty()) {
    212      if ((RandomUint64().valueOr(0) % 100) >=
    213          100 - StaticPrefs::security_tls_ech_grease_probability()) {
    214        // Setting an empty config enables GREASE mode.
    215        mSocketControl->SetEchConfig(config);
    216        mEchExtensionStatus = EchExtensionStatus::kGREASE;
    217      }
    218    }
    219  } else if (nsHttpHandler::EchConfigEnabled(true) && !config.IsEmpty()) {
    220    mSocketControl->SetEchConfig(config);
    221    mEchExtensionStatus = EchExtensionStatus::kReal;
    222    HttpConnectionActivity activity(
    223        mConnInfo->HashKey(), mConnInfo->GetOrigin(), mConnInfo->OriginPort(),
    224        mConnInfo->EndToEndSSL(), !mConnInfo->GetEchConfig().IsEmpty(),
    225        mConnInfo->IsHttp3());
    226    gHttpHandler->ObserveHttpActivityWithArgs(
    227        activity, NS_ACTIVITY_TYPE_HTTP_CONNECTION,
    228        NS_HTTP_ACTIVITY_SUBTYPE_ECH_SET, PR_Now(), 0, ""_ns);
    229  } else {
    230    mEchExtensionStatus = EchExtensionStatus::kNotPresent;
    231  }
    232 
    233  // In WebTransport, when servCertHashes is specified, it indicates that the
    234  // connection to the WebTransport server should authenticate using the
    235  // expected certificate hash. Therefore, 0RTT should be disabled in this
    236  // context to ensure the certificate hash is checked.
    237  if (StaticPrefs::network_http_http3_enable_0rtt() && !hasServCertHashes() &&
    238      NS_SUCCEEDED(SSLTokensCache::Get(peerId, token, info))) {
    239    LOG(("Found a resumption token in the cache."));
    240    mHttp3Connection->SetResumptionToken(token);
    241    mSocketControl->SetSessionCacheInfo(std::move(info));
    242    if (mHttp3Connection->IsZeroRtt()) {
    243      LOG(("Can send ZeroRtt data"));
    244      RefPtr<Http3Session> self(this);
    245      mState = ZERORTT;
    246      udpConn->ChangeConnectionState(ConnectionState::ZERORTT);
    247      mZeroRttStarted = TimeStamp::Now();
    248      // Let the nsHttpConnectionMgr know that the connection can accept
    249      // transactions.
    250      // We need to dispatch the following function to this thread so that
    251      // it is executed after the current function. At this point a
    252      // Http3Session is still being initialized and ReportHttp3Connection
    253      // will try to dispatch transaction on this session therefore it
    254      // needs to be executed after the initializationg is done.
    255      DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(
    256          NS_NewRunnableFunction("Http3Session::ReportHttp3Connection",
    257                                 [self]() { self->ReportHttp3Connection(); }));
    258      NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
    259                           "NS_DispatchToCurrentThread failed");
    260    }
    261  }
    262 
    263 #ifndef ANDROID
    264  if (mState != ZERORTT) {
    265    ZeroRttTelemetry(ZeroRttOutcome::NOT_USED);
    266  }
    267 #endif
    268 
    269  // After this line, Http3Session and HttpConnectionUDP become a cycle. We put
    270  // this line in the end of Http3Session::Init to make sure Http3Session can be
    271  // released when Http3Session::Init early returned.
    272  mUdpConn = udpConn;
    273  return NS_OK;
    274 }
    275 
    276 void Http3Session::DoSetEchConfig(const nsACString& aEchConfig) {
    277  LOG(("Http3Session::DoSetEchConfig %p of length %zu", this,
    278       aEchConfig.Length()));
    279  nsTArray<uint8_t> config;
    280  config.AppendElements(
    281      reinterpret_cast<const uint8_t*>(aEchConfig.BeginReading()),
    282      aEchConfig.Length());
    283  mHttp3Connection->SetEchConfig(config);
    284 }
    285 
    286 nsresult Http3Session::SendPriorityUpdateFrame(uint64_t aStreamId,
    287                                               uint8_t aPriorityUrgency,
    288                                               bool aPriorityIncremental) {
    289  return mHttp3Connection->PriorityUpdate(aStreamId, aPriorityUrgency,
    290                                          aPriorityIncremental);
    291 }
    292 
    293 // Shutdown the http3session and close all transactions.
    294 void Http3Session::Shutdown() {
    295  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    296 
    297  if (mTimer) {
    298    mTimer->Cancel();
    299  }
    300  mTimer = nullptr;
    301  mTimerCallback = nullptr;
    302 
    303  bool isEchRetry = mError == mozilla::psm::GetXPCOMFromNSSError(
    304                                  SSL_ERROR_ECH_RETRY_WITH_ECH);
    305  bool isNSSError = psm::IsNSSErrorCode(-1 * NS_ERROR_GET_CODE(mError));
    306  bool allowToRetryWithDifferentIPFamily =
    307      mBeforeConnectedError &&
    308      gHttpHandler->ConnMgr()->AllowToRetryDifferentIPFamilyForHttp3(mConnInfo,
    309                                                                     mError);
    310  LOG(("Http3Session::Shutdown %p allowToRetryWithDifferentIPFamily=%d", this,
    311       allowToRetryWithDifferentIPFamily));
    312  if ((mBeforeConnectedError ||
    313       (mError == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR)) &&
    314      !isNSSError && !isEchRetry && !mConnInfo->GetWebTransport() &&
    315      !allowToRetryWithDifferentIPFamily && !mDontExclude) {
    316    gHttpHandler->ExcludeHttp3(mConnInfo);
    317    if (mFirstHttpTransaction) {
    318      mFirstHttpTransaction->DisableHttp3(false);
    319    }
    320  }
    321 
    322  for (const auto& stream : mStreamTransactionHash.Values()) {
    323    if (mBeforeConnectedError) {
    324      // We have an error before we were connected, just restart transactions.
    325      // The transaction restart code path will remove AltSvc mapping and the
    326      // direct path will be used.
    327      MOZ_ASSERT(NS_FAILED(mError));
    328      if (isEchRetry) {
    329        // We have to propagate this error to nsHttpTransaction, so the
    330        // transaction will be restarted with a new echConfig.
    331        stream->Close(mError);
    332      } else if (isNSSError) {
    333        stream->Close(mError);
    334      } else {
    335        if (allowToRetryWithDifferentIPFamily && mNetAddr) {
    336          NetAddr addr;
    337          mNetAddr->GetNetAddr(&addr);
    338          gHttpHandler->ConnMgr()->SetRetryDifferentIPFamilyForHttp3(
    339              mConnInfo, addr.raw.family);
    340          // We want the transaction to be restarted with the same connection
    341          // info.
    342          stream->Transaction()->DoNotRemoveAltSvc();
    343          // We already set the preference in SetRetryDifferentIPFamilyForHttp3,
    344          // so we want to keep it for the next retry.
    345          stream->Transaction()->DoNotResetIPFamilyPreference();
    346          stream->Close(NS_ERROR_NET_RESET);
    347          // Since Http3Session::Shutdown can be called multiple times, we set
    348          // mDontExclude for not putting this domain into the excluded list.
    349          mDontExclude = true;
    350        } else {
    351          stream->Close(NS_ERROR_NET_RESET);
    352        }
    353      }
    354    } else if (!stream->HasStreamId()) {
    355      if (NS_SUCCEEDED(mError)) {
    356        // Connection has not been started yet. We can restart it.
    357        stream->Transaction()->DoNotRemoveAltSvc();
    358      }
    359      stream->Close(NS_ERROR_NET_RESET);
    360    } else if (stream->GetHttp3Stream() &&
    361               stream->GetHttp3Stream()->RecvdData()) {
    362      stream->Close(NS_ERROR_NET_PARTIAL_TRANSFER);
    363    } else if (mError == NS_ERROR_NET_HTTP3_PROTOCOL_ERROR) {
    364      stream->Close(NS_ERROR_NET_HTTP3_PROTOCOL_ERROR);
    365    } else if (mError == NS_ERROR_NET_RESET) {
    366      stream->Close(NS_ERROR_NET_RESET);
    367    } else {
    368      stream->Close(NS_ERROR_ABORT);
    369    }
    370    RemoveStreamFromQueues(stream);
    371    if (stream->HasStreamId()) {
    372      mStreamIdHash.Remove(stream->StreamId());
    373    }
    374  }
    375  mStreamTransactionHash.Clear();
    376 
    377  for (const auto& stream : mWebTransportSessions) {
    378    stream->Close(NS_ERROR_ABORT);
    379    RemoveStreamFromQueues(stream);
    380    mStreamIdHash.Remove(stream->StreamId());
    381  }
    382  mWebTransportSessions.Clear();
    383 
    384  for (const auto& stream : mTunnelStreams) {
    385    stream->Close(NS_ERROR_ABORT);
    386    RemoveStreamFromQueues(stream);
    387    mStreamIdHash.Remove(stream->StreamId());
    388  }
    389  mTunnelStreams.Clear();
    390 
    391  for (const auto& stream : mWebTransportStreams) {
    392    stream->Close(NS_ERROR_ABORT);
    393    RemoveStreamFromQueues(stream);
    394    mStreamIdHash.Remove(stream->StreamId());
    395  }
    396 
    397  RefPtr<Http3StreamBase> stream;
    398  while ((stream = mQueuedStreams.PopFront())) {
    399    LOG(("Close remaining stream in queue:%p", stream.get()));
    400    stream->SetQueued(false);
    401    stream->Close(NS_ERROR_ABORT);
    402  }
    403  mWebTransportStreams.Clear();
    404 }
    405 
    406 Http3Session::~Http3Session() {
    407  LOG3(("Http3Session::~Http3Session %p", this));
    408 #ifndef ANDROID
    409  EchOutcomeTelemetry();
    410 #endif
    411  glean::http3::request_per_conn.AccumulateSingleSample(mTransactionCount);
    412  glean::http3::blocked_by_stream_limit_per_conn.AccumulateSingleSample(
    413      mBlockedByStreamLimitCount);
    414  glean::http3::trans_blocked_by_stream_limit_per_conn.AccumulateSingleSample(
    415      mTransactionsBlockedByStreamLimitCount);
    416 
    417  glean::http3::trans_sending_blocked_by_flow_control_per_conn
    418      .AccumulateSingleSample(mTransactionsSenderBlockedByFlowControlCount);
    419 
    420  if (mTrrStreams) {
    421    mozilla::glean::networking::trr_request_count_per_conn
    422        .Get(nsPrintfCString("%s_h3", mConnInfo->Origin()))
    423        .Add(static_cast<int32_t>(mTrrStreams));
    424  }
    425 
    426  Shutdown();
    427 }
    428 
    429 // This function may return a socket error.
    430 // It will not return an error if socket error is
    431 // NS_BASE_STREAM_WOULD_BLOCK.
    432 // A caller of this function will close the Http3 connection
    433 // in case of an error.
    434 // The only callers is Http3Session::RecvData.
    435 nsresult Http3Session::ProcessInput(nsIUDPSocket* socket) {
    436  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    437  MOZ_ASSERT(mUdpConn);
    438 
    439  LOG(("Http3Session::ProcessInput writer=%p [this=%p state=%d]",
    440       mUdpConn.get(), this, mState));
    441 
    442  if (!socket || socket->IsSocketClosed()) {
    443    MOZ_DIAGNOSTIC_ASSERT(false, "UDP socket should still be open");
    444    return NS_ERROR_UNEXPECTED;
    445  }
    446 
    447  if (mUseNSPRForIO) {
    448    while (true) {
    449      nsTArray<uint8_t> data;
    450      NetAddr addr{};
    451      // RecvWithAddr actually does not return an error.
    452      nsresult rv = socket->RecvWithAddr(&addr, data);
    453      MOZ_ALWAYS_SUCCEEDS(rv);
    454      if (NS_FAILED(rv) || data.IsEmpty()) {
    455        break;
    456      }
    457      rv = mHttp3Connection->ProcessInputUseNSPRForIO(addr, data);
    458      MOZ_ALWAYS_SUCCEEDS(rv);
    459      if (NS_FAILED(rv)) {
    460        break;
    461      }
    462 
    463      LOG(("Http3Session::ProcessInput received=%zu", data.Length()));
    464      mTotalBytesRead += static_cast<int64_t>(data.Length());
    465    }
    466 
    467    return NS_OK;
    468  }
    469 
    470  // Not using NSPR.
    471 
    472  auto rv = mHttp3Connection->ProcessInput();
    473  // Note: WOULD_BLOCK is handled in neqo_glue.
    474  if (NS_FAILED(rv.result)) {
    475    mSocketError = rv.result;
    476    // If there was an error return from here. We do not need to set a timer,
    477    // because we will close the connection.
    478    return rv.result;
    479  }
    480  mTotalBytesRead += rv.bytes_read;
    481  socket->AddInputBytes(rv.bytes_read);
    482 
    483  return NS_OK;
    484 }
    485 
    486 nsresult Http3Session::ProcessTransactionRead(uint64_t stream_id) {
    487  RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(stream_id);
    488  if (!stream) {
    489    LOG(
    490        ("Http3Session::ProcessTransactionRead - stream not found "
    491         "stream_id=0x%" PRIx64 " [this=%p].",
    492         stream_id, this));
    493    return NS_OK;
    494  }
    495 
    496  return ProcessTransactionRead(stream);
    497 }
    498 
    499 nsresult Http3Session::ProcessTransactionRead(Http3StreamBase* stream) {
    500  nsresult rv = stream->WriteSegments();
    501 
    502  if (ASpdySession::SoftStreamError(rv) || stream->Done()) {
    503    LOG3(
    504        ("Http3Session::ProcessSingleTransactionRead session=%p stream=%p "
    505         "0x%" PRIx64 " cleanup stream rv=0x%" PRIx32 " done=%d.\n",
    506         this, stream, stream->StreamId(), static_cast<uint32_t>(rv),
    507         stream->Done()));
    508    CloseStream(stream,
    509                (rv == NS_BINDING_RETARGETED) ? NS_BINDING_RETARGETED : NS_OK);
    510    return NS_OK;
    511  }
    512 
    513  if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
    514    return rv;
    515  }
    516  return NS_OK;
    517 }
    518 
    519 nsresult Http3Session::ProcessEvents() {
    520  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
    521 
    522  LOG(("Http3Session::ProcessEvents [this=%p]", this));
    523 
    524  // We need an array to pick up header data or a resumption token.
    525  nsTArray<uint8_t> data;
    526  Http3Event event{};
    527  event.tag = Http3Event::Tag::NoEvent;
    528 
    529  nsresult rv = mHttp3Connection->GetEvent(&event, data);
    530  if (NS_FAILED(rv)) {
    531    LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
    532         static_cast<uint32_t>(rv)));
    533    return rv;
    534  }
    535 
    536  while (event.tag != Http3Event::Tag::NoEvent) {
    537    switch (event.tag) {
    538      case Http3Event::Tag::HeaderReady: {
    539        MOZ_ASSERT(mState == CONNECTED);
    540        LOG(("Http3Session::ProcessEvents - HeaderReady"));
    541        uint64_t id = event.header_ready.stream_id;
    542 
    543        RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(id);
    544        if (!stream) {
    545          LOG(
    546              ("Http3Session::ProcessEvents - HeaderReady - stream not found "
    547               "stream_id=0x%" PRIx64 " [this=%p].",
    548               id, this));
    549          break;
    550        }
    551 
    552        MOZ_RELEASE_ASSERT(stream->GetHttp3Stream(),
    553                           "This must be a Http3Stream");
    554 
    555        stream->SetResponseHeaders(data, event.header_ready.fin,
    556                                   event.header_ready.interim);
    557 
    558        rv = ProcessTransactionRead(stream);
    559 
    560        if (NS_FAILED(rv)) {
    561          LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
    562               static_cast<uint32_t>(rv)));
    563          return rv;
    564        }
    565 
    566        mUdpConn->NotifyDataRead();
    567        break;
    568      }
    569      case Http3Event::Tag::DataReadable: {
    570        MOZ_ASSERT(mState == CONNECTED);
    571        LOG(("Http3Session::ProcessEvents - DataReadable"));
    572        uint64_t id = event.data_readable.stream_id;
    573 
    574        nsresult rv = ProcessTransactionRead(id);
    575 
    576        if (NS_FAILED(rv)) {
    577          LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
    578               static_cast<uint32_t>(rv)));
    579          return rv;
    580        }
    581        break;
    582      }
    583      case Http3Event::Tag::DataWritable: {
    584        MOZ_ASSERT(CanSendData());
    585        LOG(("Http3Session::ProcessEvents - DataWritable"));
    586 
    587        RefPtr<Http3StreamBase> stream =
    588            mStreamIdHash.Get(event.data_writable.stream_id);
    589 
    590        if (stream) {
    591          StreamReadyToWrite(stream);
    592          stream->SetBlockedByFlowControl(false);
    593        }
    594      } break;
    595      case Http3Event::Tag::Reset:
    596        LOG(("Http3Session::ProcessEvents %p - Reset", this));
    597        ResetOrStopSendingRecvd(event.reset.stream_id, event.reset.error,
    598                                RESET);
    599        break;
    600      case Http3Event::Tag::StopSending:
    601        LOG(
    602            ("Http3Session::ProcessEvents %p - StopSeniding with error "
    603             "0x%" PRIx64,
    604             this, event.stop_sending.error));
    605        if (event.stop_sending.error == HTTP3_APP_ERROR_NO_ERROR) {
    606          RefPtr<Http3StreamBase> stream =
    607              mStreamIdHash.Get(event.data_writable.stream_id);
    608          if (stream) {
    609            RefPtr<Http3Stream> httpStream = stream->GetHttp3Stream();
    610            MOZ_RELEASE_ASSERT(httpStream, "This must be a Http3Stream");
    611            httpStream->StopSending();
    612          }
    613        } else {
    614          ResetOrStopSendingRecvd(event.reset.stream_id, event.reset.error,
    615                                  STOP_SENDING);
    616        }
    617        break;
    618      case Http3Event::Tag::PushPromise:
    619        LOG(("Http3Session::ProcessEvents - PushPromise"));
    620        break;
    621      case Http3Event::Tag::PushHeaderReady:
    622        LOG(("Http3Session::ProcessEvents - PushHeaderReady"));
    623        break;
    624      case Http3Event::Tag::PushDataReadable:
    625        LOG(("Http3Session::ProcessEvents - PushDataReadable"));
    626        break;
    627      case Http3Event::Tag::PushCanceled:
    628        LOG(("Http3Session::ProcessEvents - PushCanceled"));
    629        break;
    630      case Http3Event::Tag::RequestsCreatable:
    631        LOG(("Http3Session::ProcessEvents - StreamCreatable"));
    632        ProcessPending();
    633        break;
    634      case Http3Event::Tag::AuthenticationNeeded:
    635        LOG(("Http3Session::ProcessEvents - AuthenticationNeeded %d",
    636             mAuthenticationStarted));
    637        if (!mAuthenticationStarted) {
    638          mAuthenticationStarted = true;
    639          LOG(("Http3Session::ProcessEvents - AuthenticationNeeded called"));
    640          CallCertVerification(Nothing());
    641        }
    642        break;
    643      case Http3Event::Tag::ZeroRttRejected:
    644        LOG(("Http3Session::ProcessEvents - ZeroRttRejected"));
    645        if (mState == ZERORTT) {
    646          mState = INITIALIZING;
    647          mTransactionCount = 0;
    648          Finish0Rtt(true);
    649 #ifndef ANDROID
    650          ZeroRttTelemetry(ZeroRttOutcome::USED_REJECTED);
    651 #endif
    652        }
    653        break;
    654      case Http3Event::Tag::ResumptionToken: {
    655        LOG(("Http3Session::ProcessEvents - ResumptionToken"));
    656        if (StaticPrefs::network_http_http3_enable_0rtt() && !data.IsEmpty()) {
    657          LOG(("Got a resumption token"));
    658          nsAutoCString peerId;
    659          mSocketControl->GetPeerId(peerId);
    660          if (NS_FAILED(SSLTokensCache::Put(
    661                  peerId, data.Elements(), data.Length(), mSocketControl,
    662                  PR_Now() + event.resumption_token.expire_in))) {
    663            LOG(("Adding resumption token failed"));
    664          }
    665        }
    666      } break;
    667      case Http3Event::Tag::ConnectionConnected: {
    668        LOG(("Http3Session::ProcessEvents - ConnectionConnected"));
    669        bool was0RTT = mState == ZERORTT;
    670        mState = CONNECTED;
    671        SetSecInfo();
    672        mSocketControl->HandshakeCompleted();
    673        if (was0RTT) {
    674          Finish0Rtt(false);
    675 #ifndef ANDROID
    676          ZeroRttTelemetry(ZeroRttOutcome::USED_SUCCEEDED);
    677 #endif
    678        }
    679 
    680        OnTransportStatus(nullptr, NS_NET_STATUS_CONNECTED_TO, 0);
    681        mUdpConn->OnConnected();
    682        ReportHttp3Connection();
    683        // Maybe call ResumeSend:
    684        // In case ZeroRtt has been used and it has been rejected, 2 events will
    685        // be received: ZeroRttRejected and ConnectionConnected. ZeroRttRejected
    686        // that will put all transaction into mReadyForWrite queue and it will
    687        // call MaybeResumeSend, but that will not have an effect because the
    688        // connection is ont in CONNECTED state. When ConnectionConnected event
    689        // is received call MaybeResumeSend to trigger reads for the
    690        // zero-rtt-rejected transactions.
    691        MaybeResumeSend();
    692      } break;
    693      case Http3Event::Tag::GoawayReceived:
    694        LOG(("Http3Session::ProcessEvents - GoawayReceived"));
    695        mUdpConn->SetCloseReason(ConnectionCloseReason::GO_AWAY);
    696        mGoawayReceived = true;
    697        break;
    698      case Http3Event::Tag::ConnectionClosing:
    699        LOG(("Http3Session::ProcessEvents - ConnectionClosing"));
    700        if (NS_SUCCEEDED(mError) && !IsClosing()) {
    701          mError = NS_ERROR_NET_HTTP3_PROTOCOL_ERROR;
    702          CloseConnectionTelemetry(event.connection_closing.error, true);
    703 
    704          auto isStatelessResetOrNoError = [](CloseError& aError) -> bool {
    705            if (aError.tag == CloseError::Tag::TransportInternalErrorOther &&
    706                aError.transport_internal_error_other._0 ==
    707                    TRANSPORT_ERROR_STATELESS_RESET) {
    708              return true;
    709            }
    710            if (aError.tag == CloseError::Tag::TransportError &&
    711                aError.transport_error._0 == 0) {
    712              return true;
    713            }
    714            if (aError.tag == CloseError::Tag::PeerError &&
    715                aError.peer_error._0 == 0) {
    716              return true;
    717            }
    718            if (aError.tag == CloseError::Tag::AppError &&
    719                aError.app_error._0 == HTTP3_APP_ERROR_NO_ERROR) {
    720              return true;
    721            }
    722            if (aError.tag == CloseError::Tag::PeerAppError &&
    723                aError.peer_app_error._0 == HTTP3_APP_ERROR_NO_ERROR) {
    724              return true;
    725            }
    726            return false;
    727          };
    728          if (isStatelessResetOrNoError(event.connection_closing.error)) {
    729            mError = NS_ERROR_NET_RESET;
    730          }
    731          if (event.connection_closing.error.tag == CloseError::Tag::EchRetry) {
    732            mSocketControl->SetRetryEchConfig(Substring(
    733                reinterpret_cast<const char*>(data.Elements()), data.Length()));
    734            mError = psm::GetXPCOMFromNSSError(SSL_ERROR_ECH_RETRY_WITH_ECH);
    735          }
    736        }
    737        return mError;
    738        break;
    739      case Http3Event::Tag::ConnectionClosed:
    740        LOG(("Http3Session::ProcessEvents - ConnectionClosed"));
    741        if (NS_SUCCEEDED(mError)) {
    742          mError = NS_ERROR_NET_TIMEOUT;
    743          mUdpConn->SetCloseReason(ConnectionCloseReason::IDLE_TIMEOUT);
    744          CloseConnectionTelemetry(event.connection_closed.error, false);
    745        }
    746        mIsClosedByNeqo = true;
    747        if (event.connection_closed.error.tag == CloseError::Tag::EchRetry) {
    748          mSocketControl->SetRetryEchConfig(Substring(
    749              reinterpret_cast<const char*>(data.Elements()), data.Length()));
    750          mError = psm::GetXPCOMFromNSSError(SSL_ERROR_ECH_RETRY_WITH_ECH);
    751        }
    752        LOG(("Http3Session::ProcessEvents - ConnectionClosed error=%" PRIx32,
    753             static_cast<uint32_t>(mError)));
    754        // We need to return here and let HttpConnectionUDP close the session.
    755        return mError;
    756        break;
    757      case Http3Event::Tag::EchFallbackAuthenticationNeeded: {
    758        nsCString echPublicName(reinterpret_cast<const char*>(data.Elements()),
    759                                data.Length());
    760        LOG(
    761            ("Http3Session::ProcessEvents - EchFallbackAuthenticationNeeded "
    762             "echPublicName=%s",
    763             echPublicName.get()));
    764        if (!mAuthenticationStarted) {
    765          mAuthenticationStarted = true;
    766          CallCertVerification(Some(echPublicName));
    767        }
    768      } break;
    769      case Http3Event::Tag::WebTransport: {
    770        switch (event.web_transport._0.tag) {
    771          case WebTransportEventExternal::Tag::Negotiated:
    772            LOG(("Http3Session::ProcessEvents - WebTransport %d",
    773                 event.web_transport._0.negotiated._0));
    774            FinishNegotiation(ExtendedConnectKind::WebTransport,
    775                              event.web_transport._0.negotiated._0);
    776            break;
    777          case WebTransportEventExternal::Tag::Session: {
    778            MOZ_ASSERT(mState == CONNECTED);
    779 
    780            uint64_t id = event.web_transport._0.session._0;
    781            LOG(
    782                ("Http3Session::ProcessEvents - WebTransport Session "
    783                 " sessionId=0x%" PRIx64,
    784                 id));
    785            RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(id);
    786            if (!stream) {
    787              LOG(
    788                  ("Http3Session::ProcessEvents - WebTransport Session - "
    789                   "stream not found "
    790                   "stream_id=0x%" PRIx64 " [this=%p].",
    791                   id, this));
    792              break;
    793            }
    794 
    795            MOZ_RELEASE_ASSERT(stream->GetHttp3WebTransportSession(),
    796                               "It must be a WebTransport session");
    797            stream->SetResponseHeaders(data, false, false);
    798 
    799            rv = stream->WriteSegments();
    800 
    801            if (ASpdySession::SoftStreamError(rv) || stream->Done()) {
    802              LOG3(
    803                  ("Http3Session::ProcessSingleTransactionRead session=%p "
    804                   "stream=%p "
    805                   "0x%" PRIx64 " cleanup stream rv=0x%" PRIx32 " done=%d.\n",
    806                   this, stream.get(), stream->StreamId(),
    807                   static_cast<uint32_t>(rv), stream->Done()));
    808              // We need to keep the transaction, so we can use it to remove the
    809              // stream from mStreamTransactionHash.
    810              nsAHttpTransaction* trans = stream->Transaction();
    811              if (mStreamTransactionHash.Contains(trans)) {
    812                CloseStream(stream, (rv == NS_BINDING_RETARGETED)
    813                                        ? NS_BINDING_RETARGETED
    814                                        : NS_OK);
    815                mStreamTransactionHash.Remove(trans);
    816              } else {
    817                stream->GetHttp3WebTransportSession()->TransactionIsDone(
    818                    (rv == NS_BINDING_RETARGETED) ? NS_BINDING_RETARGETED
    819                                                  : NS_OK);
    820              }
    821              break;
    822            }
    823 
    824            if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
    825              LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
    826                   static_cast<uint32_t>(rv)));
    827              return rv;
    828            }
    829          } break;
    830          case WebTransportEventExternal::Tag::SessionClosed: {
    831            uint64_t id = event.web_transport._0.session_closed.stream_id;
    832            LOG(
    833                ("Http3Session::ProcessEvents - WebTransport SessionClosed "
    834                 " sessionId=0x%" PRIx64,
    835                 id));
    836            RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(id);
    837            if (!stream) {
    838              LOG(
    839                  ("Http3Session::ProcessEvents - WebTransport SessionClosed - "
    840                   "stream not found "
    841                   "stream_id=0x%" PRIx64 " [this=%p].",
    842                   id, this));
    843              break;
    844            }
    845 
    846            RefPtr<Http3WebTransportSession> wt =
    847                stream->GetHttp3WebTransportSession();
    848            MOZ_RELEASE_ASSERT(wt, "It must be a WebTransport session");
    849 
    850            bool cleanly = false;
    851 
    852            // TODO we do not handle the case when a WebTransport session stream
    853            // is closed before headers are sent.
    854            SessionCloseReasonExternal& reasonExternal =
    855                event.web_transport._0.session_closed.reason;
    856            uint32_t status = 0;
    857            nsCString reason = ""_ns;
    858            if (reasonExternal.tag == SessionCloseReasonExternal::Tag::Error) {
    859              status = reasonExternal.error._0;
    860            } else if (reasonExternal.tag ==
    861                       SessionCloseReasonExternal::Tag::Status) {
    862              status = reasonExternal.status._0;
    863              cleanly = true;
    864            } else {
    865              status = reasonExternal.clean._0;
    866              reason.Assign(reinterpret_cast<const char*>(data.Elements()),
    867                            data.Length());
    868              cleanly = true;
    869            }
    870            LOG(("reason.tag=%u err=%u data=%s\n",
    871                 static_cast<uint32_t>(reasonExternal.tag), status,
    872                 reason.get()));
    873            wt->OnSessionClosed(cleanly, status, reason);
    874 
    875          } break;
    876          case WebTransportEventExternal::Tag::NewStream: {
    877            LOG(
    878                ("Http3Session::ProcessEvents - WebTransport NewStream "
    879                 "streamId=0x%" PRIx64 " sessionId=0x%" PRIx64,
    880                 event.web_transport._0.new_stream.stream_id,
    881                 event.web_transport._0.new_stream.session_id));
    882            uint64_t sessionId = event.web_transport._0.new_stream.session_id;
    883            RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(sessionId);
    884            if (!stream) {
    885              LOG(
    886                  ("Http3Session::ProcessEvents - WebTransport NewStream - "
    887                   "session not found "
    888                   "sessionId=0x%" PRIx64 " [this=%p].",
    889                   sessionId, this));
    890              break;
    891            }
    892 
    893            RefPtr<Http3WebTransportSession> wt =
    894                stream->GetHttp3WebTransportSession();
    895            if (!wt) {
    896              break;
    897            }
    898 
    899            RefPtr<Http3WebTransportStream> wtStream =
    900                wt->OnIncomingWebTransportStream(
    901                    event.web_transport._0.new_stream.stream_type,
    902                    event.web_transport._0.new_stream.stream_id);
    903            if (!wtStream) {
    904              break;
    905            }
    906 
    907            // WebTransportStream is managed by Http3Session now.
    908            mWebTransportStreams.AppendElement(wtStream);
    909            mWebTransportStreamToSessionMap.InsertOrUpdate(wtStream->StreamId(),
    910                                                           wt->StreamId());
    911            mStreamIdHash.InsertOrUpdate(wtStream->StreamId(),
    912                                         std::move(wtStream));
    913          } break;
    914          case WebTransportEventExternal::Tag::Datagram:
    915            LOG(
    916                ("Http3Session::ProcessEvents - "
    917                 "WebTransportEventExternal::Tag::Datagram [this=%p]",
    918                 this));
    919            uint64_t sessionId = event.web_transport._0.datagram.session_id;
    920            RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(sessionId);
    921            if (!stream) {
    922              LOG(
    923                  ("Http3Session::ProcessEvents - WebTransport Datagram - "
    924                   "session not found "
    925                   "sessionId=0x%" PRIx64 " [this=%p].",
    926                   sessionId, this));
    927              break;
    928            }
    929 
    930            RefPtr<Http3WebTransportSession> wt =
    931                stream->GetHttp3WebTransportSession();
    932            if (!wt) {
    933              break;
    934            }
    935 
    936            wt->OnDatagramReceived(std::move(data));
    937            break;
    938        }
    939      } break;
    940      case Http3Event::Tag::ConnectUdp: {
    941        switch (event.connect_udp._0.tag) {
    942          case ConnectUdpEventExternal::Tag::Negotiated:
    943            LOG(("Http3Session::ProcessEvents - ConnectUdp Negotiated %d",
    944                 event.connect_udp._0.negotiated._0));
    945            FinishNegotiation(ExtendedConnectKind::ConnectUDP,
    946                              event.connect_udp._0.negotiated._0);
    947            break;
    948          case ConnectUdpEventExternal::Tag::Session: {
    949            MOZ_ASSERT(mState == CONNECTED);
    950 
    951            uint64_t id = event.connect_udp._0.session._0;
    952            LOG(
    953                ("Http3Session::ProcessEvents - ConnectUdp "
    954                 " streamId=0x%" PRIx64,
    955                 id));
    956            RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(id);
    957            if (!stream) {
    958              LOG(
    959                  ("Http3Session::ProcessEvents - ConnectUdp Session - "
    960                   "stream not found "
    961                   "stream_id=0x%" PRIx64 " [this=%p].",
    962                   id, this));
    963              break;
    964            }
    965 
    966            MOZ_RELEASE_ASSERT(stream->GetHttp3ConnectUDPStream(),
    967                               "It must be a ConnectUdp session");
    968            stream->SetResponseHeaders(data, false, false);
    969 
    970            rv = stream->WriteSegments();
    971 
    972            LOG(("rv=%x", static_cast<uint32_t>(rv)));
    973 
    974            if (ASpdySession::SoftStreamError(rv) || stream->Done()) {
    975              LOG3(
    976                  ("Http3Session::ProcessSingleTransactionRead session=%p "
    977                   "stream=%p "
    978                   "0x%" PRIx64 " cleanup stream rv=0x%" PRIx32 " done=%d.\n",
    979                   this, stream.get(), stream->StreamId(),
    980                   static_cast<uint32_t>(rv), stream->Done()));
    981              // We need to keep the transaction, so we can use it to remove the
    982              // stream from mStreamTransactionHash.
    983              nsAHttpTransaction* trans = stream->Transaction();
    984              if (mStreamTransactionHash.Contains(trans)) {
    985                CloseStream(stream, (rv == NS_BINDING_RETARGETED)
    986                                        ? NS_BINDING_RETARGETED
    987                                        : NS_OK);
    988                mStreamTransactionHash.Remove(trans);
    989              } else {
    990                stream->GetHttp3ConnectUDPStream()->TransactionIsDone(
    991                    (rv == NS_BINDING_RETARGETED) ? NS_BINDING_RETARGETED
    992                                                  : NS_OK);
    993              }
    994              break;
    995            }
    996 
    997            if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) {
    998              LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
    999                   static_cast<uint32_t>(rv)));
   1000              return rv;
   1001            }
   1002          } break;
   1003          case ConnectUdpEventExternal::Tag::SessionClosed: {
   1004            uint64_t id = event.connect_udp._0.session_closed.stream_id;
   1005            LOG(
   1006                ("Http3Session::ProcessEvents - connect_udp SessionClosed "
   1007                 " sessionId=0x%" PRIx64,
   1008                 id));
   1009            RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(id);
   1010            if (!stream) {
   1011              LOG(
   1012                  ("Http3Session::ProcessEvents - connect_udp SessionClosed - "
   1013                   "stream not found "
   1014                   "stream_id=0x%" PRIx64 " [this=%p].",
   1015                   id, this));
   1016              break;
   1017            }
   1018 
   1019            RefPtr<Http3ConnectUDPStream> connectUDPStream =
   1020                stream->GetHttp3ConnectUDPStream();
   1021            MOZ_RELEASE_ASSERT(connectUDPStream,
   1022                               "It must be a ConnectUDP stream");
   1023 
   1024            // TODO we do not handle the case when a ConnectUDP session stream
   1025            // is closed before headers are sent.
   1026            SessionCloseReasonExternal& reasonExternal =
   1027                event.connect_udp._0.session_closed.reason;
   1028            uint32_t status = 0;
   1029            nsCString reason = ""_ns;
   1030            if (reasonExternal.tag == SessionCloseReasonExternal::Tag::Error) {
   1031              status = reasonExternal.error._0;
   1032            } else if (reasonExternal.tag ==
   1033                       SessionCloseReasonExternal::Tag::Status) {
   1034              status = reasonExternal.status._0;
   1035            } else {
   1036              status = reasonExternal.clean._0;
   1037              reason.Assign(reinterpret_cast<const char*>(data.Elements()),
   1038                            data.Length());
   1039            }
   1040            LOG(("reason.tag=%u err=%u data=%s\n",
   1041                 static_cast<uint32_t>(reasonExternal.tag), status,
   1042                 reason.get()));
   1043            CloseStream(connectUDPStream,
   1044                        status == 0 ? NS_OK : NS_ERROR_FAILURE);
   1045          } break;
   1046          case ConnectUdpEventExternal::Tag::Datagram:
   1047            LOG(("Http3Session::ProcessEvents - ConnectUdp Datagram"));
   1048            uint64_t streamId = event.connect_udp._0.datagram.session_id;
   1049            RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(streamId);
   1050            if (!stream) {
   1051              LOG(
   1052                  ("Http3Session::ProcessEvents - ConnectUdp Datagram - "
   1053                   "stream not found "
   1054                   "streamId=0x%" PRIx64 " [this=%p].",
   1055                   streamId, this));
   1056              break;
   1057            }
   1058 
   1059            RefPtr<Http3ConnectUDPStream> tunnelStream =
   1060                stream->GetHttp3ConnectUDPStream();
   1061            if (!tunnelStream) {
   1062              break;
   1063            }
   1064            tunnelStream->OnDatagramReceived(std::move(data));
   1065            break;
   1066        }
   1067      } break;
   1068      default:
   1069        break;
   1070    }
   1071    // Delete previous content of data
   1072    data.TruncateLength(0);
   1073    rv = mHttp3Connection->GetEvent(&event, data);
   1074    if (NS_FAILED(rv)) {
   1075      LOG(("Http3Session::ProcessEvents [this=%p] rv=%" PRIx32, this,
   1076           static_cast<uint32_t>(rv)));
   1077      return rv;
   1078    }
   1079  }
   1080 
   1081  return NS_OK;
   1082 }  // namespace net
   1083 
   1084 // This function may return a socket error.
   1085 // It will not return an error if socket error is
   1086 // NS_BASE_STREAM_WOULD_BLOCK.
   1087 // A Caller of this function will close the Http3 connection
   1088 // if this function returns an error.
   1089 // Callers are:
   1090 //   1) HttpConnectionUDP::OnQuicTimeoutExpired
   1091 //   2) HttpConnectionUDP::SendData ->
   1092 //      Http3Session::SendData
   1093 nsresult Http3Session::ProcessOutput(nsIUDPSocket* socket) {
   1094  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1095  MOZ_ASSERT(mUdpConn);
   1096 
   1097  LOG(("Http3Session::ProcessOutput reader=%p, [this=%p]", mUdpConn.get(),
   1098       this));
   1099 
   1100  if (!socket || socket->IsSocketClosed()) {
   1101    MOZ_DIAGNOSTIC_ASSERT(false, "UDP socket should still be open");
   1102    return NS_ERROR_UNEXPECTED;
   1103  }
   1104 
   1105  if (mUseNSPRForIO) {
   1106    mSocket = socket;
   1107    nsresult rv = mHttp3Connection->ProcessOutputAndSendUseNSPRForIO(
   1108        this,
   1109        [](void* aContext, uint16_t aFamily, const uint8_t* aAddr,
   1110           uint16_t aPort, const uint8_t* aData, uint32_t aLength) {
   1111          Http3Session* self = (Http3Session*)aContext;
   1112 
   1113          uint32_t written = 0;
   1114          NetAddr addr;
   1115          if (NS_FAILED(RawBytesToNetAddr(aFamily, aAddr, aPort, &addr))) {
   1116            return NS_OK;
   1117          }
   1118 
   1119          LOG3(
   1120              ("Http3Session::ProcessOutput sending packet with %u bytes to %s "
   1121               "port=%d [this=%p].",
   1122               aLength, addr.ToString().get(), aPort, self));
   1123 
   1124          nsresult rv =
   1125              self->mSocket->SendWithAddress(&addr, aData, aLength, &written);
   1126 
   1127          LOG(("Http3Session::ProcessOutput sending packet rv=%d osError=%d",
   1128               static_cast<int32_t>(rv), NS_FAILED(rv) ? PR_GetOSError() : 0));
   1129          if (NS_FAILED(rv) && (rv != NS_BASE_STREAM_WOULD_BLOCK)) {
   1130            self->mSocketError = rv;
   1131            // If there was an error that is not NS_BASE_STREAM_WOULD_BLOCK
   1132            // return from here. We do not need to set a timer, because we
   1133            // will close the connection.
   1134            return rv;
   1135          }
   1136          self->mTotalBytesWritten += aLength;
   1137          self->mLastWriteTime = PR_IntervalNow();
   1138          return NS_OK;
   1139        },
   1140        [](void* aContext, uint64_t timeout) {
   1141          Http3Session* self = (Http3Session*)aContext;
   1142          self->SetupTimer(timeout);
   1143        });
   1144    mSocket = nullptr;
   1145    return rv;
   1146  }
   1147 
   1148  // Not using NSPR.
   1149 
   1150  auto rv = mHttp3Connection->ProcessOutputAndSend(
   1151      this, [](void* aContext, uint64_t timeout) {
   1152        Http3Session* self = (Http3Session*)aContext;
   1153        self->SetupTimer(timeout);
   1154      });
   1155  if (rv.result == NS_BASE_STREAM_WOULD_BLOCK) {
   1156    // The OS buffer was full. Tell the UDP socket to poll for
   1157    // write-availability.
   1158    socket->EnableWritePoll();
   1159  } else if (NS_FAILED(rv.result)) {
   1160    mSocketError = rv.result;
   1161    // If there was an error return from here. We do not need to set a timer,
   1162    // because we will close the connection.
   1163    return rv.result;
   1164  }
   1165  if (rv.bytes_written != 0) {
   1166    mTotalBytesWritten += rv.bytes_written;
   1167    mLastWriteTime = PR_IntervalNow();
   1168    socket->AddOutputBytes(rv.bytes_written);
   1169  }
   1170 
   1171  return NS_OK;
   1172 }
   1173 
   1174 // This is only called when timer expires.
   1175 // It is called by HttpConnectionUDP::OnQuicTimeout.
   1176 // If tihs function returns an error OnQuicTimeout will handle the error
   1177 // properly and close the connection.
   1178 nsresult Http3Session::ProcessOutputAndEvents(nsIUDPSocket* socket) {
   1179  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1180 
   1181  MOZ_ASSERT(mTimerShouldTrigger);
   1182 
   1183  if (Telemetry::CanRecordPrereleaseData()) {
   1184    auto now = TimeStamp::Now();
   1185    if (mTimerShouldTrigger > now) {
   1186      // See bug 1935459
   1187      glean::http3::timer_delayed.AccumulateRawDuration(0);
   1188    } else {
   1189      glean::http3::timer_delayed.AccumulateRawDuration(now -
   1190                                                        mTimerShouldTrigger);
   1191    }
   1192  }
   1193 
   1194  mTimerShouldTrigger = TimeStamp();
   1195 
   1196  nsresult rv = SendData(socket);
   1197  if (NS_FAILED(rv)) {
   1198    return rv;
   1199  }
   1200  return NS_OK;
   1201 }
   1202 
   1203 NS_IMPL_ISUPPORTS(Http3Session::OnQuicTimeout, nsITimerCallback, nsINamed)
   1204 
   1205 Http3Session::OnQuicTimeout::OnQuicTimeout(HttpConnectionUDP* aConnection)
   1206    : mConnection(aConnection) {
   1207  MOZ_ASSERT(mConnection);
   1208 }
   1209 
   1210 NS_IMETHODIMP
   1211 Http3Session::OnQuicTimeout::Notify(nsITimer* timer) {
   1212  mConnection->OnQuicTimeoutExpired();
   1213  return NS_OK;
   1214 }
   1215 
   1216 NS_IMETHODIMP
   1217 Http3Session::OnQuicTimeout::GetName(nsACString& aName) {
   1218  aName.AssignLiteral("net::HttpConnectionUDP::OnQuicTimeout");
   1219  return NS_OK;
   1220 }
   1221 
   1222 void Http3Session::SetupTimer(uint64_t aTimeout) {
   1223  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1224  // UINT64_MAX indicated a no-op from neqo, which only happens when a
   1225  // connection is in or going to be Closed state.
   1226  if (aTimeout == UINT64_MAX) {
   1227    return;
   1228  }
   1229 
   1230  LOG3(
   1231      ("Http3Session::SetupTimer to %" PRIu64 "ms [this=%p].", aTimeout, this));
   1232 
   1233  // Remember the time when the timer should trigger.
   1234  mTimerShouldTrigger =
   1235      TimeStamp::Now() + TimeDuration::FromMilliseconds(aTimeout);
   1236 
   1237  if (!mTimerCallback) {
   1238    // We can keep the same callback object for all our lifetime.
   1239    mTimerCallback = MakeRefPtr<OnQuicTimeout>(mUdpConn);
   1240  }
   1241 
   1242  if (!mTimer) {
   1243    // This can only fail on OOM and we'd crash.
   1244    mTimer = NS_NewTimer();
   1245  }
   1246 
   1247  DebugOnly<nsresult> rv = mTimer->InitWithCallback(mTimerCallback, aTimeout,
   1248                                                    nsITimer::TYPE_ONE_SHOT);
   1249  // There is no meaningful error handling we can do here. But an error here
   1250  // should only be possible if the timer thread did already shut down.
   1251  MOZ_ASSERT(NS_SUCCEEDED(rv));
   1252 }
   1253 
   1254 bool Http3Session::AddStream(nsAHttpTransaction* aHttpTransaction,
   1255                             int32_t aPriority,
   1256                             nsIInterfaceRequestor* aCallbacks) {
   1257  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1258 
   1259  nsHttpTransaction* trans = aHttpTransaction->QueryHttpTransaction();
   1260 
   1261  bool firstStream = false;
   1262  if (!mConnection) {
   1263    // Get the connection from the first transaction.
   1264    mConnection = aHttpTransaction->Connection();
   1265    firstStream = true;
   1266  }
   1267 
   1268  // Make sure we report the connectStart
   1269  auto reportConnectStart = MakeScopeExit([&] {
   1270    if (firstStream) {
   1271      OnTransportStatus(nullptr, NS_NET_STATUS_CONNECTING_TO, 0);
   1272    }
   1273  });
   1274 
   1275  if (IsClosing()) {
   1276    LOG3(
   1277        ("Http3Session::AddStream %p atrans=%p trans=%p session unusable - "
   1278         "resched.\n",
   1279         this, aHttpTransaction, trans));
   1280    aHttpTransaction->SetConnection(nullptr);
   1281    nsresult rv = gHttpHandler->InitiateTransaction(trans, trans->Priority());
   1282    if (NS_FAILED(rv)) {
   1283      LOG3(
   1284          ("Http3Session::AddStream %p atrans=%p trans=%p failed to initiate "
   1285           "transaction (0x%" PRIx32 ").\n",
   1286           this, aHttpTransaction, trans, static_cast<uint32_t>(rv)));
   1287    }
   1288    return true;
   1289  }
   1290 
   1291  aHttpTransaction->SetConnection(this);
   1292  aHttpTransaction->OnActivated();
   1293  // reset the read timers to wash away any idle time
   1294  mLastWriteTime = PR_IntervalNow();
   1295 
   1296  ClassOfService cos;
   1297  if (trans) {
   1298    cos = trans->GetClassOfService();
   1299  }
   1300 
   1301  Http3StreamBase* stream = nullptr;
   1302 
   1303  if (trans && mConnInfo->IsHttp3ProxyConnection() && !mIsInTunnel) {
   1304    LOG3(("Http3Session::AddStream new connect-udp stream %p atrans=%p.\n",
   1305          this, aHttpTransaction));
   1306    stream = new Http3ConnectUDPStream(aHttpTransaction, this,
   1307                                       NS_GetCurrentThread());
   1308  } else if (trans && trans->IsForWebTransport()) {
   1309    LOG3(("Http3Session::AddStream new  WeTransport session %p atrans=%p.\n",
   1310          this, aHttpTransaction));
   1311    stream = new Http3WebTransportSession(aHttpTransaction, this);
   1312    mHasWebTransportSession = true;
   1313  } else {
   1314    LOG3(("Http3Session::AddStream %p atrans=%p.\n", this, aHttpTransaction));
   1315    stream = new Http3Stream(aHttpTransaction, this, cos, mCurrentBrowserId);
   1316  }
   1317 
   1318  mStreamTransactionHash.InsertOrUpdate(aHttpTransaction, RefPtr{stream});
   1319 
   1320  if (mState == ZERORTT) {
   1321    if (!stream->Do0RTT()) {
   1322      LOG(("Http3Session %p will not get early data from Http3Stream %p", this,
   1323           stream));
   1324      if (!mCannotDo0RTTStreams.Contains(stream)) {
   1325        mCannotDo0RTTStreams.AppendElement(stream);
   1326      }
   1327      if (stream->GetHttp3WebTransportSession()) {
   1328        DeferIfNegotiating(ExtendedConnectKind::WebTransport, stream);
   1329      } else if (stream->GetHttp3ConnectUDPStream()) {
   1330        DeferIfNegotiating(ExtendedConnectKind::ConnectUDP, stream);
   1331      }
   1332      return true;
   1333    }
   1334    m0RTTStreams.AppendElement(stream);
   1335  }
   1336 
   1337  if (stream->GetHttp3WebTransportSession()) {
   1338    if (DeferIfNegotiating(ExtendedConnectKind::WebTransport, stream)) {
   1339      return true;
   1340    }
   1341  } else if (stream->GetHttp3ConnectUDPStream()) {
   1342    if (DeferIfNegotiating(ExtendedConnectKind::ConnectUDP, stream)) {
   1343      return true;
   1344    }
   1345  }
   1346 
   1347  if (!mFirstHttpTransaction && !IsConnected()) {
   1348    mFirstHttpTransaction = aHttpTransaction->QueryHttpTransaction();
   1349    LOG3(("Http3Session::AddStream first session=%p trans=%p ", this,
   1350          mFirstHttpTransaction.get()));
   1351  }
   1352  StreamReadyToWrite(stream);
   1353 
   1354  return true;
   1355 }
   1356 
   1357 bool Http3Session::DeferIfNegotiating(ExtendedConnectKind aKind,
   1358                                      Http3StreamBase* aStream) {
   1359  auto& st = ExtState(aKind);
   1360  if (st.mStatus == NEGOTIATING) {
   1361    if (!st.mWaiters.Contains(aStream)) {
   1362      LOG(("waiting for negotiation"));
   1363      st.mWaiters.AppendElement(aStream);
   1364    }
   1365    return true;
   1366  }
   1367  return false;
   1368 }
   1369 
   1370 void Http3Session::FinishNegotiation(ExtendedConnectKind aKind, bool aSuccess) {
   1371  auto& st = ExtState(aKind);
   1372  if (st.mWaiters.IsEmpty()) {
   1373    st.mStatus = aSuccess ? SUCCEEDED : FAILED;
   1374    return;
   1375  }
   1376 
   1377  MOZ_ASSERT(st.mStatus == NEGOTIATING);
   1378  st.mStatus = aSuccess ? SUCCEEDED : FAILED;
   1379 
   1380  for (size_t i = 0; i < st.mWaiters.Length(); ++i) {
   1381    if (st.mWaiters[i]) {
   1382      mReadyForWrite.Push(st.mWaiters[i]);
   1383    }
   1384  }
   1385  st.mWaiters.Clear();
   1386  MaybeResumeSend();
   1387 }
   1388 
   1389 bool Http3Session::CanReuse() {
   1390  // TODO: we assume "pooling" is disabled here, so we don't allow this session
   1391  // to be reused. "pooling" will be implemented in bug 1815735.
   1392  return CanSendData() && !(mGoawayReceived || mShouldClose) &&
   1393         !mHasWebTransportSession;
   1394 }
   1395 
   1396 void Http3Session::QueueStream(Http3StreamBase* stream) {
   1397  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1398  MOZ_ASSERT(!stream->Queued());
   1399 
   1400  LOG3(("Http3Session::QueueStream %p stream %p queued.", this, stream));
   1401 
   1402  stream->SetQueued(true);
   1403  mQueuedStreams.Push(stream);
   1404 }
   1405 
   1406 void Http3Session::ProcessPending() {
   1407  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1408 
   1409  RefPtr<Http3StreamBase> stream;
   1410  while ((stream = mQueuedStreams.PopFront())) {
   1411    LOG3(("Http3Session::ProcessPending %p stream %p woken from queue.", this,
   1412          stream.get()));
   1413    MOZ_ASSERT(stream->Queued());
   1414    stream->SetQueued(false);
   1415    mReadyForWrite.Push(stream);
   1416  }
   1417  MaybeResumeSend();
   1418 }
   1419 
   1420 static void RemoveStreamFromQueue(Http3StreamBase* aStream,
   1421                                  nsRefPtrDeque<Http3StreamBase>& queue) {
   1422  size_t size = queue.GetSize();
   1423  for (size_t count = 0; count < size; ++count) {
   1424    RefPtr<Http3StreamBase> stream = queue.PopFront();
   1425    if (stream != aStream) {
   1426      queue.Push(stream);
   1427    }
   1428  }
   1429 }
   1430 
   1431 void Http3Session::RemoveStreamFromQueues(Http3StreamBase* aStream) {
   1432  RemoveStreamFromQueue(aStream, mReadyForWrite);
   1433  RemoveStreamFromQueue(aStream, mQueuedStreams);
   1434  mSlowConsumersReadyForRead.RemoveElement(aStream);
   1435 }
   1436 
   1437 // This is called by Http3Stream::OnReadSegment.
   1438 // ProcessOutput will be called in Http3Session::ReadSegment that
   1439 // calls Http3Stream::OnReadSegment.
   1440 nsresult Http3Session::TryActivating(
   1441    const nsACString& aMethod, const nsACString& aScheme,
   1442    const nsACString& aAuthorityHeader, const nsACString& aPathQuery,
   1443    const nsACString& aHeaders, uint64_t* aStreamId, Http3StreamBase* aStream) {
   1444  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1445  MOZ_ASSERT(*aStreamId == UINT64_MAX);
   1446 
   1447  LOG(("Http3Session::TryActivating [stream=%p, this=%p state=%d]", aStream,
   1448       this, mState));
   1449 
   1450  if (IsClosing()) {
   1451    if (NS_FAILED(mError)) {
   1452      return mError;
   1453    }
   1454    return NS_ERROR_FAILURE;
   1455  }
   1456 
   1457  if (aStream->Queued()) {
   1458    LOG3(("Http3Session::TryActivating %p stream=%p already queued.\n", this,
   1459          aStream));
   1460    return NS_BASE_STREAM_WOULD_BLOCK;
   1461  }
   1462 
   1463  if (mState == ZERORTT) {
   1464    if (!aStream->Do0RTT()) {
   1465      // Stream can't do 0RTT - queue it for activation when the session
   1466      // reaches CONNECTED state via Finish0Rtt.
   1467      if (!mCannotDo0RTTStreams.Contains(aStream)) {
   1468        LOG(("Http3Session %p queuing stream %p for post-0RTT activation", this,
   1469             aStream));
   1470        mCannotDo0RTTStreams.AppendElement(aStream);
   1471      }
   1472      return NS_BASE_STREAM_WOULD_BLOCK;
   1473    }
   1474  }
   1475 
   1476  nsresult rv = NS_OK;
   1477  // The order of these checks is important: Http3StreamTunnel inherits from
   1478  // Http3Stream, so a tunnel will also match conditions for a regular stream.
   1479  // Ensure we handle Http3StreamTunnel cases before generic Http3Stream logic.
   1480  if (RefPtr<Http3StreamTunnel> streamTunnel =
   1481          aStream->GetHttp3StreamTunnel()) {
   1482    rv = mHttp3Connection->Connect(aAuthorityHeader, aHeaders, aStreamId, 3,
   1483                                   false);
   1484  } else if (RefPtr<Http3Stream> httpStream = aStream->GetHttp3Stream()) {
   1485    rv = mHttp3Connection->Fetch(
   1486        aMethod, aScheme, aAuthorityHeader, aPathQuery, aHeaders, aStreamId,
   1487        httpStream->PriorityUrgency(), httpStream->PriorityIncremental());
   1488  } else if (RefPtr<Http3ConnectUDPStream> udpStream =
   1489                 aStream->GetHttp3ConnectUDPStream()) {
   1490    if (DeferIfNegotiating(ExtendedConnectKind::ConnectUDP, aStream)) {
   1491      return NS_BASE_STREAM_WOULD_BLOCK;
   1492    }
   1493    rv = mHttp3Connection->CreateConnectUdp(aAuthorityHeader, aPathQuery,
   1494                                            aHeaders, aStreamId);
   1495  } else {
   1496    MOZ_RELEASE_ASSERT(aStream->GetHttp3WebTransportSession(),
   1497                       "It must be a WebTransport session");
   1498    // Don't call CreateWebTransport if we are still waiting for the negotiation
   1499    // result.
   1500    if (DeferIfNegotiating(ExtendedConnectKind::WebTransport, aStream)) {
   1501      return NS_BASE_STREAM_WOULD_BLOCK;
   1502    }
   1503    rv = mHttp3Connection->CreateWebTransport(aAuthorityHeader, aPathQuery,
   1504                                              aHeaders, aStreamId);
   1505  }
   1506 
   1507  if (NS_FAILED(rv)) {
   1508    LOG(("Http3Session::TryActivating returns error=0x%" PRIx32 "[stream=%p, "
   1509         "this=%p]",
   1510         static_cast<uint32_t>(rv), aStream, this));
   1511    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
   1512      LOG3(
   1513          ("Http3Session::TryActivating %p stream=%p no room for more "
   1514           "concurrent streams\n",
   1515           this, aStream));
   1516      mTransactionsBlockedByStreamLimitCount++;
   1517      if (mQueuedStreams.GetSize() == 0) {
   1518        mBlockedByStreamLimitCount++;
   1519      }
   1520      QueueStream(aStream);
   1521      return rv;
   1522    }
   1523 
   1524    // Previously we always returned NS_OK here, which caused the
   1525    // transaction to wait until the quic connection timed out
   1526    // after which it was retried without quic.
   1527    if (StaticPrefs::network_http_http3_fallback_to_h2_on_error()) {
   1528      return NS_ERROR_HTTP2_FALLBACK_TO_HTTP1;
   1529    }
   1530 
   1531    return rv;
   1532  }
   1533 
   1534  LOG(("Http3Session::TryActivating streamId=0x%" PRIx64
   1535       " for stream=%p [this=%p].",
   1536       *aStreamId, aStream, this));
   1537 
   1538  MOZ_ASSERT(*aStreamId != UINT64_MAX);
   1539 
   1540  if (mTransactionCount > 0 && mStreamIdHash.IsEmpty()) {
   1541    MOZ_ASSERT(mConnectionIdleStart);
   1542    MOZ_ASSERT(mFirstStreamIdReuseIdleConnection.isNothing());
   1543 
   1544    mConnectionIdleEnd = TimeStamp::Now();
   1545    mFirstStreamIdReuseIdleConnection = Some(*aStreamId);
   1546  }
   1547  mStreamIdHash.InsertOrUpdate(*aStreamId, RefPtr{aStream});
   1548  mTransactionCount++;
   1549 
   1550  return NS_OK;
   1551 }
   1552 
   1553 // This is called by Http3WebTransportStream::OnReadSegment.
   1554 // TODO: this function is almost the same as TryActivating().
   1555 // We should try to reduce the duplicate code.
   1556 nsresult Http3Session::TryActivatingWebTransportStream(
   1557    uint64_t* aStreamId, Http3StreamBase* aStream) {
   1558  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1559  MOZ_ASSERT(*aStreamId == UINT64_MAX);
   1560 
   1561  LOG(
   1562      ("Http3Session::TryActivatingWebTransportStream [stream=%p, this=%p "
   1563       "state=%d]",
   1564       aStream, this, mState));
   1565 
   1566  if (IsClosing()) {
   1567    if (NS_FAILED(mError)) {
   1568      return mError;
   1569    }
   1570    return NS_ERROR_FAILURE;
   1571  }
   1572 
   1573  if (aStream->Queued()) {
   1574    LOG3(
   1575        ("Http3Session::TryActivatingWebTransportStream %p stream=%p already "
   1576         "queued.\n",
   1577         this, aStream));
   1578    return NS_BASE_STREAM_WOULD_BLOCK;
   1579  }
   1580 
   1581  nsresult rv = NS_OK;
   1582  RefPtr<Http3WebTransportStream> wtStream =
   1583      aStream->GetHttp3WebTransportStream();
   1584  MOZ_RELEASE_ASSERT(wtStream, "It must be a WebTransport stream");
   1585  rv = mHttp3Connection->CreateWebTransportStream(
   1586      wtStream->SessionId(), wtStream->StreamType(), aStreamId);
   1587 
   1588  if (NS_FAILED(rv)) {
   1589    LOG((
   1590        "Http3Session::TryActivatingWebTransportStream returns error=0x%" PRIx32
   1591        "[stream=%p, "
   1592        "this=%p]",
   1593        static_cast<uint32_t>(rv), aStream, this));
   1594    if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
   1595      LOG3(
   1596          ("Http3Session::TryActivatingWebTransportStream %p stream=%p no room "
   1597           "for more "
   1598           "concurrent streams\n",
   1599           this, aStream));
   1600      QueueStream(aStream);
   1601      return rv;
   1602    }
   1603 
   1604    return rv;
   1605  }
   1606 
   1607  LOG(("Http3Session::TryActivatingWebTransportStream streamId=0x%" PRIx64
   1608       " for stream=%p [this=%p].",
   1609       *aStreamId, aStream, this));
   1610 
   1611  MOZ_ASSERT(*aStreamId != UINT64_MAX);
   1612 
   1613  RefPtr<Http3StreamBase> session = mStreamIdHash.Get(wtStream->SessionId());
   1614  MOZ_ASSERT(session);
   1615  Http3WebTransportSession* wtSession = session->GetHttp3WebTransportSession();
   1616  MOZ_ASSERT(wtSession);
   1617 
   1618  wtSession->RemoveWebTransportStream(wtStream);
   1619 
   1620  // WebTransportStream is managed by Http3Session now.
   1621  mWebTransportStreams.AppendElement(wtStream);
   1622  mWebTransportStreamToSessionMap.InsertOrUpdate(*aStreamId,
   1623                                                 session->StreamId());
   1624  mStreamIdHash.InsertOrUpdate(*aStreamId, std::move(wtStream));
   1625  return NS_OK;
   1626 }
   1627 
   1628 // This is only called by Http3Stream::OnReadSegment.
   1629 // ProcessOutput will be called in Http3Session::ReadSegment that
   1630 // calls Http3Stream::OnReadSegment.
   1631 void Http3Session::CloseSendingSide(uint64_t aStreamId) {
   1632  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1633  mHttp3Connection->CloseStream(aStreamId);
   1634 }
   1635 
   1636 // This is only called by Http3Stream::OnReadSegment.
   1637 // ProcessOutput will be called in Http3Session::ReadSegment that
   1638 // calls Http3Stream::OnReadSegment.
   1639 nsresult Http3Session::SendRequestBody(uint64_t aStreamId, const char* buf,
   1640                                       uint32_t count, uint32_t* countRead) {
   1641  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1642  nsresult rv = mHttp3Connection->SendRequestBody(
   1643      aStreamId, (const uint8_t*)buf, count, countRead);
   1644  if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
   1645    mTransactionsSenderBlockedByFlowControlCount++;
   1646  } else if (NS_FAILED(rv)) {
   1647    // Ignore this error. This may happen if some events are not handled yet.
   1648    // TODO we may try to add an assertion here.
   1649    // We will pretend that sender is blocked, that will cause the caller to
   1650    // stop trying.
   1651    *countRead = 0;
   1652    rv = NS_BASE_STREAM_WOULD_BLOCK;
   1653  }
   1654 
   1655  MOZ_ASSERT((*countRead != 0) || NS_FAILED(rv));
   1656  return rv;
   1657 }
   1658 
   1659 void Http3Session::ResetOrStopSendingRecvd(uint64_t aStreamId, uint64_t aError,
   1660                                           ResetType aType) {
   1661  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1662 
   1663  uint64_t sessionId = 0;
   1664  if (mWebTransportStreamToSessionMap.Get(aStreamId, &sessionId)) {
   1665    uint8_t wtError = Http3ErrorToWebTransportError(aError);
   1666    nsresult rv = GetNSResultFromWebTransportError(wtError);
   1667 
   1668    RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(aStreamId);
   1669    if (stream) {
   1670      if (aType == RESET) {
   1671        stream->SetRecvdReset();
   1672      }
   1673      RefPtr<Http3WebTransportStream> wtStream =
   1674          stream->GetHttp3WebTransportStream();
   1675      if (wtStream) {
   1676        CloseWebTransportStream(wtStream, rv);
   1677      }
   1678    }
   1679 
   1680    RefPtr<Http3StreamBase> session = mStreamIdHash.Get(sessionId);
   1681    if (session) {
   1682      Http3WebTransportSession* wtSession =
   1683          session->GetHttp3WebTransportSession();
   1684      MOZ_ASSERT(wtSession);
   1685      if (wtSession) {
   1686        if (aType == RESET) {
   1687          wtSession->OnStreamReset(aStreamId, rv);
   1688        } else {
   1689          wtSession->OnStreamStopSending(aStreamId, rv);
   1690        }
   1691      }
   1692    }
   1693    return;
   1694  }
   1695 
   1696  RefPtr<Http3StreamBase> stream = mStreamIdHash.Get(aStreamId);
   1697  if (!stream) {
   1698    return;
   1699  }
   1700 
   1701  RefPtr<Http3Stream> httpStream = stream->GetHttp3Stream();
   1702  if (!httpStream) {
   1703    return;
   1704  }
   1705 
   1706  // We only handle some of Http3 error as epecial, the rest are just equivalent
   1707  // to cancel.
   1708  if (aError == HTTP3_APP_ERROR_VERSION_FALLBACK) {
   1709    // We will restart the request and the alt-svc will be removed
   1710    // automatically.
   1711    // Also disable http3 we want http1.1.
   1712    httpStream->Transaction()->DisableHttp3(false);
   1713    httpStream->Transaction()->DisableSpdy();
   1714    CloseStream(stream, NS_ERROR_NET_RESET);
   1715  } else if (aError == HTTP3_APP_ERROR_REQUEST_REJECTED) {
   1716    // This request was rejected because server is probably busy or going away.
   1717    // We can restart the request using alt-svc. Without calling
   1718    // DoNotRemoveAltSvc the alt-svc route will be removed.
   1719    httpStream->Transaction()->DoNotRemoveAltSvc();
   1720    CloseStream(stream, NS_ERROR_NET_RESET);
   1721  } else {
   1722    if (httpStream->RecvdData()) {
   1723      CloseStream(stream, NS_ERROR_NET_PARTIAL_TRANSFER);
   1724    } else {
   1725      CloseStream(stream, NS_ERROR_NET_INTERRUPT);
   1726    }
   1727  }
   1728 }
   1729 
   1730 void Http3Session::SetConnection(nsAHttpConnection* aConn) {
   1731  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1732  mConnection = aConn;
   1733 }
   1734 
   1735 void Http3Session::GetSecurityCallbacks(nsIInterfaceRequestor** aOut) {
   1736  *aOut = nullptr;
   1737 }
   1738 
   1739 // TODO
   1740 void Http3Session::OnTransportStatus(nsITransport* aTransport, nsresult aStatus,
   1741                                     int64_t aProgress) {
   1742  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1743 
   1744  switch (aStatus) {
   1745      // These should appear only once, deliver to the first
   1746      // transaction on the session.
   1747    case NS_NET_STATUS_RESOLVING_HOST:
   1748    case NS_NET_STATUS_RESOLVED_HOST:
   1749    case NS_NET_STATUS_CONNECTING_TO:
   1750    case NS_NET_STATUS_CONNECTED_TO: {
   1751      if (!mFirstHttpTransaction) {
   1752        // if we still do not have a HttpTransaction store timings info in
   1753        // a HttpConnection.
   1754        // If some error occur it can happen that we do not have a connection.
   1755        if (mConnection) {
   1756          RefPtr<HttpConnectionBase> conn = mConnection->HttpConnection();
   1757          conn->SetEvent(aStatus);
   1758        }
   1759      } else {
   1760        mFirstHttpTransaction->OnTransportStatus(aTransport, aStatus,
   1761                                                 aProgress);
   1762      }
   1763 
   1764      if (aStatus == NS_NET_STATUS_CONNECTED_TO) {
   1765        mFirstHttpTransaction = nullptr;
   1766      }
   1767      break;
   1768    }
   1769 
   1770    default:
   1771      // The other transport events are ignored here because there is no good
   1772      // way to map them to the right transaction in HTTP3. Instead, the events
   1773      // are generated again from the HTTP3 code and passed directly to the
   1774      // correct transaction.
   1775 
   1776      // NS_NET_STATUS_SENDING_TO:
   1777      // This is generated by the socket transport when (part) of
   1778      // a transaction is written out
   1779      //
   1780      // There is no good way to map it to the right transaction in HTTP3,
   1781      // so it is ignored here and generated separately when the request
   1782      // is sent from Http3Stream.
   1783 
   1784      // NS_NET_STATUS_WAITING_FOR:
   1785      // Created by nsHttpConnection when the request has been totally sent.
   1786      // There is no good way to map it to the right transaction in HTTP3,
   1787      // so it is ignored here and generated separately when the same
   1788      // condition is complete in Http3Stream when there is no more
   1789      // request body left to be transmitted.
   1790 
   1791      // NS_NET_STATUS_RECEIVING_FROM
   1792      // Generated in Http3Stream whenever the stream reads data.
   1793 
   1794      break;
   1795  }
   1796 }
   1797 
   1798 bool Http3Session::IsDone() { return mState == CLOSED; }
   1799 
   1800 nsresult Http3Session::Status() {
   1801  MOZ_ASSERT(false, "Http3Session::Status()");
   1802  return NS_ERROR_UNEXPECTED;
   1803 }
   1804 
   1805 uint32_t Http3Session::Caps() {
   1806  MOZ_ASSERT(false, "Http3Session::Caps()");
   1807  return 0;
   1808 }
   1809 
   1810 nsresult Http3Session::ReadSegments(nsAHttpSegmentReader* reader,
   1811                                    uint32_t count, uint32_t* countRead) {
   1812  MOZ_ASSERT(false, "Http3Session::ReadSegments()");
   1813  return NS_ERROR_UNEXPECTED;
   1814 }
   1815 
   1816 nsresult Http3Session::SendData(nsIUDPSocket* socket) {
   1817  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1818 
   1819  LOG(("Http3Session::SendData [this=%p]", this));
   1820 
   1821  //   1) go through all streams/transactions that are ready to write and
   1822  //      write their data into quic streams (no network write yet).
   1823  //   2) call ProcessOutput that will loop until all available packets are
   1824  //      written to a socket or the socket returns an error code.
   1825  //   3) if we still have streams ready to write call ResumeSend()(we may
   1826  //      still have such streams because on an stream error we return earlier
   1827  //      to let the error be handled).
   1828  //   4)
   1829 
   1830  nsresult rv = NS_OK;
   1831  RefPtr<Http3StreamBase> stream;
   1832 
   1833  nsTArray<RefPtr<Http3StreamBase>> blockedStreams;
   1834 
   1835  // Step 1)
   1836  while (CanSendData() && (stream = mReadyForWrite.PopFront())) {
   1837    LOG(("Http3Session::SendData call ReadSegments from stream=%p [this=%p]",
   1838         stream.get(), this));
   1839    stream->SetInTxQueue(false);
   1840    if (stream->BlockedByFlowControl()) {
   1841      LOG(("stream %p blocked by flow control", stream.get()));
   1842      blockedStreams.AppendElement(stream);
   1843      continue;
   1844    }
   1845    rv = stream->ReadSegments();
   1846 
   1847    // on stream error we return earlier to let the error be handled.
   1848    if (NS_FAILED(rv)) {
   1849      LOG3(("Http3Session::SendData %p returns error code 0x%" PRIx32, this,
   1850            static_cast<uint32_t>(rv)));
   1851      MOZ_ASSERT(rv != NS_BASE_STREAM_WOULD_BLOCK);
   1852      if (rv == NS_BASE_STREAM_WOULD_BLOCK) {  // Just in case!
   1853        rv = NS_OK;
   1854      } else if (ASpdySession::SoftStreamError(rv)) {
   1855        CloseStream(stream, rv);
   1856        LOG3(("Http3Session::SendData %p soft error override\n", this));
   1857        rv = NS_OK;
   1858      } else {
   1859        break;
   1860      }
   1861    }
   1862  }
   1863 
   1864  if (NS_SUCCEEDED(rv)) {
   1865    // Step 2:
   1866    // Call actual network write.
   1867    rv = ProcessOutput(socket);
   1868  }
   1869 
   1870  // Step 3:
   1871  MaybeResumeSend();
   1872 
   1873  if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
   1874    rv = NS_OK;
   1875  }
   1876 
   1877  if (NS_FAILED(rv)) {
   1878    return rv;
   1879  }
   1880 
   1881  // Put the blocked streams back to the queue, since they are ready to write.
   1882  for (const auto& stream : blockedStreams) {
   1883    mReadyForWrite.Push(stream);
   1884    stream->SetInTxQueue(true);
   1885  }
   1886 
   1887  rv = ProcessEvents();
   1888 
   1889  // Let the connection know we sent some app data successfully.
   1890  if (stream && NS_SUCCEEDED(rv)) {
   1891    mUdpConn->NotifyDataWrite();
   1892  }
   1893 
   1894  return rv;
   1895 }
   1896 
   1897 void Http3Session::StreamReadyToWrite(Http3StreamBase* aStream) {
   1898  MOZ_ASSERT(aStream);
   1899  // Http3Session::StreamReadyToWrite can be called multiple times when we get
   1900  // duplicate DataWrite events from neqo at the same time. In this case, we
   1901  // only want to insert the stream in `mReadyForWrite` once.
   1902  if (aStream->IsInTxQueue()) {
   1903    return;
   1904  }
   1905 
   1906  mReadyForWrite.Push(aStream);
   1907  aStream->SetInTxQueue(true);
   1908  if (CanSendData() && mConnection) {
   1909    (void)mConnection->ResumeSend();
   1910  }
   1911 }
   1912 
   1913 void Http3Session::MaybeResumeSend() {
   1914  if ((mReadyForWrite.GetSize() > 0) && CanSendData() && mConnection) {
   1915    (void)mConnection->ResumeSend();
   1916  }
   1917 }
   1918 
   1919 nsresult Http3Session::ProcessSlowConsumers() {
   1920  if (mSlowConsumersReadyForRead.IsEmpty()) {
   1921    return NS_OK;
   1922  }
   1923 
   1924  RefPtr<Http3StreamBase> slowConsumer =
   1925      mSlowConsumersReadyForRead.ElementAt(0);
   1926  mSlowConsumersReadyForRead.RemoveElementAt(0);
   1927 
   1928  nsresult rv = ProcessTransactionRead(slowConsumer);
   1929 
   1930  return rv;
   1931 }
   1932 
   1933 nsresult Http3Session::WriteSegments(nsAHttpSegmentWriter* writer,
   1934                                     uint32_t count, uint32_t* countWritten) {
   1935  MOZ_ASSERT(false, "Http3Session::WriteSegments()");
   1936  return NS_ERROR_UNEXPECTED;
   1937 }
   1938 
   1939 nsresult Http3Session::RecvData(nsIUDPSocket* socket) {
   1940  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1941 
   1942  // Process slow consumers.
   1943  nsresult rv = ProcessSlowConsumers();
   1944  if (NS_FAILED(rv)) {
   1945    LOG3(("Http3Session %p ProcessSlowConsumers returns 0x%" PRIx32 "\n", this,
   1946          static_cast<uint32_t>(rv)));
   1947    return rv;
   1948  }
   1949 
   1950  rv = ProcessInput(socket);
   1951  if (NS_FAILED(rv)) {
   1952    return rv;
   1953  }
   1954 
   1955  rv = ProcessEvents();
   1956  if (NS_FAILED(rv)) {
   1957    return rv;
   1958  }
   1959 
   1960  rv = SendData(socket);
   1961  if (NS_FAILED(rv)) {
   1962    return rv;
   1963  }
   1964 
   1965  return NS_OK;
   1966 }
   1967 
   1968 const uint32_t HTTP3_TELEMETRY_APP_NECKO = 42;
   1969 
   1970 void Http3Session::Close(nsresult aReason) {
   1971  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   1972 
   1973  LOG(("Http3Session::Close [this=%p]", this));
   1974 
   1975  if (NS_FAILED(mError)) {
   1976    CloseInternal(false);
   1977  } else {
   1978    mError = aReason;
   1979    // If necko closes connection, this will map to the "closing" key and the
   1980    // value HTTP3_TELEMETRY_APP_NECKO.
   1981    glean::http3::connection_close_code.Get("app_closing"_ns)
   1982        .AccumulateSingleSample(HTTP3_TELEMETRY_APP_NECKO);
   1983    CloseInternal(true);
   1984  }
   1985 
   1986  if (mCleanShutdown || mIsClosedByNeqo || NS_FAILED(mSocketError)) {
   1987    // It is network-tear-down, a socker error or neqo is state CLOSED
   1988    // (it does not need to send any more packets or wait for new packets).
   1989    // We need to remove all references, so that
   1990    // Http3Session will be destroyed.
   1991    if (mTimer) {
   1992      mTimer->Cancel();
   1993    }
   1994    mTimer = nullptr;
   1995    mTimerCallback = nullptr;
   1996    mConnection = nullptr;
   1997    mUdpConn = nullptr;
   1998    mState = CLOSED;
   1999  }
   2000  if (mConnection) {
   2001    // resume sending to send CLOSE_CONNECTION frame.
   2002    (void)mConnection->ResumeSend();
   2003  }
   2004 }
   2005 
   2006 void Http3Session::CloseInternal(bool aCallNeqoClose) {
   2007  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2008 
   2009  if (IsClosing()) {
   2010    return;
   2011  }
   2012 
   2013  LOG(("Http3Session::Closing [this=%p]", this));
   2014 
   2015  if (mState != CONNECTED) {
   2016    mBeforeConnectedError = true;
   2017  }
   2018 
   2019 #ifndef ANDROID
   2020  if (mState == ZERORTT) {
   2021    ZeroRttTelemetry(aCallNeqoClose ? ZeroRttOutcome::USED_CONN_CLOSED_BY_NECKO
   2022                                    : ZeroRttOutcome::USED_CONN_ERROR);
   2023  }
   2024 #endif
   2025 
   2026  mState = CLOSING;
   2027  Shutdown();
   2028 
   2029  if (aCallNeqoClose) {
   2030    mHttp3Connection->Close(HTTP3_APP_ERROR_NO_ERROR);
   2031  }
   2032 
   2033  mStreamIdHash.Clear();
   2034  mStreamTransactionHash.Clear();
   2035 }
   2036 
   2037 nsHttpConnectionInfo* Http3Session::ConnectionInfo() {
   2038  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2039  RefPtr<nsHttpConnectionInfo> ci;
   2040  GetConnectionInfo(getter_AddRefs(ci));
   2041  return ci.get();
   2042 }
   2043 
   2044 void Http3Session::SetProxyConnectFailed() {
   2045  MOZ_ASSERT(false, "Http3Session::SetProxyConnectFailed()");
   2046 }
   2047 
   2048 nsHttpRequestHead* Http3Session::RequestHead() {
   2049  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2050  MOZ_ASSERT(false,
   2051             "Http3Session::RequestHead() "
   2052             "should not be called after http/3 is setup");
   2053  return nullptr;
   2054 }
   2055 
   2056 uint32_t Http3Session::Http1xTransactionCount() { return 0; }
   2057 
   2058 nsresult Http3Session::TakeSubTransactions(
   2059    nsTArray<RefPtr<nsAHttpTransaction>>& outTransactions) {
   2060  return NS_OK;
   2061 }
   2062 
   2063 //-----------------------------------------------------------------------------
   2064 // Pass through methods of nsAHttpConnection
   2065 //-----------------------------------------------------------------------------
   2066 
   2067 nsAHttpConnection* Http3Session::Connection() {
   2068  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2069  return mConnection;
   2070 }
   2071 
   2072 nsresult Http3Session::OnHeadersAvailable(nsAHttpTransaction* transaction,
   2073                                          nsHttpRequestHead* requestHead,
   2074                                          nsHttpResponseHead* responseHead,
   2075                                          bool* reset) {
   2076  MOZ_ASSERT(mConnection);
   2077  if (mConnection) {
   2078    return mConnection->OnHeadersAvailable(transaction, requestHead,
   2079                                           responseHead, reset);
   2080  }
   2081  return NS_OK;
   2082 }
   2083 
   2084 bool Http3Session::IsReused() {
   2085  if (mConnection) {
   2086    return mConnection->IsReused();
   2087  }
   2088  return true;
   2089 }
   2090 
   2091 nsresult Http3Session::PushBack(const char* buf, uint32_t len) {
   2092  return NS_ERROR_UNEXPECTED;
   2093 }
   2094 
   2095 already_AddRefed<HttpConnectionBase> Http3Session::TakeHttpConnection() {
   2096  LOG(("Http3Session::TakeHttpConnection %p", this));
   2097  return nullptr;
   2098 }
   2099 
   2100 already_AddRefed<HttpConnectionBase> Http3Session::HttpConnection() {
   2101  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2102  if (mConnection) {
   2103    return mConnection->HttpConnection();
   2104  }
   2105  return nullptr;
   2106 }
   2107 
   2108 void Http3Session::CloseTransaction(nsAHttpTransaction* aTransaction,
   2109                                    nsresult aResult) {
   2110  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2111  LOG3(("Http3Session::CloseTransaction %p %p 0x%" PRIx32, this, aTransaction,
   2112        static_cast<uint32_t>(aResult)));
   2113 
   2114  // Generally this arrives as a cancel event from the connection manager.
   2115 
   2116  // need to find the stream and call CloseStream() on it.
   2117  RefPtr<Http3StreamBase> stream = mStreamTransactionHash.Get(aTransaction);
   2118  if (!stream) {
   2119    LOG3(("Http3Session::CloseTransaction %p %p 0x%" PRIx32 " - not found.",
   2120          this, aTransaction, static_cast<uint32_t>(aResult)));
   2121    return;
   2122  }
   2123  LOG3(
   2124      ("Http3Session::CloseTransaction probably a cancel. this=%p, "
   2125       "trans=%p, result=0x%" PRIx32 ", streamId=0x%" PRIx64 " stream=%p",
   2126       this, aTransaction, static_cast<uint32_t>(aResult), stream->StreamId(),
   2127       stream.get()));
   2128  CloseStream(stream, aResult);
   2129  if (mConnection) {
   2130    (void)mConnection->ResumeSend();
   2131  }
   2132 }
   2133 
   2134 void Http3Session::CloseStream(Http3StreamBase* aStream, nsresult aResult) {
   2135  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2136  RefPtr<Http3WebTransportStream> wtStream =
   2137      aStream->GetHttp3WebTransportStream();
   2138  if (wtStream) {
   2139    CloseWebTransportStream(wtStream, aResult);
   2140    return;
   2141  }
   2142 
   2143  RefPtr<Http3Stream> httpStream = aStream->GetHttp3Stream();
   2144  if (httpStream && !httpStream->RecvdFin() && !httpStream->RecvdReset() &&
   2145      httpStream->HasStreamId()) {
   2146    mHttp3Connection->CancelFetch(httpStream->StreamId(),
   2147                                  HTTP3_APP_ERROR_REQUEST_CANCELLED);
   2148  }
   2149 
   2150  if ((NS_SUCCEEDED(aResult) || NS_BASE_STREAM_CLOSED == aResult) &&
   2151      mConnInfo->GetIsTrrServiceChannel()) {
   2152    // save time of last successful response
   2153    mLastTRRResponseTime = TimeStamp::Now();
   2154    mTrrStreams++;
   2155  }
   2156 
   2157  aStream->Close(aResult);
   2158  CloseStreamInternal(aStream, aResult);
   2159 }
   2160 
   2161 void Http3Session::CloseStreamInternal(Http3StreamBase* aStream,
   2162                                       nsresult aResult) {
   2163  LOG3(("Http3Session::CloseStreamInternal %p %p 0x%" PRIx32, this, aStream,
   2164        static_cast<uint32_t>(aResult)));
   2165  if (aStream->HasStreamId()) {
   2166    // We know the transaction reusing an idle connection has succeeded or
   2167    // failed.
   2168    if (mFirstStreamIdReuseIdleConnection.isSome() &&
   2169        aStream->StreamId() == *mFirstStreamIdReuseIdleConnection) {
   2170      MOZ_ASSERT(mConnectionIdleStart);
   2171      MOZ_ASSERT(mConnectionIdleEnd);
   2172 
   2173 #ifndef ANDROID
   2174      if (mConnectionIdleStart) {
   2175        mozilla::glean::netwerk::http3_time_to_reuse_idle_connection
   2176            .Get(NS_SUCCEEDED(aResult) ? "succeeded"_ns : "failed"_ns)
   2177            .AccumulateRawDuration(mConnectionIdleEnd - mConnectionIdleStart);
   2178      }
   2179 #endif
   2180 
   2181      mConnectionIdleStart = TimeStamp();
   2182      mConnectionIdleEnd = TimeStamp();
   2183      mFirstStreamIdReuseIdleConnection.reset();
   2184    }
   2185 
   2186    mStreamIdHash.Remove(aStream->StreamId());
   2187 
   2188    // Start to idle when we remove the last stream.
   2189    if (mStreamIdHash.IsEmpty()) {
   2190      mConnectionIdleStart = TimeStamp::Now();
   2191    }
   2192  }
   2193  RemoveStreamFromQueues(aStream);
   2194  if (nsAHttpTransaction* transaction = aStream->Transaction()) {
   2195    mStreamTransactionHash.Remove(transaction);
   2196  }
   2197  mWebTransportSessions.RemoveElement(aStream);
   2198  mWebTransportStreams.RemoveElement(aStream);
   2199  mTunnelStreams.RemoveElement(aStream);
   2200  // Close(NS_OK) implies that the NeqoHttp3Conn will be closed, so we can only
   2201  // do this when there is no Http3Steeam, WebTransportSession and
   2202  // WebTransportStream.
   2203  if ((mShouldClose || mGoawayReceived) && HasNoActiveStreams()) {
   2204    MOZ_ASSERT(!IsClosing());
   2205    Close(NS_OK);
   2206  }
   2207 }
   2208 
   2209 void Http3Session::CloseWebTransportStream(Http3WebTransportStream* aStream,
   2210                                           nsresult aResult) {
   2211  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2212  LOG3(("Http3Session::CloseWebTransportStream %p %p 0x%" PRIx32, this, aStream,
   2213        static_cast<uint32_t>(aResult)));
   2214  if (aStream && !aStream->RecvdFin() && !aStream->RecvdReset() &&
   2215      (aStream->HasStreamId())) {
   2216    mHttp3Connection->ResetStream(aStream->StreamId(),
   2217                                  HTTP3_APP_ERROR_REQUEST_CANCELLED);
   2218  }
   2219 
   2220  aStream->Close(aResult);
   2221  CloseStreamInternal(aStream, aResult);
   2222 }
   2223 
   2224 void Http3Session::ResetWebTransportStream(Http3WebTransportStream* aStream,
   2225                                           uint64_t aErrorCode) {
   2226  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2227  LOG3(("Http3Session::ResetWebTransportStream %p %p 0x%" PRIx64, this, aStream,
   2228        aErrorCode));
   2229  mHttp3Connection->ResetStream(aStream->StreamId(), aErrorCode);
   2230 }
   2231 
   2232 void Http3Session::StreamStopSending(Http3WebTransportStream* aStream,
   2233                                     uint8_t aErrorCode) {
   2234  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2235  LOG(("Http3Session::StreamStopSending %p %p 0x%" PRIx32, this, aStream,
   2236       static_cast<uint32_t>(aErrorCode)));
   2237  mHttp3Connection->StreamStopSending(aStream->StreamId(), aErrorCode);
   2238 }
   2239 
   2240 nsresult Http3Session::TakeTransport(nsISocketTransport**,
   2241                                     nsIAsyncInputStream**,
   2242                                     nsIAsyncOutputStream**) {
   2243  MOZ_ASSERT(false, "TakeTransport of Http3Session");
   2244  return NS_ERROR_UNEXPECTED;
   2245 }
   2246 
   2247 WebTransportSessionBase* Http3Session::GetWebTransportSession(
   2248    nsAHttpTransaction* aTransaction) {
   2249  RefPtr<Http3StreamBase> stream = mStreamTransactionHash.Get(aTransaction);
   2250 
   2251  if (!stream || !stream->GetHttp3WebTransportSession()) {
   2252    MOZ_ASSERT(false, "There must be a stream");
   2253    return nullptr;
   2254  }
   2255  RemoveStreamFromQueues(stream);
   2256  mStreamTransactionHash.Remove(aTransaction);
   2257  mWebTransportSessions.AppendElement(stream);
   2258  return stream->GetHttp3WebTransportSession();
   2259 }
   2260 
   2261 bool Http3Session::IsPersistent() { return true; }
   2262 
   2263 void Http3Session::DontReuse() {
   2264  LOG3(("Http3Session::DontReuse %p\n", this));
   2265  if (!OnSocketThread()) {
   2266    LOG3(("Http3Session %p not on socket thread\n", this));
   2267    nsCOMPtr<nsIRunnable> event = NewRunnableMethod(
   2268        "Http3Session::DontReuse", this, &Http3Session::DontReuse);
   2269    gSocketTransportService->Dispatch(event, NS_DISPATCH_NORMAL);
   2270    return;
   2271  }
   2272 
   2273  if (mGoawayReceived || IsClosing()) {
   2274    return;
   2275  }
   2276 
   2277  mShouldClose = true;
   2278  if (HasNoActiveStreams()) {
   2279    // This is a temporary workaround and should be fixed properly in Happy
   2280    // Eyeballs project. We should not exclude this domain if
   2281    // Http3Session::DontReuse is called from
   2282    // ConnectionEntry::MakeAllDontReuseExcept.
   2283    if (mUdpConn &&
   2284        mUdpConn->CloseReason() ==
   2285            ConnectionCloseReason::CLOSE_EXISTING_CONN_FOR_COALESCING) {
   2286      mDontExclude = true;
   2287    }
   2288    Close(NS_OK);
   2289  }
   2290 }
   2291 
   2292 void Http3Session::CloseWebTransportConn() {
   2293  LOG3(("Http3Session::CloseWebTransportConn %p\n", this));
   2294  // We need to dispatch, since Http3Session could be released in
   2295  // HttpConnectionUDP::CloseTransaction.
   2296  gSocketTransportService->Dispatch(
   2297      NS_NewRunnableFunction("Http3Session::CloseWebTransportConn",
   2298                             [self = RefPtr{this}]() {
   2299                               if (self->mUdpConn) {
   2300                                 self->mUdpConn->CloseTransaction(
   2301                                     self, NS_ERROR_ABORT);
   2302                               }
   2303                             }),
   2304      NS_DISPATCH_NORMAL);
   2305 }
   2306 
   2307 void Http3Session::CurrentBrowserIdChanged(uint64_t id) {
   2308  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2309 
   2310  mCurrentBrowserId = id;
   2311 
   2312  for (const auto& stream : mStreamTransactionHash.Values()) {
   2313    RefPtr<Http3Stream> httpStream = stream->GetHttp3Stream();
   2314    if (httpStream) {
   2315      httpStream->CurrentBrowserIdChanged(id);
   2316    }
   2317  }
   2318 }
   2319 
   2320 // This is called by Http3Stream::OnWriteSegment.
   2321 nsresult Http3Session::ReadResponseData(uint64_t aStreamId, char* aBuf,
   2322                                        uint32_t aCount,
   2323                                        uint32_t* aCountWritten, bool* aFin) {
   2324  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2325 
   2326  nsresult rv = mHttp3Connection->ReadResponseData(aStreamId, (uint8_t*)aBuf,
   2327                                                   aCount, aCountWritten, aFin);
   2328 
   2329  // This should not happen, i.e. stream must be present in neqo and in necko at
   2330  // the same time.
   2331  MOZ_ASSERT(rv != NS_ERROR_INVALID_ARG);
   2332  if (NS_FAILED(rv)) {
   2333    LOG3(("Http3Session::ReadResponseData return an error %" PRIx32
   2334          " [this=%p]",
   2335          static_cast<uint32_t>(rv), this));
   2336    // This error will be handled by neqo and the whole connection will be
   2337    // closed. We will return NS_BASE_STREAM_WOULD_BLOCK here.
   2338    *aCountWritten = 0;
   2339    *aFin = false;
   2340    rv = NS_BASE_STREAM_WOULD_BLOCK;
   2341  }
   2342 
   2343  MOZ_ASSERT((*aCountWritten != 0) || aFin || NS_FAILED(rv));
   2344  return rv;
   2345 }
   2346 
   2347 void Http3Session::TransactionHasDataToWrite(nsAHttpTransaction* caller) {
   2348  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2349  LOG3(("Http3Session::TransactionHasDataToWrite %p trans=%p", this, caller));
   2350 
   2351  // a trapped signal from the http transaction to the connection that
   2352  // it is no longer blocked on read.
   2353 
   2354  RefPtr<Http3StreamBase> stream = mStreamTransactionHash.Get(caller);
   2355  if (!stream) {
   2356    LOG3(("Http3Session::TransactionHasDataToWrite %p caller %p not found",
   2357          this, caller));
   2358    return;
   2359  }
   2360 
   2361  LOG3(("Http3Session::TransactionHasDataToWrite %p ID is 0x%" PRIx64, this,
   2362        stream->StreamId()));
   2363 
   2364  StreamHasDataToWrite(stream);
   2365 }
   2366 
   2367 void Http3Session::StreamHasDataToWrite(Http3StreamBase* aStream) {
   2368  if (!IsClosing()) {
   2369    StreamReadyToWrite(aStream);
   2370  } else {
   2371    LOG3(
   2372        ("Http3Session::TransactionHasDataToWrite %p closed so not setting "
   2373         "Ready4Write\n",
   2374         this));
   2375  }
   2376 
   2377  // NSPR poll will not poll the network if there are non system PR_FileDesc's
   2378  // that are ready - so we can get into a deadlock waiting for the system IO
   2379  // to come back here if we don't force the send loop manually.
   2380  (void)ForceSend();
   2381 }
   2382 
   2383 void Http3Session::TransactionHasDataToRecv(nsAHttpTransaction* caller) {
   2384  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2385  LOG3(("Http3Session::TransactionHasDataToRecv %p trans=%p", this, caller));
   2386 
   2387  // a signal from the http transaction to the connection that it will consume
   2388  // more
   2389  RefPtr<Http3StreamBase> stream = mStreamTransactionHash.Get(caller);
   2390  if (!stream) {
   2391    LOG3(("Http3Session::TransactionHasDataToRecv %p caller %p not found", this,
   2392          caller));
   2393    return;
   2394  }
   2395 
   2396  LOG3(("Http3Session::TransactionHasDataToRecv %p ID is 0x%" PRIx64 "\n", this,
   2397        stream->StreamId()));
   2398  ConnectSlowConsumer(stream);
   2399 }
   2400 
   2401 void Http3Session::ConnectSlowConsumer(Http3StreamBase* stream) {
   2402  LOG3(("Http3Session::ConnectSlowConsumer %p 0x%" PRIx64 "\n", this,
   2403        stream->StreamId()));
   2404  mSlowConsumersReadyForRead.AppendElement(stream);
   2405  (void)ForceRecv();
   2406 }
   2407 
   2408 bool Http3Session::TestJoinConnection(const nsACString& hostname,
   2409                                      int32_t port) {
   2410  return RealJoinConnection(hostname, port, true);
   2411 }
   2412 
   2413 bool Http3Session::JoinConnection(const nsACString& hostname, int32_t port) {
   2414  return RealJoinConnection(hostname, port, false);
   2415 }
   2416 
   2417 // TODO test
   2418 bool Http3Session::RealJoinConnection(const nsACString& hostname, int32_t port,
   2419                                      bool justKidding) {
   2420  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2421  if (!mConnection || !CanSendData() || mShouldClose || mGoawayReceived) {
   2422    return false;
   2423  }
   2424 
   2425  nsHttpConnectionInfo* ci = ConnectionInfo();
   2426  if (ci->UsingProxy()) {
   2427    MOZ_ASSERT(false,
   2428               "RealJoinConnection should not be called when using proxy");
   2429    return false;
   2430  }
   2431 
   2432  if (nsCString(hostname).EqualsIgnoreCase(ci->Origin()) &&
   2433      (port == ci->OriginPort())) {
   2434    return true;
   2435  }
   2436 
   2437  nsAutoCString key(hostname);
   2438  key.Append(':');
   2439  key.Append(justKidding ? 'k' : '.');
   2440  key.AppendInt(port);
   2441  bool cachedResult;
   2442  if (mJoinConnectionCache.Get(key, &cachedResult)) {
   2443    LOG(("joinconnection [%p %s] %s result=%d cache\n", this,
   2444         ConnectionInfo()->HashKey().get(), key.get(), cachedResult));
   2445    return cachedResult;
   2446  }
   2447 
   2448  nsresult rv;
   2449  bool isJoined = false;
   2450 
   2451  nsCOMPtr<nsITLSSocketControl> sslSocketControl;
   2452  mConnection->GetTLSSocketControl(getter_AddRefs(sslSocketControl));
   2453  if (!sslSocketControl) {
   2454    return false;
   2455  }
   2456 
   2457  bool joinedReturn = false;
   2458  if (justKidding) {
   2459    rv = sslSocketControl->TestJoinConnection(mConnInfo->GetNPNToken(),
   2460                                              hostname, port, &isJoined);
   2461  } else {
   2462    rv = sslSocketControl->JoinConnection(mConnInfo->GetNPNToken(), hostname,
   2463                                          port, &isJoined);
   2464  }
   2465  if (NS_SUCCEEDED(rv) && isJoined) {
   2466    joinedReturn = true;
   2467  }
   2468 
   2469  LOG(("joinconnection [%p %s] %s result=%d lookup\n", this,
   2470       ConnectionInfo()->HashKey().get(), key.get(), joinedReturn));
   2471  mJoinConnectionCache.InsertOrUpdate(key, joinedReturn);
   2472  if (!justKidding) {
   2473    // cache a kidding entry too as this one is good for both
   2474    nsAutoCString key2(hostname);
   2475    key2.Append(':');
   2476    key2.Append('k');
   2477    key2.AppendInt(port);
   2478    if (!mJoinConnectionCache.Get(key2)) {
   2479      mJoinConnectionCache.InsertOrUpdate(key2, joinedReturn);
   2480    }
   2481  }
   2482  return joinedReturn;
   2483 }
   2484 
   2485 void Http3Session::CallCertVerification(Maybe<nsCString> aEchPublicName) {
   2486  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2487  LOG(("Http3Session::CallCertVerification [this=%p]", this));
   2488 
   2489  NeqoCertificateInfo certInfo;
   2490  if (NS_FAILED(mHttp3Connection->PeerCertificateInfo(&certInfo))) {
   2491    LOG(("Http3Session::CallCertVerification [this=%p] - no cert", this));
   2492    mHttp3Connection->PeerAuthenticated(SSL_ERROR_BAD_CERTIFICATE);
   2493    mError = psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERTIFICATE);
   2494    return;
   2495  }
   2496 
   2497  if (mConnInfo->GetWebTransport()) {
   2498    // if our connection is webtransport, we might do a verification
   2499    // based on serverCertificatedHashes
   2500    const nsTArray<RefPtr<nsIWebTransportHash>>* servCertHashes =
   2501        gHttpHandler->ConnMgr()->GetServerCertHashes(mConnInfo);
   2502    if (servCertHashes && !servCertHashes->IsEmpty() &&
   2503        certInfo.certs.Length() >= 1) {
   2504      // ok, we verify based on serverCertificateHashes
   2505      mozilla::pkix::Result rv = AuthCertificateWithServerCertificateHashes(
   2506          certInfo.certs[0], *servCertHashes);
   2507      if (rv != mozilla::pkix::Result::Success) {
   2508        // ok we failed, report it back
   2509        LOG(
   2510            ("Http3Session::CallCertVerification [this=%p] "
   2511             "AuthCertificateWithServerCertificateHashes failed",
   2512             this));
   2513        mHttp3Connection->PeerAuthenticated(SSL_ERROR_BAD_CERTIFICATE);
   2514        mError = psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERTIFICATE);
   2515        return;
   2516      }
   2517      // ok, we succeded
   2518      Authenticated(0, true);
   2519      return;
   2520    }
   2521  }
   2522 
   2523  Maybe<nsTArray<nsTArray<uint8_t>>> stapledOCSPResponse;
   2524  if (certInfo.stapled_ocsp_responses_present) {
   2525    stapledOCSPResponse.emplace(std::move(certInfo.stapled_ocsp_responses));
   2526  }
   2527 
   2528  Maybe<nsTArray<uint8_t>> sctsFromTLSExtension;
   2529  if (certInfo.signed_cert_timestamp_present) {
   2530    sctsFromTLSExtension.emplace(std::move(certInfo.signed_cert_timestamp));
   2531  }
   2532 
   2533  uint32_t providerFlags;
   2534  // the return value is always NS_OK, just ignore it.
   2535  (void)mSocketControl->GetProviderFlags(&providerFlags);
   2536 
   2537  nsCString echConfig;
   2538  nsresult nsrv = mSocketControl->GetEchConfig(echConfig);
   2539  bool verifyToEchPublicName = NS_SUCCEEDED(nsrv) && !echConfig.IsEmpty() &&
   2540                               aEchPublicName && !aEchPublicName->IsEmpty();
   2541  const nsACString& hostname =
   2542      verifyToEchPublicName ? *aEchPublicName : mSocketControl->GetHostName();
   2543 
   2544  SECStatus rv = psm::AuthCertificateHookWithInfo(
   2545      mSocketControl, hostname, static_cast<const void*>(this),
   2546      std::move(certInfo.certs), stapledOCSPResponse, sctsFromTLSExtension,
   2547      providerFlags);
   2548  if ((rv != SECSuccess) && (rv != SECWouldBlock)) {
   2549    LOG(("Http3Session::CallCertVerification [this=%p] AuthCertificate failed",
   2550         this));
   2551    mHttp3Connection->PeerAuthenticated(SSL_ERROR_BAD_CERTIFICATE);
   2552    mError = psm::GetXPCOMFromNSSError(SSL_ERROR_BAD_CERTIFICATE);
   2553  }
   2554 }
   2555 
   2556 void Http3Session::Authenticated(int32_t aError,
   2557                                 bool aServCertHashesSucceeded) {
   2558  LOG(("Http3Session::Authenticated error=0x%" PRIx32 " [this=%p].", aError,
   2559       this));
   2560  if ((mState == INITIALIZING) || (mState == ZERORTT)) {
   2561    if (psm::IsNSSErrorCode(aError)) {
   2562      mError = psm::GetXPCOMFromNSSError(aError);
   2563      LOG(("Http3Session::Authenticated psm-error=0x%" PRIx32 " [this=%p].",
   2564           static_cast<uint32_t>(mError), this));
   2565    } else if (StaticPrefs::
   2566                   network_http_http3_disable_when_third_party_roots_found()) {
   2567      // In test, we use another perf value to override the value of
   2568      // hasThirdPartyRoots.
   2569      bool hasThirdPartyRoots =
   2570          (xpc::IsInAutomation() || PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR"))
   2571              ? StaticPrefs::
   2572                    network_http_http3_has_third_party_roots_found_in_automation()
   2573              : !mSocketControl->IsBuiltCertChainRootBuiltInRoot();
   2574      LOG(
   2575          ("Http3Session::Authenticated [this=%p, hasThirdPartyRoots=%d, "
   2576           "servCertHashesSucceeded=%d]",
   2577           this, hasThirdPartyRoots, aServCertHashesSucceeded));
   2578      // If serverCertificateHashes is used a thirdPartyRoot is legal
   2579      if (hasThirdPartyRoots && !aServCertHashesSucceeded) {
   2580        if (mFirstHttpTransaction) {
   2581          mFirstHttpTransaction->DisableHttp3(false);
   2582        }
   2583        mUdpConn->CloseTransaction(this, NS_ERROR_NET_RESET);
   2584        return;
   2585      }
   2586    }
   2587    mHttp3Connection->PeerAuthenticated(aError);
   2588 
   2589    // Call OnQuicTimeoutExpired to properly process neqo events and outputs.
   2590    // We call OnQuicTimeoutExpired instead of ProcessOutputAndEvents, because
   2591    // HttpConnectionUDP must close this session in case of an error.
   2592    NS_DispatchToCurrentThread(
   2593        NewRunnableMethod("net::HttpConnectionUDP::OnQuicTimeoutExpired",
   2594                          mUdpConn, &HttpConnectionUDP::OnQuicTimeoutExpired));
   2595    mUdpConn->ChangeConnectionState(ConnectionState::TRANSFERING);
   2596  }
   2597 }
   2598 
   2599 void Http3Session::SetSecInfo() {
   2600  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2601  NeqoSecretInfo secInfo;
   2602  if (NS_SUCCEEDED(mHttp3Connection->GetSecInfo(&secInfo))) {
   2603    mSocketControl->SetSSLVersionUsed(secInfo.version);
   2604    mSocketControl->SetResumed(secInfo.resumed);
   2605    mSocketControl->SetNegotiatedNPN(secInfo.alpn);
   2606 
   2607    mSocketControl->SetInfo(secInfo.cipher, secInfo.version, secInfo.group,
   2608                            secInfo.signature_scheme, secInfo.ech_accepted);
   2609    mHandshakeSucceeded = true;
   2610  }
   2611 
   2612  if (!mSocketControl->HasServerCert()) {
   2613    mSocketControl->RebuildCertificateInfoFromSSLTokenCache();
   2614  }
   2615 }
   2616 
   2617 // Transport error have values from 0x0 to 0x11.
   2618 // (https://tools.ietf.org/html/draft-ietf-quic-transport-34#section-20.1)
   2619 // We will map this error to 0-16.
   2620 // 17 will capture error codes between and including 0x12 and 0x0ff. This
   2621 // error codes are not define by the spec but who know peer may sent them.
   2622 // CryptoAlerts have value 0x100 + alert code. The range of alert code is
   2623 // 0x00-0xff. (https://tools.ietf.org/html/draft-ietf-quic-tls_34#section-4.8)
   2624 // Since telemetry does not allow more than 100 bucket, we use three diffrent
   2625 // keys to map all alert codes.
   2626 const uint32_t HTTP3_TELEMETRY_TRANSPORT_INTERNAL_ERROR = 15;
   2627 const uint32_t HTTP3_TELEMETRY_TRANSPORT_END = 16;
   2628 const uint32_t HTTP3_TELEMETRY_TRANSPORT_UNKNOWN = 17;
   2629 const uint32_t HTTP3_TELEMETRY_TRANSPORT_CRYPTO_UNKNOWN = 18;
   2630 // All errors from CloseError::Tag::CryptoError will be map to 19
   2631 const uint32_t HTTP3_TELEMETRY_CRYPTO_ERROR = 19;
   2632 
   2633 uint64_t GetCryptoAlertCode(nsCString& key, uint64_t error) {
   2634  if (error < 100) {
   2635    key.Append("_a"_ns);
   2636    return error;
   2637  }
   2638  if (error < 200) {
   2639    error -= 100;
   2640    key.Append("_b"_ns);
   2641    return error;
   2642  }
   2643  if (error < 256) {
   2644    error -= 200;
   2645    key.Append("_c"_ns);
   2646    return error;
   2647  }
   2648  return HTTP3_TELEMETRY_TRANSPORT_CRYPTO_UNKNOWN;
   2649 }
   2650 
   2651 uint64_t GetTransportErrorCodeForTelemetry(nsCString& key, uint64_t error) {
   2652  if (error <= HTTP3_TELEMETRY_TRANSPORT_END) {
   2653    return error;
   2654  }
   2655  if (error < 0x100) {
   2656    return HTTP3_TELEMETRY_TRANSPORT_UNKNOWN;
   2657  }
   2658 
   2659  return GetCryptoAlertCode(key, error - 0x100);
   2660 }
   2661 
   2662 // Http3 error codes are 0x100-0x110.
   2663 // (https://tools.ietf.org/html/draft-ietf-quic-http-33#section-8.1)
   2664 // The mapping is described below.
   2665 // 0x00-0x10 mapped to 0-16
   2666 // 0x11-0xff mapped to 17
   2667 // 0x100-0x110 mapped to 18-36
   2668 // 0x111-0x1ff mapped to 37
   2669 // 0x200-0x202 mapped to 38-40
   2670 // Others mapped to 41
   2671 const uint32_t HTTP3_TELEMETRY_APP_UNKNOWN_1 = 17;
   2672 const uint32_t HTTP3_TELEMETRY_APP_START = 18;
   2673 // Values between 0x111 and 0x1ff are no definded and will be map to 18.
   2674 const uint32_t HTTP3_TELEMETRY_APP_UNKNOWN_2 = 37;
   2675 // Error codes between 0x200 and 0x202 are related to qpack.
   2676 // (https://tools.ietf.org/html/draft-ietf-quic-qpack-20#section-6)
   2677 // They will be mapped to 19-21
   2678 const uint32_t HTTP3_TELEMETRY_APP_QPACK_START = 38;
   2679 // Values greater or equal to 0x203 are no definded and will be map to 41.
   2680 const uint32_t HTTP3_TELEMETRY_APP_UNKNOWN_3 = 41;
   2681 
   2682 uint64_t GetAppErrorCodeForTelemetry(uint64_t error) {
   2683  if (error <= 0x10) {
   2684    return error;
   2685  }
   2686  if (error <= 0xff) {
   2687    return HTTP3_TELEMETRY_APP_UNKNOWN_1;
   2688  }
   2689  if (error <= 0x110) {
   2690    return error - 0x100 + HTTP3_TELEMETRY_APP_START;
   2691  }
   2692  if (error < 0x200) {
   2693    return HTTP3_TELEMETRY_APP_UNKNOWN_2;
   2694  }
   2695  if (error <= 0x202) {
   2696    return error - 0x200 + HTTP3_TELEMETRY_APP_QPACK_START;
   2697  }
   2698  return HTTP3_TELEMETRY_APP_UNKNOWN_3;
   2699 }
   2700 
   2701 void Http3Session::CloseConnectionTelemetry(CloseError& aError, bool aClosing) {
   2702  uint64_t value = 0;
   2703  nsCString key = EmptyCString();
   2704 
   2705  switch (aError.tag) {
   2706    case CloseError::Tag::TransportInternalError:
   2707      key = "transport_internal"_ns;
   2708      value = HTTP3_TELEMETRY_TRANSPORT_INTERNAL_ERROR;
   2709      break;
   2710    case CloseError::Tag::TransportInternalErrorOther:
   2711      key = "transport_other"_ns;
   2712      value = aError.transport_internal_error_other._0;
   2713      break;
   2714    case CloseError::Tag::TransportError:
   2715      key = "transport"_ns;
   2716      value = GetTransportErrorCodeForTelemetry(key, aError.transport_error._0);
   2717      break;
   2718    case CloseError::Tag::CryptoError:
   2719      key = "transport"_ns;
   2720      value = HTTP3_TELEMETRY_CRYPTO_ERROR;
   2721      break;
   2722    case CloseError::Tag::CryptoAlert:
   2723      key = "transport_crypto_alert"_ns;
   2724      value = GetCryptoAlertCode(key, aError.crypto_alert._0);
   2725      break;
   2726    case CloseError::Tag::PeerAppError:
   2727      key = "peer_app"_ns;
   2728      value = GetAppErrorCodeForTelemetry(aError.peer_app_error._0);
   2729      break;
   2730    case CloseError::Tag::PeerError:
   2731      key = "peer_transport"_ns;
   2732      value = GetTransportErrorCodeForTelemetry(key, aError.peer_error._0);
   2733      break;
   2734    case CloseError::Tag::AppError:
   2735      key = "app"_ns;
   2736      value = GetAppErrorCodeForTelemetry(aError.app_error._0);
   2737      break;
   2738    case CloseError::Tag::EchRetry:
   2739      key = "transport_crypto_alert"_ns;
   2740      value = 100;
   2741  }
   2742 
   2743  MOZ_DIAGNOSTIC_ASSERT(value <= 100);
   2744 
   2745  key.Append(aClosing ? "_closing"_ns : "_closed"_ns);
   2746 
   2747  glean::http3::connection_close_code.Get(key).AccumulateSingleSample(value);
   2748 
   2749  Http3Stats stats{};
   2750  mHttp3Connection->GetStats(&stats);
   2751 
   2752  if (stats.packets_tx > 0) {
   2753    unsigned long loss = (stats.lost * 10000) / stats.packets_tx;
   2754    glean::http3::loss_ratio.AccumulateSingleSample(loss);
   2755 
   2756    glean::http3::late_ack.EnumGet(glean::http3::LateAckLabel::eAck)
   2757        .AccumulateSingleSample(stats.late_ack);
   2758    glean::http3::late_ack.EnumGet(glean::http3::LateAckLabel::ePto)
   2759        .AccumulateSingleSample(stats.pto_ack);
   2760 
   2761    unsigned long late_ack_ratio = (stats.late_ack * 10000) / stats.packets_tx;
   2762    unsigned long pto_ack_ratio = (stats.pto_ack * 10000) / stats.packets_tx;
   2763    glean::http3::late_ack_ratio.EnumGet(glean::http3::LateAckRatioLabel::eAck)
   2764        .AccumulateSingleSample(late_ack_ratio);
   2765    glean::http3::late_ack_ratio.EnumGet(glean::http3::LateAckRatioLabel::ePto)
   2766        .AccumulateSingleSample(pto_ack_ratio);
   2767 
   2768    for (uint32_t i = 0; i < MAX_PTO_COUNTS; i++) {
   2769      nsAutoCString key;
   2770      key.AppendInt(i);
   2771      glean::http3::counts_pto.Get(key).AccumulateSingleSample(
   2772          stats.pto_counts[i]);
   2773    }
   2774 
   2775    glean::http3::drop_dgrams.AccumulateSingleSample(stats.dropped_rx);
   2776    glean::http3::saved_dgrams.AccumulateSingleSample(stats.saved_datagrams);
   2777  }
   2778 
   2779  glean::http3::received_sent_dgrams
   2780      .EnumGet(glean::http3::ReceivedSentDgramsLabel::eReceived)
   2781      .AccumulateSingleSample(stats.packets_rx);
   2782  glean::http3::received_sent_dgrams
   2783      .EnumGet(glean::http3::ReceivedSentDgramsLabel::eSent)
   2784      .AccumulateSingleSample(stats.packets_tx);
   2785 
   2786  if (aClosing) {
   2787    RefPtr<nsHttpConnectionInfo> ci;
   2788    GetConnectionInfo(getter_AddRefs(ci));
   2789    if (ci && ci->GetIsTrrServiceChannel() && !mLastTRRResponseTime.IsNull() &&
   2790        (mGoawayReceived ||
   2791         (aError.tag == CloseError::Tag::PeerAppError &&
   2792          aError.peer_app_error._0 == HTTP3_APP_ERROR_NO_ERROR))) {
   2793      // Record telemetry keyed by TRR provider.
   2794      glean::network::trr_idle_close_time_h3.Get(TRRProviderKey())
   2795          .AccumulateRawDuration(TimeStamp::Now() - mLastTRRResponseTime);
   2796      mLastTRRResponseTime = TimeStamp();
   2797    }
   2798  }
   2799 }
   2800 
   2801 void Http3Session::Finish0Rtt(bool aRestart) {
   2802  for (size_t i = 0; i < m0RTTStreams.Length(); ++i) {
   2803    if (m0RTTStreams[i]) {
   2804      if (aRestart) {
   2805        // When we need to restart transactions remove them from all lists.
   2806        if (m0RTTStreams[i]->HasStreamId()) {
   2807          mStreamIdHash.Remove(m0RTTStreams[i]->StreamId());
   2808        }
   2809        RemoveStreamFromQueues(m0RTTStreams[i]);
   2810        // The stream is ready to write again.
   2811        mReadyForWrite.Push(m0RTTStreams[i]);
   2812      }
   2813      m0RTTStreams[i]->Finish0RTT(aRestart);
   2814    }
   2815  }
   2816 
   2817  for (size_t i = 0; i < mCannotDo0RTTStreams.Length(); ++i) {
   2818    if (mCannotDo0RTTStreams[i]) {
   2819      mReadyForWrite.Push(mCannotDo0RTTStreams[i]);
   2820    }
   2821  }
   2822  m0RTTStreams.Clear();
   2823  mCannotDo0RTTStreams.Clear();
   2824  MaybeResumeSend();
   2825 }
   2826 
   2827 void Http3Session::ReportHttp3Connection() {
   2828  if (CanSendData() && !mHttp3ConnectionReported) {
   2829    mHttp3ConnectionReported = true;
   2830    gHttpHandler->ConnMgr()->ReportHttp3Connection(mUdpConn);
   2831    MaybeResumeSend();
   2832  }
   2833 }
   2834 
   2835 #ifndef ANDROID
   2836 void Http3Session::EchOutcomeTelemetry() {
   2837  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
   2838 
   2839  glean::http3::EchOutcomeLabel label;
   2840  switch (mEchExtensionStatus) {
   2841    case EchExtensionStatus::kNotPresent:
   2842      label = glean::http3::EchOutcomeLabel::eNone;
   2843      break;
   2844    case EchExtensionStatus::kGREASE:
   2845      label = glean::http3::EchOutcomeLabel::eGrease;
   2846      break;
   2847    case EchExtensionStatus::kReal:
   2848      label = glean::http3::EchOutcomeLabel::eReal;
   2849      break;
   2850  }
   2851 
   2852  glean::http3::ech_outcome.EnumGet(label).AccumulateSingleSample(
   2853      mHandshakeSucceeded ? 0 : 1);
   2854 }
   2855 
   2856 void Http3Session::ZeroRttTelemetry(ZeroRttOutcome aOutcome) {
   2857  nsAutoCString key;
   2858 
   2859  switch (aOutcome) {
   2860    case USED_SUCCEEDED:
   2861      key = "succeeded"_ns;
   2862      break;
   2863    case USED_REJECTED:
   2864      key = "rejected"_ns;
   2865      break;
   2866    case USED_CONN_ERROR:
   2867      key = "conn_error"_ns;
   2868      break;
   2869    case USED_CONN_CLOSED_BY_NECKO:
   2870      key = "conn_closed_by_necko"_ns;
   2871      break;
   2872    default:
   2873      break;
   2874  }
   2875 
   2876  if (key.IsEmpty()) {
   2877    mozilla::glean::netwerk::http3_0rtt_state.Get("not_used"_ns).Add(1);
   2878  } else {
   2879    MOZ_ASSERT(mZeroRttStarted);
   2880    mozilla::TimeStamp zeroRttEnded = mozilla::TimeStamp::Now();
   2881    mozilla::glean::netwerk::http3_0rtt_state_duration.Get(key)
   2882        .AccumulateRawDuration(zeroRttEnded - mZeroRttStarted);
   2883 
   2884    mozilla::glean::netwerk::http3_0rtt_state.Get(key).Add(1);
   2885  }
   2886 }
   2887 #endif
   2888 
   2889 nsresult Http3Session::GetTransactionTLSSocketControl(
   2890    nsITLSSocketControl** tlsSocketControl) {
   2891  NS_IF_ADDREF(*tlsSocketControl = mSocketControl);
   2892  return NS_OK;
   2893 }
   2894 
   2895 PRIntervalTime Http3Session::LastWriteTime() { return mLastWriteTime; }
   2896 
   2897 //=========================================================================
   2898 // WebTransport
   2899 //=========================================================================
   2900 
   2901 nsresult Http3Session::CloseWebTransport(uint64_t aSessionId, uint32_t aError,
   2902                                         const nsACString& aMessage) {
   2903  return mHttp3Connection->CloseWebTransport(aSessionId, aError, aMessage);
   2904 }
   2905 
   2906 nsresult Http3Session::CreateWebTransportStream(
   2907    uint64_t aSessionId, WebTransportStreamType aStreamType,
   2908    uint64_t* aStreamId) {
   2909  return mHttp3Connection->CreateWebTransportStream(aSessionId, aStreamType,
   2910                                                    aStreamId);
   2911 }
   2912 
   2913 void Http3Session::SendDatagram(Http3WebTransportSession* aSession,
   2914                                nsTArray<uint8_t>& aData,
   2915                                uint64_t aTrackingId) {
   2916  nsresult rv = mHttp3Connection->WebTransportSendDatagram(aSession->StreamId(),
   2917                                                           aData, aTrackingId);
   2918  LOG(("Http3Session::SendDatagram %p res=%" PRIx32, this,
   2919       static_cast<uint32_t>(rv)));
   2920  if (!aTrackingId) {
   2921    return;
   2922  }
   2923 
   2924  switch (rv) {
   2925    case NS_OK:
   2926      aSession->OnOutgoingDatagramOutCome(
   2927          aTrackingId, WebTransportSessionEventListener::DatagramOutcome::SENT);
   2928      break;
   2929    case NS_ERROR_NOT_AVAILABLE:
   2930      aSession->OnOutgoingDatagramOutCome(
   2931          aTrackingId, WebTransportSessionEventListener::DatagramOutcome::
   2932                           DROPPED_TOO_MUCH_DATA);
   2933      break;
   2934    default:
   2935      aSession->OnOutgoingDatagramOutCome(
   2936          aTrackingId,
   2937          WebTransportSessionEventListener::DatagramOutcome::UNKNOWN);
   2938      break;
   2939  }
   2940 }
   2941 
   2942 uint64_t Http3Session::MaxDatagramSize(uint64_t aSessionId) {
   2943  uint64_t size = 0;
   2944  (void)mHttp3Connection->WebTransportMaxDatagramSize(aSessionId, &size);
   2945  return size;
   2946 }
   2947 
   2948 void Http3Session::SendHTTPDatagram(uint64_t aStreamId,
   2949                                    nsTArray<uint8_t>& aData,
   2950                                    uint64_t aTrackingId) {
   2951  LOG(("Http3Session::SendHTTPDatagram %p length=%zu aTrackingId=%" PRIx64,
   2952       this, aData.Length(), aTrackingId));
   2953  (void)mHttp3Connection->ConnectUdpSendDatagram(aStreamId, aData, aTrackingId);
   2954 }
   2955 
   2956 void Http3Session::SetSendOrder(Http3StreamBase* aStream,
   2957                                Maybe<int64_t> aSendOrder) {
   2958  if (!IsClosing()) {
   2959    nsresult rv = mHttp3Connection->WebTransportSetSendOrder(
   2960        aStream->StreamId(), aSendOrder);
   2961    MOZ_ASSERT(NS_SUCCEEDED(rv));
   2962    (void)rv;
   2963  }
   2964 }
   2965 
   2966 Http3Stats Http3Session::GetStats() {
   2967  if (!mHttp3Connection) {
   2968    return Http3Stats();
   2969  }
   2970 
   2971  Http3Stats stats{};
   2972  mHttp3Connection->GetStats(&stats);
   2973  return stats;
   2974 }
   2975 
   2976 already_AddRefed<HttpConnectionUDP> Http3Session::CreateTunnelStream(
   2977    nsAHttpTransaction* aHttpTransaction, nsIInterfaceRequestor* aCallbacks) {
   2978  LOG(("Http3Session::CreateTunnelStream %p aHttpTransaction=%p", this,
   2979       aHttpTransaction));
   2980  RefPtr<Http3StreamBase> stream =
   2981      new Http3ConnectUDPStream(aHttpTransaction, this, NS_GetCurrentThread());
   2982  mStreamTransactionHash.InsertOrUpdate(aHttpTransaction, RefPtr{stream});
   2983  StreamHasDataToWrite(stream);
   2984 
   2985  RefPtr<HttpConnectionUDP> conn =
   2986      stream->GetHttp3ConnectUDPStream()->CreateUDPConnection(aCallbacks);
   2987  return conn.forget();
   2988 }
   2989 
   2990 void Http3Session::FinishTunnelSetup(nsAHttpTransaction* aTransaction) {
   2991  LOG(("Http3Session::FinishTunnelSetup %p aHttpTransaction=%p", this,
   2992       aTransaction));
   2993  RefPtr<Http3StreamBase> stream = mStreamTransactionHash.Get(aTransaction);
   2994  if (!stream || !stream->GetHttp3ConnectUDPStream()) {
   2995    MOZ_ASSERT(false, "There must be a stream");
   2996    return;
   2997  }
   2998 
   2999  RemoveStreamFromQueues(stream);
   3000  mStreamTransactionHash.Remove(aTransaction);
   3001  mTunnelStreams.AppendElement(stream);
   3002 }
   3003 
   3004 already_AddRefed<nsHttpConnection> Http3Session::CreateTunnelStream(
   3005    nsAHttpTransaction* aHttpTransaction, nsIInterfaceRequestor* aCallbacks,
   3006    PRIntervalTime aRtt, bool aIsExtendedCONNECT) {
   3007  LOG(("Http3Session::CreateTunnelStream %p aHttpTransaction=%p", this,
   3008       aHttpTransaction));
   3009  RefPtr<Http3StreamBase> stream =
   3010      new Http3StreamTunnel(aHttpTransaction, this, mCurrentBrowserId);
   3011  mStreamTransactionHash.InsertOrUpdate(aHttpTransaction, RefPtr{stream});
   3012  StreamHasDataToWrite(stream);
   3013 
   3014  RefPtr<nsHttpConnection> conn =
   3015      stream->GetHttp3StreamTunnel()->CreateHttpConnection(aCallbacks, aRtt,
   3016                                                           aIsExtendedCONNECT);
   3017  return conn.forget();
   3018 }
   3019 
   3020 }  // namespace mozilla::net