Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/netwerk/protocol/http/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 42 kB image not shown  

Quelle  HttpConnectionUDP.cpp

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


// HttpLog.h should generally be included first
#include "HttpLog.h"

// Log on level :5, instead of default :4.
#undef LOG
#define LOG(args) LOG5(args)
#undef LOG_ENABLED
#define LOG_ENABLED() LOG5_ENABLED()

#define TLS_EARLY_DATA_NOT_AVAILABLE 0
#define TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED 1
#define TLS_EARLY_DATA_AVAILABLE_AND_USED 2

#include "ASpdySession.h"
#include "ConnectionHandle.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/glean/NetwerkMetrics.h"
#include "HttpConnectionUDP.h"
#include "nsHttpConnectionMgr.h"
#include "nsHttpHandler.h"
#include "nsHttpTransaction.h"
#include "Http3Session.h"
#include "nsComponentManagerUtils.h"
#include "nsIDNSRecord.h"
#include "nsIHttpChannelInternal.h"
#include "nsISocketProvider.h"
#include "nsNetAddr.h"
#include "nsINetAddr.h"
#include "nsStringStream.h"
#include "nsThreadUtils.h"

namespace mozilla {
namespace net {

//-----------------------------------------------------------------------------
// ConnectUDPTransaction
//-----------------------------------------------------------------------------

class ConnectUDPTransaction : public nsAHttpTransaction {
 public:
  NS_DECL_THREADSAFE_ISUPPORTS

  explicit ConnectUDPTransaction(nsAHttpTransaction* aTrans,
                                 nsIInputStream* aStream)
      : mTransaction(aTrans), mProxyConnectStream(aStream) {
    LOG(("ConnectUDPTransaction ctor: %p", this));
  }

  void SetConnection(nsAHttpConnection* aConn) override { mConnection = aConn; }
  nsAHttpConnection* Connection() override { return mConnection; }
  void GetSecurityCallbacks(nsIInterfaceRequestor** aCallbacks) override {
    mTransaction->GetSecurityCallbacks(aCallbacks);
  }
  void OnTransportStatus(nsITransport* transport, nsresult status,
                         int64_t progress) override {
    mTransaction->OnTransportStatus(transport, status, progress);
  }
  bool IsDone() override { return mIsDone; }
  nsresult Status() override { return mTransaction->Status(); }
  uint32_t Caps() override { return mTransaction->Caps(); }

  static nsresult ReadRequestSegment(nsIInputStream* stream, void* closure,
                                     const char* buf, uint32_t offset,
                                     uint32_t count, uint32_t* countRead) {
    ConnectUDPTransaction* trans = (ConnectUDPTransaction*)closure;
    nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
    return rv;
  }

  [[nodiscard]] nsresult ReadSegments(nsAHttpSegmentReader* reader,
                                      uint32_t count,
                                      uint32_t* countRead) override {
    mReader = reader;
    (void)mProxyConnectStream->ReadSegments(ReadRequestSegment, this, count,
                                            countRead);
    mReader = nullptr;
    uint64_t avil = 0;
    (void)mProxyConnectStream->Available(&avil);
    if (!avil) {
      mIsDone = true;
    }
    return NS_OK;
  }
  [[nodiscard]] nsresult WriteSegments(nsAHttpSegmentWriter* writer,
                                       uint32_t count,
                                       uint32_t* countWritten) override {
    // This is hacky. We should set Connection properly in
    // nsHttpConnectionMgr::DispatchTransaction.
    if (!mTransaction->Connection()) {
      mTransaction->SetConnection(mConnection);
    }
    nsresult rv = mTransaction->WriteSegments(writer, count, countWritten);
    mTransaction = nullptr;
    return rv;
  }
  void Close(nsresult reason) override {
    LOG(("ConnectUDPTransaction close mTransaction=%p", mTransaction.get()));
    if (mTransaction) {
      mTransaction->Close(reason);
    }
  }
  nsHttpConnectionInfo* ConnectionInfo() override {
    return mTransaction->ConnectionInfo();
  }
  void SetProxyConnectFailed() override {
    mTransaction->SetProxyConnectFailed();
  }
  nsHttpRequestHead* RequestHead() override {
    return mTransaction->RequestHead();
  }
  uint32_t Http1xTransactionCount() override { return 0; }
  [[nodiscard]] nsresult TakeSubTransactions(
      nsTArray<RefPtr<nsAHttpTransaction>>& outTransactions) override {
    return NS_OK;
  }

 protected:
  virtual ~ConnectUDPTransaction() {
    LOG(("ConnectUDPTransaction dtor: %p", this));
  }

  RefPtr<nsAHttpTransaction> mTransaction;
  nsCOMPtr<nsIInputStream> mProxyConnectStream;
  RefPtr<nsAHttpConnection> mConnection;
  bool mIsDone = false;
  nsAHttpSegmentReader* mReader = nullptr;
};

NS_IMPL_ISUPPORTS(ConnectUDPTransaction, nsISupportsWeakReference)

//-----------------------------------------------------------------------------
// Http3ConnectTransaction
//-----------------------------------------------------------------------------

// We need a dummy transaction to keep the Http3StreamTunnel alive.
// Http3StreamTunnel instances are owned via the transaction-to-stream map
// in Http3Session::mStreamTransactionHash.
class Http3ConnectTransaction : public ConnectUDPTransaction {
 public:
  explicit Http3ConnectTransaction(uint32_t aCaps, nsHttpConnectionInfo* aInfo)
      : ConnectUDPTransaction(nullptr, nullptr),
        mCaps(aCaps),
        mConnInfo(aInfo) {
    LOG(("Http3ConnectTransaction ctor: %p", this));
  }

  uint32_t Caps() override { return mCaps; }
  nsHttpConnectionInfo* ConnectionInfo() override { return mConnInfo; }

  void OnTransportStatus(nsITransport* transport, nsresult status,
                         int64_t progress) override {}

  [[nodiscard]] nsresult ReadSegments(nsAHttpSegmentReader* reader,
                                      uint32_t count,
                                      uint32_t* countRead) override {
    MOZ_ASSERT_UNREACHABLE("Shouldn't be called");
    return NS_ERROR_NOT_IMPLEMENTED;
  }
  [[nodiscard]] nsresult WriteSegments(nsAHttpSegmentWriter* writer,
                                       uint32_t count,
                                       uint32_t* countWritten) override {
    MOZ_ASSERT_UNREACHABLE("Shouldn't be called");
    return NS_ERROR_NOT_IMPLEMENTED;
  }

