Bug 1211000 - Move CORS preflight logic from nsCORSListenerProxy to nsCORSPreflightListener. r=ehsan, a=lizzard
authorJonas Sicking <jonas@sicking.cc>
Thu, 15 Oct 2015 02:07:25 -0700
changeset 296690 9576cf65c2b5
parent 296689 5ff2e264e7ff
child 296691 e7f899e48fc6
push id5291
push usercbook@mozilla.com
push date2015-11-11 09:59 +0000
treeherdermozilla-beta@9576cf65c2b5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, lizzard
bugs1211000
milestone43.0
Bug 1211000 - Move CORS preflight logic from nsCORSListenerProxy to nsCORSPreflightListener. r=ehsan, a=lizzard
dom/tests/mochitest/beacon/test_beaconPreflightFailure.html
netwerk/protocol/http/nsCORSListenerProxy.cpp
netwerk/protocol/http/nsCORSListenerProxy.h
netwerk/protocol/http/nsHttpChannel.cpp
--- a/dom/tests/mochitest/beacon/test_beaconPreflightFailure.html
+++ b/dom/tests/mochitest/beacon/test_beaconPreflightFailure.html
@@ -21,17 +21,17 @@ var beaconUrl = "http://example.com/test
 
 var intervalID = null;
 
 function queryIfBeaconSucceeded() {
   clearInterval(intervalID);
   var xhr = new XMLHttpRequest();
   xhr.open("GET", "beacon-preflight-handler.sjs?verify", true);
   xhr.onload = function() {
-    is(xhr.responseText, "red", "SendBeacon should have failed because of a failed preflight!");
+    is(xhr.responseText, "green", "SendBeacon should have failed because of a failed preflight!");
     SimpleTest.finish();
   };
   xhr.onerror = function() {
     ok(false, "xhr request returned error");
     SimpleTest.finish();
   };
   xhr.send();
 }
--- a/netwerk/protocol/http/nsCORSListenerProxy.cpp
+++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp
@@ -438,45 +438,20 @@ nsCORSListenerProxy::Shutdown()
 nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
                                          nsIPrincipal* aRequestingPrincipal,
                                          bool aWithCredentials)
   : mOuterListener(aOuter),
     mRequestingPrincipal(aRequestingPrincipal),
     mOriginHeaderPrincipal(aRequestingPrincipal),
     mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
     mRequestApproved(false),
-    mHasBeenCrossSite(false),
-    mIsPreflight(false)
+    mHasBeenCrossSite(false)
 {
 }
 
-nsCORSListenerProxy::nsCORSListenerProxy(nsIStreamListener* aOuter,
-                                         nsIPrincipal* aRequestingPrincipal,
-                                         bool aWithCredentials,
-                                         const nsCString& aPreflightMethod,
-                                         const nsTArray<nsCString>& aPreflightHeaders)
-  : mOuterListener(aOuter),
-    mRequestingPrincipal(aRequestingPrincipal),
-    mOriginHeaderPrincipal(aRequestingPrincipal),
-    mWithCredentials(aWithCredentials && !gDisableCORSPrivateData),
-    mRequestApproved(false),
-    mHasBeenCrossSite(false),
-    mIsPreflight(true),
-#ifdef DEBUG
-    mInited(false),
-#endif
-    mPreflightMethod(aPreflightMethod),
-    mPreflightHeaders(aPreflightHeaders)
-{
-  for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
-    ToLowerCase(mPreflightHeaders[i]);
-  }
-  mPreflightHeaders.Sort();
-}
-
 nsCORSListenerProxy::~nsCORSListenerProxy()
 {
 }
 
 nsresult
 nsCORSListenerProxy::Init(nsIChannel* aChannel, DataURIHandling aAllowDataURI)
 {
   aChannel->GetNotificationCallbacks(getter_AddRefs(mOuterNotificationCallbacks));
@@ -565,17 +540,16 @@ nsCORSListenerProxy::CheckRequestApprove
   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
   NS_ENSURE_STATE(internal);
   bool responseSynthesized = false;
   if (NS_SUCCEEDED(internal->GetResponseSynthesized(&responseSynthesized)) &&
       responseSynthesized) {
     // For synthesized responses, we don't need to perform any checks.
     // Note: This would be unsafe if we ever changed our behavior to allow
     // service workers to intercept CORS preflights.
-    MOZ_ASSERT(!mIsPreflight);
     return NS_OK;
   }
 
   // Check the Access-Control-Allow-Origin header
   nsAutoCString allowedOriginHeader;
   rv = http->GetResponseHeader(
     NS_LITERAL_CSTRING("Access-Control-Allow-Origin"), allowedOriginHeader);
   if (NS_FAILED(rv)) {
@@ -601,78 +575,16 @@ nsCORSListenerProxy::CheckRequestApprove
       NS_LITERAL_CSTRING("Access-Control-Allow-Credentials"), allowCredentialsHeader);
 
     if (!allowCredentialsHeader.EqualsLiteral("true")) {
       LogBlockedRequest(aRequest, "CORSMissingAllowCredentials", nullptr);
       return NS_ERROR_DOM_BAD_URI;
     }
   }
 
