Bug 1323036 - Preserve case of header names draft
authorTomislav Jovanovic <tomica@gmail.com>
Thu, 06 Apr 2017 01:08:11 +0200
changeset 557280 5c4514b31fd911e7d00fa3a9ffcd62e4a9e563ce
parent 551503 6f31760f0ffae62ca715a2b74f017ac059160bda
child 623007 471c8dfae23a0338e46f02aaabed7746ee756a26
push id52670
push userbmo:tomica@gmail.com
push dateThu, 06 Apr 2017 15:44:28 +0000
bugs1323036
milestone55.0a1
Bug 1323036 - Preserve case of header names MozReview-Commit-ID: 2jDHOr2GI6T
toolkit/components/extensions/test/mochitest/test_ext_webrequest_basic.html
toolkit/modules/addons/WebRequest.jsm
--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_basic.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_basic.html
@@ -317,15 +317,63 @@ add_task(function* test_webRequest_frame
   addFrame("redirection.sjs");
   addFrame("https://invalid.localhost/badrobot");
   yield extension.awaitMessage("done");
 });
 
 add_task(function* teardown() {
   yield extension.unload();
 });
+
+add_task(async function test_case_preserving() {
+  const manifest = {
+    permissions: [
+      "webRequest",
+      "webRequestBlocking",
+      "http://mochi.test/",
+    ],
+  };
+
+  async function background() {
+    // This is testing if header names preserve case,
+    // so the case-sensitive comparison is on purpose.
+    function ua({url, requestHeaders}) {
+      if (url.endsWith("?blind-add")) {
+        requestHeaders.push({name: "user-agent", value: "Blind/Add"});
+        return {requestHeaders};
+      }
+      for (const header of requestHeaders) {
+        if (header.name === "User-Agent") {
+          header.value = "Case/Sensitive";
+        }
+      }
+      return {requestHeaders};
+    }
+
+    await browser.webRequest.onBeforeSendHeaders.addListener(ua, {urls: ["<all_urls>"]}, ["blocking", "requestHeaders"]);
+    browser.test.sendMessage("ready");
+  }
+
+  const extension = ExtensionTestUtils.loadExtension({manifest, background});
+
+  await extension.startup();
+  await extension.awaitMessage("ready");
+
+  const response1 = await fetch(SimpleTest.getTestFileURL("return_headers.sjs"));
+  const headers1 = JSON.parse(await response1.text());
+
+  is(headers1["user-agent"], "Case/Sensitive", "User-Agent header matched and changed.");
+
+  const response2 = await fetch(SimpleTest.getTestFileURL("return_headers.sjs?blind-add"));
+  const headers2 = JSON.parse(await response2.text());
+
+  is(headers2["user-agent"], "Blind/Add", "User-Agent header blindly added.");
+
+  await extension.unload();
+});
+
 </script>
 </head>
 <body>
 <div id="test">Sample text</div>
 
 </body>
 </html>
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -119,23 +119,23 @@ function isThenable(value) {
 }
 
 class HeaderChanger {
   constructor(channel) {
     this.channel = channel;
 
     this.originalHeaders = new Map();
     this.visitHeaders((name, value) => {
-      this.originalHeaders.set(name.toLowerCase(), value);
+      this.originalHeaders.set(name.toLowerCase(), {name, value});
     });
   }
 
   toArray() {
     return Array.from(this.originalHeaders,
-                      ([name, value]) => ({name, value}));
+                      ([key, {name, value}]) => ({name, value}));
   }
 
   validateHeaders(headers) {
     // We should probably use schema validation for this.
 
     if (!Array.isArray(headers)) {
       return false;
     }
@@ -171,17 +171,18 @@ class HeaderChanger {
       }
     }
 
     // Set new or changed headers.
     for (let {name, value, binaryValue} of headers) {
       if (binaryValue) {
         value = String.fromCharCode(...binaryValue);
       }
-      if (value !== this.originalHeaders.get(name.toLowerCase())) {
+      let original = this.originalHeaders.get(name.toLowerCase());
+      if (!original || value !== original.value) {
         this.setHeader(name, value);
       }
     }
   }
 }
 
 class RequestHeaderChanger extends HeaderChanger {
   setHeader(name, value) {