  void Close(nsresult reason) override { mConnection = nullptr; }

 private:
  virtual ~Http3ConnectTransaction() {
    LOG(("Http3ConnectTransaction dtor: %p", this));
  }

  uint32_t mCaps = 0;
  RefPtr<nsHttpConnectionInfo> mConnInfo;
};

//-----------------------------------------------------------------------------
// HttpConnectionUDP <public>
//-----------------------------------------------------------------------------

HttpConnectionUDP::HttpConnectionUDP() : mHttpHandler(gHttpHandler) {
  LOG(("Creating HttpConnectionUDP @%p\n", this));
}

HttpConnectionUDP::~HttpConnectionUDP() {
  LOG(("Destroying HttpConnectionUDP @%p\n", this));

  if (mForceSendTimer) {
    mForceSendTimer->Cancel();
    mForceSendTimer = nullptr;
  }

  MOZ_ASSERT(mQueuedHttpConnectTransaction.IsEmpty(),
             "Should not have any queued transactions");
  MOZ_ASSERT(mQueuedConnectUdpTransaction.IsEmpty(),
             "Should not have any queued transactions");
}

nsresult HttpConnectionUDP::Init(nsHttpConnectionInfo* info,
                                 nsIDNSRecord* dnsRecord, nsresult status,
                                 nsIInterfaceRequestor* callbacks,
                                 uint32_t caps) {
  LOG1(("HttpConnectionUDP::Init this=%p", this));
  NS_ENSURE_ARG_POINTER(info);
  NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);

  mConnInfo = info;
  MOZ_ASSERT(mConnInfo);
  MOZ_ASSERT(mConnInfo->IsHttp3() || mConnInfo->IsHttp3ProxyConnection());

  mErrorBeforeConnect = status;
  mAlpnToken = mConnInfo->GetNPNToken();
  if (NS_FAILED(mErrorBeforeConnect)) {
    InitCallbacks(callbacks, "HttpConnectionUDP::mCallbacks");
    SetCloseReason(ToCloseReason(mErrorBeforeConnect));
    return mErrorBeforeConnect;
  }

  nsCOMPtr<nsIDNSAddrRecord> dnsAddrRecord = do_QueryInterface(dnsRecord);
  if (!dnsAddrRecord) {
    return NS_ERROR_FAILURE;
  }
  dnsAddrRecord->IsTRR(&mResolvedByTRR);
  dnsAddrRecord->GetEffectiveTRRMode(&mEffectiveTRRMode);
  dnsAddrRecord->GetTrrSkipReason(&mTRRSkipReason);
  NetAddr peerAddr;
  uint16_t port =
      mConnInfo->IsHttp3ProxyConnection()
          ? mConnInfo->ProxyPort()
          : (!mConnInfo->GetRoutedHost().IsEmpty() ? mConnInfo->RoutedPort()
                                                   : mConnInfo->OriginPort());

  nsresult rv = dnsAddrRecord->GetNextAddr(port, &peerAddr);
  if (NS_FAILED(rv)) {
    return rv;
  }

  // We are disabling 0.0.0.0 for non-test purposes.
  // See https://github.com/whatwg/fetch/pull/1763 for context.
  if (peerAddr.IsIPAddrAny()) {
    if (StaticPrefs::network_socket_ip_addr_any_disabled()) {
      LOG(("Connection refused because of 0.0.0.0 IP address\n"));
      return NS_ERROR_CONNECTION_REFUSED;
    }
  }

  mSocket = do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
  if (NS_FAILED(rv)) {
    return rv;
  }

  return InitCommon(mSocket, peerAddr, callbacks, caps, false);
}

nsresult HttpConnectionUDP::InitWithSocket(nsHttpConnectionInfo* info,
                                           nsIUDPSocket* aSocket,
                                           NetAddr aPeerAddr,
                                           nsIInterfaceRequestor* callbacks,
                                           uint32_t caps) {
  LOG1(("HttpConnectionUDP::InitWithSocket this=%p", this));
  NS_ENSURE_ARG_POINTER(info);
  NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);

  mConnInfo = info;
  MOZ_ASSERT(mConnInfo->IsHttp3() || mConnInfo->IsHttp3ProxyConnection());

  mErrorBeforeConnect = NS_OK;
  mAlpnToken = mConnInfo->GetNPNToken();

  return InitCommon(aSocket, aPeerAddr, callbacks, caps, true);
}

