"use strict"; const { HttpServer } = ChromeUtils.importESModule( "resource://testing-common/httpd.sys.mjs" ); function makeChannel(url) { return NetUtil.newChannel({ uri: url, loadUsingSystemPrincipal: true, }).QueryInterface(Ci.nsIHttpChannel); } ChromeUtils.defineLazyGetter(this, "URL", function () { return "http://localhost:" + httpServer.identity.primaryPort; }); let httpServer = null; function pathHandler(metadata, response) { var body; if (metadata.hasHeader("Idempotency-Key")) { response.setStatusLine(metadata.httpVersion, 200, "OK"); // echo back the header for further validation let IDK = metadata.getHeader("Idempotency-Key"); response.setHeader("Idempotency-Key", IDK, false); body = "success"; } else { response.setStatusLine( metadata.httpVersion, 500, "missing Idempotency-Key" ); body = "failed"; } response.bodyOutputStream.write(body, body.length); } // Helper-method to validate that the "Idempotency-Key" value that we // generate matches the expected format and is the same in the response. // Meant to be called inside of a test that's been added via add_task. function validateAutomaticallyAddedHeaderVal(chan) { let valueFromRequest = chan.getRequestHeader("Idempotency-Key"); let valueFromResponse = chan.getResponseHeader("Idempotency-Key"); // Validate that the header's got a nonempty value, and that the // response matches the request: Assert.notEqual(valueFromRequest, ""); Assert.notEqual(valueFromResponse, ""); Assert.equal(valueFromRequest, valueFromResponse); // Further validation on the value (just focusing on valueFromRequest, // since at this point we know that valueFromResponse is equal to it): // * We expect the value to be formatted as a nonempy double-quoted string, // (i.e. length must be at least 3), as part of the "sf-string" syntax: Assert.greaterOrEqual(valueFromRequest.length, 3); Assert.ok(valueFromRequest.startsWith('"')); Assert.ok(valueFromRequest.endsWith('"')); // * Inside the double-quotes, we expect the value to be formatted as an // unsigned integer. The spec doesn't require this, but that's the // representation we use for now, so let's be sure we don't inadvertently // stray from that format (except as a deliberate choice, which would // involve an update to this test's expectations). let quotedPortion = valueFromRequest.slice(1, -1); let parsedInteger = Number.parseInt(quotedPortion); Assert.ok(!Number.isNaN(parsedInteger)); Assert.greaterOrEqual(parsedInteger, 0); } add_setup(async () => { httpServer = new HttpServer(); httpServer.registerPathHandler("/test_bug1830022", pathHandler); httpServer.start(-1); registerCleanupFunction(async () => await httpServer.stop()); }); // tests if we add the header for the POST request add_task(async function idempotency_key_addition_for_post() { let chan = makeChannel(URL + "/test_bug1830022"); chan.requestMethod = "POST"; await new Promise(resolve => { chan.asyncOpen(new ChannelListener(resolve)); }); validateAutomaticallyAddedHeaderVal(chan); }); // tests if we add the header for the PATCH request add_task(async function idempotency_key_addition_for_patch() { let chan = makeChannel(URL + "/test_bug1830022"); chan.requestMethod = "PATCH"; await new Promise(resolve => { chan.asyncOpen(new ChannelListener(resolve)); }); validateAutomaticallyAddedHeaderVal(chan); }); // tests Idempotency key's uniqueness add_task(async function idempotency_key_uniqueness() { let chan = makeChannel(URL + "/test_bug1830022"); chan.requestMethod = "POST"; await new Promise(resolve => { chan.asyncOpen(new ChannelListener(resolve)); }); let chan2 = makeChannel(URL + "/test_bug1830022"); chan2.requestMethod = "POST"; await new Promise(resolve => { chan2.asyncOpen(new ChannelListener(resolve)); }); Assert.notEqual( chan.getRequestHeader("Idempotency-Key"), chan2.getRequestHeader("Idempotency-Key") ); // tests if the Idempotency key is same for reposts let chan3 = makeChannel(URL + "/test_bug1830022"); chan3.requestMethod = "POST"; await new Promise(resolve => { chan3.asyncOpen(new ChannelListener(resolve)); }); let cachekey = chan3.QueryInterface(Ci.nsICacheInfoChannel).cacheKey; let chan4 = makeChannel(URL + "/test_bug1830022"); chan4.requestMethod = "POST"; chan4.QueryInterface(Ci.nsICacheInfoChannel).cacheKey = cachekey; await new Promise(resolve => { chan4.asyncOpen(new ChannelListener(resolve)); }); Assert.equal( chan3.getRequestHeader("Idempotency-Key"), chan4.getRequestHeader("Idempotency-Key") ); }); // tests if we do not overwrite the header that is set before opening the channel add_task(async function idempotency_key_addition_for_post() { // construct the channel let chan = makeChannel(URL + "/test_bug1830022"); chan.setRequestHeader("Idempotency-Key", "U-V-W-X-Y-Z", false); chan.requestMethod = "POST"; await new Promise(resolve => { chan.asyncOpen(new ChannelListener(resolve)); }); Assert.equal(chan.getRequestHeader("Idempotency-Key"), "U-V-W-X-Y-Z"); Assert.equal(chan.getResponseHeader("Idempotency-Key"), "U-V-W-X-Y-Z"); });