Giter Club home page Giter Club logo

express-http-proxy's Introduction

express-http-proxy NPM version Build Status

Express middleware to proxy request to another host and pass response back to original caller.

Install

$ npm install express-http-proxy --save

Usage

proxy(host, options);

Example:

To proxy URLS starting with '/proxy' to the host 'www.google.com':

var proxy = require('express-http-proxy');
var app = require('express')();

app.use('/proxy', proxy('www.google.com'));

Streaming

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.

Promises

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');

Host

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));

Notie: Host is only the host name. Any params after in url will be ignored. For http://google.com/myPath`, myPathwill be ignored because the host name isgoogle.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', 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.

app.use(bodyParser.foo(bar))

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

  },
}))

Options

proxyReqPathResolver (supports Promises)

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);
    });
  }
}));

forwardPath

DEPRECATED. See proxyReqPathResolver

forwardPathAsync

DEPRECATED. See proxyReqPathResolver

filter (supports Promises)

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 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 proxy and call next().

userResDecorator (was: intercept) (supports Promise)

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);
    });
  }
}));
304 - Not Modified

When your proxied service returns 304, not modified, this step will be skipped, since there is no body to decorate.

exploiting references

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.

gzip responses

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.

limit

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'
}));

memoizeHost

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.

userResHeaderDecorator

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;
  }
}));

decorateRequest

REMOVED: See proxyReqOptDecorator and proxyReqBodyDecorator.

skipToNextHandlerFilter(supports Promise form)

(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;
  }
}));

proxyErrorHandler

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); }
    }
}}));

proxyReqOptDecorator (supports Promise form)

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 #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);
    })
  }
}));

proxyReqBodyDecorator (supports Promise form)

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);
      });
    })
  }
}));

https

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
}));

preserveHostHdr

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
}));

parseReqBody

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.

Note: this setting is required for binary uploads. A future version of this library may handle this for you.

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
}));

reqAsBuffer

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
}));

reqBodyEncoding

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
}));

timeout

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
}));

Trace debugging

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.

Upgrade to 1.0, transition guide and breaking changes

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 errors occur on your proxy server

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.

Questions

Q: Does it support https proxy?

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.

Q: How can I support non-standard certificate chains?

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;
  }
})

Q: How to ignore self-signed certificates ?

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

