throttle-proxy
Version:
Throttle HTTP/HTTPS/SOCKS proxy server
98 lines (77 loc) • 2.69 kB
JavaScript
const debug = require('debug')('throttle-proxy:handler');
const socks = require('socks-handler');
const Channel = require('./throttle');
const createUpstream = require('./upstream');
function replyStatusError(version, err) {
if (version === 5) {
switch (err.code) {
case 'EHOSTUNREACH':
return socks[5].REQUEST_STATUS.HOST_UNREACHABLE;
case 'ECONNREFUSED':
return socks[5].REQUEST_STATUS.CONNECTION_REFUSED;
case 'ENETUNREACH':
return socks[5].REQUEST_STATUS.NETWORK_UNREACHABLE;
default:
return socks[5].REQUEST_STATUS.SERVER_FAILURE;
}
}
return socks[4].REQUEST_STATUS.FAILED;
}
function replyStatusUnsupported(version) {
return version === 5
? socks[5].REQUEST_STATUS.COMMAND_NOT_SUPPORTED
: socks[4].REQUEST_STATUS.REFUSED;
}
function replyStatusSuccess(version) {
return version === 5
? socks[5].REQUEST_STATUS.SUCCESS
: socks[4].REQUEST_STATUS.GRANTED;
}
function createVersionedReply(version) {
return callback => (fn, data) => {
const statuses = socks[version].REQUEST_STATUS;
const statusCode = fn(version, data);
const statusText = Object.keys(statuses).find(k => statuses[k] === statusCode);
debug(`sending status ${statusText}`);
callback(statusCode);
}
}
module.exports = options => {
const inputChannel = new Channel(options.incomingSpeed);
const outputChannel = new Channel(options.outgoingSpeed);
return clientConnection => (
(arg, callback) => {
const {version, command, host, port} = arg;
const doReply = createVersionedReply(version)(callback);
// only "CONNECT" command is supported
if (command !== socks[5].COMMAND.CONNECT) {
debug(`unknown command "${command}"`);
return doReply(replyStatusUnsupported);
}
debug(`version: ${version}`);
debug(`connect to ${host}:${port}`);
const upstream = createUpstream({host, port, timeout: 3000});
const inputThrottle = inputChannel.createThrottle();
const outputThrottle = outputChannel.createThrottle();
clientConnection
.pipe(outputThrottle)
.pipe(upstream)
.pipe(inputThrottle)
.pipe(clientConnection);
function onConnectError(err) {
debug(`error ${err.code}, ${host}:${port}`);
doReply(replyStatusError, err);
}
upstream
.on('close', () => {
debug('close');
})
.on('error', onConnectError)
.on('connect', socket => {
debug('connection established');
upstream.removeListener('error', onConnectError);
return doReply(replyStatusSuccess);
});
}
);
};