importScripts('/resources/testharness.js'); setup({ explicit_done: true }); function assert_range_request(request, expectedRangeHeader, name) { assert_equals(request.headers.get('Range'), expectedRangeHeader, name); } async function broadcast(msg) { for (const client of await clients.matchAll()) { client.postMessage(msg); } } addEventListener('fetch', event => { /** @type Request */ const request = event.request; const url = new URL(request.url); const action = url.searchParams.get('action'); switch (action) { case 'range-header-filter-test': rangeHeaderFilterTest(request); return; case 'range-header-passthrough-test': rangeHeaderPassthroughTest(event); return; case 'store-ranged-response': storeRangedResponse(event); return; case 'use-stored-ranged-response': useStoredRangeResponse(event); return; case 'broadcast-accept-encoding': broadcastAcceptEncoding(event); return; } }); /** * @param {Request} request */ function rangeHeaderFilterTest(request) { const rangeValue = request.headers.get('Range'); test(() => { assert_range_request(new Request(request), rangeValue, `Untampered`); assert_range_request(new Request(request, {}), rangeValue, `Untampered (no init props set)`); assert_range_request(new Request(request, { __foo: 'bar' }), rangeValue, `Untampered (only invalid props set)`); assert_range_request(new Request(request, { mode: 'cors' }), rangeValue, `More permissive mode`); assert_range_request(request.clone(), rangeValue, `Clone`); }, "Range headers correctly preserved"); test(() => { assert_range_request(new Request(request, { headers: { Range: 'foo' } }), null, `Tampered - range header set`); assert_range_request(new Request(request, { headers: {} }), null, `Tampered - empty headers set`); assert_range_request(new Request(request, { mode: 'no-cors' }), null, `Tampered – mode set`); assert_range_request(new Request(request, { cache: 'no-cache' }), null, `Tampered – cache mode set`); }, "Range headers correctly removed"); test(() => { let headers; headers = new Request(request).headers; headers.delete('does-not-exist'); assert_equals(headers.get('Range'), rangeValue, `Preserved if no header actually removed`); headers = new Request(request).headers; headers.append('foo', 'bar'); assert_equals(headers.get('Range'), rangeValue, `Preserved if silent-failure on append (due to request-no-cors guard)`); headers = new Request(request).headers; headers.set('foo', 'bar'); assert_equals(headers.get('Range'), rangeValue, `Preserved if silent-failure on set (due to request-no-cors guard)`); headers = new Request(request).headers; headers.append('Range', 'foo'); assert_equals(headers.get('Range'), rangeValue, `Preserved if silent-failure on append (due to request-no-cors guard)`); headers = new Request(request).headers; headers.set('Range', 'foo'); assert_equals(headers.get('Range'), rangeValue, `Preserved if silent-failure on set (due to request-no-cors guard)`); headers = new Request(request).headers; headers.append('Accept', 'whatever'); assert_equals(headers.get('Range'), null, `Stripped if header successfully appended`); headers = new Request(request).headers; headers.set('Accept', 'whatever'); assert_equals(headers.get('Range'), null, `Stripped if header successfully set`); headers = new Request(request).headers; headers.delete('Accept'); assert_equals(headers.get('Range'), null, `Stripped if header successfully deleted`); headers = new Request(request).headers; headers.delete('Range'); assert_equals(headers.get('Range'), null, `Stripped if range header successfully deleted`); }, "Headers correctly filtered"); done(); } function rangeHeaderPassthroughTest(event) { /** @type Request */ const request = event.request; const url = new URL(request.url); const key = url.searchParams.get('range-received-key'); event.waitUntil(new Promise(resolve => { promise_test(async () => { await fetch(event.request); const response = await fetch('stash-take.py?key=' + key); assert_equals(await response.json(), 'range-header-received'); resolve(); }, `Include range header in network request`); done(); })); // Just send back any response, it isn't important for the test. event.respondWith(new Response('')); } let storedRangeResponseP; function storeRangedResponse(event) { /** @type Request */ const request = event.request; const id = new URL(request.url).searchParams.get('id'); storedRangeResponseP = fetch(event.request); broadcast({ id }); // Just send back any response, it isn't important for the test. event.respondWith(new Response('')); } function useStoredRangeResponse(event) { event.respondWith(async function() { const response = await storedRangeResponseP; if (!response) throw Error("Expected stored range response"); return response.clone(); }()); } function broadcastAcceptEncoding(event) { /** @type Request */ const request = event.request; const id = new URL(request.url).searchParams.get('id'); broadcast({ id, acceptEncoding: request.headers.get('Accept-Encoding') }); // Just send back any response, it isn't important for the test. event.respondWith(new Response('')); }