-  if (mIsPreflight) {
-    bool succeedded;
-    rv = http->GetRequestSucceeded(&succeedded);
-    if (NS_FAILED(rv) || !succeedded) {
-      LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr);
-      return NS_ERROR_DOM_BAD_URI;
-    }
-
-    nsAutoCString headerVal;
-    // The "Access-Control-Allow-Methods" header contains a comma separated
-    // list of method names.
-    http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
-                            headerVal);
-    bool foundMethod = mPreflightMethod.EqualsLiteral("GET") ||
-                         mPreflightMethod.EqualsLiteral("HEAD") ||
-                         mPreflightMethod.EqualsLiteral("POST");
-    nsCCharSeparatedTokenizer methodTokens(headerVal, ',');
-    while(methodTokens.hasMoreTokens()) {
-      const nsDependentCSubstring& method = methodTokens.nextToken();
-      if (method.IsEmpty()) {
-        continue;
-      }
-      if (!NS_IsValidHTTPToken(method)) {
-        LogBlockedRequest(aRequest, "CORSInvalidAllowMethod",
-                          NS_ConvertUTF8toUTF16(method).get());
-        return NS_ERROR_DOM_BAD_URI;
-      }
-      foundMethod |= mPreflightMethod.Equals(method);
-    }
-    if (!foundMethod) {
-      LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr);
-      return NS_ERROR_DOM_BAD_URI;
-    }
-
-    // The "Access-Control-Allow-Headers" header contains a comma separated
-    // list of header names.
-    http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
-                            headerVal);
-    nsTArray<nsCString> headers;
-    nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
-    while(headerTokens.hasMoreTokens()) {
-      const nsDependentCSubstring& header = headerTokens.nextToken();
-      if (header.IsEmpty()) {
-        continue;
-      }
-      if (!NS_IsValidHTTPToken(header)) {
-        LogBlockedRequest(aRequest, "CORSInvalidAllowHeader",
-                          NS_ConvertUTF8toUTF16(header).get());
-        return NS_ERROR_DOM_BAD_URI;
-      }
-      headers.AppendElement(header);
-    }
-    for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
-      if (!headers.Contains(mPreflightHeaders[i],
-                            nsCaseInsensitiveCStringArrayComparator())) {
-        LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
-                          NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
-        return NS_ERROR_DOM_BAD_URI;
-      }
-    }
-  }
-
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCORSListenerProxy::OnStopRequest(nsIRequest* aRequest,
                                    nsISupports* aContext,
                                    nsresult aStatusCode)
 {
@@ -954,21 +866,17 @@ nsCORSListenerProxy::UpdateChannel(nsICh
   }
 
   // Set CORS attributes on channel so that intercepted requests get correct
   // values. We have to do this here because the CheckMayLoad checks may lead
   // to early return. We can't be sure this is an http channel though, so we
   // can't return early on failure.
   nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aChannel);
   if (internal) {
-    if (mIsPreflight) {
-      rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS_WITH_FORCED_PREFLIGHT);
-    } else {
-      rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS);
-    }
+    rv = internal->SetCorsMode(nsIHttpChannelInternal::CORS_MODE_CORS);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = internal->SetCorsIncludeCredentials(mWithCredentials);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Check that the uri is ok to load
   rv = nsContentUtils::GetSecurityManager()->
     CheckLoadURIWithPrincipal(mRequestingPrincipal, uri,
@@ -1014,40 +922,18 @@ nsCORSListenerProxy::UpdateChannel(nsICh
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel);
   NS_ENSURE_TRUE(http, NS_ERROR_FAILURE);
 
   rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, false);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Add preflight headers if this is a preflight request
