|
|
import { Request, Response } from "express"; |
|
|
import http from "http"; |
|
|
import ProxyServer from "http-proxy"; |
|
|
import { Readable } from "stream"; |
|
|
import { |
|
|
createProxyMiddleware, |
|
|
Options, |
|
|
debugProxyErrorsPlugin, |
|
|
proxyEventsPlugin, |
|
|
} from "http-proxy-middleware"; |
|
|
import { ProxyReqMutator, stripHeaders } from "./index"; |
|
|
import { createOnProxyResHandler, ProxyResHandlerWithBody } from "../response"; |
|
|
import { createQueueMiddleware } from "../../queue"; |
|
|
import { getHttpAgents } from "../../../shared/network"; |
|
|
import { classifyErrorAndSend } from "../common"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type ProxyMiddlewareFactoryOptions = { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mutations?: ProxyReqMutator[]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
target: string | Options<Request>["router"]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
blockingResponseHandler?: ProxyResHandlerWithBody; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function createQueuedProxyMiddleware({ |
|
|
target, |
|
|
mutations, |
|
|
blockingResponseHandler, |
|
|
}: ProxyMiddlewareFactoryOptions) { |
|
|
const hpmTarget = typeof target === "string" ? target : "https://setbyrouter"; |
|
|
const hpmRouter = typeof target === "function" ? target : undefined; |
|
|
|
|
|
const [httpAgent, httpsAgent] = getHttpAgents(); |
|
|
const agent = hpmTarget.startsWith("http:") ? httpAgent : httpsAgent; |
|
|
|
|
|
const proxyMiddleware = createProxyMiddleware<Request, Response>({ |
|
|
target: hpmTarget, |
|
|
router: hpmRouter, |
|
|
agent, |
|
|
changeOrigin: true, |
|
|
toProxy: true, |
|
|
selfHandleResponse: typeof blockingResponseHandler === "function", |
|
|
|
|
|
|
|
|
|
|
|
ejectPlugins: true, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
plugins: [ |
|
|
debugProxyErrorsPlugin, |
|
|
pinoLoggerPlugin, |
|
|
proxyEventsPlugin, |
|
|
] as any, |
|
|
on: { |
|
|
proxyRes: createOnProxyResHandler( |
|
|
blockingResponseHandler ? [blockingResponseHandler] : [] |
|
|
), |
|
|
error: classifyErrorAndSend, |
|
|
}, |
|
|
buffer: ((req: Request) => { |
|
|
|
|
|
|
|
|
let payload = req.body; |
|
|
if (typeof payload === "string") { |
|
|
payload = Buffer.from(payload); |
|
|
} |
|
|
const stream = new Readable(); |
|
|
stream.push(payload); |
|
|
stream.push(null); |
|
|
return stream; |
|
|
}) as any, |
|
|
}); |
|
|
|
|
|
return createQueueMiddleware({ |
|
|
mutations: [stripHeaders, ...(mutations ?? [])], |
|
|
proxyMiddleware, |
|
|
}); |
|
|
} |
|
|
|
|
|
type ProxiedResponse = http.IncomingMessage & Response & any; |
|
|
function pinoLoggerPlugin(proxyServer: ProxyServer<Request>) { |
|
|
proxyServer.on("error", (err, req, res, target) => { |
|
|
req.log.error( |
|
|
{ originalUrl: req.originalUrl, targetUrl: String(target), err }, |
|
|
"Error occurred while proxying request to target" |
|
|
); |
|
|
}); |
|
|
proxyServer.on("proxyReq", (proxyReq, req) => { |
|
|
const { protocol, host, path } = proxyReq; |
|
|
req.log.info( |
|
|
{ |
|
|
from: req.originalUrl, |
|
|
to: `${protocol}//${host}${path}`, |
|
|
}, |
|
|
"Sending request to upstream API..." |
|
|
); |
|
|
}); |
|
|
proxyServer.on("proxyRes", (proxyRes: ProxiedResponse, req, _res) => { |
|
|
const { protocol, host, path } = proxyRes.req; |
|
|
req.log.info( |
|
|
{ |
|
|
target: `${protocol}//${host}${path}`, |
|
|
status: proxyRes.statusCode, |
|
|
contentType: proxyRes.headers["content-type"], |
|
|
contentEncoding: proxyRes.headers["content-encoding"], |
|
|
contentLength: proxyRes.headers["content-length"], |
|
|
transferEncoding: proxyRes.headers["transfer-encoding"], |
|
|
}, |
|
|
"Got response from upstream API." |
|
|
); |
|
|
}); |
|
|
} |
|
|
|