Skip to content

Commit

Permalink
refactor: avoid unnecessary inner pseudo API (#1795)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait authored Mar 27, 2024
1 parent 8203d4c commit c64748e
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 197 deletions.
51 changes: 18 additions & 33 deletions src/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,7 @@ const path = require("path");
const mime = require("mime-types");

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

/** @typedef {import("./index.js").NextFunction} NextFunction */
Expand Down Expand Up @@ -44,14 +37,6 @@ function wrapper(context) {
// eslint-disable-next-line no-param-reassign
res.locals = res.locals || {};

if (req.method && !acceptedMethods.includes(req.method)) {
await goNext();

return;
}

ready(context, processRequest, req);

async function goNext() {
if (!context.options.serverSideRender) {
return next();
Expand All @@ -72,6 +57,12 @@ function wrapper(context) {
});
}

if (req.method && !acceptedMethods.includes(req.method)) {
await goNext();

return;
}

async function processRequest() {
/** @type {import("./utils/getFilenameFromUrl").Extra} */
const extra = {};
Expand Down Expand Up @@ -121,34 +112,28 @@ function wrapper(context) {
}

headers.forEach((header) => {
setHeaderForResponse(res, header.key, header.value);
res.setHeader(header.key, header.value);
});
}

if (!getHeaderFromResponse(res, "Content-Type")) {
if (!res.getHeader("Content-Type")) {
// content-type name(like application/javascript; charset=utf-8) or false
const contentType = mime.contentType(path.extname(filename));

// Only set content-type header if media type is known
// https://tools.ietf.org/html/rfc7231#section-3.1.1.5
if (contentType) {
setHeaderForResponse(res, "Content-Type", contentType);
res.setHeader("Content-Type", contentType);
} else if (context.options.mimeTypeDefault) {
setHeaderForResponse(
res,
"Content-Type",
context.options.mimeTypeDefault,
);
res.setHeader("Content-Type", context.options.mimeTypeDefault);
}
}

if (!getHeaderFromResponse(res, "Accept-Ranges")) {
setHeaderForResponse(res, "Accept-Ranges", "bytes");
if (!res.getHeader("Accept-Ranges")) {
res.setHeader("Accept-Ranges", "bytes");
}

const rangeHeader =
/** @type {string} */
(getHeaderFromRequest(req, "range"));
const rangeHeader = /** @type {string} */ (req.headers.range);

let len = /** @type {import("fs").Stats} */ (extra.stats).size;
let offset = 0;
Expand All @@ -162,8 +147,7 @@ function wrapper(context) {
if (parsedRanges === -1) {
context.logger.error("Unsatisfiable range for 'Range' header.");

setHeaderForResponse(
res,
res.setHeader(
"Content-Range",
getValueContentRangeHeader("bytes", len),
);
Expand All @@ -189,8 +173,7 @@ function wrapper(context) {
if (parsedRanges !== -2 && parsedRanges.length === 1) {
// Content-Range
setStatusCode(res, 206);
setHeaderForResponse(
res,
res.setHeader(
"Content-Range",
getValueContentRangeHeader(
"bytes",
Expand All @@ -212,6 +195,8 @@ function wrapper(context) {
outputFileSystem: context.outputFileSystem,
});
}

ready(context, processRequest, req);
};
}

Expand Down
142 changes: 22 additions & 120 deletions src/utils/compatibleAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,118 +13,11 @@ const escapeHtml = require("./escapeHtml");

/**
* @typedef {Object} ExpectedResponse
* @property {(name: string) => string | string[] | undefined} get
* @property {(name: string, value: number | string | string[]) => void} set
* @property {(status: number) => void} status
* @property {(data: any) => void} send
* @property {(status: number) => void} [status]
* @property {(data: any) => void} [send]
* @property {(data: any) => void} [pipeInto]
*/

/**
* @template {ServerResponse} Response
* @param {Response} res
* @returns {string[]}
*/
function getHeaderNames(res) {
// Pseudo API, TODO?
if (typeof res.getHeaderNames !== "function") {
// @ts-ignore
// eslint-disable-next-line no-underscore-dangle
return Object.keys(res._headers || {});
}

// Node.js API
return res.getHeaderNames();
}

/**
* @template {IncomingMessage} Request
* @param {Request} req
* @param {string} name
* @returns {string | string[] | undefined}
*/
function getHeaderFromRequest(req, name) {
// Express API
if (
typeof (/** @type {Request & ExpectedRequest} */ (req).get) === "function"
) {
return /** @type {Request & ExpectedRequest} */ (req).get(name);
}

// Node.js API
return req.headers[name];
}

/**
* @template {ServerResponse} Response
* @param {Response} res
* @param {string} name
* @returns {number | string | string[] | undefined}
*/
function getHeaderFromResponse(res, name) {
// Express API
if (
typeof (/** @type {Response & ExpectedResponse} */ (res).get) === "function"
) {
return /** @type {Response & ExpectedResponse} */ (res).get(name);
}

// Node.js API
return res.getHeader(name);
}

/**
* @template {ServerResponse} Response
* @param {Response} res
* @param {string} name
* @param {number | string | string[]} value
* @returns {void}
*/
function setHeaderForResponse(res, name, value) {
// Express API
if (
typeof (/** @type {Response & ExpectedResponse} */ (res).set) === "function"
) {
/** @type {Response & ExpectedResponse} */
(res).set(name, typeof value === "number" ? String(value) : value);

return;
}

// Node.js API
res.setHeader(name, value);
}

/**
* @template {ServerResponse} Response
* @param {Response} res
* @param {Record<string, number | string | string[] | undefined>} headers
*/
function setHeadersForResponse(res, headers) {
const keys = Object.keys(headers);

for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = headers[key];

if (typeof value !== "undefined") {
setHeaderForResponse(res, key, value);
}
}
}

/**
* @template {ServerResponse} Response
* @param {Response} res
*/
function clearHeadersForResponse(res) {
const headers = getHeaderNames(res);

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

/**
* @template {ServerResponse} Response
* @param {Response} res
Expand Down Expand Up @@ -212,17 +105,30 @@ function sendError(req, res, status, options) {
</html>`;

// Clear existing headers
clearHeadersForResponse(res);
const headers = res.getHeaderNames();

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

if (options && options.headers) {
setHeadersForResponse(res, options.headers);
const keys = Object.keys(options.headers);

for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = options.headers[key];

if (typeof value !== "undefined") {
res.setHeader(key, value);
}
}
}

// Send basic response
setStatusCode(res, status);
setHeaderForResponse(res, "Content-Type", "text/html; charset=utf-8");
setHeaderForResponse(res, "Content-Security-Policy", "default-src 'none'");
setHeaderForResponse(res, "X-Content-Type-Options", "nosniff");
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.setHeader("Content-Security-Policy", "default-src 'none'");
res.setHeader("X-Content-Type-Options", "nosniff");

let byteLength = Buffer.byteLength(document);

Expand All @@ -232,7 +138,7 @@ function sendError(req, res, status, options) {
(options.modifyResponseData(req, res, document, byteLength)));
}

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

res.end(document);
}
Expand Down Expand Up @@ -328,7 +234,7 @@ async function send(req, res, filename, start, end, goNext, options) {
}
});

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

// Pseudo API and Koa API
if (
Expand Down Expand Up @@ -377,10 +283,6 @@ async function send(req, res, filename, start, end, goNext, options) {
}

module.exports = {
getHeaderNames,
getHeaderFromRequest,
getHeaderFromResponse,
setHeaderForResponse,
setStatusCode,
send,
sendError,
Expand Down
48 changes: 4 additions & 44 deletions types/utils/compatibleAPI.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ export type ExpectedRequest = {
get: (name: string) => string | undefined;
};
export type ExpectedResponse = {
get: (name: string) => string | string[] | undefined;
set: (name: string, value: number | string | string[]) => void;
status: (status: number) => void;
send: (data: any) => void;
status?: ((status: number) => void) | undefined;
send?: ((data: any) => void) | undefined;
pipeInto?: ((data: any) => void) | undefined;
};
/**
Expand Down Expand Up @@ -43,48 +41,10 @@ export type SendOptions<
*/
/**
* @typedef {Object} ExpectedResponse
* @property {(name: string) => string | string[] | undefined} get
* @property {(name: string, value: number | string | string[]) => void} set
* @property {(status: number) => void} status
* @property {(data: any) => void} send
* @property {(status: number) => void} [status]
* @property {(data: any) => void} [send]
* @property {(data: any) => void} [pipeInto]
*/
/**
* @template {ServerResponse} Response
* @param {Response} res
* @returns {string[]}
*/
export function getHeaderNames<
Response extends import("../index.js").ServerResponse,
>(res: Response): string[];
/**
* @template {IncomingMessage} Request
* @param {Request} req
* @param {string} name
* @returns {string | string[] | undefined}
*/
export function getHeaderFromRequest<
Request extends import("http").IncomingMessage,
>(req: Request, name: string): string | string[] | undefined;
/**
* @template {ServerResponse} Response
* @param {Response} res
* @param {string} name
* @returns {number | string | string[] | undefined}
*/
export function getHeaderFromResponse<
Response extends import("../index.js").ServerResponse,
>(res: Response, name: string): number | string | string[] | undefined;
/**
* @template {ServerResponse} Response
* @param {Response} res
* @param {string} name
* @param {number | string | string[]} value
* @returns {void}
*/
export function setHeaderForResponse<
Response extends import("../index.js").ServerResponse,
>(res: Response, name: string, value: number | string | string[]): void;
/**
* @template {ServerResponse} Response
* @param {Response} res
Expand Down

0 comments on commit c64748e

Please sign in to comment.