Release Notes
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.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.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.
1.0.5 Minor documentation and test patches
1.0.4 Minor documentation, test, and package fixes
1.0.3 Fixes 'limit option is not taken into account
1.0.2 Minor docs corrections.
1.0.1 Minor docs adjustments.
1.0.0 Major revision.
REMOVE decorateRequest, ADD proxyReqOptDecorator and proxyReqBodyDecorator.
REMOVE intercept, ADD userResDecorator
userResDecorator supports a Promise form for async operations.
General cleanup of structure and application of hooks. Documentation improvements. Update all dependencies. Re-organize code as a series of workflow steps, each (potentially) supporting a promise, and creating a reusable pattern for future development.
0.11.0 Allow author to prevent host from being memoized between requests. General program cleanup.
0.10.1 Fixed issue where 'body encoding' was being incorrectly set to the character encoding.
Dropped explicit support for node 0.10.
Intercept can now deal with gziped responses.
Author can now 'force https', even if the original request is over http.
Do not call next after ECONNRESET catch.
0.10.0 Fix regression in forwardPath implementation.
0.9.1 Documentation updates. Set 'Accept-Encoding' header to match bodyEncoding.
0.9.0 Better handling for request body when body is JSON.
0.8.0 Features: add forwardPathAsync option
Updates: modernize dependencies
Fixes: Exceptions parsing proxied response causes error: Can't set headers after they are sent. (#111)
If client request aborts, proxied request is aborted too (#107)
0.7.4 Move jscs to devDependencies to avoid conflict with nsp.
0.7.3 Adds a timeout option. Code organization and small bug fixes.
0.7.2 Collecting many minor documentation and test improvements.
0.4.0 Signature of intercept callback changed from function(data, req, res, callback) to function(rsp, data, req, res, callback) where rsp is the original response from the target

Licence

MIT

express-http-proxy's People

Contributors

brandon-leapyear avatar bridgear avatar cesine avatar codegagan avatar davebecker3 avatar davidnormo avatar declspec avatar dependabot[bot] avatar granjow avatar hagai26 avatar himdel avatar kapouer avatar kidkarolis avatar ktonon avatar legneato avatar lennym avatar liambennett avatar matthieusieben avatar monkpow avatar nik-blue-lava avatar raphaeleidus avatar salguerooo avatar sap9433 avatar sebastienroul avatar seungha-kim avatar simono avatar simplgy avatar tuxpiper avatar villadora avatar voor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

express-http-proxy's Issues

Intercepting 304 responses

Hey,

I'm unsure if this is a problem with this package, or I'm just suppose to approach the problem differently.

If the target responds with a 304, the interceptor data property is empty.

proxy('http://cacheable.target.com', {
  intercept: function (proxyRes, data, req, res, next) {
    // 1st time is a full buffer <Buffer 1f 8b 08 00 00 00 00 00 00 ...>
    // 2nd time (when responding with a 304) it's an empty buffer <Buffer >
    console.log(data);
  }
});

Chokes on trailing slash

I think it might choke in the case where there's a port with a trailing slash specified as host.

Eg:

app.use(proxy('http://fantasticmango:4242/', options));

Throws:

net.js:901
  throw new RangeError('port should be > 0 and < 65536: ' + port);
        ^
RangeError: port should be > 0 and < 65536: 0
  at Socket.connect (net.js:901:13)
  at Agent.exports.connect.exports.createConnection (net.js:92:35)

This is a bummer because url.parse() puts a trailing slash in the href property. To do it manually and generically the user needs to use hostname and port joined by a :, but only when port is present. Easier to strip the tail slash off maybe.

Want a PR to fix this in the library, or am I missing something?

I am using app code like this to work around (CS sorry):

if config.api.href[config.api.href.length - 1] is '/'
  safeHref = config.api.href.substr 0, config.api.href.length - 1
app.use proxy safeHref, options

Failed to convert header keys to lower case due to field name conflict: accept-encoding

I get this error when I use express-http-proxy >= 0.9.1:

Failed to convert header keys to lower case due to field name conflict: accept-encoding
Error: Failed to convert header keys to lower case due to field name conflict: accept-encoding
    at foo/node_modules/nock/lib/common.js:210:13
    at foo/node_modules/lodash/lodash.js:4332:15
    at baseForOwn (foo/node_modules/lodash/lodash.js:2731:24)
    at Function.forOwn (foo/node_modules/lodash/lodash.js:12042:24)
    at Object.headersFieldNamesToLowerCase (foo/node_modules/nock/lib/common.js:207:5)
    at RequestOverrider (foo/node_modules/nock/lib/request_overrider.js:111:30)
    at new OverriddenClientRequest (foo/node_modules/nock/lib/intercept.js:240:23)
    at foo/node_modules/nock/lib/intercept.js:363:13
    at Object.module.request (foo/node_modules/nock/lib/common.js:137:14)
    at runProxy (foo/node_modules/express-http-proxy/index.js:87:43)
    at invokeCallback (foo/node_modules/raw-body/index.js:262:16)
    at done (foo/node_modules/raw-body/index.js:251:7)
    at IncomingMessage.onEnd (foo/node_modules/raw-body/index.js:307:7)
    at emitNone (events.js:86:13)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:975:12)

I see the release notes for 0.9.1 says "Set 'Accept-Encoding' header to match bodyEncoding". Perhaps something goes wrong in relation to this?

Does decorateRequest support promises?

I would like to add some parameters to my proxies requests but these parameters are resolved asynchronously by functions that return promises. Is there any support for setting things like decorateRequest but in a way that will be resolved asynchronously?

Proxy cannot handle large files

Thanks for the great Express add-on!

When trying to send a large file through the proxy (more than 1 MB), an error event is thrown:

Error: request entity too large
at makeError (.../node_modules/express-http-proxy/node_modules/raw-body/index.js:184:15)
at module.exports (.../node_modules/express-http-proxy/node_modules/raw-body/index.js:40:15)
at handleProxy (.../node_modules/express-http-proxy/index.js:54:5)
at Layer.handle [as handle_request] (.../node_modules/express/lib/router/layer.js:82:5)
at trim_prefix (.../node_modules/express/lib/router/index.js:302:13)
at .../node_modules/express/lib/router/index.js:270:7
at Function.proto.process_params (.../node_modules/express/lib/router/index.js:321:12)
at next (.../node_modules/express/lib/router/index.js:261:10)
at serveStatic (.../node_modules/express/node_modules/serve-static/index.js:59:14)
at Layer.handle [as handle_request] (.../node_modules/express/lib/router/layer.js:82:5)

Could you use streams instead of reading the entire body into a buffer?

Override redirect location set by proxied host

I'm currently using this library to intercept and inject some headers for my angular app, the proxy server is listening on port 3000 and forwarding to the host on port 8080

There are some scenarios where the proxied host responds with a redirect to a URL like:

Note that the port is not quite what I need - I need it to be 3000.

As far I can tell, from looking at the source; this is not currently possible.

Questions:

  1. Am I correct?
  2. Has this requirement been raised before?
  3. Does this seem like something reasonable to be implemented?

Let me know if I can provide more information.

Support for ws protocol?

Hi, great job on this proxy module.

I'm trying to add the proxy for websocket protocols but it does not work for you guys. Is there any plans to add that?

POST with json in the body does not work.

When trying to proxy POST requests with json data in the body the server receives an empty body.

