318 lines
7.8 KiB
JavaScript

"use strict";
/** @typedef {import("../index.js").IncomingMessage} IncomingMessage */
/** @typedef {import("../index.js").ServerResponse} ServerResponse */
/** @typedef {import("../index").OutputFileSystem} OutputFileSystem */
/**
* @typedef {Object} ExpectedIncomingMessage
* @property {(name: string) => string | string[] | undefined} [getHeader]
* @property {() => string | undefined} [getMethod]
* @property {() => string | undefined} [getURL]
*/
/**
* @typedef {Object} ExpectedServerResponse
* @property {(status: number) => void} [setStatusCode]
* @property {() => number} [getStatusCode]
* @property {(name: string) => string | string[] | undefined | number} [getHeader]
* @property {(name: string, value: number | string | Readonly<string[]>) => ExpectedServerResponse} [setHeader]
* @property {(name: string) => void} [removeHeader]
* @property {(data: string | Buffer) => void} [send]
* @property {(data?: string | Buffer) => void} [finish]
* @property {() => string[]} [getResponseHeaders]
* @property {() => boolean} [getHeadersSent]
* @property {(data: any) => void} [stream]
* @property {() => any} [getOutgoing]
* @property {(name: string, value: any) => void} [setState]
* @property {() => "ready" | "open" | "readable"} [getReadyReadableStreamState]
*/
/**
* @template {IncomingMessage & ExpectedIncomingMessage} Request
* @param {Request} req
* @param {string} name
* @returns {string | string[] | undefined}
*/
function getRequestHeader(req, name) {
// Pseudo API
if (typeof req.getHeader === "function") {
return req.getHeader(name);
}
return req.headers[name];
}
/**
* @template {IncomingMessage & ExpectedIncomingMessage} Request
* @param {Request} req
* @returns {string | undefined}
*/
function getRequestMethod(req) {
// Pseudo API
if (typeof req.getMethod === "function") {
return req.getMethod();
}
return req.method;
}
/**
* @template {IncomingMessage & ExpectedIncomingMessage} Request
* @param {Request} req
* @returns {string | undefined}
*/
function getRequestURL(req) {
// Pseudo API
if (typeof req.getURL === "function") {
return req.getURL();
}
return req.url;
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @param {number} code
*/
function setStatusCode(res, code) {
// Pseudo API
if (typeof res.setStatusCode === "function") {
res.setStatusCode(code);
return;
}
// Node.js API
// eslint-disable-next-line no-param-reassign
res.statusCode = code;
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @returns {number}
*/
function getStatusCode(res) {
// Pseudo API
if (typeof res.getStatusCode === "function") {
return res.getStatusCode();
}
return res.statusCode;
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @param {string} name
* @returns {string | string[] | undefined | number}
*/
function getResponseHeader(res, name) {
// Real and Pseudo API
return res.getHeader(name);
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @param {string} name
* @param {number | string | Readonly<string[]>} value
* @returns {Response}
*/
function setResponseHeader(res, name, value) {
// Real and Pseudo API
return res.setHeader(name, value);
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @param {string} name
*/
function removeResponseHeader(res, name) {
// Real and Pseudo API
res.removeHeader(name);
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @returns {string[]}
*/
function getResponseHeaders(res) {
// Pseudo API
if (typeof res.getResponseHeaders === "function") {
return res.getResponseHeaders();
}
return res.getHeaderNames();
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @returns {boolean}
*/
function getHeadersSent(res) {
// Pseudo API
if (typeof res.getHeadersSent === "function") {
return res.getHeadersSent();
}
return res.headersSent;
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @param {import("fs").ReadStream} bufferOrStream
*/
function pipe(res, bufferOrStream) {
// Pseudo API and Koa API
if (typeof res.stream === "function") {
// Writable stream into Readable stream
res.stream(bufferOrStream);
return;
}
// Node.js API and Express API and Hapi API
bufferOrStream.pipe(res);
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @param {string | Buffer} bufferOrString
*/
function send(res, bufferOrString) {
// Pseudo API and Express API and Koa API
if (typeof res.send === "function") {
res.send(bufferOrString);
return;
}
res.end(bufferOrString);
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @param {string | Buffer} [data]
*/
function finish(res, data) {
// Pseudo API and Express API and Koa API
if (typeof res.finish === "function") {
res.finish(data);
return;
}
// Pseudo API and Express API and Koa API
res.end(data);
}
/**
* @param {string} filename
* @param {OutputFileSystem} outputFileSystem
* @param {number} start
* @param {number} end
* @returns {{ bufferOrStream: (Buffer | import("fs").ReadStream), byteLength: number }}
*/
function createReadStreamOrReadFileSync(filename, outputFileSystem, start, end) {
/** @type {string | Buffer | import("fs").ReadStream} */
let bufferOrStream;
/** @type {number} */
let byteLength;
// Stream logic
const isFsSupportsStream = typeof outputFileSystem.createReadStream === "function";
if (isFsSupportsStream) {
bufferOrStream = /** @type {import("fs").createReadStream} */
outputFileSystem.createReadStream(filename, {
start,
end
});
// Handle files with zero bytes
byteLength = end === 0 ? 0 : end - start + 1;
} else {
bufferOrStream = outputFileSystem.readFileSync(filename);
({
byteLength
} = bufferOrStream);
}
return {
bufferOrStream,
byteLength
};
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @returns {Response} res
*/
function getOutgoing(res) {
// Pseudo API and Express API and Koa API
if (typeof res.getOutgoing === "function") {
return res.getOutgoing();
}
return res;
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
*/
function initState(res) {
if (typeof res.setState === "function") {
return;
}
// fixes #282. credit @cexoso. in certain edge situations res.locals is undefined.
// eslint-disable-next-line no-param-reassign
res.locals = res.locals || {};
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @param {string} name
* @param {any} value
*/
function setState(res, name, value) {
if (typeof res.setState === "function") {
res.setState(name, value);
return;
}
/** @type {any} */
// eslint-disable-next-line no-param-reassign
res.locals[name] = value;
}
/**
* @template {ServerResponse & ExpectedServerResponse} Response
* @param {Response} res
* @returns {"ready" | "open" | "readable"}
*/
function getReadyReadableStreamState(res) {
// Pseudo API and Express API and Koa API
if (typeof res.getReadyReadableStreamState === "function") {
return res.getReadyReadableStreamState();
}
return "ready";
}
module.exports = {
setStatusCode,
getStatusCode,
getRequestHeader,
getRequestMethod,
getRequestURL,
getResponseHeader,
setResponseHeader,
removeResponseHeader,
getResponseHeaders,
getHeadersSent,
pipe,
send,
finish,
createReadStreamOrReadFileSync,
getOutgoing,
initState,
setState,
getReadyReadableStreamState
};