0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-10-30 09:08:00 -04:00
denoland-deno/extensions/web/01_mimesniff.js
2021-04-30 15:51:48 -04:00

190 lines
4.4 KiB
JavaScript

// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
// @ts-check
/// <reference path="../../core/lib.deno_core.d.ts" />
/// <reference path="../web/internal.d.ts" />
/// <reference path="../web/lib.deno_web.d.ts" />
"use strict";
((window) => {
const {
collectSequenceOfCodepoints,
HTTP_WHITESPACE,
HTTP_WHITESPACE_PREFIX_RE,
HTTP_WHITESPACE_SUFFIX_RE,
HTTP_QUOTED_STRING_TOKEN_POINT_RE,
HTTP_TOKEN_CODE_POINT_RE,
collectHttpQuotedString,
} = window.__bootstrap.infra;
/**
* @typedef MimeType
* @property {string} type
* @property {string} subtype
* @property {Map<string,string>} parameters
*/
/**
* @param {string} input
* @returns {MimeType | null}
*/
function parseMimeType(input) {
// 1.
input = input.replaceAll(HTTP_WHITESPACE_PREFIX_RE, "");
input = input.replaceAll(HTTP_WHITESPACE_SUFFIX_RE, "");
// 2.
let position = 0;
const endOfInput = input.length;
// 3.
const res1 = collectSequenceOfCodepoints(
input,
position,
(c) => c != "\u002F",
);
const type = res1.result;
position = res1.position;
// 4.
if (type === "" || !HTTP_TOKEN_CODE_POINT_RE.test(type)) return null;
// 5.
if (position >= endOfInput) return null;
// 6.
position++;
// 7.
const res2 = collectSequenceOfCodepoints(
input,
position,
(c) => c != "\u003B",
);
let subtype = res2.result;
position = res2.position;
// 8.
subtype = subtype.replaceAll(HTTP_WHITESPACE_SUFFIX_RE, "");
// 9.
if (subtype === "" || !HTTP_TOKEN_CODE_POINT_RE.test(subtype)) return null;
// 10.
const mimeType = {
type: type.toLowerCase(),
subtype: subtype.toLowerCase(),
/** @type {Map<string, string>} */
parameters: new Map(),
};
// 11.
while (position < endOfInput) {
// 11.1.
position++;
// 11.2.
const res1 = collectSequenceOfCodepoints(
input,
position,
(c) => HTTP_WHITESPACE.includes(c),
);
position = res1.position;
// 11.3.
const res2 = collectSequenceOfCodepoints(
input,
position,
(c) => c !== "\u003B" && c !== "\u003D",
);
let parameterName = res2.result;
position = res2.position;
// 11.4.
parameterName = parameterName.toLowerCase();
// 11.5.
if (position < endOfInput) {
if (input[position] == "\u003B") continue;
position++;
}
// 11.6.
if (position >= endOfInput) break;
// 11.7.
let parameterValue = null;
// 11.8.
if (input[position] === "\u0022") {
// 11.8.1.
const res = collectHttpQuotedString(input, position, true);
parameterValue = res.result;
position = res.position;
// 11.8.2.
position++;
} else { // 11.9.
// 11.9.1.
const res = collectSequenceOfCodepoints(
input,
position,
(c) => c !== "\u003B",
);
parameterValue = res.result;
position = res.position;
// 11.9.2.
parameterValue = parameterValue.replaceAll(
HTTP_WHITESPACE_SUFFIX_RE,
"",
);
// 11.9.3.
if (parameterValue === "") continue;
}
// 11.10.
if (
parameterName !== "" && HTTP_TOKEN_CODE_POINT_RE.test(parameterName) &&
HTTP_QUOTED_STRING_TOKEN_POINT_RE.test(parameterValue) &&
!mimeType.parameters.has(parameterName)
) {
mimeType.parameters.set(parameterName, parameterValue);
}
}
// 12.
return mimeType;
}
/**
* @param {MimeType} mimeType
* @returns {string}
*/
function essence(mimeType) {
return `${mimeType.type}/${mimeType.subtype}`;
}
/**
* @param {MimeType} mimeType
* @returns {string}
*/
function serializeMimeType(mimeType) {
let serialization = essence(mimeType);
for (const param of mimeType.parameters) {
serialization += `;${param[0]}=`;
let value = param[1];
if (!HTTP_TOKEN_CODE_POINT_RE.test(value)) {
value = value.replaceAll("\\", "\\\\");
value = value.replaceAll('"', '\\"');
value = `"${value}"`;
}
serialization += value;
}
return serialization;
}
window.__bootstrap.mimesniff = { parseMimeType, essence, serializeMimeType };
})(this);