nsresult HttpConnectionUDP::InitCommon(nsIUDPSocket* aSocket,
                                       const NetAddr& aPeerAddr,
                                       nsIInterfaceRequestor* callbacks,
                                       uint32_t caps, bool isInTunnel) {
  mSocket = aSocket;

  if (mConnInfo && mConnInfo->GetIsTrrServiceChannel()) {
    mSocket->MarkAsTRRServiceChannel();
  }

  NetAddr local;
  local.raw.family = aPeerAddr.raw.family;
  nsresult rv = mSocket->InitWithAddress(&local, nullptr, false1);
  if (NS_FAILED(rv)) {
    mSocket = nullptr;
    return rv;
  }

  rv = mSocket->SetRecvBufferSize(
      StaticPrefs::network_http_http3_recvBufferSize());
  if (NS_FAILED(rv)) {
    LOG(("HttpConnectionUDP::InitCommon SetRecvBufferSize failed %d [this=%p]",
         static_cast<uint32_t>(rv), this));
    mSocket->Close();
    mSocket = nullptr;
    return rv;
  }

  if (aPeerAddr.raw.family == AF_INET) {
    rv = mSocket->SetDontFragment(true);
    if (NS_FAILED(rv)) {
      LOG(("HttpConnectionUDP::InitCommon SetDontFragment failed %d [this=%p]",
           static_cast<uint32_t>(rv), this));
    }
  }

  // get the resulting socket address.
  rv = mSocket->GetLocalAddr(getter_AddRefs(mSelfAddr));
  if (NS_FAILED(rv)) {
    mSocket->Close();
    mSocket = nullptr;
    return rv;
  }

  uint32_t providerFlags = 0;
  if (caps & NS_HTTP_LOAD_ANONYMOUS) {
    providerFlags |= nsISocketProvider::ANONYMOUS_CONNECT;
  }
  mSocket->SetOriginAttributes(mConnInfo->GetOriginAttributes());
  if (mConnInfo->GetPrivate()) {
    providerFlags |= nsISocketProvider::NO_PERMANENT_STORAGE;
  }
  if (((caps & NS_HTTP_BE_CONSERVATIVE) || mConnInfo->GetBeConservative()) &&
      gHttpHandler->ConnMgr()->BeConservativeIfProxied(
          mConnInfo->ProxyInfo())) {
    providerFlags |= nsISocketProvider::BE_CONSERVATIVE;
  }
  if ((caps & NS_HTTP_IS_RETRY) ||
      (mConnInfo->GetTlsFlags() &
       nsIHttpChannelInternal::TLS_FLAG_CONFIGURE_AS_RETRY)) {
    providerFlags |= nsISocketProvider::IS_RETRY;
  }

  if (mResolvedByTRR) {
    providerFlags |= nsISocketProvider::USED_PRIVATE_DNS;
  }

  mPeerAddr = new nsNetAddr(&aPeerAddr);
  mHttp3Session = new Http3Session();
  rv = mHttp3Session->Init(mConnInfo, mSelfAddr, mPeerAddr, this, providerFlags,
                           callbacks, mSocket, isInTunnel);
  if (NS_FAILED(rv)) {
    LOG(
        ("HttpConnectionUDP::InitCommon Http3Session::Init failed [this=%p "
         "rv=%x]",
         this, static_cast<uint32_t>(rv)));
    mSocket->Close();
    mSocket = nullptr;
    mHttp3Session = nullptr;
    return rv;
  }

  mIsInTunnel = isInTunnel;
  if (mIsInTunnel) {
    mHttp3Session->SetIsInTunnel();
  }

  ChangeConnectionState(ConnectionState::INITED);
  InitCallbacks(callbacks, "HttpConnectionUDP::mCallbacks");

  // Call SyncListen at the end of this function. This call will actually
  // attach the sockte to SocketTransportService.
  rv = mSocket->SyncListen(this);
  if (NS_FAILED(rv)) {
    mSocket->Close();
    mSocket = nullptr;
    return rv;
  }

  return NS_OK;
}

// called on the socket thread
nsresult HttpConnectionUDP::Activate(nsAHttpTransaction* trans, uint32_t caps,
                                     int32_t pri) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  LOG1(("HttpConnectionUDP::Activate [this=%p trans=%p caps=%x]\n", this, trans,
        caps));

  nsHttpTransaction* hTrans = trans->QueryHttpTransaction();
  nsHttpConnectionInfo* transCI = trans->ConnectionInfo();
  NetAddr peerAddr;
  if (!transCI->UsingProxy() && hTrans &&
      NS_SUCCEEDED(GetPeerAddr(&peerAddr))) {
    // set the targetIpAddressSpace in the transaction object, this might be
    // needed by the channel for determining the kind of LNA permissions and/or
    // LNA telemetry
    auto addrSpace = peerAddr.GetIpAddressSpace();
    // h3 always uses TLS via QUIC. When network.lna.defer_https_check is
    // enabled and the QUIC handshake hasn't yet completed, defer the LNA
    // check for Private targets to OnConnected() so we don't prompt for
    // misdirected public hostnames whose certificate would have failed to
    // validate. Local targets are still checked immediately.
    bool deferPrivate = addrSpace == nsILoadInfo::IPAddressSpace::Private &&
                        StaticPrefs::network_lna_defer_https_check() &&
                        mHttp3Session && !mHttp3Session->IsConnected();
    if (deferPrivate) {
      mDeferredLnaTransactions.AppendElement(hTrans);
    } else if (!hTrans->AllowedToConnectToIpAddressSpace(addrSpace)) {
      // we could probably fail early and avoid recreating the H3 session
      // See Bug 1968908
      CloseTransaction(mHttp3Session, NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED);
      trans->Close(NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED);
      return NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED;
    }
  }

  if (!mExperienced && !trans->IsNullTransaction()) {
    mHasFirstHttpTransaction = true;
    // For QUIC we have HttpConnectionUDP before the actual connection
    // has been establish so wait for TLS handshake to be finished before
    // we mark the connection 'experienced'.
    if (!mExperienced && mHttp3Session && mHttp3Session->IsConnected()) {
      mExperienced = true;
    }
    if (mBootstrappedTimingsSet) {
      mBootstrappedTimingsSet = false;
      if (hTrans) {
        hTrans->BootstrapTimings(mBootstrappedTimings);
      }
    }
    mBootstrappedTimings = TimingStruct();
  }

  mTransactionCaps = caps;
  mPriority = pri;

  NS_ENSURE_ARG_POINTER(trans);

  mErrorBeforeConnect = CheckTunnelIsNeeded(trans);

  // Connection failures are Activated() just like regular transacions.
  // If we don't have a confirmation of a connected socket then test it
  // with a write() to get relevant error code.
  if (NS_FAILED(mErrorBeforeConnect)) {
    CloseTransaction(nullptr, mErrorBeforeConnect);
    trans->Close(mErrorBeforeConnect);
    gHttpHandler->ExcludeHttp3(mConnInfo);
    return mErrorBeforeConnect;
  }

  // When mIsInTunnel is false, this HttpConnectionUDP represents the *outer*
  // connection to the proxy. If a proxy CONNECT is still in progress,
  // we need to queue the transaction until the outer connection is fully
  // established.
  //
  // Important: we must not reset the transaction while the outer connection
  // is still connecting. Resetting here could lead to opening another HTTP/3
  // connection.
  if (IsProxyConnectInProgress() && !mIsInTunnel && hTrans) {
    if (!mConnected) {
      mQueuedHttpConnectTransaction.AppendElement(hTrans);
      (void)ResumeSend();
    } else {
      // Don't call ResetTransaction() directly here.
      // HttpConnectionUDP::Activate() may be invoked from
      // nsHttpConnectionMgr::DispatchSpdyPendingQ(), which could run while
      // enumerating all connection entries. ResetTransaction() can insert a new
      // "wild" entry, and modifying the connection-entry table during iteration
      // is not allowed.
      RefPtr<HttpConnectionUDP> self(this);
      RefPtr<nsHttpTransaction> httpTransaction(hTrans);
      nsCOMPtr<nsIRunnable> event = NS_NewRunnableFunction(
          "HttpConnectionUDP::ResetTransaction",
          [self{std::move(self)},
           httpTransaction{std::move(httpTransaction)}]() {
            self->ResetTransaction(httpTransaction);
          });

      if (StaticPrefs::network_trr_high_priority_events() && mConnInfo &&
          mConnInfo->GetIsTrrServiceChannel()) {
        event = new PrioritizableRunnable(
            event.forget(), nsIRunnablePriority::PRIORITY_MEDIUMHIGH);
      }

      NS_DispatchToCurrentThread(event);
    }
    return NS_OK;
  }

  // This is the CONNECT-UDP case. When mIsInTunnel is true, this is
  // the *inner* connection from the proxy tunnel to the destination website.
  // If the proxy CONNECT is still in progress, we cannot send any data
  // yet because Http3ConnectUDPStream is not allowed to transmit until the
  // tunnel is established. In this case, we queue the transaction and will
  // add it to the Http3Session once the proxy CONNECT completes.
  if (mIsInTunnel && IsProxyConnectInProgress() && hTrans) {
    LOG(("Queue trans %p due to proxy connct in progress", hTrans));
    mQueuedConnectUdpTransaction.AppendElement(hTrans);
    return NS_OK;
  }

  {
    nsCOMPtr<nsIInterfaceRequestor> callbacks = GetCallbacks();
    if (!mHttp3Session->AddStream(trans, pri, callbacks)) {
      MOZ_ASSERT(false);  // this cannot happen!
      trans->Close(NS_ERROR_ABORT);
      return NS_ERROR_FAILURE;
    }
  }

  if (mHasFirstHttpTransaction && mExperienced) {
    mHasFirstHttpTransaction = false;
    mExperienceState |= ConnectionExperienceState::Experienced;
  }

  (void)ResumeSend();
  return NS_OK;
}

