Skip to content

Commit

Permalink
remove bouncy
Browse files Browse the repository at this point in the history
rework request proxy to use native http request and direct socket pipe.
Cuts out bouncy which is no longer maintained and simplifies the code
path.
  • Loading branch information
defunctzombie committed Nov 5, 2015
1 parent 4f0f78d commit f8fa049
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 63 deletions.
23 changes: 23 additions & 0 deletions lib/BindingAgent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
var http = require('http');
var util = require('util');
var assert = require('assert');

// binding agent will return a given options.socket as the socket for the agent
// this is useful if you already have a socket established and want the request
// to use that socket instead of making a new one
function BindingAgent(options) {
options = options || {};
http.Agent.call(this, options);

this.socket = options.socket;
assert(this.socket, 'socket is required for BindingAgent');
this.createConnection = create_connection;
}

util.inherits(BindingAgent, http.Agent);

function create_connection(port, host, options) {
return this.socket;
}

module.exports = BindingAgent;
19 changes: 9 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,24 @@
"dependencies": {
"book": "1.3.1",
"book-git": "0.0.2",
"book-raven": "1.0.1",
"book-raven": "1.1.0",
"bookrc": "0.0.1",
"bouncy": "3.2.2",
"debug": "2.1.0",
"express": "4.10.5",
"http-proxy": "1.11.1",
"debug": "2.2.0",
"express": "4.13.3",
"http-proxy": "1.12.0",
"localenv": "0.2.2",
"on-finished": "2.2.0",
"on-finished": "2.3.0",
"optimist": "0.6.1",
"stackup": "0.0.5",
"tldjs": "1.5.1"
"stackup": "1.0.1",
"tldjs": "1.6.1"
},
"devDependencies": {
"mocha": "2.0.1",
"localtunnel": "1.8.0",
"ws": "0.6.5"
"ws": "0.8.0"
},
"scripts": {
"test": "mocha --ui qunit --reporter list -- test",
"test": "mocha --ui qunit --reporter spec",
"start": "./bin/server"
}
}
99 changes: 65 additions & 34 deletions server.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
var log = require('bookrc');
var express = require('express');
var bouncy = require('bouncy');
var tldjs = require('tldjs');
var on_finished = require('on-finished');
var debug = require('debug')('localtunnel-server');
var http_proxy = require('http-proxy');
var http = require('http');

var BindingAgent = require('./lib/BindingAgent');

