From a28df47ce4e81edde3ca9a9aaab87fce927b58f7 Mon Sep 17 00:00:00 2001 From: Aral Balkan Date: Sun, 26 Jan 2020 21:40:09 +0000 Subject: [PATCH 1/5] feat: option to fall through to middleware chain on source 404 (#248) --- src/http-proxy-middleware.ts | 12 ++++ test/e2e/_utils.ts | 13 ++++ test/e2e/http-proxy-middleware.spec.ts | 87 +++++++++++++++++++++++++- 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/src/http-proxy-middleware.ts b/src/http-proxy-middleware.ts index f3f6634f..43b0e122 100644 --- a/src/http-proxy-middleware.ts +++ b/src/http-proxy-middleware.ts @@ -48,6 +48,18 @@ export class HttpProxyMiddleware { public middleware = async (req, res, next) => { if (this.shouldProxy(this.config.context, req)) { const activeProxyOptions = await this.prepareProxyRequest(req); + + // fallthrough to the middleware chain if a resource + // cannot be found in the source being proxied + if (this.proxyOptions.fallthrough) { + this.logger.info('[HPM] Fallthrough to middleware chain requested on 404s'); + this.proxy.once('proxyRes', (proxyRes) => { + if (proxyRes.statusCode === 404) { + next(); + } + }); + } + this.proxy.web(req, res, activeProxyOptions); } else { next(); diff --git a/test/e2e/_utils.ts b/test/e2e/_utils.ts index 5eaed460..fc944ef3 100644 --- a/test/e2e/_utils.ts +++ b/test/e2e/_utils.ts @@ -16,3 +16,16 @@ export function createServer(portNumber, middleware, path?) { return server; } + +export function createServerWithFallthrough(portNumber, middleware) { + const app = express(); + + app.use(middleware); + app.use((request, response, next) => { + response.end('fell through'); + }) + + const server = app.listen(portNumber); + + return server; +} diff --git a/test/e2e/http-proxy-middleware.spec.ts b/test/e2e/http-proxy-middleware.spec.ts index 196e0de8..3a2e45ec 100644 --- a/test/e2e/http-proxy-middleware.spec.ts +++ b/test/e2e/http-proxy-middleware.spec.ts @@ -1,5 +1,5 @@ import * as http from 'http'; -import { createServer, proxyMiddleware } from './_utils'; +import { createServer, proxyMiddleware, createServerWithFallthrough } from './_utils'; describe('E2E http-proxy-middleware', () => { describe('http-proxy-middleware creation', () => { @@ -658,5 +658,90 @@ describe('E2E http-proxy-middleware', () => { expect(logMessage).not.toBeUndefined(); }); }); + + describe('404 fallthrough when requested', () => { + let proxyServer; + let targetServer; + let response; + let responseBody; + + beforeEach(() => { + const mwProxy = proxyMiddleware('/', { + target: 'http://localhost:8000', + fallthrough: true + }); + + const mwTarget = (req, res, next) => { + // return 404 error + res.statusCode = 404; + res.end(); + }; + + proxyServer = createServerWithFallthrough(3000, mwProxy); + targetServer = createServer(8000, mwTarget); + }); + + beforeEach(done => { + http.get('http://localhost:3000/', res => { + response = res; + res.on('data', chunk => { + responseBody = chunk.toString(); + done(); + }); + }); + }); + + afterEach(() => { + proxyServer.close(); + targetServer.close(); + }); + + it('should fall through', () => { + expect(response.statusCode).toBe(200); + expect(responseBody).toBe('fell through'); + }); + }); + + describe('404 do not fallthrough when not requested', () => { + let proxyServer; + let targetServer; + let response; + let responseBody; + + beforeEach(() => { + const mwProxy = proxyMiddleware('/', { + target: 'http://localhost:8000', + }); + + const mwTarget = (req, res, next) => { + // return 404 error + res.statusCode = 404; + res.end('404 error'); + }; + + proxyServer = createServerWithFallthrough(3000, mwProxy); + targetServer = createServer(8000, mwTarget); + }); + + beforeEach(done => { + http.get('http://localhost:3000/', res => { + response = res; + res.on('data', chunk => { + responseBody = chunk.toString(); + done(); + }); + }); + }); + + afterEach(() => { + proxyServer.close(); + targetServer.close(); + }); + + it('should not fall through', () => { + expect(response.statusCode).toBe(404); + expect(responseBody).toBe('404 error'); + }); + }); }); }); From 5a0c43ba4c65d00231ee9b1abed5eb4598a571d4 Mon Sep 17 00:00:00 2001 From: Aral Balkan Date: Sun, 26 Jan 2020 22:05:50 +0000 Subject: [PATCH 2/5] fix: move log statement to when the fall-through actually happens --- src/http-proxy-middleware.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http-proxy-middleware.ts b/src/http-proxy-middleware.ts index 43b0e122..d5fe3429 100644 --- a/src/http-proxy-middleware.ts +++ b/src/http-proxy-middleware.ts @@ -52,9 +52,9 @@ export class HttpProxyMiddleware { // fallthrough to the middleware chain if a resource // cannot be found in the source being proxied if (this.proxyOptions.fallthrough) { - this.logger.info('[HPM] Fallthrough to middleware chain requested on 404s'); this.proxy.once('proxyRes', (proxyRes) => { if (proxyRes.statusCode === 404) { + this.logger.info('[HPM] Falling through to middleware chain on 404'); next(); } }); From f9ca58748d875da6f941b5070714026d1e2ce1f3 Mon Sep 17 00:00:00 2001 From: Aral Balkan Date: Sun, 26 Jan 2020 23:12:24 +0000 Subject: [PATCH 3/5] fix: pass fallthrough option to node-http-proxy instance --- src/http-proxy-middleware.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/http-proxy-middleware.ts b/src/http-proxy-middleware.ts index d5fe3429..d3cc73c6 100644 --- a/src/http-proxy-middleware.ts +++ b/src/http-proxy-middleware.ts @@ -19,8 +19,13 @@ export class HttpProxyMiddleware { this.config = createConfig(context, opts); this.proxyOptions = this.config.options; + let httpProxyOptions = {} + if (context.fallthrough) { + httpProxyOptions.fallthrough = true + } + // create proxy - this.proxy = httpProxy.createProxyServer({}); + this.proxy = httpProxy.createProxyServer(httpProxyOptions); this.logger.info( `[HPM] Proxy created: ${this.config.context} -> ${this.proxyOptions.target}` ); From a5d301eb6b417e5f1f61ea3301797df2de8fb2f1 Mon Sep 17 00:00:00 2001 From: Aral Balkan Date: Sun, 26 Jan 2020 23:13:07 +0000 Subject: [PATCH 4/5] chore: use local node-http-proxy for now --- package.json | 4 ++-- yarn.lock | 32 +++++++++++++++----------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 3490e3ff..a1ccadb2 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "@commitlint/cli": "^8.0.0", "@commitlint/config-conventional": "^8.0.0", "@types/express": "^4.17.0", - "@types/http-proxy": "^1.17.0", + "@types/http-proxy": "^1.17.3", "@types/is-glob": "^4.0.0", "@types/jest": "^24.0.15", "@types/lodash": "^4.14.136", @@ -72,7 +72,7 @@ "ws": "^7.1.0" }, "dependencies": { - "http-proxy": "^1.17.0", + "http-proxy": "../node-http-proxy/", "is-glob": "^4.0.1", "lodash": "^4.17.14", "micromatch": "^4.0.2" diff --git a/yarn.lock b/yarn.lock index 0b0cf96f..245218ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -494,10 +494,10 @@ "@types/express-serve-static-core" "*" "@types/serve-static" "*" -"@types/http-proxy@^1.17.0": - version "1.17.0" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.0.tgz#baf82ff6aa2723fd29f90e3ba1384e665006863e" - integrity sha512-l+s0IoxSHqhLFJPDHRfO235kgrCkvFD8JmdV/T9C4BKBYPIjrQopGFH4r7h2e3jQqgJRCthRCAZIxDoFnj1zwQ== +"@types/http-proxy@^1.17.3": + version "1.17.3" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.3.tgz#348e1b808ff9585423cb909e9992d89ccdbf4c14" + integrity sha512-wIPqXANye5BbORbuh74exbwNzj+UWCwWyeEFJzUQ7Fq3W2NSAy+7x7nX1fgbEypr2/TdKqpeuxLnXWgzN533/Q== dependencies: "@types/node" "*" @@ -1840,10 +1840,10 @@ eventemitter3@1.x.x: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" integrity sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg= -eventemitter3@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" - integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== +eventemitter3@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" + integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== exec-sh@^0.3.2: version "0.3.2" @@ -2393,6 +2393,13 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" +http-proxy@../node-http-proxy/: + version "1.18.0" + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + http-proxy@1.15.2: version "1.15.2" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.15.2.tgz#642fdcaffe52d3448d2bda3b0079e9409064da31" @@ -2401,15 +2408,6 @@ http-proxy@1.15.2: eventemitter3 "1.x.x" requires-port "1.x.x" -http-proxy@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" - integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== - dependencies: - eventemitter3 "^3.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" From 30855ceee69e84149a3e4389f50acdd338a990de Mon Sep 17 00:00:00 2001 From: Aral Balkan Date: Mon, 27 Jan 2020 16:29:46 +0000 Subject: [PATCH 5/5] docs: document fallthrough option --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 945802a5..ea0ac89c 100644 --- a/README.md +++ b/README.md @@ -225,6 +225,9 @@ Providing an alternative way to decide which requests should be proxied; In case } ``` +- **option.fallthrough**: boolean, sets whether execution should fall through to middleware chain when a 404 is encountered at the proxy target. If set, calls `next()` on the middleware chain. +Default: `false` + - **option.logLevel**: string, ['debug', 'info', 'warn', 'error', 'silent']. Default: `'info'` - **option.logProvider**: function, modify or replace log provider. Default: `console`.