summaryrefslogtreecommitdiff
path: root/cli/js/headers.ts
diff options
context:
space:
mode:
Diffstat (limited to 'cli/js/headers.ts')
-rw-r--r--cli/js/headers.ts139
1 files changed, 139 insertions, 0 deletions
diff --git a/cli/js/headers.ts b/cli/js/headers.ts
new file mode 100644
index 000000000..dc0de54dd
--- /dev/null
+++ b/cli/js/headers.ts
@@ -0,0 +1,139 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+import * as domTypes from "./dom_types.ts";
+import { DomIterableMixin } from "./mixins/dom_iterable.ts";
+import { requiredArguments } from "./util.ts";
+
+// From node-fetch
+// Copyright (c) 2016 David Frank. MIT License.
+const invalidTokenRegex = /[^\^_`a-zA-Z\-0-9!#$%&'*+.|~]/;
+const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+function isHeaders(value: any): value is domTypes.Headers {
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ return value instanceof Headers;
+}
+
+const headerMap = Symbol("header map");
+
+// ref: https://fetch.spec.whatwg.org/#dom-headers
+class HeadersBase {
+ private [headerMap]: Map<string, string>;
+ // TODO: headerGuard? Investigate if it is needed
+ // node-fetch did not implement this but it is in the spec
+
+ private _normalizeParams(name: string, value?: string): string[] {
+ name = String(name).toLowerCase();
+ value = String(value).trim();
+ return [name, value];
+ }
+
+ // The following name/value validations are copied from
+ // https://github.com/bitinn/node-fetch/blob/master/src/headers.js
+ // Copyright (c) 2016 David Frank. MIT License.
+ private _validateName(name: string): void {
+ if (invalidTokenRegex.test(name) || name === "") {
+ throw new TypeError(`${name} is not a legal HTTP header name`);
+ }
+ }
+
+ private _validateValue(value: string): void {
+ if (invalidHeaderCharRegex.test(value)) {
+ throw new TypeError(`${value} is not a legal HTTP header value`);
+ }
+ }
+
+ constructor(init?: domTypes.HeadersInit) {
+ if (init === null) {
+ throw new TypeError(
+ "Failed to construct 'Headers'; The provided value was not valid"
+ );
+ } else if (isHeaders(init)) {
+ this[headerMap] = new Map(init);
+ } else {
+ this[headerMap] = new Map();
+ if (Array.isArray(init)) {
+ for (const tuple of init) {
+ // If header does not contain exactly two items,
+ // then throw a TypeError.
+ // ref: https://fetch.spec.whatwg.org/#concept-headers-fill
+ requiredArguments(
+ "Headers.constructor tuple array argument",
+ tuple.length,
+ 2
+ );
+
+ const [name, value] = this._normalizeParams(tuple[0], tuple[1]);
+ this._validateName(name);
+ this._validateValue(value);
+ const existingValue = this[headerMap].get(name);
+ this[headerMap].set(
+ name,
+ existingValue ? `${existingValue}, ${value}` : value
+ );
+ }
+ } else if (init) {
+ const names = Object.keys(init);
+ for (const rawName of names) {
+ const rawValue = init[rawName];
+ const [name, value] = this._normalizeParams(rawName, rawValue);
+ this._validateName(name);
+ this._validateValue(value);
+ this[headerMap].set(name, value);
+ }
+ }
+ }
+ }
+
+ // ref: https://fetch.spec.whatwg.org/#concept-headers-append
+ append(name: string, value: string): void {
+ requiredArguments("Headers.append", arguments.length, 2);
+ const [newname, newvalue] = this._normalizeParams(name, value);
+ this._validateName(newname);
+ this._validateValue(newvalue);
+ const v = this[headerMap].get(newname);
+ const str = v ? `${v}, ${newvalue}` : newvalue;
+ this[headerMap].set(newname, str);
+ }
+
+ delete(name: string): void {
+ requiredArguments("Headers.delete", arguments.length, 1);
+ const [newname] = this._normalizeParams(name);
+ this._validateName(newname);
+ this[headerMap].delete(newname);
+ }
+
+ get(name: string): string | null {
+ requiredArguments("Headers.get", arguments.length, 1);
+ const [newname] = this._normalizeParams(name);
+ this._validateName(newname);
+ const value = this[headerMap].get(newname);
+ return value || null;
+ }
+
+ has(name: string): boolean {
+ requiredArguments("Headers.has", arguments.length, 1);
+ const [newname] = this._normalizeParams(name);
+ this._validateName(newname);
+ return this[headerMap].has(newname);
+ }
+
+ set(name: string, value: string): void {
+ requiredArguments("Headers.set", arguments.length, 2);
+ const [newname, newvalue] = this._normalizeParams(name, value);
+ this._validateName(newname);
+ this._validateValue(newvalue);
+ this[headerMap].set(newname, newvalue);
+ }
+
+ get [Symbol.toStringTag](): string {
+ return "Headers";
+ }
+}
+
+// @internal
+export class Headers extends DomIterableMixin<
+ string,
+ string,
+ typeof HeadersBase
+>(HeadersBase, headerMap) {}