Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

proxy-chain

Package Overview
Dependencies
Maintainers
4
Versions
144
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

proxy-chain - npm Package Compare versions

Comparing version 0.1.31 to 0.1.32

build/handler_custom_response.js

2

build/handler_tunnel_tcp_chain.js

@@ -21,2 +21,4 @@ 'use strict';

// TODO: please rename this class to something else than "Handler", it makes it look like the class inherits from HandlerBase, which it doesn't
/**

@@ -23,0 +25,0 @@ * Represents a connection from source client to an external proxy using HTTP CONNECT tunnel, allows TCP connection.

109

build/server.js

@@ -40,2 +40,6 @@ 'use strict';

var _handler_custom_response = require('./handler_custom_response');
var _handler_custom_response2 = _interopRequireDefault(_handler_custom_response);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -113,10 +117,25 @@

* @param [options.port] Port where the server the server will listen. By default 8000.
* @param [options.prepareRequestFunction] Custom function to authenticate proxy requests
* and provide URL to chained upstream proxy. It accepts a single parameter which is an object:
* `{ connectionId: Number, request: Object, username: String, password: String, hostname: String, port: Number, isHttp: Boolean }`
* @param [options.prepareRequestFunction] Custom function to authenticate proxy requests,
* provide URL to chained upstream proxy or potentially provide function that generates a custom response to HTTP requests.
* It accepts a single parameter which is an object:
* ```{
* connectionId: Number,
* request: Object,
* username: String,
* password: String,
* hostname: String,
* port: Number,
* isHttp: Boolean
* }```
* and returns an object (or promise resolving to the object) with following form:
* `{ requestAuthentication: Boolean, upstreamProxyUrl: String }`
* ```{
* requestAuthentication: Boolean,
* upstreamProxyUrl: String,
* customResponseFunc: Function
* }```
* If `upstreamProxyUrl` is false-ish value, no upstream proxy is used.
* If `prepareRequestFunction` is not set, the proxy server will not require any authentication
* and will not use any upstream proxy.
* If `customResponseFunc` is set, it will be called to generate a custom response to the HTTP request.
* It should not be used together with `upstreamProxyUrl`.
* @param [options.authRealm] Realm used in the Proxy-Authenticate header and also in the 'Server' HTTP header. By default it's `ProxyChain`.

@@ -177,11 +196,19 @@ * @param [options.verbose] If true, the server logs

var handlerOptions = void 0;
this.prepareRequestHandling(request).then(function (handlerOpts) {
var handlerOpts = void 0;
this.prepareRequestHandling(request).then(function (result) {
handlerOpts = result;
handlerOpts.srcResponse = response;
handlerOptions = handlerOpts;
_this3.log(handlerOpts.id, 'Using HandlerForward');
var handler = new _handler_forward2.default(handlerOpts);
var handler = void 0;
if (handlerOpts.customResponseFunc) {
_this3.log(handlerOpts.id, 'Using HandlerCustomResponse');
handler = new _handler_custom_response2.default(handlerOpts);
} else {
_this3.log(handlerOpts.id, 'Using HandlerForward');
handler = new _handler_forward2.default(handlerOpts);
}
_this3.handlerRun(handler);
}).catch(function (err) {
_this3.failRequest(request, err, handlerOptions);
_this3.failRequest(request, err, handlerOpts);
});

@@ -202,6 +229,6 @@ }

var handlerOptions = void 0;
this.prepareRequestHandling(request).then(function (handlerOpts) {
handlerOptions = handlerOpts;
handlerOptions.srcHead = head;
var handlerOpts = void 0;
this.prepareRequestHandling(request).then(function (result) {
handlerOpts = result;
handlerOpts.srcHead = head;

@@ -219,3 +246,3 @@ var handler = void 0;

}).catch(function (err) {
_this4.failRequest(request, err, handlerOptions);
_this4.failRequest(request, err, handlerOpts);
});

@@ -239,3 +266,3 @@ }

var result = {
var handlerOpts = {
server: this,

@@ -249,3 +276,3 @@ id: ++this.lastHandlerId,

this.log(result.id, '!!! Handling ' + request.method + ' ' + request.url + ' HTTP/' + request.httpVersion);
this.log(handlerOpts.id, '!!! Handling ' + request.method + ' ' + request.url + ' HTTP/' + request.httpVersion);

@@ -262,3 +289,3 @@ var socket = request.socket;

// Note that request.url contains the "server.example.com:80" part
result.trgParsed = (0, _tools.parseHostHeader)(request.url);
handlerOpts.trgParsed = (0, _tools.parseHostHeader)(request.url);
_this5.stats.connectRequestCount++;

@@ -283,3 +310,3 @@ } else {

result.trgParsed = parsed;
handlerOpts.trgParsed = parsed;
isHttp = true;

@@ -289,3 +316,3 @@

}
result.trgParsed.port = result.trgParsed.port || DEFAULT_TARGET_PORT;
handlerOpts.trgParsed.port = handlerOpts.trgParsed.port || DEFAULT_TARGET_PORT;

@@ -299,8 +326,8 @@ // Authenticate the request using a user function (if provided)

var funcOpts = {
connectionId: result.id,
connectionId: handlerOpts.id,
request: request,
username: null,
password: null,
hostname: result.trgParsed.hostname,
port: result.trgParsed.port,
hostname: handlerOpts.trgParsed.hostname,
port: handlerOpts.trgParsed.port,
isHttp: isHttp

@@ -321,2 +348,3 @@ };

}
// User function returns a result directly or a promise

@@ -331,9 +359,9 @@ return _this5.prepareRequestFunction(funcOpts);

if (funcResult && funcResult.upstreamProxyUrl) {
result.upstreamProxyUrlParsed = (0, _tools.parseUrl)(funcResult.upstreamProxyUrl);
handlerOpts.upstreamProxyUrlParsed = (0, _tools.parseUrl)(funcResult.upstreamProxyUrl);
if (result.upstreamProxyUrlParsed) {
if (!result.upstreamProxyUrlParsed.hostname || !result.upstreamProxyUrlParsed.port) {
if (handlerOpts.upstreamProxyUrlParsed) {
if (!handlerOpts.upstreamProxyUrlParsed.hostname || !handlerOpts.upstreamProxyUrlParsed.port) {
throw new Error('Invalid "upstreamProxyUrl" provided: URL must have hostname and port');
}
if (result.upstreamProxyUrlParsed.scheme !== 'http') {
if (handlerOpts.upstreamProxyUrlParsed.scheme !== 'http') {
throw new Error('Invalid "upstreamProxyUrl" provided: URL must have the "http" scheme');

@@ -344,7 +372,18 @@ }

if (result.upstreamProxyUrlParsed) {
_this5.log(result.id, 'Using upstream proxy ' + (0, _tools.redactParsedUrl)(result.upstreamProxyUrlParsed));
if (funcResult && funcResult.customResponseFunc) {
_this5.log(handlerOpts.id, 'Using custom response function');
handlerOpts.customResponseFunc = funcResult.customResponseFunc;
if (!isHttp) {
throw new Error('The "customResponseFunc" option can only be used for HTTP requests.');
}
if (typeof handlerOpts.customResponseFunc !== 'function') {
throw new Error('The "customResponseFunc" option must be a function.');
}
}
return result;
if (handlerOpts.upstreamProxyUrlParsed) {
_this5.log(handlerOpts.id, 'Using upstream proxy ' + (0, _tools.redactParsedUrl)(handlerOpts.upstreamProxyUrlParsed));
}
return handlerOpts;
}).finally(function () {

@@ -383,4 +422,5 @@ if (_this5.prepareRequestFunction) socket.resume();

key: 'failRequest',
value: function failRequest(request, err, handlerOptions) {
var handlerId = handlerOptions ? handlerOptions.id : null;
value: function failRequest(request, err, handlerOpts) {
var handlerId = handlerOpts ? handlerOpts.id : null;
if (err.name === REQUEST_ERROR_NAME) {

@@ -394,7 +434,8 @@ this.log(handlerId, 'Request failed (status ' + err.statusCode + '): ' + err.message);

}
// emit connection closed if request fails and connection was already reported
if (handlerOptions) {
if (handlerOpts) {
this.log(handlerId, 'Closed because request failed with error');
this.emit('connectionClosed', {
connectionId: handlerOptions.id,
connectionId: handlerOpts.id,
stats: { srcTxBytes: 0, srcRxBytes: 0 }

@@ -401,0 +442,0 @@ });

@@ -32,13 +32,13 @@ 'use strict';

function createTunnel(proxyUrl, target) {
function createTunnel(proxyUrl, targetHost) {
var providedOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var callback = arguments[3];
// TODO: More and better validations
var _target$split = target.split(':'),
_target$split2 = _slicedToArray(_target$split, 2),
trgHost = _target$split2[0],
trgPort = _target$split2[1];
// TODO: More and better validations - yeah, make sure targetHost is really a hostname
var _targetHost$split = targetHost.split(':'),
_targetHost$split2 = _slicedToArray(_targetHost$split, 2),
trgHostname = _targetHost$split2[0],
trgPort = _targetHost$split2[1];
if (!trgHost || !trgPort) throw new Error('target needs to include both hostname and port.');
if (!trgHostname || !trgPort) throw new Error('target needs to include both hostname and port.');

@@ -78,3 +78,3 @@ var parsedProxyUrl = (0, _tools.parseUrl)(proxyUrl);

trgParsed: {
hostname: trgHost,
hostname: trgHostname,
port: trgPort

@@ -112,4 +112,2 @@ },

});
}).catch(function (err) {
throw err;
}).nodeify(callback);

@@ -126,3 +124,3 @@ }

if (!port) throw new Error('serverPath must contain port');
if (!runningServers[port]) resolve(false);
return new _bluebird2.default(function (resolve) {

@@ -129,0 +127,0 @@ if (!runningServers[port]) return resolve(false);

@@ -5,2 +5,4 @@ 'use strict';

// TODO: please move this into ./test dir
var server = net.createServer();

@@ -7,0 +9,0 @@

@@ -0,1 +1,5 @@

0.1.32 / 2018-06-08
===================
- Added `customResponseFunc` option to `prepareRequestFunction` to support custom response to HTTP requests
0.1.31 / 2018-05-21

@@ -9,5 +13,6 @@ ===================

0.1.27 / 2018-03-27
0.1.28 / 2018-03-27
===================
- Added option to create tunnels through http proxies for tcp network connections (eq. connection to mongodb/sql database through http proxy)
- Added `createTunnel()` function to create tunnels through HTTP proxies for arbitrary TCP network connections
(eq. connection to mongodb/sql database through HTTP proxy)

@@ -17,3 +22,3 @@ 0.1.27 / 2018-03-05

- Better error messages for common network errors
- Pass headers from target socket in https tunnel chains
- Pass headers from target socket in HTTPS tunnel chains

@@ -20,0 +25,0 @@ 0.1.26 / 2018-02-14

{
"name": "proxy-chain",
"version": "0.1.31",
"version": "0.1.32",
"description": "Node.js implementation of a proxy server (think Squid) with support for SSL, authentication, upstream proxy chaining, and protocol tunneling.",

@@ -44,3 +44,3 @@ "main": "build/index.js",

"portastic": "^1.0.1",
"underscore": "^1.8.3"
"underscore": "^1.9.1"
},

@@ -70,8 +70,8 @@ "devDependencies": {

"request": "^2.83.0",
"sinon": "^2.2.0",
"sinon": "^5.1.0",
"sinon-stub-promise": "^4.0.0",
"through": "^2.3.8",
"ws": "^3.3.1"
"ws": "^5.2.0"
},
"optionalDependencies": {}
}

@@ -6,3 +6,4 @@ # Programmable HTTP proxy server for Node.js

Node.js implementation of a proxy server (think Squid) with support for SSL, authentication and upstream proxy chaining.
Node.js implementation of a proxy server (think Squid) with support for SSL, authentication, upstream proxy chaining
and custom HTTP responses.
The authentication and proxy chaining configuration is defined in code and can be dynamic.

@@ -72,6 +73,60 @@ Note that the proxy server only supports Basic authentication

server.listen(() => {
console.log(`Proxy server is listening on port ${8000}`);
console.log(`Proxy server is listening on port ${server.port}`);
});
```
## Run a HTTP proxy server with custom responses
Custom responses allow you to override the response to a HTTP requests to the proxy, without contacting any target hoste.
For example, this is useful if you want to provide a HTTP proxy-style interface
to an external API or respond with some custom page to certain requests.
Note that this feature is only available for HTTP connections. That's because HTTPS
connections cannot be intercepted without access to target host's private key.
To provide a custom response, the result of the `prepareRequestFunction` function must
define the `prepareRequestFunction` property, which contains a function that generates the custom response.
The function must return an object (or a promise resolving to an object) with the following properties:
```javascript
{
// Optional HTTP status code of the response. By default it is 200.
statusCode: 200,
// Optional HTTP headers of the response
headers: {}
'X-My-Header': 'bla bla',
}
// Optional string with the body of the HTTP response
body: 'My custom response',
// Optional encoding of the body. If not provided, defaults to 'UTF-8'
encoding: 'UTF-8',
}
```
Here is a simple example:
```javascript
const ProxyChain = require('proxy-chain');
const server = new ProxyChain.Server({
port: 8000,
prepareRequestFunction: ({ request, username, password, hostname, port, isHttp }) => {
return {
customResponseFunc: () => {
return {
statusCode: 200,
body: `My custom response to ${request.url}',
}
},
};
},
});
server.listen(() => {
console.log(`Proxy server is listening on port ${server.port}`);
});
```
## Closing the server

@@ -118,17 +173,23 @@

### `createTunnel(proxyUrl, target, options, callback)`
### `createTunnel(proxyUrl, targetHost, options, callback)`
Attempts to create a network tunnel through proxy server specified in param "proxyUrl" to a network service
specified in param "target".
Creates a TCP tunnel to `targetHost` that goes through a HTTP proxy server
specified by the `proxyUrl` parameter.
The function takes optional callback that receives the path to local service.
The result of the function is local endpoint in a form of `hostname:port`.
All TCP connections made to the local endpoint will be tunneled through the proxy to the target host and port.
For example, this is useful if you want to access a certain service from a specific IP address.
The tunnel should be eventually closed by calling the `closeTunnel()` function.
The `createTunnel()` function accepts an optional Node.js-style callback that receives the path to the local endpoint.
If no callback is supplied, the function returns a promise that resolves to a String with
the path to local service.
the path to the local endpoint.
Example usage:
Example:
```javascript
const tunnel = await createTunnel('http://<username>:<password>@<proxy-server>:<port>', '<service-host>:<service-port>');
// tunnel will be in format "localhost:<randomly-assigned-port>" and while it's running
// it can be used instead of '<service-host>:<service-port>' to proxy requests.
const host = await createTunnel('http://bob:pass123@proxy.example.com:8000', 'service.example.com:356');
// Prints something like "localhost:56836"
console.log(host);
```

@@ -139,8 +200,8 @@

Closes tunnel previously started by `createTunnel()`.
Returns false if tunnel is not found or running. Otherwise the result is `true`.
The result value is `false` if the tunnel was not found or was already closed, otherwise it is `true`.
The `closeConnections` parameter indicates whether pending connections are forcibly closed.
The function takes optional callback that receives the result Boolean from the function.
If callback is not provided, the function returns a promise instead.
The function takes an optional callback that receives the result of the function.
If the callback is not provided, the function returns a promise instead.

@@ -147,0 +208,0 @@ ### `parseUrl(url)`

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc