mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00
util: add MIME utilities (#21128)
Co-authored-by: Rich Trott <rtrott@gmail.com> Co-authored-by: Antoine du Hamel <duhamelantoine1995@gmail.com> PR-URL: https://github.com/nodejs/node/pull/21128 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
1f7e8d609a
commit
87cdf7d412
16 changed files with 5123 additions and 17 deletions
|
@ -1967,6 +1967,12 @@ An invalid HTTP token was supplied.
|
||||||
|
|
||||||
An IP address is not valid.
|
An IP address is not valid.
|
||||||
|
|
||||||
|
<a id="ERR_INVALID_MIME_SYNTAX"></a>
|
||||||
|
|
||||||
|
### `ERR_INVALID_MIME_SYNTAX`
|
||||||
|
|
||||||
|
The syntax of a MIME is not valid.
|
||||||
|
|
||||||
<a id="ERR_INVALID_MODULE"></a>
|
<a id="ERR_INVALID_MODULE"></a>
|
||||||
|
|
||||||
### `ERR_INVALID_MODULE`
|
### `ERR_INVALID_MODULE`
|
||||||
|
|
353
doc/api/util.md
353
doc/api/util.md
|
@ -1024,6 +1024,355 @@ Otherwise, returns `false`.
|
||||||
See [`assert.deepStrictEqual()`][] for more information about deep strict
|
See [`assert.deepStrictEqual()`][] for more information about deep strict
|
||||||
equality.
|
equality.
|
||||||
|
|
||||||
|
## Class: `util.MIMEType`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
> Stability: 1 - Experimental
|
||||||
|
|
||||||
|
An implementation of [the MIMEType class](https://bmeck.github.io/node-proposal-mime-api/).
|
||||||
|
|
||||||
|
In accordance with browser conventions, all properties of `MIMEType` objects
|
||||||
|
are implemented as getters and setters on the class prototype, rather than as
|
||||||
|
data properties on the object itself.
|
||||||
|
|
||||||
|
A MIME string is a structured string containing multiple meaningful
|
||||||
|
components. When parsed, a `MIMEType` object is returned containing
|
||||||
|
properties for each of these components.
|
||||||
|
|
||||||
|
### Constructor: `new MIMEType(input)`
|
||||||
|
|
||||||
|
* `input` {string} The input MIME to parse
|
||||||
|
|
||||||
|
Creates a new `MIMEType` object by parsing the `input`.
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEType } from 'node:util';
|
||||||
|
|
||||||
|
const myMIME = new MIMEType('text/plain');
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEType } = require('node:util');
|
||||||
|
|
||||||
|
const myMIME = new MIMEType('text/plain');
|
||||||
|
```
|
||||||
|
|
||||||
|
A `TypeError` will be thrown if the `input` is not a valid MIME. Note
|
||||||
|
that an effort will be made to coerce the given values into strings. For
|
||||||
|
instance:
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEType } from 'node:util';
|
||||||
|
const myMIME = new MIMEType({ toString: () => 'text/plain' });
|
||||||
|
console.log(String(myMIME));
|
||||||
|
// Prints: text/plain
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEType } = require('node:util');
|
||||||
|
const myMIME = new MIMEType({ toString: () => 'text/plain' });
|
||||||
|
console.log(String(myMIME));
|
||||||
|
// Prints: text/plain
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `mime.type`
|
||||||
|
|
||||||
|
* {string}
|
||||||
|
|
||||||
|
Gets and sets the type portion of the MIME.
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEType } from 'node:util';
|
||||||
|
|
||||||
|
const myMIME = new MIMEType('text/javascript');
|
||||||
|
console.log(myMIME.type);
|
||||||
|
// Prints: text
|
||||||
|
myMIME.type = 'application';
|
||||||
|
console.log(myMIME.type);
|
||||||
|
// Prints: application
|
||||||
|
console.log(String(myMIME));
|
||||||
|
// Prints: application/javascript
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEType } = require('node:util');
|
||||||
|
|
||||||
|
const myMIME = new MIMEType('text/javascript');
|
||||||
|
console.log(myMIME.type);
|
||||||
|
// Prints: text
|
||||||
|
myMIME.type = 'application';
|
||||||
|
console.log(myMIME.type);
|
||||||
|
// Prints: application
|
||||||
|
console.log(String(myMIME));
|
||||||
|
// Prints: application/javascript/javascript
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `mime.subtype`
|
||||||
|
|
||||||
|
* {string}
|
||||||
|
|
||||||
|
Gets and sets the subtype portion of the MIME.
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEType } from 'node:util';
|
||||||
|
|
||||||
|
const myMIME = new MIMEType('text/ecmascript');
|
||||||
|
console.log(myMIME.subtype);
|
||||||
|
// Prints: ecmascript
|
||||||
|
myMIME.subtype = 'javascript';
|
||||||
|
console.log(myMIME.subtype);
|
||||||
|
// Prints: javascript
|
||||||
|
console.log(String(myMIME));
|
||||||
|
// Prints: text/javascript
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEType } = require('node:util');
|
||||||
|
|
||||||
|
const myMIME = new MIMEType('text/ecmascript');
|
||||||
|
console.log(myMIME.subtype);
|
||||||
|
// Prints: ecmascript
|
||||||
|
myMIME.subtype = 'javascript';
|
||||||
|
console.log(myMIME.subtype);
|
||||||
|
// Prints: javascript
|
||||||
|
console.log(String(myMIME));
|
||||||
|
// Prints: text/javascript
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `mime.essence`
|
||||||
|
|
||||||
|
* {string}
|
||||||
|
|
||||||
|
Gets the essence of the MIME. This property is read only.
|
||||||
|
Use `mime.type` or `mime.subtype` to alter the MIME.
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEType } from 'node:util';
|
||||||
|
|
||||||
|
const myMIME = new MIMEType('text/javascript;key=value');
|
||||||
|
console.log(myMIME.essence);
|
||||||
|
// Prints: text/javascript
|
||||||
|
myMIME.type = 'application';
|
||||||
|
console.log(myMIME.essence);
|
||||||
|
// Prints: application/javascript
|
||||||
|
console.log(String(myMIME));
|
||||||
|
// Prints: application/javascript;key=value
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEType } = require('node:util');
|
||||||
|
|
||||||
|
const myMIME = new MIMEType('text/javascript;key=value');
|
||||||
|
console.log(myMIME.essence);
|
||||||
|
// Prints: text/javascript
|
||||||
|
myMIME.type = 'application';
|
||||||
|
console.log(myMIME.essence);
|
||||||
|
// Prints: application/javascript
|
||||||
|
console.log(String(myMIME));
|
||||||
|
// Prints: application/javascript;key=value
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `mime.params`
|
||||||
|
|
||||||
|
* {MIMEParams}
|
||||||
|
|
||||||
|
Gets the [`MIMEParams`][] object representing the
|
||||||
|
parameters of the MIME. This property is read-only. See
|
||||||
|
[`MIMEParams`][] documentation for details.
|
||||||
|
|
||||||
|
#### `mime.toString()`
|
||||||
|
|
||||||
|
* Returns: {string}
|
||||||
|
|
||||||
|
The `toString()` method on the `MIMEType` object returns the serialized MIME.
|
||||||
|
|
||||||
|
Because of the need for standard compliance, this method does not allow users
|
||||||
|
to customize the serialization process of the MIME.
|
||||||
|
|
||||||
|
#### `mime.toJSON()`
|
||||||
|
|
||||||
|
* Returns: {string}
|
||||||
|
|
||||||
|
Alias for [`mime.toString()`][].
|
||||||
|
|
||||||
|
This method is automatically called when an `MIMEType` object is serialized
|
||||||
|
with [`JSON.stringify()`][].
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEType } from 'node:util';
|
||||||
|
|
||||||
|
const myMIMES = [
|
||||||
|
new MIMEType('image/png'),
|
||||||
|
new MIMEType('image/gif'),
|
||||||
|
];
|
||||||
|
console.log(JSON.stringify(myMIMES));
|
||||||
|
// Prints: ["image/png", "image/gif"]
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEType } = require('node:util');
|
||||||
|
|
||||||
|
const myMIMES = [
|
||||||
|
new MIMEType('image/png'),
|
||||||
|
new MIMEType('image/gif'),
|
||||||
|
];
|
||||||
|
console.log(JSON.stringify(myMIMES));
|
||||||
|
// Prints: ["image/png", "image/gif"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Class: `util.MIMEParams`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
The `MIMEParams` API provides read and write access to the parameters of a
|
||||||
|
`MIMEType`.
|
||||||
|
|
||||||
|
#### Constructor: `new MIMEParams()`
|
||||||
|
|
||||||
|
Creates a new `MIMEParams` object by with empty parameters
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEParams } from 'node:util';
|
||||||
|
|
||||||
|
const myParams = new MIMEParams();
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEParams } = require('node:util');
|
||||||
|
|
||||||
|
const myParams = new MIMEParams();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `mimeParams.delete(name)`
|
||||||
|
|
||||||
|
* `name` {string}
|
||||||
|
|
||||||
|
Remove all name-value pairs whose name is `name`.
|
||||||
|
|
||||||
|
#### `mimeParams.entries()`
|
||||||
|
|
||||||
|
* Returns: {Iterator}
|
||||||
|
|
||||||
|
Returns an iterator over each of the name-value pairs in the parameters.
|
||||||
|
Each item of the iterator is a JavaScript `Array`. The first item of the array
|
||||||
|
is the `name`, the second item of the array is the `value`.
|
||||||
|
|
||||||
|
#### `mimeParams.get(name)`
|
||||||
|
|
||||||
|
* `name` {string}
|
||||||
|
* Returns: {string} or `null` if there is no name-value pair with the given
|
||||||
|
`name`.
|
||||||
|
|
||||||
|
Returns the value of the first name-value pair whose name is `name`. If there
|
||||||
|
are no such pairs, `null` is returned.
|
||||||
|
|
||||||
|
#### `mimeParams.has(name)`
|
||||||
|
|
||||||
|
* `name` {string}
|
||||||
|
* Returns: {boolean}
|
||||||
|
|
||||||
|
Returns `true` if there is at least one name-value pair whose name is `name`.
|
||||||
|
|
||||||
|
#### `mimeParams.keys()`
|
||||||
|
|
||||||
|
* Returns: {Iterator}
|
||||||
|
|
||||||
|
Returns an iterator over the names of each name-value pair.
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEType } from 'node:util';
|
||||||
|
|
||||||
|
const { params } = new MIMEType('text/plain;foo=0;bar=1');
|
||||||
|
for (const name of params.keys()) {
|
||||||
|
console.log(name);
|
||||||
|
}
|
||||||
|
// Prints:
|
||||||
|
// foo
|
||||||
|
// bar
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEType } = require('node:util');
|
||||||
|
|
||||||
|
const { params } = new MIMEType('text/plain;foo=0;bar=1');
|
||||||
|
for (const name of params.keys()) {
|
||||||
|
console.log(name);
|
||||||
|
}
|
||||||
|
// Prints:
|
||||||
|
// foo
|
||||||
|
// bar
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `mimeParams.set(name, value)`
|
||||||
|
|
||||||
|
* `name` {string}
|
||||||
|
* `value` {string}
|
||||||
|
|
||||||
|
Sets the value in the `MIMEParams` object associated with `name` to
|
||||||
|
`value`. If there are any pre-existing name-value pairs whose names are `name`,
|
||||||
|
set the first such pair's value to `value`.
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEType } from 'node:util';
|
||||||
|
|
||||||
|
const { params } = new MIMEType('text/plain;foo=0;bar=1');
|
||||||
|
params.set('foo', 'def');
|
||||||
|
params.set('baz', 'xyz');
|
||||||
|
console.log(params.toString());
|
||||||
|
// Prints: foo=def&bar=1&baz=xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEType } = require('node:util');
|
||||||
|
|
||||||
|
const { params } = new MIMEType('text/plain;foo=0;bar=1');
|
||||||
|
params.set('foo', 'def');
|
||||||
|
params.set('baz', 'xyz');
|
||||||
|
console.log(params.toString());
|
||||||
|
// Prints: foo=def&bar=1&baz=xyz
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `mimeParams.values()`
|
||||||
|
|
||||||
|
* Returns: {Iterator}
|
||||||
|
|
||||||
|
Returns an iterator over the values of each name-value pair.
|
||||||
|
|
||||||
|
#### `mimeParams[@@iterator]()`
|
||||||
|
|
||||||
|
* Returns: {Iterator}
|
||||||
|
|
||||||
|
Alias for [`mimeParams.entries()`][].
|
||||||
|
|
||||||
|
```mjs
|
||||||
|
import { MIMEType } from 'node:util';
|
||||||
|
|
||||||
|
const { params } = new MIMEType('text/plain;foo=bar;xyz=baz');
|
||||||
|
for (const [name, value] of params) {
|
||||||
|
console.log(name, value);
|
||||||
|
}
|
||||||
|
// Prints:
|
||||||
|
// foo bar
|
||||||
|
// xyz baz
|
||||||
|
```
|
||||||
|
|
||||||
|
```cjs
|
||||||
|
const { MIMEType } = require('node:util');
|
||||||
|
|
||||||
|
const { params } = new MIMEType('text/plain;foo=bar;xyz=baz');
|
||||||
|
for (const [name, value] of params) {
|
||||||
|
console.log(name, value);
|
||||||
|
}
|
||||||
|
// Prints:
|
||||||
|
// foo bar
|
||||||
|
// xyz baz
|
||||||
|
```
|
||||||
|
|
||||||
## `util.parseArgs([config])`
|
## `util.parseArgs([config])`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
@ -2903,6 +3252,8 @@ util.log('Timestamped message.');
|
||||||
[`Int16Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array
|
[`Int16Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int16Array
|
||||||
[`Int32Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array
|
[`Int32Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int32Array
|
||||||
[`Int8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array
|
[`Int8Array`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Int8Array
|
||||||
|
[`JSON.stringify()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
|
||||||
|
[`MIMEparams`]: #class-utilmimeparams
|
||||||
[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
[`Map`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
|
||||||
[`Object.assign()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
[`Object.assign()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
||||||
[`Object.freeze()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
|
[`Object.freeze()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze
|
||||||
|
@ -2920,6 +3271,8 @@ util.log('Timestamped message.');
|
||||||
[`WebAssembly.Module`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module
|
[`WebAssembly.Module`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Module
|
||||||
[`assert.deepStrictEqual()`]: assert.md#assertdeepstrictequalactual-expected-message
|
[`assert.deepStrictEqual()`]: assert.md#assertdeepstrictequalactual-expected-message
|
||||||
[`console.error()`]: console.md#consoleerrordata-args
|
[`console.error()`]: console.md#consoleerrordata-args
|
||||||
|
[`mime.toString()`]: #mimetostring
|
||||||
|
[`mimeParams.entries()`]: #mimeparamsentries
|
||||||
[`napi_create_external()`]: n-api.md#napi_create_external
|
[`napi_create_external()`]: n-api.md#napi_create_external
|
||||||
[`target` and `handler`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Terminology
|
[`target` and `handler`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#Terminology
|
||||||
[`tty.hasColors()`]: tty.md#writestreamhascolorscount-env
|
[`tty.hasColors()`]: tty.md#writestreamhascolorscount-env
|
||||||
|
|
|
@ -639,6 +639,9 @@ RegExp.prototype.exec = () => null;
|
||||||
// Core
|
// Core
|
||||||
console.log(RegExpPrototypeTest(/o/, 'foo')); // false
|
console.log(RegExpPrototypeTest(/o/, 'foo')); // false
|
||||||
console.log(RegExpPrototypeExec(/o/, 'foo') !== null); // true
|
console.log(RegExpPrototypeExec(/o/, 'foo') !== null); // true
|
||||||
|
|
||||||
|
console.log(RegExpPrototypeSymbolSearch(/o/, 'foo')); // -1
|
||||||
|
console.log(SafeStringPrototypeSearch('foo', /o/)); // 1
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Don't trust `RegExp` flags
|
#### Don't trust `RegExp` flags
|
||||||
|
@ -668,19 +671,7 @@ Object.defineProperty(RegExp.prototype, 'global', { value: false });
|
||||||
|
|
||||||
// Core
|
// Core
|
||||||
console.log(RegExpPrototypeSymbolReplace(/o/g, 'foo', 'a')); // 'fao'
|
console.log(RegExpPrototypeSymbolReplace(/o/g, 'foo', 'a')); // 'fao'
|
||||||
|
console.log(RegExpPrototypeSymbolReplace(hardenRegExp(/o/g), 'foo', 'a')); // 'faa'
|
||||||
const regex = /o/g;
|
|
||||||
ObjectDefineProperties(regex, {
|
|
||||||
dotAll: { value: false },
|
|
||||||
exec: { value: undefined },
|
|
||||||
flags: { value: 'g' },
|
|
||||||
global: { value: true },
|
|
||||||
ignoreCase: { value: false },
|
|
||||||
multiline: { value: false },
|
|
||||||
unicode: { value: false },
|
|
||||||
sticky: { value: false },
|
|
||||||
});
|
|
||||||
console.log(RegExpPrototypeSymbolReplace(regex, 'foo', 'a')); // 'faa'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Defining object own properties
|
### Defining object own properties
|
||||||
|
|
|
@ -1310,6 +1310,10 @@ E('ERR_INVALID_FILE_URL_PATH', 'File URL path %s', TypeError);
|
||||||
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError);
|
E('ERR_INVALID_HANDLE_TYPE', 'This handle type cannot be sent', TypeError);
|
||||||
E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError);
|
E('ERR_INVALID_HTTP_TOKEN', '%s must be a valid HTTP token ["%s"]', TypeError);
|
||||||
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError);
|
E('ERR_INVALID_IP_ADDRESS', 'Invalid IP address: %s', TypeError);
|
||||||
|
E('ERR_INVALID_MIME_SYNTAX', (production, str, invalidIndex) => {
|
||||||
|
const msg = invalidIndex !== -1 ? ` at ${invalidIndex}` : '';
|
||||||
|
return `The MIME syntax for a ${production} in "${str}" is invalid` + msg;
|
||||||
|
}, TypeError);
|
||||||
E('ERR_INVALID_MODULE_SPECIFIER', (request, reason, base = undefined) => {
|
E('ERR_INVALID_MODULE_SPECIFIER', (request, reason, base = undefined) => {
|
||||||
return `Invalid module "${request}" ${reason}${base ?
|
return `Invalid module "${request}" ${reason}${base ?
|
||||||
` imported from ${base}` : ''}`;
|
` imported from ${base}` : ''}`;
|
||||||
|
|
367
lib/internal/mime.js
Normal file
367
lib/internal/mime.js
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {
|
||||||
|
FunctionPrototypeCall,
|
||||||
|
ObjectDefineProperty,
|
||||||
|
RegExpPrototypeExec,
|
||||||
|
SafeMap,
|
||||||
|
SafeStringPrototypeSearch,
|
||||||
|
StringPrototypeCharAt,
|
||||||
|
StringPrototypeIndexOf,
|
||||||
|
StringPrototypeSlice,
|
||||||
|
StringPrototypeToLowerCase,
|
||||||
|
SymbolIterator,
|
||||||
|
} = primordials;
|
||||||
|
const {
|
||||||
|
ERR_INVALID_MIME_SYNTAX,
|
||||||
|
} = require('internal/errors').codes;
|
||||||
|
|
||||||
|
const NOT_HTTP_TOKEN_CODE_POINT = /[^!#$%&'*+\-.^_`|~A-Za-z0-9]/g;
|
||||||
|
const NOT_HTTP_QUOTED_STRING_CODE_POINT = /[^\t\u0020-~\u0080-\u00FF]/g;
|
||||||
|
|
||||||
|
const END_BEGINNING_WHITESPACE = /[^\r\n\t ]|$/;
|
||||||
|
const START_ENDING_WHITESPACE = /[\r\n\t ]*$/;
|
||||||
|
|
||||||
|
function toASCIILower(str) {
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const char = str[i];
|
||||||
|
|
||||||
|
result += char >= 'A' && char <= 'Z' ?
|
||||||
|
StringPrototypeToLowerCase(char) :
|
||||||
|
char;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SOLIDUS = '/';
|
||||||
|
const SEMICOLON = ';';
|
||||||
|
function parseTypeAndSubtype(str) {
|
||||||
|
// Skip only HTTP whitespace from start
|
||||||
|
let position = SafeStringPrototypeSearch(str, END_BEGINNING_WHITESPACE);
|
||||||
|
// read until '/'
|
||||||
|
const typeEnd = StringPrototypeIndexOf(str, SOLIDUS, position);
|
||||||
|
const trimmedType = typeEnd === -1 ?
|
||||||
|
StringPrototypeSlice(str, position) :
|
||||||
|
StringPrototypeSlice(str, position, typeEnd);
|
||||||
|
const invalidTypeIndex = SafeStringPrototypeSearch(trimmedType,
|
||||||
|
NOT_HTTP_TOKEN_CODE_POINT);
|
||||||
|
if (trimmedType === '' || invalidTypeIndex !== -1 || typeEnd === -1) {
|
||||||
|
throw new ERR_INVALID_MIME_SYNTAX('type', str, invalidTypeIndex);
|
||||||
|
}
|
||||||
|
// skip type and '/'
|
||||||
|
position = typeEnd + 1;
|
||||||
|
const type = toASCIILower(trimmedType);
|
||||||
|
// read until ';'
|
||||||
|
const subtypeEnd = StringPrototypeIndexOf(str, SEMICOLON, position);
|
||||||
|
const rawSubtype = subtypeEnd === -1 ?
|
||||||
|
StringPrototypeSlice(str, position) :
|
||||||
|
StringPrototypeSlice(str, position, subtypeEnd);
|
||||||
|
position += rawSubtype.length;
|
||||||
|
if (subtypeEnd !== -1) {
|
||||||
|
// skip ';'
|
||||||
|
position += 1;
|
||||||
|
}
|
||||||
|
const trimmedSubtype = StringPrototypeSlice(
|
||||||
|
rawSubtype,
|
||||||
|
0,
|
||||||
|
SafeStringPrototypeSearch(rawSubtype, START_ENDING_WHITESPACE));
|
||||||
|
const invalidSubtypeIndex = SafeStringPrototypeSearch(trimmedSubtype,
|
||||||
|
NOT_HTTP_TOKEN_CODE_POINT);
|
||||||
|
if (trimmedSubtype === '' || invalidSubtypeIndex !== -1) {
|
||||||
|
throw new ERR_INVALID_MIME_SYNTAX('subtype', str, trimmedSubtype);
|
||||||
|
}
|
||||||
|
const subtype = toASCIILower(trimmedSubtype);
|
||||||
|
return {
|
||||||
|
__proto__: null,
|
||||||
|
type,
|
||||||
|
subtype,
|
||||||
|
parametersStringIndex: position,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const EQUALS_SEMICOLON_OR_END = /[;=]|$/;
|
||||||
|
const QUOTED_VALUE_PATTERN = /^(?:([\\]$)|[\\][\s\S]|[^"])*(?:(")|$)/u;
|
||||||
|
|
||||||
|
function removeBackslashes(str) {
|
||||||
|
let ret = '';
|
||||||
|
// We stop at str.length - 1 because we want to look ahead one character.
|
||||||
|
let i;
|
||||||
|
for (i = 0; i < str.length - 1; i++) {
|
||||||
|
const c = str[i];
|
||||||
|
if (c === '\\') {
|
||||||
|
i++;
|
||||||
|
ret += str[i];
|
||||||
|
} else {
|
||||||
|
ret += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We add the last character if we didn't skip to it.
|
||||||
|
if (i === str.length - 1) {
|
||||||
|
ret += str[i];
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function escapeQuoteOrSolidus(str) {
|
||||||
|
let result = '';
|
||||||
|
for (let i = 0; i < str.length; i++) {
|
||||||
|
const char = str[i];
|
||||||
|
result += (char === '"' || char === '\\') ? `\\${char}` : char;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const encode = (value) => {
|
||||||
|
if (value.length === 0) return '""';
|
||||||
|
const encode = SafeStringPrototypeSearch(value, NOT_HTTP_TOKEN_CODE_POINT) !== -1;
|
||||||
|
if (!encode) return value;
|
||||||
|
const escaped = escapeQuoteOrSolidus(value);
|
||||||
|
return `"${escaped}"`;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MIMEParams {
|
||||||
|
#data = new SafeMap();
|
||||||
|
|
||||||
|
delete(name) {
|
||||||
|
this.#data.delete(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
get(name) {
|
||||||
|
const data = this.#data;
|
||||||
|
if (data.has(name)) {
|
||||||
|
return data.get(name);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
has(name) {
|
||||||
|
return this.#data.has(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
set(name, value) {
|
||||||
|
const data = this.#data;
|
||||||
|
name = `${name}`;
|
||||||
|
value = `${value}`;
|
||||||
|
const invalidNameIndex = SafeStringPrototypeSearch(name, NOT_HTTP_TOKEN_CODE_POINT);
|
||||||
|
if (name.length === 0 || invalidNameIndex !== -1) {
|
||||||
|
throw new ERR_INVALID_MIME_SYNTAX(
|
||||||
|
'parameter name',
|
||||||
|
name,
|
||||||
|
invalidNameIndex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const invalidValueIndex = SafeStringPrototypeSearch(
|
||||||
|
value,
|
||||||
|
NOT_HTTP_QUOTED_STRING_CODE_POINT);
|
||||||
|
if (invalidValueIndex !== -1) {
|
||||||
|
throw new ERR_INVALID_MIME_SYNTAX(
|
||||||
|
'parameter value',
|
||||||
|
value,
|
||||||
|
invalidValueIndex,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
data.set(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
*entries() {
|
||||||
|
yield* this.#data.entries();
|
||||||
|
}
|
||||||
|
|
||||||
|
*keys() {
|
||||||
|
yield* this.#data.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
*values() {
|
||||||
|
yield* this.#data.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
let ret = '';
|
||||||
|
for (const { 0: key, 1: value } of this.#data) {
|
||||||
|
const encoded = encode(value);
|
||||||
|
// Ensure they are separated
|
||||||
|
if (ret.length) ret += ';';
|
||||||
|
ret += `${key}=${encoded}`;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to act as a friendly class to stringifying stuff
|
||||||
|
// not meant to be exposed to users, could inject invalid values
|
||||||
|
static parseParametersString(str, position, params) {
|
||||||
|
const paramsMap = params.#data;
|
||||||
|
const endOfSource = SafeStringPrototypeSearch(
|
||||||
|
StringPrototypeSlice(str, position),
|
||||||
|
START_ENDING_WHITESPACE
|
||||||
|
) + position;
|
||||||
|
while (position < endOfSource) {
|
||||||
|
// Skip any whitespace before parameter
|
||||||
|
position += SafeStringPrototypeSearch(
|
||||||
|
StringPrototypeSlice(str, position),
|
||||||
|
END_BEGINNING_WHITESPACE
|
||||||
|
);
|
||||||
|
// Read until ';' or '='
|
||||||
|
const afterParameterName = SafeStringPrototypeSearch(
|
||||||
|
StringPrototypeSlice(str, position),
|
||||||
|
EQUALS_SEMICOLON_OR_END
|
||||||
|
) + position;
|
||||||
|
const parameterString = toASCIILower(
|
||||||
|
StringPrototypeSlice(str, position, afterParameterName)
|
||||||
|
);
|
||||||
|
position = afterParameterName;
|
||||||
|
// If we found a terminating character
|
||||||
|
if (position < endOfSource) {
|
||||||
|
// Safe to use because we never do special actions for surrogate pairs
|
||||||
|
const char = StringPrototypeCharAt(str, position);
|
||||||
|
// Skip the terminating character
|
||||||
|
position += 1;
|
||||||
|
// Ignore parameters without values
|
||||||
|
if (char === ';') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we are at end of the string, it cannot have a value
|
||||||
|
if (position >= endOfSource) break;
|
||||||
|
// Safe to use because we never do special actions for surrogate pairs
|
||||||
|
const char = StringPrototypeCharAt(str, position);
|
||||||
|
let parameterValue = null;
|
||||||
|
if (char === '"') {
|
||||||
|
// Handle quoted-string form of values
|
||||||
|
// skip '"'
|
||||||
|
position += 1;
|
||||||
|
// Find matching closing '"' or end of string
|
||||||
|
// use $1 to see if we terminated on unmatched '\'
|
||||||
|
// use $2 to see if we terminated on a matching '"'
|
||||||
|
// so we can skip the last char in either case
|
||||||
|
const insideMatch = RegExpPrototypeExec(
|
||||||
|
QUOTED_VALUE_PATTERN,
|
||||||
|
StringPrototypeSlice(str, position));
|
||||||
|
position += insideMatch[0].length;
|
||||||
|
// Skip including last character if an unmatched '\' or '"' during
|
||||||
|
// unescape
|
||||||
|
const inside = insideMatch[1] || insideMatch[2] ?
|
||||||
|
StringPrototypeSlice(insideMatch[0], 0, -1) :
|
||||||
|
insideMatch[0];
|
||||||
|
// Unescape '\' quoted characters
|
||||||
|
parameterValue = removeBackslashes(inside);
|
||||||
|
// If we did have an unmatched '\' add it back to the end
|
||||||
|
if (insideMatch[1]) parameterValue += '\\';
|
||||||
|
} else {
|
||||||
|
// Handle the normal parameter value form
|
||||||
|
const valueEnd = StringPrototypeIndexOf(str, SEMICOLON, position);
|
||||||
|
const rawValue = valueEnd === -1 ?
|
||||||
|
StringPrototypeSlice(str, position) :
|
||||||
|
StringPrototypeSlice(str, position, valueEnd);
|
||||||
|
position += rawValue.length;
|
||||||
|
const trimmedValue = StringPrototypeSlice(
|
||||||
|
rawValue,
|
||||||
|
0,
|
||||||
|
SafeStringPrototypeSearch(rawValue, START_ENDING_WHITESPACE)
|
||||||
|
);
|
||||||
|
// Ignore parameters without values
|
||||||
|
if (trimmedValue === '') continue;
|
||||||
|
parameterValue = trimmedValue;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
parameterString !== '' &&
|
||||||
|
SafeStringPrototypeSearch(parameterString,
|
||||||
|
NOT_HTTP_TOKEN_CODE_POINT) === -1 &&
|
||||||
|
SafeStringPrototypeSearch(parameterValue,
|
||||||
|
NOT_HTTP_QUOTED_STRING_CODE_POINT) === -1 &&
|
||||||
|
params.has(parameterString) === false
|
||||||
|
) {
|
||||||
|
paramsMap.set(parameterString, parameterValue);
|
||||||
|
}
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
return paramsMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const MIMEParamsStringify = MIMEParams.prototype.toString;
|
||||||
|
ObjectDefineProperty(MIMEParams.prototype, SymbolIterator, {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: MIMEParams.prototype.entries,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
ObjectDefineProperty(MIMEParams.prototype, 'toJSON', {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: MIMEParamsStringify,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { parseParametersString } = MIMEParams;
|
||||||
|
delete MIMEParams.parseParametersString;
|
||||||
|
|
||||||
|
class MIMEType {
|
||||||
|
#type;
|
||||||
|
#subtype;
|
||||||
|
#parameters;
|
||||||
|
constructor(string) {
|
||||||
|
string = `${string}`;
|
||||||
|
const data = parseTypeAndSubtype(string);
|
||||||
|
this.#type = data.type;
|
||||||
|
this.#subtype = data.subtype;
|
||||||
|
this.#parameters = new MIMEParams();
|
||||||
|
parseParametersString(
|
||||||
|
string,
|
||||||
|
data.parametersStringIndex,
|
||||||
|
this.#parameters,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get type() {
|
||||||
|
return this.#type;
|
||||||
|
}
|
||||||
|
|
||||||
|
set type(v) {
|
||||||
|
v = `${v}`;
|
||||||
|
const invalidTypeIndex = SafeStringPrototypeSearch(v, NOT_HTTP_TOKEN_CODE_POINT);
|
||||||
|
if (v.length === 0 || invalidTypeIndex !== -1) {
|
||||||
|
throw new ERR_INVALID_MIME_SYNTAX('type', v, invalidTypeIndex);
|
||||||
|
}
|
||||||
|
this.#type = toASCIILower(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
get subtype() {
|
||||||
|
return this.#subtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
set subtype(v) {
|
||||||
|
v = `${v}`;
|
||||||
|
const invalidSubtypeIndex = SafeStringPrototypeSearch(v, NOT_HTTP_TOKEN_CODE_POINT);
|
||||||
|
if (v.length === 0 || invalidSubtypeIndex !== -1) {
|
||||||
|
throw new ERR_INVALID_MIME_SYNTAX('subtype', v, invalidSubtypeIndex);
|
||||||
|
}
|
||||||
|
this.#subtype = toASCIILower(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
get essence() {
|
||||||
|
return `${this.#type}/${this.#subtype}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get params() {
|
||||||
|
return this.#parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
let ret = `${this.#type}/${this.#subtype}`;
|
||||||
|
const paramStr = FunctionPrototypeCall(MIMEParamsStringify, this.#parameters);
|
||||||
|
if (paramStr.length) ret += `;${paramStr}`;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectDefineProperty(MIMEType.prototype, 'toJSON', {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: MIMEType.prototype.toString,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
MIMEParams,
|
||||||
|
MIMEType,
|
||||||
|
};
|
|
@ -266,12 +266,36 @@ const {
|
||||||
FinalizationRegistry,
|
FinalizationRegistry,
|
||||||
FunctionPrototypeCall,
|
FunctionPrototypeCall,
|
||||||
Map,
|
Map,
|
||||||
|
ObjectDefineProperties,
|
||||||
|
ObjectDefineProperty,
|
||||||
ObjectFreeze,
|
ObjectFreeze,
|
||||||
ObjectSetPrototypeOf,
|
ObjectSetPrototypeOf,
|
||||||
Promise,
|
Promise,
|
||||||
PromisePrototypeThen,
|
PromisePrototypeThen,
|
||||||
|
ReflectApply,
|
||||||
|
ReflectConstruct,
|
||||||
|
ReflectSet,
|
||||||
|
ReflectGet,
|
||||||
|
RegExp,
|
||||||
|
RegExpPrototype,
|
||||||
|
RegExpPrototypeExec,
|
||||||
|
RegExpPrototypeGetDotAll,
|
||||||
|
RegExpPrototypeGetFlags,
|
||||||
|
RegExpPrototypeGetGlobal,
|
||||||
|
RegExpPrototypeGetHasIndices,
|
||||||
|
RegExpPrototypeGetIgnoreCase,
|
||||||
|
RegExpPrototypeGetMultiline,
|
||||||
|
RegExpPrototypeGetSource,
|
||||||
|
RegExpPrototypeGetSticky,
|
||||||
|
RegExpPrototypeGetUnicode,
|
||||||
Set,
|
Set,
|
||||||
SymbolIterator,
|
SymbolIterator,
|
||||||
|
SymbolMatch,
|
||||||
|
SymbolMatchAll,
|
||||||
|
SymbolReplace,
|
||||||
|
SymbolSearch,
|
||||||
|
SymbolSpecies,
|
||||||
|
SymbolSplit,
|
||||||
WeakMap,
|
WeakMap,
|
||||||
WeakRef,
|
WeakRef,
|
||||||
WeakSet,
|
WeakSet,
|
||||||
|
@ -490,5 +514,128 @@ primordials.SafePromiseRace = (promises, mapFn) =>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
exec: OriginalRegExpPrototypeExec,
|
||||||
|
[SymbolMatch]: OriginalRegExpPrototypeSymbolMatch,
|
||||||
|
[SymbolMatchAll]: OriginalRegExpPrototypeSymbolMatchAll,
|
||||||
|
[SymbolReplace]: OriginalRegExpPrototypeSymbolReplace,
|
||||||
|
[SymbolSearch]: OriginalRegExpPrototypeSymbolSearch,
|
||||||
|
[SymbolSplit]: OriginalRegExpPrototypeSymbolSplit,
|
||||||
|
} = RegExpPrototype;
|
||||||
|
|
||||||
|
class RegExpLikeForStringSplitting {
|
||||||
|
#regex;
|
||||||
|
constructor() {
|
||||||
|
this.#regex = ReflectConstruct(RegExp, arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
get lastIndex() {
|
||||||
|
return ReflectGet(this.#regex, 'lastIndex');
|
||||||
|
}
|
||||||
|
set lastIndex(value) {
|
||||||
|
ReflectSet(this.#regex, 'lastIndex', value);
|
||||||
|
}
|
||||||
|
|
||||||
|
exec() {
|
||||||
|
return ReflectApply(OriginalRegExpPrototypeExec, this.#regex, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectSetPrototypeOf(RegExpLikeForStringSplitting.prototype, null);
|
||||||
|
|
||||||
|
primordials.hardenRegExp = function hardenRegExp(pattern) {
|
||||||
|
ObjectDefineProperties(pattern, {
|
||||||
|
[SymbolMatch]: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: OriginalRegExpPrototypeSymbolMatch,
|
||||||
|
},
|
||||||
|
[SymbolMatchAll]: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: OriginalRegExpPrototypeSymbolMatchAll,
|
||||||
|
},
|
||||||
|
[SymbolReplace]: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: OriginalRegExpPrototypeSymbolReplace,
|
||||||
|
},
|
||||||
|
[SymbolSearch]: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: OriginalRegExpPrototypeSymbolSearch,
|
||||||
|
},
|
||||||
|
[SymbolSplit]: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: OriginalRegExpPrototypeSymbolSplit,
|
||||||
|
},
|
||||||
|
constructor: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: {
|
||||||
|
[SymbolSpecies]: RegExpLikeForStringSplitting,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dotAll: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: RegExpPrototypeGetDotAll(pattern),
|
||||||
|
},
|
||||||
|
exec: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: OriginalRegExpPrototypeExec,
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: RegExpPrototypeGetGlobal(pattern),
|
||||||
|
},
|
||||||
|
hasIndices: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: RegExpPrototypeGetHasIndices(pattern),
|
||||||
|
},
|
||||||
|
ignoreCase: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: RegExpPrototypeGetIgnoreCase(pattern),
|
||||||
|
},
|
||||||
|
multiline: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: RegExpPrototypeGetMultiline(pattern),
|
||||||
|
},
|
||||||
|
source: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: RegExpPrototypeGetSource(pattern),
|
||||||
|
},
|
||||||
|
sticky: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: RegExpPrototypeGetSticky(pattern),
|
||||||
|
},
|
||||||
|
unicode: {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: RegExpPrototypeGetUnicode(pattern),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
ObjectDefineProperty(pattern, 'flags', {
|
||||||
|
__proto__: null,
|
||||||
|
configurable: true,
|
||||||
|
value: RegExpPrototypeGetFlags(pattern),
|
||||||
|
});
|
||||||
|
return pattern;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
primordials.SafeStringPrototypeSearch = (str, regexp) => {
|
||||||
|
regexp.lastIndex = 0;
|
||||||
|
const match = RegExpPrototypeExec(regexp, str);
|
||||||
|
return match ? match.index : -1;
|
||||||
|
};
|
||||||
|
|
||||||
ObjectSetPrototypeOf(primordials, null);
|
ObjectSetPrototypeOf(primordials, null);
|
||||||
ObjectFreeze(primordials);
|
ObjectFreeze(primordials);
|
||||||
|
|
|
@ -68,6 +68,7 @@ const {
|
||||||
validateNumber,
|
validateNumber,
|
||||||
} = require('internal/validators');
|
} = require('internal/validators');
|
||||||
const { TextDecoder, TextEncoder } = require('internal/encoding');
|
const { TextDecoder, TextEncoder } = require('internal/encoding');
|
||||||
|
const { MIMEType, MIMEParams } = require('internal/mime');
|
||||||
const { isBuffer } = require('buffer').Buffer;
|
const { isBuffer } = require('buffer').Buffer;
|
||||||
const types = require('internal/util/types');
|
const types = require('internal/util/types');
|
||||||
|
|
||||||
|
@ -385,6 +386,8 @@ module.exports = {
|
||||||
isFunction,
|
isFunction,
|
||||||
isPrimitive,
|
isPrimitive,
|
||||||
log,
|
log,
|
||||||
|
MIMEType,
|
||||||
|
MIMEParams,
|
||||||
parseArgs,
|
parseArgs,
|
||||||
promisify,
|
promisify,
|
||||||
stripVTControlCharacters,
|
stripVTControlCharacters,
|
||||||
|
|
3533
test/fixtures/mime-whatwg-generated.js
vendored
Normal file
3533
test/fixtures/mime-whatwg-generated.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
392
test/fixtures/mime-whatwg.js
vendored
Normal file
392
test/fixtures/mime-whatwg.js
vendored
Normal file
|
@ -0,0 +1,392 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// TODO: Incorporate this with the other WPT tests if it makes sense to do that.
|
||||||
|
|
||||||
|
/* The following tests are copied from WPT. Modifications to them should be
|
||||||
|
upstreamed first. Refs:
|
||||||
|
https://github.com/w3c/web-platform-tests/blob/88b75886e/url/urltestdata.json
|
||||||
|
License: http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html
|
||||||
|
*/
|
||||||
|
module.exports = [
|
||||||
|
"Basics",
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "TEXT/HTML;CHARSET=GBK",
|
||||||
|
"output": "text/html;charset=GBK",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
"Legacy comment syntax",
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=gbk(",
|
||||||
|
"output": "text/html;charset=\"gbk(\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;x=(;charset=gbk",
|
||||||
|
"output": "text/html;x=\"(\";charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
"Duplicate parameter",
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=gbk;charset=windows-1255",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=();charset=GBK",
|
||||||
|
"output": "text/html;charset=\"()\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
"Spaces",
|
||||||
|
{
|
||||||
|
"input": "text/html;charset =gbk",
|
||||||
|
"output": "text/html",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html ;charset=gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html; charset=gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset= gbk",
|
||||||
|
"output": "text/html;charset=\" gbk\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset= \"gbk\"",
|
||||||
|
"output": "text/html;charset=\" \\\"gbk\\\"\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
"0x0B and 0x0C",
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\u000Bgbk",
|
||||||
|
"output": "text/html",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\u000Cgbk",
|
||||||
|
"output": "text/html",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;\u000Bcharset=gbk",
|
||||||
|
"output": "text/html",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;\u000Ccharset=gbk",
|
||||||
|
"output": "text/html",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
"Single quotes are a token, not a delimiter",
|
||||||
|
{
|
||||||
|
"input": "text/html;charset='gbk'",
|
||||||
|
"output": "text/html;charset='gbk'",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset='gbk",
|
||||||
|
"output": "text/html;charset='gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=gbk'",
|
||||||
|
"output": "text/html;charset=gbk'",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=';charset=GBK",
|
||||||
|
"output": "text/html;charset='",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
"Invalid parameters",
|
||||||
|
{
|
||||||
|
"input": "text/html;test;charset=gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;test=;charset=gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;';charset=gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;\";charset=gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html ; ; charset=gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;;;;charset=gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset= \"\u007F;charset=GBK",
|
||||||
|
"output": "text/html;charset=GBK",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\"\u007F;charset=foo\";charset=GBK",
|
||||||
|
"output": "text/html;charset=GBK",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
"Double quotes",
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\"gbk\"",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\"gbk",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=gbk\"",
|
||||||
|
"output": "text/html;charset=\"gbk\\\"\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\" gbk\"",
|
||||||
|
"output": "text/html;charset=\" gbk\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\"gbk \"",
|
||||||
|
"output": "text/html;charset=\"gbk \"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\"\\ gbk\"",
|
||||||
|
"output": "text/html;charset=\" gbk\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\"\\g\\b\\k\"",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\"gbk\"x",
|
||||||
|
"output": "text/html;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\"\";charset=GBK",
|
||||||
|
"output": "text/html;charset=\"\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html;charset=\";charset=GBK",
|
||||||
|
"output": "text/html;charset=\";charset=GBK\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
"Unexpected code points",
|
||||||
|
{
|
||||||
|
"input": "text/html;charset={gbk}",
|
||||||
|
"output": "text/html;charset=\"{gbk}\"",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": null
|
||||||
|
},
|
||||||
|
"Parameter name longer than 127",
|
||||||
|
{
|
||||||
|
"input": "text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk",
|
||||||
|
"output": "text/html;0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789=x;charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
"type/subtype longer than 127",
|
||||||
|
{
|
||||||
|
"input": "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789",
|
||||||
|
"output": "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||||
|
},
|
||||||
|
"Valid",
|
||||||
|
{
|
||||||
|
"input": "!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
|
||||||
|
"output": "!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz/!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz;!#$%&'*+-.^_`|~0123456789abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz=!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "x/x;x=\"\t !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F\u00A0\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF\"",
|
||||||
|
"output": "x/x;x=\"\t !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0080\u0081\u0082\u0083\u0084\u0085\u0086\u0087\u0088\u0089\u008A\u008B\u008C\u008D\u008E\u008F\u0090\u0091\u0092\u0093\u0094\u0095\u0096\u0097\u0098\u0099\u009A\u009B\u009C\u009D\u009E\u009F\u00A0\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA\u00AB\u00AC\u00AD\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA\u00FB\u00FC\u00FD\u00FE\u00FF\""
|
||||||
|
},
|
||||||
|
"End-of-file handling",
|
||||||
|
{
|
||||||
|
"input": "x/x;test",
|
||||||
|
"output": "x/x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "x/x;test=\"\\",
|
||||||
|
"output": "x/x;test=\"\\\\\""
|
||||||
|
},
|
||||||
|
"Whitespace (not handled by generated-mime-types.json or above)",
|
||||||
|
{
|
||||||
|
"input": "x/x;x= ",
|
||||||
|
"output": "x/x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "x/x;x=\t",
|
||||||
|
"output": "x/x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "x/x\n\r\t ;x=x",
|
||||||
|
"output": "x/x;x=x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "\n\r\t x/x;x=x\n\r\t ",
|
||||||
|
"output": "x/x;x=x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "x/x;\n\r\t x=x\n\r\t ;x=y",
|
||||||
|
"output": "x/x;x=x"
|
||||||
|
},
|
||||||
|
"Latin1",
|
||||||
|
{
|
||||||
|
"input": "text/html;test=\u00FF;charset=gbk",
|
||||||
|
"output": "text/html;test=\"\u00FF\";charset=gbk",
|
||||||
|
"navigable": true,
|
||||||
|
"encoding": "GBK"
|
||||||
|
},
|
||||||
|
">Latin1",
|
||||||
|
{
|
||||||
|
"input": "x/x;test=\uFFFD;x=x",
|
||||||
|
"output": "x/x;x=x"
|
||||||
|
},
|
||||||
|
"Failure",
|
||||||
|
{
|
||||||
|
"input": "\u000Bx/x",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "\u000Cx/x",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "x/x\u000B",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "x/x\u000C",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "\t",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "/",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "bogus",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "bogus/",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "bogus/ ",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "bogus/bogus/;",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "</>",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "(/)",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "ÿ/ÿ",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/html(;doesnot=matter",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "{/}",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "\u0100/\u0100",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text /html",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "text/ html",
|
||||||
|
"output": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"input": "\"text/html\"",
|
||||||
|
"output": null
|
||||||
|
}
|
||||||
|
];
|
|
@ -72,6 +72,7 @@ const expectedModules = new Set([
|
||||||
'NativeModule internal/histogram',
|
'NativeModule internal/histogram',
|
||||||
'NativeModule internal/idna',
|
'NativeModule internal/idna',
|
||||||
'NativeModule internal/linkedlist',
|
'NativeModule internal/linkedlist',
|
||||||
|
'NativeModule internal/mime',
|
||||||
'NativeModule internal/modules/cjs/helpers',
|
'NativeModule internal/modules/cjs/helpers',
|
||||||
'NativeModule internal/modules/cjs/loader',
|
'NativeModule internal/modules/cjs/loader',
|
||||||
'NativeModule internal/modules/esm/assert',
|
'NativeModule internal/modules/esm/assert',
|
||||||
|
|
|
@ -164,7 +164,7 @@ new RuleTester({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'RegExpPrototypeSymbolSearch(/some regex/, "some string")',
|
code: 'RegExpPrototypeSymbolSearch(/some regex/, "some string")',
|
||||||
errors: [{ message: /looks up the "exec" property/ }],
|
errors: [{ message: /SafeStringPrototypeSearch/ }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'StringPrototypeMatch("some string", /some regex/)',
|
code: 'StringPrototypeMatch("some string", /some regex/)',
|
||||||
|
@ -204,7 +204,7 @@ new RuleTester({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'StringPrototypeSearch("some string", /some regex/)',
|
code: 'StringPrototypeSearch("some string", /some regex/)',
|
||||||
errors: [{ message: /looks up the Symbol\.search property/ }],
|
errors: [{ message: /SafeStringPrototypeSearch/ }],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'StringPrototypeSplit("some string", /some regex/)',
|
code: 'StringPrototypeSplit("some string", /some regex/)',
|
||||||
|
|
160
test/parallel/test-mime-api.js
Normal file
160
test/parallel/test-mime-api.js
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const { MIMEType, MIMEParams } = require('util');
|
||||||
|
|
||||||
|
|
||||||
|
const WHITESPACES = '\t\n\f\r ';
|
||||||
|
const NOT_HTTP_TOKEN_CODE_POINT = ',';
|
||||||
|
const NOT_HTTP_QUOTED_STRING_CODE_POINT = '\n';
|
||||||
|
|
||||||
|
const mime = new MIMEType('application/ecmascript; ');
|
||||||
|
const mime_descriptors = Object.getOwnPropertyDescriptors(mime);
|
||||||
|
const mime_proto = Object.getPrototypeOf(mime);
|
||||||
|
const mime_impersonator = Object.create(mime_proto);
|
||||||
|
for (const key of Object.keys(mime_descriptors)) {
|
||||||
|
const descriptor = mime_descriptors[key];
|
||||||
|
if (descriptor.get) {
|
||||||
|
assert.throws(descriptor.get.call(mime_impersonator), /Invalid receiver/i);
|
||||||
|
}
|
||||||
|
if (descriptor.set) {
|
||||||
|
assert.throws(descriptor.set.call(mime_impersonator, 'x'), /Invalid receiver/i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
assert.strictEqual(
|
||||||
|
JSON.stringify(mime),
|
||||||
|
JSON.stringify('application/ecmascript'));
|
||||||
|
assert.strictEqual(`${mime}`, 'application/ecmascript');
|
||||||
|
assert.strictEqual(mime.essence, 'application/ecmascript');
|
||||||
|
assert.strictEqual(mime.type, 'application');
|
||||||
|
assert.strictEqual(mime.subtype, 'ecmascript');
|
||||||
|
assert.ok(mime.params);
|
||||||
|
assert.deepStrictEqual([], [...mime.params]);
|
||||||
|
assert.strictEqual(mime.params.has('not found'), false);
|
||||||
|
assert.strictEqual(mime.params.get('not found'), null);
|
||||||
|
assert.strictEqual(mime.params.delete('not found'), undefined);
|
||||||
|
|
||||||
|
|
||||||
|
mime.type = 'text';
|
||||||
|
assert.strictEqual(mime.type, 'text');
|
||||||
|
assert.strictEqual(JSON.stringify(mime), JSON.stringify('text/ecmascript'));
|
||||||
|
assert.strictEqual(`${mime}`, 'text/ecmascript');
|
||||||
|
assert.strictEqual(mime.essence, 'text/ecmascript');
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
mime.type = `${WHITESPACES}text`;
|
||||||
|
}, /ERR_INVALID_MIME_SYNTAX/);
|
||||||
|
|
||||||
|
assert.throws(() => mime.type = '', /type/i);
|
||||||
|
assert.throws(() => mime.type = '/', /type/i);
|
||||||
|
assert.throws(() => mime.type = 'x/', /type/i);
|
||||||
|
assert.throws(() => mime.type = '/x', /type/i);
|
||||||
|
assert.throws(() => mime.type = NOT_HTTP_TOKEN_CODE_POINT, /type/i);
|
||||||
|
assert.throws(() => mime.type = `${NOT_HTTP_TOKEN_CODE_POINT}/`, /type/i);
|
||||||
|
assert.throws(() => mime.type = `/${NOT_HTTP_TOKEN_CODE_POINT}`, /type/i);
|
||||||
|
|
||||||
|
|
||||||
|
mime.subtype = 'javascript';
|
||||||
|
assert.strictEqual(mime.type, 'text');
|
||||||
|
assert.strictEqual(JSON.stringify(mime), JSON.stringify('text/javascript'));
|
||||||
|
assert.strictEqual(`${mime}`, 'text/javascript');
|
||||||
|
assert.strictEqual(mime.essence, 'text/javascript');
|
||||||
|
assert.strictEqual(`${mime.params}`, '');
|
||||||
|
assert.strictEqual(`${new MIMEParams()}`, '');
|
||||||
|
assert.strictEqual(`${new MIMEParams(mime.params)}`, '');
|
||||||
|
assert.strictEqual(`${new MIMEParams(`${mime.params}`)}`, '');
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
mime.subtype = `javascript${WHITESPACES}`;
|
||||||
|
}, /ERR_INVALID_MIME_SYNTAX/);
|
||||||
|
|
||||||
|
assert.throws(() => mime.subtype = '', /subtype/i);
|
||||||
|
assert.throws(() => mime.subtype = ';', /subtype/i);
|
||||||
|
assert.throws(() => mime.subtype = 'x;', /subtype/i);
|
||||||
|
assert.throws(() => mime.subtype = ';x', /subtype/i);
|
||||||
|
assert.throws(() => mime.subtype = NOT_HTTP_TOKEN_CODE_POINT, /subtype/i);
|
||||||
|
assert.throws(
|
||||||
|
() => mime.subtype = `${NOT_HTTP_TOKEN_CODE_POINT};`,
|
||||||
|
/subtype/i);
|
||||||
|
assert.throws(
|
||||||
|
() => mime.subtype = `;${NOT_HTTP_TOKEN_CODE_POINT}`,
|
||||||
|
/subtype/i);
|
||||||
|
|
||||||
|
|
||||||
|
const params = mime.params;
|
||||||
|
params.set('charset', 'utf-8');
|
||||||
|
assert.strictEqual(params.has('charset'), true);
|
||||||
|
assert.strictEqual(params.get('charset'), 'utf-8');
|
||||||
|
assert.deepStrictEqual([...params], [['charset', 'utf-8']]);
|
||||||
|
assert.strictEqual(
|
||||||
|
JSON.stringify(mime),
|
||||||
|
JSON.stringify('text/javascript;charset=utf-8'));
|
||||||
|
assert.strictEqual(`${mime}`, 'text/javascript;charset=utf-8');
|
||||||
|
assert.strictEqual(mime.essence, 'text/javascript');
|
||||||
|
assert.strictEqual(`${mime.params}`, 'charset=utf-8');
|
||||||
|
assert.strictEqual(`${new MIMEParams(mime.params)}`, '');
|
||||||
|
assert.strictEqual(`${new MIMEParams(`${mime.params}`)}`, '');
|
||||||
|
|
||||||
|
params.set('goal', 'module');
|
||||||
|
assert.strictEqual(params.has('goal'), true);
|
||||||
|
assert.strictEqual(params.get('goal'), 'module');
|
||||||
|
assert.deepStrictEqual([...params], [['charset', 'utf-8'], ['goal', 'module']]);
|
||||||
|
assert.strictEqual(
|
||||||
|
JSON.stringify(mime),
|
||||||
|
JSON.stringify('text/javascript;charset=utf-8;goal=module'));
|
||||||
|
assert.strictEqual(`${mime}`, 'text/javascript;charset=utf-8;goal=module');
|
||||||
|
assert.strictEqual(mime.essence, 'text/javascript');
|
||||||
|
assert.strictEqual(`${mime.params}`, 'charset=utf-8;goal=module');
|
||||||
|
assert.strictEqual(`${new MIMEParams(mime.params)}`, '');
|
||||||
|
assert.strictEqual(`${new MIMEParams(`${mime.params}`)}`, '');
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
params.set(`${WHITESPACES}goal`, 'module');
|
||||||
|
}, /ERR_INVALID_MIME_SYNTAX/);
|
||||||
|
|
||||||
|
params.set('charset', 'iso-8859-1');
|
||||||
|
assert.strictEqual(params.has('charset'), true);
|
||||||
|
assert.strictEqual(params.get('charset'), 'iso-8859-1');
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
[...params],
|
||||||
|
[['charset', 'iso-8859-1'], ['goal', 'module']]);
|
||||||
|
assert.strictEqual(
|
||||||
|
JSON.stringify(mime),
|
||||||
|
JSON.stringify('text/javascript;charset=iso-8859-1;goal=module'));
|
||||||
|
assert.strictEqual(`${mime}`, 'text/javascript;charset=iso-8859-1;goal=module');
|
||||||
|
assert.strictEqual(mime.essence, 'text/javascript');
|
||||||
|
|
||||||
|
params.delete('charset');
|
||||||
|
assert.strictEqual(params.has('charset'), false);
|
||||||
|
assert.strictEqual(params.get('charset'), null);
|
||||||
|
assert.deepStrictEqual([...params], [['goal', 'module']]);
|
||||||
|
assert.strictEqual(
|
||||||
|
JSON.stringify(mime),
|
||||||
|
JSON.stringify('text/javascript;goal=module'));
|
||||||
|
assert.strictEqual(`${mime}`, 'text/javascript;goal=module');
|
||||||
|
assert.strictEqual(mime.essence, 'text/javascript');
|
||||||
|
|
||||||
|
params.set('x', '');
|
||||||
|
assert.strictEqual(params.has('x'), true);
|
||||||
|
assert.strictEqual(params.get('x'), '');
|
||||||
|
assert.deepStrictEqual([...params], [['goal', 'module'], ['x', '']]);
|
||||||
|
assert.strictEqual(
|
||||||
|
JSON.stringify(mime),
|
||||||
|
JSON.stringify('text/javascript;goal=module;x=""'));
|
||||||
|
assert.strictEqual(`${mime}`, 'text/javascript;goal=module;x=""');
|
||||||
|
assert.strictEqual(mime.essence, 'text/javascript');
|
||||||
|
|
||||||
|
assert.throws(() => params.set('', 'x'), /parameter name/i);
|
||||||
|
assert.throws(() => params.set('=', 'x'), /parameter name/i);
|
||||||
|
assert.throws(() => params.set('x=', 'x'), /parameter name/i);
|
||||||
|
assert.throws(() => params.set('=x', 'x'), /parameter name/i);
|
||||||
|
assert.throws(() => params.set(`${NOT_HTTP_TOKEN_CODE_POINT}=`, 'x'), /parameter name/i);
|
||||||
|
assert.throws(() => params.set(`${NOT_HTTP_TOKEN_CODE_POINT}x`, 'x'), /parameter name/i);
|
||||||
|
assert.throws(() => params.set(`x${NOT_HTTP_TOKEN_CODE_POINT}`, 'x'), /parameter name/i);
|
||||||
|
|
||||||
|
assert.throws(() => params.set('x', `${NOT_HTTP_QUOTED_STRING_CODE_POINT};`), /parameter value/i);
|
||||||
|
assert.throws(() => params.set('x', `${NOT_HTTP_QUOTED_STRING_CODE_POINT}x`), /parameter value/i);
|
||||||
|
assert.throws(() => params.set('x', `x${NOT_HTTP_QUOTED_STRING_CODE_POINT}`), /parameter value/i);
|
23
test/parallel/test-mime-whatwg.js
Normal file
23
test/parallel/test-mime-whatwg.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const { MIMEType } = require('util');
|
||||||
|
const fixtures = require('../common/fixtures');
|
||||||
|
|
||||||
|
function test(mimes) {
|
||||||
|
for (const entry of mimes) {
|
||||||
|
if (typeof entry === 'string') continue;
|
||||||
|
const { input, output } = entry;
|
||||||
|
if (output === null) {
|
||||||
|
assert.throws(() => new MIMEType(input), /ERR_INVALID_MIME_SYNTAX/i);
|
||||||
|
} else {
|
||||||
|
const str = `${new MIMEType(input)}`;
|
||||||
|
assert.strictEqual(str, output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// These come from https://github.com/web-platform-tests/wpt/tree/master/mimesniff/mime-types/resources
|
||||||
|
test(require(fixtures.path('./mime-whatwg.js')));
|
||||||
|
test(require(fixtures.path('./mime-whatwg-generated.js')));
|
119
test/parallel/test-primordials-regexp.js
Normal file
119
test/parallel/test-primordials-regexp.js
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
// Flags: --expose-internals
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { mustNotCall } = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
|
||||||
|
const {
|
||||||
|
RegExpPrototypeSymbolReplace,
|
||||||
|
RegExpPrototypeSymbolSearch,
|
||||||
|
RegExpPrototypeSymbolSplit,
|
||||||
|
SafeStringPrototypeSearch,
|
||||||
|
hardenRegExp,
|
||||||
|
} = require('internal/test/binding').primordials;
|
||||||
|
|
||||||
|
|
||||||
|
Object.defineProperties(RegExp.prototype, {
|
||||||
|
[Symbol.match]: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%[@@match]'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%[@@match]'),
|
||||||
|
},
|
||||||
|
[Symbol.matchAll]: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%[@@matchAll]'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%[@@matchAll]'),
|
||||||
|
},
|
||||||
|
[Symbol.replace]: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%[@@replace]'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%[@@replace]'),
|
||||||
|
},
|
||||||
|
[Symbol.search]: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%[@@search]'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%[@@search]'),
|
||||||
|
},
|
||||||
|
[Symbol.split]: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%[@@split]'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%[@@split]'),
|
||||||
|
},
|
||||||
|
dotAll: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.dotAll'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.dotAll'),
|
||||||
|
},
|
||||||
|
exec: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.exec'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.exec'),
|
||||||
|
},
|
||||||
|
flags: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.flags'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.flags'),
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.global'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.global'),
|
||||||
|
},
|
||||||
|
hasIndices: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.hasIndices'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.hasIndices'),
|
||||||
|
},
|
||||||
|
ignoreCase: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.ignoreCase'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.ignoreCase'),
|
||||||
|
},
|
||||||
|
multiline: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.multiline'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.multiline'),
|
||||||
|
},
|
||||||
|
source: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.source'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.source'),
|
||||||
|
},
|
||||||
|
sticky: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.sticky'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.sticky'),
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.test'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.test'),
|
||||||
|
},
|
||||||
|
toString: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.toString'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.toString'),
|
||||||
|
},
|
||||||
|
unicode: {
|
||||||
|
get: mustNotCall('get %RegExp.prototype%.unicode'),
|
||||||
|
set: mustNotCall('set %RegExp.prototype%.unicode'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
hardenRegExp(hardenRegExp(/1/));
|
||||||
|
|
||||||
|
// IMO there are no valid use cases in node core to use RegExpPrototypeSymbolMatch
|
||||||
|
// or RegExpPrototypeSymbolMatchAll, they are inherently unsafe.
|
||||||
|
|
||||||
|
{
|
||||||
|
const myRegex = hardenRegExp(/a/);
|
||||||
|
assert.strictEqual(RegExpPrototypeSymbolReplace(myRegex, 'baar', 'e'), 'bear');
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const myRegex = hardenRegExp(/a/g);
|
||||||
|
assert.strictEqual(RegExpPrototypeSymbolReplace(myRegex, 'baar', 'e'), 'beer');
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const myRegex = hardenRegExp(/a/);
|
||||||
|
assert.strictEqual(RegExpPrototypeSymbolSearch(myRegex, 'baar'), 1);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const myRegex = /a/;
|
||||||
|
assert.strictEqual(SafeStringPrototypeSearch('baar', myRegex), 1);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const myRegex = hardenRegExp(/a/);
|
||||||
|
assert.deepStrictEqual(RegExpPrototypeSymbolSplit(myRegex, 'baar', 0), []);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const myRegex = hardenRegExp(/a/);
|
||||||
|
assert.deepStrictEqual(RegExpPrototypeSymbolSplit(myRegex, 'baar', 1), ['b']);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const myRegex = hardenRegExp(/a/);
|
||||||
|
assert.deepStrictEqual(RegExpPrototypeSymbolSplit(myRegex, 'baar'), ['b', '', 'r']);
|
||||||
|
}
|
|
@ -217,6 +217,8 @@ const customTypesMap = {
|
||||||
'URL': 'url.html#the-whatwg-url-api',
|
'URL': 'url.html#the-whatwg-url-api',
|
||||||
'URLSearchParams': 'url.html#class-urlsearchparams',
|
'URLSearchParams': 'url.html#class-urlsearchparams',
|
||||||
|
|
||||||
|
'MIMEParams': 'util.html#class-utilmimeparams',
|
||||||
|
|
||||||
'vm.Module': 'vm.html#class-vmmodule',
|
'vm.Module': 'vm.html#class-vmmodule',
|
||||||
'vm.Script': 'vm.html#class-vmscript',
|
'vm.Script': 'vm.html#class-vmscript',
|
||||||
'vm.SourceTextModule': 'vm.html#class-vmsourcetextmodule',
|
'vm.SourceTextModule': 'vm.html#class-vmsourcetextmodule',
|
||||||
|
|
|
@ -135,17 +135,22 @@ module.exports = {
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[CallExpression(/^RegExpPrototypeSymbol(Match|MatchAll|Search)$/)](node) {
|
[CallExpression(/^RegExpPrototypeSymbol(Match|MatchAll)$/)](node) {
|
||||||
context.report({
|
context.report({
|
||||||
node,
|
node,
|
||||||
message: node.callee.name + ' looks up the "exec" property of `this` value',
|
message: node.callee.name + ' looks up the "exec" property of `this` value',
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
[CallExpression(/^(RegExpPrototypeSymbol|StringPrototype)Search$/)](node) {
|
||||||
|
context.report({
|
||||||
|
node,
|
||||||
|
message: node.callee.name + ' is unsafe, use SafeStringPrototypeSearch instead',
|
||||||
|
});
|
||||||
|
},
|
||||||
...createUnsafeStringMethodReport(context, '%String.prototype.match%', 'Symbol.match'),
|
...createUnsafeStringMethodReport(context, '%String.prototype.match%', 'Symbol.match'),
|
||||||
...createUnsafeStringMethodReport(context, '%String.prototype.matchAll%', 'Symbol.matchAll'),
|
...createUnsafeStringMethodReport(context, '%String.prototype.matchAll%', 'Symbol.matchAll'),
|
||||||
...createUnsafeStringMethodOnRegexReport(context, '%String.prototype.replace%', 'Symbol.replace'),
|
...createUnsafeStringMethodOnRegexReport(context, '%String.prototype.replace%', 'Symbol.replace'),
|
||||||
...createUnsafeStringMethodOnRegexReport(context, '%String.prototype.replaceAll%', 'Symbol.replace'),
|
...createUnsafeStringMethodOnRegexReport(context, '%String.prototype.replaceAll%', 'Symbol.replace'),
|
||||||
...createUnsafeStringMethodReport(context, '%String.prototype.search%', 'Symbol.search'),
|
|
||||||
...createUnsafeStringMethodOnRegexReport(context, '%String.prototype.split%', 'Symbol.split'),
|
...createUnsafeStringMethodOnRegexReport(context, '%String.prototype.split%', 'Symbol.split'),
|
||||||
|
|
||||||
'NewExpression[callee.name="Proxy"][arguments.1.type="ObjectExpression"]'(node) {
|
'NewExpression[callee.name="Proxy"][arguments.1.type="ObjectExpression"]'(node) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue