reduced code duplication for proxying

This commit is contained in:
tigerbot 2017-05-17 14:06:24 -06:00
parent d25ceadf4a
commit df3a818914
6 changed files with 93 additions and 123 deletions

View File

@ -80,40 +80,16 @@ module.exports.create = function (deps, config) {
}
function createTcpForwarder(mod) {
var destination = mod.address.split(':');
var connected = false;
return function (conn) {
var newConn = deps.net.createConnection({
port: destination[1]
, host: destination[0] || '127.0.0.1'
var newConnOpts = require('./domain-utils').separatePort(mod.address);
, remoteFamily: conn.remoteFamily
, remoteAddress: conn.remoteAddress
, remotePort: conn.remotePort
}, function () {
connected = true;
newConn.pipe(conn);
conn.pipe(newConn);
});
// Not sure how to effectively report this to the user or client, but we need to listen
// for the event to prevent it from crashing us.
newConn.on('error', function (err) {
if (connected) {
console.error('TCP forward remote error', err);
conn.end();
} else {
console.log('TCP forward connection error', err);
require('./proxy-err-resp').sendBadGateway(conn, err, config.debug);
}
});
conn.on('error', function (err) {
console.error('TCP forward client error', err);
newConn.end();
['remote', 'local'].forEach(function (end) {
['Family', 'Address', 'Port'].forEach(function (name) {
newConnOpts[end+name] = conn[end+name];
});
});
deps.proxy(conn, newConnOpts);
};
}

View File

@ -275,7 +275,6 @@ module.exports.create = function (deps, conf, greenlockMiddleware) {
return false;
}
var connected = false;
var newConnOpts = separatePort(mod.address);
newConnOpts.servername = separatePort(headers.host).host;
newConnOpts.data = opts.firstChunk;
@ -284,28 +283,7 @@ module.exports.create = function (deps, conf, greenlockMiddleware) {
newConnOpts.remoteAddress = opts.address || conn.remoteAddress;
newConnOpts.remotePort = opts.port || conn.remotePort;
var newConn = deps.net.createConnection(newConnOpts, function () {
connected = true;
newConn.write(opts.firstChunk);
newConn.pipe(conn);
conn.pipe(newConn);
});
// Not sure how to effectively report this to the user or client, but we need to listen
// for the event to prevent it from crashing us.
newConn.on('error', function (err) {
if (connected) {
console.error('HTTP proxy remote error', err);
conn.end();
} else {
require('../proxy-err-resp').sendBadGateway(conn, err, conf.debug);
}
});
conn.on('error', function (err) {
console.error('HTTP proxy client error', err);
newConn.end();
});
deps.proxy(conn, newConnOpts, opts.firstChunk);
return true;
}

View File

@ -186,49 +186,24 @@ module.exports.create = function (deps, config, netHandler) {
});
function proxy(socket, opts, mod) {
var destination = mod.address.split(':');
var connected = false;
var newConnOpts = require('../domain-utils').separatePort(mod.address);
newConnOpts.servername = opts.servername;
newConnOpts.data = opts.firstChunk;
var newConn = deps.net.createConnection({
port: destination[1]
, host: destination[0] || '127.0.0.1'
newConnOpts.remoteFamily = opts.family || extractSocketProp(socket, 'remoteFamily');
newConnOpts.remoteAddress = opts.address || extractSocketProp(socket, 'remoteAddress');
newConnOpts.remotePort = opts.port || extractSocketProp(socket, 'remotePort');
, servername: opts.servername
, data: opts.firstChunk
, remoteFamily: opts.family || extractSocketProp(socket, 'remoteFamily')
, remoteAddress: opts.address || extractSocketProp(socket, 'remoteAddress')
, remotePort: opts.port || extractSocketProp(socket, 'remotePort')
}, function () {
connected = true;
if (!opts.hyperPeek) {
newConn.write(opts.firstChunk);
}
newConn.pipe(socket);
socket.pipe(newConn);
});
// Not sure how to effectively report this to the user or client, but we need to listen
// for the event to prevent it from crashing us.
newConn.on('error', function (err) {
if (connected) {
console.error('TLS proxy remote error', err);
socket.end();
deps.proxy(socket, newConnOpts, opts.firstChunk, function () {
// This function is called in the event of a connection error and should decrypt
// the socket so the proxy module can send a 502 HTTP response.
var tlsOpts = localhostCerts.mergeTlsOptions('localhost.daplie.me', {isServer: true});
if (opts.hyperPeek) {
return new tls.TLSSocket(socket, tlsOpts);
} else {
console.log('TLS proxy connection error', err);
var tlsOpts = localhostCerts.mergeTlsOptions('localhost.daplie.me', {isServer: true});
var decrypted;
if (opts.hyperPeek) {
decrypted = new tls.TLSSocket(socket, tlsOpts);
} else {
decrypted = new tls.TLSSocket(wrapSocket(socket, opts), tlsOpts);
}
require('../proxy-err-resp').sendBadGateway(decrypted, err, config.debug);
return new tls.TLSSocket(wrapSocket(socket, opts), tlsOpts);
}
});
socket.on('error', function (err) {
console.error('TLS proxy client error', err);
newConn.end();
});
}
function terminate(socket, opts) {

71
lib/proxy-conn.js Normal file
View File

@ -0,0 +1,71 @@
'use strict';
function getRespBody(err, debug) {
if (debug) {
return err.toString();
}
if (err.code === 'ECONNREFUSED') {
return 'The connection was refused. Most likely the service being connected to '
+ 'has stopped running or the configuration is wrong.';
}
return 'Bad Gateway: ' + err.code;
}
function sendBadGateway(conn, err, debug) {
var body = getRespBody(err, debug);
conn.write([
'HTTP/1.1 502 Bad Gateway'
, 'Date: ' + (new Date()).toUTCString()
, 'Connection: close'
, 'Content-Type: text/html'
, 'Content-Length: ' + body.length
, ''
, body
].join('\r\n'));
conn.end();
}
module.exports.getRespBody = getRespBody;
module.exports.sendBadGateway = sendBadGateway;
module.exports.create = function (deps, config) {
return function proxy(conn, newConnOpts, firstChunk, decrypt) {
var connected = false;
var newConn = deps.net.createConnection(newConnOpts, function () {
connected = true;
if (firstChunk) {
newConn.write(firstChunk);
}
newConn.pipe(conn);
conn.pipe(newConn);
});
newConn.on('error', function (err) {
if (connected) {
// Not sure how to report this to a user or a client. We can assume that some data
// has already been exchanged, so we can't really be sure what we can send in addition
// that wouldn't result in a parse error.
console.log('proxy remote error', err);
conn.end();
} else {
console.log('proxy connection error', err);
if (decrypt) {
sendBadGateway(decrypt(conn), err, config.debug);
} else {
sendBadGateway(conn, err, config.debug);
}
}
});
// Listening for this largely to prevent uncaught exceptions.
conn.on('error', function (err) {
console.log('proxy client error', err);
newConn.end();
});
};
};

View File

@ -1,32 +0,0 @@
'use strict';
function getRespBody(err, debug) {
if (debug) {
return err.toString();
}
if (err.code === 'ECONNREFUSED') {
return 'The connection was refused. Most likely the service being connected to '
+ 'has stopped running or the configuration is wrong.';
}
return 'Bad Gateway: ' + err.code;
}
function sendBadGateway(conn, err, debug) {
var body = getRespBody(err, debug);
conn.write([
'HTTP/1.1 502 Bad Gateway'
, 'Date: ' + (new Date()).toUTCString()
, 'Connection: close'
, 'Content-Type: text/html'
, 'Content-Length: ' + body.length
, ''
, body
].join('\r\n'));
conn.end();
}
module.exports.getRespBody = getRespBody;
module.exports.sendBadGateway = sendBadGateway;

View File

@ -9,5 +9,7 @@ process.on('message', function (conf) {
// HTTP proxying connection creation is not something we currently control.
, net: require('net')
};
deps.proxy = require('./proxy-conn').create(deps, conf);
require('./goldilocks.js').create(deps, conf);
});