From 4b5584c505e8b6616a1f357a2cb970b1380796f2 Mon Sep 17 00:00:00 2001 From: Brendan Arnold Date: Sat, 11 May 2024 21:00:46 +0100 Subject: [PATCH 1/2] Add HTTP/1.1 support in proxying --- .../reverse-proxy-config/default.conf.template | 6 ++++++ server/package-lock.json | 8 ++++---- server/package.json | 2 +- server/src/handlers.ts | 8 +------- server/src/helpers.ts | 17 ++++++++++++----- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/deploy/docker/reverse-proxy-config/default.conf.template b/deploy/docker/reverse-proxy-config/default.conf.template index 66674582b..73de283a2 100644 --- a/deploy/docker/reverse-proxy-config/default.conf.template +++ b/deploy/docker/reverse-proxy-config/default.conf.template @@ -41,6 +41,12 @@ server { add_header X-Cache-Status $upstream_cache_status; proxy_buffering on; + # Envoy (used in Azure Container Apps) requires HTTP 1.1 and also does chunked transfer + # so we need to make sure that the connection header isn't set to default of 'close' + proxy_http_version 1.1; + proxy_set_header "Connection" ""; + + # A health check endpoint location = /health { return 204; diff --git a/server/package-lock.json b/server/package-lock.json index 2a7277aac..51eefd56c 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -50,7 +50,7 @@ "@types/connect-timeout": "^0.0.34", "@types/express": "^4.17.11", "@types/fb": "^0.0.28", - "@types/http-proxy": "^1.17.5", + "@types/http-proxy": "^1.17.14", "@types/lru-cache": "^5.1.0", "@types/node": "^14.14.39", "@types/nodemailer": "^6.4.1", @@ -461,9 +461,9 @@ } }, "node_modules/@types/http-proxy": { - "version": "1.17.10", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.10.tgz", - "integrity": "sha512-Qs5aULi+zV1bwKAg5z1PWnDXWmsn+LxIvUGv6E2+OOMYhclZMO+OXd9pYVf2gLykf2I7IV2u7oTHwChPNsvJ7g==", + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", "dev": true, "dependencies": { "@types/node": "*" diff --git a/server/package.json b/server/package.json index 0bba51ae0..484a0a45c 100644 --- a/server/package.json +++ b/server/package.json @@ -53,7 +53,7 @@ "@types/connect-timeout": "^0.0.34", "@types/express": "^4.17.11", "@types/fb": "^0.0.28", - "@types/http-proxy": "^1.17.5", + "@types/http-proxy": "^1.17.14", "@types/lru-cache": "^5.1.0", "@types/node": "^14.14.39", "@types/nodemailer": "^6.4.1", diff --git a/server/src/handlers.ts b/server/src/handlers.ts index dd69c1c37..515ed59d8 100644 --- a/server/src/handlers.ts +++ b/server/src/handlers.ts @@ -6806,13 +6806,7 @@ let handle_GET_conditionalIndexFetcher = (function () { function handle_GET_localFile_dev_only( req: Request, - res: { - writeHead: ( - arg0: number, - arg1?: { "Content-Type": string } | undefined - ) => void; - end: (arg0?: undefined, arg1?: string) => void; - } + res: Response ) { const filenameParts = String(req.path).split("/"); filenameParts.shift(); diff --git a/server/src/helpers.ts b/server/src/helpers.ts index 532d521fa..505a67b79 100644 --- a/server/src/helpers.ts +++ b/server/src/helpers.ts @@ -1,6 +1,8 @@ "use strict"; import akismetLib from "akismet"; +import http from 'http'; +import https from 'https'; import async from "async"; import badwords from "badwords/object"; import crypto from "crypto"; @@ -5752,7 +5754,7 @@ function makeFileFetcher( } let url = "http://" + hostname + ":" + port + path; console.log("info", "fetch file from " + url); - let fsReq = request.get(url) + let fsReq = request.get(url, { forever: true }) fsReq .on("error", function (err: any) { @@ -6119,11 +6121,10 @@ function browserSupportsPushState(req: { headers?: { [x: string]: string } }) { return !/MSIE [23456789]/.test(req?.headers?.["user-agent"] || ""); } -// 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.ts(7009) -// @ts-ignore -let routingProxy = new httpProxy.createProxyServer(); -function proxy(req: Request, res: any) { +let routingProxy = httpProxy.createProxyServer(); + +function proxy(req: Request, res: Response) { let hostname = buildStaticHostname(req, res); if (!hostname) { let host = req?.headers?.host || ""; @@ -6148,7 +6149,13 @@ function proxy(req: Request, res: any) { let port = process.env.STATIC_FILES_PORT; // set the host header too, since S3 will look at that (or the routing proxy will patch up the request.. not sure which) if (req && req.headers && req.headers.host) req.headers.host = hostname; + + const isHttps = port === '443' + routingProxy.web(req, res, { + agent: isHttps + ? new https.Agent({ keepAlive: true }) + : new http.Agent({ keepAlive: true }), target: { host: hostname, port: port, From ff24cdfb524a523c4f4b68666fcc5dd3e030b3d2 Mon Sep 17 00:00:00 2001 From: Brendan Arnold Date: Sat, 11 May 2024 21:22:15 +0100 Subject: [PATCH 2/2] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d085df2bc..fb34ff217 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Bugs +- [#97](https://github.com/DFE-Digital/polis-whitelabel/issues/97) Only partial responses returned when deployed to Azure Container App - [#91](https://github.com/DFE-Digital/polis-whitelabel/issues/91) Allow no email transport to be configured without crash - [#87](https://github.com/DFE-Digital/polis-whitelabel/issues/85) Client report no longer hardcoded to pol.is domain - [#85](https://github.com/DFE-Digital/polis-whitelabel/issues/85) Fix local Docker Compose script