mirror of
https://github.com/servo/servo.git
synced 2025-08-08 23:15:33 +01:00
Update web-platform-tests to revision 8a2ceb5f18911302b7a5c1cd2791f4ab50ad4326
This commit is contained in:
parent
462c272380
commit
1f531f66ea
5377 changed files with 174916 additions and 84369 deletions
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Request signals & the cache API</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
promise_test(async () => {
|
||||
await caches.delete('test');
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const request = new Request('../resources/data.json', { signal });
|
||||
|
||||
const cache = await caches.open('test');
|
||||
await cache.put(request, new Response(''));
|
||||
|
||||
const requests = await cache.keys();
|
||||
|
||||
assert_equals(requests.length, 1, 'Ensuring cleanup worked');
|
||||
|
||||
const [cachedRequest] = requests;
|
||||
|
||||
controller.abort();
|
||||
|
||||
assert_false(cachedRequest.signal.aborted, "Request from cache shouldn't be aborted");
|
||||
|
||||
const data = await fetch(cachedRequest).then(r => r.json());
|
||||
assert_equals(data.key, 'value', 'Fetch fully completes');
|
||||
}, "Signals are not stored in the cache API");
|
||||
|
||||
promise_test(async () => {
|
||||
await caches.delete('test');
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const request = new Request('../resources/data.json', { signal });
|
||||
controller.abort();
|
||||
|
||||
const cache = await caches.open('test');
|
||||
await cache.put(request, new Response(''));
|
||||
|
||||
const requests = await cache.keys();
|
||||
|
||||
assert_equals(requests.length, 1, 'Ensuring cleanup worked');
|
||||
|
||||
const [cachedRequest] = requests;
|
||||
|
||||
assert_false(cachedRequest.signal.aborted, "Request from cache shouldn't be aborted");
|
||||
|
||||
const data = await fetch(cachedRequest).then(r => r.json());
|
||||
assert_equals(data.key, 'value', 'Fetch fully completes');
|
||||
}, "Signals are not stored in the cache API, even if they're already aborted");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>General fetch abort tests in a service worker</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
(async function() {
|
||||
const scope = 'does/not/exist';
|
||||
|
||||
let reg = await navigator.serviceWorker.getRegistration(scope);
|
||||
if (reg) await reg.unregister();
|
||||
|
||||
reg = await navigator.serviceWorker.register('general.any.worker.js', {scope});
|
||||
|
||||
fetch_tests_from_worker(reg.installing);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>General fetch abort tests - shared worker</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
fetch_tests_from_worker(new SharedWorker("general.any.worker.js"));
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
524
tests/wpt/web-platform-tests/fetch/api/abort/general.any.js
Normal file
524
tests/wpt/web-platform-tests/fetch/api/abort/general.any.js
Normal file
|
@ -0,0 +1,524 @@
|
|||
// META: script=/common/utils.js
|
||||
// META: script=../request/request-error.js
|
||||
|
||||
const BODY_METHODS = ['arrayBuffer', 'blob', 'formData', 'json', 'text'];
|
||||
|
||||
// This is used to close connections that weren't correctly closed during the tests,
|
||||
// otherwise you can end up running out of HTTP connections.
|
||||
let requestAbortKeys = [];
|
||||
|
||||
function abortRequests() {
|
||||
const keys = requestAbortKeys;
|
||||
requestAbortKeys = [];
|
||||
return Promise.all(
|
||||
keys.map(key => fetch(`../resources/stash-put.py?key=${key}&value=close`))
|
||||
);
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const fetchPromise = fetch('../resources/data.json', { signal });
|
||||
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
}, "Aborting rejects with AbortError");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const url = new URL('../resources/data.json', location);
|
||||
url.hostname = 'www1.' + url.hostname;
|
||||
|
||||
const fetchPromise = fetch(url, {
|
||||
signal,
|
||||
mode: 'no-cors'
|
||||
});
|
||||
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
}, "Aborting rejects with AbortError - no-cors");
|
||||
|
||||
// Test that errors thrown from the request constructor take priority over abort errors.
|
||||
// badRequestArgTests is from response-error.js
|
||||
for (const { args, testName } of badRequestArgTests) {
|
||||
promise_test(async t => {
|
||||
try {
|
||||
// If this doesn't throw, we'll effectively skip the test.
|
||||
// It'll fail properly in ../request/request-error.html
|
||||
new Request(...args);
|
||||
}
|
||||
catch (err) {
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
|
||||
// Add signal to 2nd arg
|
||||
args[1] = args[1] || {};
|
||||
args[1].signal = controller.signal;
|
||||
await promise_rejects(t, err, fetch(...args));
|
||||
}
|
||||
}, `TypeError from request constructor takes priority - ${testName}`);
|
||||
}
|
||||
|
||||
test(() => {
|
||||
const request = new Request('');
|
||||
assert_true(Boolean(request.signal), "Signal member is present & truthy");
|
||||
assert_equals(request.signal.constructor, AbortSignal);
|
||||
}, "Request objects have a signal property");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const request = new Request('../resources/data.json', { signal });
|
||||
|
||||
assert_true(Boolean(request.signal), "Signal member is present & truthy");
|
||||
assert_equals(request.signal.constructor, AbortSignal);
|
||||
assert_not_equals(request.signal, signal, 'Request has a new signal, not a reference');
|
||||
|
||||
const fetchPromise = fetch(request);
|
||||
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
}, "Signal on request object");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const request = new Request('../resources/data.json', { signal });
|
||||
const requestFromRequest = new Request(request);
|
||||
|
||||
const fetchPromise = fetch(requestFromRequest);
|
||||
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
}, "Signal on request object created from request object");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const request = new Request('../resources/data.json');
|
||||
const requestFromRequest = new Request(request, { signal });
|
||||
|
||||
const fetchPromise = fetch(requestFromRequest);
|
||||
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
}, "Signal on request object created from request object, with signal on second request");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const request = new Request('../resources/data.json', { signal: new AbortController().signal });
|
||||
const requestFromRequest = new Request(request, { signal });
|
||||
|
||||
const fetchPromise = fetch(requestFromRequest);
|
||||
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
}, "Signal on request object created from request object, with signal on second request overriding another");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const request = new Request('../resources/data.json', { signal });
|
||||
|
||||
const fetchPromise = fetch(request, {method: 'POST'});
|
||||
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
}, "Signal retained after unrelated properties are overridden by fetch");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const request = new Request('../resources/data.json', { signal });
|
||||
|
||||
const data = await fetch(request, { signal: null }).then(r => r.json());
|
||||
assert_equals(data.key, 'value', 'Fetch fully completes');
|
||||
}, "Signal removed by setting to null");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const log = [];
|
||||
|
||||
await Promise.all([
|
||||
fetch('../resources/data.json', { signal }).then(
|
||||
() => assert_unreached("Fetch must not resolve"),
|
||||
() => log.push('fetch-reject')
|
||||
),
|
||||
Promise.resolve().then(() => log.push('next-microtask'))
|
||||
]);
|
||||
|
||||
assert_array_equals(log, ['fetch-reject', 'next-microtask']);
|
||||
}, "Already aborted signal rejects immediately");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const request = new Request('../resources/data.json', {
|
||||
signal,
|
||||
method: 'POST',
|
||||
body: 'foo',
|
||||
headers: { 'Content-Type': 'text/plain' }
|
||||
});
|
||||
|
||||
await fetch(request).catch(() => {});
|
||||
|
||||
assert_true(request.bodyUsed, "Body has been used");
|
||||
}, "Request is still 'used' if signal is aborted before fetching");
|
||||
|
||||
for (const bodyMethod of BODY_METHODS) {
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
|
||||
const log = [];
|
||||
const response = await fetch('../resources/data.json', { signal });
|
||||
|
||||
controller.abort();
|
||||
|
||||
const bodyPromise = response[bodyMethod]();
|
||||
|
||||
await Promise.all([
|
||||
bodyPromise.catch(() => log.push(`${bodyMethod}-reject`)),
|
||||
Promise.resolve().then(() => log.push('next-microtask'))
|
||||
]);
|
||||
|
||||
await promise_rejects(t, "AbortError", bodyPromise);
|
||||
|
||||
assert_array_equals(log, [`${bodyMethod}-reject`, 'next-microtask']);
|
||||
}, `response.${bodyMethod}() rejects if already aborted`);
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
await abortRequests();
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const stateKey = token();
|
||||
const abortKey = token();
|
||||
requestAbortKeys.push(abortKey);
|
||||
controller.abort();
|
||||
|
||||
await fetch(`../resources/infinite-slow-response.py?stateKey=${stateKey}&abortKey=${abortKey}`, { signal }).catch(() => {});
|
||||
|
||||
// I'm hoping this will give the browser enough time to (incorrectly) make the request
|
||||
// above, if it intends to.
|
||||
await fetch('../resources/data.json').then(r => r.json());
|
||||
|
||||
const response = await fetch(`../resources/stash-take.py?key=${stateKey}`);
|
||||
const data = await response.json();
|
||||
|
||||
assert_equals(data, null, "Request hasn't been made to the server");
|
||||
}, "Already aborted signal does not make request");
|
||||
|
||||
promise_test(async t => {
|
||||
await abortRequests();
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const fetches = [];
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const abortKey = token();
|
||||
requestAbortKeys.push(abortKey);
|
||||
|
||||
fetches.push(
|
||||
fetch(`../resources/infinite-slow-response.py?${i}&abortKey=${abortKey}`, { signal })
|
||||
);
|
||||
}
|
||||
|
||||
for (const fetchPromise of fetches) {
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
}
|
||||
}, "Already aborted signal can be used for many fetches");
|
||||
|
||||
promise_test(async t => {
|
||||
await abortRequests();
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
|
||||
await fetch('../resources/data.json', { signal }).then(r => r.json());
|
||||
|
||||
controller.abort();
|
||||
|
||||
const fetches = [];
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const abortKey = token();
|
||||
requestAbortKeys.push(abortKey);
|
||||
|
||||
fetches.push(
|
||||
fetch(`../resources/infinite-slow-response.py?${i}&abortKey=${abortKey}`, { signal })
|
||||
);
|
||||
}
|
||||
|
||||
for (const fetchPromise of fetches) {
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
}
|
||||
}, "Signal can be used to abort other fetches, even if another fetch succeeded before aborting");
|
||||
|
||||
promise_test(async t => {
|
||||
await abortRequests();
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const stateKey = token();
|
||||
const abortKey = token();
|
||||
requestAbortKeys.push(abortKey);
|
||||
|
||||
await fetch(`../resources/infinite-slow-response.py?stateKey=${stateKey}&abortKey=${abortKey}`, { signal });
|
||||
|
||||
const beforeAbortResult = await fetch(`../resources/stash-take.py?key=${stateKey}`).then(r => r.json());
|
||||
assert_equals(beforeAbortResult, "open", "Connection is open");
|
||||
|
||||
controller.abort();
|
||||
|
||||
// The connection won't close immediately, but it should close at some point:
|
||||
const start = Date.now();
|
||||
|
||||
while (true) {
|
||||
// Stop spinning if 10 seconds have passed
|
||||
if (Date.now() - start > 10000) throw Error('Timed out');
|
||||
|
||||
const afterAbortResult = await fetch(`../resources/stash-take.py?key=${stateKey}`).then(r => r.json());
|
||||
if (afterAbortResult == 'closed') break;
|
||||
}
|
||||
}, "Underlying connection is closed when aborting after receiving response");
|
||||
|
||||
promise_test(async t => {
|
||||
await abortRequests();
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const stateKey = token();
|
||||
const abortKey = token();
|
||||
requestAbortKeys.push(abortKey);
|
||||
|
||||
const url = new URL(`../resources/infinite-slow-response.py?stateKey=${stateKey}&abortKey=${abortKey}`, location);
|
||||
url.hostname = 'www1.' + url.hostname;
|
||||
|
||||
await fetch(url, {
|
||||
signal,
|
||||
mode: 'no-cors'
|
||||
});
|
||||
|
||||
const stashTakeURL = new URL(`../resources/stash-take.py?key=${stateKey}`, location);
|
||||
stashTakeURL.hostname = 'www1.' + stashTakeURL.hostname;
|
||||
|
||||
const beforeAbortResult = await fetch(stashTakeURL).then(r => r.json());
|
||||
assert_equals(beforeAbortResult, "open", "Connection is open");
|
||||
|
||||
controller.abort();
|
||||
|
||||
// The connection won't close immediately, but it should close at some point:
|
||||
const start = Date.now();
|
||||
|
||||
while (true) {
|
||||
// Stop spinning if 10 seconds have passed
|
||||
if (Date.now() - start > 10000) throw Error('Timed out');
|
||||
|
||||
const afterAbortResult = await fetch(stashTakeURL).then(r => r.json());
|
||||
if (afterAbortResult == 'closed') break;
|
||||
}
|
||||
}, "Underlying connection is closed when aborting after receiving response - no-cors");
|
||||
|
||||
for (const bodyMethod of BODY_METHODS) {
|
||||
promise_test(async t => {
|
||||
await abortRequests();
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const stateKey = token();
|
||||
const abortKey = token();
|
||||
requestAbortKeys.push(abortKey);
|
||||
|
||||
const response = await fetch(`../resources/infinite-slow-response.py?stateKey=${stateKey}&abortKey=${abortKey}`, { signal });
|
||||
|
||||
const beforeAbortResult = await fetch(`../resources/stash-take.py?key=${stateKey}`).then(r => r.json());
|
||||
assert_equals(beforeAbortResult, "open", "Connection is open");
|
||||
|
||||
const bodyPromise = response[bodyMethod]();
|
||||
|
||||
controller.abort();
|
||||
|
||||
await promise_rejects(t, "AbortError", bodyPromise);
|
||||
|
||||
const start = Date.now();
|
||||
|
||||
while (true) {
|
||||
// Stop spinning if 10 seconds have passed
|
||||
if (Date.now() - start > 10000) throw Error('Timed out');
|
||||
|
||||
const afterAbortResult = await fetch(`../resources/stash-take.py?key=${stateKey}`).then(r => r.json());
|
||||
if (afterAbortResult == 'closed') break;
|
||||
}
|
||||
}, `Fetch aborted & connection closed when aborted after calling response.${bodyMethod}()`);
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
await abortRequests();
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const stateKey = token();
|
||||
const abortKey = token();
|
||||
requestAbortKeys.push(abortKey);
|
||||
|
||||
const response = await fetch(`../resources/infinite-slow-response.py?stateKey=${stateKey}&abortKey=${abortKey}`, { signal });
|
||||
const reader = response.body.getReader();
|
||||
|
||||
controller.abort();
|
||||
|
||||
await promise_rejects(t, "AbortError", reader.read());
|
||||
await promise_rejects(t, "AbortError", reader.closed);
|
||||
|
||||
// The connection won't close immediately, but it should close at some point:
|
||||
const start = Date.now();
|
||||
|
||||
while (true) {
|
||||
// Stop spinning if 10 seconds have passed
|
||||
if (Date.now() - start > 10000) throw Error('Timed out');
|
||||
|
||||
const afterAbortResult = await fetch(`../resources/stash-take.py?key=${stateKey}`).then(r => r.json());
|
||||
if (afterAbortResult == 'closed') break;
|
||||
}
|
||||
}, "Stream errors once aborted. Underlying connection closed.");
|
||||
|
||||
promise_test(async t => {
|
||||
await abortRequests();
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
const stateKey = token();
|
||||
const abortKey = token();
|
||||
requestAbortKeys.push(abortKey);
|
||||
|
||||
const response = await fetch(`../resources/infinite-slow-response.py?stateKey=${stateKey}&abortKey=${abortKey}`, { signal });
|
||||
const reader = response.body.getReader();
|
||||
|
||||
await reader.read();
|
||||
|
||||
controller.abort();
|
||||
|
||||
await promise_rejects(t, "AbortError", reader.read());
|
||||
await promise_rejects(t, "AbortError", reader.closed);
|
||||
|
||||
// The connection won't close immediately, but it should close at some point:
|
||||
const start = Date.now();
|
||||
|
||||
while (true) {
|
||||
// Stop spinning if 10 seconds have passed
|
||||
if (Date.now() - start > 10000) throw Error('Timed out');
|
||||
|
||||
const afterAbortResult = await fetch(`../resources/stash-take.py?key=${stateKey}`).then(r => r.json());
|
||||
if (afterAbortResult == 'closed') break;
|
||||
}
|
||||
}, "Stream errors once aborted, after reading. Underlying connection closed.");
|
||||
|
||||
promise_test(async t => {
|
||||
await abortRequests();
|
||||
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
|
||||
const response = await fetch(`../resources/empty.txt`, { signal });
|
||||
|
||||
// Read whole response to ensure close signal has sent.
|
||||
await response.clone().text();
|
||||
|
||||
const reader = response.body.getReader();
|
||||
|
||||
controller.abort();
|
||||
|
||||
const item = await reader.read();
|
||||
|
||||
assert_true(item.done, "Stream is done");
|
||||
}, "Stream will not error if body is empty. It's closed with an empty queue before it errors.");
|
||||
|
||||
promise_test(async t => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
let cancelReason;
|
||||
|
||||
const body = new ReadableStream({
|
||||
pull(controller) {
|
||||
controller.enqueue(new Uint8Array([42]));
|
||||
},
|
||||
cancel(reason) {
|
||||
cancelReason = reason;
|
||||
}
|
||||
});
|
||||
|
||||
const fetchPromise = fetch('../resources/empty.txt', {
|
||||
body, signal,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'text/plain'
|
||||
}
|
||||
});
|
||||
|
||||
assert_true(!!cancelReason, 'Cancel called sync');
|
||||
assert_equals(cancelReason.constructor, DOMException);
|
||||
assert_equals(cancelReason.name, 'AbortError');
|
||||
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
|
||||
const fetchErr = await fetchPromise.catch(e => e);
|
||||
|
||||
assert_equals(cancelReason, fetchErr, "Fetch rejects with same error instance");
|
||||
}, "Readable stream synchronously cancels with AbortError if aborted before reading");
|
||||
|
||||
test(() => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const request = new Request('.', { signal });
|
||||
const requestSignal = request.signal;
|
||||
|
||||
const clonedRequest = request.clone();
|
||||
|
||||
assert_equals(requestSignal, request.signal, "Original request signal the same after cloning");
|
||||
assert_true(request.signal.aborted, "Original request signal aborted");
|
||||
assert_not_equals(clonedRequest.signal, request.signal, "Cloned request has different signal");
|
||||
assert_true(clonedRequest.signal.aborted, "Cloned request signal aborted");
|
||||
}, "Signal state is cloned");
|
||||
|
||||
test(() => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
|
||||
const request = new Request('.', { signal });
|
||||
const clonedRequest = request.clone();
|
||||
|
||||
const log = [];
|
||||
|
||||
request.signal.addEventListener('abort', () => log.push('original-aborted'));
|
||||
clonedRequest.signal.addEventListener('abort', () => log.push('clone-aborted'));
|
||||
|
||||
controller.abort();
|
||||
|
||||
assert_array_equals(log, ['clone-aborted', 'original-aborted'], "Abort events fired in correct order");
|
||||
assert_true(request.signal.aborted, 'Signal aborted');
|
||||
assert_true(clonedRequest.signal.aborted, 'Signal aborted');
|
||||
}, "Clone aborts with original controller");
|
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Aborting fetch when intercepted by a service worker</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../../../service-workers/service-worker/resources/test-helpers.sub.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
// Duplicating this resource to make service worker scoping simpler.
|
||||
const SCOPE = '../resources/basic.html';
|
||||
const BODY_METHODS = ['arrayBuffer', 'blob', 'formData', 'json', 'text'];
|
||||
|
||||
async function cleanup() {
|
||||
for (const iframe of document.querySelectorAll('.test-iframe')) {
|
||||
iframe.parentNode.removeChild(iframe);
|
||||
}
|
||||
|
||||
const reg = await navigator.serviceWorker.getRegistration(SCOPE);
|
||||
if (reg) await reg.unregister();
|
||||
}
|
||||
|
||||
async function setupRegistration(t) {
|
||||
await cleanup();
|
||||
const reg = await navigator.serviceWorker.register('../resources/sw-intercept.js', { scope: SCOPE });
|
||||
await wait_for_state(t, reg.installing, 'activated');
|
||||
return reg;
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
await setupRegistration(t);
|
||||
const iframe = await with_iframe(SCOPE);
|
||||
const w = iframe.contentWindow;
|
||||
|
||||
const controller = new w.AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
|
||||
const nextData = new Promise(resolve => {
|
||||
w.navigator.serviceWorker.addEventListener('message', function once(event) {
|
||||
w.navigator.serviceWorker.removeEventListener('message', once);
|
||||
resolve(event.data);
|
||||
})
|
||||
});
|
||||
|
||||
const fetchPromise = w.fetch('data.json', { signal });
|
||||
|
||||
await promise_rejects(t, "AbortError", fetchPromise);
|
||||
|
||||
await w.fetch('data.json?no-abort');
|
||||
|
||||
assert_true((await nextData).endsWith('?no-abort'), "Aborted request does not go through service worker");
|
||||
}, "Already aborted request does not land in service worker");
|
||||
|
||||
for (const bodyMethod of BODY_METHODS) {
|
||||
promise_test(async t => {
|
||||
await setupRegistration(t);
|
||||
const iframe = await with_iframe(SCOPE);
|
||||
const w = iframe.contentWindow;
|
||||
|
||||
const controller = new w.AbortController();
|
||||
const signal = controller.signal;
|
||||
|
||||
const log = [];
|
||||
const response = await w.fetch('data.json', { signal });
|
||||
|
||||
controller.abort();
|
||||
|
||||
const bodyPromise = response[bodyMethod]();
|
||||
|
||||
await Promise.all([
|
||||
bodyPromise.catch(() => log.push(`${bodyMethod}-reject`)),
|
||||
Promise.resolve().then(() => log.push('next-microtask'))
|
||||
]);
|
||||
|
||||
await promise_rejects(t, "AbortError", bodyPromise);
|
||||
|
||||
assert_array_equals(log, [`${bodyMethod}-reject`, 'next-microtask']);
|
||||
}, `response.${bodyMethod}() rejects if already aborted`);
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
await setupRegistration(t);
|
||||
const iframe = await with_iframe(SCOPE);
|
||||
const w = iframe.contentWindow;
|
||||
|
||||
const controller = new w.AbortController();
|
||||
const signal = controller.signal;
|
||||
|
||||
const response = await w.fetch('data.json', { signal });
|
||||
const reader = response.body.getReader();
|
||||
|
||||
controller.abort();
|
||||
|
||||
await promise_rejects(t, "AbortError", reader.read());
|
||||
await promise_rejects(t, "AbortError", reader.closed);
|
||||
}, "Stream errors once aborted.");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -7,23 +7,27 @@
|
|||
<script>
|
||||
var noop = function() {};
|
||||
|
||||
["text/csv",
|
||||
"audio/aiff",
|
||||
"audio/midi",
|
||||
"audio/whatever",
|
||||
"video/avi",
|
||||
"video/fli",
|
||||
"video/whatever",
|
||||
"image/jpeg",
|
||||
"image/gif",
|
||||
"image/whatever"].forEach(function(test_case) {
|
||||
async_test(function(t) {
|
||||
var script = document.createElement("script");
|
||||
script.onerror = t.step_func_done(noop);
|
||||
script.onload = t.unreached_func("Unexpected load event");
|
||||
script.src = "../resources/script-with-header.py?mime=" + test_case;
|
||||
document.body.appendChild(script);
|
||||
}, "Should fail loading script with " + test_case + " MIME type");
|
||||
["non-empty", "empty"].forEach(function(content) {
|
||||
["text/csv",
|
||||
"audio/aiff",
|
||||
"audio/midi",
|
||||
"audio/whatever",
|
||||
"video/avi",
|
||||
"video/fli",
|
||||
"video/whatever",
|
||||
"image/jpeg",
|
||||
"image/gif",
|
||||
"image/whatever"].forEach(function(test_case) {
|
||||
async_test(function(t) {
|
||||
var script = document.createElement("script");
|
||||
script.onerror = t.step_func_done(noop);
|
||||
script.onload = t.unreached_func("Unexpected load event");
|
||||
script.src = "../resources/script-with-header.py?content=" + content +
|
||||
"&mime=" + test_case;
|
||||
document.body.appendChild(script);
|
||||
}, "Should fail loading " + content + " script with " + test_case +
|
||||
" MIME type");
|
||||
});
|
||||
});
|
||||
|
||||
["html", "plain"].forEach(function(test_case) {
|
||||
|
|
|
@ -3,16 +3,26 @@ if (this.document === undefined) {
|
|||
importScripts("../resources/utils.js");
|
||||
}
|
||||
|
||||
function integrity(desc, url, integrity, shouldPass) {
|
||||
if (shouldPass) {
|
||||
function integrity(desc, url, integrity, initRequestMode, shouldPass) {
|
||||
var fetchRequestInit = {'integrity': integrity}
|
||||
if (!!initRequestMode && initRequestMode !== "") {
|
||||
fetchRequestInit.mode = initRequestMode;
|
||||
}
|
||||
|
||||
if (shouldPass) {
|
||||
promise_test(function(test) {
|
||||
return fetch(url, {'integrity': integrity}).then(function(resp) {
|
||||
assert_equals(resp.status, 200, "Response's status is 200");
|
||||
return fetch(url, fetchRequestInit).then(function(resp) {
|
||||
if (initRequestMode !== "no-cors") {
|
||||
assert_equals(resp.status, 200, "Response's status is 200");
|
||||
} else {
|
||||
assert_equals(resp.status, 0, "Opaque response's status is 0");
|
||||
assert_equals(resp.type, "opaque");
|
||||
}
|
||||
});
|
||||
}, desc);
|
||||
} else {
|
||||
promise_test(function(test) {
|
||||
return promise_rejects(test, new TypeError(), fetch(url, {'integrity': integrity}));
|
||||
return promise_rejects(test, new TypeError(), fetch(url, fetchRequestInit));
|
||||
}, desc);
|
||||
}
|
||||
}
|
||||
|
@ -27,19 +37,43 @@ var url = "../resources/top.txt";
|
|||
var corsUrl = "http://{{host}}:{{ports[http][1]}}" + dirname(location.pathname) + RESOURCES_DIR + "top.txt";
|
||||
/* Enable CORS*/
|
||||
corsUrl += "?pipe=header(Access-Control-Allow-Origin,*)";
|
||||
var corsUrl2 = "https://{{host}}:{{ports[https][0]}}/fetch/api/resource/top.txt";
|
||||
|
||||
integrity("Empty string integrity", url, "", true);
|
||||
integrity("SHA-256 integrity", url, topSha256, true);
|
||||
integrity("SHA-384 integrity", url, topSha384, true);
|
||||
integrity("SHA-512 integrity", url, topSha512, true);
|
||||
integrity("Invalid integrity", url, invalidSha256, false);
|
||||
integrity("Multiple integrities: valid stronger than invalid", url, invalidSha256 + " " + topSha384, true);
|
||||
integrity("Multiple integrities: invalid stronger than valid", url, invalidSha512 + " " + topSha384, false);
|
||||
integrity("Multiple integrities: invalid as strong as valid", url, invalidSha512 + " " + topSha512, true);
|
||||
integrity("Multiple integrities: both are valid", url, topSha384 + " " + topSha512, true);
|
||||
integrity("Multiple integrities: both are invalid", url, invalidSha256 + " " + invalidSha512, false);
|
||||
integrity("CORS empty integrity", corsUrl, "", true);
|
||||
integrity("CORS SHA-512 integrity", corsUrl, topSha512, true);
|
||||
integrity("CORS invalid integrity", corsUrl, invalidSha512, false);
|
||||
integrity("Empty string integrity", url, "", /* initRequestMode */ undefined,
|
||||
/* shouldPass */ true);
|
||||
integrity("SHA-256 integrity", url, topSha256, /* initRequestMode */ undefined,
|
||||
/* shouldPass */ true);
|
||||
integrity("SHA-384 integrity", url, topSha384, /* initRequestMode */ undefined,
|
||||
/* shouldPass */ true);
|
||||
integrity("SHA-512 integrity", url, topSha512, /* initRequestMode */ undefined,
|
||||
/* shouldPass */ true);
|
||||
integrity("Invalid integrity", url, invalidSha256,
|
||||
/* initRequestMode */ undefined, /* shouldPass */ false);
|
||||
integrity("Multiple integrities: valid stronger than invalid", url,
|
||||
invalidSha256 + " " + topSha384, /* initRequestMode */ undefined,
|
||||
/* shouldPass */ true);
|
||||
integrity("Multiple integrities: invalid stronger than valid",
|
||||
url, invalidSha512 + " " + topSha384, /* initRequestMode */ undefined,
|
||||
/* shouldPass */ false);
|
||||
integrity("Multiple integrities: invalid as strong as valid", url,
|
||||
invalidSha512 + " " + topSha512, /* initRequestMode */ undefined,
|
||||
/* shouldPass */ true);
|
||||
integrity("Multiple integrities: both are valid", url,
|
||||
topSha384 + " " + topSha512, /* initRequestMode */ undefined,
|
||||
/* shouldPass */ true);
|
||||
integrity("Multiple integrities: both are invalid", url,
|
||||
invalidSha256 + " " + invalidSha512, /* initRequestMode */ undefined,
|
||||
/* shouldPass */ false);
|
||||
integrity("CORS empty integrity", corsUrl, "", /* initRequestMode */ undefined,
|
||||
/* shouldPass */ true);
|
||||
integrity("CORS SHA-512 integrity", corsUrl, topSha512,
|
||||
/* initRequestMode */ undefined, /* shouldPass */ true);
|
||||
integrity("CORS invalid integrity", corsUrl, invalidSha512,
|
||||
/* initRequestMode */ undefined, /* shouldPass */ false);
|
||||
|
||||
integrity("Empty string integrity for opaque response", corsUrl2, "",
|
||||
/* initRequestMode */ "no-cors", /* shouldPass */ true);
|
||||
integrity("SHA-* integrity for opaque response", corsUrl2, topSha512,
|
||||
/* initRequestMode */ "no-cors", /* shouldPass */ false);
|
||||
|
||||
done();
|
||||
|
|
|
@ -4,7 +4,7 @@ if (this.document === undefined) {
|
|||
}
|
||||
|
||||
const url = "http://{{host}}:{{ports[http][1]}}" + dirname(location.pathname) + RESOURCES_DIR + "top.txt",
|
||||
sharedHeaders = "?pipe=header(Access-Control-Expose-Headers,*)|header(Test,X)|header(Set-Cookie,X)|"
|
||||
sharedHeaders = "?pipe=header(Access-Control-Expose-Headers,*)|header(Test,X)|header(Set-Cookie,X)|header(*,whoa)|"
|
||||
|
||||
promise_test(() => {
|
||||
const headers = "header(Access-Control-Allow-Origin,*)"
|
||||
|
@ -13,6 +13,7 @@ promise_test(() => {
|
|||
assert_equals(resp.type , "cors")
|
||||
assert_equals(resp.headers.get("test"), "X")
|
||||
assert_equals(resp.headers.get("set-cookie"), null)
|
||||
assert_equals(resp.headers.get("*"), "whoa")
|
||||
})
|
||||
}, "Basic Access-Control-Expose-Headers: * support")
|
||||
|
||||
|
@ -25,7 +26,19 @@ promise_test(() => {
|
|||
assert_equals(resp.headers.get("content-type"), "text/plain") // safelisted
|
||||
assert_equals(resp.headers.get("test"), null)
|
||||
assert_equals(resp.headers.get("set-cookie"), null)
|
||||
assert_equals(resp.headers.get("*"), "whoa")
|
||||
})
|
||||
}, "Cannot use * for credentialed fetches")
|
||||
}, "* for credentialed fetches only matches literally")
|
||||
|
||||
promise_test(() => {
|
||||
const headers = "header(Access-Control-Allow-Origin,*)|header(Access-Control-Expose-Headers,set-cookie)"
|
||||
return fetch(url + sharedHeaders + headers).then(resp => {
|
||||
assert_equals(resp.status, 200)
|
||||
assert_equals(resp.type , "cors")
|
||||
assert_equals(resp.headers.get("test"), "X")
|
||||
assert_equals(resp.headers.get("set-cookie"), null)
|
||||
assert_equals(resp.headers.get("*"), "whoa")
|
||||
})
|
||||
}, "* can be one of several values")
|
||||
|
||||
done();
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// META: script=/common/utils.js
|
||||
// META: script=../resources/utils.js
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
|
||||
var cors_url = get_host_info().HTTP_REMOTE_ORIGIN +
|
||||
dirname(location.pathname) +
|
||||
RESOURCES_DIR +
|
||||
"preflight.py";
|
||||
|
||||
promise_test((test) => {
|
||||
var uuid_token = token();
|
||||
var request_url =
|
||||
cors_url + "?token=" + uuid_token + "&max_age=12000&allow_methods=POST" +
|
||||
"&allow_headers=x-test-header";
|
||||
return fetch(cors_url + "?token=" + uuid_token + "&clear-stash")
|
||||
.then(() => {
|
||||
return fetch(
|
||||
new Request(request_url,
|
||||
{
|
||||
mode: "cors",
|
||||
method: "POST",
|
||||
headers: [["x-test-header", "test1"]]
|
||||
}));
|
||||
})
|
||||
.then((resp) => {
|
||||
assert_equals(resp.status, 200, "Response's status is 200");
|
||||
assert_equals(resp.headers.get("x-did-preflight"), "1", "Preflight request has been made");
|
||||
return fetch(cors_url + "?token=" + uuid_token + "&clear-stash");
|
||||
})
|
||||
.then((res) => res.text())
|
||||
.then((txt) => {
|
||||
assert_equals(txt, "1", "Server stash must be cleared.");
|
||||
return fetch(
|
||||
new Request(request_url,
|
||||
{
|
||||
mode: "cors",
|
||||
method: "POST",
|
||||
headers: [["x-test-header", "test2"]]
|
||||
}));
|
||||
})
|
||||
.then((resp) => {
|
||||
assert_equals(resp.status, 200, "Response's status is 200");
|
||||
assert_equals(resp.headers.get("x-did-preflight"), "0", "Preflight request has not been made");
|
||||
return fetch(cors_url + "?token=" + uuid_token + "&clear-stash");
|
||||
});
|
||||
});
|
|
@ -16,7 +16,7 @@ function preflightTest(succeeds, withCredentials, allowMethod, allowHeader, useM
|
|||
if (useMethod) {
|
||||
requestInit.method = useMethod
|
||||
}
|
||||
if (useHeader) {
|
||||
if (useHeader.length > 0) {
|
||||
requestInit.headers = [useHeader]
|
||||
}
|
||||
testURL += "allow_methods=" + allowMethod + "&"
|
||||
|
@ -36,7 +36,9 @@ preflightTest(true, false, "get", "x-test", "GET", ["X-Test", "1"])
|
|||
preflightTest(true, false, "*", "x-test", "SUPER", ["X-Test", "1"])
|
||||
preflightTest(true, false, "*", "*", "OK", ["X-Test", "1"])
|
||||
preflightTest(false, true, "*", "*", "OK", ["X-Test", "1"])
|
||||
preflightTest(false, true, "*", "", "PUT", undefined)
|
||||
preflightTest(false, true, "put", "*", "PUT", undefined)
|
||||
preflightTest(false, true, "*", "", "PUT", [])
|
||||
preflightTest(true, true, "PUT", "*", "PUT", [])
|
||||
preflightTest(false, true, "put", "*", "PUT", [])
|
||||
preflightTest(false, true, "get", "*", "GET", ["X-Test", "1"])
|
||||
preflightTest(false, true, "*", "*", "GET", ["X-Test", "1"])
|
||||
preflightTest(true, true, "*", "*", "*", ["*", "1"])
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Headers nameshake</title>
|
||||
<title>Headers have combined (and sorted) values</title>
|
||||
<meta name="help" href="https://fetch.spec.whatwg.org/#headers">
|
||||
<meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
|
@ -53,6 +53,24 @@
|
|||
assert_equals(headers.get(name), (value + ", " + "newSingleValue"));
|
||||
}
|
||||
}, "Check append methods when called with already used name");
|
||||
|
||||
test(() => {
|
||||
const headers = new Headers([["1", "a"],["1", "b"]]);
|
||||
for(let header of headers) {
|
||||
assert_array_equals(header, ["1", "a, b"]);
|
||||
}
|
||||
}, "Iterate combined values");
|
||||
|
||||
test(() => {
|
||||
const headers = new Headers([["2", "a"], ["1", "b"], ["2", "b"]]),
|
||||
expected = [["1", "b"], ["2", "a, b"]];
|
||||
let i = 0;
|
||||
for(let header of headers) {
|
||||
assert_array_equals(header, expected[i]);
|
||||
i++;
|
||||
}
|
||||
assert_equals(i, 2);
|
||||
}, "Iterate combined values in sorted order")
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -251,59 +251,21 @@ test(function() {
|
|||
ownKeys: function() {
|
||||
return [ "a", "c", "a", "c" ];
|
||||
},
|
||||
getCalls: 0,
|
||||
gotCOnce: false,
|
||||
get: function(target, name, receiver) {
|
||||
if (name == "c") {
|
||||
this.gotCOnce = true;
|
||||
}
|
||||
if (typeof name == "string") {
|
||||
return ++this.getCalls;
|
||||
}
|
||||
return Reflect.get(target, name, receiver);
|
||||
},
|
||||
getOwnPropertyDescriptor: function(target, name) {
|
||||
var desc = Reflect.getOwnPropertyDescriptor(target, name);
|
||||
if (name == "c" && this.gotCOnce) {
|
||||
desc.enumerable = false;
|
||||
}
|
||||
return desc;
|
||||
}
|
||||
};
|
||||
var lyingProxy = new Proxy(record, lyingHandler);
|
||||
var proxy = new Proxy(lyingProxy, loggingHandler);
|
||||
var h = new Headers(proxy);
|
||||
|
||||
assert_equals(log.length, 9);
|
||||
// Returning duplicate keys from ownKeys() throws a TypeError.
|
||||
assert_throws(new TypeError(),
|
||||
function() { var h = new Headers(proxy); });
|
||||
|
||||
assert_equals(log.length, 2);
|
||||
// The first thing is the [[Get]] of Symbol.iterator to figure out whether
|
||||
// we're a sequence, during overload resolution.
|
||||
assert_array_equals(log[0], ["get", lyingProxy, Symbol.iterator, proxy]);
|
||||
// Then we have the [[OwnPropertyKeys]] from
|
||||
// https://heycam.github.io/webidl/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", lyingProxy]);
|
||||
// Then the [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[2], ["getOwnPropertyDescriptor", lyingProxy, "a"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[3], ["get", lyingProxy, "a", proxy]);
|
||||
// Then the second [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[4], ["getOwnPropertyDescriptor", lyingProxy, "c"]);
|
||||
// Then the second [[Get]] from step 5.2.
|
||||
assert_array_equals(log[5], ["get", lyingProxy, "c", proxy]);
|
||||
// Then the third [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[6], ["getOwnPropertyDescriptor", lyingProxy, "a"]);
|
||||
// Then the third [[Get]] from step 5.2.
|
||||
assert_array_equals(log[7], ["get", lyingProxy, "a", proxy]);
|
||||
// Then the fourth [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[8], ["getOwnPropertyDescriptor", lyingProxy, "c"]);
|
||||
// No [[Get]] because not enumerable.
|
||||
|
||||
// Check the results.
|
||||
assert_equals([...h].length, 2);
|
||||
assert_array_equals([...h.keys()], ["a", "c"]);
|
||||
assert_true(h.has("a"));
|
||||
assert_equals(h.get("a"), "3");
|
||||
assert_true(h.has("c"));
|
||||
assert_equals(h.get("c"), "2");
|
||||
}, "Correct operation ordering with repeated keys");
|
||||
|
||||
test(function() {
|
||||
|
@ -395,5 +357,4 @@ test(function() {
|
|||
assert_true(h.has("c"));
|
||||
assert_equals(h.get("c"), "d");
|
||||
}, "Operation with non-enumerable Symbol keys");
|
||||
|
||||
</script>
|
||||
|
|
|
@ -8,54 +8,20 @@
|
|||
<meta name="author" title="Canon Research France" href="https://www.crf.canon.fr">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="request-error.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
test(function() {
|
||||
assert_throws(new TypeError() , function() { new Request("", {"window" : "http://test.url"}); },
|
||||
"Expect TypeError exception");
|
||||
},"RequestInit's window is not null");
|
||||
|
||||
test(function() {
|
||||
assert_throws(new TypeError() , function() { new Request("http://:not a valid URL"); },
|
||||
"Expect TypeError exception");
|
||||
},"Input URL is not valid")
|
||||
|
||||
test(function() {
|
||||
assert_throws(new TypeError() , function() { new Request("http://user:pass@test.url"); },
|
||||
"Expect TypeError exception");
|
||||
},"Input URL has credentials");
|
||||
|
||||
test(function() {
|
||||
assert_throws(new TypeError() , function() { new Request("", {"mode" : "navigate"}); },
|
||||
"Expect TypeError exception");
|
||||
},"RequestInit's mode is navigate");
|
||||
|
||||
test(function() {
|
||||
assert_throws(new TypeError() , function() { new Request("", {"referrer" : "http://:not a valid URL"}); },
|
||||
"Expect TypeError exception");
|
||||
},"RequestInit's referrer is invalid");
|
||||
|
||||
test(function() {
|
||||
assert_throws(new TypeError() , function() { new Request("", {"method" : "IN VALID"}); },
|
||||
"Expect TypeError exception");
|
||||
}, "RequestInit's method is invalid");
|
||||
|
||||
test(function() {
|
||||
assert_throws(new TypeError() , function() { new Request("", {"method" : "TRACE"}); },
|
||||
"Expect TypeError exception");
|
||||
}, "RequestInit's method is forbidden");
|
||||
|
||||
test(function() {
|
||||
assert_throws(new TypeError() , function() { new Request("", {"mode" : "no-cors", "method" : "PUT"}); },
|
||||
"Expect TypeError exception");
|
||||
},"RequestInit's mode is no-cors and method is not simple");
|
||||
|
||||
test(function() {
|
||||
assert_throws(new TypeError() ,
|
||||
function() { new Request("", {"mode" : "cors", "cache" : "only-if-cached"}); },
|
||||
"Expect TypeError exception");
|
||||
},"RequestInit's cache mode is only-if-cached and mode is not same-origin");
|
||||
// badRequestArgTests is from response-error.js
|
||||
for (const { args, testName } of badRequestArgTests) {
|
||||
test(() => {
|
||||
assert_throws(
|
||||
new TypeError(),
|
||||
() => new Request(...args),
|
||||
"Expect TypeError exception"
|
||||
);
|
||||
}, testName);
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var initialHeaders = new Headers([["Content-Type", "potato"]]);
|
||||
|
@ -86,28 +52,10 @@
|
|||
assert_equals(request.headers.get("Content-Type"), "potato");
|
||||
}, "Request should get its content-type from init headers if one is provided");
|
||||
|
||||
var parameters = ["referrerPolicy", "mode", "credentials", "cache", "redirect"];
|
||||
parameters.forEach(function(parameter) {
|
||||
test(function() {
|
||||
var options = { };
|
||||
options[parameter] = "BAD";
|
||||
assert_throws(new TypeError(), function() { new Request("", options); });
|
||||
},"Bad " + parameter +" init parameter value");
|
||||
});
|
||||
|
||||
function testOnlyIfCachedMode(fetchMode, ok) {
|
||||
test(function() {
|
||||
var options = {"cache": "only-if-cached", "mode": fetchMode};
|
||||
if (ok)
|
||||
new Request("test", options);
|
||||
else
|
||||
assert_throws(new TypeError(), function() { new Request("test", options); });
|
||||
}, "Request with cache mode: only-if-cached and fetch mode: " + fetchMode);
|
||||
}
|
||||
testOnlyIfCachedMode("same-origin", true);
|
||||
testOnlyIfCachedMode("cors", false);
|
||||
testOnlyIfCachedMode("no-cors", false);
|
||||
|
||||
test(function() {
|
||||
var options = {"cache": "only-if-cached", "mode": "same-origin"};
|
||||
new Request("test", options);
|
||||
}, "Request with cache mode: only-if-cached and fetch mode: same-origin");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
const badRequestArgTests = [
|
||||
{
|
||||
args: ["", { "window": "http://test.url" }],
|
||||
testName: "RequestInit's window is not null"
|
||||
},
|
||||
{
|
||||
args: ["http://:not a valid URL"],
|
||||
testName: "Input URL is not valid"
|
||||
},
|
||||
{
|
||||
args: ["http://user:pass@test.url"],
|
||||
testName: "Input URL has credentials"
|
||||
},
|
||||
{
|
||||
args: ["", { "mode": "navigate" }],
|
||||
testName: "RequestInit's mode is navigate"
|
||||
},
|
||||
{
|
||||
args: ["", { "referrer": "http://:not a valid URL" }],
|
||||
testName: "RequestInit's referrer is invalid"
|
||||
},
|
||||
{
|
||||
args: ["", { "method": "IN VALID" }],
|
||||
testName: "RequestInit's method is invalid"
|
||||
},
|
||||
{
|
||||
args: ["", { "method": "TRACE" }],
|
||||
testName: "RequestInit's method is forbidden"
|
||||
},
|
||||
{
|
||||
args: ["", { "mode": "no-cors", "method": "PUT" }],
|
||||
testName: "RequestInit's mode is no-cors and method is not simple"
|
||||
},
|
||||
{
|
||||
args: ["", { "mode": "cors", "cache": "only-if-cached" }],
|
||||
testName: "RequestInit's cache mode is only-if-cached and mode is not same-origin"
|
||||
},
|
||||
{
|
||||
args: ["test", { "cache": "only-if-cached", "mode": "cors" }],
|
||||
testName: "Request with cache mode: only-if-cached and fetch mode cors"
|
||||
},
|
||||
{
|
||||
args: ["test", { "cache": "only-if-cached", "mode": "no-cors" }],
|
||||
testName: "Request with cache mode: only-if-cached and fetch mode no-cors"
|
||||
}
|
||||
];
|
||||
|
||||
badRequestArgTests.push(
|
||||
...["referrerPolicy", "mode", "credentials", "cache", "redirect"].map(optionProp => {
|
||||
const options = {};
|
||||
options[optionProp] = "BAD";
|
||||
return {
|
||||
args: ["", options],
|
||||
testName: `Bad ${optionProp} init parameter value`
|
||||
};
|
||||
})
|
||||
);
|
|
@ -168,6 +168,19 @@
|
|||
});
|
||||
}, "Testing empty Request Content-Type header");
|
||||
|
||||
test(function() {
|
||||
const request1 = new Request("");
|
||||
assert_equals(request1.headers, request1.headers);
|
||||
|
||||
const request2 = new Request("", {"headers": {"X-Foo": "bar"}});
|
||||
assert_equals(request2.headers, request2.headers);
|
||||
const headers = request2.headers;
|
||||
request2.headers.set("X-Foo", "quux");
|
||||
assert_equals(headers, request2.headers);
|
||||
headers.set("X-Other-Header", "baz");
|
||||
assert_equals(headers, request2.headers);
|
||||
}, "Test that Request.headers has the [SameObject] extended attribute");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -37,7 +37,6 @@
|
|||
readonly attribute USVString url;
|
||||
[SameObject] readonly attribute Headers headers;
|
||||
|
||||
readonly attribute RequestType type;
|
||||
readonly attribute RequestDestination destination;
|
||||
readonly attribute USVString referrer;
|
||||
readonly attribute ReferrerPolicy referrerPolicy;
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
var attributes = ["method",
|
||||
"url",
|
||||
"headers",
|
||||
"type",
|
||||
"destination",
|
||||
"referrer",
|
||||
"referrerPolicy",
|
||||
|
@ -56,11 +55,6 @@
|
|||
return;
|
||||
break;
|
||||
|
||||
case "type":
|
||||
defaultValue = "";
|
||||
newValue = "style";
|
||||
break;
|
||||
|
||||
case "destination":
|
||||
defaultValue = "";
|
||||
newValue = "worker";
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Request.type attribute should not exist</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id=log></div>
|
||||
<script>
|
||||
var request = new Request("https://domfarolino.com");
|
||||
test(() => {
|
||||
assert_equals(request.type, undefined, "request.type should be undefined");
|
||||
}, "'type' getter should not exist on Request objects");
|
||||
</script>
|
|
@ -0,0 +1,25 @@
|
|||
<!doctype html>
|
||||
<meta charset=windows-1252>
|
||||
<title>Fetch: URL encoding</title>
|
||||
<script src=/resources/testharness.js></script>
|
||||
<script src=/resources/testharnessreport.js></script>
|
||||
<script>
|
||||
const expectedURL = new URL("?%C3%9F", location.href).href;
|
||||
const expectedURL2 = new URL("?%EF%BF%BD", location.href).href;
|
||||
test(() => {
|
||||
let r = new Request("?\u00DF");
|
||||
assert_equals(r.url, expectedURL);
|
||||
|
||||
r = new Request("?\uD83D");
|
||||
assert_equals(r.url, expectedURL2);
|
||||
}, "URL encoding and Request");
|
||||
|
||||
promise_test(() => {
|
||||
return fetch("?\u00DF").then(res => {
|
||||
assert_equals(res.url, expectedURL);
|
||||
return fetch("?\uD83D").then(res2 => {
|
||||
assert_equals(res2.url, expectedURL2);
|
||||
});
|
||||
});
|
||||
}, "URL encoding and fetch()");
|
||||
</script>
|
|
@ -0,0 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<!--
|
||||
Duplicating /common/blank.html to make service worker scoping simpler in
|
||||
../abort/serviceworker-intercepted.https.html
|
||||
-->
|
|
@ -0,0 +1,36 @@
|
|||
import time
|
||||
|
||||
|
||||
def url_dir(request):
|
||||
return '/'.join(request.url_parts.path.split('/')[:-1]) + '/'
|
||||
|
||||
|
||||
def stash_write(request, key, value):
|
||||
"""Write to the stash, overwriting any previous value"""
|
||||
request.server.stash.take(key, url_dir(request))
|
||||
request.server.stash.put(key, value, url_dir(request))
|
||||
|
||||
|
||||
def main(request, response):
|
||||
stateKey = request.GET.first("stateKey", "")
|
||||
abortKey = request.GET.first("abortKey", "")
|
||||
|
||||
if stateKey:
|
||||
stash_write(request, stateKey, 'open')
|
||||
|
||||
response.headers.set("Content-type", "text/plain")
|
||||
response.write_status_headers()
|
||||
|
||||
# Writing an initial 2k so browsers realise it's there. *shrug*
|
||||
response.writer.write("." * 2048)
|
||||
|
||||
while True:
|
||||
if not response.writer.flush():
|
||||
break
|
||||
if abortKey and request.server.stash.take(abortKey, url_dir(request)):
|
||||
break
|
||||
response.writer.write(".")
|
||||
time.sleep(0.01)
|
||||
|
||||
if stateKey:
|
||||
stash_write(request, stateKey, 'closed')
|
|
@ -12,6 +12,12 @@ def main(request, response):
|
|||
else:
|
||||
headers.append(("Access-Control-Allow-Origin", "*"))
|
||||
|
||||
if "clear-stash" in request.GET:
|
||||
if request.server.stash.take(token) is not None:
|
||||
return headers, "1"
|
||||
else:
|
||||
return headers, "0"
|
||||
|
||||
if "credentials" in request.GET:
|
||||
headers.append(("Access-Control-Allow-Credentials", "true"))
|
||||
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
def main(request, response):
|
||||
headers = [("Content-type", request.GET.first("mime"))]
|
||||
content = "console.log('Script loaded')"
|
||||
if "content" in request.GET and request.GET.first("content") == "empty":
|
||||
content = ''
|
||||
else:
|
||||
content = "console.log('Script loaded')"
|
||||
return 200, headers, content
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
def main(request, response):
|
||||
url_dir = '/'.join(request.url_parts.path.split('/')[:-1]) + '/'
|
||||
key = request.GET.first("key")
|
||||
value = request.GET.first("value")
|
||||
request.server.stash.put(key, value, url_dir)
|
||||
response.headers.set('Access-Control-Allow-Origin', '*')
|
||||
return "done"
|
|
@ -0,0 +1,9 @@
|
|||
from wptserve.handlers import json_handler
|
||||
|
||||
|
||||
@json_handler
|
||||
def main(request, response):
|
||||
dir = '/'.join(request.url_parts.path.split('/')[:-1]) + '/'
|
||||
key = request.GET.first("key")
|
||||
response.headers.set('Access-Control-Allow-Origin', '*')
|
||||
return request.server.stash.take(key, dir)
|
|
@ -0,0 +1,10 @@
|
|||
async function broadcast(msg) {
|
||||
for (const client of await clients.matchAll()) {
|
||||
client.postMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener('fetch', event => {
|
||||
event.waitUntil(broadcast(event.request.url));
|
||||
event.respondWith(fetch(event.request));
|
||||
});
|
|
@ -81,6 +81,26 @@ function validateStreamFromString(reader, expectedValue, retrievedArrayBuffer) {
|
|||
});
|
||||
}
|
||||
|
||||
function validateStreamFromPartialString(reader, expectedValue, retrievedArrayBuffer) {
|
||||
return reader.read().then(function(data) {
|
||||
if (!data.done) {
|
||||
assert_true(data.value instanceof Uint8Array, "Fetch ReadableStream chunks should be Uint8Array");
|
||||
var newBuffer;
|
||||
if (retrievedArrayBuffer) {
|
||||
newBuffer = new ArrayBuffer(data.value.length + retrievedArrayBuffer.length);
|
||||
newBuffer.set(retrievedArrayBuffer, 0);
|
||||
newBuffer.set(data.value, retrievedArrayBuffer.length);
|
||||
} else {
|
||||
newBuffer = data.value;
|
||||
}
|
||||
return validateStreamFromPartialString(reader, expectedValue, newBuffer);
|
||||
}
|
||||
|
||||
var string = new TextDecoder("utf-8").decode(retrievedArrayBuffer);
|
||||
return assert_true(string.search(expectedValue) != -1, "Retrieve and verify stream");
|
||||
});
|
||||
}
|
||||
|
||||
// From streams tests
|
||||
function delay(milliseconds)
|
||||
{
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
<body>
|
||||
<script src="../resources/utils.js"></script>
|
||||
<script>
|
||||
|
||||
promise_test(function(test) {
|
||||
return new Response(new Blob([], { "type" : "text/plain" })).body.cancel();
|
||||
}, "Cancelling a starting blob Response stream");
|
||||
|
@ -51,16 +50,19 @@ promise_test(function() {
|
|||
}, "Cancelling a loading Response stream");
|
||||
|
||||
promise_test(function() {
|
||||
async function readAll(reader) {
|
||||
while (true) {
|
||||
const {value, done} = await reader.read();
|
||||
if (done)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return fetch(RESOURCES_DIR + "top.txt").then(function(response) {
|
||||
var reader = response.body.getReader();
|
||||
var closedPromise = reader.closed.then(function() {
|
||||
return reader.cancel();
|
||||
});
|
||||
reader.read();
|
||||
return closedPromise;
|
||||
return readAll(reader).then(() => reader.cancel());
|
||||
});
|
||||
}, "Cancelling a closed Response stream");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -57,15 +57,16 @@ promise_test(function(test) {
|
|||
|
||||
promise_test(function(test) {
|
||||
var response = new Response(formData);
|
||||
return validateStreamFromString(response.body.getReader(), "name=value");
|
||||
return validateStreamFromPartialString(response.body.getReader(),
|
||||
"Content-Disposition: form-data; name=\"name\"\r\n\r\nvalue");
|
||||
}, "Read form data response's body as readableStream");
|
||||
|
||||
test(function() {
|
||||
assert_equals(Response.error().body, null);
|
||||
}, "Getting an error Response stream");
|
||||
|
||||
promise_test(function(test) {
|
||||
assert_equals(Response.redirect(301).body, null);
|
||||
test(function() {
|
||||
assert_equals(Response.redirect("/").body, null);
|
||||
}, "Getting a redirect Response stream");
|
||||
|
||||
</script>
|
||||
|
|
|
@ -58,6 +58,19 @@
|
|||
"Expect response.ok is " + isOkStatus(response.status));
|
||||
}
|
||||
}, "Check " + attributeName + " init values and associated getter");
|
||||
|
||||
test(function() {
|
||||
const response1 = new Response("");
|
||||
assert_equals(response1.headers, response1.headers);
|
||||
|
||||
const response2 = new Response("", {"headers": {"X-Foo": "bar"}});
|
||||
assert_equals(response2.headers, response2.headers);
|
||||
const headers = response2.headers;
|
||||
response2.headers.set("X-Foo", "quux");
|
||||
assert_equals(headers, response2.headers);
|
||||
headers.set("X-Other-Header", "baz");
|
||||
assert_equals(headers, response2.headers);
|
||||
}, "Test that Response.headers has the [SameObject] extended attribute");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>ReadableStream disturbed tests, via Response's bodyUsed property</title>
|
||||
<link rel="author" title="Domenic Denicola" href="mailto:d@domenic.me">
|
||||
<link rel="author" title="Takeshi Yoshino" href="mailto:tyoshino@chromium.org">
|
||||
|
||||
<link rel="help" href="https://streams.spec.whatwg.org/#is-readable-stream-disturbed">
|
||||
<link rel="help" href="https://fetch.spec.whatwg.org/#dom-body-bodyused">
|
||||
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream();
|
||||
const response = new Response(stream);
|
||||
assert_false(response.bodyUsed, "On construction");
|
||||
|
||||
const reader = stream.getReader();
|
||||
assert_false(response.bodyUsed, "After getting a reader");
|
||||
|
||||
reader.read();
|
||||
assert_true(response.bodyUsed, "After calling stream.read()");
|
||||
}, "A non-closed stream on which read() has been called");
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream();
|
||||
const response = new Response(stream);
|
||||
assert_false(response.bodyUsed, "On construction");
|
||||
|
||||
const reader = stream.getReader();
|
||||
assert_false(response.bodyUsed, "After getting a reader");
|
||||
|
||||
reader.cancel();
|
||||
assert_true(response.bodyUsed, "After calling stream.cancel()");
|
||||
}, "A non-closed stream on which cancel() has been called");
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
c.close();
|
||||
}
|
||||
});
|
||||
const response = new Response(stream);
|
||||
assert_false(response.bodyUsed, "On construction");
|
||||
|
||||
const reader = stream.getReader();
|
||||
assert_false(response.bodyUsed, "After getting a reader");
|
||||
|
||||
reader.read();
|
||||
assert_true(response.bodyUsed, "After calling stream.read()");
|
||||
}, "A closed stream on which read() has been called");
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
c.error(new Error("some error"));
|
||||
}
|
||||
});
|
||||
const response = new Response(stream);
|
||||
assert_false(response.bodyUsed, "On construction");
|
||||
|
||||
const reader = stream.getReader();
|
||||
assert_false(response.bodyUsed, "After getting a reader");
|
||||
|
||||
reader.read();
|
||||
assert_true(response.bodyUsed, "After calling stream.read()");
|
||||
}, "An errored stream on which read() has been called");
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream({
|
||||
start(c) {
|
||||
c.error(new Error("some error"));
|
||||
}
|
||||
});
|
||||
const response = new Response(stream);
|
||||
assert_false(response.bodyUsed, "On construction");
|
||||
|
||||
const reader = stream.getReader();
|
||||
assert_false(response.bodyUsed, "After getting a reader");
|
||||
|
||||
reader.cancel();
|
||||
assert_true(response.bodyUsed, "After calling stream.cancel()");
|
||||
}, "An errored stream on which cancel() has been called");
|
||||
|
||||
</script>
|
Loading…
Add table
Add a link
Reference in a new issue