Bug 1546358 - Forward cache entry check to parent process r=dragana
authorKershaw Chang <kershaw@mozilla.com>
Mon, 25 May 2020 11:07:07 +0000
changeset 531879 a92c423736e5979a8cf039017fd707d41f665b9c
parent 531878 b21cc935a336b7c79550d42804f18a8154dff437
child 531880 609908f93d016f494594dee7b78978bbab969166
push id37447
push usernbeleuzu@mozilla.com
push dateMon, 25 May 2020 15:42:48 +0000
treeherdermozilla-central@e7641c1071ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdragana
bugs1546358
milestone78.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 1546358 - Forward cache entry check to parent process r=dragana Differential Revision: https://phabricator.services.mozilla.com/D75941
netwerk/ipc/PSocketProcess.ipdl
netwerk/ipc/SocketProcessParent.cpp
netwerk/ipc/SocketProcessParent.h
netwerk/protocol/http/CachePushChecker.cpp
netwerk/protocol/http/CachePushChecker.h
netwerk/protocol/http/Http2Session.cpp
netwerk/protocol/http/Http2Session.h
netwerk/protocol/http/HttpConnectionMgrParent.cpp
netwerk/protocol/http/moz.build
netwerk/protocol/http/nsHttpActivityDistributor.cpp
--- a/netwerk/ipc/PSocketProcess.ipdl
+++ b/netwerk/ipc/PSocketProcess.ipdl
@@ -83,16 +83,20 @@ parent:
                         int32_t aPort,
                         uint32_t aProviderFlags,
                         uint32_t aProviderTlsFlags,
                         ByteArray aServerCert,
                         ByteArray? aClientCert,
                         ByteArray[] aCollectedCANames)
     returns (bool aSucceeded, ByteArray aOutCert, ByteArray aOutKey, ByteArray[] aBuiltChain);
   async PProxyConfigLookup(nsIURI aUri, uint32_t aFlags);
+  async CachePushCheck(nsIURI aPushedURL,
+                       OriginAttributes aOriginAttributes,
+                       nsCString aRequestString)
+    returns (bool aAccepted);
 
 child:
   async PreferenceUpdate(Pref pref);
   async RequestMemoryReport(uint32_t generation,
                             bool anonymize,
                             bool minimizeMemoryUsage,
                             FileDescriptor? DMDFile);
   async SetOffline(bool offline);
--- a/netwerk/ipc/SocketProcessParent.cpp
+++ b/netwerk/ipc/SocketProcessParent.cpp
@@ -2,16 +2,17 @@
 /* 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/. */
 
 #include "SocketProcessParent.h"
 #include "SocketProcessLogging.h"
 
 #include "AltServiceParent.h"
+#include "CachePushChecker.h"
 #include "HttpTransactionParent.h"
 #include "SocketProcessHost.h"
 #include "mozilla/dom/MemoryReportRequest.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/IPCStreamAlloc.h"
 #include "mozilla/ipc/PChildToParentStreamParent.h"
 #include "mozilla/ipc/PParentToChildStreamParent.h"
 #include "mozilla/net/DNSRequestParent.h"
