Skip to content

💡 pull in updates from upstream #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 47 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0363c66
Bump glob-parent from 5.1.1 to 5.1.2
dependabot[bot] Jun 12, 2021
7c25ccc
Bump lodash from 4.17.19 to 4.17.21
dependabot[bot] May 7, 2021
f6881dc
Bump y18n from 4.0.0 to 4.0.1
dependabot[bot] Apr 1, 2021
e2474ea
1.6.3
nik-blue-lava Oct 26, 2021
0f33292
Update aging tests to explicitly use https on httpbin calls.
monkpow Jul 25, 2023
ed6b3a4
Bump minimist from 1.2.5 to 1.2.8
dependabot[bot] Mar 4, 2023
b7f7521
Bump cookiejar from 2.1.2 to 2.1.4
dependabot[bot] Jan 23, 2023
36547d9
Bump express from 4.16.3 to 4.17.3
dependabot[bot] Dec 8, 2022
802cdb8
Bump pathval from 1.1.0 to 1.1.1
dependabot[bot] Feb 11, 2022
ec9710e
Fix use of deprecated OutgoingMessage.prototype._headers #509
Oct 27, 2022
5cadfe5
[adhoc] Update dependencies to node 16 versions.
nik-blue-lava Sep 4, 2023
c379f12
[adhoc] Address lint issues.
nik-blue-lava Sep 4, 2023
633f0f6
[adhoc] Update verbs test to use a non-deprecated attribute.
nik-blue-lava Sep 4, 2023
257415e
Tag 2.0.0 release. Update version history.
nik-blue-lava Sep 4, 2023
0122595
Instead of new Buffer using Buffer.from as per https://nodejs.org/api…
Jul 26, 2023
9af657e
[#526] Add small test check for raised question.
nik-blue-lava Sep 11, 2023
7ec6740
[#390] Kills extra call to next().
Nov 14, 2018
21479d2
[501] Repairs => Can't use proxy() twice in Express middleware stack.
nik-blue-lava Sep 11, 2023
49d72de
Add tests for using proxyReqBodyDecorator with parseReqBody=false
brandon-leapyear Oct 30, 2020
e1d0cd8
Fix using proxyReqBodyDecorator with parseReqBody=false
brandon-leapyear Oct 30, 2020
0d5bf43
Explicitly check assumptions to avoid hanging on already read request
brandon-leapyear Oct 30, 2020
a530d14
Update README
brandon-leapyear Jul 22, 2021
fe5c2bd
[489] Fixes => content-types can have semi-colons, and we need to parse
nik-blue-lava Sep 11, 2023
9ef10a5
Update README.md (#493)
stefdelec Sep 11, 2023
8d05c8f
Bump debug from 3.2.5 to 3.2.7 (#531)
dependabot[bot] Oct 2, 2023
6d31f98
minor lint (#542)
monkpow Aug 8, 2024
49f3442
Update README in anticipation of new release.
monkpow Aug 8, 2024
353c5bb
2.1.0
monkpow Aug 8, 2024
410ba31
Correct formatting in README release notes section.
monkpow Aug 8, 2024
b05cb04
2.1.1
monkpow Aug 8, 2024
0f1a486
[#545] Fixes => `content-length` request header is removed when parse…
monkpow Aug 15, 2024
3815a2c
adding code coverage tooling
monkpow Apr 23, 2025
74f57aa
commit dotfile updates
monkpow Apr 24, 2025
6a08808
Update dep versions
monkpow Apr 24, 2025
1920b57
Remove (many) instances of httpbin in test suite to increase stability
monkpow Apr 23, 2025
2d729fc
back out module type tweak
monkpow May 2, 2025
5268cf6
fixes UserDecorator
monkpow May 2, 2025
dc85c03
tiny test tidy
monkpow May 2, 2025
04a03b1
extract timeout
monkpow May 2, 2025
23c5d51
Reduce timeouts where possible.
monkpow May 2, 2025
2048e8f
[548] Update docs and tests to show how to retain proxyRes headers.
monkpow May 2, 2025
c5574ea
Incremental cleanup and organization of test files. (#561)
monkpow May 3, 2025
ef7c956
Improve organization and rigor of tests (#562)
monkpow May 6, 2025
b5d7dd0
[adhoc] Remove accidentally committed `only`. (#563)
monkpow May 6, 2025
791f196
:feat: pull in all current changes from forked lib
awfulstew Jun 18, 2025
453ee48
:package: updating package version to be off upstream latest
awfulstew Jun 18, 2025
199a34d
:shirt: missed merge conflict cleanup
awfulstew Jun 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"root": true,
"env": {
"browser": true,
"es6": true,
Expand Down Expand Up @@ -39,11 +40,11 @@
120
],
"max-depth": [
2,
2
1,
4
],
"complexity": [
2,
1,
15
],
"new-cap": 2,
Expand Down Expand Up @@ -150,7 +151,7 @@
],
"lines-around-comment": [
2,
{ "afterLineComment": true, "allowBlockEnd": true }
{ "afterLineComment": false, "allowBlockEnd": true }
],
"semi": [
2,
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

strategy:
matrix:
node-version: [10.x, 12.x, 14.x]
node-version: [16.x, 18.x, 20.x]

steps:
- uses: actions/checkout@v2
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@ node_modules
*.log

# no Webstorm info at all
.idea/*
.idea/*

coverage/
coverage.lcov
.nyc_output/
tmp/
28 changes: 28 additions & 0 deletions .nycrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"all": true,
"include": [
"index.js",
"lib/**/*.js",
"app/**/*.js"
],
"exclude": [
"test/**/*.js",
"**/*.test.js",
"**/*.spec.js",
"coverage/**",
".nyc_output/**"
],
"reporter": [
"text",
"text-summary",
"html",
"lcov"
],
"report-dir": "./coverage",
"check-coverage": true,
"per-file": true,
"lines": 80,
"statements": 80,
"functions": 80,
"branches": 80
}
93 changes: 83 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# express-http-proxy [![NPM version](https://badge.fury.io/js/express-http-proxy.svg)](http://badge.fury.io/js/express-http-proxy) [![Build Status](https://travis-ci.org/villadora/express-http-proxy.svg?branch=master)](https://travis-ci.org/villadora/express-http-proxy)
# express-http-proxy [![NPM version](https://badge.fury.io/js/express-http-proxy.svg)](http://badge.fury.io/js/express-http-proxy) [![Build Status](https://travis-ci.org/villadora/express-http-proxy.svg?branch=master)](https://travis-ci.org/villadora/express-http-proxy)


Express middleware to proxy request to another host and pass response back to original caller.
Expand All @@ -24,6 +24,51 @@ var app = require('express')();
app.use('/proxy', proxy('www.google.com'));
```

### 30k view

The proxy middleware:
* proxies request to your server to an arbitrary server, and
* provide hooks to decorate and filter requests to the proxy target, and
* provide hooks you to decorate and filter proxy responses before returning them to the client.

```

Client Express App Proxy Middleware Target Server
| | | |
| HTTP Request | | |
|-------------------------->| | |
| | Request | |
| |--------------------------->| |
| | | +------------------------+ |
| | | | Request Preprocessing | |
| | | | 1. filter requests | |
| | | | 2. resolve proxy host | |
| | | | 3. decorate proxy opts | |
| | | | 4. decorate proxy req | |
| | | | 5. resolve req path | |
| | | +------------------------+ |
| | | Forwarded Request |
| | |---------------------------->|
| | | |
| | | Response with Headers |
| | |<----------------------------|
| | | |
| | | +------------------------+ |
| | | | Response Processing | |
| | | | 1. skip to next? | |
| | | | 2. copy proxy headers | |
| | | | 3. decorate headers | |
| | | | 4. decorate response | |
| | | +------------------------+ |
| | | |
| | Modified Response | |
| |<---------------------------| |
| Final Response | | |
|<--------------------------| | |
| | | |

```

### Streaming

Proxy requests and user responses are piped/streamed/chunked by default.
Expand Down Expand Up @@ -73,19 +118,36 @@ function selectProxyHost() {

app.use('/', proxy(selectProxyHost));
```

Notie: Host is only the host name. Any params after in url will be ignored. For ``http://google.com/myPath`, ``myPath`` will be ignored because the host name is ``google.com``.
See ``proxyReqPathResolver`` for more detailed path information.


### Middleware mixing

If you use 'https://www.npmjs.com/package/body-parser' you should declare it AFTER the proxy configuration, otherwise original 'POST' body could be modified and not proxied correctly.

```

app.use('/proxy', 'http://foo.bar.com')
```js
app.use('/proxy', proxy('http://foo.bar.com'))

// Declare use of body-parser AFTER the use of proxy
app.use(bodyParser.foo(bar))
app.use('/api', ...)
```

If this cannot be avoided and you MUST proxy after `body-parser` has been registered, set `parseReqBody` to `false` and explicitly specify the body you wish to send in `proxyReqBodyDecorator`.

```js
app.use(bodyParser.foo(bar))

app.use('/proxy', proxy('http://foo.bar.com', {
parseReqBody: false,
proxyReqBodyDecorator: function () {

},
}))
```

### Options

#### proxyReqPathResolver (supports Promises)
Expand Down Expand Up @@ -155,17 +217,17 @@ Promise form:

```js
app.use(proxy('localhost:12346', {
filter: function (req, res) {
return new Promise(function (resolve) {
filter: function (req, res) {
return new Promise(function (resolve) {
resolve(req.method === 'GET');
});
});
}
}));
```

Note that in the previous example, `resolve(false)` will execute the happy path
for filter here (skipping the rest of the proxy, and calling `next()`).
`reject()` will also skip the rest of proxy and call `next()`.
`reject()` will also skip the rest of proxy and call `next()`.

#### userResDecorator (was: intercept) (supports Promise)

Expand Down Expand Up @@ -259,6 +321,11 @@ first request.

When a `userResHeaderDecorator` is defined, the return of this method will replace (rather than be merged on to) the headers for `userRes`.

> Note that by default, headers from the PROXY response CLOBBER all headers that may have previously been set on the userResponse.
> Authors have the option of constructing any combination of proxyRes and userRes headers in the `userResHeaderDecorator`.
> Check the tests for this method for examples.


```js
app.use('/proxy', proxy('www.google.com', {
userResHeaderDecorator(headers, userReq, userRes, proxyReq, proxyRes) {
Expand Down Expand Up @@ -588,14 +655,20 @@ app.use('/', proxy('internalhost.example.com', {

| Release | Notes |
| --- | --- |
| next | Fixes => content-length request header is removed when parseReqBody is false (#549) |
| 2.1.1 | (trivial) Fixes formatting in README.|
| 2.1.0 | Fixes parsing error in content-types. Improves behavior of proxyReqBodyDecorator when parseReqBody=false. Repairs issue where authors can't use proxy() twice in Express middleware stack. Fix `new Buffer` deprecation warning. |
| 2.0.0 | Update all dependencies; set stage for next iteration. `express-http-proxy` interface has not changed, but the underlying libraries are not guaranteed to be backward compatible. Versions beyond this point are expected to be run in node verions >= 16. |
| ----- | ----------------------------------------------------------------------- |
| 1.6.3 | [#453] Author should be able to delete headers in userResHeaderDecorator.
| 1.6.2 | Update node.js versions used by ci. |
| 1.6.1 | Minor bug fixes and documentation. |
| 1.6.0 | Do gzip and gunzip aysyncronously. Test and documentation improvements, dependency updates. |
| 1.5.1 | Fixes bug in stringifying debug messages. |
| 1.5.0 | Fixes bug in `filter` signature. Fix bug in skipToNextHandler, add expressHttpProxy value to user res when skipped. Add tests for host as ip address. |
| 1.4.0 | DEPRECATED. Critical bug in the `filter` api.|
| 1.4.0 | DEPRECATED. Critical bug in the `filter` api.|
| 1.3.0 | DEPRECATED. Critical bug in the `filter` api. `filter` now supports Promises. Update linter to eslint. |
| 1.2.0 | Auto-stream when no decorations are made to req/res. Improved docs, fixes issues in maybeSkipToNexthandler, allow authors to manage error handling. |
| 1.2.0 | Auto-stream when no decorations are made to req/res. Improved docs, fixes issues in maybeSkipToNexthandler, allow authors to manage error handling. |
| 1.1.0 | Add step to allow response headers to be modified.
| 1.0.7 | Update dependencies. Improve docs on promise rejection. Fix promise rejection on body limit. Improve debug output. |
| 1.0.6 | Fixes preserveHostHdr not working, skip userResDecorator on 304, add maybeSkipToNext, test improvements and cleanup. |
Expand Down
7 changes: 6 additions & 1 deletion app/steps/buildProxyReq.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ function buildProxyReq(Container) {

return Promise
.all([parseBody, createReqOptions])
.then(function(responseArray) {
.then(function (responseArray) {
req.body = responseArray[0];
Container.proxy.bodyContent = responseArray[0];
Container.proxy.reqBuilder = responseArray[1];
debug('proxy request options:', Container.proxy.reqBuilder);
return Container;
})
.catch(function (err) {
debug('error occurred while building proxy request:', err);
return Promise.reject(err);
});
}

Expand Down
12 changes: 6 additions & 6 deletions app/steps/copyProxyResHeadersToUserRes.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
'use strict';

function copyProxyResHeadersToUserRes(container) {
return new Promise(function(resolve) {
return new Promise(function (resolve) {
var res = container.user.res;
var rsp = container.proxy.res;

if (!res.headersSent) {
res.status(rsp.statusCode);
Object.keys(rsp.headers)
.filter(function(item) { return item !== 'transfer-encoding'; })
.forEach(function(item) {
res.set(item, rsp.headers[item]);
res.status(rsp.statusCode);
Object.keys(rsp.headers)
.filter(function (item) { return item !== 'transfer-encoding'; })
.forEach(function (item) {
res.set(item, rsp.headers[item]);
});
}

Expand Down
2 changes: 1 addition & 1 deletion app/steps/decorateProxyReqBody.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function decorateProxyReqBody(container) {

return Promise
.resolve(resolverFn(container.proxy.bodyContent, container.user.req))
.then(function(bodyContent) {
.then(function (bodyContent) {
container.proxy.bodyContent = bodyContent;
return Promise.resolve(container);
});
Expand Down
24 changes: 12 additions & 12 deletions app/steps/decorateUserRes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ function isResGzipped(res) {
}

function zipOrUnzip(method) {
return function(rspData, res) {
return function (rspData, res) {
return new Promise(function (resolve, reject) {
if (isResGzipped(res) && rspData.length) {
zlib[method](rspData, function(err, buffer) {
if(err) {
zlib[method](rspData, function (err, buffer) {
if (err) {
reject(err);
} else {
resolve(buffer);
Expand All @@ -37,11 +37,11 @@ function verifyBuffer(rspd, reject) {

function updateHeaders(res, rspdBefore, rspdAfter, reject) {
if (!res.headersSent) {
res.set('content-length', rspdAfter.length);
res.set('content-length', rspdAfter.length);
} else if (rspdAfter.length !== rspdBefore.length) {
var error = '"Content-Length" is already sent,' +
var error = '"Content-Length" is already sent,' +
'the length of response data can not be changed';
return reject(new Error(error));
return reject(new Error(error));
}
}

Expand All @@ -56,27 +56,27 @@ function decorateProxyResBody(container) {
var proxyRes = container.proxy.res;
var req = container.user.req;
var res = container.user.res;
var originalResData;
var originalResData;

if (res.statusCode === 304) {
debug('Skipping userResDecorator on response 304');
return Promise.resolve(container);
}

return proxyResDataPromise
.then(function(proxyResData){
.then(function (proxyResData) {
originalResData = proxyResData;
return resolverFn(proxyRes, proxyResData, req, res);
})
.then(function(modifiedResData) {
return new Promise(function(resolve, reject) {
.then(function (modifiedResData) {
return new Promise(function (resolve, reject) {
var rspd = as.buffer(modifiedResData, container.options);
verifyBuffer(rspd, reject);
updateHeaders(res, originalResData, rspd, reject);
maybeZipPromise(rspd, container.proxy.res).then(function(buffer) {
maybeZipPromise(rspd, container.proxy.res).then(function (buffer) {
container.proxy.resData = buffer;
resolve(container);
}).catch(function(error){
}).catch(function (error) {
reject(error);
});
});
Expand Down
14 changes: 7 additions & 7 deletions app/steps/decorateUserResHeaders.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
'use strict';

var getHeaders = require('../../lib/getHeaders');

function decorateUserResHeaders(container) {
var resolverFn = container.options.userResHeaderDecorator;
var headers = container.user.res.getHeaders ? container.user.res.getHeaders() : container.user.res._headers;
var headers = getHeaders(container.user.res);

if (!resolverFn) {
return Promise.resolve(container);
}

const clearAllHeaders = (res) => {
for (const header in res._headers) {
res.removeHeader(header)
for (const header in getHeaders(res)) {
res.removeHeader(header);
}
}
};

return Promise
.resolve(resolverFn(headers, container.user.req, container.user.res, container.proxy.req, container.proxy.res))
.then(function(headers) {
return new Promise(function(resolve) {
.then(function (headers) {
return new Promise(function (resolve) {
clearAllHeaders(container.user.res);
container.user.res.set(headers);
resolve(container);
Expand Down
2 changes: 1 addition & 1 deletion app/steps/handleProxyErrors.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ function connectionResetHandler(err, res) {
if (err && err.code === 'ECONNRESET') {
debug('Error: Connection reset due to timeout.');
res.setHeader('X-Timeout-Reason', 'express-http-proxy reset the request.');
res.writeHead(504, {'Content-Type': 'text/plain'});
res.writeHead(504, { 'Content-Type': 'text/plain' });
res.end();
}
}
Expand Down
Loading