mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-08 01:20:27 +00:00
feat: HTTP add TLS fingerprint fallback (#1444)
This commit is contained in:
68 files changed
+7243
-1604
No files matched your search
+11
@@ -0,0 +1,11 @@
|
||||
# The 3-Clause BSD License
|
||||
|
||||
Copyright © web-platform-tests contributors
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Web Platform Tests provenance
|
||||
|
||||
The files under `fetch/` are copied from the official
|
||||
[web-platform-tests/wpt](https://github.com/web-platform-tests/wpt) repository.
|
||||
|
||||
- Upstream commit: `ce5d9e7e28b27528213bceea40d9e78462487105`
|
||||
- License: BSD-3-Clause; see `LICENSE.md`
|
||||
- Canonical Fetch tests: `fetch/`
|
||||
|
||||
Gopeed executes applicable worker-style Fetch API tests at the extension
|
||||
JavaScript boundary. The checked-in runner currently executes unchanged WPT
|
||||
tests for Headers, no-CORS header guards, Request construction and body
|
||||
consumption, ReadableStream bodies, keepalive, Request errors, Response
|
||||
construction and body consumption, immutable network response headers, and
|
||||
the Response static constructors.
|
||||
|
||||
Passing this subset is an extension-worker Fetch API compatibility claim, not
|
||||
a claim that Gopeed is a browser. Browser-only suites that require a document,
|
||||
CORS origin enforcement or preflight, CSP, Mixed Content, Service Workers,
|
||||
browser HTTP cache, navigation, browser authentication UI, or WPT's
|
||||
multi-origin server infrastructure are intentionally excluded. Gopeed also
|
||||
keeps one extension-specific behavior: `redirect: "manual"` exposes the HTTP
|
||||
redirect response so download extensions can inspect `Location`, instead of
|
||||
returning a browser `opaqueredirect` response.
|
||||
|
||||
`wpt_harness.js` is a small Goja adapter maintained by Gopeed; it is not copied
|
||||
from WPT. Test logic below `fetch/` is unchanged; the checked-in copy may only
|
||||
normalize a final newline.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
[
|
||||
["accept", "\""],
|
||||
["accept", "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"],
|
||||
["accept-language", "\u0001"],
|
||||
["accept-language", "@"],
|
||||
["authorization", "basics"],
|
||||
["content-language", "\u0001"],
|
||||
["content-language", "@"],
|
||||
["content-type", "text/html"],
|
||||
["content-type", "text/plain; long=0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901"],
|
||||
["range", "bytes 0-"],
|
||||
["test", "hi"]
|
||||
]
|
||||
@@ -0,0 +1,266 @@
|
||||
// META: title=Headers set-cookie special cases
|
||||
// META: global=window,worker
|
||||
|
||||
const headerList = [
|
||||
["set-cookie", "foo=bar"],
|
||||
["Set-Cookie", "fizz=buzz; domain=example.com"],
|
||||
];
|
||||
|
||||
const setCookie2HeaderList = [
|
||||
["set-cookie2", "foo2=bar2"],
|
||||
["Set-Cookie2", "fizz2=buzz2; domain=example2.com"],
|
||||
];
|
||||
|
||||
function assert_nested_array_equals(actual, expected) {
|
||||
assert_equals(actual.length, expected.length, "Array length is not equal");
|
||||
for (let i = 0; i < expected.length; i++) {
|
||||
assert_array_equals(actual[i], expected[i]);
|
||||
}
|
||||
}
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers(headerList);
|
||||
assert_equals(
|
||||
headers.get("set-cookie"),
|
||||
"foo=bar, fizz=buzz; domain=example.com",
|
||||
);
|
||||
}, "Headers.prototype.get combines set-cookie headers in order");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers(headerList);
|
||||
const list = [...headers];
|
||||
assert_nested_array_equals(list, [
|
||||
["set-cookie", "foo=bar"],
|
||||
["set-cookie", "fizz=buzz; domain=example.com"],
|
||||
]);
|
||||
}, "Headers iterator does not combine set-cookie headers");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers(setCookie2HeaderList);
|
||||
const list = [...headers];
|
||||
assert_nested_array_equals(list, [
|
||||
["set-cookie2", "foo2=bar2, fizz2=buzz2; domain=example2.com"],
|
||||
]);
|
||||
}, "Headers iterator does not special case set-cookie2 headers");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers([...headerList, ...setCookie2HeaderList]);
|
||||
const list = [...headers];
|
||||
assert_nested_array_equals(list, [
|
||||
["set-cookie", "foo=bar"],
|
||||
["set-cookie", "fizz=buzz; domain=example.com"],
|
||||
["set-cookie2", "foo2=bar2, fizz2=buzz2; domain=example2.com"],
|
||||
]);
|
||||
}, "Headers iterator does not combine set-cookie & set-cookie2 headers");
|
||||
|
||||
test(function () {
|
||||
// Values are in non alphabetic order, and the iterator should yield in the
|
||||
// headers in the exact order of the input.
|
||||
const headers = new Headers([
|
||||
["set-cookie", "z=z"],
|
||||
["set-cookie", "a=a"],
|
||||
["set-cookie", "n=n"],
|
||||
]);
|
||||
const list = [...headers];
|
||||
assert_nested_array_equals(list, [
|
||||
["set-cookie", "z=z"],
|
||||
["set-cookie", "a=a"],
|
||||
["set-cookie", "n=n"],
|
||||
]);
|
||||
}, "Headers iterator preserves set-cookie ordering");
|
||||
|
||||
test(
|
||||
function () {
|
||||
const headers = new Headers([
|
||||
["xylophone-header", "1"],
|
||||
["best-header", "2"],
|
||||
["set-cookie", "3"],
|
||||
["a-cool-header", "4"],
|
||||
["set-cookie", "5"],
|
||||
["a-cool-header", "6"],
|
||||
["best-header", "7"],
|
||||
]);
|
||||
const list = [...headers];
|
||||
assert_nested_array_equals(list, [
|
||||
["a-cool-header", "4, 6"],
|
||||
["best-header", "2, 7"],
|
||||
["set-cookie", "3"],
|
||||
["set-cookie", "5"],
|
||||
["xylophone-header", "1"],
|
||||
]);
|
||||
},
|
||||
"Headers iterator preserves per header ordering, but sorts keys alphabetically",
|
||||
);
|
||||
|
||||
test(
|
||||
function () {
|
||||
const headers = new Headers([
|
||||
["xylophone-header", "7"],
|
||||
["best-header", "6"],
|
||||
["set-cookie", "5"],
|
||||
["a-cool-header", "4"],
|
||||
["set-cookie", "3"],
|
||||
["a-cool-header", "2"],
|
||||
["best-header", "1"],
|
||||
]);
|
||||
const list = [...headers];
|
||||
assert_nested_array_equals(list, [
|
||||
["a-cool-header", "4, 2"],
|
||||
["best-header", "6, 1"],
|
||||
["set-cookie", "5"],
|
||||
["set-cookie", "3"],
|
||||
["xylophone-header", "7"],
|
||||
]);
|
||||
},
|
||||
"Headers iterator preserves per header ordering, but sorts keys alphabetically (and ignores value ordering)",
|
||||
);
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers([["fizz", "buzz"], ["X-Header", "test"]]);
|
||||
const iterator = headers[Symbol.iterator]();
|
||||
assert_array_equals(iterator.next().value, ["fizz", "buzz"]);
|
||||
headers.append("Set-Cookie", "a=b");
|
||||
assert_array_equals(iterator.next().value, ["set-cookie", "a=b"]);
|
||||
headers.append("Accept", "text/html");
|
||||
assert_array_equals(iterator.next().value, ["set-cookie", "a=b"]);
|
||||
assert_array_equals(iterator.next().value, ["x-header", "test"]);
|
||||
headers.append("set-cookie", "c=d");
|
||||
assert_array_equals(iterator.next().value, ["x-header", "test"]);
|
||||
assert_true(iterator.next().done);
|
||||
}, "Headers iterator is correctly updated with set-cookie changes");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers([
|
||||
["set-cookie", "a"],
|
||||
["set-cookie", "b"],
|
||||
["set-cookie", "c"]
|
||||
]);
|
||||
const iterator = headers[Symbol.iterator]();
|
||||
assert_array_equals(iterator.next().value, ["set-cookie", "a"]);
|
||||
headers.delete("set-cookie");
|
||||
headers.append("set-cookie", "d");
|
||||
headers.append("set-cookie", "e");
|
||||
headers.append("set-cookie", "f");
|
||||
assert_array_equals(iterator.next().value, ["set-cookie", "e"]);
|
||||
assert_array_equals(iterator.next().value, ["set-cookie", "f"]);
|
||||
assert_true(iterator.next().done);
|
||||
}, "Headers iterator is correctly updated with set-cookie changes #2");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers(headerList);
|
||||
assert_true(headers.has("sEt-cOoKiE"));
|
||||
}, "Headers.prototype.has works for set-cookie");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers(setCookie2HeaderList);
|
||||
headers.append("set-Cookie", "foo=bar");
|
||||
headers.append("sEt-cOoKiE", "fizz=buzz");
|
||||
const list = [...headers];
|
||||
assert_nested_array_equals(list, [
|
||||
["set-cookie", "foo=bar"],
|
||||
["set-cookie", "fizz=buzz"],
|
||||
["set-cookie2", "foo2=bar2, fizz2=buzz2; domain=example2.com"],
|
||||
]);
|
||||
}, "Headers.prototype.append works for set-cookie");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers(headerList);
|
||||
headers.set("set-cookie", "foo2=bar2");
|
||||
const list = [...headers];
|
||||
assert_nested_array_equals(list, [
|
||||
["set-cookie", "foo2=bar2"],
|
||||
]);
|
||||
}, "Headers.prototype.set works for set-cookie");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers(headerList);
|
||||
headers.delete("set-Cookie");
|
||||
const list = [...headers];
|
||||
assert_nested_array_equals(list, []);
|
||||
}, "Headers.prototype.delete works for set-cookie");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers();
|
||||
assert_array_equals(headers.getSetCookie(), []);
|
||||
}, "Headers.prototype.getSetCookie with no headers present");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers([headerList[0]]);
|
||||
assert_array_equals(headers.getSetCookie(), ["foo=bar"]);
|
||||
}, "Headers.prototype.getSetCookie with one header");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers({ "Set-Cookie": "foo=bar" });
|
||||
assert_array_equals(headers.getSetCookie(), ["foo=bar"]);
|
||||
}, "Headers.prototype.getSetCookie with one header created from an object");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers(headerList);
|
||||
assert_array_equals(headers.getSetCookie(), [
|
||||
"foo=bar",
|
||||
"fizz=buzz; domain=example.com",
|
||||
]);
|
||||
}, "Headers.prototype.getSetCookie with multiple headers");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers([["set-cookie", ""]]);
|
||||
assert_array_equals(headers.getSetCookie(), [""]);
|
||||
}, "Headers.prototype.getSetCookie with an empty header");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers([["set-cookie", "x"], ["set-cookie", "x"]]);
|
||||
assert_array_equals(headers.getSetCookie(), ["x", "x"]);
|
||||
}, "Headers.prototype.getSetCookie with two equal headers");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers([
|
||||
["set-cookie2", "x"],
|
||||
["set-cookie", "y"],
|
||||
["set-cookie2", "z"],
|
||||
]);
|
||||
assert_array_equals(headers.getSetCookie(), ["y"]);
|
||||
}, "Headers.prototype.getSetCookie ignores set-cookie2 headers");
|
||||
|
||||
test(function () {
|
||||
// Values are in non alphabetic order, and the iterator should yield in the
|
||||
// headers in the exact order of the input.
|
||||
const headers = new Headers([
|
||||
["set-cookie", "z=z"],
|
||||
["set-cookie", "a=a"],
|
||||
["set-cookie", "n=n"],
|
||||
]);
|
||||
assert_array_equals(headers.getSetCookie(), ["z=z", "a=a", "n=n"]);
|
||||
}, "Headers.prototype.getSetCookie preserves header ordering");
|
||||
|
||||
test(function () {
|
||||
const headers = new Headers({"Set-Cookie": " a=b\n"});
|
||||
headers.append("set-cookie", "\n\rc=d ");
|
||||
assert_nested_array_equals([...headers], [
|
||||
["set-cookie", "a=b"],
|
||||
["set-cookie", "c=d"]
|
||||
]);
|
||||
headers.set("set-cookie", "\te=f ");
|
||||
assert_nested_array_equals([...headers], [["set-cookie", "e=f"]]);
|
||||
}, "Adding Set-Cookie headers normalizes their value");
|
||||
|
||||
test(function () {
|
||||
assert_throws_js(TypeError, () => {
|
||||
new Headers({"set-cookie": "\0"});
|
||||
});
|
||||
|
||||
const headers = new Headers();
|
||||
assert_throws_js(TypeError, () => {
|
||||
headers.append("Set-Cookie", "a\nb");
|
||||
});
|
||||
assert_throws_js(TypeError, () => {
|
||||
headers.set("Set-Cookie", "a\rb");
|
||||
});
|
||||
}, "Adding invalid Set-Cookie headers throws");
|
||||
|
||||
test(function () {
|
||||
const response = new Response();
|
||||
response.headers.append("Set-Cookie", "foo=bar");
|
||||
assert_array_equals(response.headers.getSetCookie(), []);
|
||||
response.headers.append("sEt-cOokIe", "bar=baz");
|
||||
assert_array_equals(response.headers.getSetCookie(), []);
|
||||
}, "Set-Cookie is a forbidden response header");
|
||||
@@ -0,0 +1,275 @@
|
||||
// META: title=Headers structure
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
test(function() {
|
||||
new Headers();
|
||||
}, "Create headers from no parameter");
|
||||
|
||||
test(function() {
|
||||
new Headers(undefined);
|
||||
}, "Create headers from undefined parameter");
|
||||
|
||||
test(function() {
|
||||
new Headers({});
|
||||
}, "Create headers from empty object");
|
||||
|
||||
var parameters = [null, 1];
|
||||
parameters.forEach(function(parameter) {
|
||||
test(function() {
|
||||
assert_throws_js(TypeError, function() { new Headers(parameter) });
|
||||
}, "Create headers with " + parameter + " should throw");
|
||||
});
|
||||
|
||||
var headerDict = {"name1": "value1",
|
||||
"name2": "value2",
|
||||
"name3": "value3",
|
||||
"name4": null,
|
||||
"name5": undefined,
|
||||
"name6": 1,
|
||||
"Content-Type": "value4"
|
||||
};
|
||||
|
||||
var headerSeq = [];
|
||||
for (var name in headerDict)
|
||||
headerSeq.push([name, headerDict[name]]);
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerSeq);
|
||||
for (name in headerDict) {
|
||||
assert_equals(headers.get(name), String(headerDict[name]),
|
||||
"name: " + name + " has value: " + headerDict[name]);
|
||||
}
|
||||
assert_equals(headers.get("length"), null, "init should be treated as a sequence, not as a dictionary");
|
||||
}, "Create headers with sequence");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerDict);
|
||||
for (name in headerDict) {
|
||||
assert_equals(headers.get(name), String(headerDict[name]),
|
||||
"name: " + name + " has value: " + headerDict[name]);
|
||||
}
|
||||
}, "Create headers with record");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerDict);
|
||||
var headers2 = new Headers(headers);
|
||||
for (name in headerDict) {
|
||||
assert_equals(headers2.get(name), String(headerDict[name]),
|
||||
"name: " + name + " has value: " + headerDict[name]);
|
||||
}
|
||||
}, "Create headers with existing headers");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers()
|
||||
headers[Symbol.iterator] = function *() {
|
||||
yield ["test", "test"]
|
||||
}
|
||||
var headers2 = new Headers(headers)
|
||||
assert_equals(headers2.get("test"), "test")
|
||||
}, "Create headers with existing headers with custom iterator");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
for (name in headerDict) {
|
||||
headers.append(name, headerDict[name]);
|
||||
assert_equals(headers.get(name), String(headerDict[name]),
|
||||
"name: " + name + " has value: " + headerDict[name]);
|
||||
}
|
||||
}, "Check append method");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
for (name in headerDict) {
|
||||
headers.set(name, headerDict[name]);
|
||||
assert_equals(headers.get(name), String(headerDict[name]),
|
||||
"name: " + name + " has value: " + headerDict[name]);
|
||||
}
|
||||
}, "Check set method");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerDict);
|
||||
for (name in headerDict)
|
||||
assert_true(headers.has(name),"headers has name " + name);
|
||||
|
||||
assert_false(headers.has("nameNotInHeaders"),"headers do not have header: nameNotInHeaders");
|
||||
}, "Check has method");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerDict);
|
||||
for (name in headerDict) {
|
||||
assert_true(headers.has(name),"headers have a header: " + name);
|
||||
headers.delete(name)
|
||||
assert_true(!headers.has(name),"headers do not have anymore a header: " + name);
|
||||
}
|
||||
}, "Check delete method");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerDict);
|
||||
for (name in headerDict)
|
||||
assert_equals(headers.get(name), String(headerDict[name]),
|
||||
"name: " + name + " has value: " + headerDict[name]);
|
||||
|
||||
assert_equals(headers.get("nameNotInHeaders"), null, "header: nameNotInHeaders has no value");
|
||||
}, "Check get method");
|
||||
|
||||
var headerEntriesDict = {"name1": "value1",
|
||||
"Name2": "value2",
|
||||
"name": "value3",
|
||||
"content-Type": "value4",
|
||||
"Content-Typ": "value5",
|
||||
"Content-Types": "value6"
|
||||
};
|
||||
var sortedHeaderDict = {};
|
||||
var headerValues = [];
|
||||
var sortedHeaderKeys = Object.keys(headerEntriesDict).map(function(value) {
|
||||
sortedHeaderDict[value.toLowerCase()] = headerEntriesDict[value];
|
||||
headerValues.push(headerEntriesDict[value]);
|
||||
return value.toLowerCase();
|
||||
}).sort();
|
||||
|
||||
var iteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
|
||||
function checkIteratorProperties(iterator) {
|
||||
var prototype = Object.getPrototypeOf(iterator);
|
||||
assert_equals(Object.getPrototypeOf(prototype), iteratorPrototype);
|
||||
|
||||
var descriptor = Object.getOwnPropertyDescriptor(prototype, "next");
|
||||
assert_true(descriptor.configurable, "configurable");
|
||||
assert_true(descriptor.enumerable, "enumerable");
|
||||
assert_true(descriptor.writable, "writable");
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerEntriesDict);
|
||||
var actual = headers.keys();
|
||||
checkIteratorProperties(actual);
|
||||
|
||||
sortedHeaderKeys.forEach(function(key) {
|
||||
const entry = actual.next();
|
||||
assert_false(entry.done);
|
||||
assert_equals(entry.value, key);
|
||||
});
|
||||
assert_true(actual.next().done);
|
||||
assert_true(actual.next().done);
|
||||
|
||||
for (const key of headers.keys())
|
||||
assert_true(sortedHeaderKeys.indexOf(key) != -1);
|
||||
}, "Check keys method");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerEntriesDict);
|
||||
var actual = headers.values();
|
||||
checkIteratorProperties(actual);
|
||||
|
||||
sortedHeaderKeys.forEach(function(key) {
|
||||
const entry = actual.next();
|
||||
assert_false(entry.done);
|
||||
assert_equals(entry.value, sortedHeaderDict[key]);
|
||||
});
|
||||
assert_true(actual.next().done);
|
||||
assert_true(actual.next().done);
|
||||
|
||||
for (const value of headers.values())
|
||||
assert_true(headerValues.indexOf(value) != -1);
|
||||
}, "Check values method");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerEntriesDict);
|
||||
var actual = headers.entries();
|
||||
checkIteratorProperties(actual);
|
||||
|
||||
sortedHeaderKeys.forEach(function(key) {
|
||||
const entry = actual.next();
|
||||
assert_false(entry.done);
|
||||
assert_equals(entry.value[0], key);
|
||||
assert_equals(entry.value[1], sortedHeaderDict[key]);
|
||||
});
|
||||
assert_true(actual.next().done);
|
||||
assert_true(actual.next().done);
|
||||
|
||||
for (const entry of headers.entries())
|
||||
assert_equals(entry[1], sortedHeaderDict[entry[0]]);
|
||||
}, "Check entries method");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerEntriesDict);
|
||||
var actual = headers[Symbol.iterator]();
|
||||
|
||||
sortedHeaderKeys.forEach(function(key) {
|
||||
const entry = actual.next();
|
||||
assert_false(entry.done);
|
||||
assert_equals(entry.value[0], key);
|
||||
assert_equals(entry.value[1], sortedHeaderDict[key]);
|
||||
});
|
||||
assert_true(actual.next().done);
|
||||
assert_true(actual.next().done);
|
||||
}, "Check Symbol.iterator method");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerEntriesDict);
|
||||
var reference = sortedHeaderKeys[Symbol.iterator]();
|
||||
headers.forEach(function(value, key, container) {
|
||||
assert_equals(headers, container);
|
||||
const entry = reference.next();
|
||||
assert_false(entry.done);
|
||||
assert_equals(key, entry.value);
|
||||
assert_equals(value, sortedHeaderDict[entry.value]);
|
||||
});
|
||||
assert_true(reference.next().done);
|
||||
}, "Check forEach method");
|
||||
|
||||
test(() => {
|
||||
const headers = new Headers({"foo": "2", "baz": "1", "BAR": "0"});
|
||||
const actualKeys = [];
|
||||
const actualValues = [];
|
||||
for (const [header, value] of headers) {
|
||||
actualKeys.push(header);
|
||||
actualValues.push(value);
|
||||
headers.delete("foo");
|
||||
}
|
||||
assert_array_equals(actualKeys, ["bar", "baz"]);
|
||||
assert_array_equals(actualValues, ["0", "1"]);
|
||||
}, "Iteration skips elements removed while iterating");
|
||||
|
||||
test(() => {
|
||||
const headers = new Headers({"foo": "2", "baz": "1", "BAR": "0", "quux": "3"});
|
||||
const actualKeys = [];
|
||||
const actualValues = [];
|
||||
for (const [header, value] of headers) {
|
||||
actualKeys.push(header);
|
||||
actualValues.push(value);
|
||||
if (header === "baz")
|
||||
headers.delete("bar");
|
||||
}
|
||||
assert_array_equals(actualKeys, ["bar", "baz", "quux"]);
|
||||
assert_array_equals(actualValues, ["0", "1", "3"]);
|
||||
}, "Removing elements already iterated over causes an element to be skipped during iteration");
|
||||
|
||||
test(() => {
|
||||
const headers = new Headers({"foo": "2", "baz": "1", "BAR": "0", "quux": "3"});
|
||||
const actualKeys = [];
|
||||
const actualValues = [];
|
||||
for (const [header, value] of headers) {
|
||||
actualKeys.push(header);
|
||||
actualValues.push(value);
|
||||
if (header === "baz")
|
||||
headers.append("X-yZ", "4");
|
||||
}
|
||||
assert_array_equals(actualKeys, ["bar", "baz", "foo", "quux", "x-yz"]);
|
||||
assert_array_equals(actualValues, ["0", "1", "2", "3", "4"]);
|
||||
}, "Appending a value pair during iteration causes it to be reached during iteration");
|
||||
|
||||
test(() => {
|
||||
const headers = new Headers({"foo": "2", "baz": "1", "BAR": "0", "quux": "3"});
|
||||
const actualKeys = [];
|
||||
const actualValues = [];
|
||||
for (const [header, value] of headers) {
|
||||
actualKeys.push(header);
|
||||
actualValues.push(value);
|
||||
if (header === "baz")
|
||||
headers.append("abc", "-1");
|
||||
}
|
||||
assert_array_equals(actualKeys, ["bar", "baz", "baz", "foo", "quux"]);
|
||||
assert_array_equals(actualValues, ["0", "1", "1", "2", "3"]);
|
||||
}, "Prepending a value pair before the current element position causes it to be skipped during iteration and adds the current element a second time");
|
||||
@@ -0,0 +1,54 @@
|
||||
// META: title=Headers case management
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
var headerDictCase = {"UPPERCASE": "value1",
|
||||
"lowercase": "value2",
|
||||
"mixedCase": "value3",
|
||||
"Content-TYPE": "value4"
|
||||
};
|
||||
|
||||
function checkHeadersCase(originalName, headersToCheck, expectedDict) {
|
||||
var lowCaseName = originalName.toLowerCase();
|
||||
var upCaseName = originalName.toUpperCase();
|
||||
var expectedValue = expectedDict[originalName];
|
||||
assert_equals(headersToCheck.get(originalName), expectedValue,
|
||||
"name: " + originalName + " has value: " + expectedValue);
|
||||
assert_equals(headersToCheck.get(lowCaseName), expectedValue,
|
||||
"name: " + lowCaseName + " has value: " + expectedValue);
|
||||
assert_equals(headersToCheck.get(upCaseName), expectedValue,
|
||||
"name: " + upCaseName + " has value: " + expectedValue);
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerDictCase);
|
||||
for (const name in headerDictCase)
|
||||
checkHeadersCase(name, headers, headerDictCase)
|
||||
}, "Create headers, names use characters with different case");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
for (const name in headerDictCase) {
|
||||
headers.append(name, headerDictCase[name]);
|
||||
checkHeadersCase(name, headers, headerDictCase);
|
||||
}
|
||||
}, "Check append method, names use characters with different case");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
for (const name in headerDictCase) {
|
||||
headers.set(name, headerDictCase[name]);
|
||||
checkHeadersCase(name, headers, headerDictCase);
|
||||
}
|
||||
}, "Check set method, names use characters with different case");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
for (const name in headerDictCase)
|
||||
headers.set(name, headerDictCase[name]);
|
||||
for (const name in headerDictCase)
|
||||
headers.delete(name.toLowerCase());
|
||||
for (const name in headerDictCase)
|
||||
assert_false(headers.has(name), "header " + name + " should have been deleted");
|
||||
}, "Check delete method, names use characters with different case");
|
||||
@@ -0,0 +1,66 @@
|
||||
// META: title=Headers have combined (and sorted) values
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
var headerSeqCombine = [["single", "singleValue"],
|
||||
["double", "doubleValue1"],
|
||||
["double", "doubleValue2"],
|
||||
["triple", "tripleValue1"],
|
||||
["triple", "tripleValue2"],
|
||||
["triple", "tripleValue3"]
|
||||
];
|
||||
var expectedDict = {"single": "singleValue",
|
||||
"double": "doubleValue1, doubleValue2",
|
||||
"triple": "tripleValue1, tripleValue2, tripleValue3"
|
||||
};
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerSeqCombine);
|
||||
for (const name in expectedDict)
|
||||
assert_equals(headers.get(name), expectedDict[name]);
|
||||
}, "Create headers using same name for different values");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerSeqCombine);
|
||||
for (const name in expectedDict) {
|
||||
assert_true(headers.has(name), "name: " + name + " has value(s)");
|
||||
headers.delete(name);
|
||||
assert_false(headers.has(name), "name: " + name + " has no value(s) anymore");
|
||||
}
|
||||
}, "Check delete and has methods when using same name for different values");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerSeqCombine);
|
||||
for (const name in expectedDict) {
|
||||
headers.set(name,"newSingleValue");
|
||||
assert_equals(headers.get(name), "newSingleValue", "name: " + name + " has value: newSingleValue");
|
||||
}
|
||||
}, "Check set methods when called with already used name");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers(headerSeqCombine);
|
||||
for (const name in expectedDict) {
|
||||
var value = headers.get(name);
|
||||
headers.append(name,"newSingleValue");
|
||||
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")
|
||||
@@ -0,0 +1,96 @@
|
||||
// META: title=Headers errors
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
test(function() {
|
||||
assert_throws_js(TypeError, function() { new Headers([["name"]]); });
|
||||
}, "Create headers giving an array having one string as init argument");
|
||||
|
||||
test(function() {
|
||||
assert_throws_js(TypeError, function() { new Headers([["invalid", "invalidValue1", "invalidValue2"]]); });
|
||||
}, "Create headers giving an array having three strings as init argument");
|
||||
|
||||
test(function() {
|
||||
assert_throws_js(TypeError, function() { new Headers([["invalidĀ", "Value1"]]); });
|
||||
}, "Create headers giving bad header name as init argument");
|
||||
|
||||
test(function() {
|
||||
assert_throws_js(TypeError, function() { new Headers([["name", "invalidValueĀ"]]); });
|
||||
}, "Create headers giving bad header value as init argument");
|
||||
|
||||
var badNames = ["invalidĀ", {}];
|
||||
var badValues = ["invalidĀ"];
|
||||
|
||||
badNames.forEach(function(name) {
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
assert_throws_js(TypeError, function() { headers.get(name); });
|
||||
}, "Check headers get with an invalid name " + name);
|
||||
});
|
||||
|
||||
badNames.forEach(function(name) {
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
assert_throws_js(TypeError, function() { headers.delete(name); });
|
||||
}, "Check headers delete with an invalid name " + name);
|
||||
});
|
||||
|
||||
badNames.forEach(function(name) {
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
assert_throws_js(TypeError, function() { headers.has(name); });
|
||||
}, "Check headers has with an invalid name " + name);
|
||||
});
|
||||
|
||||
badNames.forEach(function(name) {
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
assert_throws_js(TypeError, function() { headers.set(name, "Value1"); });
|
||||
}, "Check headers set with an invalid name " + name);
|
||||
});
|
||||
|
||||
badValues.forEach(function(value) {
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
assert_throws_js(TypeError, function() { headers.set("name", value); });
|
||||
}, "Check headers set with an invalid value " + value);
|
||||
});
|
||||
|
||||
badNames.forEach(function(name) {
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
assert_throws_js(TypeError, function() { headers.append("invalidĀ", "Value1"); });
|
||||
}, "Check headers append with an invalid name " + name);
|
||||
});
|
||||
|
||||
badValues.forEach(function(value) {
|
||||
test(function() {
|
||||
var headers = new Headers();
|
||||
assert_throws_js(TypeError, function() { headers.append("name", value); });
|
||||
}, "Check headers append with an invalid value " + value);
|
||||
});
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers([["name", "value"]]);
|
||||
assert_throws_js(TypeError, function() { headers.forEach(); });
|
||||
assert_throws_js(TypeError, function() { headers.forEach(undefined); });
|
||||
assert_throws_js(TypeError, function() { headers.forEach(1); });
|
||||
}, "Headers forEach throws if argument is not callable");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers([["name1", "value1"], ["name2", "value2"], ["name3", "value3"]]);
|
||||
var counter = 0;
|
||||
try {
|
||||
headers.forEach(function(value, name) {
|
||||
counter++;
|
||||
if (name == "name2")
|
||||
throw "error";
|
||||
});
|
||||
} catch (e) {
|
||||
assert_equals(counter, 2);
|
||||
assert_equals(e, "error");
|
||||
return;
|
||||
}
|
||||
assert_unreached();
|
||||
}, "Headers forEach loop should stop if callback is throwing exception");
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// META: global=window,worker
|
||||
|
||||
let forbiddenMethods = [
|
||||
"TRACE",
|
||||
"TRACK",
|
||||
"CONNECT",
|
||||
"trace",
|
||||
"track",
|
||||
"connect",
|
||||
"\rtrace",
|
||||
"\ttrack",
|
||||
"\nconnect",
|
||||
"trace,",
|
||||
"GET,track ",
|
||||
" connect",
|
||||
];
|
||||
|
||||
let overrideHeaders = [
|
||||
"x-http-method-override",
|
||||
"x-http-method",
|
||||
"x-method-override",
|
||||
"X-HTTP-METHOD-OVERRIDE",
|
||||
"X-HTTP-METHOD",
|
||||
"X-METHOD-OVERRIDE",
|
||||
];
|
||||
|
||||
for (forbiddenMethod of forbiddenMethods) {
|
||||
for (overrideHeader of overrideHeaders) {
|
||||
test(() => {
|
||||
let r = new Request("https://site.example/");
|
||||
r.headers.append(overrideHeader, forbiddenMethod);
|
||||
assert_false(r.headers.has(overrideHeader));
|
||||
}, `header ${overrideHeader} is forbidden to use value ${forbiddenMethod}`);
|
||||
}
|
||||
}
|
||||
|
||||
let permittedValues = [
|
||||
"GETTRACE",
|
||||
"GET",
|
||||
"\",TRACE\",",
|
||||
];
|
||||
|
||||
for (permittedValue of permittedValues) {
|
||||
for (overrideHeader of overrideHeaders) {
|
||||
test(() => {
|
||||
let r = new Request("https://site.example/");
|
||||
r.headers.append(overrideHeader, permittedValue);
|
||||
assert_equals(permittedValue, r.headers.get(overrideHeader));
|
||||
}, `header ${overrideHeader} is allowed to use value ${permittedValue}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
promise_test(() => fetch("../cors/resources/not-cors-safelisted.json").then(res => res.json().then(runTests)), "Loading data…");
|
||||
|
||||
const longValue = "s".repeat(127);
|
||||
|
||||
[
|
||||
{
|
||||
"headers": ["accept", "accept-language", "content-language"],
|
||||
"values": [longValue, "", longValue]
|
||||
},
|
||||
{
|
||||
"headers": ["accept", "accept-language", "content-language"],
|
||||
"values": ["", longValue]
|
||||
},
|
||||
{
|
||||
"headers": ["content-type"],
|
||||
"values": ["text/plain;" + "s".repeat(116), "text/plain"]
|
||||
}
|
||||
].forEach(testItem => {
|
||||
testItem.headers.forEach(header => {
|
||||
test(() => {
|
||||
const noCorsHeaders = new Request("about:blank", { mode: "no-cors" }).headers;
|
||||
testItem.values.forEach((value) => {
|
||||
noCorsHeaders.append(header, value);
|
||||
assert_equals(noCorsHeaders.get(header), testItem.values[0], '1');
|
||||
});
|
||||
noCorsHeaders.set(header, testItem.values.join(", "));
|
||||
assert_equals(noCorsHeaders.get(header), testItem.values[0], '2');
|
||||
noCorsHeaders.delete(header);
|
||||
assert_false(noCorsHeaders.has(header));
|
||||
}, "\"no-cors\" Headers object cannot have " + header + " set to " + testItem.values.join(", "));
|
||||
});
|
||||
});
|
||||
|
||||
function runTests(testArray) {
|
||||
testArray = testArray.concat([
|
||||
["dpr", "2"],
|
||||
["rtt", "1.0"],
|
||||
["downlink", "-1.0"],
|
||||
["ect", "6g"],
|
||||
["save-data", "on"],
|
||||
["viewport-width", "100"],
|
||||
["width", "100"],
|
||||
["unknown", "doesitmatter"]
|
||||
]);
|
||||
testArray.forEach(testItem => {
|
||||
const [headerName, headerValue] = testItem;
|
||||
test(() => {
|
||||
const noCorsHeaders = new Request("about:blank", { mode: "no-cors" }).headers;
|
||||
noCorsHeaders.append(headerName, headerValue);
|
||||
assert_false(noCorsHeaders.has(headerName));
|
||||
noCorsHeaders.set(headerName, headerValue);
|
||||
assert_false(noCorsHeaders.has(headerName));
|
||||
}, "\"no-cors\" Headers object cannot have " + headerName + "/" + headerValue + " as header");
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// META: title=Headers normalize values
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
const expectations = {
|
||||
"name1": [" space ", "space"],
|
||||
"name2": ["\ttab\t", "tab"],
|
||||
"name3": [" spaceAndTab\t", "spaceAndTab"],
|
||||
"name4": ["\r\n newLine", "newLine"], //obs-fold cases
|
||||
"name5": ["newLine\r\n ", "newLine"],
|
||||
"name6": ["\r\n\tnewLine", "newLine"],
|
||||
"name7": ["\t\f\tnewLine\n", "\f\tnewLine"],
|
||||
"name8": ["newLine\xa0", "newLine\xa0"], // \xa0 == non breaking space
|
||||
};
|
||||
|
||||
test(function () {
|
||||
const headerDict = Object.fromEntries(
|
||||
Object.entries(expectations).map(([name, [actual]]) => [name, actual]),
|
||||
);
|
||||
var headers = new Headers(headerDict);
|
||||
for (const name in expectations) {
|
||||
const expected = expectations[name][1];
|
||||
assert_equals(
|
||||
headers.get(name),
|
||||
expected,
|
||||
"name: " + name + " has normalized value: " + expected,
|
||||
);
|
||||
}
|
||||
}, "Create headers with not normalized values");
|
||||
|
||||
test(function () {
|
||||
var headers = new Headers();
|
||||
for (const name in expectations) {
|
||||
headers.append(name, expectations[name][0]);
|
||||
const expected = expectations[name][1];
|
||||
assert_equals(
|
||||
headers.get(name),
|
||||
expected,
|
||||
"name: " + name + " has value: " + expected,
|
||||
);
|
||||
}
|
||||
}, "Check append method with not normalized values");
|
||||
|
||||
test(function () {
|
||||
var headers = new Headers();
|
||||
for (const name in expectations) {
|
||||
headers.set(name, expectations[name][0]);
|
||||
const expected = expectations[name][1];
|
||||
assert_equals(
|
||||
headers.get(name),
|
||||
expected,
|
||||
"name: " + name + " has value: " + expected,
|
||||
);
|
||||
}
|
||||
}, "Check set method with not normalized values");
|
||||
@@ -0,0 +1,357 @@
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
var log = [];
|
||||
function clearLog() {
|
||||
log = [];
|
||||
}
|
||||
function addLogEntry(name, args) {
|
||||
log.push([ name, ...args ]);
|
||||
}
|
||||
|
||||
var loggingHandler = {
|
||||
};
|
||||
|
||||
setup(function() {
|
||||
for (let prop of Object.getOwnPropertyNames(Reflect)) {
|
||||
loggingHandler[prop] = function(...args) {
|
||||
addLogEntry(prop, args);
|
||||
return Reflect[prop](...args);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test(function() {
|
||||
var h = new Headers();
|
||||
assert_equals([...h].length, 0);
|
||||
}, "Passing nothing to Headers constructor");
|
||||
|
||||
test(function() {
|
||||
var h = new Headers(undefined);
|
||||
assert_equals([...h].length, 0);
|
||||
}, "Passing undefined to Headers constructor");
|
||||
|
||||
test(function() {
|
||||
assert_throws_js(TypeError, function() {
|
||||
var h = new Headers(null);
|
||||
});
|
||||
}, "Passing null to Headers constructor");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var record = { a: "b" };
|
||||
var proxy = new Proxy(record, loggingHandler);
|
||||
var h = new Headers(proxy);
|
||||
|
||||
assert_equals(log.length, 4);
|
||||
// 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", record, Symbol.iterator, proxy]);
|
||||
// Then we have the [[OwnPropertyKeys]] from
|
||||
// https://webidl.spec.whatwg.org/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", record]);
|
||||
// Then the [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[2], ["getOwnPropertyDescriptor", record, "a"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[3], ["get", record, "a", proxy]);
|
||||
|
||||
// Check the results.
|
||||
assert_equals([...h].length, 1);
|
||||
assert_array_equals([...h.keys()], ["a"]);
|
||||
assert_true(h.has("a"));
|
||||
assert_equals(h.get("a"), "b");
|
||||
}, "Basic operation with one property");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var recordProto = { c: "d" };
|
||||
var record = Object.create(recordProto, { a: { value: "b", enumerable: true } });
|
||||
var proxy = new Proxy(record, loggingHandler);
|
||||
var h = new Headers(proxy);
|
||||
|
||||
assert_equals(log.length, 4);
|
||||
// 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", record, Symbol.iterator, proxy]);
|
||||
// Then we have the [[OwnPropertyKeys]] from
|
||||
// https://webidl.spec.whatwg.org/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", record]);
|
||||
// Then the [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[2], ["getOwnPropertyDescriptor", record, "a"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[3], ["get", record, "a", proxy]);
|
||||
|
||||
// Check the results.
|
||||
assert_equals([...h].length, 1);
|
||||
assert_array_equals([...h.keys()], ["a"]);
|
||||
assert_true(h.has("a"));
|
||||
assert_equals(h.get("a"), "b");
|
||||
}, "Basic operation with one property and a proto");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var record = { a: "b", c: "d" };
|
||||
var proxy = new Proxy(record, loggingHandler);
|
||||
var h = new Headers(proxy);
|
||||
|
||||
assert_equals(log.length, 6);
|
||||
// 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", record, Symbol.iterator, proxy]);
|
||||
// Then we have the [[OwnPropertyKeys]] from
|
||||
// https://webidl.spec.whatwg.org/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", record]);
|
||||
// Then the [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[2], ["getOwnPropertyDescriptor", record, "a"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[3], ["get", record, "a", proxy]);
|
||||
// Then the second [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[4], ["getOwnPropertyDescriptor", record, "c"]);
|
||||
// Then the second [[Get]] from step 5.2.
|
||||
assert_array_equals(log[5], ["get", record, "c", proxy]);
|
||||
|
||||
// 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"), "b");
|
||||
assert_true(h.has("c"));
|
||||
assert_equals(h.get("c"), "d");
|
||||
}, "Correct operation ordering with two properties");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var record = { a: "b", "\uFFFF": "d" };
|
||||
var proxy = new Proxy(record, loggingHandler);
|
||||
assert_throws_js(TypeError, function() {
|
||||
var h = new Headers(proxy);
|
||||
});
|
||||
|
||||
assert_equals(log.length, 5);
|
||||
// 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", record, Symbol.iterator, proxy]);
|
||||
// Then we have the [[OwnPropertyKeys]] from
|
||||
// https://webidl.spec.whatwg.org/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", record]);
|
||||
// Then the [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[2], ["getOwnPropertyDescriptor", record, "a"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[3], ["get", record, "a", proxy]);
|
||||
// Then the second [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[4], ["getOwnPropertyDescriptor", record, "\uFFFF"]);
|
||||
// The second [[Get]] never happens, because we convert the invalid name to a
|
||||
// ByteString first and throw.
|
||||
}, "Correct operation ordering with two properties one of which has an invalid name");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var record = { a: "\uFFFF", c: "d" }
|
||||
var proxy = new Proxy(record, loggingHandler);
|
||||
assert_throws_js(TypeError, function() {
|
||||
var h = new Headers(proxy);
|
||||
});
|
||||
|
||||
assert_equals(log.length, 4);
|
||||
// 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", record, Symbol.iterator, proxy]);
|
||||
// Then we have the [[OwnPropertyKeys]] from
|
||||
// https://webidl.spec.whatwg.org/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", record]);
|
||||
// Then the [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[2], ["getOwnPropertyDescriptor", record, "a"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[3], ["get", record, "a", proxy]);
|
||||
// Nothing else after this, because converting the result of that [[Get]] to a
|
||||
// ByteString throws.
|
||||
}, "Correct operation ordering with two properties one of which has an invalid value");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var record = {};
|
||||
Object.defineProperty(record, "a", { value: "b", enumerable: false });
|
||||
Object.defineProperty(record, "c", { value: "d", enumerable: true });
|
||||
Object.defineProperty(record, "e", { value: "f", enumerable: false });
|
||||
var proxy = new Proxy(record, loggingHandler);
|
||||
var h = new Headers(proxy);
|
||||
|
||||
assert_equals(log.length, 6);
|
||||
// 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", record, Symbol.iterator, proxy]);
|
||||
// Then we have the [[OwnPropertyKeys]] from
|
||||
// https://webidl.spec.whatwg.org/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", record]);
|
||||
// Then the [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[2], ["getOwnPropertyDescriptor", record, "a"]);
|
||||
// No [[Get]] because not enumerable
|
||||
// Then the second [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[3], ["getOwnPropertyDescriptor", record, "c"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[4], ["get", record, "c", proxy]);
|
||||
// Then the third [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[5], ["getOwnPropertyDescriptor", record, "e"]);
|
||||
// No [[Get]] because not enumerable
|
||||
|
||||
// Check the results.
|
||||
assert_equals([...h].length, 1);
|
||||
assert_array_equals([...h.keys()], ["c"]);
|
||||
assert_true(h.has("c"));
|
||||
assert_equals(h.get("c"), "d");
|
||||
}, "Correct operation ordering with non-enumerable properties");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var record = {a: "b", c: "d", e: "f"};
|
||||
var lyingHandler = {
|
||||
getOwnPropertyDescriptor: function(target, name) {
|
||||
if (name == "a" || name == "e") {
|
||||
return undefined;
|
||||
}
|
||||
return Reflect.getOwnPropertyDescriptor(target, name);
|
||||
}
|
||||
};
|
||||
var lyingProxy = new Proxy(record, lyingHandler);
|
||||
var proxy = new Proxy(lyingProxy, loggingHandler);
|
||||
var h = new Headers(proxy);
|
||||
|
||||
assert_equals(log.length, 6);
|
||||
// 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://webidl.spec.whatwg.org/#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"]);
|
||||
// No [[Get]] because no descriptor
|
||||
// Then the second [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[3], ["getOwnPropertyDescriptor", lyingProxy, "c"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[4], ["get", lyingProxy, "c", proxy]);
|
||||
// Then the third [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[5], ["getOwnPropertyDescriptor", lyingProxy, "e"]);
|
||||
// No [[Get]] because no descriptor
|
||||
|
||||
// Check the results.
|
||||
assert_equals([...h].length, 1);
|
||||
assert_array_equals([...h.keys()], ["c"]);
|
||||
assert_true(h.has("c"));
|
||||
assert_equals(h.get("c"), "d");
|
||||
}, "Correct operation ordering with undefined descriptors");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var record = {a: "b", c: "d"};
|
||||
var lyingHandler = {
|
||||
ownKeys: function() {
|
||||
return [ "a", "c", "a", "c" ];
|
||||
},
|
||||
};
|
||||
var lyingProxy = new Proxy(record, lyingHandler);
|
||||
var proxy = new Proxy(lyingProxy, loggingHandler);
|
||||
|
||||
// Returning duplicate keys from ownKeys() throws a TypeError.
|
||||
assert_throws_js(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://webidl.spec.whatwg.org/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", lyingProxy]);
|
||||
}, "Correct operation ordering with repeated keys");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var record = {
|
||||
a: "b",
|
||||
[Symbol.toStringTag]: {
|
||||
// Make sure the ToString conversion of the value happens
|
||||
// after the ToString conversion of the key.
|
||||
toString: function () { addLogEntry("toString", [this]); return "nope"; }
|
||||
},
|
||||
c: "d" };
|
||||
var proxy = new Proxy(record, loggingHandler);
|
||||
assert_throws_js(TypeError,
|
||||
function() { var h = new Headers(proxy); });
|
||||
|
||||
assert_equals(log.length, 7);
|
||||
// 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", record, Symbol.iterator, proxy]);
|
||||
// Then we have the [[OwnPropertyKeys]] from
|
||||
// https://webidl.spec.whatwg.org/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", record]);
|
||||
// Then the [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[2], ["getOwnPropertyDescriptor", record, "a"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[3], ["get", record, "a", proxy]);
|
||||
// Then the second [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[4], ["getOwnPropertyDescriptor", record, "c"]);
|
||||
// Then the second [[Get]] from step 5.2.
|
||||
assert_array_equals(log[5], ["get", record, "c", proxy]);
|
||||
// Then the third [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[6], ["getOwnPropertyDescriptor", record,
|
||||
Symbol.toStringTag]);
|
||||
// Then we throw an exception converting the Symbol to a string, before we do
|
||||
// the third [[Get]].
|
||||
}, "Basic operation with Symbol keys");
|
||||
|
||||
test(function() {
|
||||
this.add_cleanup(clearLog);
|
||||
var record = {
|
||||
a: {
|
||||
toString: function() { addLogEntry("toString", [this]); return "b"; }
|
||||
},
|
||||
[Symbol.toStringTag]: {
|
||||
toString: function () { addLogEntry("toString", [this]); return "nope"; }
|
||||
},
|
||||
c: {
|
||||
toString: function() { addLogEntry("toString", [this]); return "d"; }
|
||||
}
|
||||
};
|
||||
// Now make that Symbol-named property not enumerable.
|
||||
Object.defineProperty(record, Symbol.toStringTag, { enumerable: false });
|
||||
assert_array_equals(Reflect.ownKeys(record),
|
||||
["a", "c", Symbol.toStringTag]);
|
||||
|
||||
var proxy = new Proxy(record, loggingHandler);
|
||||
var h = new Headers(proxy);
|
||||
|
||||
assert_equals(log.length, 9);
|
||||
// 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", record, Symbol.iterator, proxy]);
|
||||
// Then we have the [[OwnPropertyKeys]] from
|
||||
// https://webidl.spec.whatwg.org/#es-to-record step 4.
|
||||
assert_array_equals(log[1], ["ownKeys", record]);
|
||||
// Then the [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[2], ["getOwnPropertyDescriptor", record, "a"]);
|
||||
// Then the [[Get]] from step 5.2.
|
||||
assert_array_equals(log[3], ["get", record, "a", proxy]);
|
||||
// Then the ToString on the value.
|
||||
assert_array_equals(log[4], ["toString", record.a]);
|
||||
// Then the second [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[5], ["getOwnPropertyDescriptor", record, "c"]);
|
||||
// Then the second [[Get]] from step 5.2.
|
||||
assert_array_equals(log[6], ["get", record, "c", proxy]);
|
||||
// Then the ToString on the value.
|
||||
assert_array_equals(log[7], ["toString", record.c]);
|
||||
// Then the third [[GetOwnProperty]] from step 5.1.
|
||||
assert_array_equals(log[8], ["getOwnPropertyDescriptor", record,
|
||||
Symbol.toStringTag]);
|
||||
// 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"), "b");
|
||||
assert_true(h.has("c"));
|
||||
assert_equals(h.get("c"), "d");
|
||||
}, "Operation with non-enumerable Symbol keys");
|
||||
@@ -0,0 +1,20 @@
|
||||
// META: title=Headers basic
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
var headers = new Headers();
|
||||
var methods = ["append",
|
||||
"delete",
|
||||
"get",
|
||||
"has",
|
||||
"set",
|
||||
//Headers is iterable
|
||||
"entries",
|
||||
"keys",
|
||||
"values"
|
||||
];
|
||||
for (var idx in methods)
|
||||
test(function() {
|
||||
assert_true(methods[idx] in headers, "headers has " + methods[idx] + " method");
|
||||
}, "Headers has " + methods[idx] + " method");
|
||||
@@ -0,0 +1,13 @@
|
||||
// META: global=window,worker
|
||||
|
||||
// https://fetch.spec.whatwg.org/#forbidden-method
|
||||
for (const method of [
|
||||
'CONNECT', 'TRACE', 'TRACK',
|
||||
'connect', 'trace', 'track'
|
||||
]) {
|
||||
test(function() {
|
||||
assert_throws_js(TypeError,
|
||||
function() { new Request('./', {method: method}); }
|
||||
);
|
||||
}, 'Request() with a forbidden method ' + method + ' must throw.');
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
// META: global=window,worker
|
||||
|
||||
promise_test(async () => {
|
||||
const stream = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(new TextEncoder().encode("hello"));
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
|
||||
const r1 = new Request("https://example.com/", { method: "POST", body: stream, duplex: "half" });
|
||||
const r2 = r1.clone();
|
||||
assert_false(r2.bodyUsed, "clone should not be marked as used");
|
||||
assert_not_equals(r2.body, null, "clone should have a body");
|
||||
|
||||
// Constructing a new Request from the clone should preserve the body.
|
||||
const r3 = new Request(r2);
|
||||
assert_not_equals(r3.body, null, "Request constructed from clone should have a body");
|
||||
|
||||
const text = await r3.text();
|
||||
assert_equals(text, "hello", "body content should be preserved through clone() and new Request()");
|
||||
}, "new Request(clone) preserves a ReadableStream body that came from clone()");
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
promise_test(async function () {
|
||||
const req1 = new Request("https://example.com/", {
|
||||
body: "req1",
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
const text1 = await req1.text();
|
||||
assert_equals(
|
||||
text1,
|
||||
"req1",
|
||||
"The body of the first request should be 'req1'."
|
||||
);
|
||||
|
||||
const req2 = new Request(req1, { body: "req2" });
|
||||
const bodyText = await req2.text();
|
||||
assert_equals(
|
||||
bodyText,
|
||||
"req2",
|
||||
"The body of the second request should be overridden to 'req2'."
|
||||
);
|
||||
|
||||
}, "Check that the body of a new request can be overridden when created from an existing Request object");
|
||||
|
||||
promise_test(async function () {
|
||||
const req1 = new Request("https://example.com/", {
|
||||
body: "req1",
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
const req2 = new Request("https://example.com/", req1);
|
||||
const bodyText = await req2.text();
|
||||
assert_equals(
|
||||
bodyText,
|
||||
"req1",
|
||||
"The body of the second request should be the same as the first."
|
||||
);
|
||||
}, "Check that the body of a new request can be duplicated from an existing Request object");
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Request consume empty bodies
|
||||
|
||||
function checkBodyText(test, request) {
|
||||
return request.text().then(function(bodyAsText) {
|
||||
assert_equals(bodyAsText, "", "Resolved value should be empty");
|
||||
assert_false(request.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
async function checkBodyBlob(test, request) {
|
||||
const bodyAsBlob = await request.blob();
|
||||
const body = await bodyAsBlob.text();
|
||||
assert_equals(body, "", "Resolved value should be empty");
|
||||
assert_false(request.bodyUsed);
|
||||
}
|
||||
|
||||
function checkBodyArrayBuffer(test, request) {
|
||||
return request.arrayBuffer().then(function(bodyAsArrayBuffer) {
|
||||
assert_equals(bodyAsArrayBuffer.byteLength, 0, "Resolved value should be empty");
|
||||
assert_false(request.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
function checkBodyJSON(test, request) {
|
||||
return request.json().then(
|
||||
function(bodyAsJSON) {
|
||||
assert_unreached("JSON parsing should fail");
|
||||
},
|
||||
function() {
|
||||
assert_false(request.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
function checkBodyFormData(test, request) {
|
||||
return request.formData().then(function(bodyAsFormData) {
|
||||
assert_true(bodyAsFormData instanceof FormData, "Should receive a FormData");
|
||||
assert_false(request.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
function checkBodyFormDataError(test, request) {
|
||||
return promise_rejects_js(test, TypeError, request.formData()).then(function() {
|
||||
assert_false(request.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
function checkRequestWithNoBody(bodyType, checkFunction, headers = []) {
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"method": "POST", "headers": headers});
|
||||
assert_false(request.bodyUsed);
|
||||
return checkFunction(test, request);
|
||||
}, "Consume request's body as " + bodyType);
|
||||
}
|
||||
|
||||
checkRequestWithNoBody("text", checkBodyText);
|
||||
checkRequestWithNoBody("blob", checkBodyBlob);
|
||||
checkRequestWithNoBody("arrayBuffer", checkBodyArrayBuffer);
|
||||
checkRequestWithNoBody("json (error case)", checkBodyJSON);
|
||||
checkRequestWithNoBody("formData with correct multipart type (error case)", checkBodyFormDataError, [["Content-Type", 'multipart/form-data; boundary="boundary"']]);
|
||||
checkRequestWithNoBody("formData with correct urlencoded type", checkBodyFormData, [["Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"]]);
|
||||
checkRequestWithNoBody("formData without correct type (error case)", checkBodyFormDataError);
|
||||
|
||||
function checkRequestWithEmptyBody(bodyType, body, asText) {
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"method": "POST", "body": body});
|
||||
assert_false(request.bodyUsed, "bodyUsed is false at init");
|
||||
if (asText) {
|
||||
return request.text().then(function(bodyAsString) {
|
||||
assert_equals(bodyAsString.length, 0, "Resolved value should be empty");
|
||||
assert_true(request.bodyUsed, "bodyUsed is true after being consumed");
|
||||
});
|
||||
}
|
||||
return request.arrayBuffer().then(function(bodyAsArrayBuffer) {
|
||||
assert_equals(bodyAsArrayBuffer.byteLength, 0, "Resolved value should be empty");
|
||||
assert_true(request.bodyUsed, "bodyUsed is true after being consumed");
|
||||
});
|
||||
}, "Consume empty " + bodyType + " request body as " + (asText ? "text" : "arrayBuffer"));
|
||||
}
|
||||
|
||||
// FIXME: Add BufferSource, FormData and URLSearchParams.
|
||||
checkRequestWithEmptyBody("blob", new Blob([], { "type" : "text/plain" }), false);
|
||||
checkRequestWithEmptyBody("text", "", false);
|
||||
checkRequestWithEmptyBody("blob", new Blob([], { "type" : "text/plain" }), true);
|
||||
checkRequestWithEmptyBody("text", "", true);
|
||||
checkRequestWithEmptyBody("URLSearchParams", new URLSearchParams(""), true);
|
||||
// FIXME: This test assumes that the empty string be returned but it is not clear whether that is right. See https://github.com/web-platform-tests/wpt/pull/3950.
|
||||
checkRequestWithEmptyBody("FormData", new FormData(), true);
|
||||
checkRequestWithEmptyBody("ArrayBuffer", new ArrayBuffer(), true);
|
||||
@@ -0,0 +1,148 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Request consume
|
||||
// META: script=../resources/utils.js
|
||||
|
||||
function checkBodyText(request, expectedBody) {
|
||||
return request.text().then(function(bodyAsText) {
|
||||
assert_equals(bodyAsText, expectedBody, "Retrieve and verify request's body");
|
||||
assert_true(request.bodyUsed, "body as text: bodyUsed turned true");
|
||||
});
|
||||
}
|
||||
|
||||
async function checkBodyBlob(request, expectedBody, checkContentType) {
|
||||
const bodyAsBlob = await request.blob();
|
||||
|
||||
if (checkContentType)
|
||||
assert_equals(bodyAsBlob.type, "text/plain", "Blob body type should be computed from the request Content-Type");
|
||||
|
||||
const body = await bodyAsBlob.text();
|
||||
assert_equals(body, expectedBody, "Retrieve and verify request's body");
|
||||
assert_true(request.bodyUsed, "body as blob: bodyUsed turned true");
|
||||
}
|
||||
|
||||
function checkBodyArrayBuffer(request, expectedBody) {
|
||||
return request.arrayBuffer().then(function(bodyAsArrayBuffer) {
|
||||
validateBufferFromString(bodyAsArrayBuffer, expectedBody, "Retrieve and verify request's body");
|
||||
assert_true(request.bodyUsed, "body as arrayBuffer: bodyUsed turned true");
|
||||
});
|
||||
}
|
||||
|
||||
function checkBodyBytes(request, expectedBody) {
|
||||
return request.bytes().then(function(bodyAsUint8Array) {
|
||||
assert_true(bodyAsUint8Array instanceof Uint8Array);
|
||||
validateBufferFromString(bodyAsUint8Array.buffer, expectedBody, "Retrieve and verify request's body");
|
||||
assert_true(request.bodyUsed, "body as bytes: bodyUsed turned true");
|
||||
});
|
||||
}
|
||||
|
||||
function checkBodyJSON(request, expectedBody) {
|
||||
return request.json().then(function(bodyAsJSON) {
|
||||
var strBody = JSON.stringify(bodyAsJSON)
|
||||
assert_equals(strBody, expectedBody, "Retrieve and verify request's body");
|
||||
assert_true(request.bodyUsed, "body as json: bodyUsed turned true");
|
||||
});
|
||||
}
|
||||
|
||||
function checkBodyFormData(request, expectedBody) {
|
||||
return request.formData().then(function(bodyAsFormData) {
|
||||
assert_true(bodyAsFormData instanceof FormData, "Should receive a FormData");
|
||||
assert_true(request.bodyUsed, "body as formData: bodyUsed turned true");
|
||||
});
|
||||
}
|
||||
|
||||
function checkRequestBody(body, expected, bodyType) {
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"method": "POST", "body": body, "headers": [["Content-Type", "text/PLAIN"]] });
|
||||
assert_false(request.bodyUsed, "bodyUsed is false at init");
|
||||
return checkBodyText(request, expected);
|
||||
}, "Consume " + bodyType + " request's body as text");
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"method": "POST", "body": body });
|
||||
assert_false(request.bodyUsed, "bodyUsed is false at init");
|
||||
return checkBodyBlob(request, expected);
|
||||
}, "Consume " + bodyType + " request's body as blob");
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"method": "POST", "body": body });
|
||||
assert_false(request.bodyUsed, "bodyUsed is false at init");
|
||||
return checkBodyArrayBuffer(request, expected);
|
||||
}, "Consume " + bodyType + " request's body as arrayBuffer");
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"method": "POST", "body": body });
|
||||
assert_false(request.bodyUsed, "bodyUsed is false at init");
|
||||
return checkBodyBytes(request, expected);
|
||||
}, "Consume " + bodyType + " request's body as bytes");
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"method": "POST", "body": body });
|
||||
assert_false(request.bodyUsed, "bodyUsed is false at init");
|
||||
return checkBodyJSON(request, expected);
|
||||
}, "Consume " + bodyType + " request's body as JSON");
|
||||
}
|
||||
|
||||
var textData = JSON.stringify("This is response's body");
|
||||
var blob = new Blob([textData], { "type" : "text/plain" });
|
||||
|
||||
checkRequestBody(textData, textData, "String");
|
||||
|
||||
var string = "\"123456\"";
|
||||
function getArrayBuffer() {
|
||||
var arrayBuffer = new ArrayBuffer(8);
|
||||
var int8Array = new Int8Array(arrayBuffer);
|
||||
for (var cptr = 0; cptr < 8; cptr++)
|
||||
int8Array[cptr] = string.charCodeAt(cptr);
|
||||
return arrayBuffer;
|
||||
}
|
||||
|
||||
function getArrayBufferWithZeros() {
|
||||
var arrayBuffer = new ArrayBuffer(10);
|
||||
var int8Array = new Int8Array(arrayBuffer);
|
||||
for (var cptr = 0; cptr < 8; cptr++)
|
||||
int8Array[cptr + 1] = string.charCodeAt(cptr);
|
||||
return arrayBuffer;
|
||||
}
|
||||
|
||||
checkRequestBody(getArrayBuffer(), string, "ArrayBuffer");
|
||||
checkRequestBody(new Uint8Array(getArrayBuffer()), string, "Uint8Array");
|
||||
checkRequestBody(new Int8Array(getArrayBufferWithZeros(), 1, 8), string, "Int8Array");
|
||||
checkRequestBody(new Float32Array(getArrayBuffer()), string, "Float32Array");
|
||||
checkRequestBody(new DataView(getArrayBufferWithZeros(), 1, 8), string, "DataView");
|
||||
|
||||
promise_test(function(test) {
|
||||
var formData = new FormData();
|
||||
formData.append("name", "value")
|
||||
var request = new Request("", {"method": "POST", "body": formData });
|
||||
assert_false(request.bodyUsed, "bodyUsed is false at init");
|
||||
return checkBodyFormData(request, formData);
|
||||
}, "Consume FormData request's body as FormData");
|
||||
|
||||
function checkBlobResponseBody(blobBody, blobData, bodyType, checkFunction) {
|
||||
promise_test(function(test) {
|
||||
var response = new Response(blobBody);
|
||||
assert_false(response.bodyUsed, "bodyUsed is false at init");
|
||||
return checkFunction(response, blobData);
|
||||
}, "Consume blob response's body as " + bodyType);
|
||||
}
|
||||
|
||||
checkBlobResponseBody(blob, textData, "blob", checkBodyBlob);
|
||||
checkBlobResponseBody(blob, textData, "text", checkBodyText);
|
||||
checkBlobResponseBody(blob, textData, "json", checkBodyJSON);
|
||||
checkBlobResponseBody(blob, textData, "arrayBuffer", checkBodyArrayBuffer);
|
||||
checkBlobResponseBody(blob, textData, "bytes", checkBodyBytes);
|
||||
checkBlobResponseBody(new Blob([""]), "", "blob (empty blob as input)", checkBodyBlob);
|
||||
|
||||
var goodJSONValues = ["null", "1", "true", "\"string\""];
|
||||
goodJSONValues.forEach(function(value) {
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"method": "POST", "body": value});
|
||||
return request.json().then(function(v) {
|
||||
assert_equals(v, JSON.parse(value));
|
||||
});
|
||||
}, "Consume JSON from text: '" + JSON.stringify(value) + "'");
|
||||
});
|
||||
|
||||
var badJSONValues = ["undefined", "{", "a", "["];
|
||||
badJSONValues.forEach(function(value) {
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"method": "POST", "body": value});
|
||||
return promise_rejects_js(test, SyntaxError, request.json());
|
||||
}, "Trying to consume bad JSON text as JSON: '" + value + "'");
|
||||
});
|
||||
@@ -0,0 +1,109 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Request disturbed
|
||||
// META: script=../resources/utils.js
|
||||
|
||||
var initValuesDict = {"method" : "POST",
|
||||
"body" : "Request's body"
|
||||
};
|
||||
|
||||
var noBodyConsumed = new Request("");
|
||||
var bodyConsumed = new Request("", initValuesDict);
|
||||
|
||||
test(() => {
|
||||
assert_equals(noBodyConsumed.body, null, "body's default value is null");
|
||||
assert_false(noBodyConsumed.bodyUsed , "bodyUsed is false when request is not disturbed");
|
||||
assert_not_equals(bodyConsumed.body, null, "non-null body");
|
||||
assert_true(bodyConsumed.body instanceof ReadableStream, "non-null body type");
|
||||
assert_false(noBodyConsumed.bodyUsed, "bodyUsed is false when request is not disturbed");
|
||||
}, "Request's body: initial state");
|
||||
|
||||
noBodyConsumed.blob();
|
||||
bodyConsumed.blob();
|
||||
|
||||
test(function() {
|
||||
assert_false(noBodyConsumed.bodyUsed , "bodyUsed is false when request is not disturbed");
|
||||
try {
|
||||
noBodyConsumed.clone();
|
||||
} catch (e) {
|
||||
assert_unreached("Can use request not disturbed for creating or cloning request");
|
||||
}
|
||||
}, "Request without body cannot be disturbed");
|
||||
|
||||
test(function() {
|
||||
assert_true(bodyConsumed.bodyUsed , "bodyUsed is true when request is disturbed");
|
||||
assert_throws_js(TypeError, function() { bodyConsumed.clone(); });
|
||||
}, "Check cloning a disturbed request");
|
||||
|
||||
test(function() {
|
||||
assert_true(bodyConsumed.bodyUsed , "bodyUsed is true when request is disturbed");
|
||||
assert_throws_js(TypeError, function() { new Request(bodyConsumed); });
|
||||
}, "Check creating a new request from a disturbed request");
|
||||
|
||||
promise_test(function() {
|
||||
assert_true(bodyConsumed.bodyUsed , "bodyUsed is true when request is disturbed");
|
||||
const originalBody = bodyConsumed.body;
|
||||
const bodyReplaced = new Request(bodyConsumed, { body: "Replaced body" });
|
||||
assert_not_equals(bodyReplaced.body, originalBody, "new request's body is new");
|
||||
assert_false(bodyReplaced.bodyUsed, "bodyUsed is false when request is not disturbed");
|
||||
return bodyReplaced.text().then(text => {
|
||||
assert_equals(text, "Replaced body");
|
||||
});
|
||||
}, "Check creating a new request with a new body from a disturbed request");
|
||||
|
||||
promise_test(function() {
|
||||
var bodyRequest = new Request("", initValuesDict);
|
||||
const originalBody = bodyRequest.body;
|
||||
assert_false(bodyRequest.bodyUsed , "bodyUsed is false when request is not disturbed");
|
||||
var requestFromRequest = new Request(bodyRequest);
|
||||
assert_true(bodyRequest.bodyUsed , "bodyUsed is true when request is disturbed");
|
||||
assert_equals(bodyRequest.body, originalBody, "body should not change");
|
||||
assert_not_equals(originalBody, undefined, "body should not be undefined");
|
||||
assert_not_equals(originalBody, null, "body should not be null");
|
||||
assert_not_equals(requestFromRequest.body, originalBody, "new request's body is new");
|
||||
return requestFromRequest.text().then(text => {
|
||||
assert_equals(text, "Request's body");
|
||||
});
|
||||
}, "Input request used for creating new request became disturbed");
|
||||
|
||||
promise_test(() => {
|
||||
const bodyRequest = new Request("", initValuesDict);
|
||||
const originalBody = bodyRequest.body;
|
||||
assert_false(bodyRequest.bodyUsed , "bodyUsed is false when request is not disturbed");
|
||||
const requestFromRequest = new Request(bodyRequest, { body : "init body" });
|
||||
assert_true(bodyRequest.bodyUsed , "bodyUsed is true when request is disturbed");
|
||||
assert_equals(bodyRequest.body, originalBody, "body should not change");
|
||||
assert_not_equals(originalBody, undefined, "body should not be undefined");
|
||||
assert_not_equals(originalBody, null, "body should not be null");
|
||||
assert_not_equals(requestFromRequest.body, originalBody, "new request's body is new");
|
||||
|
||||
return requestFromRequest.text().then(text => {
|
||||
assert_equals(text, "init body");
|
||||
});
|
||||
}, "Input request used for creating new request became disturbed even if body is not used");
|
||||
|
||||
promise_test(function(test) {
|
||||
assert_true(bodyConsumed.bodyUsed , "bodyUsed is true when request is disturbed");
|
||||
return promise_rejects_js(test, TypeError, bodyConsumed.blob());
|
||||
}, "Check consuming a disturbed request");
|
||||
|
||||
test(function() {
|
||||
var req = new Request(URL, {method: 'POST', body: 'hello'});
|
||||
assert_false(req.bodyUsed,
|
||||
'Request should not be flagged as used if it has not been ' +
|
||||
'consumed.');
|
||||
assert_throws_js(TypeError,
|
||||
function() { new Request(req, {method: 'GET'}); },
|
||||
'A get request may not have body.');
|
||||
|
||||
assert_false(req.bodyUsed, 'After the GET case');
|
||||
|
||||
assert_throws_js(TypeError,
|
||||
function() { new Request(req, {method: 'CONNECT'}); },
|
||||
'Request() with a forbidden method must throw.');
|
||||
|
||||
assert_false(req.bodyUsed, 'After the forbidden method case');
|
||||
|
||||
var req2 = new Request(req);
|
||||
assert_true(req.bodyUsed,
|
||||
'Request should be flagged as used if it has been consumed.');
|
||||
}, 'Request construction failure should not set "bodyUsed"');
|
||||
@@ -0,0 +1,56 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Request error
|
||||
// META: script=request-error.js
|
||||
|
||||
// badRequestArgTests is from response-error.js
|
||||
for (const { args, testName } of badRequestArgTests) {
|
||||
test(() => {
|
||||
assert_throws_js(
|
||||
TypeError,
|
||||
() => new Request(...args),
|
||||
"Expect TypeError exception"
|
||||
);
|
||||
}, testName);
|
||||
}
|
||||
|
||||
test(function() {
|
||||
assert_throws_js(
|
||||
TypeError,
|
||||
() => Request("about:blank"),
|
||||
"Calling Request constructor without 'new' must throw"
|
||||
);
|
||||
});
|
||||
|
||||
test(function() {
|
||||
var initialHeaders = new Headers([["Content-Type", "potato"]]);
|
||||
var initialRequest = new Request("", {"headers" : initialHeaders});
|
||||
var request = new Request(initialRequest);
|
||||
assert_equals(request.headers.get("Content-Type"), "potato");
|
||||
}, "Request should get its content-type from the init request");
|
||||
|
||||
test(function() {
|
||||
var initialHeaders = new Headers([["Content-Type", "potato"]]);
|
||||
var initialRequest = new Request("", {"headers" : initialHeaders});
|
||||
var headers = new Headers([]);
|
||||
var request = new Request(initialRequest, {"headers" : headers});
|
||||
assert_false(request.headers.has("Content-Type"));
|
||||
}, "Request should not get its content-type from the init request if init headers are provided");
|
||||
|
||||
test(function() {
|
||||
var initialHeaders = new Headers([["Content-Type-Extra", "potato"]]);
|
||||
var initialRequest = new Request("", {"headers" : initialHeaders, "body" : "this is my plate", "method" : "POST"});
|
||||
var request = new Request(initialRequest);
|
||||
assert_equals(request.headers.get("Content-Type"), "text/plain;charset=UTF-8");
|
||||
}, "Request should get its content-type from the body if none is provided");
|
||||
|
||||
test(function() {
|
||||
var initialHeaders = new Headers([["Content-Type", "potato"]]);
|
||||
var initialRequest = new Request("", {"headers" : initialHeaders, "body" : "this is my plate", "method" : "POST"});
|
||||
var request = new Request(initialRequest);
|
||||
assert_equals(request.headers.get("Content-Type"), "potato");
|
||||
}, "Request should get its content-type from init headers if one is provided");
|
||||
|
||||
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");
|
||||
@@ -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:[email protected]"],
|
||||
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`
|
||||
};
|
||||
})
|
||||
);
|
||||
@@ -0,0 +1,177 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Request Headers
|
||||
|
||||
var validRequestHeaders = [
|
||||
["Content-Type", "OK"],
|
||||
["Potato", "OK"],
|
||||
["proxy", "OK"],
|
||||
["proxya", "OK"],
|
||||
["sec", "OK"],
|
||||
["secb", "OK"],
|
||||
["Set-Cookie2", "OK"],
|
||||
["User-Agent", "OK"],
|
||||
];
|
||||
var invalidRequestHeaders = [
|
||||
["Accept-Charset", "KO"],
|
||||
["accept-charset", "KO"],
|
||||
["ACCEPT-ENCODING", "KO"],
|
||||
["Accept-Encoding", "KO"],
|
||||
["Access-Control-Request-Headers", "KO"],
|
||||
["Access-Control-Request-Method", "KO"],
|
||||
["Connection", "KO"],
|
||||
["Content-Length", "KO"],
|
||||
["Cookie", "KO"],
|
||||
["Cookie2", "KO"],
|
||||
["Date", "KO"],
|
||||
["DNT", "KO"],
|
||||
["Expect", "KO"],
|
||||
["Host", "KO"],
|
||||
["Keep-Alive", "KO"],
|
||||
["Origin", "KO"],
|
||||
["Referer", "KO"],
|
||||
["Set-Cookie", "KO"],
|
||||
["TE", "KO"],
|
||||
["Trailer", "KO"],
|
||||
["Transfer-Encoding", "KO"],
|
||||
["Upgrade", "KO"],
|
||||
["Via", "KO"],
|
||||
["Proxy-", "KO"],
|
||||
["proxy-a", "KO"],
|
||||
["Sec-", "KO"],
|
||||
["sec-b", "KO"],
|
||||
];
|
||||
|
||||
var validRequestNoCorsHeaders = [
|
||||
["Accept", "OK"],
|
||||
["Accept-Language", "OK"],
|
||||
["content-language", "OK"],
|
||||
["content-type", "application/x-www-form-urlencoded"],
|
||||
["content-type", "application/x-www-form-urlencoded;charset=UTF-8"],
|
||||
["content-type", "multipart/form-data"],
|
||||
["content-type", "multipart/form-data;charset=UTF-8"],
|
||||
["content-TYPE", "text/plain"],
|
||||
["CONTENT-type", "text/plain;charset=UTF-8"],
|
||||
];
|
||||
var invalidRequestNoCorsHeaders = [
|
||||
["Content-Type", "KO"],
|
||||
["Potato", "KO"],
|
||||
["proxy", "KO"],
|
||||
["proxya", "KO"],
|
||||
["sec", "KO"],
|
||||
["secb", "KO"],
|
||||
["Empty-Value", ""],
|
||||
];
|
||||
|
||||
validRequestHeaders.forEach(function(header) {
|
||||
test(function() {
|
||||
var request = new Request("");
|
||||
request.headers.set(header[0], header[1]);
|
||||
assert_equals(request.headers.get(header[0]), header[1]);
|
||||
}, "Adding valid request header \"" + header[0] + ": " + header[1] + "\"");
|
||||
});
|
||||
invalidRequestHeaders.forEach(function(header) {
|
||||
test(function() {
|
||||
var request = new Request("");
|
||||
request.headers.set(header[0], header[1]);
|
||||
assert_equals(request.headers.get(header[0]), null);
|
||||
}, "Adding invalid request header \"" + header[0] + ": " + header[1] + "\"");
|
||||
});
|
||||
|
||||
validRequestNoCorsHeaders.forEach(function(header) {
|
||||
test(function() {
|
||||
var requestNoCors = new Request("", {"mode": "no-cors"});
|
||||
requestNoCors.headers.set(header[0], header[1]);
|
||||
assert_equals(requestNoCors.headers.get(header[0]), header[1]);
|
||||
}, "Adding valid no-cors request header \"" + header[0] + ": " + header[1] + "\"");
|
||||
});
|
||||
invalidRequestNoCorsHeaders.forEach(function(header) {
|
||||
test(function() {
|
||||
var requestNoCors = new Request("", {"mode": "no-cors"});
|
||||
requestNoCors.headers.set(header[0], header[1]);
|
||||
assert_equals(requestNoCors.headers.get(header[0]), null);
|
||||
}, "Adding invalid no-cors request header \"" + header[0] + ": " + header[1] + "\"");
|
||||
});
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers([["Cookie2", "potato"]]);
|
||||
var request = new Request("", {"headers": headers});
|
||||
assert_equals(request.headers.get("Cookie2"), null);
|
||||
}, "Check that request constructor is filtering headers provided as init parameter");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers([["Content-Type", "potato"]]);
|
||||
var request = new Request("", {"headers": headers, "mode": "no-cors"});
|
||||
assert_equals(request.headers.get("Content-Type"), null);
|
||||
}, "Check that no-cors request constructor is filtering headers provided as init parameter");
|
||||
|
||||
test(function() {
|
||||
var headers = new Headers([["Content-Type", "potato"]]);
|
||||
var initialRequest = new Request("", {"headers": headers});
|
||||
var request = new Request(initialRequest, {"mode": "no-cors"});
|
||||
assert_equals(request.headers.get("Content-Type"), null);
|
||||
}, "Check that no-cors request constructor is filtering headers provided as part of request parameter");
|
||||
|
||||
test(function() {
|
||||
var initialHeaders = new Headers([["Content-Type", "potato"]]);
|
||||
var initialRequest = new Request("", {"headers" : initialHeaders});
|
||||
var request = new Request(initialRequest);
|
||||
assert_equals(request.headers.get("Content-Type"), "potato");
|
||||
}, "Request should get its content-type from the init request");
|
||||
|
||||
test(function() {
|
||||
var initialHeaders = new Headers([["Content-Type", "potato"]]);
|
||||
var initialRequest = new Request("", {"headers" : initialHeaders});
|
||||
var headers = new Headers([]);
|
||||
var request = new Request(initialRequest, {"headers" : headers});
|
||||
assert_false(request.headers.has("Content-Type"));
|
||||
}, "Request should not get its content-type from the init request if init headers are provided");
|
||||
|
||||
test(function() {
|
||||
var initialHeaders = new Headers([["Content-Type-Extra", "potato"]]);
|
||||
var initialRequest = new Request("", {"headers" : initialHeaders, "body" : "this is my plate", "method" : "POST"});
|
||||
var request = new Request(initialRequest);
|
||||
assert_equals(request.headers.get("Content-Type"), "text/plain;charset=UTF-8");
|
||||
}, "Request should get its content-type from the body if none is provided");
|
||||
|
||||
test(function() {
|
||||
var initialHeaders = new Headers([["Content-Type", "potato"]]);
|
||||
var initialRequest = new Request("", {"headers" : initialHeaders, "body" : "this is my plate", "method" : "POST"});
|
||||
var request = new Request(initialRequest);
|
||||
assert_equals(request.headers.get("Content-Type"), "potato");
|
||||
}, "Request should get its content-type from init headers if one is provided");
|
||||
|
||||
test(function() {
|
||||
var array = [["hello", "worldAHH"]];
|
||||
var object = {"hello": 'worldOOH'};
|
||||
var headers = new Headers(array);
|
||||
|
||||
assert_equals(headers.get("hello"), "worldAHH");
|
||||
|
||||
var request1 = new Request("", {"headers": headers});
|
||||
var request2 = new Request("", {"headers": array});
|
||||
var request3 = new Request("", {"headers": object});
|
||||
|
||||
assert_equals(request1.headers.get("hello"), "worldAHH");
|
||||
assert_equals(request2.headers.get("hello"), "worldAHH");
|
||||
assert_equals(request3.headers.get("hello"), "worldOOH");
|
||||
}, "Testing request header creations with various objects");
|
||||
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", {"headers" : [["Content-Type", ""]], "body" : "this is my plate", "method" : "POST"});
|
||||
return request.blob().then(function(blob) {
|
||||
assert_equals(blob.type, "", "Blob type should be the empty string");
|
||||
});
|
||||
}, "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");
|
||||
@@ -0,0 +1,60 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Request init: headers and body
|
||||
|
||||
test(function() {
|
||||
var headerDict = {"name1": "value1",
|
||||
"name2": "value2",
|
||||
"name3": "value3"
|
||||
};
|
||||
var headers = new Headers(headerDict);
|
||||
var request = new Request("", { "headers" : headers })
|
||||
for (var name in headerDict) {
|
||||
assert_equals(request.headers.get(name), headerDict[name],
|
||||
"request's headers has " + name + " : " + headerDict[name]);
|
||||
}
|
||||
}, "Initialize Request with headers values");
|
||||
|
||||
function makeRequestInit(body, method) {
|
||||
return {"method": method, "body": body};
|
||||
}
|
||||
|
||||
function checkRequestInit(body, bodyType, expectedTextBody) {
|
||||
promise_test(function(test) {
|
||||
var request = new Request("", makeRequestInit(body, "POST"));
|
||||
if (body) {
|
||||
assert_throws_js(TypeError, function() { new Request("", makeRequestInit(body, "GET")); });
|
||||
assert_throws_js(TypeError, function() { new Request("", makeRequestInit(body, "HEAD")); });
|
||||
} else {
|
||||
new Request("", makeRequestInit(body, "GET")); // should not throw
|
||||
}
|
||||
var reqHeaders = request.headers;
|
||||
var mime = reqHeaders.get("Content-Type");
|
||||
assert_true(!body || (mime && mime.search(bodyType) > -1), "Content-Type header should be \"" + bodyType + "\", not \"" + mime + "\"");
|
||||
return request.text().then(function(bodyAsText) {
|
||||
//not equals: cannot guess formData exact value
|
||||
assert_true( bodyAsText.search(expectedTextBody) > -1, "Retrieve and verify request body");
|
||||
});
|
||||
}, `Initialize Request's body with "${body}", ${bodyType}`);
|
||||
}
|
||||
|
||||
var blob = new Blob(["This is a blob"], {type: "application/octet-binary"});
|
||||
var formaData = new FormData();
|
||||
formaData.append("name", "value");
|
||||
var usvString = "This is a USVString"
|
||||
|
||||
checkRequestInit(undefined, undefined, "");
|
||||
checkRequestInit(null, null, "");
|
||||
checkRequestInit(blob, "application/octet-binary", "This is a blob");
|
||||
checkRequestInit(formaData, "multipart/form-data", "name=\"name\"\r\n\r\nvalue");
|
||||
checkRequestInit(usvString, "text/plain;charset=UTF-8", "This is a USVString");
|
||||
checkRequestInit({toString: () => "hi!"}, "text/plain;charset=UTF-8", "hi!");
|
||||
|
||||
// Ensure test does not time out in case of missing URLSearchParams support.
|
||||
if (self.URLSearchParams) {
|
||||
var urlSearchParams = new URLSearchParams("name=value");
|
||||
checkRequestInit(urlSearchParams, "application/x-www-form-urlencoded;charset=UTF-8", "name=value");
|
||||
} else {
|
||||
promise_test(function(test) {
|
||||
return Promise.reject("URLSearchParams not supported");
|
||||
}, "Initialize Request's body with application/x-www-form-urlencoded;charset=UTF-8");
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
function requestFromBody(body) {
|
||||
return new Request(
|
||||
"https://example.com",
|
||||
{
|
||||
method: "POST",
|
||||
body,
|
||||
duplex: "half",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test(() => {
|
||||
const request = requestFromBody(undefined);
|
||||
assert_equals(request.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Request with empty body");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([]);
|
||||
const request = requestFromBody(blob);
|
||||
assert_equals(request.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Request with Blob body (no type set)");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([], { type: "" });
|
||||
const request = requestFromBody(blob);
|
||||
assert_equals(request.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Request with Blob body (empty type)");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([], { type: "a/b; c=d" });
|
||||
const request = requestFromBody(blob);
|
||||
assert_equals(request.headers.get("Content-Type"), "a/b; c=d");
|
||||
}, "Default Content-Type for Request with Blob body (set type)");
|
||||
|
||||
test(() => {
|
||||
const buffer = new Uint8Array();
|
||||
const request = requestFromBody(buffer);
|
||||
assert_equals(request.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Request with buffer source body");
|
||||
|
||||
promise_test(async () => {
|
||||
const formData = new FormData();
|
||||
formData.append("a", "b");
|
||||
const request = requestFromBody(formData);
|
||||
const boundary = (await request.text()).split("\r\n")[0].slice(2);
|
||||
assert_equals(
|
||||
request.headers.get("Content-Type"),
|
||||
`multipart/form-data; boundary=${boundary}`,
|
||||
);
|
||||
}, "Default Content-Type for Request with FormData body");
|
||||
|
||||
test(() => {
|
||||
const usp = new URLSearchParams();
|
||||
const request = requestFromBody(usp);
|
||||
assert_equals(
|
||||
request.headers.get("Content-Type"),
|
||||
"application/x-www-form-urlencoded;charset=UTF-8",
|
||||
);
|
||||
}, "Default Content-Type for Request with URLSearchParams body");
|
||||
|
||||
test(() => {
|
||||
const request = requestFromBody("");
|
||||
assert_equals(
|
||||
request.headers.get("Content-Type"),
|
||||
"text/plain;charset=UTF-8",
|
||||
);
|
||||
}, "Default Content-Type for Request with string body");
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream();
|
||||
const request = requestFromBody(stream);
|
||||
assert_equals(request.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Request with ReadableStream body");
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const OVERRIDE_MIME = "test/only; mime=type";
|
||||
|
||||
function requestFromBodyWithOverrideMime(body) {
|
||||
return new Request(
|
||||
"https://example.com",
|
||||
{
|
||||
method: "POST",
|
||||
body,
|
||||
headers: { "Content-Type": OVERRIDE_MIME },
|
||||
duplex: "half",
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
test(() => {
|
||||
const request = requestFromBodyWithOverrideMime(undefined);
|
||||
assert_equals(request.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Request with empty body");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([]);
|
||||
const request = requestFromBodyWithOverrideMime(blob);
|
||||
assert_equals(request.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Request with Blob body (no type set)");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([], { type: "" });
|
||||
const request = requestFromBodyWithOverrideMime(blob);
|
||||
assert_equals(request.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Request with Blob body (empty type)");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([], { type: "a/b; c=d" });
|
||||
const request = requestFromBodyWithOverrideMime(blob);
|
||||
assert_equals(request.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Request with Blob body (set type)");
|
||||
|
||||
test(() => {
|
||||
const buffer = new Uint8Array();
|
||||
const request = requestFromBodyWithOverrideMime(buffer);
|
||||
assert_equals(request.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Request with buffer source body");
|
||||
|
||||
test(() => {
|
||||
const formData = new FormData();
|
||||
const request = requestFromBodyWithOverrideMime(formData);
|
||||
assert_equals(request.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Request with FormData body");
|
||||
|
||||
test(() => {
|
||||
const usp = new URLSearchParams();
|
||||
const request = requestFromBodyWithOverrideMime(usp);
|
||||
assert_equals(request.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Request with URLSearchParams body");
|
||||
|
||||
test(() => {
|
||||
const request = requestFromBodyWithOverrideMime("");
|
||||
assert_equals(request.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Request with string body");
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream();
|
||||
const request = requestFromBodyWithOverrideMime(stream);
|
||||
assert_equals(request.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Request with ReadableStream body");
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
var priorities = ["high",
|
||||
"low",
|
||||
"auto"
|
||||
];
|
||||
|
||||
for (idx in priorities) {
|
||||
test(() => {
|
||||
new Request("", {priority: priorities[idx]});
|
||||
}, "new Request() with a '" + priorities[idx] + "' priority does not throw an error");
|
||||
}
|
||||
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => {
|
||||
new Request("", {priority: 'invalid'});
|
||||
}, "a new Request() must throw a TypeError if RequestInit's priority is an invalid value");
|
||||
}, "new Request() throws a TypeError if any of RequestInit's members' values are invalid");
|
||||
|
||||
for (idx in priorities) {
|
||||
promise_test(function(t) {
|
||||
return fetch('hello.txt', { priority: priorities[idx] });
|
||||
}, "fetch() with a '" + priorities[idx] + "' priority completes successfully");
|
||||
}
|
||||
|
||||
promise_test(function(t) {
|
||||
return promise_rejects_js(t, TypeError, fetch('hello.txt', { priority: 'invalid' }));
|
||||
}, "fetch() with an invalid priority returns a rejected promise with a TypeError");
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
const duplex = "half";
|
||||
const method = "POST";
|
||||
|
||||
test(() => {
|
||||
const body = new ReadableStream();
|
||||
const request = new Request("...", { method, body, duplex });
|
||||
assert_equals(request.body, body);
|
||||
}, "Constructing a Request with a stream holds the original object.");
|
||||
|
||||
test((t) => {
|
||||
const body = new ReadableStream();
|
||||
body.getReader();
|
||||
assert_throws_js(TypeError,
|
||||
() => new Request("...", { method, body, duplex }));
|
||||
}, "Constructing a Request with a stream on which getReader() is called");
|
||||
|
||||
test((t) => {
|
||||
const body = new ReadableStream();
|
||||
body.getReader().read();
|
||||
assert_throws_js(TypeError,
|
||||
() => new Request("...", { method, body, duplex }));
|
||||
}, "Constructing a Request with a stream on which read() is called");
|
||||
|
||||
promise_test(async (t) => {
|
||||
const body = new ReadableStream({ pull: c => c.enqueue(new Uint8Array()) });
|
||||
const reader = body.getReader();
|
||||
await reader.read();
|
||||
reader.releaseLock();
|
||||
assert_throws_js(TypeError,
|
||||
() => new Request("...", { method, body, duplex }));
|
||||
}, "Constructing a Request with a stream on which read() and releaseLock() are called");
|
||||
|
||||
test((t) => {
|
||||
const request = new Request("...", { method: "POST", body: "..." });
|
||||
request.body.getReader();
|
||||
assert_throws_js(TypeError, () => new Request(request));
|
||||
// This doesn't throw.
|
||||
new Request(request, { body: "..." });
|
||||
}, "Constructing a Request with a Request on which body.getReader() is called");
|
||||
|
||||
test((t) => {
|
||||
const request = new Request("...", { method: "POST", body: "..." });
|
||||
request.body.getReader().read();
|
||||
assert_throws_js(TypeError, () => new Request(request));
|
||||
// This doesn't throw.
|
||||
new Request(request, { body: "..." });
|
||||
}, "Constructing a Request with a Request on which body.getReader().read() is called");
|
||||
|
||||
promise_test(async (t) => {
|
||||
const request = new Request("...", { method: "POST", body: "..." });
|
||||
const reader = request.body.getReader();
|
||||
await reader.read();
|
||||
reader.releaseLock();
|
||||
assert_throws_js(TypeError, () => new Request(request));
|
||||
// This doesn't throw.
|
||||
new Request(request, { body: "..." });
|
||||
}, "Constructing a Request with a Request on which read() and releaseLock() are called");
|
||||
|
||||
test((t) => {
|
||||
new Request("...", { method, body: null });
|
||||
}, "It is OK to omit .duplex when the body is null.");
|
||||
|
||||
test((t) => {
|
||||
new Request("...", { method, body: "..." });
|
||||
}, "It is OK to omit .duplex when the body is a string.");
|
||||
|
||||
test((t) => {
|
||||
new Request("...", { method, body: new Uint8Array(3) });
|
||||
}, "It is OK to omit .duplex when the body is a Uint8Array.");
|
||||
|
||||
test((t) => {
|
||||
new Request("...", { method, body: new Blob([]) });
|
||||
}, "It is OK to omit .duplex when the body is a Blob.");
|
||||
|
||||
test((t) => {
|
||||
const body = new ReadableStream();
|
||||
assert_throws_js(TypeError,
|
||||
() => new Request("...", { method, body }));
|
||||
}, "It is error to omit .duplex when the body is a ReadableStream.");
|
||||
|
||||
test((t) => {
|
||||
new Request("...", { method, body: null, duplex: "half" });
|
||||
}, "It is OK to set .duplex = 'half' when the body is null.");
|
||||
|
||||
test((t) => {
|
||||
new Request("...", { method, body: "...", duplex: "half" });
|
||||
}, "It is OK to set .duplex = 'half' when the body is a string.");
|
||||
|
||||
test((t) => {
|
||||
new Request("...", { method, body: new Uint8Array(3), duplex: "half" });
|
||||
}, "It is OK to set .duplex = 'half' when the body is a Uint8Array.");
|
||||
|
||||
test((t) => {
|
||||
new Request("...", { method, body: new Blob([]), duplex: "half" });
|
||||
}, "It is OK to set .duplex = 'half' when the body is a Blob.");
|
||||
|
||||
test((t) => {
|
||||
const body = new ReadableStream();
|
||||
new Request("...", { method, body, duplex: "half" });
|
||||
}, "It is OK to set .duplex = 'half' when the body is a ReadableStream.");
|
||||
|
||||
test((t) => {
|
||||
const body = null;
|
||||
const duplex = "full";
|
||||
assert_throws_js(TypeError,
|
||||
() => new Request("...", { method, body, duplex }));
|
||||
}, "It is error to set .duplex = 'full' when the body is null.");
|
||||
|
||||
test((t) => {
|
||||
const body = "...";
|
||||
const duplex = "full";
|
||||
assert_throws_js(TypeError,
|
||||
() => new Request("...", { method, body, duplex }));
|
||||
}, "It is error to set .duplex = 'full' when the body is a string.");
|
||||
|
||||
test((t) => {
|
||||
const body = new Uint8Array(3);
|
||||
const duplex = "full";
|
||||
assert_throws_js(TypeError,
|
||||
() => new Request("...", { method, body, duplex }));
|
||||
}, "It is error to set .duplex = 'full' when the body is a Uint8Array.");
|
||||
|
||||
test((t) => {
|
||||
const body = new Blob([]);
|
||||
const duplex = "full";
|
||||
assert_throws_js(TypeError,
|
||||
() => new Request("...", { method, body, duplex }));
|
||||
}, "It is error to set .duplex = 'full' when the body is a Blob.");
|
||||
|
||||
test((t) => {
|
||||
const body = new ReadableStream();
|
||||
const duplex = "full";
|
||||
assert_throws_js(TypeError,
|
||||
() => new Request("...", { method, body, duplex }));
|
||||
}, "It is error to set .duplex = 'full' when the body is a ReadableStream.");
|
||||
|
||||
test((t) => {
|
||||
const body = new ReadableStream();
|
||||
const duplex = "half";
|
||||
const req1 = new Request("...", { method, body, duplex });
|
||||
const req2 = new Request(req1);
|
||||
}, "It is OK to omit duplex when init.body is not given and input.body is given.");
|
||||
@@ -0,0 +1,17 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Request keepalive
|
||||
// META: script=/common/utils.js
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
|
||||
test(() => {
|
||||
assert_false(new Request('/').keepalive, 'default');
|
||||
assert_true(new Request('/', {keepalive: true}).keepalive, 'true');
|
||||
assert_false(new Request('/', {keepalive: false}).keepalive, 'false');
|
||||
assert_true(new Request('/', {keepalive: 1}).keepalive, 'truish');
|
||||
assert_false(new Request('/', {keepalive: 0}).keepalive, 'falsy');
|
||||
}, 'keepalive flag');
|
||||
|
||||
test(() => {
|
||||
const init = {method: 'POST', keepalive: true, body: new ReadableStream()};
|
||||
assert_throws_js(TypeError, () => {new Request('/', init)});
|
||||
}, 'keepalive flag with stream body');
|
||||
@@ -0,0 +1,143 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Request structure
|
||||
|
||||
var request = new Request("");
|
||||
var methods = ["clone",
|
||||
//Request implements Body
|
||||
"arrayBuffer",
|
||||
"blob",
|
||||
"formData",
|
||||
"json",
|
||||
"text"
|
||||
];
|
||||
var attributes = ["method",
|
||||
"url",
|
||||
"headers",
|
||||
"destination",
|
||||
"referrer",
|
||||
"referrerPolicy",
|
||||
"mode",
|
||||
"credentials",
|
||||
"cache",
|
||||
"redirect",
|
||||
"integrity",
|
||||
"isReloadNavigation",
|
||||
"isHistoryNavigation",
|
||||
"duplex",
|
||||
//Request implements Body
|
||||
"bodyUsed"
|
||||
];
|
||||
var internalAttributes = ["priority",
|
||||
"internalpriority",
|
||||
"blocking"
|
||||
];
|
||||
|
||||
function isReadOnly(request, attributeToCheck) {
|
||||
var defaultValue = undefined;
|
||||
var newValue = undefined;
|
||||
switch (attributeToCheck) {
|
||||
case "method":
|
||||
defaultValue = "GET";
|
||||
newValue = "POST";
|
||||
break;
|
||||
|
||||
case "url":
|
||||
//default value is base url
|
||||
//i.e http://example.com/fetch/api/request-structure.html
|
||||
newValue = "http://url.test";
|
||||
break;
|
||||
|
||||
case "headers":
|
||||
request.headers = new Headers ({"name":"value"});
|
||||
assert_false(request.headers.has("name"), "Headers attribute is read only");
|
||||
return;
|
||||
|
||||
case "destination":
|
||||
defaultValue = "";
|
||||
newValue = "worker";
|
||||
break;
|
||||
|
||||
case "referrer":
|
||||
defaultValue = "about:client";
|
||||
newValue = "http://url.test";
|
||||
break;
|
||||
|
||||
case "referrerPolicy":
|
||||
defaultValue = "";
|
||||
newValue = "unsafe-url";
|
||||
break;
|
||||
|
||||
case "mode":
|
||||
defaultValue = "cors";
|
||||
newValue = "navigate";
|
||||
break;
|
||||
|
||||
case "credentials":
|
||||
defaultValue = "same-origin";
|
||||
newValue = "cors";
|
||||
break;
|
||||
|
||||
case "cache":
|
||||
defaultValue = "default";
|
||||
newValue = "reload";
|
||||
break;
|
||||
|
||||
case "redirect":
|
||||
defaultValue = "follow";
|
||||
newValue = "manual";
|
||||
break;
|
||||
|
||||
case "integrity":
|
||||
newValue = "CannotWriteIntegrity";
|
||||
break;
|
||||
|
||||
case "bodyUsed":
|
||||
defaultValue = false;
|
||||
newValue = true;
|
||||
break;
|
||||
|
||||
case "isReloadNavigation":
|
||||
defaultValue = false;
|
||||
newValue = true;
|
||||
break;
|
||||
|
||||
case "isHistoryNavigation":
|
||||
defaultValue = false;
|
||||
newValue = true;
|
||||
break;
|
||||
|
||||
case "duplex":
|
||||
defaultValue = "half";
|
||||
newValue = "full";
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
request[attributeToCheck] = newValue;
|
||||
if (defaultValue === undefined)
|
||||
assert_not_equals(request[attributeToCheck], newValue, "Attribute " + attributeToCheck + " is read only");
|
||||
else
|
||||
assert_equals(request[attributeToCheck], defaultValue,
|
||||
"Attribute " + attributeToCheck + " is read only. Default value is " + defaultValue);
|
||||
}
|
||||
|
||||
for (var idx in methods) {
|
||||
test(function() {
|
||||
assert_true(methods[idx] in request, "request has " + methods[idx] + " method");
|
||||
}, "Request has " + methods[idx] + " method");
|
||||
}
|
||||
|
||||
for (var idx in attributes) {
|
||||
test(function() {
|
||||
assert_true(attributes[idx] in request, "request has " + attributes[idx] + " attribute");
|
||||
isReadOnly(request, attributes[idx]);
|
||||
}, "Check " + attributes[idx] + " attribute");
|
||||
}
|
||||
|
||||
for (var idx in internalAttributes) {
|
||||
test(function() {
|
||||
assert_false(internalAttributes[idx] in request, "request does not expose " + internalAttributes[idx] + " attribute");
|
||||
}, "Request does not expose " + internalAttributes[idx] + " attribute");
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
var RESOURCES_DIR = "../resources/";
|
||||
|
||||
function dirname(path) {
|
||||
return path.replace(/\/[^\/]*$/, '/')
|
||||
}
|
||||
|
||||
function checkRequest(request, ExpectedValuesDict) {
|
||||
for (var attribute in ExpectedValuesDict) {
|
||||
switch(attribute) {
|
||||
case "headers":
|
||||
for (var key in ExpectedValuesDict["headers"].keys()) {
|
||||
assert_equals(request["headers"].get(key), ExpectedValuesDict["headers"].get(key),
|
||||
"Check headers attribute has " + key + ":" + ExpectedValuesDict["headers"].get(key));
|
||||
}
|
||||
break;
|
||||
|
||||
case "body":
|
||||
//for checking body's content, a dedicated asyncronous/promise test should be used
|
||||
assert_true(request["headers"].has("Content-Type") , "Check request has body using Content-Type header")
|
||||
break;
|
||||
|
||||
case "method":
|
||||
case "referrer":
|
||||
case "referrerPolicy":
|
||||
case "credentials":
|
||||
case "cache":
|
||||
case "redirect":
|
||||
case "integrity":
|
||||
case "url":
|
||||
case "destination":
|
||||
assert_equals(request[attribute], ExpectedValuesDict[attribute], "Check " + attribute + " attribute")
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function stringToArray(str) {
|
||||
var array = new Uint8Array(str.length);
|
||||
for (var i=0, strLen = str.length; i < strLen; i++)
|
||||
array[i] = str.charCodeAt(i);
|
||||
return array;
|
||||
}
|
||||
|
||||
function encode_utf8(str)
|
||||
{
|
||||
if (self.TextEncoder)
|
||||
return (new TextEncoder).encode(str);
|
||||
return stringToArray(unescape(encodeURIComponent(str)));
|
||||
}
|
||||
|
||||
function validateBufferFromString(buffer, expectedValue, message)
|
||||
{
|
||||
return assert_array_equals(new Uint8Array(buffer !== undefined ? buffer : []), stringToArray(expectedValue), message);
|
||||
}
|
||||
|
||||
function validateStreamFromString(reader, expectedValue, retrievedArrayBuffer) {
|
||||
// Passing Uint8Array for byte streams; non-byte streams will simply ignore it
|
||||
return reader.read(new Uint8Array(64)).then(function(data) {
|
||||
if (!data.done) {
|
||||
assert_true(data.value instanceof Uint8Array, "Fetch ReadableStream chunks should be Uint8Array");
|
||||
var newBuffer;
|
||||
if (retrievedArrayBuffer) {
|
||||
newBuffer = new Uint8Array(data.value.length + retrievedArrayBuffer.length);
|
||||
newBuffer.set(retrievedArrayBuffer, 0);
|
||||
newBuffer.set(data.value, retrievedArrayBuffer.length);
|
||||
} else {
|
||||
newBuffer = data.value;
|
||||
}
|
||||
return validateStreamFromString(reader, expectedValue, newBuffer);
|
||||
}
|
||||
validateBufferFromString(retrievedArrayBuffer, expectedValue, "Retrieve and verify stream");
|
||||
});
|
||||
}
|
||||
|
||||
function validateStreamFromPartialString(reader, expectedValue, retrievedArrayBuffer) {
|
||||
// Passing Uint8Array for byte streams; non-byte streams will simply ignore it
|
||||
return reader.read(new Uint8Array(64)).then(function(data) {
|
||||
if (!data.done) {
|
||||
assert_true(data.value instanceof Uint8Array, "Fetch ReadableStream chunks should be Uint8Array");
|
||||
var newBuffer;
|
||||
if (retrievedArrayBuffer) {
|
||||
newBuffer = new Uint8Array(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)
|
||||
{
|
||||
return new Promise(function(resolve) {
|
||||
step_timeout(resolve, milliseconds);
|
||||
});
|
||||
}
|
||||
|
||||
function requestForbiddenHeaders(desc, forbiddenHeaders) {
|
||||
var url = RESOURCES_DIR + "inspect-headers.py";
|
||||
var requestInit = {"headers": forbiddenHeaders}
|
||||
var urlParameters = "?headers=" + Object.keys(forbiddenHeaders).join("|");
|
||||
|
||||
promise_test(function(test){
|
||||
return fetch(url + urlParameters, requestInit).then(function(resp) {
|
||||
assert_equals(resp.status, 200, "HTTP status is 200");
|
||||
assert_equals(resp.type , "basic", "Response's type is basic");
|
||||
for (var header in forbiddenHeaders)
|
||||
assert_not_equals(resp.headers.get("x-request-" + header), forbiddenHeaders[header], header + " does not have the value we defined");
|
||||
});
|
||||
}, desc);
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response consume empty bodies
|
||||
|
||||
function checkBodyText(test, response) {
|
||||
return response.text().then(function(bodyAsText) {
|
||||
assert_equals(bodyAsText, "", "Resolved value should be empty");
|
||||
assert_false(response.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
async function checkBodyBlob(test, response) {
|
||||
const bodyAsBlob = await response.blob();
|
||||
const body = await bodyAsBlob.text();
|
||||
|
||||
assert_equals(body, "", "Resolved value should be empty");
|
||||
assert_false(response.bodyUsed);
|
||||
}
|
||||
|
||||
function checkBodyArrayBuffer(test, response) {
|
||||
return response.arrayBuffer().then(function(bodyAsArrayBuffer) {
|
||||
assert_equals(bodyAsArrayBuffer.byteLength, 0, "Resolved value should be empty");
|
||||
assert_false(response.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
function checkBodyJSON(test, response) {
|
||||
return response.json().then(
|
||||
function(bodyAsJSON) {
|
||||
assert_unreached("JSON parsing should fail");
|
||||
},
|
||||
function() {
|
||||
assert_false(response.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
function checkBodyFormData(test, response) {
|
||||
return response.formData().then(function(bodyAsFormData) {
|
||||
assert_true(bodyAsFormData instanceof FormData, "Should receive a FormData");
|
||||
assert_false(response.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
function checkBodyFormDataError(test, response) {
|
||||
return promise_rejects_js(test, TypeError, response.formData()).then(function() {
|
||||
assert_false(response.bodyUsed);
|
||||
});
|
||||
}
|
||||
|
||||
function checkResponseWithNoBody(bodyType, checkFunction, headers = []) {
|
||||
promise_test(function(test) {
|
||||
var response = new Response(undefined, { "headers": headers });
|
||||
assert_false(response.bodyUsed);
|
||||
return checkFunction(test, response);
|
||||
}, "Consume response's body as " + bodyType);
|
||||
}
|
||||
|
||||
checkResponseWithNoBody("text", checkBodyText);
|
||||
checkResponseWithNoBody("blob", checkBodyBlob);
|
||||
checkResponseWithNoBody("arrayBuffer", checkBodyArrayBuffer);
|
||||
checkResponseWithNoBody("json (error case)", checkBodyJSON);
|
||||
checkResponseWithNoBody("formData with correct multipart type (error case)", checkBodyFormDataError, [["Content-Type", 'multipart/form-data; boundary="boundary"']]);
|
||||
checkResponseWithNoBody("formData with correct urlencoded type", checkBodyFormData, [["Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"]]);
|
||||
checkResponseWithNoBody("formData without correct type (error case)", checkBodyFormDataError);
|
||||
|
||||
function checkResponseWithEmptyBody(bodyType, body, asText) {
|
||||
promise_test(function(test) {
|
||||
var response = new Response(body);
|
||||
assert_false(response.bodyUsed, "bodyUsed is false at init");
|
||||
if (asText) {
|
||||
return response.text().then(function(bodyAsString) {
|
||||
assert_equals(bodyAsString.length, 0, "Resolved value should be empty");
|
||||
assert_true(response.bodyUsed, "bodyUsed is true after being consumed");
|
||||
});
|
||||
}
|
||||
return response.arrayBuffer().then(function(bodyAsArrayBuffer) {
|
||||
assert_equals(bodyAsArrayBuffer.byteLength, 0, "Resolved value should be empty");
|
||||
assert_true(response.bodyUsed, "bodyUsed is true after being consumed");
|
||||
});
|
||||
}, "Consume empty " + bodyType + " response body as " + (asText ? "text" : "arrayBuffer"));
|
||||
}
|
||||
|
||||
checkResponseWithEmptyBody("blob", new Blob([], { "type" : "text/plain" }), false);
|
||||
checkResponseWithEmptyBody("text", "", false);
|
||||
checkResponseWithEmptyBody("blob", new Blob([], { "type" : "text/plain" }), true);
|
||||
checkResponseWithEmptyBody("text", "", true);
|
||||
checkResponseWithEmptyBody("URLSearchParams", new URLSearchParams(""), true);
|
||||
checkResponseWithEmptyBody("FormData", new FormData(), true);
|
||||
checkResponseWithEmptyBody("ArrayBuffer", new ArrayBuffer(), true);
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response consume
|
||||
// META: script=../resources/utils.js
|
||||
|
||||
promise_test(function(test) {
|
||||
var body = "";
|
||||
var response = new Response("");
|
||||
return validateStreamFromString(response.body.getReader(), "");
|
||||
}, "Read empty text response's body as readableStream");
|
||||
|
||||
promise_test(function(test) {
|
||||
var response = new Response(new Blob([], { "type" : "text/plain" }));
|
||||
return validateStreamFromString(response.body.getReader(), "");
|
||||
}, "Read empty blob response's body as readableStream");
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append("name", "value");
|
||||
var textData = JSON.stringify("This is response's body");
|
||||
var blob = new Blob([textData], { "type" : "text/plain" });
|
||||
var urlSearchParamsData = "name=value";
|
||||
var urlSearchParams = new URLSearchParams(urlSearchParamsData);
|
||||
|
||||
for (const mode of [undefined, "byob"]) {
|
||||
promise_test(function(test) {
|
||||
var response = new Response(blob);
|
||||
return validateStreamFromString(response.body.getReader({ mode }), textData);
|
||||
}, `Read blob response's body as readableStream with mode=${mode}`);
|
||||
|
||||
promise_test(function(test) {
|
||||
var response = new Response(textData);
|
||||
return validateStreamFromString(response.body.getReader({ mode }), textData);
|
||||
}, `Read text response's body as readableStream with mode=${mode}`);
|
||||
|
||||
promise_test(function(test) {
|
||||
var response = new Response(urlSearchParams);
|
||||
return validateStreamFromString(response.body.getReader({ mode }), urlSearchParamsData);
|
||||
}, `Read URLSearchParams response's body as readableStream with mode=${mode}`);
|
||||
|
||||
promise_test(function(test) {
|
||||
var arrayBuffer = new ArrayBuffer(textData.length);
|
||||
var int8Array = new Int8Array(arrayBuffer);
|
||||
for (var cptr = 0; cptr < textData.length; cptr++)
|
||||
int8Array[cptr] = textData.charCodeAt(cptr);
|
||||
|
||||
return validateStreamFromString(new Response(arrayBuffer).body.getReader({ mode }), textData);
|
||||
}, `Read array buffer response's body as readableStream with mode=${mode}`);
|
||||
|
||||
promise_test(function(test) {
|
||||
var response = new Response(formData);
|
||||
return validateStreamFromPartialString(response.body.getReader({ mode }),
|
||||
"Content-Disposition: form-data; name=\"name\"\r\n\r\nvalue");
|
||||
}, `Read form data response's body as readableStream with mode=${mode}`);
|
||||
}
|
||||
|
||||
test(function() {
|
||||
assert_equals(Response.error().body, null);
|
||||
}, "Getting an error Response stream");
|
||||
|
||||
test(function() {
|
||||
assert_equals(Response.redirect("/").body, null);
|
||||
}, "Getting a redirect Response stream");
|
||||
|
||||
promise_test(async function(test) {
|
||||
var buffer = new ArrayBuffer(textData.length);
|
||||
|
||||
var body = new Response(textData).body;
|
||||
const reader = body.getReader( {mode: 'byob'} );
|
||||
|
||||
let offset = 3;
|
||||
while (offset < textData.length) {
|
||||
const {done, value} = await reader.read(new Uint8Array(buffer, offset));
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
buffer = value.buffer;
|
||||
offset += value.byteLength;
|
||||
}
|
||||
|
||||
validateBufferFromString(buffer, `\0\0\0\"This is response's bo`, 'Buffer should be validated');
|
||||
}, `Reading with offset from Response stream`);
|
||||
@@ -0,0 +1,27 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response error
|
||||
|
||||
var invalidStatus = [0, 100, 199, 600, 1000];
|
||||
invalidStatus.forEach(function(status) {
|
||||
test(function() {
|
||||
assert_throws_js(RangeError, function() { new Response("", { "status" : status }); },
|
||||
"Expect RangeError exception when status is " + status);
|
||||
},"Throws RangeError when responseInit's status is " + status);
|
||||
});
|
||||
|
||||
var invalidStatusText = ["\n", "Ā"];
|
||||
invalidStatusText.forEach(function(statusText) {
|
||||
test(function() {
|
||||
assert_throws_js(TypeError, function() { new Response("", { "statusText" : statusText }); },
|
||||
"Expect TypeError exception " + statusText);
|
||||
},"Throws TypeError when responseInit's statusText is " + statusText);
|
||||
});
|
||||
|
||||
var nullBodyStatus = [204, 205, 304];
|
||||
nullBodyStatus.forEach(function(status) {
|
||||
test(function() {
|
||||
assert_throws_js(TypeError,
|
||||
function() { new Response("body", {"status" : status }); },
|
||||
"Expect TypeError exception ");
|
||||
},"Throws TypeError when building a response with body and a body status of " + status);
|
||||
});
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// META: global=window,worker
|
||||
|
||||
"use strict";
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream();
|
||||
stream.getReader();
|
||||
assert_throws_js(TypeError, () => new Response(stream));
|
||||
}, "Constructing a Response with a stream on which getReader() is called");
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream();
|
||||
stream.getReader().read();
|
||||
assert_throws_js(TypeError, () => new Response(stream));
|
||||
}, "Constructing a Response with a stream on which read() is called");
|
||||
|
||||
promise_test(async () => {
|
||||
const stream = new ReadableStream({ pull: c => c.enqueue(new Uint8Array()) }),
|
||||
reader = stream.getReader();
|
||||
await reader.read();
|
||||
reader.releaseLock();
|
||||
assert_throws_js(TypeError, () => new Response(stream));
|
||||
}, "Constructing a Response with a stream on which read() and releaseLock() are called");
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response: error static method
|
||||
|
||||
promise_test (async () => {
|
||||
const response = await fetch("../resources/data.json");
|
||||
assert_throws_js(TypeError, () => { response.headers.append("name", "value"); });
|
||||
assert_not_equals(response.headers.get("name"), "value", "response headers should be immutable");
|
||||
}, "Ensure response headers are immutable");
|
||||
@@ -0,0 +1,64 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response init: simple cases
|
||||
|
||||
var defaultValues = { "type" : "default",
|
||||
"url" : "",
|
||||
"ok" : true,
|
||||
"status" : 200,
|
||||
"statusText" : "",
|
||||
"body" : null
|
||||
};
|
||||
|
||||
var statusCodes = { "givenValues" : [200, 300, 400, 500, 599],
|
||||
"expectedValues" : [200, 300, 400, 500, 599]
|
||||
};
|
||||
var statusTexts = { "givenValues" : ["", "OK", "with space", String.fromCharCode(0x80)],
|
||||
"expectedValues" : ["", "OK", "with space", String.fromCharCode(0x80)]
|
||||
};
|
||||
var initValuesDict = { "status" : statusCodes,
|
||||
"statusText" : statusTexts
|
||||
};
|
||||
|
||||
function isOkStatus(status) {
|
||||
return 200 <= status && 299 >= status;
|
||||
}
|
||||
|
||||
var response = new Response();
|
||||
for (var attributeName in defaultValues) {
|
||||
test(function() {
|
||||
var expectedValue = defaultValues[attributeName];
|
||||
assert_equals(response[attributeName], expectedValue,
|
||||
"Expect default response." + attributeName + " is " + expectedValue);
|
||||
}, "Check default value for " + attributeName + " attribute");
|
||||
}
|
||||
|
||||
for (var attributeName in initValuesDict) {
|
||||
test(function() {
|
||||
var valuesToTest = initValuesDict[attributeName];
|
||||
for (var valueIdx in valuesToTest["givenValues"]) {
|
||||
var givenValue = valuesToTest["givenValues"][valueIdx];
|
||||
var expectedValue = valuesToTest["expectedValues"][valueIdx];
|
||||
var responseInit = {};
|
||||
responseInit[attributeName] = givenValue;
|
||||
var response = new Response("", responseInit);
|
||||
assert_equals(response[attributeName], expectedValue,
|
||||
"Expect response." + attributeName + " is " + expectedValue +
|
||||
" when initialized with " + givenValue);
|
||||
assert_equals(response.ok, isOkStatus(response.status),
|
||||
"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");
|
||||
@@ -0,0 +1,61 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response init: body and headers
|
||||
// META: script=../resources/utils.js
|
||||
|
||||
test(function() {
|
||||
var headerDict = {"name1": "value1",
|
||||
"name2": "value2",
|
||||
"name3": "value3"
|
||||
};
|
||||
var headers = new Headers(headerDict);
|
||||
var response = new Response("", { "headers" : headers })
|
||||
for (var name in headerDict) {
|
||||
assert_equals(response.headers.get(name), headerDict[name],
|
||||
"response's headers has " + name + " : " + headerDict[name]);
|
||||
}
|
||||
}, "Initialize Response with headers values");
|
||||
|
||||
function checkResponseInit(body, bodyType, expectedTextBody) {
|
||||
promise_test(function(test) {
|
||||
var response = new Response(body);
|
||||
var resHeaders = response.headers;
|
||||
var mime = resHeaders.get("Content-Type");
|
||||
assert_true(mime && mime.search(bodyType) > -1, "Content-Type header should be \"" + bodyType + "\" ");
|
||||
return response.text().then(function(bodyAsText) {
|
||||
//not equals: cannot guess formData exact value
|
||||
assert_true(bodyAsText.search(expectedTextBody) > -1, "Retrieve and verify response body");
|
||||
});
|
||||
}, "Initialize Response's body with " + bodyType);
|
||||
}
|
||||
|
||||
var blob = new Blob(["This is a blob"], {type: "application/octet-binary"});
|
||||
var formaData = new FormData();
|
||||
formaData.append("name", "value");
|
||||
var urlSearchParams = "URLSearchParams are not supported";
|
||||
//avoid test timeout if not implemented
|
||||
if (self.URLSearchParams)
|
||||
urlSearchParams = new URLSearchParams("name=value");
|
||||
var usvString = "This is a USVString"
|
||||
|
||||
checkResponseInit(blob, "application/octet-binary", "This is a blob");
|
||||
checkResponseInit(formaData, "multipart/form-data", "name=\"name\"\r\n\r\nvalue");
|
||||
checkResponseInit(urlSearchParams, "application/x-www-form-urlencoded;charset=UTF-8", "name=value");
|
||||
checkResponseInit(usvString, "text/plain;charset=UTF-8", "This is a USVString");
|
||||
|
||||
promise_test(function(test) {
|
||||
var body = "This is response body";
|
||||
var response = new Response(body);
|
||||
return validateStreamFromString(response.body.getReader(), body);
|
||||
}, "Read Response's body as readableStream");
|
||||
|
||||
promise_test(function(test) {
|
||||
var response = new Response("This is my fork", {"headers" : [["Content-Type", ""]]});
|
||||
return response.blob().then(function(blob) {
|
||||
assert_equals(blob.type, "", "Blob type should be the empty string");
|
||||
});
|
||||
}, "Testing empty Response Content-Type header");
|
||||
|
||||
test(function() {
|
||||
var response = new Response(null, {status: 204});
|
||||
assert_equals(response.body, null);
|
||||
}, "Testing null Response body");
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
test(() => {
|
||||
const response = new Response();
|
||||
assert_equals(response.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Response with empty body");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([]);
|
||||
const response = new Response(blob);
|
||||
assert_equals(response.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Response with Blob body (no type set)");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([], { type: "" });
|
||||
const response = new Response(blob);
|
||||
assert_equals(response.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Response with Blob body (empty type)");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([], { type: "a/b; c=d" });
|
||||
const response = new Response(blob);
|
||||
assert_equals(response.headers.get("Content-Type"), "a/b; c=d");
|
||||
}, "Default Content-Type for Response with Blob body (set type)");
|
||||
|
||||
test(() => {
|
||||
const buffer = new Uint8Array();
|
||||
const response = new Response(buffer);
|
||||
assert_equals(response.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Response with buffer source body");
|
||||
|
||||
promise_test(async () => {
|
||||
const formData = new FormData();
|
||||
formData.append("a", "b");
|
||||
const response = new Response(formData);
|
||||
const boundary = (await response.text()).split("\r\n")[0].slice(2);
|
||||
assert_equals(
|
||||
response.headers.get("Content-Type"),
|
||||
`multipart/form-data; boundary=${boundary}`,
|
||||
);
|
||||
}, "Default Content-Type for Response with FormData body");
|
||||
|
||||
test(() => {
|
||||
const usp = new URLSearchParams();
|
||||
const response = new Response(usp);
|
||||
assert_equals(
|
||||
response.headers.get("Content-Type"),
|
||||
"application/x-www-form-urlencoded;charset=UTF-8",
|
||||
);
|
||||
}, "Default Content-Type for Response with URLSearchParams body");
|
||||
|
||||
test(() => {
|
||||
const response = new Response("");
|
||||
assert_equals(
|
||||
response.headers.get("Content-Type"),
|
||||
"text/plain;charset=UTF-8",
|
||||
);
|
||||
}, "Default Content-Type for Response with string body");
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream();
|
||||
const response = new Response(stream);
|
||||
assert_equals(response.headers.get("Content-Type"), null);
|
||||
}, "Default Content-Type for Response with ReadableStream body");
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
const OVERRIDE_MIME = "test/only; mime=type";
|
||||
|
||||
function responseWithOverrideMime(body) {
|
||||
return new Response(
|
||||
body,
|
||||
{ headers: { "Content-Type": OVERRIDE_MIME } },
|
||||
);
|
||||
}
|
||||
|
||||
test(() => {
|
||||
const response = responseWithOverrideMime(undefined);
|
||||
assert_equals(response.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Response with empty body");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([]);
|
||||
const response = responseWithOverrideMime(blob);
|
||||
assert_equals(response.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Response with Blob body (no type set)");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([], { type: "" });
|
||||
const response = responseWithOverrideMime(blob);
|
||||
assert_equals(response.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Response with Blob body (empty type)");
|
||||
|
||||
test(() => {
|
||||
const blob = new Blob([], { type: "a/b; c=d" });
|
||||
const response = responseWithOverrideMime(blob);
|
||||
assert_equals(response.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Response with Blob body (set type)");
|
||||
|
||||
test(() => {
|
||||
const buffer = new Uint8Array();
|
||||
const response = responseWithOverrideMime(buffer);
|
||||
assert_equals(response.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Response with buffer source body");
|
||||
|
||||
test(() => {
|
||||
const formData = new FormData();
|
||||
const response = responseWithOverrideMime(formData);
|
||||
assert_equals(response.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Response with FormData body");
|
||||
|
||||
test(() => {
|
||||
const usp = new URLSearchParams();
|
||||
const response = responseWithOverrideMime(usp);
|
||||
assert_equals(response.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Response with URLSearchParams body");
|
||||
|
||||
test(() => {
|
||||
const response = responseWithOverrideMime("");
|
||||
assert_equals(response.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Response with string body");
|
||||
|
||||
test(() => {
|
||||
const stream = new ReadableStream();
|
||||
const response = responseWithOverrideMime(stream);
|
||||
assert_equals(response.headers.get("Content-Type"), OVERRIDE_MIME);
|
||||
}, "Can override Content-Type for Response with ReadableStream body");
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response: error static method
|
||||
|
||||
test(function() {
|
||||
var responseError = Response.error();
|
||||
assert_equals(responseError.type, "error", "Network error response's type is error");
|
||||
assert_equals(responseError.status, 0, "Network error response's status is 0");
|
||||
assert_equals(responseError.statusText, "", "Network error response's statusText is empty");
|
||||
assert_equals(responseError.body, null, "Network error response's body is null");
|
||||
|
||||
assert_true(responseError.headers.entries().next().done, "Headers should be empty");
|
||||
}, "Check response returned by static method error()");
|
||||
|
||||
test(function() {
|
||||
const headers = Response.error().headers;
|
||||
|
||||
// Avoid false positives if expected API is not available
|
||||
assert_true(!!headers);
|
||||
assert_equals(typeof headers.append, 'function');
|
||||
|
||||
assert_throws_js(TypeError, function () { headers.append('name', 'value'); });
|
||||
}, "the 'guard' of the Headers instance should be immutable");
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response: json static method
|
||||
|
||||
const APPLICATION_JSON = "application/json";
|
||||
const FOO_BAR = "foo/bar";
|
||||
|
||||
const INIT_TESTS = [
|
||||
[undefined, 200, "", APPLICATION_JSON, {}],
|
||||
[{ status: 400 }, 400, "", APPLICATION_JSON, {}],
|
||||
[{ statusText: "foo" }, 200, "foo", APPLICATION_JSON, {}],
|
||||
[{ headers: {} }, 200, "", APPLICATION_JSON, {}],
|
||||
[{ headers: { "content-type": FOO_BAR } }, 200, "", FOO_BAR, {}],
|
||||
[{ headers: { "x-foo": "bar" } }, 200, "", APPLICATION_JSON, { "x-foo": "bar" }],
|
||||
];
|
||||
|
||||
for (const [init, expectedStatus, expectedStatusText, expectedContentType, expectedHeaders] of INIT_TESTS) {
|
||||
promise_test(async function () {
|
||||
const response = Response.json("hello world", init);
|
||||
assert_equals(response.type, "default", "Response's type is default");
|
||||
assert_equals(response.status, expectedStatus, "Response's status is " + expectedStatus);
|
||||
assert_equals(response.statusText, expectedStatusText, "Response's statusText is " + JSON.stringify(expectedStatusText));
|
||||
assert_equals(response.headers.get("content-type"), expectedContentType, "Response's content-type is " + expectedContentType);
|
||||
for (const key in expectedHeaders) {
|
||||
assert_equals(response.headers.get(key), expectedHeaders[key], "Response's header " + key + " is " + JSON.stringify(expectedHeaders[key]));
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
assert_equals(data, "hello world", "Response's body is 'hello world'");
|
||||
}, `Check response returned by static json() with init ${JSON.stringify(init)}`);
|
||||
}
|
||||
|
||||
const nullBodyStatus = [204, 205, 304];
|
||||
for (const status of nullBodyStatus) {
|
||||
test(function () {
|
||||
assert_throws_js(
|
||||
TypeError,
|
||||
function () {
|
||||
Response.json("hello world", { status: status });
|
||||
},
|
||||
);
|
||||
}, `Throws TypeError when calling static json() with a status of ${status}`);
|
||||
}
|
||||
|
||||
promise_test(async function () {
|
||||
const response = Response.json({ foo: "bar" });
|
||||
const data = await response.json();
|
||||
assert_equals(typeof data, "object", "Response's json body is an object");
|
||||
assert_equals(data.foo, "bar", "Response's json body is { foo: 'bar' }");
|
||||
}, "Check static json() encodes JSON objects correctly");
|
||||
|
||||
test(function () {
|
||||
assert_throws_js(
|
||||
TypeError,
|
||||
function () {
|
||||
Response.json(Symbol("foo"));
|
||||
},
|
||||
);
|
||||
}, "Check static json() throws when data is not encodable");
|
||||
|
||||
test(function () {
|
||||
const a = { b: 1 };
|
||||
a.a = a;
|
||||
assert_throws_js(
|
||||
TypeError,
|
||||
function () {
|
||||
Response.json(a);
|
||||
},
|
||||
);
|
||||
}, "Check static json() throws when data is circular");
|
||||
|
||||
promise_test(async function () {
|
||||
class CustomError extends Error {
|
||||
name = "CustomError";
|
||||
}
|
||||
assert_throws_js(
|
||||
CustomError,
|
||||
function () {
|
||||
Response.json({ get foo() { throw new CustomError("bar") }});
|
||||
}
|
||||
)
|
||||
}, "Check static json() propagates JSON serializer errors");
|
||||
|
||||
const encodingChecks = [
|
||||
["𝌆", [34, 240, 157, 140, 134, 34]],
|
||||
["\uDF06\uD834", [34, 92, 117, 100, 102, 48, 54, 92, 117, 100, 56, 51, 52, 34]],
|
||||
["\uDEAD", [34, 92, 117, 100, 101, 97, 100, 34]],
|
||||
];
|
||||
|
||||
for (const [input, expected] of encodingChecks) {
|
||||
promise_test(async function () {
|
||||
const response = Response.json(input);
|
||||
const buffer = await response.arrayBuffer();
|
||||
const data = new Uint8Array(buffer);
|
||||
assert_array_equals(data, expected);
|
||||
}, `Check response returned by static json() with input ${input}`);
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response: redirect static method
|
||||
|
||||
var url = "http://test.url:1234/";
|
||||
test(function() {
|
||||
const redirectResponse = Response.redirect(url);
|
||||
assert_equals(redirectResponse.type, "default");
|
||||
assert_false(redirectResponse.redirected);
|
||||
assert_false(redirectResponse.ok);
|
||||
assert_equals(redirectResponse.status, 302, "Default redirect status is 302");
|
||||
assert_equals(redirectResponse.headers.get("Location"), url,
|
||||
"redirected response has Location header with the correct url");
|
||||
assert_equals(redirectResponse.statusText, "");
|
||||
}, "Check default redirect response");
|
||||
|
||||
[301, 302, 303, 307, 308].forEach(function(status) {
|
||||
test(function() {
|
||||
const redirectResponse = Response.redirect(url, status);
|
||||
assert_equals(redirectResponse.type, "default");
|
||||
assert_false(redirectResponse.redirected);
|
||||
assert_false(redirectResponse.ok);
|
||||
assert_equals(redirectResponse.status, status, "Redirect status is " + status);
|
||||
assert_equals(redirectResponse.headers.get("Location"), url);
|
||||
assert_equals(redirectResponse.statusText, "");
|
||||
}, "Check response returned by static method redirect(), status = " + status);
|
||||
});
|
||||
|
||||
test(function() {
|
||||
var invalidUrl = "http://:This is not an url";
|
||||
assert_throws_js(TypeError, function() { Response.redirect(invalidUrl); },
|
||||
"Expect TypeError exception");
|
||||
}, "Check error returned when giving invalid url to redirect()");
|
||||
|
||||
var invalidRedirectStatus = [200, 309, 400, 500];
|
||||
invalidRedirectStatus.forEach(function(invalidStatus) {
|
||||
test(function() {
|
||||
assert_throws_js(RangeError, function() { Response.redirect(url, invalidStatus); },
|
||||
"Expect RangeError exception");
|
||||
}, "Check error returned when giving invalid status to redirect(), status = " + invalidStatus);
|
||||
});
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// META: global=window,worker
|
||||
// META: title=Response causes TypeError from bad chunk type
|
||||
|
||||
function runChunkTest(responseReaderMethod, testDescription) {
|
||||
promise_test(test => {
|
||||
let stream = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue("not Uint8Array");
|
||||
controller.close();
|
||||
}
|
||||
});
|
||||
|
||||
return promise_rejects_js(test, TypeError,
|
||||
new Response(stream)[responseReaderMethod](),
|
||||
'TypeError should propagate'
|
||||
)
|
||||
}, testDescription)
|
||||
}
|
||||
|
||||
runChunkTest('arrayBuffer', 'ReadableStream with non-Uint8Array chunk passed to Response.arrayBuffer() causes TypeError');
|
||||
runChunkTest('blob', 'ReadableStream with non-Uint8Array chunk passed to Response.blob() causes TypeError');
|
||||
runChunkTest('bytes', 'ReadableStream with non-Uint8Array chunk passed to Response.bytes() causes TypeError');
|
||||
runChunkTest('formData', 'ReadableStream with non-Uint8Array chunk passed to Response.formData() causes TypeError');
|
||||
runChunkTest('json', 'ReadableStream with non-Uint8Array chunk passed to Response.json() causes TypeError');
|
||||
runChunkTest('text', 'ReadableStream with non-Uint8Array chunk passed to Response.text() causes TypeError');
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
(function () {
|
||||
const failures = [];
|
||||
const pending = [];
|
||||
let passed = 0;
|
||||
|
||||
function format(value) {
|
||||
if (typeof value === "string") return JSON.stringify(value);
|
||||
try { return JSON.stringify(value); } catch (_) { return String(value); }
|
||||
}
|
||||
|
||||
function fail(message) {
|
||||
throw new Error(message || "assertion failed");
|
||||
}
|
||||
|
||||
globalThis.self = globalThis;
|
||||
globalThis.setup = function (callback) {
|
||||
callback.call({ add_cleanup() {} });
|
||||
};
|
||||
globalThis.test = function (callback, name) {
|
||||
const cleanups = [];
|
||||
try {
|
||||
callback.call({ add_cleanup(fn) { cleanups.push(fn); } });
|
||||
passed++;
|
||||
} catch (error) {
|
||||
failures.push((name || "unnamed test") + ": " + (error && error.stack ? error.stack : error));
|
||||
} finally {
|
||||
for (let index = cleanups.length - 1; index >= 0; index--) {
|
||||
try { cleanups[index](); } catch (_) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
globalThis.promise_test = function (callback, name) {
|
||||
const cleanups = [];
|
||||
const context = {
|
||||
add_cleanup(fn) { cleanups.push(fn); },
|
||||
step_timeout(fn, delay) { return setTimeout(fn, delay); },
|
||||
unreached_func(message) { return function () { assert_unreached(message); }; },
|
||||
};
|
||||
const promise = Promise.resolve().then(() => callback.call(context, context)).then(() => {
|
||||
passed++;
|
||||
}, (error) => {
|
||||
failures.push((name || "unnamed promise test") + ": " + (error && error.stack ? error.stack : error));
|
||||
}).finally(() => {
|
||||
for (let index = cleanups.length - 1; index >= 0; index--) {
|
||||
try { cleanups[index](); } catch (_) {}
|
||||
}
|
||||
});
|
||||
pending.push(promise);
|
||||
return promise;
|
||||
};
|
||||
globalThis.assert_true = function (value, message) {
|
||||
if (value !== true) fail((message || "expected true") + "; got " + format(value));
|
||||
};
|
||||
globalThis.assert_false = function (value, message) {
|
||||
if (value !== false) fail((message || "expected false") + "; got " + format(value));
|
||||
};
|
||||
globalThis.assert_equals = function (actual, expected, message) {
|
||||
if (!Object.is(actual, expected)) {
|
||||
fail((message || "values differ") + "; expected " + format(expected) + ", got " + format(actual));
|
||||
}
|
||||
};
|
||||
globalThis.assert_not_equals = function (actual, expected, message) {
|
||||
if (Object.is(actual, expected)) {
|
||||
fail((message || "values should differ") + "; both were " + format(actual));
|
||||
}
|
||||
};
|
||||
globalThis.assert_array_equals = function (actual, expected, message) {
|
||||
if (!actual || !expected || actual.length !== expected.length) {
|
||||
fail((message || "arrays differ") + "; expected " + format(expected) + ", got " + format(actual));
|
||||
}
|
||||
for (let index = 0; index < expected.length; index++) {
|
||||
if (!Object.is(actual[index], expected[index])) {
|
||||
fail((message || "arrays differ") + " at " + index + "; expected " + format(expected) + ", got " + format(actual));
|
||||
}
|
||||
}
|
||||
};
|
||||
globalThis.assert_nested_array_equals = function (actual, expected, message) {
|
||||
if (!actual || !expected || actual.length !== expected.length) {
|
||||
fail((message || "nested arrays differ") + "; expected " + format(expected) + ", got " + format(actual));
|
||||
}
|
||||
for (let index = 0; index < expected.length; index++) {
|
||||
assert_array_equals(actual[index], expected[index], (message || "nested arrays differ") + " at " + index);
|
||||
}
|
||||
};
|
||||
globalThis.assert_unreached = function (message) {
|
||||
fail(message || "assert_unreached was reached");
|
||||
};
|
||||
globalThis.assert_throws_js = function (constructor, callback, message) {
|
||||
let thrown = null;
|
||||
try { callback(); } catch (error) { thrown = error; }
|
||||
if (thrown == null) fail((message || "expected exception") + "; nothing was thrown");
|
||||
if (!(thrown instanceof constructor)) {
|
||||
fail((message || "wrong exception") + "; expected " + constructor.name + ", got " + thrown);
|
||||
}
|
||||
};
|
||||
globalThis.promise_rejects_js = function (_test, constructor, promise, message) {
|
||||
return Promise.resolve(promise).then(() => {
|
||||
fail((message || "expected promise rejection") + "; promise resolved");
|
||||
}, (error) => {
|
||||
if (!(error instanceof constructor)) {
|
||||
fail((message || "wrong rejection") + "; expected " + constructor.name + ", got " + error);
|
||||
}
|
||||
});
|
||||
};
|
||||
globalThis.assert_greater_than = function (actual, expected, message) {
|
||||
if (!(actual > expected)) fail((message || "expected greater value") + "; expected > " + expected + ", got " + actual);
|
||||
};
|
||||
globalThis.__wptFinish = async function () {
|
||||
await Promise.all(pending);
|
||||
if (failures.length) throw new Error(failures.join("\n\n"));
|
||||
return JSON.stringify({ passed, failed: 0 });
|
||||
};
|
||||
})();
|
||||
Reference in new issue
Block a user