85 lines
2.2 KiB
TypeScript
85 lines
2.2 KiB
TypeScript
import { Base64Encode } from "base64-stream";
|
|
import iconv from "iconv-lite";
|
|
import CreateLogCtx from "lib/logger/logger";
|
|
import getRawBody from "raw-body";
|
|
import { TransformResponse } from "utils/transform-response";
|
|
import { Transform } from "stream";
|
|
import { promisify } from "util";
|
|
import zlib, { inflate } from "zlib";
|
|
import type { RequestHandler } from "express";
|
|
|
|
const logger = CreateLogCtx(__filename);
|
|
const inflateAsync = promisify(inflate);
|
|
|
|
export const DFIRequestResponse: (mustUseDfi: boolean) => RequestHandler = (mustUseDfi) => {
|
|
return async (req, res, next) => {
|
|
if (Number(req.headers["content-length"] ?? 0) === 0) {
|
|
next();
|
|
return;
|
|
}
|
|
|
|
const isUsingDfi = req.headers.pragma?.toUpperCase() === "DFI";
|
|
|
|
if (mustUseDfi && !isUsingDfi) {
|
|
logger.error("Received request that was not DFI-encoded.", {
|
|
pragma: req.headers.pragma ?? null,
|
|
});
|
|
return res.status(200).send("");
|
|
}
|
|
|
|
const rawBody = await getRawBody(req, { encoding: "utf-8" });
|
|
|
|
let body: string;
|
|
|
|
if (isUsingDfi) {
|
|
const compressedBuffer = Buffer.from(rawBody, "base64");
|
|
const buffer = await inflateAsync(compressedBuffer);
|
|
|
|
body = buffer.toString("utf-8").trim();
|
|
} else {
|
|
body = rawBody.trim();
|
|
}
|
|
|
|
// Keys and values are not URL escaped, but interpolated in (why)
|
|
// This should be fine. I think.
|
|
// eslint-disable-next-line require-atomic-updates
|
|
req.body = Object.fromEntries(body.split("&").map((s) => s.split("=")));
|
|
req.safeBody = req.body as Record<string, unknown>;
|
|
|
|
const transformers: Array<NodeJS.ReadWriteStream> = [
|
|
iconv.decodeStream("utf-8"),
|
|
iconv.encodeStream(req.body.encode ?? "EUC-JP"),
|
|
];
|
|
|
|
if (req.headers.pragma?.toUpperCase() === "DFI") {
|
|
transformers.push(
|
|
zlib.createDeflate({ flush: zlib.constants.Z_SYNC_FLUSH }),
|
|
new Base64Encode()
|
|
);
|
|
}
|
|
|
|
transformers.push(
|
|
new Transform({
|
|
transform(chunk, _encoding, callback) {
|
|
callback(null, chunk);
|
|
},
|
|
final(callback) {
|
|
this.push(Buffer.from("\r\n", "utf-8"));
|
|
callback(null);
|
|
},
|
|
})
|
|
);
|
|
TransformResponse(res, ...transformers);
|
|
|
|
const _send = res.send;
|
|
|
|
res.send = (params) => {
|
|
const body = `${new URLSearchParams(params).toString()}`;
|
|
|
|
return _send.call(res, body);
|
|
};
|
|
|
|
next();
|
|
};
|
|
};
|