Bug 1600965 - Add support for blocking on the DNS resolution for HTTP channels; r=dragana
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 15 Jan 2020 14:53:12 +0000
changeset 510361 bc95275bcf45bf25221f06f3dd280b6036b173fd
parent 510360 e7f80b0307ea5e7cd146f726dea501e0241e189a
child 510362 6e16d9619d92382c29e9eaada54876d275a3fb37
push id37020
push userccoroiu@mozilla.com
push dateWed, 15 Jan 2020 21:36:21 +0000
treeherdermozilla-central@c35bb210b8ae [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1600965
milestone74.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1600965 - Add support for blocking on the DNS resolution for HTTP channels; r=dragana Differential Revision: https://phabricator.services.mozilla.com/D56799
modules/libpref/init/StaticPrefList.yaml
modules/libpref/init/all.js
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -7046,16 +7046,22 @@
   mirror: always
 
 # Max time to shutdown the resolver threads
 - name: network.dns.resolver_shutdown_timeout_ms
   type: uint32_t
   value: 2000
   mirror: always
 
+# Whether the SOCKS proxy should be in charge of DNS resolution.
+- name: network.proxy.socks_remote_dns
+  type: bool
+  value: false
+  mirror: always
+
 # Some requests during a page load are marked as "tail", mainly trackers, but not only.
 # This pref controls whether such requests are put to the tail, behind other requests
 # emerging during page loading process.
 - name: network.http.tailing.enabled
   type: bool
   value: true
   mirror: always
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1962,17 +1962,16 @@ pref("network.proxy.ftp",               
 pref("network.proxy.ftp_port",              0);
 pref("network.proxy.http",                  "");
 pref("network.proxy.http_port",             0);
 pref("network.proxy.ssl",                   "");
 pref("network.proxy.ssl_port",              0);
 pref("network.proxy.socks",                 "");
 pref("network.proxy.socks_port",            0);
 pref("network.proxy.socks_version",         5);
-pref("network.proxy.socks_remote_dns",      false);
 pref("network.proxy.proxy_over_tls",        true);
 pref("network.proxy.no_proxies_on",         "");
 // Set true to allow resolving proxy for localhost
 pref("network.proxy.allow_hijacking_localhost", false);
 pref("network.proxy.failover_timeout",      1800); // 30 minutes
 pref("network.online",                      true); //online/offline
 
 // The interval in seconds to move the cookies in the child process.
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -827,18 +827,55 @@ nsresult nsHttpChannel::ContinueConnect(
     return NS_ERROR_DOCUMENT_NOT_CACHED;
   }
 
   // hit the net...
   return DoConnect();
 }
 
 nsresult nsHttpChannel::DoConnect(HttpTransactionShell* aTransWithStickyConn) {
-  LOG(("nsHttpChannel::DoConnect [this=%p, aTransWithStickyConn=%p]\n", this,
-       aTransWithStickyConn));
+  LOG(("nsHttpChannel::DoConnect [this=%p]\n", this));
+
+  if (!mDNSBlockingPromise.IsEmpty()) {
+    LOG(("  waiting for DNS prefetch"));
+
+    // Transaction is passed only from auth retry for which we will definitely
+    // not block on DNS to alter the origin server name for IP; it has already
+    // been done.
+    MOZ_ASSERT(!aTransWithStickyConn);
+    MOZ_ASSERT(mDNSBlockingThenable);
+
+    nsCOMPtr<nsISerialEventTarget> target(do_GetMainThread());
+    RefPtr<nsHttpChannel> self(this);
+    mDNSBlockingThenable->Then(
+        target, __func__,
+        [self](const nsCOMPtr<nsIDNSRecord>& aRec) {
+          nsresult rv = self->DoConnectActual(nullptr);
+          if (NS_FAILED(rv)) {
+            self->CloseCacheEntry(false);
+            Unused << self->AsyncAbort(rv);
+          }
+        },
+        [self](nsresult err) {
+          self->CloseCacheEntry(false);
+          Unused << self->AsyncAbort(err);
+        });
+
+    // The connection will continue when the promise is resolved in
+    // OnLookupComplete.
+    return NS_OK;
+  }
+
+  return DoConnectActual(aTransWithStickyConn);
+}
+
+nsresult nsHttpChannel::DoConnectActual(
+    HttpTransactionShell* aTransWithStickyConn) {
+  LOG(("nsHttpChannel::DoConnectActual [this=%p, aTransWithStickyConn=%p]\n",
+       this, aTransWithStickyConn));
 
   nsresult rv = SetupTransaction();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (aTransWithStickyConn) {
     rv = gHttpHandler->InitiateTransactionWithStickyConn(
@@ -6601,16 +6638,36 @@ nsHttpChannel::GetOrCreateChannelClassif
     LOG(("nsHttpChannel [%p] created nsChannelClassifier [%p]\n", this,
          mChannelClassifier.get()));
   }
 
   RefPtr<nsChannelClassifier> classifier = mChannelClassifier;
   return classifier.forget();
 }
 
+uint16_t nsHttpChannel::GetProxyDNSStrategy() {
+  // This function currently only supports returning DNS_PREFETCH_ORIGIN.
+  // Support for the rest of the DNS_* flags will be added later.
+
+  if (!mProxyInfo) {
+    return DNS_PREFETCH_ORIGIN;
+  }
+
+  nsAutoCString type;
+  mProxyInfo->GetType(type);
+
+  if (!StaticPrefs::network_proxy_socks_remote_dns()) {
+    if (type.EqualsLiteral("socks")) {
+      return DNS_PREFETCH_ORIGIN;
+    }
+  }
+
+  return 0;
+}
+
 // BeginConnect() SHOULD NOT call AsyncAbort(). AsyncAbort will be called by
 // functions that called BeginConnect if needed. Only AsyncOpenFinal,
 // MaybeResolveProxyAndBeginConnect and OnProxyAvailable ever call
 // BeginConnect.
 nsresult nsHttpChannel::BeginConnect() {
   LOG(("nsHttpChannel::BeginConnect [this=%p]\n", this));
   nsresult rv;
 
@@ -6810,17 +6867,26 @@ nsresult nsHttpChannel::BeginConnect() {
            "[this=%p]\n",
            this));
       return NS_OK;
     }
 
     ReEvaluateReferrerAfterTrackingStatusIsKnown();
   }
 