void HttpConnectionUDP::OnConnected() {
  LOG(("HttpConnectionUDP::OnConnected %p", this));
  MOZ_ASSERT(!mConnected, "Called more than once");

  mConnected = true;

  // Deferred LNA check: now that the QUIC handshake has succeeded (and the
  // peer presented a valid certificate for the requested name), check
  // whether any deferred transactions are permitted to reach this address
  // space. If denied, tear down the session and close all of them.
  if (!mDeferredLnaTransactions.IsEmpty()) {
    nsTArray<RefPtr<nsHttpTransaction>> deferred =
        std::move(mDeferredLnaTransactions);
    NetAddr peerAddr;
    if (NS_SUCCEEDED(GetPeerAddr(&peerAddr))) {
      auto addrSpace = peerAddr.GetIpAddressSpace();
      bool denied = false;
      for (const auto& t : deferred) {
        if (!t->AllowedToConnectToIpAddressSpace(addrSpace)) {
          denied = true;
          // We might want to break here, but AllowedToConnectToIpAddressSpace
          // has a side effect of setting mTargetIpAddressSpace. In practice
          // the array will usually just contain one transaction.
        }
      }
      if (denied) {
        DontReuse();
        CloseTransaction(mHttp3Session, NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED);
        for (const auto& t : deferred) {
          t->Close(NS_ERROR_LOCAL_NETWORK_ACCESS_DENIED);
        }
        return;
      }
    }
  }

  if (mIsInTunnel) {
    return;
  }

  for (const auto& trans : mQueuedHttpConnectTransaction) {
    ResetTransaction(trans);
  }
  mQueuedHttpConnectTransaction.Clear();
}

already_AddRefed<nsIInputStream> HttpConnectionUDP::CreateProxyConnectStream(
    nsAHttpTransaction* trans) {
  UniquePtr<nsHttpRequestHead> request = MakeUnique<nsHttpRequestHead>();
  nsAutoCString host;
  DebugOnly<nsresult> rv{};
  rv = nsHttpHandler::GenerateHostPort(
      nsDependentCString(trans->ConnectionInfo()->Origin()),
      trans->ConnectionInfo()->OriginPort(), host);
  MOZ_ASSERT(NS_SUCCEEDED(rv));

  request->SetMethod("CONNECT"_ns);
  request->SetVersion(gHttpHandler->HttpVersion());

  bool shouldResistFingerprinting = trans->Caps() & NS_HTTP_USE_RFP;
  rv = request->SetHeader(nsHttp::User_Agent,
                          gHttpHandler->UserAgent(shouldResistFingerprinting));
  MOZ_ASSERT(NS_SUCCEEDED(rv));

  rv = request->SetHeader(nsHttp::Host, host);
  MOZ_ASSERT(NS_SUCCEEDED(rv));

  nsAutoCString val;
  if (NS_SUCCEEDED(
          trans->RequestHead()->GetHeader(nsHttp::Proxy_Authorization, val))) {
    // TODO: we should use Proxy_Authorization here.
    rv = request->SetHeader(nsHttp::Authorization, val);
    MOZ_ASSERT(NS_SUCCEEDED(rv));
  }

  nsAutoCString result;
  request->Flatten(result, false);
  if (LOG1_ENABLED()) {
    LOG(("HttpConnectionUDP::MakeConnectString for transaction=%p[",
         trans->QueryHttpTransaction()));
    LogHeaders(result.get());
    LOG(("]"));
  }
  result.AppendLiteral("\r\n");

  nsCOMPtr<nsIInputStream> stream;
  NS_NewCStringInputStream(getter_AddRefs(stream), std::move(result));
  return stream.forget();
}

