Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test has a WPT meta file that expects 5 subtest issues.
- This WPT test may be referenced by the following Test IDs:
- /service-workers/service-worker/fetch-event.https.h2.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<meta name=timeout content=long>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/common/utils.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
const worker = 'resources/fetch-event-test-worker.js';
const messageChannel = new MessageChannel();
const serviceWorkerPort = messageChannel.port1;
const method = 'POST';
const duplex = 'half';
function createBody(t) {
const rs = new ReadableStream({start(c) {
c.enqueue('i a');
c.enqueue('m the request');
step_timeout(t.step_func(() => {
c.enqueue(' body');
c.close();
}, 10));
}});
return rs.pipeThrough(new TextEncoderStream());
}
promise_test(async t => {
const scope = 'resources/';
const registration =
await service_worker_unregister_and_register(t, worker, scope);
const installing = registration.installing;
await wait_for_state(t, installing, 'activated');
installing.postMessage({ port: messageChannel.port2 }, [messageChannel.port2]);
const postResult = await new Promise(resolve => serviceWorkerPort.onmessage = e => resolve(e.data));
assert_equals(postResult, "ok");
// This will happen after all other tests
promise_test(t => {
return registration.unregister();
}, 'restore global state');
}, 'global setup');
// Test that the service worker can read FetchEvent#body when it is made from
// a ReadableStream. It responds with request body it read.
promise_test(async t => {
const body = createBody(t);
// Set page_url to "?ignore" so the service worker falls back to network
// for the main resource request, and add a suffix to avoid colliding
// with other tests.
const page_url = `resources/simple.html?ignore&id=${token()}`;
const frame = await with_iframe(page_url);
t.add_cleanup(() => { frame.remove(); });
const response = await frame.contentWindow.fetch('simple.html?request-body', {
method, body, duplex});
assert_equals(response.status, 200, 'status');
const text = await response.text();
assert_equals(text, 'i am the request body', 'body');
}, 'The streaming request body is readable in the service worker.');
// Network fallback
promise_test(async t => {
const body = createBody(t);
// Set page_url to "?ignore" so the service worker falls back to network
// for the main resource request, and add a suffix to avoid colliding
// with other tests.
const page_url = `resources/simple.html?ignore&id=${token()}`;
const frame = await with_iframe(page_url);
t.add_cleanup(() => { frame.remove(); });
// Add "?ignore" so that the service worker falls back to
// echo-content.h2.py.
const echo_url = '/fetch/api/resources/echo-content.h2.py?ignore';
const response =
await frame.contentWindow.fetch(echo_url, { method, body, duplex});
assert_equals(response.status, 200, 'status');
const text = await response.text();
assert_equals(text, 'i am the request body', 'body');
}, 'Network fallback for streaming upload.');
// When the streaming body is used in the service worker, network fallback
// fails.
promise_test(async t => {
const body = createBody(t);
// Set page_url to "?ignore" so the service worker falls back to network
// for the main resource request, and add a suffix to avoid colliding
// with other tests.
const page_url = `resources/simple.html?ignore&id=${token()}`;
const frame = await with_iframe(page_url);
t.add_cleanup(() => { frame.remove(); });
const echo_url = '/fetch/api/resources/echo-content.h2.py?use-and-ignore';
const w = frame.contentWindow;
await promise_rejects_js(t, w.TypeError, w.fetch(echo_url, {
method, body, duplex}));
}, 'When the streaming request body is used, network fallback fails.');
// When the streaming body is used by clone() in the service worker, network
// fallback succeeds, and the service worker can read the full body from the
// clone even though it does not handle the fetch event itself.
promise_test(async t => {
const body = createBody(t);
// Set page_url to "?ignore" so the service worker falls back to network
// for the main resource request, and add a suffix to avoid colliding
// with other tests.
const page_url = `resources/simple.html?ignore&id=${token()}`;
const frame = await with_iframe(page_url);
t.add_cleanup(() => { frame.remove(); });
// Add "?clone-and-ignore" so that the service worker falls back to
// echo-content.h2.py.
const echo_url = '/fetch/api/resources/echo-content.h2.py?clone-and-ignore';
const clonedBodyFromWorker = new Promise(resolve => {
serviceWorkerPort.onmessage = e => resolve(e.data);
});
const response = await frame.contentWindow.fetch(echo_url, {
method, body, duplex});
assert_equals(response.status, 200, 'status');
const text = await response.text();
assert_equals(text, 'i am the request body', 'body');
assert_equals(await clonedBodyFromWorker, 'i am the request body',
'body read by the service worker from the cloned request');
}, 'Running clone() in the service worker does not prevent network fallback, and the service worker can read the request body via the clone.');
promise_test(async t => {
const body = createBody(t);
const page_url = `resources/simple.html?ignore&id=${token()}`;
const frame = await with_iframe(page_url);
t.add_cleanup(() => { frame.remove(); });
const echo_url =
'/fetch/api/resources/echo-content.h2.py?clone-after-ignore';
const cloneResultFromWorker = new Promise(resolve => {
serviceWorkerPort.onmessage = e => resolve(e.data);
});
const response = await frame.contentWindow.fetch(echo_url, {
method, body, duplex});
assert_equals(response.status, 200, 'status');
const text = await response.text();
assert_equals(text, 'i am the request body', 'body');
assert_equals(await cloneResultFromWorker, 'TypeError',
'cloning the request after leaving the fetch event unhandled should ' +
'throw a TypeError, since its body was canceled');
}, 'Cloning the request after leaving the fetch event unhandled throws because the request body was canceled.');
</script>
</body>