-  if (mIsPreflight) {
-    rv = http->
-      SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
-                       mPreflightMethod, false);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!mPreflightHeaders.IsEmpty()) {
-      nsAutoCString headers;
-      for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
-        if (i != 0) {
-          headers += ',';
-        }
-        headers += mPreflightHeaders[i];
-      }
-      rv = http->
-        SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
-                         headers, false);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-  }
-
   // Make cookie-less if needed
-  if (mIsPreflight || !mWithCredentials) {
+  if (!mWithCredentials) {
     nsLoadFlags flags;
     rv = http->GetLoadFlags(&flags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     flags |= nsIRequest::LOAD_ANONYMOUS;
     rv = http->SetLoadFlags(flags);
     NS_ENSURE_SUCCESS(rv, rv);
   }
@@ -1060,41 +946,44 @@ nsCORSListenerProxy::UpdateChannel(nsICh
 
 // Class used as streamlistener and notification callback when
 // doing the initial OPTIONS request for a CORS check
 class nsCORSPreflightListener final : public nsIStreamListener,
                                       public nsIInterfaceRequestor,
                                       public nsIChannelEventSink
 {
 public:
-  nsCORSPreflightListener(nsIChannel* aOuterChannel,
-                          nsIStreamListener* aOuterListener,
-                          nsISupports* aOuterContext,
-                          nsIPrincipal* aReferrerPrincipal,
+  nsCORSPreflightListener(nsIPrincipal* aReferrerPrincipal,
                           nsICorsPreflightCallback* aCallback,
-                          bool aWithCredentials)
-   : mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
-     mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
-     mCallback(aCallback), mWithCredentials(aWithCredentials)
-  { }
+                          bool aWithCredentials,
+                          const nsCString& aPreflightMethod,
+                          const nsTArray<nsCString>& aPreflightHeaders)
+   : mPreflightMethod(aPreflightMethod),
+     mPreflightHeaders(aPreflightHeaders),
+     mReferrerPrincipal(aReferrerPrincipal),
+     mCallback(aCallback),
+     mWithCredentials(aWithCredentials)
+  {
+  }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSICHANNELEVENTSINK
 
+  nsresult CheckPreflightRequestApproved(nsIRequest* aRequest);
+
 private:
   ~nsCORSPreflightListener() {}
 
   void AddResultToCache(nsIRequest* aRequest);
 
-  nsCOMPtr<nsIChannel> mOuterChannel;
-  nsCOMPtr<nsIStreamListener> mOuterListener;
-  nsCOMPtr<nsISupports> mOuterContext;
+  nsCString mPreflightMethod;
+  nsTArray<nsCString> mPreflightHeaders;
   nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
   nsCOMPtr<nsICorsPreflightCallback> mCallback;
   bool mWithCredentials;
 };
 
 NS_IMPL_ISUPPORTS(nsCORSPreflightListener, nsIStreamListener,
                   nsIRequestObserver, nsIInterfaceRequestor,
                   nsIChannelEventSink)
@@ -1211,22 +1100,31 @@ nsCORSPreflightListener::AddResultToCach
     }
   }
 }
 
 NS_IMETHODIMP
 nsCORSPreflightListener::OnStartRequest(nsIRequest *aRequest,
                                         nsISupports *aContext)
 {
-  nsresult status;
-  nsresult rv = aRequest->GetStatus(&status);
+#ifdef DEBUG
+  {
+    nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
+    bool responseSynthesized = false;
+    if (internal &&
+        NS_SUCCEEDED(internal->GetResponseSynthesized(&responseSynthesized))) {
+      // For synthesized responses, we don't need to perform any checks.
+      // This would be unsafe if we ever changed our behavior to allow
+      // service workers to intercept CORS preflights.
+      MOZ_ASSERT(!responseSynthesized);
+    }
+  }
+#endif
 
-  if (NS_SUCCEEDED(rv)) {
-    rv = status;
-  }
+  nsresult rv = CheckPreflightRequestApproved(aRequest);
 
   if (NS_SUCCEEDED(rv)) {
     // Everything worked, try to cache and then fire off the actual request.
     AddResultToCache(aRequest);
 
     mCallback->OnPreflightSucceeded();
   } else {
     mCallback->OnPreflightFailed(rv);
@@ -1235,19 +1133,16 @@ nsCORSPreflightListener::OnStartRequest(
   return rv;
 }
 
 NS_IMETHODIMP
 nsCORSPreflightListener::OnStopRequest(nsIRequest *aRequest,
                                        nsISupports *aContext,
                                        nsresult aStatus)
 {
-  mOuterChannel = nullptr;
-  mOuterListener = nullptr;
-  mOuterContext = nullptr;
   mCallback = nullptr;
   return NS_OK;
 }
 
 /** nsIStreamListener methods **/
 
 NS_IMETHODIMP
 nsCORSPreflightListener::OnDataAvailable(nsIRequest *aRequest,
@@ -1270,16 +1165,92 @@ nsCORSPreflightListener::AsyncOnChannelR
   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) &&
       !NS_IsHSTSUpgradeRedirect(aOldChannel, aNewChannel, aFlags))
     return NS_ERROR_DOM_BAD_URI;
 
   callback->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
+nsresult
+nsCORSPreflightListener::CheckPreflightRequestApproved(nsIRequest* aRequest)
+{
+  nsresult status;
+  nsresult rv = aRequest->GetStatus(&status);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_SUCCESS(status, status);
+
+  // Test that things worked on a HTTP level
+  nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest);
+  nsCOMPtr<nsIHttpChannelInternal> internal = do_QueryInterface(aRequest);
+  NS_ENSURE_STATE(internal);
+
+  bool succeedded;
+  rv = http->GetRequestSucceeded(&succeedded);
+  if (NS_FAILED(rv) || !succeedded) {
+    LogBlockedRequest(aRequest, "CORSPreflightDidNotSucceed", nullptr);
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
+  nsAutoCString headerVal;
+  // The "Access-Control-Allow-Methods" header contains a comma separated
+  // list of method names.
+  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
+                          headerVal);
+  bool foundMethod = mPreflightMethod.EqualsLiteral("GET") ||
+                       mPreflightMethod.EqualsLiteral("HEAD") ||
+                       mPreflightMethod.EqualsLiteral("POST");
+  nsCCharSeparatedTokenizer methodTokens(headerVal, ',');
+  while(methodTokens.hasMoreTokens()) {
+    const nsDependentCSubstring& method = methodTokens.nextToken();
+    if (method.IsEmpty()) {
+      continue;
+    }
+    if (!NS_IsValidHTTPToken(method)) {
+      LogBlockedRequest(aRequest, "CORSInvalidAllowMethod",
+                        NS_ConvertUTF8toUTF16(method).get());
+      return NS_ERROR_DOM_BAD_URI;
+    }
+    foundMethod |= mPreflightMethod.Equals(method);
+  }
+  if (!foundMethod) {
+    LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr);
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
+  // The "Access-Control-Allow-Headers" header contains a comma separated
+  // list of header names.
+  http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
+                          headerVal);
+  nsTArray<nsCString> headers;
+  nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
+  while(headerTokens.hasMoreTokens()) {
+    const nsDependentCSubstring& header = headerTokens.nextToken();
+    if (header.IsEmpty()) {
+      continue;
+    }
+    if (!NS_IsValidHTTPToken(header)) {
+      LogBlockedRequest(aRequest, "CORSInvalidAllowHeader",
+                        NS_ConvertUTF8toUTF16(header).get());
+      return NS_ERROR_DOM_BAD_URI;
+    }
+    headers.AppendElement(header);
+  }
+  for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
+    if (!headers.Contains(mPreflightHeaders[i],
+                          nsCaseInsensitiveCStringArrayComparator())) {
+      LogBlockedRequest(aRequest, "CORSMissingAllowHeaderFromPreflight",
+                        NS_ConvertUTF8toUTF16(mPreflightHeaders[i]).get());
+      return NS_ERROR_DOM_BAD_URI;
+    }
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsCORSPreflightListener::GetInterface(const nsIID & aIID, void **aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
 void
 nsCORSListenerProxy::RemoveFromCorsPreflightCache(nsIURI* aURI,
@@ -1288,25 +1259,29 @@ nsCORSListenerProxy::RemoveFromCorsPrefl
   MOZ_ASSERT(XRE_IsParentProcess());
   if (sPreflightCache) {
     sPreflightCache->RemoveEntries(aURI, aRequestingPrincipal);
   }
 }
 
 nsresult
 nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
-                                        nsIStreamListener* aListener,
                                         nsIPrincipal* aPrincipal,
                                         nsICorsPreflightCallback* aCallback,
                                         bool aWithCredentials,
                                         nsTArray<nsCString>& aUnsafeHeaders,
                                         nsIChannel** aPreflightChannel)
 {
   *aPreflightChannel = nullptr;
 
+  if (gDisableCORS) {
+    LogBlockedRequest(aRequestChannel, "CORSDisabled", nullptr);
+    return NS_ERROR_DOM_BAD_URI;
+  }
+
   nsAutoCString method;
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequestChannel));
   NS_ENSURE_TRUE(httpChannel, NS_ERROR_UNEXPECTED);
   httpChannel->GetRequestMethod(method);
 
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1342,54 +1317,83 @@ nsCORSListenerProxy::StartCORSPreflight(
   nsCOMPtr<nsILoadGroup> loadGroup;
   rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsLoadFlags loadFlags;
   rv = aRequestChannel->GetLoadFlags(&loadFlags);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Preflight requests should never be intercepted by service workers.
+  // Preflight requests should never be intercepted by service workers and
+  // are always anonymous.
   // NOTE: We ignore CORS checks on synthesized responses (see the CORS
   // preflights, then we need to extend the GetResponseSynthesized() check in
   // nsCORSListenerProxy::CheckRequestApproved()). If we change our behavior
   // here and allow service workers to intercept CORS preflights, then that
   // check won't be safe any more.
-  loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
+  loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER |
+               nsIRequest::LOAD_ANONYMOUS;
 
   nsCOMPtr<nsIChannel> preflightChannel;
   rv = NS_NewChannelInternal(getter_AddRefs(preflightChannel),
                              uri,
                              loadInfo,
                              loadGroup,
                              nullptr,   // aCallbacks
                              loadFlags);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Set method and headers
   nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel);
   NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!");
 
   rv = preHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS"));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = preHttp->
