mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-07 17:10:29 +00:00
feat: HTTP add TLS fingerprint fallback (#1444)
This commit is contained in:
68 files changed
+7243
-1604
No files matched your search
+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');
|
||||
Reference in new issue
Block a user