From 05f3cbfbc9b8d801252e19ad4d033bce90404ecd Mon Sep 17 00:00:00 2001 From: nitish-egov <137176807+nitish-egov@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:03:04 +0530 Subject: [PATCH 1/3] added gzip handler in middleware --- .../project-factory/src/server/app.ts | 17 ++++++-- .../src/server/utils/gzipHandler.ts | 41 +++++++++++++++++++ .../utils/middlewares/requestMiddleware.ts | 8 ++-- 3 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 health-services/project-factory/src/server/utils/gzipHandler.ts diff --git a/health-services/project-factory/src/server/app.ts b/health-services/project-factory/src/server/app.ts index f662b76d104..c1ca2179a3b 100644 --- a/health-services/project-factory/src/server/app.ts +++ b/health-services/project-factory/src/server/app.ts @@ -11,6 +11,7 @@ import { tracingMiddleware } from "./tracing"; import { createProxyMiddleware } from "http-proxy-middleware"; import * as v8 from "v8"; import { logger } from "./utils/logger"; +import { handleGzipRequest } from "./utils/gzipHandler"; const printMemoryInMB = (memoryInBytes: number) => { const memoryInMB = memoryInBytes / (1024 * 1024); // Convert bytes to MB @@ -41,16 +42,24 @@ class App { } private initializeMiddlewares() { - this.app.use( - bodyParser.json({ limit: config.app.incomingRequestPayloadLimit }) - ); + this.app.use((req, res, next) => { + const contentType = req.headers["content-type"]; + + if (contentType === "application/gzip") { + return handleGzipRequest(req, res, next); + } + + return bodyParser.json({ limit: config.app.incomingRequestPayloadLimit })(req, res, next); + }); + + this.app.use( bodyParser.urlencoded({ limit: config.app.incomingRequestPayloadLimit, extended: true, }) ); - this.app.use(bodyParser.json()); + // this.app.use(bodyParser.json()); this.app.use(tracingMiddleware); this.app.use(requestMiddleware); this.app.use(errorLogger); diff --git a/health-services/project-factory/src/server/utils/gzipHandler.ts b/health-services/project-factory/src/server/utils/gzipHandler.ts new file mode 100644 index 00000000000..5b40ce6a0bb --- /dev/null +++ b/health-services/project-factory/src/server/utils/gzipHandler.ts @@ -0,0 +1,41 @@ +import zlib from "zlib"; +import { Request, Response, NextFunction } from "express"; +import { logger } from "./logger"; + +export const handleGzipRequest = ( + req: Request, + res: Response, + next: NextFunction +) => { + const buffers: Buffer[] = []; + + req.on("data", (chunk) => buffers.push(chunk)); + req.on("end", () => { + try { + const gzipBuffer = Buffer.concat(buffers); + + // Decompress the Gzip data + zlib.gunzip(gzipBuffer, (err, decompressedBuffer) => { + if (err) { + logger.error("Error decompressing Gzip file:", err); + res.status(500).send("Invalid Gzip file"); + return; + } + + try { + // Convert the decompressed buffer to string and parse as JSON + const jsonData = decompressedBuffer.toString(); + const parsedData = JSON.parse(jsonData); + req.body = parsedData; // Attach parsed data to the request body + next(); // Proceed to next middleware + } catch (parseError) { + logger.error("Error parsing JSON data:", parseError); + res.status(500).send("Invalid JSON in Gzip content"); + } + }); + } catch (err) { + logger.error("Error processing Gzip content:", err); + res.status(500).send("Invalid Gzip content"); + } + }); +}; \ No newline at end of file diff --git a/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts b/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts index a8bfd34a330..5c5c1eb32be 100644 --- a/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts +++ b/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts @@ -18,9 +18,9 @@ const requestMiddleware = (req: Request, res: Response, next: NextFunction) => { logger.info(`RECEIVED A HTTP REQUEST :: URI :: ${req.url}`); // Check if the content type is 'application/json' const contentType = req.headers['content-type']; - if (!contentType || !contentType.split(';').map(part => part.trim()).includes('application/json')) { - // If content type is not 'application/json', throw Unsupported Media Type error - let e: any = new Error("Unsupported Media Type: Content-Type should be 'application/json'"); + if (!contentType || !contentType.split(';').map(part => part.trim()).includes('application/json') && !contentType.split(';').map(part => part.trim()).includes('application/gzip')) { + // If content type is not 'application/json' or 'application/gzip', throw Unsupported Media Type error + let e: any = new Error("Unsupported Media Type: Content-Type should be 'application/json' or 'application/gzip'"); e = Object.assign(e, { status: 415, code: "UNSUPPORTED_MEDIA_TYPE" }); errorResponder(e, req, res, 415) return; @@ -43,4 +43,4 @@ const requestMiddleware = (req: Request, res: Response, next: NextFunction) => { } }; -export default requestMiddleware; // Exporting the requestMiddleware function for use in Express middleware chain +export default requestMiddleware; // Exporting the requestMiddleware function for use in Express middleware chain \ No newline at end of file From 091aab36031c2ed063be49cdc3db11d63836f2e8 Mon Sep 17 00:00:00 2001 From: nitish-egov <137176807+nitish-egov@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:53:38 +0530 Subject: [PATCH 2/3] added gzip handler specific to request middleware --- .../project-factory/src/server/app.ts | 15 ++-------- .../src/server/utils/gzipHandler.ts | 28 +++++++++++++++++-- .../utils/middlewares/requestMiddleware.ts | 27 ++++++++++-------- 3 files changed, 44 insertions(+), 26 deletions(-) diff --git a/health-services/project-factory/src/server/app.ts b/health-services/project-factory/src/server/app.ts index c1ca2179a3b..002adf6afd9 100644 --- a/health-services/project-factory/src/server/app.ts +++ b/health-services/project-factory/src/server/app.ts @@ -11,7 +11,6 @@ import { tracingMiddleware } from "./tracing"; import { createProxyMiddleware } from "http-proxy-middleware"; import * as v8 from "v8"; import { logger } from "./utils/logger"; -import { handleGzipRequest } from "./utils/gzipHandler"; const printMemoryInMB = (memoryInBytes: number) => { const memoryInMB = memoryInBytes / (1024 * 1024); // Convert bytes to MB @@ -42,17 +41,9 @@ class App { } private initializeMiddlewares() { - this.app.use((req, res, next) => { - const contentType = req.headers["content-type"]; - - if (contentType === "application/gzip") { - return handleGzipRequest(req, res, next); - } - - return bodyParser.json({ limit: config.app.incomingRequestPayloadLimit })(req, res, next); - }); - - + this.app.use( + bodyParser.json({ limit: config.app.incomingRequestPayloadLimit }) + ); this.app.use( bodyParser.urlencoded({ limit: config.app.incomingRequestPayloadLimit, diff --git a/health-services/project-factory/src/server/utils/gzipHandler.ts b/health-services/project-factory/src/server/utils/gzipHandler.ts index 5b40ce6a0bb..b2f824631eb 100644 --- a/health-services/project-factory/src/server/utils/gzipHandler.ts +++ b/health-services/project-factory/src/server/utils/gzipHandler.ts @@ -1,6 +1,17 @@ import zlib from "zlib"; import { Request, Response, NextFunction } from "express"; +const { object, string } = require("yup"); // Importing object and string from yup for schema validation import { logger } from "./logger"; +import { errorResponder } from "./genericUtils"; + + +const requestSchema = object({ + apiId: string().nullable(), // Nullable string field for API ID + action: string().nullable(), // Nullable string field for action + msgId: string().required(), // Required string field for message ID + authToken: string().nullable(), // Nullable string field for authentication token + userInfo: object().nonNullable() // Non-nullable object field for user information +}); export const handleGzipRequest = ( req: Request, @@ -13,6 +24,7 @@ export const handleGzipRequest = ( req.on("end", () => { try { const gzipBuffer = Buffer.concat(buffers); + logger.info("Gzip buffer size:", gzipBuffer.length); // Decompress the Gzip data zlib.gunzip(gzipBuffer, (err, decompressedBuffer) => { @@ -24,10 +36,20 @@ export const handleGzipRequest = ( try { // Convert the decompressed buffer to string and parse as JSON - const jsonData = decompressedBuffer.toString(); + const jsonData = decompressedBuffer.toString().trim(); + logger.info("Decompressed JSON data:", jsonData); const parsedData = JSON.parse(jsonData); - req.body = parsedData; // Attach parsed data to the request body - next(); // Proceed to next middleware + req.body = parsedData; + // Validation 1: Check if tenantId is present + if (!req?.body?.RequestInfo?.userInfo?.tenantId) { + let e: any = new Error("RequestInfo.userInfo.tenantId is missing"); + e = Object.assign(e, { status: 400, code: "VALIDATION_ERROR" }); + return errorResponder(e, req, res, 400); // Return error response if tenantId is missing + } + + // Validation 2: Validate the request payload against the defined schema + requestSchema.validateSync(req.body.RequestInfo); // Assuming validateSync is synchronous + next(); // Proceed to next middleware or controller if valid } catch (parseError) { logger.error("Error parsing JSON data:", parseError); res.status(500).send("Invalid JSON in Gzip content"); diff --git a/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts b/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts index 5c5c1eb32be..425bfef2096 100644 --- a/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts +++ b/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts @@ -2,6 +2,7 @@ import { NextFunction, Request, Response } from "express"; // Importing necessar const { object, string } = require("yup"); // Importing object and string from yup for schema validation import { errorResponder } from "../genericUtils"; // Importing errorResponder function from genericUtils import { logger } from "../logger"; +import { handleGzipRequest } from "../gzipHandler"; // Defining the request schema using yup const requestSchema = object({ @@ -25,18 +26,22 @@ const requestMiddleware = (req: Request, res: Response, next: NextFunction) => { errorResponder(e, req, res, 415) return; } - // Check if tenantId is missing in RequestInfo.userInfo - if (!req?.body?.RequestInfo?.userInfo?.tenantId) { - // If tenantId is missing, throw Validation Error - let e: any = new Error("RequestInfo.userInfo.tenantId is missing"); - e = Object.assign(e, { status: 400, code: "VALIDATION_ERROR" }); - errorResponder(e, req, res, 400) - return; + if (contentType === 'application/gzip') { + return handleGzipRequest(req, res, next); + } else { + // Check if tenantId is missing in RequestInfo.userInfo + if (!req?.body?.RequestInfo?.userInfo?.tenantId) { + // If tenantId is missing, throw Validation Error + let e: any = new Error("RequestInfo.userInfo.tenantId is missing"); + e = Object.assign(e, { status: 400, code: "VALIDATION_ERROR" }); + errorResponder(e, req, res, 400) + return; + } + // Validate request payload against the defined schema + requestSchema.validateSync(req.body.RequestInfo); + // If validation succeeds, proceed to the next middleware + next(); } - // Validate request payload against the defined schema - requestSchema.validateSync(req.body.RequestInfo); - // If validation succeeds, proceed to the next middleware - next(); } catch (error) { // If an error occurs during validation process, handle the error using errorResponder function errorResponder(error, req, res); From 7ebd358f3e792608a497a67b52d8ae459a29280d Mon Sep 17 00:00:00 2001 From: nitish-egov <137176807+nitish-egov@users.noreply.github.com> Date: Thu, 19 Dec 2024 13:39:30 +0530 Subject: [PATCH 3/3] refactored gzip handler --- .../src/server/utils/gzipHandler.ts | 87 +++++++------------ .../utils/middlewares/requestMiddleware.ts | 32 +++---- 2 files changed, 46 insertions(+), 73 deletions(-) diff --git a/health-services/project-factory/src/server/utils/gzipHandler.ts b/health-services/project-factory/src/server/utils/gzipHandler.ts index b2f824631eb..1f06070b6e3 100644 --- a/health-services/project-factory/src/server/utils/gzipHandler.ts +++ b/health-services/project-factory/src/server/utils/gzipHandler.ts @@ -1,63 +1,36 @@ -import zlib from "zlib"; -import { Request, Response, NextFunction } from "express"; -const { object, string } = require("yup"); // Importing object and string from yup for schema validation -import { logger } from "./logger"; -import { errorResponder } from "./genericUtils"; +import { Request } from "express"; +import * as zlib from "zlib"; - -const requestSchema = object({ - apiId: string().nullable(), // Nullable string field for API ID - action: string().nullable(), // Nullable string field for action - msgId: string().required(), // Required string field for message ID - authToken: string().nullable(), // Nullable string field for authentication token - userInfo: object().nonNullable() // Non-nullable object field for user information -}); - -export const handleGzipRequest = ( - req: Request, - res: Response, - next: NextFunction -) => { +export const handleGzipRequest = async (req: Request): Promise => { const buffers: Buffer[] = []; - req.on("data", (chunk) => buffers.push(chunk)); - req.on("end", () => { - try { - const gzipBuffer = Buffer.concat(buffers); - logger.info("Gzip buffer size:", gzipBuffer.length); - - // Decompress the Gzip data - zlib.gunzip(gzipBuffer, (err, decompressedBuffer) => { - if (err) { - logger.error("Error decompressing Gzip file:", err); - res.status(500).send("Invalid Gzip file"); - return; - } + // Collect data chunks from the request + await new Promise((resolve, reject) => { + req.on("data", (chunk: any) => buffers.push(chunk)); + req.on("end", resolve); + req.on("error", reject); + }); - try { - // Convert the decompressed buffer to string and parse as JSON - const jsonData = decompressedBuffer.toString().trim(); - logger.info("Decompressed JSON data:", jsonData); - const parsedData = JSON.parse(jsonData); - req.body = parsedData; - // Validation 1: Check if tenantId is present - if (!req?.body?.RequestInfo?.userInfo?.tenantId) { - let e: any = new Error("RequestInfo.userInfo.tenantId is missing"); - e = Object.assign(e, { status: 400, code: "VALIDATION_ERROR" }); - return errorResponder(e, req, res, 400); // Return error response if tenantId is missing - } + // Concatenate and decompress the data + const gzipBuffer = Buffer.concat(buffers); + try { + const decompressedData = await decompressGzip(gzipBuffer); + req.body = decompressedData; // Assign the parsed data to req.body + } catch (err: any) { + throw new Error(`Failed to process Gzip data: ${err.message}`); + } +}; - // Validation 2: Validate the request payload against the defined schema - requestSchema.validateSync(req.body.RequestInfo); // Assuming validateSync is synchronous - next(); // Proceed to next middleware or controller if valid - } catch (parseError) { - logger.error("Error parsing JSON data:", parseError); - res.status(500).send("Invalid JSON in Gzip content"); - } - }); - } catch (err) { - logger.error("Error processing Gzip content:", err); - res.status(500).send("Invalid Gzip content"); - } +// Helper function to decompress Gzip data +const decompressGzip = (gzipBuffer: Buffer): Promise => { + return new Promise((resolve, reject) => { + zlib.gunzip(gzipBuffer, (err, result) => { + if (err) return reject(err); + try { + resolve(JSON.parse(result.toString())); + } catch (parseErr) { + reject(new Error("Invalid JSON format in decompressed data")); + } + }); }); -}; \ No newline at end of file +}; diff --git a/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts b/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts index 425bfef2096..f3f0f3e8db4 100644 --- a/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts +++ b/health-services/project-factory/src/server/utils/middlewares/requestMiddleware.ts @@ -14,7 +14,7 @@ const requestSchema = object({ }); // Middleware function to validate request payload -const requestMiddleware = (req: Request, res: Response, next: NextFunction) => { +const requestMiddleware = async (req: Request, res: Response, next: NextFunction) => { try { logger.info(`RECEIVED A HTTP REQUEST :: URI :: ${req.url}`); // Check if the content type is 'application/json' @@ -27,22 +27,22 @@ const requestMiddleware = (req: Request, res: Response, next: NextFunction) => { return; } if (contentType === 'application/gzip') { - return handleGzipRequest(req, res, next); - } else { - // Check if tenantId is missing in RequestInfo.userInfo - if (!req?.body?.RequestInfo?.userInfo?.tenantId) { - // If tenantId is missing, throw Validation Error - let e: any = new Error("RequestInfo.userInfo.tenantId is missing"); - e = Object.assign(e, { status: 400, code: "VALIDATION_ERROR" }); - errorResponder(e, req, res, 400) - return; - } - // Validate request payload against the defined schema - requestSchema.validateSync(req.body.RequestInfo); - // If validation succeeds, proceed to the next middleware - next(); + await handleGzipRequest(req); } - } catch (error) { + // Check if tenantId is missing in RequestInfo.userInfo + if (!req?.body?.RequestInfo?.userInfo?.tenantId) { + // If tenantId is missing, throw Validation Error + let e: any = new Error("RequestInfo.userInfo.tenantId is missing"); + e = Object.assign(e, { status: 400, code: "VALIDATION_ERROR" }); + errorResponder(e, req, res, 400) + return; + } + // Validate request payload against the defined schema + requestSchema.validateSync(req.body.RequestInfo); + // If validation succeeds, proceed to the next middleware + next(); + } + catch (error) { // If an error occurs during validation process, handle the error using errorResponder function errorResponder(error, req, res); }