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
@@ -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");
|
||||
Reference in new issue
Block a user