71 lines
2.3 KiB
TypeScript
71 lines
2.3 KiB
TypeScript
import { sendProxyError } from "../common";
|
|
import type { RawResponseBodyHandler } from "./index";
|
|
import { decompressBuffer } from "./compression";
|
|
|
|
/**
|
|
* Handles the response from the upstream service and decodes the body if
|
|
* necessary. If the response is JSON, it will be parsed and returned as an
|
|
* object. Otherwise, it will be returned as a string. Does not handle streaming
|
|
* responses.
|
|
* @throws {Error} Unsupported content-encoding or invalid application/json body
|
|
*/
|
|
export const handleBlockingResponse: RawResponseBodyHandler = async (
|
|
proxyRes,
|
|
req,
|
|
res
|
|
) => {
|
|
if (req.isStreaming) {
|
|
const err = new Error(
|
|
"handleBlockingResponse called for a streaming request."
|
|
);
|
|
req.log.error({ stack: err.stack, api: req.inboundApi }, err.message);
|
|
throw err;
|
|
}
|
|
|
|
return new Promise((resolve, reject) => {
|
|
let chunks: Buffer[] = [];
|
|
proxyRes.on("data", (chunk) => chunks.push(chunk));
|
|
proxyRes.on("end", async () => {
|
|
const contentEncoding = proxyRes.headers["content-encoding"];
|
|
const contentType = proxyRes.headers["content-type"];
|
|
let body: string | Buffer = Buffer.concat(chunks);
|
|
const rejectWithMessage = function (msg: string, err: Error) {
|
|
const error = `${msg} (${err.message})`;
|
|
req.log.warn(
|
|
{ msg: error, stack: err.stack },
|
|
"Error in blocking response handler"
|
|
);
|
|
sendProxyError(req, res, 500, "Internal Server Error", { error });
|
|
return reject(error);
|
|
};
|
|
|
|
try {
|
|
body = await decompressBuffer(body, contentEncoding);
|
|
} catch (e) {
|
|
return rejectWithMessage(`Could not decode response body`, e);
|
|
}
|
|
|
|
try {
|
|
return resolve(tryParseAsJson(body, contentType));
|
|
} catch (e) {
|
|
return rejectWithMessage("API responded with invalid JSON", e);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
function tryParseAsJson(body: string, contentType?: string) {
|
|
// If the response is declared as JSON, it must parse or we will throw
|
|
if (contentType?.includes("application/json")) {
|
|
return JSON.parse(body);
|
|
}
|
|
// If it's not declared as JSON, some APIs we'll try to parse it as JSON
|
|
// anyway since some APIs return the wrong content-type header in some cases.
|
|
// If it fails to parse, we'll just return the raw body without throwing.
|
|
try {
|
|
return JSON.parse(body);
|
|
} catch (e) {
|
|
return body;
|
|
}
|
|
}
|