nsresult HttpConnectionUDP::CreateTunnelStream(
    nsAHttpTransaction* httpTransaction, HttpConnectionBase** aHttpConnection,
    bool aIsExtendedCONNECT) {
  LOG(("HttpConnectionUDP::CreateTunnelStream %p", this));
  if (!mHttp3Session) {
    return NS_ERROR_UNEXPECTED;
  }

  bool isHttp3 = httpTransaction->ConnectionInfo()->IsHttp3();

  if (!isHttp3) {
    RefPtr<Http3ConnectTransaction> trans = new Http3ConnectTransaction(
        httpTransaction->Caps(), httpTransaction->ConnectionInfo());
    nsCOMPtr<nsIInterfaceRequestor> callbacks = GetCallbacks();
    RefPtr<nsHttpConnection> conn =
        mHttp3Session->CreateTunnelStream(trans, callbacks, mRtt, false);
    RefPtr<ConnectionHandle> handle = new ConnectionHandle(conn);
    trans->SetConnection(handle);

    conn.forget(aHttpConnection);
    return NS_OK;
  }

  nsCOMPtr<nsIInputStream> proxyConnectStream =
      CreateProxyConnectStream(httpTransaction);
  if (!proxyConnectStream) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  RefPtr<ConnectUDPTransaction> trans =
      new ConnectUDPTransaction(httpTransaction, proxyConnectStream);
  nsCOMPtr<nsIInterfaceRequestor> callbacks2 = GetCallbacks();
  RefPtr<HttpConnectionUDP> conn =
      mHttp3Session->CreateTunnelStream(trans, callbacks2);
  RefPtr<ConnectionHandle> handle = new ConnectionHandle(conn);
  trans->SetConnection(handle);

  conn.forget(aHttpConnection);
  return NS_OK;
}

void HttpConnectionUDP::Close(nsresult reason, bool aIsShutdown) {
  LOG(("HttpConnectionUDP::Close [this=%p reason=%" PRIx32 "]\n", this,
       static_cast<uint32_t>(reason)));

  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  if (mConnectionState != ConnectionState::CLOSED) {
    RecordConnectionCloseTelemetry(reason);
    ChangeConnectionState(ConnectionState::CLOSED);
  }

  if (mForceSendTimer) {
    mForceSendTimer->Cancel();
    mForceSendTimer = nullptr;
  }

  if (!mTrafficCategory.IsEmpty()) {
    HttpTrafficAnalyzer* hta = gHttpHandler->GetHttpTrafficAnalyzer();
    if (hta) {
      hta->IncrementHttpConnection(std::move(mTrafficCategory));
      MOZ_ASSERT(mTrafficCategory.IsEmpty());
    }
  }

  nsCOMPtr<nsIUDPSocket> socket = std::move(mSocket);
  if (socket) {
    socket->Close();
  }
  if (mHttp3Session) {
    mHttp3Session->SetCleanShutdown(true);
    mHttp3Session->Close(reason);
    mHttp3Session = nullptr;
  }

  for (const auto& trans : mQueuedHttpConnectTransaction) {
    trans->Close(reason);
  }
  mQueuedHttpConnectTransaction.Clear();
  for (const auto& trans : mQueuedConnectUdpTransaction) {
    trans->Close(reason);
  }
  mQueuedConnectUdpTransaction.Clear();
}

void HttpConnectionUDP::DontReuse() {
  LOG(("HttpConnectionUDP::DontReuse %p http3session=%p\n", this,
       mHttp3Session.get()));
  mDontReuse = true;
  if (mHttp3Session) {
    mHttp3Session->DontReuse();
  }
}

bool HttpConnectionUDP::TestJoinConnection(const nsACString& hostname,
                                           int32_t port) {
  if (mHttp3Session && CanDirectlyActivate()) {
    return mHttp3Session->TestJoinConnection(hostname, port);
  }

  return false;
}

bool HttpConnectionUDP::JoinConnection(const nsACString& hostname,
                                       int32_t port) {
  if (mHttp3Session && CanDirectlyActivate()) {
    return mHttp3Session->JoinConnection(hostname, port);
  }

  return false;
}

bool HttpConnectionUDP::CanReuse() {
  if (NS_FAILED(mErrorBeforeConnect)) {
    return false;
  }
  if (mDontReuse) {
    return false;
  }

  if (mHttp3Session) {
    return mHttp3Session->CanReuse();
  }
  return false;
}

bool HttpConnectionUDP::CanDirectlyActivate() {
  // return true if a new transaction can be addded to ths connection at any
  // time through Activate(). In practice this means this is a healthy SPDY
  // connection with room for more concurrent streams.

  if (mHttp3Session) {
    return CanReuse();
  }
  return false;
}

//----------------------------------------------------------------------------
// HttpConnectionUDP::nsAHttpConnection compatible methods
//----------------------------------------------------------------------------

nsresult HttpConnectionUDP::OnHeadersAvailable(nsAHttpTransaction* trans,
                                               nsHttpRequestHead* requestHead,
                                               nsHttpResponseHead* responseHead,
                                               bool* reset) {
  LOG(
      ("HttpConnectionUDP::OnHeadersAvailable [this=%p trans=%p "
       "response-head=%p]\n",
       this, trans, responseHead));

  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  NS_ENSURE_ARG_POINTER(trans);
  MOZ_ASSERT(responseHead, "No response head?");

  DebugOnly<nsresult> rv =
      responseHead->SetHeader(nsHttp::X_Firefox_Http3, mAlpnToken);
  MOZ_ASSERT(NS_SUCCEEDED(rv));

  uint16_t responseStatus = responseHead->Status();
  nsHttpTransaction* hTrans = trans->QueryHttpTransaction();
  if (mState == HttpConnectionState::SETTING_UP_TUNNEL) {
    HandleTunnelResponse(hTrans, *responseHead, reset);
    return NS_OK;
  }

  // deal with 408 Server Timeouts
  static const PRIntervalTime k1000ms = PR_MillisecondsToInterval(1000);
  if (responseStatus == 408) {
    // If this error could be due to a persistent connection reuse then
    // we pass an error code of NS_ERROR_NET_RESET to
    // trigger the transaction 'restart' mechanism.  We tell it to reset its
    // response headers so that it will be ready to receive the new response.
    if (mIsReused &&
        ((PR_IntervalNow() - mHttp3Session->LastWriteTime()) < k1000ms)) {
      CloseTransaction(mHttp3Session, NS_ERROR_NET_RESET);
      *reset = true;
      return NS_OK;
    }
  }

  return NS_OK;
}

