Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

util: add MIME utilities #21128

Merged
merged 13 commits into from
Oct 19, 2022
Prev Previous commit
Next Next commit
refactor static method
aduh95 committed Oct 16, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
commit fab5557cf7ef83d593c8421f0b776032f1652483
182 changes: 90 additions & 92 deletions lib/internal/mime.js
Original file line number Diff line number Diff line change
@@ -103,93 +103,6 @@ function removeBackslashes(str) {
return ret;
}

function parseParametersString(str, position, paramsMap = new SafeMap()) {
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,
NotHTTPTokenCodePoint) === -1 &&
SafeStringPrototypeSearch(parameterValue,
NotHTTPQuotedStringCodePoint) === -1 &&
paramsMap.has(parameterString) === false
) {
paramsMap.set(parameterString, parameterValue);
}
position++;
}
return paramsMap;
}

function escapeQuoteOrSolidus(str) {
let result = '';
@@ -277,8 +190,93 @@ class MIMEParams {

// Used to act as a friendly class to stringifying stuff
// not meant to be exposed to users, could inject invalid values
static _data(o) {
return o.#data;
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,
NotHTTPTokenCodePoint) === -1 &&
SafeStringPrototypeSearch(parameterValue,
NotHTTPQuotedStringCodePoint) === -1 &&
params.has(parameterString) === false
) {
paramsMap.set(parameterString, parameterValue);
}
position++;
}
return paramsMap;
}
}
const MIMEParamsStringify = MIMEParams.prototype.toString;
@@ -295,8 +293,8 @@ ObjectDefineProperty(MIMEParams.prototype, 'toJSON', {
writable: true,
});

const getMIMEParamsData = MIMEParams._data;
delete MIMEParams._data;
const { parseParametersString } = MIMEParams;
delete MIMEParams.parseParametersString;

class MIMEType {
#type;
@@ -311,7 +309,7 @@ class MIMEType {
parseParametersString(
string,
data.parametersStringIndex,
getMIMEParamsData(this.#parameters)
this.#parameters,
);
}