UNPKG

cgi-core

Version:

Lightweight, zero-dependency middleware for hosting CGI scripts with HTTP/1.1 support

169 lines (151 loc) 4.98 kB
"use strict"; // Copyright (c) 2024-2025 lfortin // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Object.defineProperty(exports, "__esModule", { value: true }); const { STATUS_CODES } = require("node:http"); const { parseResponse, getRequestLogger } = require("./util"); const requestLogger = getRequestLogger(); function logRequest(req, statusCode) { const log = requestLogger(req, statusCode); if (log) { console.log(log); } } function errorHandler(data) { const { req, res, config } = this; const statusCode = 500; if (config.debugOutput) { res.statusCode = statusCode; if (!res.headersSent) { res.setHeader("Content-Type", "text/plain"); } res.write(`${statusCode}: ${STATUS_CODES[statusCode]}\n\n`); res.end(data); req.destroy(); // Terminate the request if (config.logRequests) { logRequest(req, res.statusCode); } } else { terminateRequest(req, res, statusCode, config); } } function terminateRequest(req, res, statusCode = 500, config) { const statusPage = config.statusPages[statusCode]; res.statusCode = statusCode; if (res.headersSent) { res.end(STATUS_CODES[statusCode]); } else if (statusPage) { res.setHeader("Content-Type", statusPage.contentType || "text/html"); res.end(statusPage.content || ""); } else { res.setHeader("Content-Type", "text/plain"); res.end(STATUS_CODES[statusCode]); } req.destroy(); // Terminate the request if (config.logRequests) { logRequest(req, res.statusCode); } } async function streamRequestPayload(child, req, config) { if (child.stdin) { // this just prevents exiting main node process and exits child process instead child.stdin.on("error", () => {}); const handleRequestPayload = function () { let chunk; while (null !== (chunk = req.read(config.requestChunkSize))) { child.stdin.write(chunk); } }; handleRequestPayload(); req.on("readable", handleRequestPayload); return new Promise((resolve) => { req.on("end", () => { child.stdin.end(""); resolve(true); }); }); } } async function streamResponsePayload(child, req, res, config) { if (res.headersSent) { return; } if (child.stdout) { let initChunkRead = false; let stdoutEnded = false; let headers; let bodyContent; let status; child.stdout.pause(); // this just prevents exiting main node process and exits child process instead child.stdout.on("error", () => {}); const handleResponsePayload = async function () { let chunk; while (null !== (chunk = child.stdout.read(config.responseChunkSize))) { if (!initChunkRead) { try { ({ headers, bodyContent, status } = await parseResponse(chunk)); } catch (err) { errorHandler.apply({ req, res, config }, [err.message]); initChunkRead = true; child.stdout.removeListener("readable", handleResponsePayload); child.stdout.resume(); return; } if (!res.headersSent) { res.statusCode = status || 200; res.setHeaders(new Headers(headers)); } res.write(bodyContent); } else { res.write(chunk); } initChunkRead = true; } process.nextTick(() => { if (stdoutEnded) { res.end(""); if (config.logRequests) { logRequest(req, res.statusCode); } } }); }; handleResponsePayload(); child.stdout.on("readable", handleResponsePayload); child.stdout.on("end", () => { stdoutEnded = true; }); } else { res.statusCode = 204; res.end(""); if (config.logRequests) { logRequest(req, res.statusCode); } } } module.exports = { logRequest, errorHandler, terminateRequest, streamRequestPayload, streamResponsePayload, };