void HttpConnectionUDP::HandleTunnelResponse(
    nsHttpTransaction* aHttpTransaction, const nsHttpResponseHead& responseHead,
    bool* reset) {
  LOG(("HttpConnectionUDP::HandleTunnelResponse mIsInTunnel=%d", mIsInTunnel));
  MOZ_ASSERT(TunnelSetupInProgress());
  MOZ_ASSERT(mIsInTunnel);

  mProxyConnectResponseHead =
      MakeRefPtr<ProxyConnectResponseHead>(responseHead);
  if (responseHead.Status() == 200) {
    ChangeState(HttpConnectionState::REQUEST);
  }

  bool onlyConnect = mTransactionCaps & NS_HTTP_CONNECT_ONLY;
  aHttpTransaction->OnProxyConnectComplete(mProxyConnectResponseHead);
  if (responseHead.Status() == 200) {
    LOG(("proxy CONNECT succeeded! onlyconnect=%d mIsInTunnel=%d\n",
         onlyConnect, mIsInTunnel));
    // If we're only connecting, we don't need to reset the transaction
    // state. We need to upgrade the socket now without doing the actual
    // http request.
    if (!onlyConnect) {
      *reset = true;
    }

    for (const auto& trans : mQueuedConnectUdpTransaction) {
      LOG(("add trans=%p", trans.get()));
      nsCOMPtr<nsIInterfaceRequestor> callbacks = GetCallbacks();
      if (!mHttp3Session->AddStream(trans, trans->Priority(), callbacks)) {
        MOZ_ASSERT(false);  // this cannot happen!
        trans->Close(NS_ERROR_ABORT);
      }
    }
    mQueuedConnectUdpTransaction.Clear();
    mProxyConnectSucceeded = true;
    (void)ResumeSend();
  } else {
    LOG(("proxy CONNECT failed! onlyconnect=%d\n", onlyConnect));
    aHttpTransaction->SetProxyConnectFailed();
    mQueuedConnectUdpTransaction.Clear();
  }
}

void HttpConnectionUDP::ResetTransaction(nsHttpTransaction* aHttpTransaction) {
  LOG(("HttpConnectionUDP::ResetTransaction [this=%p mState=%d]\n", this,
       static_cast<uint32_t>(mState)));

  if (!mAlreadyWildcard) {
    RefPtr<nsHttpConnectionInfo> wildCardProxyCi;
    nsresult rv = mConnInfo->CreateWildCard(getter_AddRefs(wildCardProxyCi));
    if (NS_FAILED(rv)) {
      CloseTransaction(mHttp3Session, rv);
      aHttpTransaction->Close(rv);
      return;
    }
    gHttpHandler->ConnMgr()->MoveToWildCardConnEntry(mConnInfo, wildCardProxyCi,
                                                     this);
    mConnInfo = wildCardProxyCi;
    mAlreadyWildcard = true;
  }

  // Both Http3Session and nsHttpTransaction keeps a strong reference to a
  // ConnectionHandle, which itself holds a strong ref back to the concrete
  // connection, and when the last reference to the handle drops, the underlying
  // connection is released.
  // Notes:
  // 1) SetConnection() may drop the previous
  //    connection reference, which can immediately destroy that handle.
  // Avoid calling it unless we actually need to create/attach a handle.
  // 2) For speculative connections, the transaction may already have a handle.
  //    Reuse it rather than creating a new one.
  // 3) Only set the session's connection if it doesn't already have one.
  if (!mHttp3Session->Connection()) {
    if (!aHttpTransaction->Connection()) {
      RefPtr<ConnectionHandle> handle = new ConnectionHandle(this);
      aHttpTransaction->SetConnection(handle);
    }
    mHttp3Session->SetConnection(aHttpTransaction->Connection());
  }
  aHttpTransaction->SetConnection(nullptr);

  aHttpTransaction->DoNotRemoveAltSvc();
  // This transaction may have NS_HTTP_STICKY_CONNECTION set, so we must call
  // MakeRestartable() explicitly to ensure it can be restarted.
  aHttpTransaction->MakeRestartable();
  aHttpTransaction->Close(NS_ERROR_NET_RESET);
}

bool HttpConnectionUDP::IsReused() { return mIsReused; }

nsresult HttpConnectionUDP::TakeTransport(
    nsISocketTransport** aTransport, nsIAsyncInputStream** aInputStream,
    nsIAsyncOutputStream** aOutputStream) {
  return NS_ERROR_FAILURE;
}

void HttpConnectionUDP::GetTLSSocketControl(nsITLSSocketControl** secinfo) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  LOG(("HttpConnectionUDP::GetTLSSocketControl http3Session=%p\n",
       mHttp3Session.get()));

  if (mHttp3Session &&
      NS_SUCCEEDED(mHttp3Session->GetTransactionTLSSocketControl(secinfo))) {
    return;
  }

  *secinfo = nullptr;
}

nsresult HttpConnectionUDP::PushBack(const char* data, uint32_t length) {
  LOG(("HttpConnectionUDP::PushBack [this=%p, length=%d]\n", this, length));

  return NS_ERROR_UNEXPECTED;
}

class HttpConnectionUDPForceIO : public Runnable, public nsIRunnablePriority {
 public:
  NS_DECL_ISUPPORTS_INHERITED
  NS_DECL_NSIRUNNABLEPRIORITY

  HttpConnectionUDPForceIO(HttpConnectionUDP* aConn, bool doRecv)
      : Runnable("net::HttpConnectionUDPForceIO"),
        mConn(aConn),
        mDoRecv(doRecv) {}

  NS_IMETHOD Run() override {
    MOZ_ASSERT(OnSocketThread(), "not on socket thread");

    if (mDoRecv) {
      return mConn->RecvData();
    }

    MOZ_ASSERT(mConn->mForceSendPending);
    mConn->mForceSendPending = false;

    return mConn->SendData();
  }

 private:
  ~HttpConnectionUDPForceIO() = default;

  RefPtr<HttpConnectionUDP> mConn;
  bool mDoRecv;
};

NS_IMPL_ISUPPORTS_INHERITED(HttpConnectionUDPForceIO, Runnable,
                            nsIRunnablePriority)

NS_IMETHODIMP
HttpConnectionUDPForceIO::GetPriority(uint32_t* aPriority) {
  if (StaticPrefs::network_trr_high_priority_events() && mConn->mConnInfo &&
      mConn->mConnInfo->GetIsTrrServiceChannel()) {
    *aPriority = nsIRunnablePriority::PRIORITY_MEDIUMHIGH;
  } else {
    *aPriority = nsIRunnablePriority::PRIORITY_NORMAL;
  }
  return NS_OK;
}

nsresult HttpConnectionUDP::ResumeSend() {
  LOG(("HttpConnectionUDP::ResumeSend [this=%p]\n", this));
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  RefPtr<HttpConnectionUDP> self(this);
  nsCOMPtr<nsIRunnable> event =
      NS_NewRunnableFunction("HttpConnectionUDP::CallSendData",
                             [self{std::move(self)}]() { self->SendData(); });

  if (StaticPrefs::network_trr_high_priority_events() && mConnInfo &&
      mConnInfo->GetIsTrrServiceChannel()) {
    event = new PrioritizableRunnable(event.forget(),
                                      nsIRunnablePriority::PRIORITY_MEDIUMHIGH);
  }

  NS_DispatchToCurrentThread(event);
  return NS_OK;
}

nsresult HttpConnectionUDP::ResumeRecv() { return NS_OK; }

void HttpConnectionUDP::ForceSendIO(nsITimer* aTimer, void* aClosure) {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  HttpConnectionUDP* self = static_cast<HttpConnectionUDP*>(aClosure);
  MOZ_ASSERT(aTimer == self->mForceSendTimer);
  self->mForceSendTimer = nullptr;
  NS_DispatchToCurrentThread(new HttpConnectionUDPForceIO(self, false));
}

nsresult HttpConnectionUDP::MaybeForceSendIO() {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  // due to bug 1213084 sometimes real I/O events do not get serviced when
  // NSPR derived I/O events are ready and this can cause a deadlock with
  // https over https proxying. Normally we would expect the write callback to
  // be invoked before this timer goes off, but set it at the old windows
  // tick interval (kForceDelay) as a backup for those circumstances.
  static const uint32_t kForceDelay = 17;  // ms

  if (mForceSendPending) {
    return NS_OK;
  }
  MOZ_ASSERT(!mForceSendTimer);
  mForceSendPending = true;
  return NS_NewTimerWithFuncCallback(
      getter_AddRefs(mForceSendTimer), HttpConnectionUDP::ForceSendIO, this,
      kForceDelay, nsITimer::TYPE_ONE_SHOT,
      "net::HttpConnectionUDP::MaybeForceSendIO"_ns);
}

// trigger an asynchronous read
nsresult HttpConnectionUDP::ForceRecv() {
  LOG(("HttpConnectionUDP::ForceRecv [this=%p]\n", this));
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  return NS_DispatchToCurrentThread(new HttpConnectionUDPForceIO(this, true));
}

// trigger an asynchronous write
nsresult HttpConnectionUDP::ForceSend() {
  LOG(("HttpConnectionUDP::ForceSend [this=%p]\n", this));
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  return MaybeForceSendIO();
}

HttpVersion HttpConnectionUDP::Version() { return HttpVersion::v3_0; }

PRIntervalTime HttpConnectionUDP::LastWriteTime() {
  return mHttp3Session->LastWriteTime();
}

//-----------------------------------------------------------------------------
// HttpConnectionUDP <private>
//-----------------------------------------------------------------------------

void HttpConnectionUDP::CloseTransaction(nsAHttpTransaction* trans,
                                         nsresult reason, bool aIsShutdown) {
  LOG(("HttpConnectionUDP::CloseTransaction[this=%p trans=%p reason=%" PRIx32
       "]\n",
       this, trans, static_cast<uint32_t>(reason)));

  // CloseTransaction may be called by nsHttpTransaction when a fallback
  // is needed. In this case, the transaction is still in mQueuedTransaction
  // and the proxy connect is still in progress.
  bool transInQueue = mQueuedHttpConnectTransaction.Contains(trans) ||
                      mQueuedConnectUdpTransaction.Contains(trans);
  MOZ_ASSERT(trans == mHttp3Session ||
             (transInQueue && IsProxyConnectInProgress()));
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  if (NS_SUCCEEDED(reason) || (reason == NS_BASE_STREAM_CLOSED)) {
    if (aIsShutdown && mHttp3Session) {
      // The underlying socket has been closed. Cancel the Http3Session timer
      // to prevent it from firing on the now-closed socket.
      mHttp3Session->SetCleanShutdown(true);
      mHttp3Session->Close(reason);
    }
    return;
  }

  // The connection and security errors clear out alt-svc mappings
  // in case any previously validated ones are now invalid
  if (((reason == NS_ERROR_NET_RESET) ||
       (NS_ERROR_GET_MODULE(reason) == NS_ERROR_MODULE_SECURITY)) &&
      mConnInfo && !(mTransactionCaps & NS_HTTP_ERROR_SOFTLY)) {
    gHttpHandler->ClearHostMapping(mConnInfo);
  }

  mDontReuse = true;
  if (mHttp3Session) {
    // When proxy connnect failed, we call Http3Session::SetCleanShutdown to
    // force Http3Session to release this UDP connection.
    mHttp3Session->SetCleanShutdown(aIsShutdown || transInQueue ||
                                    (mIsInTunnel && !mProxyConnectSucceeded));
    mHttp3Session->Close(reason);
    if (!mHttp3Session->IsClosed()) {
      // During closing phase we still keep mHttp3Session session,
      // to resend CLOSE_CONNECTION frames.
      return;
    }
  }

  mHttp3Session = nullptr;

  {
    MutexAutoLock lock(mCallbacksLock);
    mCallbacks = nullptr;
  }

  Close(reason, aIsShutdown);

  // flag the connection as reused here for convenience sake. certainly
  // it might be going away instead ;-)
  mIsReused = true;
}

void HttpConnectionUDP::OnQuicTimeoutExpired() {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");
  LOG(("HttpConnectionUDP::OnQuicTimeoutExpired [this=%p]\n", this));
  // if the transaction was dropped...
  if (!mHttp3Session) {
    LOG(("  no transaction; ignoring event\n"));
    return;
  }

  nsresult rv = mHttp3Session->ProcessOutputAndEvents(mSocket);
  if (NS_FAILED(rv)) {
    CloseTransaction(mHttp3Session, rv);
  }
}