+    SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"),
+                     method, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsTArray<nsCString> preflightHeaders;
+  if (!aUnsafeHeaders.IsEmpty()) {
+    for (uint32_t i = 0; i < aUnsafeHeaders.Length(); ++i) {
+      preflightHeaders.AppendElement();
+      ToLowerCase(aUnsafeHeaders[i], preflightHeaders[i]);
+    }
+    preflightHeaders.Sort();
+    nsAutoCString headers;
+    for (uint32_t i = 0; i < preflightHeaders.Length(); ++i) {
+      if (i != 0) {
+        headers += ',';
+      }
+      headers += preflightHeaders[i];
+    }
+    rv = preHttp->
+      SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"),
+                       headers, false);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   // Set up listener which will start the original channel
-  nsCOMPtr<nsIStreamListener> preflightListener =
-    new nsCORSPreflightListener(aRequestChannel, aListener, nullptr, aPrincipal,
-                                aCallback, aWithCredentials);
-  NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
+  nsRefPtr<nsCORSPreflightListener> preflightListener =
+    new nsCORSPreflightListener(aPrincipal, aCallback, aWithCredentials,
+                                method, preflightHeaders);
+
+  rv = preflightChannel->SetNotificationCallbacks(preflightListener);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   // Start preflight
   if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
     rv = preflightChannel->AsyncOpen2(preflightListener);
   }
   else {
     nsRefPtr<nsCORSListenerProxy> corsListener =
       new nsCORSListenerProxy(preflightListener, aPrincipal,
-                              aWithCredentials, method,
-                              aUnsafeHeaders);
+                              aWithCredentials);
     rv = corsListener->Init(preflightChannel, DataURIHandling::Disallow);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = preflightChannel->AsyncOpen(corsListener, nullptr);
   }
   NS_ENSURE_SUCCESS(rv, rv);
   
   // Return newly created preflight channel
   preflightChannel.forget(aPreflightChannel);
