From 6446bc532840319ac9603c016e2ef419094fdeec Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 2 Nov 2018 21:43:37 -0400 Subject: [PATCH] Move fetch headers into its own file. --- BUILD.gn | 1 + js/fetch.ts | 84 +--------------------- js/fetch_test.ts | 168 -------------------------------------------- js/globals.ts | 3 +- js/headers.ts | 85 ++++++++++++++++++++++ js/headers_test.ts | 171 +++++++++++++++++++++++++++++++++++++++++++++ js/unit_tests.ts | 1 + 7 files changed, 261 insertions(+), 252 deletions(-) create mode 100644 js/headers.ts create mode 100644 js/headers_test.ts diff --git a/BUILD.gn b/BUILD.gn index c3c18107dd..6c8172103a 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -86,6 +86,7 @@ ts_sources = [ "js/dom_types.ts", "js/errors.ts", "js/fetch.ts", + "js/headers.ts", "js/file_info.ts", "js/files.ts", "js/flatbuffers.ts", diff --git a/js/fetch.ts b/js/fetch.ts index f4c7aaf811..16172b776f 100644 --- a/js/fetch.ts +++ b/js/fetch.ts @@ -13,89 +13,7 @@ import * as msg from "gen/msg_generated"; import * as domTypes from "./dom_types"; import { TextDecoder } from "./text_encoding"; import { DenoBlob } from "./blob"; -import { DomIterableMixin } from "./mixins/dom_iterable"; - -// tslint:disable-next-line:no-any -function isHeaders(value: any): value is domTypes.Headers { - return value instanceof Headers; -} - -const headerMap = Symbol("header map"); - -// ref: https://fetch.spec.whatwg.org/#dom-headers -class HeadersBase { - private [headerMap]: Map; - - private _normalizeParams(name: string, value?: string): string[] { - name = String(name).toLowerCase(); - value = String(value).trim(); - return [name, 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 [rawName, rawValue] of init) { - const [name, value] = this._normalizeParams(rawName, rawValue); - 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[headerMap].set(name, value); - } - } - } - } - - append(name: string, value: string): void { - const [newname, newvalue] = this._normalizeParams(name, value); - const v = this[headerMap].get(newname); - const str = v ? `${v}, ${newvalue}` : newvalue; - this[headerMap].set(newname, str); - } - - delete(name: string): void { - const [newname] = this._normalizeParams(name); - this[headerMap].delete(newname); - } - - get(name: string): string | null { - const [newname] = this._normalizeParams(name); - const value = this[headerMap].get(newname); - return value || null; - } - - has(name: string): boolean { - const [newname] = this._normalizeParams(name); - return this[headerMap].has(newname); - } - - set(name: string, value: string): void { - const [newname, newvalue] = this._normalizeParams(name, value); - this[headerMap].set(newname, newvalue); - } -} - -// @internal -// tslint:disable-next-line:variable-name -export const Headers = DomIterableMixin( - HeadersBase, - headerMap -); +import { Headers } from "./headers"; class FetchResponse implements domTypes.Response { readonly url: string = ""; diff --git a/js/fetch_test.ts b/js/fetch_test.ts index f92968d78c..0c60efb9d6 100644 --- a/js/fetch_test.ts +++ b/js/fetch_test.ts @@ -46,171 +46,3 @@ testPerm({ net: true }, async function responseClone() { assertEqual(ab[i], ab1[i]); } }); - -// Logic heavily copied from web-platform-tests, make -// sure pass mostly header basic test -/* tslint:disable-next-line:max-line-length */ -// ref: https://github.com/web-platform-tests/wpt/blob/7c50c216081d6ea3c9afe553ee7b64534020a1b2/fetch/api/headers/headers-basic.html -/* tslint:disable:no-unused-expression */ -test(function newHeaderTest() { - new Headers(); - new Headers(undefined); - new Headers({}); - try { - new Headers(null); - } catch (e) { - assertEqual( - e.message, - "Failed to construct 'Headers'; The provided value was not valid" - ); - } -}); - -const headerDict = { - name1: "value1", - name2: "value2", - name3: "value3", - name4: undefined, - "Content-Type": "value4" -}; -const headerSeq = []; -for (const name in headerDict) { - headerSeq.push([name, headerDict[name]]); -} - -test(function newHeaderWithSequence() { - const headers = new Headers(headerSeq); - for (const name in headerDict) { - assertEqual(headers.get(name), String(headerDict[name])); - } - assertEqual(headers.get("length"), null); -}); - -test(function newHeaderWithRecord() { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assertEqual(headers.get(name), String(headerDict[name])); - } -}); - -test(function newHeaderWithHeadersInstance() { - const headers = new Headers(headerDict); - const headers2 = new Headers(headers); - for (const name in headerDict) { - assertEqual(headers2.get(name), String(headerDict[name])); - } -}); - -test(function headerAppendSuccess() { - const headers = new Headers(); - for (const name in headerDict) { - headers.append(name, headerDict[name]); - assertEqual(headers.get(name), String(headerDict[name])); - } -}); - -test(function headerSetSuccess() { - const headers = new Headers(); - for (const name in headerDict) { - headers.set(name, headerDict[name]); - assertEqual(headers.get(name), String(headerDict[name])); - } -}); - -test(function headerHasSuccess() { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assert(headers.has(name), "headers has name " + name); - /* tslint:disable-next-line:max-line-length */ - assert( - !headers.has("nameNotInHeaders"), - "headers do not have header: nameNotInHeaders" - ); - } -}); - -test(function headerDeleteSuccess() { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assert(headers.has(name), "headers have a header: " + name); - headers.delete(name); - assert(!headers.has(name), "headers do not have anymore a header: " + name); - } -}); - -test(function headerGetSuccess() { - const headers = new Headers(headerDict); - for (const name in headerDict) { - assertEqual(headers.get(name), String(headerDict[name])); - assertEqual(headers.get("nameNotInHeaders"), null); - } -}); - -test(function headerEntriesSuccess() { - const headers = new Headers(headerDict); - const iterators = headers.entries(); - for (const it of iterators) { - const key = it[0]; - const value = it[1]; - assert(headers.has(key)); - assertEqual(value, headers.get(key)); - } -}); - -test(function headerKeysSuccess() { - const headers = new Headers(headerDict); - const iterators = headers.keys(); - for (const it of iterators) { - assert(headers.has(it)); - } -}); - -test(function headerValuesSuccess() { - const headers = new Headers(headerDict); - const iterators = headers.values(); - const entries = headers.entries(); - const values = []; - for (const pair of entries) { - values.push(pair[1]); - } - for (const it of iterators) { - assert(values.includes(it)); - } -}); - -const headerEntriesDict = { - name1: "value1", - Name2: "value2", - name: "value3", - "content-Type": "value4", - "Content-Typ": "value5", - "Content-Types": "value6" -}; - -test(function headerForEachSuccess() { - const headers = new Headers(headerEntriesDict); - const keys = Object.keys(headerEntriesDict); - keys.forEach(key => { - const value = headerEntriesDict[key]; - const newkey = key.toLowerCase(); - headerEntriesDict[newkey] = value; - }); - let callNum = 0; - headers.forEach((value, key, container) => { - assertEqual(headers, container); - assertEqual(value, headerEntriesDict[key]); - callNum++; - }); - assertEqual(callNum, keys.length); -}); - -test(function headerSymbolIteratorSuccess() { - assert(Symbol.iterator in Headers.prototype); - const headers = new Headers(headerEntriesDict); - for (const header of headers) { - const key = header[0]; - const value = header[1]; - assert(headers.has(key)); - assertEqual(value, headers.get(key)); - } -}); diff --git a/js/globals.ts b/js/globals.ts index d29d3b3da0..a09e6ed9b4 100644 --- a/js/globals.ts +++ b/js/globals.ts @@ -2,6 +2,7 @@ import * as blob from "./blob"; import * as console_ from "./console"; import * as fetch_ from "./fetch"; +import { Headers } from "./headers"; import { globalEval } from "./global_eval"; import { libdeno } from "./libdeno"; import * as textEncoding from "./text_encoding"; @@ -41,5 +42,5 @@ window.fetch = fetch_.fetch; // using the `as` keyword to mask the internal types when generating the // runtime library -window.Headers = fetch_.Headers as domTypes.HeadersConstructor; +window.Headers = Headers as domTypes.HeadersConstructor; window.Blob = blob.DenoBlob; diff --git a/js/headers.ts b/js/headers.ts new file mode 100644 index 0000000000..6892b0ef21 --- /dev/null +++ b/js/headers.ts @@ -0,0 +1,85 @@ +// Copyright 2018 the Deno authors. All rights reserved. MIT license. +import * as domTypes from "./dom_types"; +import { DomIterableMixin } from "./mixins/dom_iterable"; + +// tslint:disable-next-line:no-any +function isHeaders(value: any): value is domTypes.Headers { + return value instanceof Headers; +} + +const headerMap = Symbol("header map"); + +// ref: https://fetch.spec.whatwg.org/#dom-headers +class HeadersBase { + private [headerMap]: Map; + + private _normalizeParams(name: string, value?: string): string[] { + name = String(name).toLowerCase(); + value = String(value).trim(); + return [name, 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 [rawName, rawValue] of init) { + const [name, value] = this._normalizeParams(rawName, rawValue); + 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[headerMap].set(name, value); + } + } + } + } + + append(name: string, value: string): void { + const [newname, newvalue] = this._normalizeParams(name, value); + const v = this[headerMap].get(newname); + const str = v ? `${v}, ${newvalue}` : newvalue; + this[headerMap].set(newname, str); + } + + delete(name: string): void { + const [newname] = this._normalizeParams(name); + this[headerMap].delete(newname); + } + + get(name: string): string | null { + const [newname] = this._normalizeParams(name); + const value = this[headerMap].get(newname); + return value || null; + } + + has(name: string): boolean { + const [newname] = this._normalizeParams(name); + return this[headerMap].has(newname); + } + + set(name: string, value: string): void { + const [newname, newvalue] = this._normalizeParams(name, value); + this[headerMap].set(newname, newvalue); + } +} + +// @internal +// tslint:disable-next-line:variable-name +export const Headers = DomIterableMixin( + HeadersBase, + headerMap +); diff --git a/js/headers_test.ts b/js/headers_test.ts new file mode 100644 index 0000000000..6980c3d7e9 --- /dev/null +++ b/js/headers_test.ts @@ -0,0 +1,171 @@ +// Copyright 2018 the Deno authors. All rights reserved. MIT license. +import { test, testPerm, assert, assertEqual } from "./test_util.ts"; +import * as deno from "deno"; + +// Logic heavily copied from web-platform-tests, make +// sure pass mostly header basic test +/* tslint:disable-next-line:max-line-length */ +// ref: https://github.com/web-platform-tests/wpt/blob/7c50c216081d6ea3c9afe553ee7b64534020a1b2/fetch/api/headers/headers-basic.html +/* tslint:disable:no-unused-expression */ +test(function newHeaderTest() { + new Headers(); + new Headers(undefined); + new Headers({}); + try { + new Headers(null); + } catch (e) { + assertEqual( + e.message, + "Failed to construct 'Headers'; The provided value was not valid" + ); + } +}); + +const headerDict = { + name1: "value1", + name2: "value2", + name3: "value3", + name4: undefined, + "Content-Type": "value4" +}; +const headerSeq = []; +for (const name in headerDict) { + headerSeq.push([name, headerDict[name]]); +} + +test(function newHeaderWithSequence() { + const headers = new Headers(headerSeq); + for (const name in headerDict) { + assertEqual(headers.get(name), String(headerDict[name])); + } + assertEqual(headers.get("length"), null); +}); + +test(function newHeaderWithRecord() { + const headers = new Headers(headerDict); + for (const name in headerDict) { + assertEqual(headers.get(name), String(headerDict[name])); + } +}); + +test(function newHeaderWithHeadersInstance() { + const headers = new Headers(headerDict); + const headers2 = new Headers(headers); + for (const name in headerDict) { + assertEqual(headers2.get(name), String(headerDict[name])); + } +}); + +test(function headerAppendSuccess() { + const headers = new Headers(); + for (const name in headerDict) { + headers.append(name, headerDict[name]); + assertEqual(headers.get(name), String(headerDict[name])); + } +}); + +test(function headerSetSuccess() { + const headers = new Headers(); + for (const name in headerDict) { + headers.set(name, headerDict[name]); + assertEqual(headers.get(name), String(headerDict[name])); + } +}); + +test(function headerHasSuccess() { + const headers = new Headers(headerDict); + for (const name in headerDict) { + assert(headers.has(name), "headers has name " + name); + /* tslint:disable-next-line:max-line-length */ + assert( + !headers.has("nameNotInHeaders"), + "headers do not have header: nameNotInHeaders" + ); + } +}); + +test(function headerDeleteSuccess() { + const headers = new Headers(headerDict); + for (const name in headerDict) { + assert(headers.has(name), "headers have a header: " + name); + headers.delete(name); + assert(!headers.has(name), "headers do not have anymore a header: " + name); + } +}); + +test(function headerGetSuccess() { + const headers = new Headers(headerDict); + for (const name in headerDict) { + assertEqual(headers.get(name), String(headerDict[name])); + assertEqual(headers.get("nameNotInHeaders"), null); + } +}); + +test(function headerEntriesSuccess() { + const headers = new Headers(headerDict); + const iterators = headers.entries(); + for (const it of iterators) { + const key = it[0]; + const value = it[1]; + assert(headers.has(key)); + assertEqual(value, headers.get(key)); + } +}); + +test(function headerKeysSuccess() { + const headers = new Headers(headerDict); + const iterators = headers.keys(); + for (const it of iterators) { + assert(headers.has(it)); + } +}); + +test(function headerValuesSuccess() { + const headers = new Headers(headerDict); + const iterators = headers.values(); + const entries = headers.entries(); + const values = []; + for (const pair of entries) { + values.push(pair[1]); + } + for (const it of iterators) { + assert(values.includes(it)); + } +}); + +const headerEntriesDict = { + name1: "value1", + Name2: "value2", + name: "value3", + "content-Type": "value4", + "Content-Typ": "value5", + "Content-Types": "value6" +}; + +test(function headerForEachSuccess() { + const headers = new Headers(headerEntriesDict); + const keys = Object.keys(headerEntriesDict); + keys.forEach(key => { + const value = headerEntriesDict[key]; + const newkey = key.toLowerCase(); + headerEntriesDict[newkey] = value; + }); + let callNum = 0; + headers.forEach((value, key, container) => { + assertEqual(headers, container); + assertEqual(value, headerEntriesDict[key]); + callNum++; + }); + assertEqual(callNum, keys.length); +}); + +test(function headerSymbolIteratorSuccess() { + assert(Symbol.iterator in Headers.prototype); + const headers = new Headers(headerEntriesDict); + for (const header of headers) { + const key = header[0]; + const value = header[1]; + assert(headers.has(key)); + assertEqual(value, headers.get(key)); + } +}); diff --git a/js/unit_tests.ts b/js/unit_tests.ts index 22e5fbdc04..fb3d0018ee 100644 --- a/js/unit_tests.ts +++ b/js/unit_tests.ts @@ -11,6 +11,7 @@ import "./copy_file_test.ts"; import "./dir_test"; import "./fetch_test.ts"; import "./files_test.ts"; +import "./headers_test.ts"; import "./make_temp_dir_test.ts"; import "./metrics_test.ts"; import "./mixins/dom_iterable_test.ts";