Skip to content

Commit 124f248

Browse files
committed
util: make MIMEParams accessors case-insensitive
MIME parameter names are case-insensitive and the parser already ASCII-lowercases them, but MIMEParams.get(), has(), delete() and set() compared against the raw argument. As a result a parsed parameter was unreachable by the casing the caller used (e.g. params.get('Charset') returned null for "Charset=utf-8"), and set() could create duplicate, case-colliding parameters. Lowercase the name in the accessors to match the parser, consistent with the whatwg-mimetype reference implementation. Signed-off-by: Daijiro Wachi <daijiro.wachi@gmail.com>
1 parent c612f35 commit 124f248

2 files changed

Lines changed: 31 additions & 3 deletions

File tree

lib/internal/mime.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,13 @@ class MIMEParams {
148148
*/
149149
delete(name) {
150150
this.#parse();
151-
this.#data.delete(name);
151+
this.#data.delete(toASCIILower(`${name}`));
152152
}
153153

154154
get(name) {
155155
this.#parse();
156156
const data = this.#data;
157+
name = toASCIILower(`${name}`);
157158
if (data.has(name)) {
158159
return data.get(name);
159160
}
@@ -162,13 +163,13 @@ class MIMEParams {
162163

163164
has(name) {
164165
this.#parse();
165-
return this.#data.has(name);
166+
return this.#data.has(toASCIILower(`${name}`));
166167
}
167168

168169
set(name, value) {
169170
this.#parse();
170171
const data = this.#data;
171-
name = `${name}`;
172+
name = toASCIILower(`${name}`);
172173
value = `${value}`;
173174
const invalidNameIndex = SafeStringPrototypeSearch(name, NOT_HTTP_TOKEN_CODE_POINT);
174175
if (name.length === 0 || invalidNameIndex !== -1) {

test/parallel/test-mime-api.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,30 @@ assert.throws(() => params.set(`x${NOT_HTTP_TOKEN_CODE_POINT}`, 'x'), /parameter
158158
assert.throws(() => params.set('x', `${NOT_HTTP_QUOTED_STRING_CODE_POINT};`), /parameter value/i);
159159
assert.throws(() => params.set('x', `${NOT_HTTP_QUOTED_STRING_CODE_POINT}x`), /parameter value/i);
160160
assert.throws(() => params.set('x', `x${NOT_HTTP_QUOTED_STRING_CODE_POINT}`), /parameter value/i);
161+
162+
// MIME parameter names are case-insensitive. The parser lower-cases them, so
163+
// the MIMEParams accessors must lower-case their argument too; otherwise a
164+
// parsed parameter is unreachable by the casing the caller actually used and
165+
// `set()` can create case-colliding duplicate parameters.
166+
// Refs: https://mimesniff.spec.whatwg.org/ (parameter names are ASCII-lowercased)
167+
{
168+
const mime = new MIMEType('text/plain;Charset=value');
169+
assert.strictEqual(mime.params.get('Charset'), 'value');
170+
assert.strictEqual(mime.params.get('charset'), 'value');
171+
assert.strictEqual(mime.params.get('CHARSET'), 'value');
172+
assert.strictEqual(mime.params.has('Charset'), true);
173+
assert.strictEqual(`${mime.params}`, 'charset=value');
174+
175+
// `set()` with a mixed-case name overwrites the same parameter rather than
176+
// adding a second, case-colliding one.
177+
const params = new MIMEType('text/plain').params;
178+
params.set('Foo', 'a');
179+
params.set('foo', 'b');
180+
assert.deepStrictEqual([...params], [['foo', 'b']]);
181+
assert.strictEqual(`${params}`, 'foo=b');
182+
183+
// `delete()` is case-insensitive as well.
184+
params.delete('FOO');
185+
assert.strictEqual(params.has('foo'), false);
186+
assert.deepStrictEqual([...params], []);
187+
}

0 commit comments

Comments
 (0)