-  MaybeStartDNSPrefetch();
+  rv = MaybeStartDNSPrefetch();
+  if (NS_FAILED(rv)) {
+    auto dnsStrategy = GetProxyDNSStrategy();
+    if (dnsStrategy & DNS_BLOCK_ON_ORIGIN_RESOLVE) {
+      // TODO: Should this be fatal?
+      return rv;
+    }
+    // Otherwise this shouldn't be fatal.
+    return NS_OK;
+  }
 
   rv = ContinueBeginConnectWithResult();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (shouldBeClassified) {
     // Start nsChannelClassifier to catch phishing and malware URIs.
@@ -6829,40 +6895,65 @@ nsresult nsHttpChannel::BeginConnect() {
     LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
          channelClassifier.get(), this));
     channelClassifier->Start();
   }
 
   return NS_OK;
 }
 
-void nsHttpChannel::MaybeStartDNSPrefetch() {
-  if (!mConnectionInfo->UsingHttpProxy() &&
-      !(mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE))) {
-    // Start a DNS lookup very early in case the real open is queued the DNS can
-    // happen in parallel. Do not do so in the presence of an HTTP proxy as
-    // all lookups other than for the proxy itself are done by the proxy.
-    // Also we don't do a lookup if the LOAD_NO_NETWORK_IO or
-    // LOAD_ONLY_FROM_CACHE flags are set.
-    //
-    // We keep the DNS prefetch object around so that we can retrieve
-    // timing information from it. There is no guarantee that we actually
-    // use the DNS prefetch data for the real connection, but as we keep
-    // this data around for 3 minutes by default, this should almost always
-    // be correct, and even when it isn't, the timing still represents _a_
-    // valid DNS lookup timing for the site, even if it is not _the_
-    // timing we used.
-    LOG(("nsHttpChannel::MaybeStartDNSPrefetch [this=%p] prefetching%s\n", this,
-         mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : ""));
+nsresult nsHttpChannel::MaybeStartDNSPrefetch() {
+  // Start a DNS lookup very early in case the real open is queued the DNS can
+  // happen in parallel. Do not do so in the presence of an HTTP proxy as
+  // all lookups other than for the proxy itself are done by the proxy.
+  // Also we don't do a lookup if the LOAD_NO_NETWORK_IO or
+  // LOAD_ONLY_FROM_CACHE flags are set.
+  //
+  // We keep the DNS prefetch object around so that we can retrieve
+  // timing information from it. There is no guarantee that we actually
+  // use the DNS prefetch data for the real connection, but as we keep
+  // this data around for 3 minutes by default, this should almost always
+  // be correct, and even when it isn't, the timing still represents _a_
+  // valid DNS lookup timing for the site, even if it is not _the_
+  // timing we used.
+  if (mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE)) {
+    return NS_OK;
+  }
+
+  auto dnsStrategy = GetProxyDNSStrategy();
+
+  LOG(
+      ("nsHttpChannel::MaybeStartDNSPrefetch [this=%p, strategy=%u] "
+       "prefetching%s\n",
+       this, dnsStrategy,
+       mCaps & NS_HTTP_REFRESH_DNS ? ", refresh requested" : ""));
+
+  if (dnsStrategy & DNS_PREFETCH_ORIGIN) {
     OriginAttributes originAttributes;
     NS_GetOriginAttributes(this, originAttributes);
+
     mDNSPrefetch = new nsDNSPrefetch(
         mURI, originAttributes, nsIRequest::GetTRRMode(), this, mTimingEnabled);
-    mDNSPrefetch->PrefetchHigh(mCaps & NS_HTTP_REFRESH_DNS);
-  }
+    nsresult rv = mDNSPrefetch->PrefetchHigh(mCaps & NS_HTTP_REFRESH_DNS);
+
+    if (dnsStrategy & DNS_BLOCK_ON_ORIGIN_RESOLVE) {
+      LOG(("  blocking on prefetching origin"));
+
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        LOG(("  lookup failed with 0x%08" PRIx32 ", aborting request",
+             static_cast<uint32_t>(rv)));
+        return rv;
+      }
+
+      // Resolved in OnLookupComplete.
+      mDNSBlockingThenable = mDNSBlockingPromise.Ensure(__func__);
+    }
+  }
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) {
   if (mCacheEntry && !mCacheEntryIsWriteOnly) {
     int64_t dataSize = 0;
     mCacheEntry->GetDataSize(&dataSize);
     *aEncodedBodySize = dataSize;
@@ -7581,18 +7672,18 @@ nsresult nsHttpChannel::ProcessCrossOrig
                                      content);
 
   // 3.2.1.6 If policy is null, and embedder policy is "require-corp", set
   // policy to "same-origin".
   if (StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
     RefPtr<mozilla::dom::BrowsingContext> ctx;
     mLoadInfo->GetBrowsingContext(getter_AddRefs(ctx));
 
-    // Note that we treat invalid value as "cross-origin", which spec indicates.
-    // We might want to make that stricter.
+    // Note that we treat invalid value as "cross-origin", which spec
+    // indicates. We might want to make that stricter.
     if (content.IsEmpty() && ctx &&
         ctx->GetEmbedderPolicy() == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP) {
       content = NS_LITERAL_CSTRING("same-origin");
     }
   }
 
   if (content.IsEmpty()) {
     return NS_OK;
@@ -7661,18 +7752,19 @@ nsHttpChannel::OnStartRequest(nsIRequest
 
   if (mRaceCacheWithNetwork) {
     LOG(
         ("  racingNetAndCache - mFirstResponseSource:%d fromCache:%d "
          "fromNet:%d\n",
          static_cast<int32_t>(mFirstResponseSource), request == mCachePump,
          request == mTransactionPump));
     if (mFirstResponseSource == RESPONSE_PENDING) {
-      // When the cache wins mFirstResponseSource is set to RESPONSE_FROM_CACHE
-      // earlier in ReadFromCache, so this must be a response from the network.
+      // When the cache wins mFirstResponseSource is set to
+      // RESPONSE_FROM_CACHE earlier in ReadFromCache, so this must be a
+      // response from the network.
       MOZ_ASSERT(request == mTransactionPump);
       LOG(("  First response from network\n"));
       {
         // Race condition with OnCacheEntryCheck, which is not limited
         // to main thread.
         mozilla::MutexAutoLock lock(mRCWNLock);
         mFirstResponseSource = RESPONSE_FROM_NETWORK;
         mOnStartRequestTimestamp = TimeStamp::Now();
@@ -7726,18 +7818,18 @@ nsHttpChannel::OnStartRequest(nsIRequest
       // is guaranteed to own a reference to the connection.
       mSecurityInfo = mTransaction->SecurityInfo();
     }
   }
 
   // don't enter this block if we're reading from the cache...
   if (NS_SUCCEEDED(mStatus) && !mCachePump && mTransaction) {
     // mTransactionPump doesn't hit OnInputStreamReady and call this until
-    // all of the response headers have been acquired, so we can take ownership
-    // of them from the transaction.
+    // all of the response headers have been acquired, so we can take
+    // ownership of them from the transaction.
     mResponseHead = mTransaction->TakeResponseHead();
     // the response head may be null if the transaction was cancelled.  in
     // which case we just need to call OnStartRequest/OnStopRequest.
     if (mResponseHead) return ProcessResponse();
 
     NS_WARNING("No response head in OnStartRequest");
   }
 
@@ -7911,17 +8003,18 @@ nsHttpChannel::OnStopRequest(nsIRequest*
     mCacheReadEnd = TimeStamp::Now();
 
     ReportNetVSCacheTelemetry();
   }
 
   // allow content to be cached if it was loaded successfully (bug #482935)
   bool contentComplete = NS_SUCCEEDED(status);
 
-  // honor the cancelation status even if the underlying transaction completed.
+  // honor the cancelation status even if the underlying transaction
+  // completed.
   if (mCanceled || NS_FAILED(mStatus)) status = mStatus;
 
   if (mCachedContentIsPartial) {
     if (NS_SUCCEEDED(status)) {
       // mTransactionPump should be suspended
       MOZ_ASSERT(request != mTransactionPump,
                  "byte-range transaction finished prematurely");
 
@@ -8192,17 +8285,18 @@ nsresult nsHttpChannel::ContinueOnStopRe
         // Content wasn't upgraded but is already HTTPS
         upgradeKey = NS_LITERAL_CSTRING("enabledNoReason");
       }
     }
     // shift http to https disposition enums
     chanDisposition =
         static_cast<ChannelDisposition>(chanDisposition + kHttpsCanceled);
   } else if (mLoadInfo && mLoadInfo->GetBrowserWouldUpgradeInsecureRequests()) {
-    // HTTP content the browser would upgrade to HTTPS if upgrading was enabled
+    // HTTP content the browser would upgrade to HTTPS if upgrading was
+    // enabled
     upgradeKey = NS_LITERAL_CSTRING("disabledUpgrade");
   } else {
     // HTTP content that wouldn't upgrade
     upgradeKey = nsMixedContentBlocker::ShouldUpgradeMixedDisplayContent()
                      ? NS_LITERAL_CSTRING("enabledWont")
                      : NS_LITERAL_CSTRING("disabledWont");
   }
   Telemetry::AccumulateCategoricalKeyed(upgradeKey, upgradeChanDisposition);
@@ -8253,18 +8347,19 @@ nsresult nsHttpChannel::ContinueOnStopRe
   }
 
   mIsPending = false;
   mStatus = aStatus;
 
   // perform any final cache operations before we close the cache entry.
   if (mCacheEntry && mRequestTimeInitialized) {
     bool writeAccess;
-    // New implementation just returns value of the !mCacheEntryIsReadOnly flag
-    // passed in. Old implementation checks on nsICache::ACCESS_WRITE flag.
+    // New implementation just returns value of the !mCacheEntryIsReadOnly
+    // flag passed in. Old implementation checks on nsICache::ACCESS_WRITE
+    // flag.
     mCacheEntry->HasWriteAccess(!mCacheEntryIsReadOnly, &writeAccess);
     if (writeAccess) {
       nsresult rv = FinalizeCacheEntry();
       if (NS_FAILED(rv)) {
         LOG(("FinalizeCacheEntry failed (%08x)", static_cast<uint32_t>(rv)));
       }
     }
   }
@@ -8596,20 +8691,20 @@ nsHttpChannel::OnTransportStatus(nsITran
     nsAutoCString host;
     mURI->GetHost(host);
     if (!(mLoadFlags & LOAD_BACKGROUND)) {
       mProgressSink->OnStatus(this, nullptr, status,
                               NS_ConvertUTF8toUTF16(host).get());
     } else {
       nsCOMPtr<nsIParentChannel> parentChannel;
       NS_QueryNotificationCallbacks(this, parentChannel);
-      // If the event sink is |HttpChannelParent|, we have to send status events
-      // to it even if LOAD_BACKGROUND is set. |HttpChannelParent| needs to be
-      // aware of whether the status is |NS_NET_STATUS_RECEIVING_FROM| or
-      // |NS_NET_STATUS_READING|.
+      // If the event sink is |HttpChannelParent|, we have to send status
+      // events to it even if LOAD_BACKGROUND is set. |HttpChannelParent|
+      // needs to be aware of whether the status is
+      // |NS_NET_STATUS_RECEIVING_FROM| or |NS_NET_STATUS_READING|.
       // LOAD_BACKGROUND is checked again in |HttpChannelChild|, so the final
       // consumer won't get this event.
       if (SameCOMIdentity(parentChannel, mProgressSink)) {
         mProgressSink->OnStatus(this, nullptr, status,
                                 NS_ConvertUTF8toUTF16(host).get());
       }
     }
 
@@ -9249,16 +9344,25 @@ nsHttpChannel::OnLookupComplete(nsICance
   // Unset DNS cache refresh if it was requested,
   if (mCaps & NS_HTTP_REFRESH_DNS) {
     mCaps &= ~NS_HTTP_REFRESH_DNS;
     if (mTransaction) {
       mTransaction->SetDNSWasRefreshed();
     }
   }
 
+  if (!mDNSBlockingPromise.IsEmpty()) {
+    if (NS_SUCCEEDED(status)) {
+      nsCOMPtr<nsIDNSRecord> record(rec);
+      mDNSBlockingPromise.Resolve(record, __func__);
+    } else {
+      mDNSBlockingPromise.Reject(status, __func__);
+    }
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::OnLookupByTypeComplete(nsICancelable* aRequest,
                                       nsIDNSByTypeRecord* aRes,
                                       nsresult aStatus) {
   return NS_OK;
@@ -9636,22 +9740,23 @@ nsHttpChannel::ResumeInternal() {
               cachePump->Resume();
             }
 
             // Any newly created pumps were suspended once because of
             // mAsyncResumePending. Problem is that the stream listener
             // notification is already pending in the queue right now, because
             // AsyncRead doesn't (regardless if called after Suspend) respect
             // the suspend coutner and the right order would not be preserved.
-            // Hence, we do another dispatch round to actually Resume after the
-            // notification from the original pump.
+            // Hence, we do another dispatch round to actually Resume after
+            // the notification from the original pump.
             if (transactionPump != self->mTransactionPump &&
                 self->mTransactionPump) {
               LOG(
-                  ("nsHttpChannel::CallOnResume async-resuming new transaction "
+                  ("nsHttpChannel::CallOnResume async-resuming new "
+                   "transaction "
                    "pump %p, this=%p",
                    self->mTransactionPump.get(), self.get()));
 
               nsCOMPtr<nsIRequest> pump = self->mTransactionPump;
               NS_DispatchToCurrentThread(NS_NewRunnableFunction(
                   "nsHttpChannel::CallOnResume new transaction",
                   [pump{std::move(pump)}]() { pump->Resume(); }));
             }
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -30,27 +30,30 @@
 #include "AlternateServices.h"
 #include "nsIRaceCacheWithNetwork.h"
 #include "mozilla/extensions/PStreamFilterParent.h"
 #include "mozilla/Mutex.h"
 #include "nsIProcessSwitchRequestor.h"
 
 class nsDNSPrefetch;
 class nsICancelable;
+class nsIDNSRecord;
 class nsIHttpChannelAuthProvider;
 class nsInputStreamPump;
 class nsITransportSecurityInfo;
 
 namespace mozilla {
 namespace net {
 
 class nsChannelClassifier;
 class Http2PushedStream;
 class HttpChannelSecurityWarningReporter;
 
+using DNSPromise = MozPromise<nsCOMPtr<nsIDNSRecord>, nsresult, false>;
+
 //-----------------------------------------------------------------------------
 // nsHttpChannel
 //-----------------------------------------------------------------------------
 
 // Use to support QI nsIChannel to nsHttpChannel
 #define NS_HTTPCHANNEL_IID                           \
   {                                                  \
     0x301bf95b, 0x7bb3, 0x4ae1, {                    \
@@ -300,17 +303,33 @@ class nsHttpChannel final : public HttpB
   bool RequestIsConditional();
   void HandleContinueCancellingByURLClassifier(nsresult aErrorCode);
   nsresult CancelInternal(nsresult status);
   void ContinueCancellingByURLClassifier(nsresult aErrorCode);
 
   // Connections will only be established in this function.
   // (including DNS prefetch and speculative connection.)
   nsresult MaybeResolveProxyAndBeginConnect();
-  void MaybeStartDNSPrefetch();
+  nsresult MaybeStartDNSPrefetch();
+
+  // Tells the channel to resolve the origin of the end server we are connecting
+  // to.
+  static uint16_t const DNS_PREFETCH_ORIGIN = 1 << 0;
+  // Tells the channel to resolve the host name of the proxy.
+  static uint16_t const DNS_PREFETCH_PROXY = 1 << 1;
+  // Will be set if the current channel uses an HTTP/HTTPS proxy.
+  static uint16_t const DNS_PROXY_IS_HTTP = 1 << 2;
+  // Tells the channel to wait for the result of the origin server resolution
+  // before any connection attempts are made.
+  static uint16_t const DNS_BLOCK_ON_ORIGIN_RESOLVE = 1 << 3;
+
+  // Based on the proxy configuration determine the strategy for resolving the
+  // end server host name.
+  // Returns a combination of the above flags.
+  uint16_t GetProxyDNSStrategy();
 
   // We might synchronously or asynchronously call BeginConnect,
   // which includes DNS prefetch and speculative connection, according to
   // whether an async tracker lookup is required. If the tracker lookup
   // is required, this funciton will just return NS_OK and BeginConnect()
   // will be called when callback. See Bug 1325054 for more information.
   nsresult BeginConnect();
   MOZ_MUST_USE nsresult ContinueBeginConnectWithResult();
@@ -429,16 +448,18 @@ class nsHttpChannel final : public HttpB
               const std::function<nsresult(nsHttpChannel*, nsresult)>&
                   aContinueOnStopRequestFunc);
   MOZ_MUST_USE nsresult
   ContinueDoAuthRetry(HttpTransactionShell* aTransWithStickyConn,
                       const std::function<nsresult(nsHttpChannel*, nsresult)>&
                           aContinueOnStopRequestFunc);
   MOZ_MUST_USE nsresult
   DoConnect(HttpTransactionShell* aTransWithStickyConn = nullptr);
+  MOZ_MUST_USE nsresult
+  DoConnectActual(HttpTransactionShell* aTransWithStickyConn);
   MOZ_MUST_USE nsresult ContinueOnStopRequestAfterAuthRetry(
       nsresult aStatus, bool aAuthRetry, bool aIsFromNet, bool aContentComplete,
       HttpTransactionShell* aTransWithStickyConn);
   MOZ_MUST_USE nsresult ContinueOnStopRequest(nsresult status, bool aIsFromNet,
                                               bool aContentComplete);
 
   void HandleAsyncRedirectChannelToHttps();
   MOZ_MUST_USE nsresult StartRedirectChannelToHttps();
@@ -817,16 +838,24 @@ class nsHttpChannel final : public HttpB
   // OnCacheEntryCheck are no longer valid.
   bool mIgnoreCacheEntry;
   // Lock preventing OnCacheEntryCheck and SetupTransaction being called at
   // the same time.
   mozilla::Mutex mRCWNLock;
 
   TimeStamp mNavigationStartTimeStamp;
 
+  // Promise that blocks connection creation when we want to resolve the origin
+  // host name to be able to give the configured proxy only the resolved IP
+  // to not leak names.
+  MozPromiseHolder<DNSPromise> mDNSBlockingPromise;
+  // When we hit DoConnect before the resolution is done, Then() will be set
+  // here to resume DoConnect.
+  RefPtr<DNSPromise> mDNSBlockingThenable;
+
   // We update the value of mProxyConnectResponseCode when OnStartRequest is
   // called and reset the value when we switch to another failover proxy.
   int32_t mProxyConnectResponseCode;
 
  protected:
   virtual void DoNotifyListenerCleanup() override;
 
   // Override ReleaseListeners() because mChannelClassifier only exists