/* global context testDone:true */ var requestURL = "//mochi.test:8888/tests/dom/cache/test/mochitest/vary.sjs?" + context; var name = "match-vary" + context; function checkResponse(r, response, responseText) { ok(r !== response, "The objects should not be the same"); is( r.url, response.url.replace("#fragment", ""), "The URLs should be the same" ); is(r.status, response.status, "The status codes should be the same"); is(r.type, response.type, "The response types should be the same"); is(r.ok, response.ok, "Both responses should have succeeded"); is( r.statusText, response.statusText, "Both responses should have the same status text" ); is( r.headers.get("Vary"), response.headers.get("Vary"), "Both responses should have the same Vary header" ); return r.text().then(function (text) { is(text, responseText, "The response body should be correct"); }); } // Returns a Promise that will be resolved to an object with the following // properties: // * cache: A Cache object that contains one entry fetched with headers. // * response: A Response object which is the result of fetching a request // with the specified headers. // * responseText: The body of the above response object. function setupTest(headers) { return setupTestMultipleEntries([headers]).then(function (test) { return { response: test.response[0], responseText: test.responseText[0], cache: test.cache, }; }); } function setupTestMultipleEntries(headers) { ok(Array.isArray(headers), "headers should be an array"); return new Promise(function (resolve, reject) { var response, responseText, cache; Promise.all( headers.map(function (h) { return fetch(requestURL, { headers: h }); }) ) .then(function (res) { response = res; return Promise.all( response.map(function (r) { return r.text(); }) ); }) .then(function (text) { responseText = text; return caches.open(name); }) .then(function (c) { cache = c; return Promise.all( headers.map(function (h) { return c.add(new Request(requestURL, { headers: h })); }) ); }) .then(function () { resolve({ response, responseText, cache }); }) .catch(function (err) { reject(err); }); }); } function testBasics() { var test; return setupTest({ WhatToVary: "Custom" }) .then(function (t) { test = t; // Ensure that searching without specifying a Custom header succeeds. return test.cache.match(requestURL); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }) .then(function () { // Ensure that searching with a non-matching value for the Custom header fails. return test.cache.match( new Request(requestURL, { headers: { Custom: "foo=bar" } }) ); }) .then(function (r) { is( typeof r, "undefined", "Searching for a request with an unknown Vary header should not succeed" ); // Ensure that searching with a non-matching value for the Custom header but with ignoreVary set succeeds. return test.cache.match( new Request(requestURL, { headers: { Custom: "foo=bar" } }), { ignoreVary: true } ); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }); } function testBasicKeys() { function checkRequest(reqs) { is(reqs.length, 1, "One request expected"); ok(reqs[0].url.includes(requestURL), "The correct request expected"); ok( reqs[0].headers.get("WhatToVary"), "Custom", "The correct request headers expected" ); } var test; return setupTest({ WhatToVary: "Custom" }) .then(function (t) { test = t; // Ensure that searching without specifying a Custom header succeeds. return test.cache.keys(requestURL); }) .then(function (r) { return checkRequest(r); }) .then(function () { // Ensure that searching with a non-matching value for the Custom header fails. return test.cache.keys( new Request(requestURL, { headers: { Custom: "foo=bar" } }) ); }) .then(function (r) { is( r.length, 0, "Searching for a request with an unknown Vary header should not succeed" ); // Ensure that searching with a non-matching value for the Custom header but with ignoreVary set succeeds. return test.cache.keys( new Request(requestURL, { headers: { Custom: "foo=bar" } }), { ignoreVary: true } ); }) .then(function (r) { return checkRequest(r); }); } function testStar() { function ensurePromiseRejected(promise) { return promise.then( function () { ok(false, "Promise should be rejected"); }, function (err) { is( err.name, "TypeError", "Attempting to store a Response with a Vary:* header must fail" ); } ); } return new Promise(function (resolve, reject) { var cache; caches.open(name).then(function (c) { cache = c; Promise.all([ ensurePromiseRejected( cache.add( new Request(requestURL + "1", { headers: { WhatToVary: "*" } }) ) ), ensurePromiseRejected( cache.addAll([ new Request(requestURL + "2", { headers: { WhatToVary: "*" } }), requestURL + "3", ]) ), ensurePromiseRejected( fetch( new Request(requestURL + "4", { headers: { WhatToVary: "*" } }) ).then(function (response) { return cache.put(requestURL + "4", response); }) ), ensurePromiseRejected( cache.add( new Request(requestURL + "5", { headers: { WhatToVary: "*,User-Agent" }, }) ) ), ensurePromiseRejected( cache.addAll([ new Request(requestURL + "6", { headers: { WhatToVary: "*,User-Agent" }, }), requestURL + "7", ]) ), ensurePromiseRejected( fetch( new Request(requestURL + "8", { headers: { WhatToVary: "*,User-Agent" }, }) ).then(function (response) { return cache.put(requestURL + "8", response); }) ), ensurePromiseRejected( cache.add( new Request(requestURL + "9", { headers: { WhatToVary: "User-Agent,*" }, }) ) ), ensurePromiseRejected( cache.addAll([ new Request(requestURL + "10", { headers: { WhatToVary: "User-Agent,*" }, }), requestURL + "10", ]) ), ensurePromiseRejected( fetch( new Request(requestURL + "11", { headers: { WhatToVary: "User-Agent,*" }, }) ).then(function (response) { return cache.put(requestURL + "11", response); }) ), ]).then(reject, resolve); }); }); } function testMatch() { var test; return setupTest({ WhatToVary: "Custom", Custom: "foo=bar" }) .then(function (t) { test = t; // Ensure that searching with a different Custom header fails. return test.cache.match( new Request(requestURL, { headers: { Custom: "bar=baz" } }) ); }) .then(function (r) { is( typeof r, "undefined", "Searching for a request with a non-matching Custom header should not succeed" ); // Ensure that searching with the same Custom header succeeds. return test.cache.match( new Request(requestURL, { headers: { Custom: "foo=bar" } }) ); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }); } function testInvalidHeaderName() { var test; return setupTest({ WhatToVary: "Foo/Bar, Custom-User-Agent" }) .then(function (t) { test = t; // Ensure that searching with a different User-Agent header fails. return test.cache.match( new Request(requestURL, { headers: { "Custom-User-Agent": "MyUA" } }) ); }) .then(function (r) { is( typeof r, "undefined", "Searching for a request with a non-matching Custom-User-Agent header should not succeed" ); // Ensure that searching with a different Custom-User-Agent header but with ignoreVary succeeds. return test.cache.match( new Request(requestURL, { headers: { "Custom-User-Agent": "MyUA" } }), { ignoreVary: true } ); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }) .then(function () { // Ensure that we do not mistakenly recognize the tokens in the invalid header name. return test.cache.match( new Request(requestURL, { headers: { Foo: "foobar" } }) ); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }); } function testMultipleHeaders() { var test; return setupTest({ WhatToVary: "Custom-Referer,\tCustom-Accept-Encoding" }) .then(function (t) { test = t; // Ensure that searching with a different Referer header fails. return test.cache.match( new Request(requestURL, { headers: { "Custom-Referer": "https://somesite.com/" }, }) ); }) .then(function (r) { is( typeof r, "undefined", "Searching for a request with a non-matching Custom-Referer header should not succeed" ); // Ensure that searching with a different Custom-Referer header but with ignoreVary succeeds. return test.cache.match( new Request(requestURL, { headers: { "Custom-Referer": "https://somesite.com/" }, }), { ignoreVary: true } ); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }) .then(function () { // Ensure that searching with a different Custom-Accept-Encoding header fails. return test.cache.match( new Request(requestURL, { headers: { "Custom-Accept-Encoding": "myencoding" }, }) ); }) .then(function (r) { is( typeof r, "undefined", "Searching for a request with a non-matching Custom-Accept-Encoding header should not succeed" ); // Ensure that searching with a different Custom-Accept-Encoding header but with ignoreVary succeeds. return test.cache.match( new Request(requestURL, { headers: { "Custom-Accept-Encoding": "myencoding" }, }), { ignoreVary: true } ); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }) .then(function () { // Ensure that searching with an empty Custom-Referer header succeeds. return test.cache.match( new Request(requestURL, { headers: { "Custom-Referer": "" } }) ); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }) .then(function () { // Ensure that searching with an empty Custom-Accept-Encoding header succeeds. return test.cache.match( new Request(requestURL, { headers: { "Custom-Accept-Encoding": "" } }) ); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }) .then(function () { // Ensure that searching with an empty Custom-Referer header but with a different Custom-Accept-Encoding header fails. return test.cache.match( new Request(requestURL, { headers: { "Custom-Referer": "", "Custom-Accept-Encoding": "myencoding", }, }) ); }) .then(function (r) { is( typeof r, "undefined", "Searching for a request with a non-matching Custom-Accept-Encoding header should not succeed" ); // Ensure that searching with an empty Custom-Referer header but with a different Custom-Accept-Encoding header and ignoreVary succeeds. return test.cache.match( new Request(requestURL, { headers: { "Custom-Referer": "", "Custom-Accept-Encoding": "myencoding", }, }), { ignoreVary: true } ); }) .then(function (r) { return checkResponse(r, test.response, test.responseText); }); } function testMultipleCacheEntries() { var test; return setupTestMultipleEntries([ { WhatToVary: "Accept-Language", "Accept-Language": "en-US" }, { WhatToVary: "Accept-Language", "Accept-Language": "en-US, fa-IR" }, ]) .then(function (t) { test = t; return test.cache.matchAll(); }) .then(function (r) { is(r.length, 2, "Two cache entries should be stored in the DB"); // Ensure that searching without specifying an Accept-Language header fails. return test.cache.matchAll(requestURL); }) .then(function (r) { is( r.length, 0, "Searching for a request without specifying an Accept-Language header should not succeed" ); // Ensure that searching without specifying an Accept-Language header but with ignoreVary succeeds. return test.cache.matchAll(requestURL, { ignoreVary: true }); }) .then(function (r) { return Promise.all([ checkResponse(r[0], test.response[0], test.responseText[0]), checkResponse(r[1], test.response[1], test.responseText[1]), ]); }) .then(function () { // Ensure that searching with Accept-Language: en-US succeeds. return test.cache.matchAll( new Request(requestURL, { headers: { "Accept-Language": "en-US" } }) ); }) .then(function (r) { is(r.length, 1, "One cache entry should be found"); return checkResponse(r[0], test.response[0], test.responseText[0]); }) .then(function () { // Ensure that searching with Accept-Language: en-US,fa-IR succeeds. return test.cache.matchAll( new Request(requestURL, { headers: { "Accept-Language": "en-US, fa-IR" }, }) ); }) .then(function (r) { is(r.length, 1, "One cache entry should be found"); return checkResponse(r[0], test.response[1], test.responseText[1]); }) .then(function () { // Ensure that searching with a valid Accept-Language header but with ignoreVary returns both entries. return test.cache.matchAll( new Request(requestURL, { headers: { "Accept-Language": "en-US" } }), { ignoreVary: true } ); }) .then(function (r) { return Promise.all([ checkResponse(r[0], test.response[0], test.responseText[0]), checkResponse(r[1], test.response[1], test.responseText[1]), ]); }) .then(function () { // Ensure that searching with Accept-Language: fa-IR fails. return test.cache.matchAll( new Request(requestURL, { headers: { "Accept-Language": "fa-IR" } }) ); }) .then(function (r) { is( r.length, 0, "Searching for a request with a different Accept-Language header should not succeed" ); // Ensure that searching with Accept-Language: fa-IR but with ignoreVary should succeed. return test.cache.matchAll( new Request(requestURL, { headers: { "Accept-Language": "fa-IR" } }), { ignoreVary: true } ); }) .then(function (r) { is(r.length, 2, "Two cache entries should be found"); return Promise.all([ checkResponse(r[0], test.response[0], test.responseText[0]), checkResponse(r[1], test.response[1], test.responseText[1]), ]); }); } // Make sure to clean up after each test step. function step(testPromise) { return testPromise.then( function () { caches.delete(name); }, function () { caches.delete(name); } ); } step(testBasics()) .then(function () { return step(testBasicKeys()); }) .then(function () { return step(testStar()); }) .then(function () { return step(testMatch()); }) .then(function () { return step(testInvalidHeaderName()); }) .then(function () { return step(testMultipleHeaders()); }) .then(function () { return step(testMultipleCacheEntries()); }) .then(function () { testDone(); });