Bug 1149250 add support for https upgrades from webextensions, r?mayhemer,bz,rpl draft
authorShane Caraveo <scaraveo@mozilla.com>
Fri, 08 Dec 2017 12:12:37 -0800
changeset 710159 5fc20db20a4e75bc5d59b18f509e5b156506959e
parent 708379 c0c5e19a32b8b1b74b9dc0360800aec01665f9b0
child 743526 ed50ca51b0ac1fe02dad8c74b88b98345a6131ae
push id92762
push usermixedpuppy@gmail.com
push dateFri, 08 Dec 2017 20:13:30 +0000
reviewersmayhemer, bz, rpl
bugs1149250
milestone59.0a1
Bug 1149250 add support for https upgrades from webextensions, r?mayhemer,bz,rpl MozReview-Commit-ID: ChIs2Q6bgEn
dom/webidl/ChannelWrapper.webidl
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
netwerk/protocol/http/NullHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsIHttpChannel.idl
netwerk/protocol/viewsource/nsViewSourceChannel.cpp
toolkit/components/extensions/schemas/web_request.json
toolkit/components/extensions/test/mochitest/mochitest-common.ini
toolkit/components/extensions/test/mochitest/test_ext_webrequest_upgrade.html
toolkit/components/extensions/webrequest/ChannelWrapper.cpp
toolkit/components/extensions/webrequest/ChannelWrapper.h
toolkit/modules/addons/WebRequest.jsm
--- a/dom/webidl/ChannelWrapper.webidl
+++ b/dom/webidl/ChannelWrapper.webidl
@@ -66,21 +66,32 @@ interface ChannelWrapper : EventTarget {
    * Cancels the request with the given nsresult status code.
    */
   [Throws]
   void cancel(unsigned long result);
 
   /**
    * Redirects the wrapped HTTP channel to the given URI. For other channel
    * types, this method will throw. The redirect is an internal redirect, and
-   * the  behavior is the same as nsIHttpChannel.redirectTo.
+   * the behavior is the same as nsIHttpChannel.redirectTo.
    */
   [Throws]
   void redirectTo(URI url);
 
+  /**
+   * Requests an upgrade of the HTTP channel to a secure request. For other channel
+   * types, this method will throw. The redirect is an internal redirect, and
+   * the behavior is the same as nsIHttpChannel.upgradeToSecure. Setting this
+   * flag is only effective during the WebRequest.onBeforeRequest in
+   * Web Extensions, calling this at any other point during the request will
+   * have no effect. Setting this flag in addition to calling redirectTo
+   * results in the redirect happening rather than the upgrade request.
+   */
+  [Throws]
+  void upgradeToSecure();
 
   /**
    * The content type of the request, usually as read from the Content-Type
    * header. This should be used in preference to the header to determine the
    * content type of the channel.
    */
   [Pure]
   attribute ByteString contentType;
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -156,16 +156,17 @@ HttpBaseChannel::HttpBaseChannel()
   : mCanceled(false)
   , mStartPos(UINT64_MAX)
   , mStatus(NS_OK)
   , mLoadFlags(LOAD_NORMAL)
   , mCaps(0)
   , mClassOfService(0)
   , mPriority(PRIORITY_NORMAL)
   , mRedirectionLimit(gHttpHandler->RedirectionLimit())
+  , mUpgradeToSecure(false)
   , mApplyConversion(true)
   , mIsPending(false)
   , mWasOpened(false)
   , mRequestObserversCalled(false)
   , mResponseHeadersModified(false)
   , mAllowSTS(true)
   , mThirdPartyFlags(0)
   , mUploadStreamHasHeaders(false)
@@ -196,16 +197,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mInternalRedirectCount(0)
   , mForcePending(false)
   , mCorsIncludeCredentials(false)
   , mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS)
   , mRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW)
   , mFetchCacheMode(nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT)
   , mOnStartRequestCalled(false)
   , mOnStopRequestCalled(false)
+  , mUpgradableToSecure(true)
   , mAfterOnStartRequestBegun(false)
   , mTransferSize(0)
   , mDecodedBodySize(0)
   , mEncodedBodySize(0)
   , mRequestContextID(0)
   , mContentWindowId(0)
   , mTopLevelOuterContentWindowId(0)
   , mRequireCORSPreflight(false)
