0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-10-29 08:58:01 -04:00

check runtime FormData and Headers params (#1415)

This commit is contained in:
迷渡 2018-12-27 10:12:55 +08:00 committed by Ryan Dahl
parent 9bfe9a005b
commit 5e518b7417
5 changed files with 197 additions and 6 deletions

View file

@ -3,6 +3,7 @@ import * as domTypes from "./dom_types";
import * as blob from "./blob"; import * as blob from "./blob";
import * as file from "./file"; import * as file from "./file";
import { DomIterableMixin } from "./mixins/dom_iterable"; import { DomIterableMixin } from "./mixins/dom_iterable";
import { requiredArguments } from "./util";
const dataSymbol = Symbol("data"); const dataSymbol = Symbol("data");
@ -18,6 +19,8 @@ class FormDataBase {
append(name: string, value: string): void; append(name: string, value: string): void;
append(name: string, value: blob.DenoBlob, filename?: string): void; append(name: string, value: blob.DenoBlob, filename?: string): void;
append(name: string, value: string | blob.DenoBlob, filename?: string): void { append(name: string, value: string | blob.DenoBlob, filename?: string): void {
requiredArguments("FormData.append", arguments.length, 2);
name = String(name);
if (value instanceof blob.DenoBlob) { if (value instanceof blob.DenoBlob) {
const dfile = new file.DenoFile([value], filename || name); const dfile = new file.DenoFile([value], filename || name);
this[dataSymbol].push([name, dfile]); this[dataSymbol].push([name, dfile]);
@ -31,6 +34,8 @@ class FormDataBase {
* formData.delete('name'); * formData.delete('name');
*/ */
delete(name: string): void { delete(name: string): void {
requiredArguments("FormData.delete", arguments.length, 1);
name = String(name);
let i = 0; let i = 0;
while (i < this[dataSymbol].length) { while (i < this[dataSymbol].length) {
if (this[dataSymbol][i][0] === name) { if (this[dataSymbol][i][0] === name) {
@ -47,6 +52,8 @@ class FormDataBase {
* formData.getAll('name'); * formData.getAll('name');
*/ */
getAll(name: string): domTypes.FormDataEntryValue[] { getAll(name: string): domTypes.FormDataEntryValue[] {
requiredArguments("FormData.getAll", arguments.length, 1);
name = String(name);
const values = []; const values = [];
for (const entry of this[dataSymbol]) { for (const entry of this[dataSymbol]) {
if (entry[0] === name) { if (entry[0] === name) {
@ -63,6 +70,8 @@ class FormDataBase {
* formData.get('name'); * formData.get('name');
*/ */
get(name: string): domTypes.FormDataEntryValue | null { get(name: string): domTypes.FormDataEntryValue | null {
requiredArguments("FormData.get", arguments.length, 1);
name = String(name);
for (const entry of this[dataSymbol]) { for (const entry of this[dataSymbol]) {
if (entry[0] === name) { if (entry[0] === name) {
return entry[1]; return entry[1];
@ -78,23 +87,53 @@ class FormDataBase {
* formData.has('name'); * formData.has('name');
*/ */
has(name: string): boolean { has(name: string): boolean {
requiredArguments("FormData.has", arguments.length, 1);
name = String(name);
return this[dataSymbol].some(entry => entry[0] === name); return this[dataSymbol].some(entry => entry[0] === name);
} }
/** Sets a new value for an existing key inside a `FormData` object, or /** Sets a new value for an existing key inside a `FormData` object, or
* adds the key/value if it does not already exist. * adds the key/value if it does not already exist.
* ref: https://xhr.spec.whatwg.org/#dom-formdata-set
* *
* formData.set('name', 'value'); * formData.set('name', 'value');
*/ */
set(name: string, value: string): void; set(name: string, value: string): void;
set(name: string, value: blob.DenoBlob, filename?: string): void; set(name: string, value: blob.DenoBlob, filename?: string): void;
set(name: string, value: string | blob.DenoBlob, filename?: string): void { set(name: string, value: string | blob.DenoBlob, filename?: string): void {
this.delete(name); requiredArguments("FormData.set", arguments.length, 2);
if (value instanceof blob.DenoBlob) { name = String(name);
const dfile = new file.DenoFile([value], filename || name);
this[dataSymbol].push([name, dfile]); // If there are any entries in the context objects entry list whose name
} else { // is name, replace the first such entry with entry and remove the others
this[dataSymbol].push([name, String(value)]); let found = false;
let i = 0;
while (i < this[dataSymbol].length) {
if (this[dataSymbol][i][0] === name) {
if (!found) {
if (value instanceof blob.DenoBlob) {
const dfile = new file.DenoFile([value], filename || name);
this[dataSymbol][i][1] = dfile;
} else {
this[dataSymbol][i][1] = String(value);
}
found = true;
} else {
this[dataSymbol].splice(i, 1);
continue;
}
}
i++;
}
// Otherwise, append entry to the context objects entry list.
if (!found) {
if (value instanceof blob.DenoBlob) {
const dfile = new file.DenoFile([value], filename || name);
this[dataSymbol].push([name, dfile]);
} else {
this[dataSymbol].push([name, String(value)]);
}
} }
} }
} }

View file

@ -96,3 +96,73 @@ test(function formDataParamsForEachSuccess() {
}); });
assertEqual(callNum, init.length); assertEqual(callNum, init.length);
}); });
test(function formDataParamsArgumentsCheck() {
const methodRequireOneParam = ["delete", "getAll", "get", "has", "forEach"];
const methodRequireTwoParams = ["append", "set"];
methodRequireOneParam.forEach(method => {
const formData = new FormData();
let hasThrown = 0;
let errMsg = "";
try {
formData[method]();
hasThrown = 1;
} catch (err) {
errMsg = err.message;
if (err instanceof TypeError) {
hasThrown = 2;
} else {
hasThrown = 3;
}
}
assertEqual(hasThrown, 2);
assertEqual(
errMsg,
`FormData.${method} requires at least 1 argument, but only 0 present`
);
});
methodRequireTwoParams.forEach(method => {
const formData = new FormData();
let hasThrown = 0;
let errMsg = "";
try {
formData[method]();
hasThrown = 1;
} catch (err) {
errMsg = err.message;
if (err instanceof TypeError) {
hasThrown = 2;
} else {
hasThrown = 3;
}
}
assertEqual(hasThrown, 2);
assertEqual(
errMsg,
`FormData.${method} requires at least 2 arguments, but only 0 present`
);
hasThrown = 0;
errMsg = "";
try {
formData[method]("foo");
hasThrown = 1;
} catch (err) {
errMsg = err.message;
if (err instanceof TypeError) {
hasThrown = 2;
} else {
hasThrown = 3;
}
}
assertEqual(hasThrown, 2);
assertEqual(
errMsg,
`FormData.${method} requires at least 2 arguments, but only 1 present`
);
});
});

View file

@ -1,6 +1,7 @@
// Copyright 2018 the Deno authors. All rights reserved. MIT license. // Copyright 2018 the Deno authors. All rights reserved. MIT license.
import * as domTypes from "./dom_types"; import * as domTypes from "./dom_types";
import { DomIterableMixin } from "./mixins/dom_iterable"; import { DomIterableMixin } from "./mixins/dom_iterable";
import { requiredArguments } from "./util";
// From node-fetch // From node-fetch
// Copyright (c) 2016 David Frank. MIT License. // Copyright (c) 2016 David Frank. MIT License.
@ -75,6 +76,7 @@ class HeadersBase {
// ref: https://fetch.spec.whatwg.org/#concept-headers-append // ref: https://fetch.spec.whatwg.org/#concept-headers-append
append(name: string, value: string): void { append(name: string, value: string): void {
requiredArguments("Headers.append", arguments.length, 2);
const [newname, newvalue] = this._normalizeParams(name, value); const [newname, newvalue] = this._normalizeParams(name, value);
this._validateName(newname); this._validateName(newname);
this._validateValue(newvalue); this._validateValue(newvalue);
@ -84,12 +86,14 @@ class HeadersBase {
} }
delete(name: string): void { delete(name: string): void {
requiredArguments("Headers.delete", arguments.length, 1);
const [newname] = this._normalizeParams(name); const [newname] = this._normalizeParams(name);
this._validateName(newname); this._validateName(newname);
this[headerMap].delete(newname); this[headerMap].delete(newname);
} }
get(name: string): string | null { get(name: string): string | null {
requiredArguments("Headers.get", arguments.length, 1);
const [newname] = this._normalizeParams(name); const [newname] = this._normalizeParams(name);
this._validateName(newname); this._validateName(newname);
const value = this[headerMap].get(newname); const value = this[headerMap].get(newname);
@ -97,12 +101,14 @@ class HeadersBase {
} }
has(name: string): boolean { has(name: string): boolean {
requiredArguments("Headers.has", arguments.length, 1);
const [newname] = this._normalizeParams(name); const [newname] = this._normalizeParams(name);
this._validateName(newname); this._validateName(newname);
return this[headerMap].has(newname); return this[headerMap].has(newname);
} }
set(name: string, value: string): void { set(name: string, value: string): void {
requiredArguments("Headers.set", arguments.length, 2);
const [newname, newvalue] = this._normalizeParams(name, value); const [newname, newvalue] = this._normalizeParams(name, value);
this._validateName(newname); this._validateName(newname);
this._validateValue(newvalue); this._validateValue(newvalue);

View file

@ -228,3 +228,73 @@ test(function headerIllegalReject() {
// 'o k' is valid value but invalid name // 'o k' is valid value but invalid name
new Headers({ "He-y": "o k" }); new Headers({ "He-y": "o k" });
}); });
test(function headerParamsArgumentsCheck() {
const methodRequireOneParam = ["delete", "get", "has", "forEach"];
const methodRequireTwoParams = ["append", "set"];
methodRequireOneParam.forEach(method => {
const headers = new Headers();
let hasThrown = 0;
let errMsg = "";
try {
headers[method]();
hasThrown = 1;
} catch (err) {
errMsg = err.message;
if (err instanceof TypeError) {
hasThrown = 2;
} else {
hasThrown = 3;
}
}
assertEqual(hasThrown, 2);
assertEqual(
errMsg,
`Headers.${method} requires at least 1 argument, but only 0 present`
);
});
methodRequireTwoParams.forEach(method => {
const headers = new Headers();
let hasThrown = 0;
let errMsg = "";
try {
headers[method]();
hasThrown = 1;
} catch (err) {
errMsg = err.message;
if (err instanceof TypeError) {
hasThrown = 2;
} else {
hasThrown = 3;
}
}
assertEqual(hasThrown, 2);
assertEqual(
errMsg,
`Headers.${method} requires at least 2 arguments, but only 0 present`
);
hasThrown = 0;
errMsg = "";
try {
headers[method]("foo");
hasThrown = 1;
} catch (err) {
errMsg = err.message;
if (err instanceof TypeError) {
hasThrown = 2;
} else {
hasThrown = 3;
}
}
assertEqual(hasThrown, 2);
assertEqual(
errMsg,
`Headers.${method} requires at least 2 arguments, but only 1 present`
);
});
});

View file

@ -1,5 +1,6 @@
import { DomIterable } from "../dom_types"; import { DomIterable } from "../dom_types";
import { globalEval } from "../global_eval"; import { globalEval } from "../global_eval";
import { requiredArguments } from "../util";
// if we import it directly from "globals" it will break the unit tests so we // if we import it directly from "globals" it will break the unit tests so we
// have to grab a reference to the global scope a different way // have to grab a reference to the global scope a different way
@ -52,6 +53,11 @@ export function DomIterableMixin<K, V, TBase extends Constructor>(
// tslint:disable-next-line:no-any // tslint:disable-next-line:no-any
thisArg?: any thisArg?: any
): void { ): void {
requiredArguments(
`${this.constructor.name}.forEach`,
arguments.length,
1
);
callbackfn = callbackfn.bind(thisArg == null ? window : Object(thisArg)); callbackfn = callbackfn.bind(thisArg == null ? window : Object(thisArg));
for (const [key, value] of (this as any)[dataSymbol]) { for (const [key, value] of (this as any)[dataSymbol]) {
callbackfn(value, key, this); callbackfn(value, key, this);