165 lines
3.4 KiB
JavaScript
165 lines
3.4 KiB
JavaScript
"use strict";
|
|
|
|
const {
|
|
isForbidden,
|
|
isForbiddenResponse,
|
|
isPrivilegedNoCORSRequest,
|
|
isNoCORSSafelistedRequest,
|
|
isCORSWhitelisted
|
|
} = require("./header-types");
|
|
const HeaderList = require("./header-list");
|
|
|
|
function assertName(name) {
|
|
if (!name.match(/^[!#$%&'*+\-.^`|~\w]+$/)) {
|
|
throw new TypeError("name is invalid");
|
|
}
|
|
}
|
|
|
|
function assertValue(value) {
|
|
if (value.match(/[\0\r\n]/)) {
|
|
throw new TypeError("value is invalid");
|
|
}
|
|
}
|
|
|
|
class HeadersImpl {
|
|
constructor(globalObject, args) {
|
|
this.guard = "none";
|
|
this.headersList = new HeaderList();
|
|
|
|
if (args[0]) {
|
|
this._fill(args[0]);
|
|
}
|
|
}
|
|
|
|
_fill(init) {
|
|
if (Array.isArray(init)) {
|
|
for (const header of init) {
|
|
if (header.length !== 2) {
|
|
throw new TypeError("init is invalid");
|
|
}
|
|
this.append(header[0], header[1]);
|
|
}
|
|
} else {
|
|
for (const key of Object.keys(init)) {
|
|
this.append(key, init[key]);
|
|
}
|
|
}
|
|
}
|
|
|
|
has(name) {
|
|
assertName(name);
|
|
return this.headersList.contains(name);
|
|
}
|
|
|
|
get(name) {
|
|
assertName(name);
|
|
return this.headersList.get(name);
|
|
}
|
|
|
|
_removePrivilegedNoCORSHeaders() {
|
|
this.headersList.delete("range");
|
|
}
|
|
|
|
append(name, value) {
|
|
value = value.trim();
|
|
assertName(name);
|
|
assertValue(value);
|
|
|
|
switch (this.guard) {
|
|
case "immutable":
|
|
throw new TypeError("Headers is immutable");
|
|
case "request":
|
|
if (isForbidden(name)) {
|
|
return;
|
|
}
|
|
break;
|
|
case "request-no-cors": {
|
|
let temporaryValue = this.get(name);
|
|
if (temporaryValue === null) {
|
|
temporaryValue = value;
|
|
} else {
|
|
temporaryValue += `, ${value}`;
|
|
}
|
|
if (!isCORSWhitelisted(name, value)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case "response":
|
|
if (isForbiddenResponse(name)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
|
|
this.headersList.append(name, value);
|
|
this._removePrivilegedNoCORSHeaders();
|
|
}
|
|
|
|
set(name, value) {
|
|
value = value.trim();
|
|
assertName(name);
|
|
assertValue(value);
|
|
|
|
switch (this.guard) {
|
|
case "immutable":
|
|
throw new TypeError("Headers is immutable");
|
|
case "request":
|
|
if (isForbidden(name)) {
|
|
return;
|
|
}
|
|
break;
|
|
case "request-no-cors": {
|
|
if (!isCORSWhitelisted(name, value)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case "response":
|
|
if (isForbiddenResponse(name)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
this.headersList.set(name, value);
|
|
this._removePrivilegedNoCORSHeaders();
|
|
}
|
|
|
|
delete(name) {
|
|
assertName(name);
|
|
|
|
switch (this.guard) {
|
|
case "immutable":
|
|
throw new TypeError("Headers is immutable");
|
|
case "request":
|
|
if (isForbidden(name)) {
|
|
return;
|
|
}
|
|
break;
|
|
case "request-no-cors": {
|
|
if (
|
|
!isNoCORSSafelistedRequest(name) &&
|
|
!isPrivilegedNoCORSRequest(name)
|
|
) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case "response":
|
|
if (isForbiddenResponse(name)) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
this.headersList.delete(name);
|
|
this._removePrivilegedNoCORSHeaders();
|
|
}
|
|
|
|
* [Symbol.iterator]() {
|
|
for (const header of this.headersList.sortAndCombine()) {
|
|
yield header;
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.implementation = HeadersImpl;
|