Skip to content

Commit

Permalink
Move fetch headers into its own file.
Browse files Browse the repository at this point in the history
  • Loading branch information
ry committed Nov 3, 2018
1 parent ee24254 commit 6446bc5
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 252 deletions.
1 change: 1 addition & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
84 changes: 1 addition & 83 deletions js/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>;

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<string, string, typeof HeadersBase>(
HeadersBase,
headerMap
);
import { Headers } from "./headers";

class FetchResponse implements domTypes.Response {
readonly url: string = "";
Expand Down
168 changes: 0 additions & 168 deletions js/fetch_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
});
3 changes: 2 additions & 1 deletion js/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;
85 changes: 85 additions & 0 deletions js/headers.ts
Original file line number Diff line number Diff line change
@@ -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<string, string>;

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<string, string, typeof HeadersBase>(
HeadersBase,
headerMap
);
Loading

0 comments on commit 6446bc5

Please sign in to comment.