managed-http-proxy
Version:
An extended implementation of node-http-proxy to allow the creation of multiple managed proxy servers in Node and webpack-dev-server environments
865 lines (700 loc) • 31.7 kB
JavaScript
/// //<reference path="./http_proxy_server.d.ts" />
//@ts-ignore
/// <reference types="managed-http-proxy" />
//@ts-check
const { IncomingMessage, ServerResponse } = require("http");
const httpProxy = require("http-proxy");
const queryString = require("querystring");
const zlib = require("zlib");
/**
* @type {import("managed-http-proxy").ActiveProxyServersMap}
*/
let runningServers = new Map();
const URL_PARAM_FULL_MARKER = "/:";
const URL_MATCH_ALL_FULL_MARKER = "/*";
const URL_MATCH_ALL_SPECIAL_MARKER = "*";
const URL_QUERY_MARKER = "?";
const URL_PARAM_COLON_MARKER = ":";
const URL_SPLITTER = "/";
/**
* @type {import("managed-http-proxy").HttpProxyServer}
*/
const HttpProxyServer = {
createProxyServer: (options) => {
if(!options.target){
throw new Error("No target provided. Proxy server will not be created");
}
const proxyServer = httpProxy.createProxyServer(options);
//Get id. Use to reference correct server entry and Handlers for listeners (Automatically zero bases it)
const serverId = runningServers.size;
//Set up listeners
//Request
proxyServer.on("proxyReq", (proxyReq, req, res, options) => {
//@ts-expect-error Property 'body' doesn't exist in type IncomingMessage
if(!req.body || !Object.keys(req.body).length){
return;
}
/**
* @type {string}
*/
//@ts-expect-error Type string | number | string[] is not assignable to type string
const contentType = proxyReq.getHeader('Content-Type');
let bodyData;
if(contentType){
if(contentType.includes("application/json")){
//@ts-expect-error Property 'body' doesn't exist in type IncomingMessage
bodyData = JSON.stringify(req.body);
} else if(contentType.includes("application/x-www-form-urlencoded")){
//@ts-expect-error Property 'body' doesn't exist in type IncomingMessage
bodyData = queryString.stringify(req.body);
}
}
if(bodyData){
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
proxyReq.write(bodyData);
}
});
//Response
proxyServer.on("proxyRes", onProxyResponse.bind(this, serverId));
//Create our server object
/**
* @type {import("managed-http-proxy").ProxyServer}
*/
let serverObject = {};
serverObject.server = proxyServer;
serverObject.target = options.target;
serverObject.Handlers = new Map();
serverObject.dynamicHandlerUrls = [];
//Add to map
runningServers.set(serverId, serverObject);
console.log(`Proxy server successfully created with id ${serverId} and target ${options.target}`);
//Return the serverId
return serverId;
},
getServerMiddleware: (serverId, method, url, registrationOptions) => {
if(!method || !url){
throw new Error("Provide the method and url for the server middleware to be created");
}
if(!url.startsWith(URL_SPLITTER)){
throw new Error("The provided url must start with a forward slash (/) - culprit: " + url);
}
const serverObject = runningServers.get(serverId);
if(!serverObject){
throw new Error(`Server with id ${serverId} not found. Middleware cannot be created`);
}
//Do registration
doRegistration(serverId, method, url, registrationOptions);
const server = serverObject.server;
/**
*
* @param {import("express").Request} req
* @param {import("express").Response} res
* @param {import("express").NextFunction} next
*/
const middleware = (req, res, next) => {
/**
* @type {import("http-proxy").ServerOptions}
*/
let middlewareOptions;
if(serverObject.Handlers){
//OLD WAY WITHOUT OVERRIDE. Preferred
const requestHandler = serverObject.Handlers.get(getHandlerContext(req.method, req.originalUrl, serverId));
// const overrideTarget = registrationOptions && registrationOptions.request && registrationOptions.request.options ? registrationOptions.request.options.target : null;
// const requestHandler = serverObject.Handlers.get(getHandlerContext(req.method, overrideTarget || req.originalUrl, serverId));
middlewareOptions = requestHandler && requestHandler.request ? requestHandler.request.options : null;
}
middlewareOptions = {
...middlewareOptions,
target: res.locals[HttpProxyServer._DYNAMIC_TARGET_OVERRIDE] ? `${serverObject.target}${res.locals[HttpProxyServer._DYNAMIC_TARGET_OVERRIDE]}` :
middlewareOptions && middlewareOptions.target ? `${serverObject.target}${middlewareOptions.target}` : `${serverObject.target}${req.baseUrl}`
};
console.log("Making proxy request with target"); //Show this target also includes base url ? No, override it
console.log(middlewareOptions.target);
server.web(req, res, middlewareOptions, (e) => {
console.log("Failed to proxy with error:\n");
console.log(e);
res.sendStatus(500);
});
}
return middleware;
},
responseHelpers: {
/**
* @param {number} statusCode
*/
isStatusOK: (statusCode) => {
const statusOk = statusCode === 200 || statusCode === 304
|| statusCode === 201 || statusCode === 202;
if(!statusOk){
console.log("Status not OK with code: " + statusCode);
}
return statusOk;
},
/**
*
* @param {number} statusCode
* @returns
*/
shouldUseCache: (statusCode) => {
return statusCode === 304;
}
}
}
/**
* Register request handler, request options, response handler, redirect handler
* for a given path and request method
*
* Can only be called once for any given path and request method
*
* @param {number} serverId
* @param {import("managed-http-proxy").HandlerMethods} method
* @param {string} url Ensure this is the same url used to make the request to the actual server, in case you are mutating the paths on call using target. Thus should have the same value to the path in target (without host)
* @param {import("managed-http-proxy").ProxyServerRegistrationOptions} registrationOptions
*/
function doRegistration(serverId, method, url, registrationOptions){
//Remove last / if put
url = url.charAt(url.length - 1) === URL_SPLITTER ? url.slice(0, url.length - 2) : url;
//Set the context. Refuse if already have it set
const context = setHandlerContext(method, url, serverId);
//Ensure the options are okay
registrationOptions = checkAndStandardizeOptions(registrationOptions);
//register the request handler
registerRequestHandlerAndOptions(serverId, context, registrationOptions.request);
//register the response handler
registerResponseHandler(serverId, context, registrationOptions.response);
console.log(`Completed registration for server ID ${serverId} with context ${context}`);
}
/**
*
* @param {import("managed-http-proxy").ProxyServerRegistrationOptions} registrationOptions
* @returns {import("managed-http-proxy").ProxyServerRegistrationOptions} Registration options with standardized options
*/
function checkAndStandardizeOptions(registrationOptions){
//Allow to pass null or undefined
if(!registrationOptions){
registrationOptions = getDefaultRegistrationOptions();
}
//Get original values
let _responseHandlers = registrationOptions.response;
let _requestOptions = registrationOptions.request ? registrationOptions.request.options : null;
//Populate relevant object properties and standardize options
const standardizedOptions = populateAndstandardizeOptions(registrationOptions);
if(_requestOptions || _responseHandlers) { //Do checks if any of the options had been provided
//Update to standardized options
_requestOptions = standardizedOptions.request.options;
_responseHandlers = standardizedOptions.response;
//Check validity of passed options
if(_requestOptions.selfHandleResponse && !_responseHandlers.responseHandler){
throw new Error(`selfHandleResponse is true. Please provide a response handler`);
}
if(!_requestOptions.followRedirects && !_responseHandlers.redirectHandler){
throw new Error(`followRedirects is false. Please provide a redirect handler`);
}
if(!_requestOptions.selfHandleResponse && _responseHandlers.responseHandler){
throw new Error(`selfHandleResponse is false but a response handler has been provided. It will not be called`);
}
}
return registrationOptions;
}
/**
* Standardized options and fill in missing values
* @param {import("managed-http-proxy").ProxyServerRegistrationOptions} registrationOptions
* @returns {import("managed-http-proxy").ProxyServerRegistrationOptions} Standardized options
*/
function populateAndstandardizeOptions(registrationOptions){
const responseHandlers = registrationOptions.response;
let requestOptions = registrationOptions.request ? registrationOptions.request.options : null;
//If no response handler entries
if(!responseHandlers){
registrationOptions.response = {
responseHandler: null,
redirectHandler: null
}
}
if(!requestOptions){ //If no request option entries
requestOptions = getDefaultMiddlewareOptions();
//Update the new options to the object
registrationOptions.request = {
...registrationOptions.request,
options: requestOptions
};
} else if(requestOptions.followRedirects === null || requestOptions.followRedirects === undefined){
//Add the default followRedirects value if not set originally
registrationOptions.request.options = {
followRedirects: true,
...requestOptions
}
}
return registrationOptions;
}
/**
* Set the request handler and options for a given path and request method
*
* Only call if you wish to uniquely handle requests
*
* @param {number} serverId
* @param {string} context
* @param {import("managed-http-proxy").ProxyServerRequestHandler} requestHandler
*/
function registerRequestHandlerAndOptions(serverId, context, requestHandler){
runningServers.get(serverId).Handlers.set(context, {
request: requestHandler
});
}
/**
* Set the response handler and options for a given path and request method
*
* Only call if you wish to uniquely handle responses
*
* @param {number} serverId
* @param {string} context
* @param {import("managed-http-proxy").ProxyServerResponseHandlers} handlers
*/
function registerResponseHandler(serverId, context, handlers){
const currentHandler = runningServers.get(serverId).Handlers.get(context);
runningServers.get(serverId).Handlers.set(context, {
request: currentHandler.request,
response: handlers
});
}
/**
* Code inspired by: https://github.com/chimurai/http-proxy-middleware/blob/d7623983e18f0daa724a3fcc0b5d4d1812e4c3c1/src/handlers/response-interceptor.ts#L18
* (export function responseInterceptor)
*
* Listener for proxy response event
* @param {number} serverId
* @param {IncomingMessage} proxyRes
* @param {IncomingMessage} req
* @param {ServerResponse} res
*/
async function onProxyResponse(serverId, proxyRes, req, res){
//Only allow this if set to selfHandleRes. Apparrently, http-proxy just fires this whether the flag is true or not. Difference, res.end() has been called
const reqMethod = res.req.method;
//Old. Not supporting override targets
//@ts-expect-error
const reqBaseUrl = res.req.originalUrl;
//@ts-expect-err property req doesn't exist (Overriding using target)
// const reqBaseUrl = proxyRes.req.path; //Supports retargeting
const resStatusCode = proxyRes.statusCode;
const handlerContext = getHandlerContext(reqMethod, reqBaseUrl, serverId);
const handlers = runningServers.get(serverId).Handlers.get(handlerContext);
//If redirecting, follow if allowed to
//@ts-expect-error Type req doesn't exist on type IncomingMessage
if(handlers && proxyRes.req._redirectable && proxyRes.req._redirectable._isRedirect){
console.log("Redirecting");
//@ts-expect-error Type req doesn't exist on type IncomingMessage
const redirectUrl = proxyRes.req._redirectable._currentUrl;
if(handlers.request.options.followRedirects){
//Redirect to correct url
//@ts-expect-error Type redirect doesn't exist on type ServerResponse<IncomingMessage>
res.redirect(redirectUrl);
} else {
handlers.response.redirectHandler(res, redirectUrl);
}
} else if(handlers && handlers.request.options.selfHandleResponse){
const originalProxyRes = proxyRes; //Use by chimurai's code in interceptor. Personally, I see no need
let buffer = Buffer.from("", "utf-8");
//decompress the response from the proxy
const _proxyRes = decompress(proxyRes, proxyRes.headers['content-encoding']);
//Concat the data stream
_proxyRes.on("data", (chunk) => {
buffer = Buffer.concat([buffer, chunk]);
});
_proxyRes.on("end", async () => {
//Copy original headers to res
copyHeaders(proxyRes, res);
//Call our handler, if one is there
//Call interceptor with intercepted response
//https://github.com/chimurai/http-proxy-middleware/blob/d7623983e18f0daa724a3fcc0b5d4d1812e4c3c1/src/handlers/response-interceptor.ts#L37
//If handler available for url, let it handle the response. Else, handle locally
console.log(`Handler to be triggered for server ID ${serverId} and context ${handlerContext}`);
let interceptedBuffer = Buffer.from(buffer);
/**
* @type {import("managed-http-proxy").ResponseHandlerResult}
*/
let resHandlerResult;
if(runningServers.get(serverId).Handlers){
const handlerObj = runningServers.get(serverId).Handlers.get(handlerContext);
if(handlerObj && handlerObj.response){
const responseHandler = handlerObj.response.responseHandler;
if(responseHandler){
//Call handlers if not a 304;
if(!HttpProxyServer.responseHelpers.shouldUseCache(resStatusCode)){
console.log("Handler for response fired");
//@ts-expect-error Some weird type mismatch. Doesn't affect code execution.
resHandlerResult = await responseHandler(buffer, res, resStatusCode, ResponseGenerator);
} else {
console.log("Unmodified. Handler not triggered");
resHandlerResult = ResponseGenerator.respondUnmodified();
}
interceptedBuffer = Buffer.from(resHandlerResult.interceptedResponse);
}
}
}
//Set the correct content-length (with double byte character support)
res.setHeader("content-length", Buffer.byteLength(interceptedBuffer, 'utf-8'));
//Set the status codes and status messages. These implictly call writeHead() after which no more heads can be written
if(resHandlerResult.status){
res.statusCode = resHandlerResult.status.code;
res.statusMessage = resHandlerResult.status.msg;
} else {
//Put original status code and status messages
res.statusCode = originalProxyRes.statusCode;
res.statusMessage = originalProxyRes.statusMessage;
}
//Write the buffer to the response
res.write(interceptedBuffer);
res.end();
});
}
}
/**
* Streaming decompression of proxy response
* source: https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L116
* via: https://github.com/chimurai/http-proxy-middleware/blob/d7623983e18f0daa724a3fcc0b5d4d1812e4c3c1/src/handlers/response-interceptor.ts#L57
*
* @param {IncomingMessage} proxyRes
* @param {string} contentEncoding
*/
function decompress(proxyRes, contentEncoding){
let _proxyRes = proxyRes;
let decompress;
switch(contentEncoding){
case 'gzip':
decompress = zlib.createGunzip();
break;
case 'br':
decompress = zlib.createBrotliDecompress();
break;
case 'deflate':
decompress = zlib.createInflate();
break;
default:
break;
}
if(decompress){
_proxyRes.pipe(decompress);
//@ts-expect-error
_proxyRes = decompress;
}
return _proxyRes;
}
/**
* Copy original headers
* https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L78
* @param {IncomingMessage} originalResponse
* @param {ServerResponse} response
*/
function copyHeaders(originalResponse, response){
if(response.setHeader) {
let keys = Object.keys(originalResponse.headers);
//ignore chunked, brotli, gzip, deflate headers
keys = keys.filter((key) => !['content-encoding', 'transfer-encoding'].includes(key));
keys.forEach((key) => {
let value = originalResponse.headers[key];
if(key === 'set-cookie'){
//remove the cookie domain (Will set for client as default)
//TODO improve this and work based on options
value = Array.isArray(value) ? value : [value];
value = value.map((x) => x.replace(/Domain=[^;]+?/i, ''));
}
response.setHeader(key, value);
});
} else {
//@ts-expect-error
response.headers = originalResponse.headers;
}
}
/**
* Set the context for a handler
* Rejects if context already set with handlers
* @param {import("managed-http-proxy").HandlerMethods} method
* @param {string} url
* @param {number} serverId
* @returns {string}
*/
function setHandlerContext(method, url, serverId){
const isDynamicUrl = handlerUrlDynamic(url);
if(isDynamicUrl){
if(!runningServers.get(serverId).dynamicHandlerUrls.includes(url)){
console.log("Registering dynamic url: " + url);
runningServers.get(serverId).dynamicHandlerUrls.push(url);
} else {
throw new Error("Dynamic handler url already set: " + url);
}
}
const context = getSimpleHandlerContext(method, url);
//If we have handlers set for this context and serverID already, reject
if(runningServers.get(serverId).Handlers.get(context)){
if(isDynamicUrl){
//Remove from dynamic urls array
runningServers.get(serverId).dynamicHandlerUrls.splice(runningServers.get(serverId).dynamicHandlerUrls.findIndex((dynamicUrl) => dynamicUrl === url), 1);
}
throw new Error(`Registration already done for context ${context} with server ID ${serverId}`);
}
return context;
}
/**
* Know if a handler url is dynamic or not
* @param {string} url
*/
function handlerUrlDynamic(url){
return url.includes(URL_PARAM_FULL_MARKER) || url.endsWith(URL_MATCH_ALL_FULL_MARKER); //|| url.includes(URL_QUERY_MARKER)
}
/**
* CHECK FIRST IF URL MATCHES DYNAMIC and rewire url to dynamic one
* Get the context for the correct handlers and options for a route
* @param {import("managed-http-proxy").HandlerMethods | string} method
* @param {string} url
* @param {number} serverId
* @returns {string}
*/
function getHandlerContext(method, url, serverId){
return getSimpleHandlerContext(method, mapContextUrl(method, url, serverId));
}
/**
* map a passed url properly. If dynamic, map to dynamic. If not dynamic, return as is
* @param {import("managed-http-proxy").HandlerMethods | string} method
* @param {string} url
* @param {number} serverId
* @returns {string} The mapped url
*/
function mapContextUrl(method, url, serverId){
//Remove all queries from url
const _url = removeQueriesFromUrl(url);
//If simple context hits to a handler obj, no mapping to continue. Else, map
//Helps to also avoid hitting dynamic that is not dynamic i.e predetermined patterns almost matching
//dynamic in regex. For instance /user/:id and /user/register
if(runningServers.get(serverId).Handlers.get(getSimpleHandlerContext(method, _url))){
return _url;
} else {
let mappedURL;
//Check if matches any dynamic then map appropriately
const dynamicURLSList = runningServers.get(serverId).dynamicHandlerUrls;
let dynamicURL, _dynamicURL, tokenizedDynamicUrl, tokenizedUrl;
for(let i = 0; i < dynamicURLSList.length; i++){
dynamicURL = dynamicURLSList[i];
_dynamicURL = removePrecedingForwardSlash(dynamicURL); //TODO Should remove this slash from the urls BEFORE adding to list
if(dynamicURL.endsWith(URL_MATCH_ALL_FULL_MARKER)){
//Processing potential match all hit urlCongruentToMatchAll()
//Tokenize based on dynamic url without match all marker. Should have two tokens
if(urlCongruentToMatchAll(_dynamicURL, removePrecedingForwardSlash(_url))){
mappedURL = dynamicURL;
break;
}
} else {
tokenizedDynamicUrl = _dynamicURL.split(URL_SPLITTER);
tokenizedUrl = removePrecedingForwardSlash(_url).split(URL_SPLITTER);
//Process if length same for parameterized or
if(tokenizedDynamicUrl.length === tokenizedUrl.length){
//Make sure the url is congruent to dynamic url using RegEx and return
if(urlCongruentToDynamic(_dynamicURL, _url)){
//Pass
mappedURL = dynamicURL;
break;
}
}
}
}
if(!mappedURL){
console.warn(`\n\nUrl ${url} should be dynamic, but failed to map. Returning base url\n\n`);
}
// console.log("END OF LOOP: Mapped URL for context is " + (mappedURL ? mappedURL : _url) + " from " + _url);
return mappedURL ? mappedURL : _url;
}
}
/**
*
* @param {string} url
*/
function removeQueriesFromUrl(url){
//tokenize
let tokenizedUrl = url.split(URL_QUERY_MARKER);
//URL now of index 0 from split
url = tokenizedUrl[0];
//Remove last /
url = url.charAt(url.length - 1) === URL_SPLITTER ? url.slice(0, url.length - 1) : url;
return url;
}
/**
* Tries to see whether a dynamic match all url is congruent to a given full url
*
* URLs have preceding slash removed
* @param {string} _dynamicURL dynamic url
* @param {string} _url url to be matched
* @returns {boolean} whether the url is congruent to match all
*/
function urlCongruentToMatchAll(_dynamicURL, _url){
//The given url should start with the raw url of the match all dynamic url i.e url without the /* (URL_MATCH_ALL) special marker
const _matchAllDynamicRaw = _dynamicURL.replace(URL_MATCH_ALL_FULL_MARKER, "");
if(_url.startsWith(_matchAllDynamicRaw)){
//Match found
return true;
}
return false;
}
/**
* By default, adds a preceding / to the given url, if missing, for final RegEx check
* Easier to process
*
* @param {string} _dynamicUrl
* @param {string} url
* @returns {boolean} whether the url is congruent to the dynamic url
*/
function urlCongruentToDynamic(_dynamicUrl, url){
//Split the dynamic url based on the URL_SPLITTER
const tokenizedDynamic = _dynamicUrl.split(URL_SPLITTER);
let processedDynamic = "";
//Get the indices of params markers. Thinking of query markers later?
let paramIndices = [];
for(let i = 0; i < tokenizedDynamic.length; i++){
if(tokenizedDynamic[i].charAt(0) === URL_PARAM_COLON_MARKER){
paramIndices.push(i);
}
}
//Join tokens before this indices with the URL SPLITTER, then escape, then add appropriate regex marker for dynamic points
for(let i = 0; i <= paramIndices.length; i++){
let startIndex = i === 0 ? 0 : paramIndices[i - 1] + 1;
let endIndex = i < paramIndices.length ? paramIndices[i] : tokenizedDynamic.length;
//Only do processing if not out of bounds
if(startIndex < tokenizedDynamic.length){
//creating a url without the dynamics (that's ahead of it). Then, where dynamic should be, replacing with match all
let reformedUrl = `/${tokenizedDynamic.slice(startIndex, endIndex).join(URL_SPLITTER)}`;
//Ensure not "/"". Caused by splitting at times
if(i < paramIndices.length){
//Current param infront of processed url. Add marker at end with backslash
//https://stackoverflow.com/questions/2912894/how-to-match-any-character-in-regular-expression - the .* reference
processedDynamic+= escapeForRegExp(`${reformedUrl !== "/" ? `${reformedUrl}/` : "/"}`) + ".*"; //New .* will better match all characters except new line //"[\s\S]*"
} else {
//All params passed. Only escape reformed url. No / escape and character group
processedDynamic+= escapeForRegExp(`${reformedUrl}`);
}
}
}
//Post process url
if(url.charAt(0) !== URL_SPLITTER){
url = `/${url}`;
}
//convert to regex and check. Return answer
return new RegExp(processedDynamic, "i").test(url);
}
/**
* @param {string} url
*/
function removePrecedingForwardSlash(url){
return url.charAt(0) === "/" && url.charAt(1) !== URL_MATCH_ALL_SPECIAL_MARKER ? url.substring(1, url.length) : url;
}
/**
*
* @param {string} string
* @returns {string} Escaped string to allow creation of a regular expression from the string
*
* https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
*/
function escapeForRegExp(string){
return string.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&")
}
/**
*
* @param {import("managed-http-proxy").HandlerMethods | string} method
* @param {string} url
* @returns {string}
*/
function getSimpleHandlerContext(method, url){
return `${method} | ${url}`;
}
/**
* @returns {import("managed-http-proxy").ProxyServerRegistrationOptions}
*/
function getDefaultRegistrationOptions(){
return {
request: null,
response: null
}
}
/**
* Read more at: https://www.npmjs.com/package/http-proxy#options
* @returns {import("http-proxy").ServerOptions}
*/
function getDefaultMiddlewareOptions(){
return {
//@ts-expect-error boolean is allowed
hostRewrite: false, //rewrites the location hostname on (201/301/302/307/308) redirects
autoRewrite: false, //rewrites the location host/port on (201/301/302/307/308) redirects based on requested host/port. Default: false.
protocolRewrite: null, //rewrites the location protocol on (201/301/302/307/308) redirects to 'http' or 'https'. Default: null.
cookieDomainRewrite: false, //rewrites domain of set-cookie headers (IMPLEMENT THIS OPTION CHECK IN CODE)
cookiePathRewrite: false, //rewrites path of set-cookie headers.
followRedirects: true, //specify whether you want to follow redirects (by default. If false, handle explicitly in res handler)
}
}
const ResponseGenerator = {
/**
* Will return the rendered html as a string or an empty string with a fail status code (500)
*
* @param {import("express").Response} res
* @param {string} view
* @param {*} options
* @returns {Promise<import("managed-http-proxy").ResponseHandlerResult>} response as rendered HTML
*/
renderView: async (res, view, options) => {
return new Promise((resolve) => {
let renderedHTML;
let statusCode;
let statusMsg;
res.render(view, { layout: view, ...options }, (err, html) => {
renderedHTML = err ? '' : html;
statusCode = err ? 500 : 200;
statusMsg = err ? "Internal server error" : "OK";
if(err){
console.log(err);
};
//Set the appropriate headers
res.setHeader('content-type', "text/html");
resolve({
interceptedResponse: renderedHTML,
status: {
code: statusCode,
msg: statusMsg
}
});
});
});
},
/**
*
* @param {number} code
* @param {string} msg
* @returns {import("managed-http-proxy").ResponseHandlerResult} string empty. Just passed to buffer
*/
errorCode: (code, msg) => {
return {
interceptedResponse: '',
status: {
code: code,
msg: msg
}
};
},
/**
* @returns {import("managed-http-proxy").ResponseHandlerResult} a 304 code to the browser
*/
respondUnmodified: () => {
return {
interceptedResponse: '',
status: {
code: 304,
msg: ''
}
};
},
parseBuffer: {
/**
*
* @param {Buffer} buffer
*/
json: (buffer) => {
return JSON.parse(buffer.toString())
}
},
}
module.exports = HttpProxyServer;