I managed to work around it with:
app.use('/api..., proxy(be, { decorateRequest: function(reqOpt, req) { reqOpt.bodyContent = JSON.stringify(reqOpt.bodyContent) return reqOpt; } }));
It seems that bodyContent is an object instead of a string ...

Dynamic host

Hi,

I can't make my host dynamic, i use this code snippet:

var proxy = require('express-http-proxy');
var app = require('express')();
app.use('/proxy', 
      function(req,res) {
          return proxy(req.session.dynamic_host);
      }
).listen(8080);
console.log("listening on port 8080")

The host is in the session params.

With this code, it try to download the page until the timeout appears, it seems to be a blocking some where but i have any idea how to solve it.

Please can someone help there ?

Disable/destroy the proxy

Probably not the right place for this, but how do I disable/destroy the proxy?
Or maybe even something that resembles this:

app.get('/make', function (req, res) {

    subdomain = 'sub1'; //From DB
    originalSite = 'www.example.com';
    app.use(vhost(subdomain+'.mysite.com', proxy(originalSite)));
    res.send("Created");
});

app.get('/destroy', function (req, res) {

   ????? 
});

Node 5.6

I am not able to get an HTTPS proxy to work since updating to Node 5.6. Any chance I could get some help with this?

Posting to a different host with a specific port

I'm attempting proxy a post to a different host with a specific port number but I'm getting the following error.

net.js:927
throw new RangeError('port should be >= 0 and < 65536: ' + port);
^

RangeError: port should be >= 0 and < 65536: 3002path1/path2
at lookupAndConnect (net.js:927:13)
at Socket.connect (net.js:904:5)
at Agent.exports.connect.exports.createConnection (net.js:63:35)
at Agent.createSocket (_http_agent.js:174:16)
at Agent.addRequest (_http_agent.js:143:23)
at new ClientRequest (_http_client.js:133:16)
at Object.exports.request (http.js:31:10)
at c:\Users\jeff.pierson\Source\ReadyTouch\uk\gmaweb\node_modules\express-http-proxy\index.js:96:50
at IncomingMessage.onEnd (c:\MYWEBAPPDIR\node_modules\express-http-proxy\node_modules\raw-body\index.js:149:7)
at IncomingMessage.g (events.js:260:16)

Notice the text 3002path1/path2 which makes it look like the port number is being smashed together with the path when constructing the URL.

Usage example having error

In Usage section, you mentioned one example to intercept proxy response. It give following error.

ReferenceError: wrap is not defined

at following lines.

req.bodyContent = wrap(req.bodyContent);

Can you please fix it? also let me know if I'm doing something wrong here.

Evaluate host() function

Hi guys,

I'm having the following situation over here: based on the incoming request I want to decide to proxy to host x or y. So ideally I want to evaluate the host(req) function every time there is request to my proxy instead of evaluating only the first time.

I've looked into the source code and see multiple solutions. One of them is removing this line of code (if statement to check if the host is already determined in a previous request), this won't break tests. Instead of removing we can also extend the if statement to make it a little smarter.

I'm curious about your vision on this topic.

port passed in proxy(host) string is ignored

A custom port value passed to proxy() like proxy('http://localhost:5678') is completely ignored, even though some layers of the code seem to expect or support this.

parseHost has logic for returning an object containing a port inside the host string.

However, the exported proxy() function splits apart the passed-in host string and overwrites the input host with whatever is on the left side of the host:port prior to the point where parseHost() is called.

The port on the right-hand side of the original input is then never read - the reqOpt object gets assigned a field port: options.port || parsedHost.port, but since the original input host was overwritten after the string splitting, the parsedHost.port never contains anything but the default port for the protocol.

This results in a usage like proxy('http://localhost:8080') in sending proxied requests to port 80 on localhost, not 8080.

TLS support?

Hi,

thanks for the wonderful project. This module is saving me a lot of time.

I was wondering if there is a way to reverse proxy to a https server?

thanks

Proxy works for GET but POST loses body

I have configured a proxy that works perfectly for GET requests, but the request JSON body doesn't get through on POST requests.

app.use('/cart', proxy(GS_API_URL, {
  reqAsBuffer: true,
  reqBodyEncoding: null,
  decorateRequest: function(proxyReq, originalReq) {
    proxyReq.headers = {
      'Authorization': 'Bearer ' + originalReq.session.gsApiToken
    };
    return proxyReq;
  },
  forwardPath: function(req) {
    return '/v1/cart' + req.path;
  },
  intercept: function(targetResponse, responseData, req, res, callback) {
    if (targetResponse.statusCode === 401) {
      req.session.destroy();
      return callback(new gsErrors.UnauthorizedError('API token rejected while fetching cart contents'));
    }
    return callback(null, responseData);
  }
}));

I recently added the reqAsBuffer and reqBodyEncoding options after seeing #114, but that doesn't seem to help. I have tried this with only reqAsBuffer set and without either of those two set. I do have body-parser running on this app, but that middleware is after the proxy.

At the destination server, I'm running body-parser on this request and it sets req.body to an empty object. I previously had this API running on the Express server where I'm currently building the proxy, so I'm certain the front-end is sending the body as intended. Can you see why I'm not able to get the JSON body through the proxy to my other Express server?

UPDATE: I should clarify that the GET request I have working through the proxy does not have a body. The problem might be that the body is not getting through regardless of the method.

Access to reponse headers

Hello villadora,

How can I get access to the response headers of the response sent by the target server. I need to authenticate the response from the target based on response headers. Can we change the code to something like:

 if (intercept) {
            intercept(rsp, rspData, req, res, function(err, rspd, sent) {
              if (err) {

This will mimic the request modules callback signature too, which is request(requestOpts, function (err, response, body)

Test for https fails if proxy is a function

While not in the documentation, the code allows the 'proxy' to be a function or a string - in our case the code is much cleaner (since we don't know the final proxy protocol or url until the request comes in.

However, the check for https does not work properly because the test does not call the function.

Line 19: var ishttps = /^https/.test(host);

Delaying the https determination until after the method is called and returns a url would be reasonable.

Intercept fallthrough

Presently, there are two options if using intercept: return/send the content, or throw an error. The third option that is not available is to continue the middleware chain normally, taking no action.

I dont think I ought to have to compose Error-middleware to handle failure cases of http-proxy; intercept should let me simply call next() to continue the processing chain.

If I do try to call a naked next(), say- on this line- I get TypeError: Cannot read property 'length' of undefined. This should instead continue the middleware chain normally instead, I feel.

Possible to add query parameters to proxy request?

Is it possible to add a string of query parameters to the end of the path of the proxy request before sending?

I've tried adding ProxyReq.query in decorateRequest and formatting the parameters as a JSON object and also tried as a string, but doesn't look like decorateRequest handles query parameters that way.

Just wanted to see if there was a way to do it. Thanks!

Edit: I ended up working it out by adding the query parameters to proxyReq.path in decorateRequest.

DEPTH_ZERO_SELF_SIGNED_CERT error

Can be avoided by:

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" # Avoids DEPTH_ZERO_SELF_SIGNED_CERT error for self-signed certs

But maybe there is a better option.
I didn't find the option in your readme.

There is an option called rejectUnauthorized.
https://nodejs.org/api/tls.html

Seems that it's the option to look into:

var req = (ssl ? https : http).request({ rejectUnauthorized������������������������������������������������������: false })

You'll have to make an opiton for this one

protocol is not resolved correctly if host is function

You can easily detect this from the code.
ishttps is resolved in constructor.
however if "host" is function, it should be resolved in handleProxy just like the hostname - but its not.

the proxy basically does not allow me to dynamically decide the host and support https at the same time.

major issue, might have to use some other library.

Exceptions parsing proxied response causes error: Can't set headers after they are sent

If an exception occurs while parsing the proxied response, a 'Can't set headers after they are sent' error is thrown. This also causes my node server to exit.

The stack trace looks like this:

Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
    at ServerResponse.header (/Users/jenniferrullmann/code/proj/node_modules/express/lib/response.js:718:10)
    at ServerResponse.send (/Users/jenniferrullmann/code/proj/node_modules/express/lib/response.js:176:10)
    at /Users/jenniferrullmann/code/proj/node_modules/express-http-proxy/index.js:147:21
    at app.use.proxy.intercept (/Users/jenniferrullmann/code/proj/server.js:47:7)
    at IncomingMessage.<anonymous> (/Users/jenniferrullmann/code/proj/node_modules/express-http-proxy/index.js:113:13)
    at emitNone (events.js:72:20)
    at IncomingMessage.emit (events.js:166:7)
    at endReadableNT (_stream_readable.js:905:12)
    at doNTCallback2 (node.js:441:9)

If I modify https://github.com/villadora/express-http-proxy/blob/master/index.js#L120 code to check if the headers have been set:

if (!res.headersSent) {
    res.send(rspData);
}

then I can see that an error occured parsing the proxied response body:

Error: Parse Error
    at Error (native)
    at TLSSocket.socketOnData (_http_client.js:305:20)
    at emitOne (events.js:77:13)
    at TLSSocket.emit (events.js:169:7)
    at readableAddChunk (_stream_readable.js:146:16)
    at TLSSocket.Readable.push (_stream_readable.js:110:10)
    at TLSWrap.onread (net.js:523:20)

Here's a SO question that goes into detail about why this happens. It appears that something upstream of express-http-proxy is calling writeHead earlier than usual because of this parsing error.

For now, I'm working around the problem by intercepting the response in my server, so my node server stays up and I can see the root exception.

  // Proxy requests to the upstream API
  app.use('/api', proxy(upstream_api_url, {
    intercept: function(rsp, data, req, res, callback) {
      callback(null, data, res.headersSent);
    }
  }))

Seems like adding a check for headersSent before calling res.send would fix the issue, and I could remove the silly workaround.

querystring.stringify for post data

I needed to add some code to forward postdata properly.
after getRawBody the bodyContent is an object.
maybe because I used the bodyParser middlewhare?

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
    extended: false
}));

my solution was changing line 87 to:

bodyContent = require('querystring').stringify(reqOpt.bodyContent);

but maybe this will only work with body parser.
if it can be a tring before we can do

bodyContent = typeof bodyContent == 'string' ? bodyContent : require('querystring').stringify(reqOpt.bodyContent);

decorateRequest options

Hi,

Thanks for the great module!

I'm trying to use decorateRequest to add an X-Forwarded-For header, so the back-end service has some idea of the original endpoint. It looks like the IP address of the request (req.ip) isn't available as an option in reqOpt:

  var reqOpt = {
    hostname: (typeof host == 'function') ? host(req) : host.toString(),
    port: port,
    headers: hds,
    method: req.method,
    path: path,
    bodyContent: bodyContent,
    params: req.params
  };

Would it be possible to add this in ? Or perhaps there is a better way to set X-Forwarded-For ?
Thanks!

Timeout when using docker container

Hi,

I don't know what it is but the requests i am processing are not getting forwarded to another docker container. The request is always stopping due to a timeout.

app.use('/proxy', proxy('secondhost:8080', {
   forwardPath: function(req, res) {
    return require('url').parse(req.url).path;
  }
}));

secondhost is written in the /etc/hosts file by docker-compose.

When I attach to the container docker exec -i -t first_container bash I can ping the second host and make http requests with curl.

What is wrong here?

nsp complains about jscs dependency security vulnerabilities

My project is using nsp and getting this complaint. Is jscs needed other than devDependencies?

11:37:10 1 vulnerabilities found

│ Regular Expression Denial of Service │ Name │ minimatch │
│ Installed │ 2.0.10
│ Vulnerable │ <=3.0.1 │
│ Patched │ >=3.0.2 │
│ Path │ [email protected] > [email protected] > [email protected] > [email protected] > [email protected]

│ More Info │ https://nodesecurity.io/advisories/118

when I post json data to proxy, it alway return "Empty reply from server"?

But when I post same data to the third server, it return ok. how can I do?
1, post to the third server .
curl -X "POST" http://localhost:9000/v2/images -d '{"image": {}}'

  • Hostname was NOT found in DNS cache
  • Trying ::1...
  • connect to ::1 port 9000 failed: Connection refused
  • Trying 127.0.0.1...
  • Connected to localhost (127.0.0.1) port 9000 (#0)

    POST /v2/images HTTP/1.1
    User-Agent: curl/7.35.0
    Host: localhost:9000
    Content-type: application/json
    Accept:application/json
    Content-Length: 13

  • upload completely sent off: 13 out of 13 bytes
  • HTTP 1.0, assume close after body
    < HTTP/1.0 400 Bad Request
    < Date: Fri, 26 Sep 2014 14:52:05 GMT
    < Server: WSGIServer/0.1 Python/2.7.6
    < Content-Type: text/html; charset=UTF-8
    < Content-Length: 0
    <
  • Closing connection 0

2, post to proxy. it waits for a timeout.
curl -X "POST" http://localhost:3000/proxy/v2/images -d '{"image": {}}' -H "Content-type: application/json" -H "Accept:application/json" -v

  • Hostname was NOT found in DNS cache
  • Trying ::1...
  • connect to ::1 port 3000 failed: Connection refused
  • Trying 127.0.0.1...
  • Connected to localhost (127.0.0.1) port 3000 (#0)

    POST /proxy/v2/images HTTP/1.1
    User-Agent: curl/7.35.0
    Host: localhost:3000
    Content-type: application/json
    Accept:application/json
    Content-Length: 13

  • upload completely sent off: 13 out of 13 bytes
  • Empty reply from server
  • Connection #0 to host localhost left intact
    curl: (52) Empty reply from server

Connection timeout

Can you add options for connection timeout?
request.socket.setTimeout(60000); // 60 sec

I can see a lot of errors:
Error: connect ETIMEDOUT
at exports._errnoException (util.js:746:11)
at TCPConnectWrap.afterConnect as oncomplete

Thanks

How to handle 301 redirects?

Hi @villadora,

I'm having some troubles when trying to proxy a host containing a 301 redirect.

Basically, I'm redirect(as usual) to target's domain, leaving my localhost proxy environment.

You can use as example: http://globo.com

Thank you in advance.

question: proxying a multipart request changes file byte array data

Hi, I am trying to proxy a multipart file request to my Java Spring server but for some reason the file bytes that Spring receives is not the same as the file that was uploaded. For example, a file whose byte array length is 3000 is transformed to about 6000. Any ideas why that's happening ?

This is the snippet that does proxies the request:

  var apiProxy = proxy('http://localhost:8080', {
      forwardPath: function (req, res) {
          return '/api/v2/photos/upload';
      }
  });

  app.all("/api/upload", apiProxy);

Connection Close Issue

Maybe I am being ignorant. I am using this module for the first time today. But in all my tests, the "Connection" header is always "close". It should be "alive" as per HTTP 1.1

Am I doing something wrong?

proxy multipart request

Hi there,
I have used your lib to create a proxy server. I use body-parser and multer middleware for parsing request bodies.
everything is going fine except for the multipart requests. is there anyway to proxy these requests?

rewrite path before sending the request

I have a nodejs app running at localhost:4000 From here I want to proxy to localhost:15672 and authorize the user directly to his homepage. so localhost:4000/manage should show the response from localhost:15672/#/login/user/password

A simple app.get does work, but the URL contains credentials in pathname and so want to find an internal way to use it somehow. I am using express-http-proxy library already, but dont understand how to change pathname before sending the request. I have been stuck way too long, so a quick help will be greatly appreciated.

I have tried this but does not work:

`app.use('/manage', ensureAuthenticated, proxy('http://localhost:15672', {
decorateRequest: function(reqOpt){
reqOpt.hostname = 'localhost:15672';
reqOpt.path = '/manage/#/login/user/password';
return reqOpt;
}

}))
app.listen(4000);`

Node 4 Support

I am getting the below error when proxying to an HTTPS address and only when using Node 4:

Error: write EPROTO
    at Object.exports._errnoException (util.js:874:11)
    at exports._exceptionWithHostPort (util.js:897:20)
    at WriteWrap.afterWrite (net.js:763:14)

If I switch back to node 0.12, everything works fine. This may very well be an issue on my end but it is strange this only happens when I change to Node 4. Any ideas?

Support for decorateResponse?

I need to tack on a property to a json response within the proxy before it hits the client. I'm currently using forwardPath and decorateRequest but I'm not sure how is expected to be done. Is this something that I should be using intercept with?

Thanks for the library, it has been serving me great since I got it set up!

decorateRequest example gives errors

I am trying to run the following example from the docs and would like to modify it to reach to one of my Web applications. But this gives me errors like wrap not defined and if I remove wrap, it says
TypeError: Cannot read property 'length' of undefined. I know I am missing something, I am new to Nodejs and Express but any help will be great.

//Example code
app.use('/proxy', proxy('www.google.com', {
  decorateRequest: function(reqOpt, req) {
    reqOpt.headers['Content-Type'] = '';
    reqOpt.method = 'GET';
    reqOpt.bodyContent = wrap(req.bodyContent);
    return reqOpt;
  }
}));

Also my use case is to reach to 'https://localhost:3000/api/user' and add header as
reqOpt.headers['X-WEBAUTH-USER'] = 'test';
so I can reach to my local app bypassing the login page for user 'test'. Am I on the right track to use the decorateRequest function for this?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.