Skip to content

Commit

Permalink
feat: prefer to use fs.createReadStream over fs.readFileSync to r…
Browse files Browse the repository at this point in the history
…ead files
  • Loading branch information
alexander-akait authored Mar 19, 2024
1 parent 1258fdd commit ab533de
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 196 deletions.
17 changes: 8 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,12 @@ const noop = () => {};
* @return {Promise<void>}
*/

/** @typedef {import("./utils/getFilenameFromUrl").Extra} Extra */

/**
* @callback GetFilenameFromUrl
* @param {string} url
* @param {Extra=} extra
* @returns {string | undefined}
*/

Expand Down Expand Up @@ -263,18 +266,14 @@ function wdm(compiler, options = {}) {
}
}

const instance = /** @type {API<RequestInternal, ResponseInternal>} */ (
middleware(context)
);
const instance =
/** @type {API<RequestInternal, ResponseInternal>} */
(middleware(context));

// API
/** @type {API<RequestInternal, ResponseInternal>} */
(instance).getFilenameFromUrl =
/**
* @param {string} url
* @returns {string|undefined}
*/
(url) => getFilenameFromUrl(context, url);
(instance).getFilenameFromUrl = (url, extra) =>
getFilenameFromUrl(context, url, extra);

/** @type {API<RequestInternal, ResponseInternal>} */
(instance).waitUntilValid = (callback = noop) => {
Expand Down
138 changes: 23 additions & 115 deletions src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ const mime = require("mime-types");

const getFilenameFromUrl = require("./utils/getFilenameFromUrl");
const {
getHeaderNames,
getHeaderFromRequest,
getHeaderFromResponse,
setHeaderForResponse,
setStatusCode,
send,
sendError,
} = require("./utils/compatibleAPI");
const ready = require("./utils/ready");

Expand All @@ -27,28 +27,6 @@ function getValueContentRangeHeader(type, size, range) {
return `${type} ${range ? `${range.start}-${range.end}` : "*"}/${size}`;
}

/**
* @param {string | number} title
* @param {string} body
* @returns {string}
*/
function createHtmlDocument(title, body) {
return (
`${
"<!DOCTYPE html>\n" +
'<html lang="en">\n' +
"<head>\n" +
'<meta charset="utf-8">\n' +
"<title>"
}${title}</title>\n` +
`</head>\n` +
`<body>\n` +
`<pre>${body}</pre>\n` +
`</body>\n` +
`</html>\n`
);
}

const BYTES_RANGE_REGEXP = /^ *bytes/i;

/**
Expand Down Expand Up @@ -94,9 +72,12 @@ function wrapper(context) {
}

async function processRequest() {
/** @type {import("./utils/getFilenameFromUrl").Extra} */
const extra = {};
const filename = getFilenameFromUrl(
context,
/** @type {string} */ (req.url),
extra,
);

if (!filename) {
Expand Down Expand Up @@ -161,68 +142,29 @@ function wrapper(context) {

const rangeHeader = getHeaderFromRequest(req, "range");

let start;
let end;
let len = /** @type {import("fs").Stats} */ (extra.stats).size;
let offset = 0;

if (rangeHeader && BYTES_RANGE_REGEXP.test(rangeHeader)) {
const size = await new Promise((resolve) => {
/** @type {import("fs").lstat} */
(context.outputFileSystem.lstat)(filename, (error, stats) => {
if (error) {
context.logger.error(error);

return;
}

resolve(stats.size);
});
});

// eslint-disable-next-line global-require
const parsedRanges = require("range-parser")(size, rangeHeader, {
const parsedRanges = require("range-parser")(len, rangeHeader, {
combine: true,
});

if (parsedRanges === -1) {
const message = "Unsatisfiable range for 'Range' header.";

context.logger.error(message);

const existingHeaders = getHeaderNames(res);
context.logger.error("Unsatisfiable range for 'Range' header.");

for (let i = 0; i < existingHeaders.length; i++) {
res.removeHeader(existingHeaders[i]);
}

setStatusCode(res, 416);
setHeaderForResponse(
res,
"Content-Range",
getValueContentRangeHeader("bytes", size),
getValueContentRangeHeader("bytes", len),
);
setHeaderForResponse(res, "Content-Type", "text/html; charset=utf-8");

/** @type {string | Buffer | import("fs").ReadStream} */
let document = createHtmlDocument(416, `Error: ${message}`);
let byteLength = Buffer.byteLength(document);

setHeaderForResponse(
res,
"Content-Length",
Buffer.byteLength(document),
);

if (context.options.modifyResponseData) {
({ data: document, byteLength } =
context.options.modifyResponseData(
req,
res,
document,
byteLength,
));
}

send(req, res, document, byteLength);
sendError(req, res, 416, {
headers: {
"Content-Range": res.getHeader("Content-Range"),
},
});

return;
} else if (parsedRanges === -2) {
Expand All @@ -243,57 +185,23 @@ function wrapper(context) {
"Content-Range",
getValueContentRangeHeader(
"bytes",
size,
len,
/** @type {import("range-parser").Ranges} */ (parsedRanges)[0],
),
);

[{ start, end }] = parsedRanges;
offset += parsedRanges[0].start;
len = parsedRanges[0].end - parsedRanges[0].start + 1;
}
}

const isFsSupportsStream =
typeof context.outputFileSystem.createReadStream === "function";

let bufferOrStream;
let byteLength;

try {
if (
typeof start !== "undefined" &&
typeof end !== "undefined" &&
isFsSupportsStream
) {
bufferOrStream =
/** @type {import("fs").createReadStream} */
(context.outputFileSystem.createReadStream)(filename, {
start,
end,
});
byteLength = end - start + 1;
} else {
bufferOrStream = /** @type {import("fs").readFileSync} */ (
context.outputFileSystem.readFileSync
)(filename);
({ byteLength } = bufferOrStream);
}
} catch (_ignoreError) {
await goNext();
const start = offset;
const end = Math.max(offset, offset + len - 1);

return;
}

if (context.options.modifyResponseData) {
({ data: bufferOrStream, byteLength } =
context.options.modifyResponseData(
req,
res,
bufferOrStream,
byteLength,
));
}

send(req, res, bufferOrStream, byteLength);
send(req, res, filename, start, end, goNext, {
modifyResponseData: context.options.modifyResponseData,
outputFileSystem: context.outputFileSystem,
});
}
};
}
Expand Down
Loading

0 comments on commit ab533de

Please sign in to comment.