--- a/netwerk/protocol/http/nsCORSListenerProxy.h
+++ b/netwerk/protocol/http/nsCORSListenerProxy.h
@@ -68,25 +68,17 @@ public:
 private:
   // Only HttpChannelParent can call RemoveFromCorsPreflightCache
   friend class mozilla::net::HttpChannelParent;
   // Only nsHttpChannel can invoke CORS preflights
   friend class mozilla::net::nsHttpChannel;
 
   static void RemoveFromCorsPreflightCache(nsIURI* aURI,
                                            nsIPrincipal* aRequestingPrincipal);
-
-  nsCORSListenerProxy(nsIStreamListener* aOuter,
-                      nsIPrincipal* aRequestingPrincipal,
-                      bool aWithCredentials,
-                      const nsCString& aPreflightMethod,
-                      const nsTArray<nsCString>& aPreflightHeaders);
-
   static nsresult StartCORSPreflight(nsIChannel* aRequestChannel,
-                                     nsIStreamListener* aListener,
                                      nsIPrincipal* aPrincipal,
                                      nsICorsPreflightCallback* aCallback,
                                      bool aWithCredentials,
                                      nsTArray<nsCString>& aACUnsafeHeaders,
                                      nsIChannel** aPreflightChannel);
 
   ~nsCORSListenerProxy();
 