@@ -339,16 +340,27 @@ SocketProcessParent::AllocPProxyConfigLo
 
 mozilla::ipc::IPCResult SocketProcessParent::RecvPProxyConfigLookupConstructor(
     PProxyConfigLookupParent* aActor, nsIURI* aURI,
     const uint32_t& aProxyResolveFlags) {
   static_cast<ProxyConfigLookupParent*>(aActor)->DoProxyLookup();
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult SocketProcessParent::RecvCachePushCheck(
+    nsIURI* aPushedURL, OriginAttributes&& aOriginAttributes,
+    nsCString&& aRequestString, CachePushCheckResolver&& aResolver) {
+  RefPtr<CachePushChecker> checker = new CachePushChecker(
+      aPushedURL, aOriginAttributes, aRequestString, aResolver);
+  if (NS_FAILED(checker->DoCheck())) {
+    aResolver(false);
+  }
+  return IPC_OK();
+}
+
 // To ensure that IPDL is finished before SocketParent gets deleted.
 class DeferredDeleteSocketProcessParent : public Runnable {
  public:
   explicit DeferredDeleteSocketProcessParent(
       UniquePtr<SocketProcessParent>&& aParent)
       : Runnable("net::DeferredDeleteSocketProcessParent"),
         mParent(std::move(aParent)) {}
 
--- a/netwerk/ipc/SocketProcessParent.h
+++ b/netwerk/ipc/SocketProcessParent.h
@@ -104,16 +104,20 @@ class SocketProcessParent final
       nsTArray<ByteArray>* aBuiltChain);
 
   already_AddRefed<PProxyConfigLookupParent> AllocPProxyConfigLookupParent(
       nsIURI* aURI, const uint32_t& aProxyResolveFlags);
   mozilla::ipc::IPCResult RecvPProxyConfigLookupConstructor(
       PProxyConfigLookupParent* aActor, nsIURI* aURI,
       const uint32_t& aProxyResolveFlags) override;
 
+  mozilla::ipc::IPCResult RecvCachePushCheck(
+      nsIURI* aPushedURL, OriginAttributes&& aOriginAttributes,
+      nsCString&& aRequestString, CachePushCheckResolver&& aResolver);
+
  private:
   SocketProcessHost* mHost;
   UniquePtr<dom::MemoryReportRequestHost> mMemoryReportRequest;
 
   static void Destroy(UniquePtr<SocketProcessParent>&& aParent);
 };
 
 }  // namespace net
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/CachePushChecker.cpp
@@ -0,0 +1,248 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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/. */
+
+#include "CachePushChecker.h"
+
+#include "LoadContextInfo.h"
+#include "mozilla/net/SocketProcessChild.h"
+#include "nsICacheEntry.h"
+#include "nsICacheStorageService.h"
+#include "nsICacheStorage.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(CachePushChecker, nsICacheEntryOpenCallback);
+
+CachePushChecker::CachePushChecker(nsIURI* aPushedURL,
+                                   const OriginAttributes& aOriginAttributes,
+                                   const nsACString& aRequestString,
+                                   std::function<void(bool)>&& aCallback)
+    : mPushedURL(aPushedURL),
+      mOriginAttributes(aOriginAttributes),
+      mRequestString(aRequestString),
+      mCallback(std::move(aCallback)),
+      mCurrentEventTarget(GetCurrentThreadEventTarget()) {}
+
+nsresult CachePushChecker::DoCheck() {
+  if (XRE_IsSocketProcess()) {
+    RefPtr<CachePushChecker> self = this;
+    return NS_DispatchToMainThread(
+        NS_NewRunnableFunction(
+            "CachePushChecker::DoCheck",
+            [self]() {
+              if (SocketProcessChild* child =
+                      SocketProcessChild::GetSingleton()) {
+                child
+                    ->SendCachePushCheck(self->mPushedURL,
+                                         self->mOriginAttributes,
+                                         self->mRequestString)
+                    ->Then(
+                        GetCurrentThreadSerialEventTarget(), __func__,
+                        [self](bool aResult) { self->InvokeCallback(aResult); },
+                        [](const mozilla::ipc::ResponseRejectReason) {});
+              }
+            }),
+        NS_DISPATCH_NORMAL);
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsICacheStorageService> css =
+      do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  RefPtr<LoadContextInfo> lci = GetLoadContextInfo(false, mOriginAttributes);
+  nsCOMPtr<nsICacheStorage> ds;
+  rv = css->DiskCacheStorage(lci, false, getter_AddRefs(ds));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return ds->AsyncOpenURI(
+      mPushedURL, EmptyCString(),
+      nsICacheStorage::OPEN_READONLY | nsICacheStorage::OPEN_SECRETLY, this);
+}
+
+NS_IMETHODIMP
+CachePushChecker::OnCacheEntryCheck(nsICacheEntry* entry,
+                                    nsIApplicationCache* appCache,
+                                    uint32_t* result) {
+  MOZ_ASSERT(XRE_IsParentProcess());
+
+  // We never care to fully open the entry, since we won't actually use it.
+  // We just want to be able to do all our checks to see if a future channel can
+  // use this entry, or if we need to accept the push.
+  *result = nsICacheEntryOpenCallback::ENTRY_NOT_WANTED;
+
+  bool isForcedValid = false;
+  entry->GetIsForcedValid(&isForcedValid);
+
+  nsHttpRequestHead requestHead;
+  requestHead.ParseHeaderSet(mRequestString.BeginReading());
+  nsHttpResponseHead cachedResponseHead;
+  bool acceptPush = true;
+  auto onExitGuard = MakeScopeExit([&] { InvokeCallback(acceptPush); });
+
+  nsresult rv =
+      nsHttp::GetHttpResponseHeadFromCacheEntry(entry, &cachedResponseHead);
+  if (NS_FAILED(rv)) {
+    // Couldn't make sense of what's in the cache entry, go ahead and accept
+    // the push.
+    return NS_OK;
+  }
+
+  if ((cachedResponseHead.Status() / 100) != 2) {
+    // Assume the push is sending us a success, while we don't have one in the
+    // cache, so we'll accept the push.
+    return NS_OK;
+  }
+
+  // Get the method that was used to generate the cached response
+  nsCString buf;
+  rv = entry->GetMetaDataElement("request-method", getter_Copies(buf));
+  if (NS_FAILED(rv)) {
+    // Can't check request method, accept the push
+    return NS_OK;
+  }
+  nsAutoCString pushedMethod;
+  requestHead.Method(pushedMethod);
+  if (!buf.Equals(pushedMethod)) {
+    // Methods don't match, accept the push
+    return NS_OK;
+  }
+
+  int64_t size, contentLength;
+  rv = nsHttp::CheckPartial(entry, &size, &contentLength, &cachedResponseHead);
+  if (NS_FAILED(rv)) {
+    // Couldn't figure out if this was partial or not, accept the push.
+    return NS_OK;
+  }
+
+  if (size == int64_t(-1) || contentLength != size) {
+    // This is partial content in the cache, accept the push.
+    return NS_OK;
+  }
+
+  nsAutoCString requestedETag;
+  if (NS_FAILED(requestHead.GetHeader(nsHttp::If_Match, requestedETag))) {
+    // Can't check etag
+    return NS_OK;
+  }
+  if (!requestedETag.IsEmpty()) {
+    nsAutoCString cachedETag;
+    if (NS_FAILED(cachedResponseHead.GetHeader(nsHttp::ETag, cachedETag))) {
+      // Can't check etag
+      return NS_OK;
+    }
+    if (!requestedETag.Equals(cachedETag)) {
+      // ETags don't match, accept the push.
+      return NS_OK;
+    }
+  }
+
+  nsAutoCString imsString;
+  Unused << requestHead.GetHeader(nsHttp::If_Modified_Since, imsString);
+  if (!buf.IsEmpty()) {
+    uint32_t ims = buf.ToInteger(&rv);
+    uint32_t lm;
+    rv = cachedResponseHead.GetLastModifiedValue(&lm);
+    if (NS_SUCCEEDED(rv) && lm && lm < ims) {
+      // The push appears to be newer than what's in our cache, accept it.
+      return NS_OK;
+    }
+  }
+
+  nsAutoCString cacheControlRequestHeader;
+  Unused << requestHead.GetHeader(nsHttp::Cache_Control,
+                                  cacheControlRequestHeader);
+  CacheControlParser cacheControlRequest(cacheControlRequestHeader);
+  if (cacheControlRequest.NoStore()) {
+    // Don't use a no-store cache entry, accept the push.
+    return NS_OK;
+  }
+
+  nsCString cachedAuth;
+  rv = entry->GetMetaDataElement("auth", getter_Copies(cachedAuth));
+  if (NS_SUCCEEDED(rv)) {
+    uint32_t lastModifiedTime;
+    rv = entry->GetLastModified(&lastModifiedTime);
+    if (NS_SUCCEEDED(rv)) {
+      if ((gHttpHandler->SessionStartTime() > lastModifiedTime) &&
+          !cachedAuth.IsEmpty()) {
+        // Need to revalidate this, as the auth is old. Accept the push.
+        return NS_OK;
+      }
+
+      if (cachedAuth.IsEmpty() &&
+          requestHead.HasHeader(nsHttp::Authorization)) {
+        // They're pushing us something with auth, but we didn't cache anything
+        // with auth. Accept the push.
+        return NS_OK;
+      }
+    }
+  }
+
+  bool weaklyFramed, isImmutable;
+  nsHttp::DetermineFramingAndImmutability(entry, &cachedResponseHead, true,
+                                          &weaklyFramed, &isImmutable);
+
+  // We'll need this value in later computations...
+  uint32_t lastModifiedTime;
+  rv = entry->GetLastModified(&lastModifiedTime);
+  if (NS_FAILED(rv)) {
+    // Ugh, this really sucks. OK, accept the push.
+    return NS_OK;
+  }
+
+  // Determine if this is the first time that this cache entry
+  // has been accessed during this session.
+  bool fromPreviousSession =
+      (gHttpHandler->SessionStartTime() > lastModifiedTime);
+
+  bool validationRequired = nsHttp::ValidationRequired(
+      isForcedValid, &cachedResponseHead, 0 /*NWGH: ??? - loadFlags*/, false,
+      isImmutable, false, requestHead, entry, cacheControlRequest,
+      fromPreviousSession);
+
+  if (validationRequired) {
+    // A real channel would most likely hit the net at this point, so let's
+    // accept the push.
+    return NS_OK;
+  }
+
+  // If we get here, then we would be able to use this cache entry. Cancel the
+  // push so as not to waste any more bandwidth.
+  acceptPush = false;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+CachePushChecker::OnCacheEntryAvailable(nsICacheEntry* entry, bool isNew,
+                                        nsIApplicationCache* appCache,
+                                        nsresult result) {
+  // Nothing to do here, all the work is in OnCacheEntryCheck.
+  return NS_OK;
+}
+
+void CachePushChecker::InvokeCallback(bool aResult) {
+  RefPtr<CachePushChecker> self = this;
+  auto task = [self, aResult]() { self->mCallback(aResult); };
+  if (!mCurrentEventTarget->IsOnCurrentThread()) {
+    mCurrentEventTarget->Dispatch(
+        NS_NewRunnableFunction("CachePushChecker::InvokeCallback",
+                               std::move(task)),
+        NS_DISPATCH_NORMAL);
+    return;
+  }
+
+  task();
+}
+
+}  // namespace net
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/CachePushChecker.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* 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/. */
+
+#ifndef CachePushChecker_h__
+#define CachePushChecker_h__
+
+#include <functional>
+#include "mozilla/OriginAttributes.h"
+#include "nsICacheEntryOpenCallback.h"
+#include "nsIEventTarget.h"
+#include "nsString.h"
+#include "nsIURI.h"
+
+namespace mozilla {
+namespace net {
+
+class CachePushChecker final : public nsICacheEntryOpenCallback {
+ public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSICACHEENTRYOPENCALLBACK
+
+  explicit CachePushChecker(nsIURI* aPushedURL,
+                            const OriginAttributes& aOriginAttributes,
+                            const nsACString& aRequestString,
+                            std::function<void(bool)>&& aCallback);
+  nsresult DoCheck();
+
+ private:
+  ~CachePushChecker() = default;
+  void InvokeCallback(bool aResult);
+
+  nsCOMPtr<nsIURI> mPushedURL;
+  OriginAttributes mOriginAttributes;
+  nsCString mRequestString;
+  std::function<void(bool)> mCallback;
+  nsCOMPtr<nsIEventTarget> mCurrentEventTarget;
+};
+
+}  // namespace net
+}  // namespace mozilla
+
+#endif
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -31,20 +31,18 @@
 #include "nsISupportsPriority.h"
 #include "nsStandardURL.h"
 #include "nsURLHelper.h"
 #include "prnetdb.h"
 #include "sslt.h"
 #include "mozilla/Sprintf.h"
 #include "nsSocketTransportService2.h"
 #include "nsNetUtil.h"
-#include "nsICacheEntry.h"
-#include "nsICacheStorageService.h"
-#include "nsICacheStorage.h"
 #include "CacheControlParser.h"
+#include "CachePushChecker.h"
 #include "LoadContextInfo.h"
 #include "TCPFastOpenLayer.h"
 #include "nsQueryObject.h"
 
 namespace mozilla {
 namespace net {
 
 // Http2Session has multiple inheritance of things that implement nsISupports
@@ -2028,235 +2026,66 @@ nsresult Http2Session::RecvPushPromise(H
       self->CleanupStream(pushedWeak, NS_ERROR_FAILURE, REFUSED_STREAM_ERROR);
       self->ResetDownstreamState();
       return NS_OK;
     }
 
     // Kick off a lookup into the HTTP cache so we can cancel the push if it's
     // unneeded (we already have it in our local regular cache). See bug
     // 1367551.
-    nsCOMPtr<nsICacheStorageService> css =
-        do_GetService("@mozilla.org/netwerk/cache-storage-service;1");
-    mozilla::OriginAttributes oa;
-    pushedWeak->GetOriginAttributes(&oa);
-    RefPtr<LoadContextInfo> lci = GetLoadContextInfo(false, oa);
-    nsCOMPtr<nsICacheStorage> ds;
-    css->DiskCacheStorage(lci, false, getter_AddRefs(ds));
+
     // Build up our full URL for the cache lookup
     nsAutoCString spec;
     spec.Assign(pushedWeak->Origin());
     spec.Append(pushedWeak->Path());
     nsCOMPtr<nsIURI> pushedURL;
     // Nifty trick: this doesn't actually do anything origin-specific, it's just
     // named that way. So by passing it the full spec here, we get a URL with
     // the full path.
     // Another nifty trick! Even though this is using nsIURIs (which are not
     // generally ok off the main thread), since we're not using the protocol
     // handler to create any URIs, this will work just fine here. Don't try this
     // at home, though, kids. I'm a trained professional.
     if (NS_SUCCEEDED(Http2Stream::MakeOriginURL(spec, pushedURL))) {
       LOG3(("Http2Session::RecvPushPromise %p check disk cache for entry",
             self));
-      RefPtr<CachePushCheckCallback> cpcc = new CachePushCheckCallback(
-          self, promisedID,
-          static_cast<Http2PushedStream*>(pushedWeak.get())
-              ->GetRequestString());
-      if (NS_FAILED(ds->AsyncOpenURI(
-              pushedURL, EmptyCString(),
-              nsICacheStorage::OPEN_READONLY | nsICacheStorage::OPEN_SECRETLY,
-              cpcc))) {
+      mozilla::OriginAttributes oa;
+      pushedWeak->GetOriginAttributes(&oa);
+      RefPtr<Http2Session> session = self;
+      auto callback = [session, promisedID](bool aAccepted) {
+        MOZ_ASSERT(OnSocketThread());
+
+        if (!aAccepted) {
+          session->CleanupStream(promisedID, NS_ERROR_FAILURE,
+                                 Http2Session::REFUSED_STREAM_ERROR);
+        }
+      };
+      RefPtr<CachePushChecker> checker = new CachePushChecker(
+          pushedURL, oa,
+          static_cast<Http2PushedStream*>(pushedWeak.get())->GetRequestString(),
+          std::move(callback));
+      if (NS_FAILED(checker->DoCheck())) {
         LOG3(
             ("Http2Session::RecvPushPromise %p failed to open cache entry for "
              "push check",
              self));
-      } else if (!pushedWeak) {
-        // We have an up to date entry in our cache, so the stream was closed
-        // and released in CachePushCheckCallback::OnCacheEntryCheck().
-        return NS_OK;
       }
     }
   }
 
   pushedWeak->SetHTTPState(Http2Stream::RESERVED_BY_REMOTE);
   static_assert(Http2Stream::kWorstPriority >= 0,
                 "kWorstPriority out of range");
   uint32_t priorityDependency = pushedWeak->PriorityDependency();
   uint8_t priorityWeight = pushedWeak->PriorityWeight();
   self->SendPriorityFrame(promisedID, priorityDependency, priorityWeight);
   self->ResetDownstreamState();
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS(Http2Session::CachePushCheckCallback,
-                  nsICacheEntryOpenCallback);
-
-Http2Session::CachePushCheckCallback::CachePushCheckCallback(
-    Http2Session* session, uint32_t promisedID, const nsACString& requestString)
-    : mPromisedID(promisedID) {
-  mSession = session;
-  mRequestHead.ParseHeaderSet(requestString.BeginReading());
-}
-
-NS_IMETHODIMP
-Http2Session::CachePushCheckCallback::OnCacheEntryCheck(
-    nsICacheEntry* entry, nsIApplicationCache* appCache, uint32_t* result) {
-  MOZ_ASSERT(OnSocketThread(), "Not on socket thread?!");
-
-  // We never care to fully open the entry, since we won't actually use it.
-  // We just want to be able to do all our checks to see if a future channel can
-  // use this entry, or if we need to accept the push.
-  *result = nsICacheEntryOpenCallback::ENTRY_NOT_WANTED;
-
-  bool isForcedValid = false;
-  entry->GetIsForcedValid(&isForcedValid);
-
-  nsHttpResponseHead cachedResponseHead;
-  nsresult rv =
-      nsHttp::GetHttpResponseHeadFromCacheEntry(entry, &cachedResponseHead);
-  if (NS_FAILED(rv)) {
-    // Couldn't make sense of what's in the cache entry, go ahead and accept
-    // the push.
-    return NS_OK;
-  }
-
-  if ((cachedResponseHead.Status() / 100) != 2) {
-    // Assume the push is sending us a success, while we don't have one in the
-    // cache, so we'll accept the push.
-    return NS_OK;
-  }
-
-  // Get the method that was used to generate the cached response
-  nsCString buf;
-  rv = entry->GetMetaDataElement("request-method", getter_Copies(buf));
-  if (NS_FAILED(rv)) {
-    // Can't check request method, accept the push
-    return NS_OK;
-  }
-  nsAutoCString pushedMethod;
-  mRequestHead.Method(pushedMethod);
-  if (!buf.Equals(pushedMethod)) {
-    // Methods don't match, accept the push
-    return NS_OK;
-  }
-
-  int64_t size, contentLength;
-  rv = nsHttp::CheckPartial(entry, &size, &contentLength, &cachedResponseHead);
-  if (NS_FAILED(rv)) {
-    // Couldn't figure out if this was partial or not, accept the push.
-    return NS_OK;
-  }
-
-  if (size == int64_t(-1) || contentLength != size) {
-    // This is partial content in the cache, accept the push.
-    return NS_OK;
-  }
-
-  nsAutoCString requestedETag;
-  if (NS_FAILED(mRequestHead.GetHeader(nsHttp::If_Match, requestedETag))) {
-    // Can't check etag
-    return NS_OK;
-  }
-  if (!requestedETag.IsEmpty()) {
-    nsAutoCString cachedETag;
-    if (NS_FAILED(cachedResponseHead.GetHeader(nsHttp::ETag, cachedETag))) {
-      // Can't check etag
-      return NS_OK;
-    }
-    if (!requestedETag.Equals(cachedETag)) {
-      // ETags don't match, accept the push.
-      return NS_OK;
-    }
-  }
-
-  nsAutoCString imsString;
-  Unused << mRequestHead.GetHeader(nsHttp::If_Modified_Since, imsString);
-  if (!buf.IsEmpty()) {
-    uint32_t ims = buf.ToInteger(&rv);
-    uint32_t lm;
-    rv = cachedResponseHead.GetLastModifiedValue(&lm);
-    if (NS_SUCCEEDED(rv) && lm && lm < ims) {
-      // The push appears to be newer than what's in our cache, accept it.
-      return NS_OK;
-    }
-  }
-
-  nsAutoCString cacheControlRequestHeader;
-  Unused << mRequestHead.GetHeader(nsHttp::Cache_Control,
-                                   cacheControlRequestHeader);
-  CacheControlParser cacheControlRequest(cacheControlRequestHeader);
-  if (cacheControlRequest.NoStore()) {
-    // Don't use a no-store cache entry, accept the push.
-    return NS_OK;
-  }
-
-  nsCString cachedAuth;
-  rv = entry->GetMetaDataElement("auth", getter_Copies(cachedAuth));
-  if (NS_SUCCEEDED(rv)) {
-    uint32_t lastModifiedTime;
-    rv = entry->GetLastModified(&lastModifiedTime);
-    if (NS_SUCCEEDED(rv)) {
-      if ((gHttpHandler->SessionStartTime() > lastModifiedTime) &&
-          !cachedAuth.IsEmpty()) {
-        // Need to revalidate this, as the auth is old. Accept the push.
-        return NS_OK;
-      }
-
-      if (cachedAuth.IsEmpty() &&
-          mRequestHead.HasHeader(nsHttp::Authorization)) {
-        // They're pushing us something with auth, but we didn't cache anything
-        // with auth. Accept the push.
-        return NS_OK;
-      }
-    }
-  }
-
-  bool weaklyFramed, isImmutable;
-  nsHttp::DetermineFramingAndImmutability(entry, &cachedResponseHead, true,
-                                          &weaklyFramed, &isImmutable);
-
-  // We'll need this value in later computations...
-  uint32_t lastModifiedTime;
-  rv = entry->GetLastModified(&lastModifiedTime);
-  if (NS_FAILED(rv)) {
-    // Ugh, this really sucks. OK, accept the push.
-    return NS_OK;
-  }
-
-  // Determine if this is the first time that this cache entry
-  // has been accessed during this session.
-  bool fromPreviousSession =
-      (gHttpHandler->SessionStartTime() > lastModifiedTime);
-
-  bool validationRequired = nsHttp::ValidationRequired(
-      isForcedValid, &cachedResponseHead, 0 /*NWGH: ??? - loadFlags*/, false,
-      isImmutable, false, mRequestHead, entry, cacheControlRequest,
-      fromPreviousSession);
-
-  if (validationRequired) {
-    // A real channel would most likely hit the net at this point, so let's
-    // accept the push.
-    return NS_OK;
-  }
-
-  // If we get here, then we would be able to use this cache entry. Cancel the
-  // push so as not to waste any more bandwidth.
-  mSession->CleanupStream(mPromisedID, NS_ERROR_FAILURE,
-                          Http2Session::REFUSED_STREAM_ERROR);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-Http2Session::CachePushCheckCallback::OnCacheEntryAvailable(
-    nsICacheEntry* entry, bool isNew, nsIApplicationCache* appCache,
-    nsresult result) {
-  // Nothing to do here, all the work is in OnCacheEntryCheck.
-  return NS_OK;
-}
-
 nsresult Http2Session::RecvPing(Http2Session* self) {
   MOZ_ASSERT(self->mInputFrameType == FRAME_TYPE_PING);
 
   LOG3(("Http2Session::RecvPing %p PING Flags 0x%X.", self,
         self->mInputFrameFlags));
 
   if (self->mInputFrameDataSize != 8) {
     LOG3(("Http2Session::RecvPing %p PING had wrong amount of data %d", self,
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -559,32 +559,16 @@ class Http2Session final : public ASpdyS
   nsDataHashtable<nsCStringHashKey, bool> mOriginFrame;
 
   nsDataHashtable<nsCStringHashKey, bool> mJoinConnectionCache;
 
   uint64_t mCurrentForegroundTabOuterContentWindowId;
 
   uint32_t mCntActivated;
 
-  class CachePushCheckCallback final : public nsICacheEntryOpenCallback {
-   public:
-    CachePushCheckCallback(Http2Session* session, uint32_t promisedID,
-                           const nsACString& requestString);
-
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSICACHEENTRYOPENCALLBACK
-
-   private:
-    ~CachePushCheckCallback() = default;
-
-    RefPtr<Http2Session> mSession;
-    uint32_t mPromisedID;
-    nsHttpRequestHead mRequestHead;
-  };
-
   // A h2 session will be created before all socket events are trigered,
   // e.g. NS_NET_STATUS_TLS_HANDSHAKE_ENDED and for TFO many others.
   // We should propagate this events to the first nsHttpTransaction.
   RefPtr<nsHttpTransaction> mFirstHttpTransaction;
   bool mTlsHandshakeFinished;
 
   bool mCheckNetworkStallsWithTFO;
   PRIntervalTime mLastRequestBytesSentTime;
--- a/netwerk/protocol/http/HttpConnectionMgrParent.cpp
+++ b/netwerk/protocol/http/HttpConnectionMgrParent.cpp
@@ -8,16 +8,17 @@
 #include "HttpLog.h"
 
 #include "HttpConnectionMgrParent.h"
 #include "AltSvcTransactionParent.h"
 #include "mozilla/net/HttpTransactionParent.h"
 #include "nsHttpConnectionInfo.h"
 #include "nsISpeculativeConnect.h"
 #include "nsIOService.h"
+#include "nsQueryObject.h"
 
 namespace mozilla {
 namespace net {
 
 NS_IMPL_ISUPPORTS0(HttpConnectionMgrParent)
 
 HttpConnectionMgrParent::HttpConnectionMgrParent() : mShutDown(false) {}
 
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -82,16 +82,17 @@ UNIFIED_SOURCES += [
     'AltServiceParent.cpp',
     'AltSvcTransactionChild.cpp',
     'AltSvcTransactionParent.cpp',
     'ASpdySession.cpp',
     'BackgroundChannelRegistrar.cpp',
     'BackgroundDataBridgeChild.cpp',
     'BackgroundDataBridgeParent.cpp',
     'CacheControlParser.cpp',
+    'CachePushChecker.cpp',
     'ClassifierDummyChannel.cpp',
     'ClassifierDummyChannelChild.cpp',
     'ClassifierDummyChannelParent.cpp',
     'ConnectionDiagnostics.cpp',
     'DelayHttpChannelQueue.cpp',
     'Http2Compression.cpp',
     'Http2Push.cpp',
     'Http2Session.cpp',
--- a/netwerk/protocol/http/nsHttpActivityDistributor.cpp
+++ b/netwerk/protocol/http/nsHttpActivityDistributor.cpp
@@ -3,18 +3,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "mozilla/net/SocketProcessChild.h"
 #include "mozilla/net/SocketProcessParent.h"
 #include "nsHttpActivityDistributor.h"
+#include "nsHttpHandler.h"
 #include "nsCOMPtr.h"
 #include "nsIOService.h"
+#include "nsQueryObject.h"
 #include "nsThreadUtils.h"
 #include "NullHttpChannel.h"
 
 namespace mozilla {
 namespace net {
 
 typedef nsMainThreadPtrHolder<nsIHttpActivityObserver> ObserverHolder;
 typedef nsMainThreadPtrHandle<nsIHttpActivityObserver> ObserverHandle;