express-http-proxy
Advanced tools
Comparing version 1.1.0 to 1.2.0
@@ -13,3 +13,3 @@ 'use strict'; | ||
return function(rspData, res) { | ||
return (isResGzipped(res)) ? zlib[method](rspData) : rspData; | ||
return (isResGzipped(res) && rspData.length) ? zlib[method](rspData) : rspData; | ||
}; | ||
@@ -16,0 +16,0 @@ } |
@@ -13,6 +13,7 @@ 'use strict'; | ||
.then(function(shouldSkipToNext) { | ||
return (shouldSkipToNext) ? Promise.reject() : Promise.resolve(container); | ||
}); | ||
return (shouldSkipToNext) ? container.user.next() : Promise.resolve(container); | ||
}) | ||
.catch(Promise.reject); | ||
} | ||
module.exports = maybeSkipToNextHandler; |
@@ -7,3 +7,2 @@ 'use strict'; | ||
var req = Container.user.req; | ||
var res = Container.user.res; | ||
var bodyContent = Container.proxy.bodyContent; | ||
@@ -16,2 +15,7 @@ var reqOpt = Container.proxy.reqBuilder; | ||
var proxyReq = protocol.request(reqOpt, function(rsp) { | ||
if (options.stream) { | ||
Container.proxy.res = rsp; | ||
return resolve(Container); | ||
} | ||
var chunks = []; | ||
@@ -35,15 +39,3 @@ rsp.on('data', function(chunk) { chunks.push(chunk); }); | ||
// TODO: do reject here and handle this later on | ||
proxyReq.on('error', function(err) { | ||
// reject(error); | ||
if (err.code === 'ECONNRESET') { | ||
res.setHeader('X-Timout-Reason', | ||
'express-http-proxy timed out your request after ' + | ||
options.timeout + 'ms.'); | ||
res.writeHead(504, {'Content-Type': 'text/plain'}); | ||
res.end(); | ||
} else { | ||
reject(err); | ||
} | ||
}); | ||
proxyReq.on('error', reject); | ||
@@ -62,3 +54,14 @@ // this guy should go elsewhere, down the chain | ||
if (bodyContent.length) { | ||
proxyReq.write(bodyContent); | ||
var body = bodyContent; | ||
var contentType = proxyReq.getHeader('Content-Type'); | ||
if (contentType === 'x-www-form-urlencoded' || contentType === 'application/x-www-form-urlencoded') { | ||
try { | ||
var params = JSON.parse(body); | ||
body = Object.keys(params).map(function(k) { return k + '=' + params[k]; }).join('&'); | ||
} catch (e) { | ||
// bodyContent is not json-format | ||
} | ||
} | ||
proxyReq.setHeader('Content-Length', Buffer.byteLength(body)); | ||
proxyReq.write(body); | ||
} | ||
@@ -65,0 +68,0 @@ proxyReq.end(); |
@@ -5,3 +5,7 @@ 'use strict'; | ||
if (!Container.user.res.headersSent) { | ||
Container.user.res.send(Container.proxy.resData); | ||
if (Container.options.stream) { | ||
Container.proxy.res.pipe(Container.user.res); | ||
} else { | ||
Container.user.res.send(Container.proxy.resData); | ||
} | ||
} | ||
@@ -8,0 +12,0 @@ return Promise.resolve(Container); |
25
index.js
'use strict'; | ||
// ROADMAP: Major refactoring April 2017 | ||
// It would be easier to follow if we extract to simpler functions, and used | ||
// a standard, step-wise set of filters with clearer edges and borders. It | ||
// would be more useful if authors could use Promises for all over-rideable | ||
// steps. | ||
// * Breaks proxying into a series of discrete steps, many of which can be swapped out by authors. | ||
// * Uses Promises to support async. | ||
// * Uses a quasi-Global called Container to tidy up the argument passing between the major work-flow steps. | ||
// complete: Break workflow into composable steps without changing them much | ||
// complete: extract workflow methods from main file | ||
// complete: cleanup options interface | ||
// complete: change hook names to be different than the workflow steps. | ||
// *: cleanup host is processed twice | ||
// *: cleanup workflow methods so they all present as over-rideable thennables | ||
// *: Update/add tests to unit test workflow steps independently | ||
// complete: update docs and release | ||
@@ -28,2 +18,3 @@ var ScopeContainer = require('./lib/scopeContainer'); | ||
var decorateUserResHeaders = require('./app/steps/decorateUserResHeaders'); | ||
var handleProxyErrors = require('./app/steps/handleProxyErrors'); | ||
var maybeSkipToNextHandler = require('./app/steps/maybeSkipToNextHandler'); | ||
@@ -59,4 +50,10 @@ var prepareProxyReq = require('./app/steps/prepareProxyReq'); | ||
.then(sendUserRes) | ||
.catch(next); | ||
.catch(function(err) { | ||
var resolver = (container.options.proxyErrorHandler) ? | ||
container.options.proxyErrorHandler : | ||
handleProxyErrors; | ||
resolver(err, res, next); | ||
}); | ||
}; | ||
}; | ||
@@ -41,2 +41,3 @@ 'use strict'; | ||
userResHeaderDecorator: options.userResHeaderDecorator, | ||
proxyErrorHandler: options.proxyErrorHandler, | ||
filter: options.filter || defaultFilter, | ||
@@ -57,2 +58,7 @@ // For backwards compatability, we default to legacy behavior for newly added settings. | ||
// automatically opt into stream mode if no response modifiers are specified | ||
resolved.stream = !resolved.skipToNextHandlerFilter && | ||
!resolved.userResDecorator && | ||
!resolved.userResHeaderDecorator; | ||
debug(resolved); | ||
@@ -59,0 +65,0 @@ return resolved; |
{ | ||
"name": "express-http-proxy", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "http proxy middleware for express", | ||
@@ -5,0 +5,0 @@ "engines": { |
@@ -5,4 +5,2 @@ # 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) [![Dependency Status](https://gemnasium.com/villadora/express-http-proxy.svg)](https://gemnasium.com/villadora/express-http-proxy) | ||
## NOTE: version 1.0.0 released: breaking changes, transition guide at bottom of doc. | ||
## Install | ||
@@ -29,5 +27,14 @@ | ||
### 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. | ||
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```; | ||
@@ -51,2 +58,22 @@ | ||
### 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); | ||
``` | ||
### Options | ||
@@ -56,2 +83,7 @@ | ||
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```. | ||
Provide a proxyReqPathResolver function if you'd like to | ||
@@ -76,3 +108,4 @@ operate on the path before issuing the proxy request. Use a Promise for async | ||
setTimeout(function () { // simulate async | ||
var resolvedPathValue = "http://google.com"; | ||
// in this case I expect a request to /proxy => localhost:12345/a/different/path | ||
var resolvedPathValue = "/a/different/path"; | ||
resolve(resolvedPathValue); | ||
@@ -213,3 +246,3 @@ }, 200); | ||
#### skipToNextHandlerFilter(supports Promise form) | ||
(experimental: this interface may change in upcoming versions) | ||
(experimental: this interface may change in upcoming versions) | ||
@@ -226,4 +259,36 @@ 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. | ||
### 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```. | ||
```js | ||
// 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) | ||
@@ -317,4 +382,6 @@ | ||
This defaults to true in order to preserve legacy behavior. | ||
##### 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. | ||
@@ -366,7 +433,6 @@ | ||
#### timeout | ||
By default, node does not express a timeout on connections. | ||
Use timeout option to impose a specific 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. | ||
@@ -466,4 +532,16 @@ | ||
### 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. | ||
```js | ||
app.use('/', proxy('internalhost.example.com', { | ||
proxyReqOptDecorator: function(proxyReqOpts, originalReq) { | ||
proxyReqOpts.rejectUnauthorized = false | ||
return proxyReqOpts; | ||
} | ||
}) | ||
``` | ||
## Release Notes | ||
@@ -473,3 +551,4 @@ | ||
| --- | --- | | ||
| 1.0.8 | Add step to allow response headers to be modified. | ||
| 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. | | ||
@@ -476,0 +555,0 @@ | 1.0.6 | Fixes preserveHostHdr not working, skip userResDecorator on 304, add maybeSkipToNext, test improvements and cleanup. | |
@@ -53,3 +53,3 @@ 'use strict'; | ||
.end(function(err) { | ||
fs.unlink(filename); | ||
fs.unlinkSync(filename); | ||
// This test is both broken and I think unnecessary. | ||
@@ -85,3 +85,3 @@ // Its broken because http.bin no longer supports /post, but this test assertion is based on the old | ||
.end(function(err) { | ||
fs.unlink(filename); | ||
fs.unlinkSync(filename); | ||
// This test is both broken and I think unnecessary. | ||
@@ -110,3 +110,3 @@ // Its broken because http.bin no longer supports /post, but this test assertion is based on the old | ||
.end(function(err) { | ||
fs.unlink(filename); | ||
fs.unlinkSync(filename); | ||
assert(err === null); | ||
@@ -113,0 +113,0 @@ // This test is both broken and I think unnecessary. |
@@ -16,4 +16,7 @@ 'use strict'; | ||
// TODO: This seems like a bug factory. We will have intermittent port conflicts, yeah? | ||
var firstPort = Math.floor(Math.random() * 10000); | ||
var secondPort = Math.floor(Math.random() * 10000); | ||
function randomNumberInPortRange() { | ||
return Math.floor(Math.random() * 48000) + 1024; | ||
} | ||
var firstPort = randomNumberInPortRange(); | ||
var secondPort = randomNumberInPortRange(); | ||
@@ -20,0 +23,0 @@ var hostFn = function(req) { |
@@ -33,7 +33,5 @@ var express = require('express'); | ||
.get('/') | ||
.expect(408) | ||
.expect('X-Timout-Reason', 'express-http-proxy timed out your request after 100 ms.') | ||
.end(function() { | ||
done(); | ||
}); | ||
.expect(504) | ||
.expect('X-Timeout-Reason', 'express-http-proxy reset the request.') | ||
.end(done); | ||
} | ||
@@ -40,0 +38,0 @@ |
var assert = require('assert'); | ||
var express = require('express'); | ||
var bodyParser = require('body-parser'); | ||
var request = require('supertest'); | ||
@@ -14,2 +15,4 @@ var proxy = require('../'); | ||
app = express(); | ||
app.use(bodyParser.json()); | ||
app.use(bodyParser.urlencoded({ extended: false })); | ||
app.use(proxy('httpbin.org')); | ||
@@ -41,2 +44,13 @@ }); | ||
it('test proxy post by x-www-form-urlencoded', function(done) { | ||
request(app) | ||
.post('/post') | ||
.set('Content-Type', 'application/x-www-form-urlencoded') | ||
.send('mypost=hello') | ||
.end(function(err, res) { | ||
assert.equal(JSON.stringify(res.body.form), '{"mypost":"hello"}'); | ||
done(err); | ||
}); | ||
}); | ||
it('test proxy put', function(done) { | ||
@@ -43,0 +57,0 @@ request(app) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
99334
54
2222
566
5