From e69a48e5352e67191e965feb6a203e5ab9f04fa6 Mon Sep 17 00:00:00 2001 From: James Nylen Date: Sun, 25 Jan 2015 04:48:22 -0600 Subject: [PATCH] Allow explicitly disabling tunneling for proxied https destinations Before, tunneling would default to false for http destinations and be forced to true for https destinations. Now, allow specifying `tunnel : false` to perform a regular GET request to HTTPS destinations. This is insecure, so make a note accordingly. Supersedes #1344 and fixes #1293. --- README.md | 16 ++++++++++++---- request.js | 13 ++++++------- tests/test-tunnel.js | 11 +++++++---- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5486dbeb7..2c0271cbd 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,10 @@ or other features, it is generally simpler to go with a straightforward HTTP proxy in this case. However, if you would like to force a tunneling proxy, you may set the `tunnel` option to `true`. +You can also make a standard proxied `http` request by explicitly setting +`tunnel : false`, but **note that this will allow the proxy to see the traffic +to/from the destination server**. + If you are using a tunneling proxy, you may set the `proxyHeaderWhiteList` to share certain headers with the proxy. @@ -609,10 +613,14 @@ The first argument can be either a `url` or an `options` object. The only requir * `httpSignature` - Options for the [HTTP Signature Scheme](https://github.com/joyent/node-http-signature/blob/master/http_signing.md) using [Joyent's library](https://github.com/joyent/node-http-signature). The `keyId` and `key` properties must be specified. See the docs for other options. * `localAddress` - Local interface to bind for network connections. * `gzip` - If `true`, add an `Accept-Encoding` header to request compressed content encodings from the server (if not already present) and decode supported content encodings in the response. **Note:** Automatic decoding of the response content is performed on the body data returned through `request` (both through the `request` stream and passed to the callback function) but is not performed on the `response` stream (available from the `response` event) which is the unmodified `http.IncomingMessage` object which may contain compressed data. See example below. -* `tunnel` - If `true`, then *always* use a tunneling proxy. If - `false` (default), then tunneling will only be used if the - destination is `https`, or if a previous request in the redirect - chain used a tunneling proxy. +* `tunnel` - controls the behavior of + [HTTP `CONNECT` tunneling](https://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_tunneling) + as follows: + * `undefined` (default) - `true` if the destination is `https` or a previous + request in the redirect chain used a tunneling proxy, `false` otherwise + * `true` - always tunnel to the destination by making a `CONNECT` request to + the proxy + * `false` - request the destination as a `GET` request. * `proxyHeaderWhiteList` - A whitelist of headers to send to a tunneling proxy. * `proxyHeaderExclusiveList` - A whitelist of headers to send diff --git a/request.js b/request.js index 8d7b1ce47..a4a5330a2 100644 --- a/request.js +++ b/request.js @@ -209,7 +209,6 @@ function rfc3986 (str) { } function Request (options) { - // if tunnel property of options was not given default to false // if given the method property in options, set property explicitMethod to true // extend the Request instance with any non-reserved properties @@ -228,13 +227,9 @@ function Request (options) { self.readable = true self.writable = true - if (typeof options.tunnel === 'undefined') { - options.tunnel = false - } if (options.method) { self.explicitMethod = true } - self.canTunnel = options.tunnel !== false && tunnel self.init(options) } @@ -263,7 +258,7 @@ Request.prototype.setupTunnel = function () { return false } - if (!self.tunnel && self.uri.protocol !== 'https:') { + if (!self.tunnel) { return false } @@ -384,7 +379,11 @@ Request.prototype.init = function (options) { } // Pass in `tunnel:true` to *always* tunnel through proxies - self.tunnel = !!options.tunnel + if (typeof options.tunnel === 'undefined') { + self.tunnel = (self.uri.protocol === 'https:') + } else { + self.tunnel = !!options.tunnel + } if (self.proxy) { self.setupTunnel() } diff --git a/tests/test-tunnel.js b/tests/test-tunnel.js index 1972f2481..51f2a2672 100644 --- a/tests/test-tunnel.js +++ b/tests/test-tunnel.js @@ -19,6 +19,8 @@ var events = [] cert : path.resolve(__dirname, 'ssl/ca/localhost.crt') } +// this is needed for 'https over http, tunnel=false' test +// from https://github.com/coolaj86/node-ssl-root-cas/blob/v1.1.9-beta/ssl-root-cas.js#L4267-L4281 var httpsOpts = https.globalAgent.options httpsOpts.ca = httpsOpts.ca || [] httpsOpts.ca.push(ca) @@ -181,9 +183,9 @@ runTest('https over http, tunnel=true', { runTest('https over http, tunnel=false', { url : ss.url, proxy : s.url, - tunnel : false // currently has no effect + tunnel : false }, [ - 'http connect to localhost:' + ss.port, + 'http proxy to https', 'https response', '200 https ok' ]) @@ -213,9 +215,10 @@ runTest('https over https, tunnel=true', { runTest('https over https, tunnel=false', { url : ss.url, proxy : ss.url, - tunnel : false // currently has no effect + tunnel : false, + pool : false // must disable pooling here or Node.js hangs }, [ - 'https connect to localhost:' + ss.port, + 'https proxy to https', 'https response', '200 https ok' ])