var proxy = http_proxy.createProxyServer({
target: 'http://localtunnel.github.io'
Expand Down Expand Up @@ -34,7 +36,7 @@ var stats = {
tunnels: 0
};

function maybe_bounce(req, res, bounce) {
function maybe_bounce(req, res, sock, head) {
// without a hostname, we won't know who the request is for
var hostname = req.headers.host;
if (!hostname) {
Expand All @@ -58,17 +60,23 @@ function maybe_bounce(req, res, bounce) {
return true;
}

// flag if we already finished before we get a socket
// we can't respond to these requests
var finished = false;
on_finished(res, function(err) {
if (req.headers['upgrade'] == 'websocket') {
return;
}
if (sock) {
sock.once('end', function() {
finished = true;
});
}

finished = true;
req.connection.destroy();
});
if (res) {
// flag if we already finished before we get a socket
// we can't respond to these requests
on_finished(res, function(err) {
finished = true;
req.connection.destroy();
});
}

// TODO add a timeout, if we run out of sockets, then just 502

// get client port
client.next_socket(function(socket, done) {
Expand All @@ -91,26 +99,46 @@ function maybe_bounce(req, res, bounce) {
return;
}

var stream = bounce(socket, { headers: { connection: 'close' } });
// websocket requests are special in that we simply re-create the header info
// and directly pipe the socket data
// avoids having to rebuild the request and handle upgrades via the http client
if (res === null) {
var arr = [req.method + ' ' + req.url + ' HTTP/' + req.httpVersion];
for (var i=0 ; i < (req.rawHeaders.length-1) ; i+=2) {
arr.push(req.rawHeaders[i] + ': ' + req.rawHeaders[i+1]);
}

stream.on('error', function(err) {
socket.destroy();
req.connection.destroy();
done();
});
arr.push('');
arr.push('');

// return the socket to the client pool
stream.once('end', function() {
done();
socket.pipe(sock).pipe(socket);
socket.write(arr.join('\r\n'));
socket.once('end', function() {
done();
});

return;
}

var agent = new BindingAgent({
socket: socket
});

on_finished(res, function(err) {
if (err) {
req.connection.destroy();
socket.destroy();
var opt = {
path: req.url,
agent: agent,
method: req.method,
headers: req.headers
};

var client_req = http.request(opt, function(client_res) {
client_res.pipe(res);
on_finished(client_res, function(err) {
done();
}
});
});

req.pipe(client_req);
});

return true;
Expand Down Expand Up @@ -187,7 +215,7 @@ module.exports = function(opt) {
});

app.get('/:req_id', function(req, res, next) {
var req_id = req.param('req_id');
var req_id = req.params.req_id;

// limit requested hostnames to 20 characters
if (! /^[a-z0-9]{4,20}$/.test(req_id)) {
Expand Down Expand Up @@ -216,20 +244,23 @@ module.exports = function(opt) {
});
});

var app_port = 0;
var app_server = app.listen(app_port, function() {
app_port = app_server.address().port;
});
var server = http.createServer();

var server = bouncy(function(req, res, bounce) {
server.on('request', function(req, res) {
debug('request %s', req.url);
if (maybe_bounce(req, res, null, null)) {
return;
};

app(req, res);
});

// if we should bounce this request, then don't send to our server
if (maybe_bounce(req, res, bounce)) {
server.on('upgrade', function(req, socket, head) {
if (maybe_bounce(req, null, socket, head)) {
return;
};

bounce(app_port);
socket.destroy();
});

return server;
Expand Down
12 changes: 6 additions & 6 deletions test/basic.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ var localtunnel = require('localtunnel');

var localtunnel_server = require('../server')();

suite('basic');

var lt_server_port

test('set up localtunnel server', function(done) {
before('set up localtunnel server', function(done) {
var server = localtunnel_server.listen(function() {
lt_server_port = server.address().port;
console.log('lt server on:', lt_server_port);
done();
});
});
Expand Down Expand Up @@ -42,7 +43,7 @@ test('landing page', function(done) {
req.end();
});

test('set up local http server', function(done) {
before('set up local http server', function(done) {
var server = http.createServer();
server.on('request', function(req, res) {
res.write('foo');
Expand All @@ -52,12 +53,11 @@ test('set up local http server', function(done) {
var port = server.address().port;

test._fake_port = port;
console.log('local http on:', port);
done();
});
});

test('set up localtunnel client', function(done) {
before('set up localtunnel client', function(done) {
var opt = {
host: 'http://localhost:' + lt_server_port,
};
Expand Down Expand Up @@ -144,6 +144,6 @@ test('request uppercase domain', function(done) {
});
});

test('shutdown', function() {
after('shutdown', function() {
localtunnel_server.close();
});
12 changes: 6 additions & 6 deletions test/queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@ var url = require('url');
var assert = require('assert');
var localtunnel = require('localtunnel');

suite('queue');

var localtunnel_server = require('../server')({
max_tcp_sockets: 1
});

var server;
var lt_server_port;

test('set up localtunnel server', function(done) {
before('set up localtunnel server', function(done) {
var lt_server = localtunnel_server.listen(function() {
lt_server_port = lt_server.address().port;
console.log('lt server on:', lt_server_port);
done();
});
});

test('set up local http server', function(done) {
before('set up local http server', function(done) {
server = http.createServer();
server.on('request', function(req, res) {
// respond sometime later
Expand All @@ -32,12 +33,11 @@ test('set up local http server', function(done) {
var port = server.address().port;

test._fake_port = port;
console.log('local http on:', port);
done();
});
});

test('set up localtunnel client', function(done) {
before('set up localtunnel client', function(done) {
var opt = {
host: 'http://localhost:' + lt_server_port,
};
Expand Down Expand Up @@ -97,7 +97,7 @@ test('query localtunnel server w/ ident', function(done) {
}
});

test('shutdown', function() {
after('shutdown', function() {
localtunnel_server.close();
});

68 changes: 68 additions & 0 deletions test/simple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
var http = require('http');
var url = require('url');
var assert = require('assert');
var localtunnel = require('localtunnel');

var localtunnel_server = require('../server')({
max_tcp_sockets: 2
});

var lt_server_port

suite('simple');

test('set up localtunnel server', function(done) {
var server = localtunnel_server.listen(function() {
lt_server_port = server.address().port;
done();
});
});

test('set up local http server', function(done) {
var server = http.createServer(function(req, res) {
res.end('hello world!');
});

server.listen(function() {
test._fake_port = server.address().port;
done();
});
});

test('set up localtunnel client', function(done) {
var opt = {
host: 'http://localhost:' + lt_server_port,
};

localtunnel(test._fake_port, opt, function(err, tunnel) {
assert.ifError(err);
var url = tunnel.url;
assert.ok(new RegExp('^http:\/\/.*localhost:' + lt_server_port + '$').test(url));
test._fake_url = url;
done(err);
});
});

test('should respond to request', function(done) {
var hostname = url.parse(test._fake_url).hostname;
var opt = {
host: 'localhost',
port: lt_server_port,
headers: {
host: hostname + '.tld'
}
};

http.get(opt, function(res) {
var body = '';
res.setEncoding('utf-8');
res.on('data', function(chunk) {
body += chunk;
});

res.on('end', function() {
assert.equal(body, 'hello world!');
done();
});
});
});
Loading

0 comments on commit f8fa049

Please sign in to comment.