@@ -2192,16 +2194,30 @@ HttpBaseChannel::RedirectTo(nsIURI *targ
   // This would break the nsIStreamListener contract.
   NS_ENSURE_FALSE(mOnStartRequestCalled, NS_ERROR_NOT_AVAILABLE);
 
   mAPIRedirectToURI = targetURI;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HttpBaseChannel::UpgradeToSecure()
+{
+  // Upgrades are handled internally between http-on-modify-request and
+  // http-on-before-connect, which means upgrades are only possible during
+  // on-modify, or WebRequest.onBeforeRequest in Web Extensions.  Once we are
+  // past the code path where upgrades are handled, attempting an upgrade
+  // will throw an error.
+  NS_ENSURE_TRUE(mUpgradableToSecure, NS_ERROR_NOT_AVAILABLE);
+
+  mUpgradeToSecure = true;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::GetRequestContextID(uint64_t *aRCID)
 {
   NS_ENSURE_ARG_POINTER(aRCID);
   *aRCID = mRequestContextID;
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -190,16 +190,17 @@ public:
   NS_IMETHOD SetRedirectionLimit(uint32_t value) override;
   NS_IMETHOD IsNoStoreResponse(bool *value) override;
   NS_IMETHOD IsNoCacheResponse(bool *value) override;
   NS_IMETHOD IsPrivateResponse(bool *value) override;
   NS_IMETHOD GetResponseStatus(uint32_t *aValue) override;
   NS_IMETHOD GetResponseStatusText(nsACString& aValue) override;
   NS_IMETHOD GetRequestSucceeded(bool *aValue) override;
   NS_IMETHOD RedirectTo(nsIURI *newURI) override;
+  NS_IMETHOD UpgradeToSecure() override;
   NS_IMETHOD GetRequestContextID(uint64_t *aRCID) override;
   NS_IMETHOD GetTransferSize(uint64_t *aTransferSize) override;
   NS_IMETHOD GetDecodedBodySize(uint64_t *aDecodedBodySize) override;
   NS_IMETHOD GetEncodedBodySize(uint64_t *aEncodedBodySize) override;
   NS_IMETHOD SetRequestContextID(uint64_t aRCID) override;
   NS_IMETHOD GetIsMainDocumentChannel(bool* aValue) override;
   NS_IMETHOD SetIsMainDocumentChannel(bool aValue) override;
   NS_IMETHOD GetProtocolVersion(nsACString & aProtocolVersion) override;
@@ -539,16 +540,17 @@ protected:
 
   Atomic<nsresult, ReleaseAcquire>  mStatus;
   uint32_t                          mLoadFlags;
   uint32_t                          mCaps;
   uint32_t                          mClassOfService;
   int16_t                           mPriority;
   uint8_t                           mRedirectionLimit;
 
+  uint32_t                          mUpgradeToSecure            : 1;
   uint32_t                          mApplyConversion            : 1;
   uint32_t                          mIsPending                  : 1;
   uint32_t                          mWasOpened                  : 1;
   // if 1 all "http-on-{opening|modify|etc}-request" observers have been called
   uint32_t                          mRequestObserversCalled     : 1;
   uint32_t                          mResponseHeadersModified    : 1;
   uint32_t                          mAllowSTS                   : 1;
   uint32_t                          mThirdPartyFlags            : 3;
@@ -643,16 +645,20 @@ protected:
   uint32_t mRedirectMode;
   uint32_t mFetchCacheMode;
 
   // These parameters are used to ensure that we do not call OnStartRequest and
   // OnStopRequest more than once.
   bool mOnStartRequestCalled;
   bool mOnStopRequestCalled;
 
+  // Defaults to true.  This is set to false when it is no longer possible
+  // to upgrade the request to a secure channel.
+  uint32_t                          mUpgradableToSecure : 1;
+
   // Defaults to false. Is set to true at the begining of OnStartRequest.
   // Used to ensure methods can't be called before OnStartRequest.
   bool mAfterOnStartRequestBegun;
 
   uint64_t mTransferSize;
   uint64_t mDecodedBodySize;
   uint64_t mEncodedBodySize;
 
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -2829,16 +2829,23 @@ HttpChannelChild::SetEmptyRequestHeader(
 NS_IMETHODIMP
 HttpChannelChild::RedirectTo(nsIURI *newURI)
 {
   // disabled until/unless addons run in child or something else needs this
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+HttpChannelChild::UpgradeToSecure()
+{
+  // disabled until/unless addons run in child or something else needs this
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 HttpChannelChild::GetProtocolVersion(nsACString& aProtocolVersion)
 {
   aProtocolVersion = mProtocolVersion;
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIHttpChannelInternal
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -91,16 +91,17 @@ public:
 
   // HttpBaseChannel::nsIHttpChannel
   NS_IMETHOD SetReferrerWithPolicy(nsIURI *referrer, uint32_t referrerPolicy) override;
   NS_IMETHOD SetRequestHeader(const nsACString& aHeader,
                               const nsACString& aValue,
                               bool aMerge) override;
   NS_IMETHOD SetEmptyRequestHeader(const nsACString& aHeader) override;
   NS_IMETHOD RedirectTo(nsIURI *newURI) override;
+  NS_IMETHOD UpgradeToSecure() override;
   NS_IMETHOD GetProtocolVersion(nsACString& aProtocolVersion) override;
   // nsIHttpChannelInternal
   NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) override;
   // nsISupportsPriority
   NS_IMETHOD SetPriority(int32_t value) override;
   // nsIClassOfService
   NS_IMETHOD SetClassFlags(uint32_t inFlags) override;
   NS_IMETHOD AddClassFlags(uint32_t inFlags) override;
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -284,16 +284,22 @@ NullHttpChannel::IsPrivateResponse(bool 
 
 NS_IMETHODIMP
 NullHttpChannel::RedirectTo(nsIURI *aNewURI)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+NullHttpChannel::UpgradeToSecure()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::GetRequestContextID(uint64_t *_retval)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::SetRequestContextID(uint64_t rcID)
 {
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -455,26 +455,30 @@ nsHttpChannel::OnBeforeConnect()
     OriginAttributes originAttributes;
     if (!NS_GetOriginAttributes(this, originAttributes)) {
         return NS_ERROR_FAILURE;
     }
     bool isHttp = false;
     rv = mURI->SchemeIs("http", &isHttp);
     NS_ENSURE_SUCCESS(rv,rv);
 
+    // At this point it is no longer possible to call HttpBaseChannel::UpgradeToSecure.
+    mUpgradableToSecure = false;
     if (isHttp) {
-        bool shouldUpgrade = false;
-        rv = NS_ShouldSecureUpgrade(mURI,
-                                    mLoadInfo,
-                                    resultPrincipal,
-                                    mPrivateBrowsing,
-                                    mAllowSTS,
-                                    originAttributes,
-                                    shouldUpgrade);
-        NS_ENSURE_SUCCESS(rv, rv);
+        bool shouldUpgrade = mUpgradeToSecure;
+        if (!shouldUpgrade) {
+            rv = NS_ShouldSecureUpgrade(mURI,
+                                        mLoadInfo,
+                                        resultPrincipal,
+                                        mPrivateBrowsing,
+                                        mAllowSTS,
+                                        originAttributes,
+                                        shouldUpgrade);
+            NS_ENSURE_SUCCESS(rv, rv);
+        }
         if (shouldUpgrade) {
             return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps);
         }
     }
 
     // ensure that we are using a valid hostname
     if (!net_IsValidHostName(nsDependentCString(mConnectionInfo->Origin())))
         return NS_ERROR_UNKNOWN_HOST;
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -440,16 +440,28 @@ interface nsIHttpChannel : nsIChannel
      * caller to call it wins.
      *
      * @throws NS_ERROR_NOT_AVAILABLE if called after the channel has already
      *         started to deliver the content to its listener.
      */
     [must_use] void redirectTo(in nsIURI aTargetURI);
 
     /**
+     * Flags a channel to be upgraded to HTTPS.
+     *
+     * Upgrading to a secure channel must happen before or during
+     * "http-on-modify-request". If redirectTo is called early as well, it
+     * will win and upgradeToSecure will be a no-op.
+     *
+     * @throws NS_ERROR_NOT_AVAILABLE if called after the channel has already
+     *         started to deliver the content to its listener.
+     */
+    [must_use] void upgradeToSecure();
+
+    /**
      * Identifies the request context for this load.
      */
     [noscript, must_use] attribute uint64_t requestContextID;
 
     /**
      * Unique ID of the channel, shared between parent and child. Needed if
      * the channel activity needs to be monitored across process boundaries,
      * like in devtools net monitor. See bug 1274556.
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -1073,16 +1073,23 @@ nsViewSourceChannel::IsPrivateResponse(b
 NS_IMETHODIMP
 nsViewSourceChannel::RedirectTo(nsIURI *uri)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->RedirectTo(uri);
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::UpgradeToSecure()
+{
+    return !mHttpChannel ? NS_ERROR_NULL_POINTER :
+        mHttpChannel->UpgradeToSecure();
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::GetRequestContextID(uint64_t *_retval)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->GetRequestContextID(_retval);
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::SetRequestContextID(uint64_t rcid)
--- a/toolkit/components/extensions/schemas/web_request.json
+++ b/toolkit/components/extensions/schemas/web_request.json
@@ -145,16 +145,21 @@
             "optional": true,
             "description": "If true, the request is cancelled. Used in onBeforeRequest, this prevents the request from being sent."
           },
           "redirectUrl": {
             "type": "string",
             "optional": true,
             "description": "Only used as a response to the onBeforeRequest and onHeadersReceived events. If set, the original request is prevented from being sent/completed and is instead redirected to the given URL. Redirections to non-HTTP schemes such as data: are allowed. Redirects initiated by a redirect action use the original request method for the redirect, with one exception: If the redirect is initiated at the onHeadersReceived stage, then the redirect will be issued using the GET method."
           },
+          "upgradeToSecure": {
+            "type": "boolean",
+            "optional": true,
+            "description": "Only used as a response to the onBeforeRequest event. If set, the original request is prevented from being sent/completed and is instead upgraded to a secure request.  If any extension returns <code>redirectUrl</code> during onBeforeRequest, <code>upgradeToSecure</code> will have no affect."
+          },
           "requestHeaders": {
             "$ref": "HttpHeaders",
             "optional": true,
             "description": "Only used as a response to the onBeforeSendHeaders event. If set, the request is made with these request headers instead."
           },
           "responseHeaders": {
             "$ref": "HttpHeaders",
             "optional": true,
--- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini
@@ -135,16 +135,17 @@ skip-if = os == 'android'
 [test_ext_webrequest_hsts.html]
 [test_ext_webrequest_basic.html]
 skip-if = os == 'android' && debug # bug 1397615
 [test_ext_webrequest_filter.html]
 [test_ext_webrequest_frameId.html]
 [test_ext_webrequest_responseBody.html]
 skip-if = os == 'android' || os == 'linux' # linux, bug 1398120
 [test_ext_webrequest_suspend.html]
+[test_ext_webrequest_upgrade.html]
 [test_ext_webrequest_upload.html]
 skip-if = os == 'android' # Currently fails in emulator tests
 [test_ext_webrequest_permission.html]
 [test_ext_webrequest_websocket.html]
 [test_ext_webnavigation.html]
 skip-if = os == 'android' && debug # bug 1397615
 [test_ext_webnavigation_filters.html]
 skip-if = os == 'android' && debug # bug 1397615
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_upgrade.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for simple WebExtension</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+add_task(function* test_webRequest_upgrade() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: [
+        "webRequest",
+        "webRequestBlocking",
+        "*://mochi.test/tests/*",
+      ],
+    },
+    background() {
+      browser.webRequest.onSendHeaders.addListener((details) => {
+        // At this point, the request should have been upgraded.
+        browser.test.assertTrue(details.url.startsWith("https:"), "request is upgraded");
+        browser.test.assertTrue(details.url.includes("file_sample"), "redirect after upgrade worked");
+        browser.test.sendMessage("finished");
+      }, {urls: ["*://mochi.test/tests/*"]});
+
+      browser.webRequest.onBeforeRequest.addListener((details) => {
+        browser.test.log(`onBeforeRequest ${details.requestId} ${details.url}`);
+        let url = new URL(details.url);
+        if (url.protocol == "http:") {
+          return {upgradeToSecure: true};
+        }
+        // After the channel is initially upgraded, we get another onBeforeRequest
+        // call.  Here we can redirect again to a new url.
+        if (details.url.includes("file_mixed.html")) {
+          let redirectUrl = new URL("file_sample.html", details.url).href;
+          return {redirectUrl};
+        }
+      }, {urls: ["*://mochi.test/tests/*"]}, ["blocking"]);
+    },
+  });
+
+  yield extension.startup();
+  let win = window.open("http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_mixed.html");
+  yield extension.awaitMessage("finished");
+  win.close();
+  yield extension.unload();
+});
+
+add_task(function* test_webRequest_redirect_wins() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: [
+        "webRequest",
+        "webRequestBlocking",
+        "*://mochi.test/tests/*",
+      ],
+    },
+    background() {
+      browser.webRequest.onSendHeaders.addListener((details) => {
+        // At this point, the request should have been redirected instead of upgraded.
+        browser.test.assertTrue(details.url.includes("file_sample"), "request was redirected");
+        browser.test.sendMessage("finished");
+      }, {urls: ["*://mochi.test/tests/*"]});
+
+      browser.webRequest.onBeforeRequest.addListener((details) => {
+        if (details.url.includes("file_mixed.html")) {
+          let redirectUrl = new URL("file_sample.html", details.url).href;
+          return {upgradeToSecure: true, redirectUrl};
+        }
+      }, {urls: ["*://mochi.test/tests/*"]}, ["blocking"]);
+    },
+  });
+
+  yield extension.startup();
+  let win = window.open("http://mochi.test:8888/tests/toolkit/components/extensions/test/mochitest/file_mixed.html");
+  yield extension.awaitMessage("finished");
+  win.close();
+  yield extension.unload();
+});
+</script>
+
+</body>
+</html>
--- a/toolkit/components/extensions/webrequest/ChannelWrapper.cpp
+++ b/toolkit/components/extensions/webrequest/ChannelWrapper.cpp
@@ -125,16 +125,28 @@ ChannelWrapper::RedirectTo(nsIURI* aURI,
     rv = chan->RedirectTo(aURI);
   }
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
   }
 }
 
 void
+ChannelWrapper::UpgradeToSecure(ErrorResult& aRv)
+{
+  nsresult rv = NS_ERROR_UNEXPECTED;
+  if (nsCOMPtr<nsIHttpChannel> chan = MaybeHttpChannel()) {
+    rv = chan->UpgradeToSecure();
+  }
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+  }
+}
+
+void
 ChannelWrapper::SetSuspended(bool aSuspended, ErrorResult& aRv)
 {
   if (aSuspended != mSuspended) {
     nsresult rv = NS_ERROR_UNEXPECTED;
     if (nsCOMPtr<nsIChannel> chan = MaybeChannel()) {
       if (aSuspended) {
         rv = chan->Suspend();
       } else {
--- a/toolkit/components/extensions/webrequest/ChannelWrapper.h
+++ b/toolkit/components/extensions/webrequest/ChannelWrapper.h
@@ -128,16 +128,17 @@ public:
   already_AddRefed<nsIChannel> GetChannel() const { return MaybeChannel(); }
 
   void SetChannel(nsIChannel* aChannel);
 
 
   void Cancel(uint32_t result, ErrorResult& aRv);
 
   void RedirectTo(nsIURI* uri, ErrorResult& aRv);
+  void UpgradeToSecure(ErrorResult& aRv);
 
 
   bool Suspended() const { return mSuspended; }
 
   void SetSuspended(bool aSuspended, ErrorResult& aRv);
 
 
   void GetContentType(nsCString& aContentType) const;
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -816,16 +816,24 @@ HttpObserverManager = {
             channel.suspended = false;
             channel.redirectTo(Services.io.newURI(result.redirectUrl));
             return;
           } catch (e) {
             Cu.reportError(e);
           }
         }
 
+        if (result.upgradeToSecure && kind === "opening") {
+          try {
+            channel.upgradeToSecure();
+          } catch (e) {
+            Cu.reportError(e);
+          }
+        }
+
         if (opts.requestHeaders && result.requestHeaders && requestHeaders) {
           requestHeaders.applyChanges(result.requestHeaders);
         }
 
         if (opts.responseHeaders && result.responseHeaders && responseHeaders) {
           responseHeaders.applyChanges(result.responseHeaders);
         }
       }