From 7e4df8acd098b1947c1d7554c4410db14cee572f Mon Sep 17 00:00:00 2001
From: Matthias Osswald <mat.osswald@sap.com>
Date: Mon, 25 May 2020 13:05:12 +0200
Subject: [PATCH] feat(proxy): use keepAlive agent

Using an agent with keepAlive improves performance as the socket is
reused across multiple requests.
---
 lib/middleware/proxy.js            | 21 +++++++++++++++++----
 test/unit/middleware/proxy.spec.js | 20 ++++++++++++++++++++
 2 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/lib/middleware/proxy.js b/lib/middleware/proxy.js
index 4f01c3d66..7c9a43214 100644
--- a/lib/middleware/proxy.js
+++ b/lib/middleware/proxy.js
@@ -1,4 +1,6 @@
 const url = require('url')
+const { Agent: httpAgent } = require('http')
+const { Agent: httpsAgent } = require('https')
 const httpProxy = require('http-proxy')
 const _ = require('lodash')
 
@@ -42,11 +44,14 @@ function parseProxyConfig (proxies, config) {
       port = config.port
     }
     const changeOrigin = proxyConfiguration.changeOrigin || false
+    const Agent = https ? httpsAgent : httpAgent
+    const agent = new Agent({ keepAlive: true })
     const proxy = httpProxy.createProxyServer({
       target: { host: hostname, port, https, protocol },
       xfwd: true,
       changeOrigin: changeOrigin,
-      secure: config.proxyValidateSSL
+      secure: config.proxyValidateSSL,
+      agent
     })
 
     ;['proxyReq', 'proxyRes'].forEach(function (name) {
@@ -66,7 +71,7 @@ function parseProxyConfig (proxies, config) {
       res.destroy()
     })
 
-    return { path: proxyPath, baseUrl: pathname, host: hostname, port, https, proxy }
+    return { path: proxyPath, baseUrl: pathname, host: hostname, port, https, proxy, agent }
   }), 'path').reverse()
 }
 
@@ -112,6 +117,14 @@ function createProxyHandler (proxies, urlRoot) {
   return createProxy
 }
 
-exports.create = function (/* config */config, /* config.proxies */proxies) {
-  return createProxyHandler(parseProxyConfig(proxies, config), config.urlRoot)
+exports.create = function (/* config */config, /* config.proxies */proxies, /* emitter */emitter) {
+  const proxyRecords = parseProxyConfig(proxies, config)
+  emitter.on('exit', (done) => {
+    log.debug('Destroying proxy agents')
+    proxyRecords.forEach((proxyRecord) => {
+      proxyRecord.agent.destroy()
+    })
+    done()
+  })
+  return createProxyHandler(proxyRecords, config.urlRoot)
 }
diff --git a/test/unit/middleware/proxy.spec.js b/test/unit/middleware/proxy.spec.js
index 5e97e2290..6bfc2c80c 100644
--- a/test/unit/middleware/proxy.spec.js
+++ b/test/unit/middleware/proxy.spec.js
@@ -352,4 +352,24 @@ describe('middleware.proxy', () => {
   it('should handle empty proxy config', () => {
     expect(m.parseProxyConfig({})).to.deep.equal([])
   })
+
+  it('should use http agent with keepAlive=true', () => {
+    const proxy = { '/base': 'http://localhost:8000/proxy' }
+    const parsedProxyConfig = m.parseProxyConfig(proxy, {})
+    expect(parsedProxyConfig).to.have.length(1)
+    expect(parsedProxyConfig[0].proxy.options.agent).to.containSubset({
+      keepAlive: true,
+      protocol: 'http:'
+    })
+  })
+
+  it('should use https agent with keepAlive=true', () => {
+    const proxy = { '/base': 'https://localhost:8000/proxy' }
+    const parsedProxyConfig = m.parseProxyConfig(proxy, {})
+    expect(parsedProxyConfig).to.have.length(1)
+    expect(parsedProxyConfig[0].proxy.options.agent).to.containSubset({
+      keepAlive: true,
+      protocol: 'https:'
+    })
+  })
 })