From 3e84b15f748d1383aeb095ba34a04b431420c3e5 Mon Sep 17 00:00:00 2001 From: chimurai <655241+chimurai@users.noreply.github.com> Date: Tue, 14 Mar 2023 21:08:27 +0100 Subject: [PATCH] refactor: minor type improvements (#895) --- src/handlers/response-interceptor.ts | 6 +-- src/http-proxy-middleware.ts | 14 ++++--- src/legacy/options-adapter.ts | 4 +- src/path-filter.ts | 20 +++++----- src/path-rewriter.ts | 8 ++-- src/plugins/default/error-response-plugin.ts | 5 +-- src/types.ts | 4 +- test/e2e/express-error-middleware.spec.ts | 2 +- test/e2e/http-proxy-middleware.spec.ts | 8 ++-- test/e2e/plugins.spec.ts | 4 +- test/unit/response-interceptor.spec.ts | 41 +++++++++----------- tsconfig.json | 4 +- 12 files changed, 62 insertions(+), 58 deletions(-) diff --git a/src/handlers/response-interceptor.ts b/src/handlers/response-interceptor.ts index 1631a5d0..4cf2db63 100644 --- a/src/handlers/response-interceptor.ts +++ b/src/handlers/response-interceptor.ts @@ -67,9 +67,9 @@ export function responseInterceptor< */ function decompress( proxyRes: TReq, - contentEncoding: string -): TReq { - let _proxyRes = proxyRes; + contentEncoding?: string +): TReq | zlib.Gunzip | zlib.Inflate | zlib.BrotliDecompress { + let _proxyRes: TReq | zlib.Gunzip | zlib.Inflate | zlib.BrotliDecompress = proxyRes; let decompress; switch (contentEncoding) { diff --git a/src/http-proxy-middleware.ts b/src/http-proxy-middleware.ts index f1465ff9..7f9bf07f 100644 --- a/src/http-proxy-middleware.ts +++ b/src/http-proxy-middleware.ts @@ -1,3 +1,4 @@ +import type * as net from 'net'; import type * as http from 'http'; import type * as https from 'https'; import type { RequestHandler, Options, Filter } from './types'; @@ -38,7 +39,7 @@ export class HttpProxyMiddleware { } // https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this - public middleware: RequestHandler = async (req, res, next?) => { + public middleware: RequestHandler = (async (req, res, next?) => { if (this.shouldProxy(this.proxyOptions.pathFilter, req)) { try { const activeProxyOptions = await this.prepareProxyRequest(req); @@ -73,7 +74,7 @@ export class HttpProxyMiddleware { // use initial request to access the server object to subscribe to http upgrade event this.catchUpgradeRequest(server); } - }; + }) as RequestHandler; private registerPlugins(proxy: httpProxy, options: Options) { const plugins = getPlugins(options); @@ -93,7 +94,7 @@ export class HttpProxyMiddleware { } }; - private handleUpgrade = async (req: http.IncomingMessage, socket, head) => { + private handleUpgrade = async (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => { if (this.shouldProxy(this.proxyOptions.pathFilter, req)) { const activeProxyOptions = await this.prepareProxyRequest(req); this.proxy.ws(req, socket, head, activeProxyOptions); @@ -104,7 +105,10 @@ export class HttpProxyMiddleware { /** * Determine whether request should be proxied. */ - private shouldProxy = (pathFilter: Filter, req: http.IncomingMessage): boolean => { + private shouldProxy = ( + pathFilter: Filter | undefined, + req: http.IncomingMessage + ): boolean => { return matchPathFilter(pathFilter, req.url, req); }; @@ -138,7 +142,7 @@ export class HttpProxyMiddleware { }; // Modify option.target when router present. - private applyRouter = async (req: http.IncomingMessage, options) => { + private applyRouter = async (req: http.IncomingMessage, options: Options) => { let newTarget; if (options.router) { diff --git a/src/legacy/options-adapter.ts b/src/legacy/options-adapter.ts index eb28b7da..f5283b1b 100644 --- a/src/legacy/options-adapter.ts +++ b/src/legacy/options-adapter.ts @@ -24,7 +24,7 @@ export function legacyOptionsAdapter( legacyContext: Filter | LegacyOptions, legacyOptions: LegacyOptions ): Options { - let options: LegacyOptions; + let options: LegacyOptions = {}; let logger: Logger; // https://github.com/chimurai/http-proxy-middleware/pull/716 @@ -58,6 +58,8 @@ export function legacyOptionsAdapter( } else if (legacyContext && !legacyOptions) { options = { ...(legacyContext as LegacyOptions) }; logger = getLegacyLogger(options); + } else { + logger = getLegacyLogger({}) as never; } // map old event names to new event names diff --git a/src/path-filter.ts b/src/path-filter.ts index 6053fcbe..434dc893 100644 --- a/src/path-filter.ts +++ b/src/path-filter.ts @@ -7,7 +7,7 @@ import type * as http from 'http'; export function matchPathFilter( pathFilter: Filter = '/', - uri: string, + uri: string | undefined, req: http.IncomingMessage ): boolean { // single path @@ -34,8 +34,8 @@ export function matchPathFilter( // custom matching if (typeof pathFilter === 'function') { - const pathname = getUrlPathName(uri); - return pathFilter(pathname, req as unknown as TReq); + const pathname = getUrlPathName(uri) as string; + return pathFilter(pathname, req as TReq); } throw new Error(ERRORS.ERR_CONTEXT_MATCHER_GENERIC); @@ -46,18 +46,18 @@ export function matchPathFilter( * @param {String} uri 'http://example.org/api/b/c/d.html' * @return {Boolean} */ -function matchSingleStringPath(pathFilter: string, uri: string) { +function matchSingleStringPath(pathFilter: string, uri?: string) { const pathname = getUrlPathName(uri); - return pathname.indexOf(pathFilter) === 0; + return pathname?.indexOf(pathFilter) === 0; } -function matchSingleGlobPath(pattern: string | string[], uri: string) { - const pathname = getUrlPathName(uri); +function matchSingleGlobPath(pattern: string | string[], uri?: string) { + const pathname = getUrlPathName(uri) as string; const matches = micromatch([pathname], pattern); return matches && matches.length > 0; } -function matchMultiGlobPath(patternList: string | string[], uri: string) { +function matchMultiGlobPath(patternList: string | string[], uri?: string) { return matchSingleGlobPath(patternList, uri); } @@ -66,7 +66,7 @@ function matchMultiGlobPath(patternList: string | string[], uri: string) { * @param {String} uri 'http://example.org/api/b/c/d.html' * @return {Boolean} */ -function matchMultiPath(pathFilterList: string[], uri: string) { +function matchMultiPath(pathFilterList: string[], uri?: string) { let isMultiPath = false; for (const context of pathFilterList) { @@ -85,7 +85,7 @@ function matchMultiPath(pathFilterList: string[], uri: string) { * @param {String} uri from req.url * @return {String} RFC 3986 path */ -function getUrlPathName(uri: string) { +function getUrlPathName(uri?: string) { return uri && url.parse(uri).pathname; } diff --git a/src/path-rewriter.ts b/src/path-rewriter.ts index 43782443..14912dad 100644 --- a/src/path-rewriter.ts +++ b/src/path-rewriter.ts @@ -4,6 +4,8 @@ import { Debug } from './debug'; const debug = Debug.extend('path-rewriter'); +type RewriteRule = { regex: RegExp; value: string }; + /** * Create rewrite function, to cache parsed rewrite rules. * @@ -11,7 +13,7 @@ const debug = Debug.extend('path-rewriter'); * @return {Function} Function to rewrite paths; This function should accept `path` (request.url) as parameter */ export function createPathRewriter(rewriteConfig) { - let rulesCache; + let rulesCache: RewriteRule[]; if (!isValidRewriteConfig(rewriteConfig)) { return; @@ -52,8 +54,8 @@ function isValidRewriteConfig(rewriteConfig) { } } -function parsePathRewriteRules(rewriteConfig) { - const rules = []; +function parsePathRewriteRules(rewriteConfig: Record) { + const rules: RewriteRule[] = []; if (isPlainObj(rewriteConfig)) { for (const [key, value] of Object.entries(rewriteConfig)) { diff --git a/src/plugins/default/error-response-plugin.ts b/src/plugins/default/error-response-plugin.ts index 25dd8378..4d9113db 100644 --- a/src/plugins/default/error-response-plugin.ts +++ b/src/plugins/default/error-response-plugin.ts @@ -1,15 +1,14 @@ import { getStatusCode } from '../../status-code'; import { Plugin } from '../../types'; -import type * as http from 'http'; export const errorResponsePlugin: Plugin = (proxyServer, options) => { - proxyServer.on('error', (err, req, res: http.ServerResponse, target?) => { + proxyServer.on('error', (err, req, res, target?) => { // Re-throw error. Not recoverable since req & res are empty. if (!req && !res) { throw err; // "Error: Must provide a proper URL as target" } - if (res.writeHead && !res.headersSent) { + if ('writeHead' in res && !res.headersSent) { const statusCode = getStatusCode((err as unknown as any).code); res.writeHead(statusCode); } diff --git a/src/types.ts b/src/types.ts index 61c278a0..0fbfdfd9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,7 +17,7 @@ export interface RequestHandler< TNext = NextFunction > { (req: TReq, res: TRes, next?: TNext): void | Promise; - upgrade?: (req: http.IncomingMessage, socket: net.Socket, head: any) => void; + upgrade: (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => void; } export type Filter = @@ -67,7 +67,7 @@ export interface Options string) + | ((path: string, req: TReq) => string | undefined) | ((path: string, req: TReq) => Promise); /** * Access the internal http-proxy server instance to customize behavior diff --git a/test/e2e/express-error-middleware.spec.ts b/test/e2e/express-error-middleware.spec.ts index 53a6ac2b..9d4e0830 100644 --- a/test/e2e/express-error-middleware.spec.ts +++ b/test/e2e/express-error-middleware.spec.ts @@ -18,7 +18,7 @@ describe('express error middleware', () => { const app = createApp(proxyMiddleware, errorMiddleware); const response = await request(app).get('/get').expect(504); - expect(httpProxyError.message).toBe('Must provide a proper URL as target'); + expect(httpProxyError?.message).toBe('Must provide a proper URL as target'); expect(response.text).toBe('Something broke!'); }); }); diff --git a/test/e2e/http-proxy-middleware.spec.ts b/test/e2e/http-proxy-middleware.spec.ts index cd113f91..6ad49a57 100644 --- a/test/e2e/http-proxy-middleware.spec.ts +++ b/test/e2e/http-proxy-middleware.spec.ts @@ -256,7 +256,7 @@ describe('E2E http-proxy-middleware', () => { }); it('should send request header "host" to target server', async () => { - let completedRequest: CompletedRequest; + let completedRequest: CompletedRequest | undefined; await mockTargetServer.forGet().thenCallback((req) => { completedRequest = req; @@ -265,7 +265,7 @@ describe('E2E http-proxy-middleware', () => { const response = await agent.get(`/api/some/endpoint/index.html`).expect(200); expect(response.text).toBe('OK'); - expect(completedRequest.headers.host).toBe('foobar.dev'); + expect(completedRequest?.headers.host).toBe('foobar.dev'); }); }); @@ -374,7 +374,7 @@ describe('E2E http-proxy-middleware', () => { }); it('should add `x-added` as custom header to request"', async () => { - let completedRequest: CompletedRequest; + let completedRequest: CompletedRequest | undefined; await mockTargetServer.forGet().thenCallback((req) => { completedRequest = req; return { statusCode: 200 }; @@ -382,7 +382,7 @@ describe('E2E http-proxy-middleware', () => { await agent.get(`/api/foo/bar`).expect(200); - expect(completedRequest.headers['x-added']).toBe('added-from-hpm'); + expect(completedRequest?.headers['x-added']).toBe('added-from-hpm'); }); }); diff --git a/test/e2e/plugins.spec.ts b/test/e2e/plugins.spec.ts index 935b7af6..d5576f64 100644 --- a/test/e2e/plugins.spec.ts +++ b/test/e2e/plugins.spec.ts @@ -16,8 +16,8 @@ describe('E2E Plugins', () => { }); it('should register a plugin and access the http-proxy object', async () => { - let proxyReqUrl: string; - let responseStatusCode: number; + let proxyReqUrl: string | undefined; + let responseStatusCode: number | undefined; mockTargetServer.forGet('/users/1').thenReply(200, '{"userName":"John"}'); diff --git a/test/unit/response-interceptor.spec.ts b/test/unit/response-interceptor.spec.ts index 03ec9567..588b73f8 100644 --- a/test/unit/response-interceptor.spec.ts +++ b/test/unit/response-interceptor.spec.ts @@ -1,9 +1,10 @@ import { IncomingMessage, ServerResponse } from 'http'; +import { Socket } from 'net'; import { responseInterceptor } from '../../src/handlers/response-interceptor'; const fakeProxyResponse = () => { - const httpIncomingMessage = new IncomingMessage(null); + const httpIncomingMessage = new IncomingMessage(new Socket()); httpIncomingMessage._read = () => ({}); return httpIncomingMessage; }; @@ -24,38 +25,32 @@ const waitInterceptorHandler = (ms = 1): Promise => describe('responseInterceptor', () => { it('should write body on end proxy event', async () => { - const httpIncomingMessage = fakeProxyResponse(); - const response = fakeResponse(); + const proxyRes = fakeProxyResponse(); + const req = fakeProxyResponse(); + const res = fakeResponse(); - responseInterceptor(async () => JSON.stringify({ someField: '' }))( - httpIncomingMessage, - null, - response - ); + responseInterceptor(async () => JSON.stringify({ someField: '' }))(proxyRes, req, res); - httpIncomingMessage.emit('end'); + proxyRes.emit('end'); await waitInterceptorHandler(); const expectedBody = JSON.stringify({ someField: '' }); - expect(response.setHeader).toHaveBeenCalledWith('content-length', expectedBody.length); - expect(response.write).toHaveBeenCalledWith(Buffer.from(expectedBody)); - expect(response.end).toHaveBeenCalledWith(); + expect(res.setHeader).toHaveBeenCalledWith('content-length', expectedBody.length); + expect(res.write).toHaveBeenCalledWith(Buffer.from(expectedBody)); + expect(res.end).toHaveBeenCalledWith(); }); it('should end with error when receive a proxy error event', async () => { - const httpIncomingMessage = fakeProxyResponse(); - const response = fakeResponse(); + const proxyRes = fakeProxyResponse(); + const req = fakeProxyResponse(); + const res = fakeResponse(); - responseInterceptor(async () => JSON.stringify({ someField: '' }))( - httpIncomingMessage, - null, - response - ); + responseInterceptor(async () => JSON.stringify({ someField: '' }))(proxyRes, req, res); - httpIncomingMessage.emit('error', new Error('some error message')); + proxyRes.emit('error', new Error('some error message')); - expect(response.setHeader).not.toHaveBeenCalled(); - expect(response.write).not.toHaveBeenCalled(); - expect(response.end).toHaveBeenCalledWith('Error fetching proxied request: some error message'); + expect(res.setHeader).not.toHaveBeenCalled(); + expect(res.write).not.toHaveBeenCalled(); + expect(res.end).toHaveBeenCalledWith('Error fetching proxied request: some error message'); }); }); diff --git a/tsconfig.json b/tsconfig.json index 4073e3a4..49e23b82 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,9 @@ "moduleResolution": "node", "target": "es2019", "incremental": true, - "declaration": true + "declaration": true, + "strict": true, + "noImplicitAny": false }, "include": ["./src"] }