feat: replace "mime-types" library with custom MIME type lookup function that uses "mime-db" library
This commit is contained in:
parent
b925ff04f6
commit
070c282910
6 changed files with 162 additions and 22 deletions
18
package-lock.json
generated
18
package-lock.json
generated
|
@ -9,7 +9,7 @@
|
|||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"formidable": "^2.1.2",
|
||||
"mime-types": "^2.1.35",
|
||||
"mime-db": "^1.53.0",
|
||||
"ocsp": "^1.2.0",
|
||||
"tar": "^6.2.1"
|
||||
},
|
||||
|
@ -8233,9 +8233,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"version": "1.53.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz",
|
||||
"integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
|
@ -8244,6 +8244,7 @@
|
|||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
|
@ -8251,6 +8252,15 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-fn": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"formidable": "^2.1.2",
|
||||
"mime-types": "^2.1.35",
|
||||
"mime-db": "^1.53.0",
|
||||
"ocsp": "^1.2.0",
|
||||
"tar": "^6.2.1"
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@ const fs = require("fs");
|
|||
const os = require("os");
|
||||
const path = require("path");
|
||||
const zlib = require("zlib");
|
||||
const mime = require("mime-types");
|
||||
const { getMimeType, checkIfCompressible } = require("../utils/mimeTypes.js");
|
||||
const defaultPageCSS = require("../res/defaultPageCSS.js");
|
||||
const matchHostname = require("../utils/matchHostname.js");
|
||||
const ipMatch = require("../utils/ipMatch.js");
|
||||
|
@ -200,7 +200,7 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
"bytes " + begin + "-" + end + "/" + filelen;
|
||||
rhd["Content-Length"] = end - begin + 1;
|
||||
delete rhd["Content-Type"];
|
||||
const mtype = mime.contentType(ext);
|
||||
const mtype = getMimeType(ext);
|
||||
if (mtype && ext != "") rhd["Content-Type"] = mtype;
|
||||
if (fileETag) rhd["ETag"] = fileETag;
|
||||
|
||||
|
@ -338,7 +338,7 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
let useGzip =
|
||||
ext != "gz" && filelen > 256 && acceptEncoding.match(/\bgzip\b/);
|
||||
|
||||
let isCompressable = true;
|
||||
let isCompressible = checkIfCompressible(ext);
|
||||
try {
|
||||
// Check for files not to compressed and compression enabling setting. Also check for browser quirks and adjust compression accordingly
|
||||
if (
|
||||
|
@ -346,7 +346,7 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
config.enableCompression !== true ||
|
||||
!canCompress(href, config.dontCompress)
|
||||
) {
|
||||
isCompressable = false; // Compression is disabled
|
||||
isCompressible = false; // Compression is disabled
|
||||
} else if (
|
||||
ext != "html" &&
|
||||
ext != "htm" &&
|
||||
|
@ -362,9 +362,9 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
req.headers["user-agent"]
|
||||
)
|
||||
) {
|
||||
isCompressable = false; // Netscape 4.x doesn't handle compressed data properly outside of HTML documents.
|
||||
isCompressible = false; // Netscape 4.x doesn't handle compressed data properly outside of HTML documents.
|
||||
} else if (/^w3m\/[^ ]*$/.test(req.headers["user-agent"])) {
|
||||
isCompressable = false; // w3m doesn't handle compressed data properly outside of HTML documents.
|
||||
isCompressible = false; // w3m doesn't handle compressed data properly outside of HTML documents.
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
|
@ -375,7 +375,7 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
req.headers["user-agent"]
|
||||
)
|
||||
) {
|
||||
isCompressable = false; // Netscape 4.06-4.08 doesn't handle compressed data properly.
|
||||
isCompressible = false; // Netscape 4.06-4.08 doesn't handle compressed data properly.
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
@ -384,7 +384,7 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
}
|
||||
|
||||
// Bun 1.1 has definition for zlib.createBrotliCompress, but throws an error while invoking the function.
|
||||
if (process.isBun && useBrotli && isCompressable) {
|
||||
if (process.isBun && useBrotli && isCompressible) {
|
||||
try {
|
||||
zlib.createBrotliCompress();
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
@ -395,11 +395,11 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
|
||||
try {
|
||||
let hdhds = {};
|
||||
if (useBrotli && isCompressable) {
|
||||
if (useBrotli && isCompressible) {
|
||||
hdhds["Content-Encoding"] = "br";
|
||||
} else if (useDeflate && isCompressable) {
|
||||
} else if (useDeflate && isCompressible) {
|
||||
hdhds["Content-Encoding"] = "deflate";
|
||||
} else if (useGzip && isCompressable) {
|
||||
} else if (useGzip && isCompressible) {
|
||||
hdhds["Content-Encoding"] = "gzip";
|
||||
} else {
|
||||
if (ext == "html") {
|
||||
|
@ -411,7 +411,7 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
}
|
||||
hdhds["Accept-Ranges"] = "bytes";
|
||||
delete hdhds["Content-Type"];
|
||||
const mtype = mime.contentType(ext);
|
||||
const mtype = getMimeType(ext);
|
||||
if (mtype && ext != "") hdhds["Content-Type"] = mtype;
|
||||
if (fileETag) hdhds["ETag"] = fileETag;
|
||||
|
||||
|
@ -442,13 +442,13 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
.on("open", () => {
|
||||
try {
|
||||
let resStream = {};
|
||||
if (useBrotli && isCompressable) {
|
||||
if (useBrotli && isCompressible) {
|
||||
resStream = zlib.createBrotliCompress();
|
||||
resStream.pipe(res);
|
||||
} else if (useDeflate && isCompressable) {
|
||||
} else if (useDeflate && isCompressible) {
|
||||
resStream = zlib.createDeflateRaw();
|
||||
resStream.pipe(res);
|
||||
} else if (useGzip && isCompressable) {
|
||||
} else if (useGzip && isCompressible) {
|
||||
resStream = zlib.createGzip();
|
||||
resStream.pipe(res);
|
||||
} else {
|
||||
|
@ -754,7 +754,7 @@ module.exports = (req, res, logFacilities, config, next) => {
|
|||
const ename = filelist[i].name;
|
||||
let eext = ename.match(/\.([^.]+)$/);
|
||||
eext = eext ? eext[1] : "";
|
||||
const emime = eext ? mime.contentType(eext) : false;
|
||||
const emime = eext ? getMimeType(eext) : false;
|
||||
if (filelist[i].errored) {
|
||||
directoryListingRows.push(
|
||||
`<tr><td style="width: 24px;"><img src="/.dirimages/bad.png" alt="[BAD]" width="24px" height="24px" /></td><td style="word-wrap: break-word; word-break: break-word; overflow-wrap: break-word;"><a href="${(
|
||||
|
|
67
src/utils/mimeTypes.js
Normal file
67
src/utils/mimeTypes.js
Normal file
|
@ -0,0 +1,67 @@
|
|||
const mimeDb = require("mime-db");
|
||||
let optimizedMimeDb = {};
|
||||
|
||||
// Initialize the optimized MIME type database, similarly to what "mime-types" library does
|
||||
Object.keys(mimeDb).forEach((mimeType) => {
|
||||
const sourcePreference = ["nginx", "apache", undefined, "iana"];
|
||||
if (
|
||||
mimeType != "application/octet-stream" &&
|
||||
mimeDb[mimeType].extensions &&
|
||||
mimeDb[mimeType].extensions.length > 0
|
||||
) {
|
||||
mimeDb[mimeType].extensions.forEach((extension) => {
|
||||
if (optimizedMimeDb[extension]) {
|
||||
const from = sourcePreference.indexOf(
|
||||
optimizedMimeDb[extension].source
|
||||
);
|
||||
const to = sourcePreference.indexOf(mimeDb[mimeType].source);
|
||||
if (
|
||||
from != -1 &&
|
||||
to != -1 &&
|
||||
(from > to ||
|
||||
(from === to && mimeType.substring(0, 12) == "application/"))
|
||||
)
|
||||
return;
|
||||
}
|
||||
optimizedMimeDb[extension] = {
|
||||
type: mimeType,
|
||||
charset: mimeDb[mimeType].charset,
|
||||
source: mimeDb[mimeType].source,
|
||||
compressible: mimeDb[mimeType].compressible
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Function to get the MIME type from the extension
|
||||
function getMimeType(extension) {
|
||||
if (!extension || typeof extension !== "string") return false;
|
||||
const extensionMatch = extension.match(/\.([^.]+)$/);
|
||||
const normalizedExtension = extensionMatch ? extensionMatch[1] : extension;
|
||||
if (optimizedMimeDb[normalizedExtension])
|
||||
return (
|
||||
optimizedMimeDb[normalizedExtension].type +
|
||||
(optimizedMimeDb[normalizedExtension].charset
|
||||
? "; charset=" +
|
||||
optimizedMimeDb[normalizedExtension].charset.toLowerCase()
|
||||
: "")
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to check if the file is compressible from the extension
|
||||
function checkIfCompressible(extension) {
|
||||
if (!extension || typeof extension !== "string") return true;
|
||||
const extensionMatch = extension.match(/\.([^.]+)$/);
|
||||
const normalizedExtension = extensionMatch ? extensionMatch[1] : extension;
|
||||
if (optimizedMimeDb[normalizedExtension])
|
||||
return optimizedMimeDb[normalizedExtension].compressible === undefined
|
||||
? true
|
||||
: optimizedMimeDb[normalizedExtension].compressible;
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getMimeType: getMimeType,
|
||||
checkIfCompressible: checkIfCompressible
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "SVR.JS Core",
|
||||
"externalPackages": ["mime-types"],
|
||||
"externalPackages": ["mime-db"],
|
||||
"packageJSON": {
|
||||
"name": "svrjs-core",
|
||||
"description": "A library for static file serving, built from SVR.JS source code.",
|
||||
|
|
63
tests/utils/mimeTypes.test.js
Normal file
63
tests/utils/mimeTypes.test.js
Normal file
|
@ -0,0 +1,63 @@
|
|||
const {
|
||||
getMimeType,
|
||||
checkIfCompressible
|
||||
} = require("../../src/utils/mimeTypes.js");
|
||||
|
||||
describe("MIME type utilities", () => {
|
||||
describe("getMimeType", () => {
|
||||
test("should return the correct MIME type for a given extension", () => {
|
||||
const extension = "html";
|
||||
const expectedMimeType = "text/html";
|
||||
expect(getMimeType(extension)).toBe(expectedMimeType);
|
||||
});
|
||||
|
||||
test("should return false for an unknown extension", () => {
|
||||
const extension = "unknown";
|
||||
expect(getMimeType(extension)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return false for an invalid extension", () => {
|
||||
const extension = 123;
|
||||
expect(getMimeType(extension)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return the correct MIME type for an extension with a dot", () => {
|
||||
const extension = ".html";
|
||||
const expectedMimeType = "text/html";
|
||||
expect(getMimeType(extension)).toBe(expectedMimeType);
|
||||
});
|
||||
|
||||
test("should return the correct MIME type for an extension with a charset", () => {
|
||||
const extension = "css";
|
||||
const expectedMimeType = "text/css; charset=utf-8";
|
||||
expect(getMimeType(extension)).toBe(expectedMimeType);
|
||||
});
|
||||
});
|
||||
|
||||
describe("checkIfCompressible", () => {
|
||||
test("should return true for a compressible extension", () => {
|
||||
const extension = "html";
|
||||
expect(checkIfCompressible(extension)).toBe(true);
|
||||
});
|
||||
|
||||
test("should return false for a non-compressible extension", () => {
|
||||
const extension = "jpg";
|
||||
expect(checkIfCompressible(extension)).toBe(false);
|
||||
});
|
||||
|
||||
test("should return true for an unknown extension", () => {
|
||||
const extension = "unknown";
|
||||
expect(checkIfCompressible(extension)).toBe(true);
|
||||
});
|
||||
|
||||
test("should return true for an invalid extension", () => {
|
||||
const extension = 123;
|
||||
expect(checkIfCompressible(extension)).toBe(true);
|
||||
});
|
||||
|
||||
test("should return true for an extension with a dot", () => {
|
||||
const extension = ".html";
|
||||
expect(checkIfCompressible(extension)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue