Security News
Weekly Downloads Now Available in npm Package Search Results
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
express-http-proxy-2
Advanced tools
Fork of express-http-proxy including:
Express middleware to proxy request to another host and pass the response back to the original caller.
$ npm install express-http-proxy-2 --save
proxy(host, options);
To proxy URLs starting with '/proxy' to the host 'www.google.com':
var proxy = require('express-http-proxy-2');
var app = require('express')();
app.use('/proxy', proxy('www.google.com'));
Proxy requests and user responses are piped/streamed/chunked by default.
If you define a response modifier (userResDecorator, userResHeaderDecorator), or need to inspect the response before continuing (maybeSkipToNext), streaming is disabled, and the request and response are buffered. This can cause performance issues with large payloads.
Many function hooks support Promises.
If any Promise is rejected, next(x)
is called in the hosting application, where x
is whatever you pass to Promise.reject
;
e.g.
app.use(proxy('/reject-promise', {
proxyReqOptDecorator: function() {
return Promise.reject('An arbitrary rejection message.');
}
}));
eventually calls
next('An arbitrary rejection messasage');
The first positional argument is for the proxy host; in many cases, you will use a static string here, eg.
app.use('/', proxy('http://google.com'))
However, this argument can also be a function, and that function can be
memoized or computed on each request, based on the setting of
memoizeHost
.
function selectProxyHost() {
return (new Date() % 2) ? 'http://google.com' : 'http://altavista.com';
}
app.use('/', proxy(selectProxyHost));
If you use 'https://www.npmjs.com/package/body-parser' you should declare it AFTER the proxy configuration, otherwise, the original 'POST' body could be modified and not proxied correctly.
app.use('/proxy', 'http://foo.bar.com')
// Declare use of body-parser AFTER the use of proxy
app.use(bodyParser.foo(bar))
app.use('/api', ...)
Note: In express-http-proxy
, the path
is considered the portion of
the url after the host, and including all query params. E.g. for the URL
http://smoogle.com/search/path?q=123
; the path is
/search/path?q=123
. Authors using this resolver must also handle the query parameter portion of the path.
Provide a proxyReqPathResolver function if you'd like to operate on the path before issuing the proxy request. Use a Promise for async operations.
app.use(proxy('localhost:12345', {
proxyReqPathResolver: function (req) {
var parts = req.url.split('?');
var queryString = parts[1];
var updatedPath = parts[0].replace(/test/, 'tent');
return updatedPath + (queryString ? '?' + queryString : '');
}
}));
Promise form
app.use('/proxy', proxy('localhost:12345', {
proxyReqPathResolver: function(req) {
return new Promise(function (resolve, reject) {
setTimeout(function () { // simulate async
var parts = req.url.split('?');
var queryString = parts[1];
var updatedPath = parts[0].replace(/test/, 'tent');
var resolvedPathValue = updatedPath + (queryString ? '?' + queryString : '');
resolve(resolvedPathValue);
}, 200);
});
}
}));
DEPRECATED. See proxyReqPathResolver
DEPRECATED. See proxyReqPathResolver
The filter
option can be used to limit what requests are proxied. Return
true
to continue to execute proxy; return false-y to skip proxy for this
request.
For example, if you only want to proxy get a request:
app.use('/proxy', proxy('www.google.com', {
filter: function(req, res) {
return req.method == 'GET';
}
}));
Promise form:
app.use(proxy('localhost:12346', {
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 the proxy and call next()
.
You can modify the proxy's response before sending it to the client.
app.use('/proxy', proxy('www.google.com', {
userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
data = JSON.parse(proxyResData.toString('utf8'));
data.newProperty = 'exciting data';
return JSON.stringify(data);
}
}));
app.use(proxy('httpbin.org', {
userResDecorator: function(proxyRes, proxyResData) {
return new Promise(function(resolve) {
proxyResData.funkyMessage = 'oi io oo ii';
setTimeout(function() {
resolve(proxyResData);
}, 200);
});
}
}));
When your proxied service returns 304, not modified, this step will be skipped, since there is no body to decorate.
The intent is that this be used to modify the proxy response data only.
Note: The other arguments (proxyRes, userReq, userRes) are passed by reference, so you can currently exploit this to modify either response's headers, for instance, but this is not a reliable interface. I expect to close this exploit in a future release, while providing an additional hook for mutating the userRes before sending.
If your proxy response is gzipped, this program will automatically unzip it before passing to your function, then zip it back up before piping it to the user response. There is currently no way to short-circuit this behavior.
This sets the body size limit (default: 1mb
). If the body size is larger than the specified (or default) limit,
a 413 Request Entity Too Large
error will be returned. See bytes.js for
a list of supported formats.
app.use('/proxy', proxy('www.google.com', {
limit: '5mb'
}));
Defaults to true
.
When true, the host
argument will be parsed on first request, and
memoized for subsequent requests.
When false
, host
argument will be parsed on each request.
E.g.,
function coinToss() { return Math.random() > .5 }
function getHost() { return coinToss() ? 'http://yahoo.com' : 'http://google.com' }
app.use(proxy(getHost, {
memoizeHost: false
}))
In this example, when memoizeHost:false
, the coinToss occurs on each
request, and each request could get either value.
Conversely, When memoizeHost:true
, the coinToss would occur on the first
request, and all additional requests would return the value resolved on the
first request.
When a userResHeaderDecorator
is defined, the return of this method will replace (rather than be merged on to) the headers for userRes
.
app.use('/proxy', proxy('www.google.com', {
userResHeaderDecorator(headers, userReq, userRes, proxyReq, proxyRes) {
// recieves an Object of headers, returns an Object of headers.
return headers;
}
}));
REMOVED: See proxyReqOptDecorator
and proxyReqBodyDecorator
.
(experimental: this interface may change in upcoming versions)
Allows you to inspect the proxy response, and decide if you want to continue processing (via express-http-proxy) or call next()
to return control to express.
app.use('/proxy', proxy('www.google.com', {
skipToNextHandlerFilter: function(proxyRes) {
return proxyRes.statusCode === 404;
}
}));
By default, express-http-proxy
will pass any errors except ECONNRESET to
next, so that your application can handle or react to them, or just drop
through to your default error handling. ECONNRESET errors are immediately
returned to the user for historical reasons.
If you would like to modify this behavior, you can provide your own proxyErrorHandler
.
// Example of skipping all error handling.
app.use(proxy('localhost:12346', {
proxyErrorHandler: function(err, res, next) {
next(err);
}
}));
// Example of rolling your own
app.use(proxy('localhost:12346', {
proxyErrorHandler: function(err, res, next) {
switch (err && err.code) {
case 'ECONNRESET': { return res.status(405).send('504 became 405'); }
case 'ECONNREFUSED': { return res.status(200).send('gotcher back'); }
default: { next(err); }
}
}}));
You can override most request options before issuing the proxyRequest. proxyReqOpt represents the options argument passed to the (http|https).request module.
NOTE: req.path cannot be changed via this method; use proxyReqPathResolver
instead. (see https://github.com/villadora/express-http-proxy/issues/243)
app.use('/proxy', proxy('www.google.com', {
proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
// you can update headers
proxyReqOpts.headers['Content-Type'] = 'text/html';
// you can change the method
proxyReqOpts.method = 'GET';
return proxyReqOpts;
}
}));
You can use a Promise for async style.
app.use('/proxy', proxy('www.google.com', {
proxyReqOptDecorator: function(proxyReqOpts, srcReq) {
return new Promise(function(resolve, reject) {
proxyReqOpts.headers['Content-Type'] = 'text/html';
resolve(proxyReqOpts);
})
}
}));
You can mutate the body content before sending the proxyRequest.
app.use('/proxy', proxy('www.google.com', {
proxyReqBodyDecorator: function(bodyContent, srcReq) {
return bodyContent.split('').reverse().join('');
}
}));
You can use a Promise for async style.
app.use('/proxy', proxy('www.google.com', {
proxyReqBodyDecorator: function(proxyReq, srcReq) {
return new Promise(function(resolve, reject) {
http.get('http://dev/null', function (err, res) {
if (err) { reject(err); }
resolve(res);
});
})
}
}));
Normally, your proxy request will be made on the same protocol as the host
parameter. If you'd like to force the proxy request to be https, use this
option.
app.use('/proxy', proxy('www.google.com', {
https: true
}));
You can copy the host HTTP header to the proxied express server using the preserveHostHdr
option.
app.use('/proxy', proxy('www.google.com', {
preserveHostHdr: true
}));
The parseReqBody
option allows you to control parsing the request body.
For example, disabling body parsing is useful for large uploads where it would be inefficient
to hold the data in memory.
This defaults to true in order to preserve legacy behavior.
When false, no action will be taken on the body and accordingly req.body
will no longer be set.
Note that setting this to false overrides reqAsBuffer
and reqBodyEncoding
below.
app.use('/proxy', proxy('www.google.com', {
parseReqBody: false
}));
Note: this is an experimental feature. ymmv
The reqAsBuffer
option allows you to ensure the req body is encoded as a Node
Buffer
when sending a proxied request. Any value for this is truthy.
This defaults to to false in order to preserve legacy behavior. Note that
the value of reqBodyEnconding
is used as the encoding when coercing strings
(and stringified JSON) to Buffer.
Ignored if parseReqBody
is set to false.
app.use('/proxy', proxy('www.google.com', {
reqAsBuffer: true
}));
Encoding used to decode request body. Defaults to utf-8
.
Use null
to preserve as Buffer when proxied request body is a Buffer. (e.g image upload)
Accept any values supported by raw-body.
The same encoding is used in the intercept method.
Ignored if parseReqBody
is set to false.
app.use('/post', proxy('httpbin.org', {
reqBodyEncoding: null
}));
By default, node does not express a timeout on connections. Use timeout option to impose a specific timeout. Timed-out requests will respond with 504 status code and a X-Timeout-Reason header.
app.use('/', proxy('httpbin.org', {
timeout: 2000 // in milliseconds, two seconds
}));
The node-debug module is used to provide a trace debugging capability.
DEBUG=express-http-proxy npm run YOUR_PROGRAM
DEBUG=express-http-proxy npm run YOUR_PROGRAM | grep 'express-http-proxy' # to filter down to just these messages
Will trace the execution of the express-http-proxy module in order to aide debugging.
decorateRequest
has been REMOVED, and will generate an error when called. See proxyReqOptDecorator
and proxyReqBodyDecorator
.
Resolution: Most authors will simply need to change the method name for their decorateRequest method; if author was decorating reqOpts and reqBody in the same method, this will need to be split up.
intercept
has been REMOVED, and will generate an error when called. See userResDecorator
.
Resolution: Most authors will simply need to change the method name from intercept
to userResDecorator
, and exit the method by returning the value, rather than passing it to a callback. E.g.:
Before:
app.use('/proxy', proxy('www.google.com', {
intercept: function(proxyRes, proxyResData, userReq, userRes, cb) {
data = JSON.parse(proxyResData.toString('utf8'));
data.newProperty = 'exciting data';
cb(null, JSON.stringify(data));
}
}));
Now:
app.use('/proxy', proxy('www.google.com', {
userResDecorator: function(proxyRes, proxyResData, userReq, userRes) {
data = JSON.parse(proxyResData.toString('utf8'));
data.newProperty = 'exciting data';
return JSON.stringify(data);
}
}));
forwardPath
and forwardPathAsync
have been DEPRECATED and will generate a warning when called. See proxyReqPathResolver
.
Resolution: Simple update the name of either forwardPath
or forwardPathAsync
to proxyReqPathResolver
.
When your proxy server responds with an error, express-http-proxy returns a response with the same status code. See test/catchingErrors
for syntax details.
When your proxy server times out, express-http-proxy will continue to wait indefinitely for a response, unless you define a timeout
as described above.
The library will automatically use https if the provided path has 'https://' or ':443'. You may also set option https
to true to always use https.
You can use proxyReqOptDecorator
to ammend any auth or challenge headers required to succeed https.
You can use the ability to decorate the proxy request prior to sending. See proxyReqOptDecorator
for more details.
app.use('/', proxy('internalhost.example.com', {
proxyReqOptDecorator: function(proxyReqOpts, originalReq) {
proxyReqOpts.ca = [caCert, intermediaryCert]
return proxyReqOpts;
}
})
You can set the rejectUnauthorized
value in proxy request options prior to sending. See proxyReqOptDecorator
for more details.
app.use('/', proxy('internalhost.example.com', {
proxyReqOptDecorator: function(proxyReqOpts, originalReq) {
proxyReqOpts.rejectUnauthorized = false
return proxyReqOpts;
}
}))
Release | Notes |
---|---|
1.0.0 | Initial release |
MIT
FAQs
http proxy middleware for express
We found that express-http-proxy-2 demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.
Security News
A Stanford study reveals 9.5% of engineers contribute almost nothing, costing tech $90B annually, with remote work fueling the rise of "ghost engineers."
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.