mirror of
https://github.com/bitinflow/server.git
synced 2026-03-13 13:35:53 +00:00
When clients disconnect, their tcp server should be shutdown and the id released after a grace period.
138 lines
3.6 KiB
JavaScript
138 lines
3.6 KiB
JavaScript
// builtin
|
|
var net = require('net');
|
|
var url = require('url');
|
|
var request = require('request');
|
|
var EventEmitter = require('events').EventEmitter;
|
|
|
|
// request upstream url and connection info
|
|
var request_url = function(params, cb) {
|
|
request(params, function(err, res, body) {
|
|
if (err) {
|
|
cb(err);
|
|
}
|
|
|
|
cb(null, body);
|
|
});
|
|
};
|
|
|
|
var connect = function(opt) {
|
|
var ev = new EventEmitter();
|
|
|
|
// local port
|
|
var local_port = opt.port;
|
|
|
|
var base_uri = opt.host + '/';
|
|
|
|
// optionally override the upstream server
|
|
var upstream = url.parse(opt.host);
|
|
|
|
// no subdomain at first, maybe use requested domain
|
|
var assigned_domain = opt.subdomain;
|
|
|
|
// connect to upstream given connection parameters
|
|
var tunnel = function (remote_host, remote_port, max_conn) {
|
|
var count = 0;
|
|
|
|
// open 5 connections to the localtunnel server
|
|
// allows for resources to be served faster
|
|
for (var count = 0 ; count < max_conn ; ++count) {
|
|
var upstream = duplex(remote_host, remote_port, 'localhost', local_port);
|
|
upstream.once('end', function() {
|
|
// all upstream connections have been closed
|
|
if (--count <= 0) {
|
|
tunnel(remote_host, remote_port, max_conn);
|
|
}
|
|
});
|
|
|
|
upstream.on('error', function(err) {
|
|
console.error(err);
|
|
});
|
|
}
|
|
};
|
|
|
|
var params = {
|
|
path: '/',
|
|
json: true
|
|
};
|
|
|
|
// where to quest
|
|
params.uri = base_uri + ((assigned_domain) ? assigned_domain : '?new');
|
|
|
|
request_url(params, function(err, body) {
|
|
|
|
if (err) {
|
|
ev.emit('error', new Error('tunnel server not available: %s, retry 1s', err.message));
|
|
|
|
// retry interval for id request
|
|
return setTimeout(function() {
|
|
connect_proxy(opt);
|
|
}, 1000);
|
|
}
|
|
|
|
// our assigned hostname and tcp port
|
|
var port = body.port;
|
|
var host = upstream.hostname;
|
|
|
|
// store the id so we can try to get the same one
|
|
assigned_domain = body.id;
|
|
|
|
tunnel(host, port, body.max_conn_count || 1);
|
|
|
|
ev.emit('url', body.url);
|
|
});
|
|
|
|
return ev;
|
|
};
|
|
|
|
var duplex = function(remote_host, remote_port, local_host, local_port) {
|
|
var ev = new EventEmitter();
|
|
|
|
// connect to remote tcp server
|
|
var upstream = net.createConnection(remote_port, remote_host);
|
|
var internal;
|
|
|
|
// when upstream connection is closed, close other associated connections
|
|
upstream.once('end', function() {
|
|
ev.emit('error', new Error('upstream connection terminated'));
|
|
|
|
// sever connection to internal server
|
|
// on reconnect we will re-establish
|
|
internal.end();
|
|
|
|
ev.emit('end');
|
|
});
|
|
|
|
upstream.on('error', function(err) {
|
|
ev.emit('error', err);
|
|
});
|
|
|
|
(function connect_internal() {
|
|
|
|
internal = net.createConnection(local_port, local_host);
|
|
internal.on('error', function() {
|
|
ev.emit('error', new Error('error connecting to local server. retrying in 1s'));
|
|
setTimeout(function() {
|
|
connect_internal();
|
|
}, 1000);
|
|
});
|
|
|
|
internal.on('end', function() {
|
|
ev.emit('error', new Error('disconnected from local server. retrying in 1s'));
|
|
setTimeout(function() {
|
|
connect_internal();
|
|
}, 1000);
|
|
});
|
|
|
|
internal.on('connect', function() {
|
|
console.log('connected to local server');
|
|
});
|
|
|
|
upstream.pipe(internal).pipe(upstream);
|
|
})();
|
|
|
|
return ev;
|
|
}
|
|
|
|
module.exports.connect = connect;
|
|
|