@@ -103,20 +95,17 @@ private:
   nsCOMPtr<nsINetworkInterceptController> mInterceptController;
   bool mWithCredentials;
   bool mRequestApproved;
   // Please note that the member variable mHasBeenCrossSite may rely on the
   // promise that the CSP directive 'upgrade-insecure-requests' upgrades
   // an http: request to https: in nsHttpChannel::Connect() and hence
   // a request might not be marked as cross site request based on that promise.
   bool mHasBeenCrossSite;
-  bool mIsPreflight;
 #ifdef DEBUG
   bool mInited;
 #endif
-  nsCString mPreflightMethod;
-  nsTArray<nsCString> mPreflightHeaders;
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIChannel> mOldRedirectChannel;
   nsCOMPtr<nsIChannel> mNewRedirectChannel;
 };
 
 #endif
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -449,17 +449,17 @@ nsresult
 nsHttpChannel::ContinueConnect()
 {
     // If we need to start a CORS preflight, do it now!
     // Note that it is important to do this before the early returns below.
     if (!mIsCorsPreflightDone && mRequireCORSPreflight &&
         mInterceptCache != INTERCEPTED) {
         MOZ_ASSERT(!mPreflightChannel);
         nsresult rv =
-            nsCORSListenerProxy::StartCORSPreflight(this, mListener,
+            nsCORSListenerProxy::StartCORSPreflight(this,
                                                     mPreflightPrincipal, this,
                                                     mWithCredentials,
                                                     mUnsafeHeaders,
                                                     getter_AddRefs(mPreflightChannel));
         return rv;
     }
 
     MOZ_RELEASE_ASSERT(!(mRequireCORSPreflight &&