//-----------------------------------------------------------------------------
// HttpConnectionUDP::nsISupports
//-----------------------------------------------------------------------------

NS_IMPL_ADDREF(HttpConnectionUDP)
NS_IMPL_RELEASE(HttpConnectionUDP)

NS_INTERFACE_MAP_BEGIN(HttpConnectionUDP)
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  NS_INTERFACE_MAP_ENTRY(nsIUDPSocketSyncListener)
  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
  NS_INTERFACE_MAP_ENTRY(HttpConnectionBase)
  NS_INTERFACE_MAP_ENTRY_CONCRETE(HttpConnectionUDP)
NS_INTERFACE_MAP_END

void HttpConnectionUDP::NotifyDataRead() {
  mExperienceState |= ConnectionExperienceState::First_Response_Received;
}

void HttpConnectionUDP::NotifyDataWrite() {
  mExperienceState |= ConnectionExperienceState::First_Request_Sent;
}

// called on the socket transport thread
nsresult HttpConnectionUDP::RecvData() {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  // if the transaction was dropped...
  if (!mHttp3Session) {
    LOG(("  no Http3Session; ignoring event\n"));
    return NS_OK;
  }

  nsresult rv = mHttp3Session->RecvData(mSocket);
  LOG(("HttpConnectionUDP::OnInputReady %p rv=%" PRIx32, this,
       static_cast<uint32_t>(rv)));

  if (NS_FAILED(rv)) CloseTransaction(mHttp3Session, rv);

  return NS_OK;
}

// called on the socket transport thread
nsresult HttpConnectionUDP::SendData() {
  MOZ_ASSERT(OnSocketThread(), "not on socket thread");

  // if the transaction was dropped...
  if (!mHttp3Session) {
    LOG(("  no Http3Session; ignoring event\n"));
    return NS_OK;
  }

  nsresult rv = mHttp3Session->SendData(mSocket);
  LOG(("HttpConnectionUDP::OnInputReady %p rv=%" PRIx32, this,
       static_cast<uint32_t>(rv)));

  if (NS_FAILED(rv)) CloseTransaction(mHttp3Session, rv);

  return NS_OK;
}

//-----------------------------------------------------------------------------
// HttpConnectionUDP::nsIInterfaceRequestor
//-----------------------------------------------------------------------------

// not called on the socket transport thread
NS_IMETHODIMP
HttpConnectionUDP::GetInterface(const nsIID& iid, void** result) {
  // NOTE: This function is only called on the UI thread via sync proxy from
  //       the socket transport thread.  If that weren't the case, then we'd
  //       have to worry about the possibility of mHttp3Session going away
  //       part-way through this function call.  See CloseTransaction.

  // NOTE - there is a bug here, the call to getinterface is proxied off the
  // nss thread, not the ui thread as the above comment says. So there is
  // indeed a chance of mSession going away. bug 615342

  MOZ_ASSERT(!OnSocketThread(), "on socket thread");

  nsCOMPtr<nsIInterfaceRequestor> callbacks = GetCallbacks();
  if (callbacks) return callbacks->GetInterface(iid, result);
  return NS_ERROR_NO_INTERFACE;
}

void HttpConnectionUDP::SetEvent(nsresult aStatus) {
  switch (aStatus) {
    case NS_NET_STATUS_RESOLVING_HOST:
      mBootstrappedTimings.domainLookupStart = TimeStamp::Now();
      break;
    case NS_NET_STATUS_RESOLVED_HOST:
      mBootstrappedTimings.domainLookupEnd = TimeStamp::Now();
      break;
    case NS_NET_STATUS_CONNECTING_TO:
      mBootstrappedTimings.connectStart = TimeStamp::Now();
      mBootstrappedTimings.secureConnectionStart =
          mBootstrappedTimings.connectStart;
      break;
    case NS_NET_STATUS_CONNECTED_TO:
      mBootstrappedTimings.connectEnd = TimeStamp::Now();
      break;
    default:
      break;
  }
}

bool HttpConnectionUDP::LastTransactionExpectedNoContent() {
  return mLastTransactionExpectedNoContent;
}

void HttpConnectionUDP::SetLastTransactionExpectedNoContent(bool val) {
  mLastTransactionExpectedNoContent = val;
}

bool HttpConnectionUDP::IsPersistent() { return !mDontReuse; }

nsAHttpTransaction* HttpConnectionUDP::Transaction() { return mHttp3Session; }

int64_t HttpConnectionUDP::BytesWritten() {
  if (!mHttp3Session) {
    return 0;
  }
  return mHttp3Session->GetBytesWritten();
}

NS_IMETHODIMP HttpConnectionUDP::OnPacketReceived(nsIUDPSocket* aSocket) {
  RecvData();
  return NS_OK;
}

NS_IMETHODIMP HttpConnectionUDP::OnStopListening(nsIUDPSocket* aSocket,
                                                 nsresult aStatus) {
  // At this point, the UDP socket has already been closed. Set aIsShutdown to
  // true to ensure that mHttp3Session is also closed.
  CloseTransaction(mHttp3Session, aStatus, true);
  return NS_OK;
}

nsresult HttpConnectionUDP::GetSelfAddr(NetAddr* addr) {
  if (mSelfAddr) {
    return mSelfAddr->GetNetAddr(addr);
  }
  return NS_ERROR_FAILURE;
}

nsresult HttpConnectionUDP::GetPeerAddr(NetAddr* addr) {
  if (mPeerAddr) {
    return mPeerAddr->GetNetAddr(addr);
  }
  return NS_ERROR_FAILURE;
}

bool HttpConnectionUDP::ResolvedByTRR() { return mResolvedByTRR; }

nsIRequest::TRRMode HttpConnectionUDP::EffectiveTRRMode() {
  return mEffectiveTRRMode;
}

TRRSkippedReason HttpConnectionUDP::TRRSkipReason() { return mTRRSkipReason; }

Http3Stats HttpConnectionUDP::GetStats() {
  if (!mHttp3Session) {
    return Http3Stats();
  }
  return mHttp3Session->GetStats();
}

void HttpConnectionUDP::SetDontExclude() {
  if (mHttp3Session) {
    mHttp3Session->SetDontExclude();
  }
}

}  // namespace net
}  // namespace mozilla

Messung V0.5 in Prozent
C=92 H=97 G=94

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.