mirror of
https://github.com/bitinflow/server.git
synced 2026-03-13 13:35:53 +00:00
remove bouncy
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.
This commit is contained in:
23
lib/BindingAgent.js
Normal file
23
lib/BindingAgent.js
Normal file
@@ -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
package.json
19
package.json
@@ -11,25 +11,24 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"book": "1.3.1",
|
"book": "1.3.1",
|
||||||
"book-git": "0.0.2",
|
"book-git": "0.0.2",
|
||||||
"book-raven": "1.0.1",
|
"book-raven": "1.1.0",
|
||||||
"bookrc": "0.0.1",
|
"bookrc": "0.0.1",
|
||||||
"bouncy": "3.2.2",
|
"debug": "2.2.0",
|
||||||
"debug": "2.1.0",
|
"express": "4.13.3",
|
||||||
"express": "4.10.5",
|
"http-proxy": "1.12.0",
|
||||||
"http-proxy": "1.11.1",
|
|
||||||
"localenv": "0.2.2",
|
"localenv": "0.2.2",
|
||||||
"on-finished": "2.2.0",
|
"on-finished": "2.3.0",
|
||||||
"optimist": "0.6.1",
|
"optimist": "0.6.1",
|
||||||
"stackup": "0.0.5",
|
"stackup": "1.0.1",
|
||||||
"tldjs": "1.5.1"
|
"tldjs": "1.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "2.0.1",
|
"mocha": "2.0.1",
|
||||||
"localtunnel": "1.8.0",
|
"localtunnel": "1.8.0",
|
||||||
"ws": "0.6.5"
|
"ws": "0.8.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha --ui qunit --reporter list -- test",
|
"test": "mocha --ui qunit --reporter spec",
|
||||||
"start": "./bin/server"
|
"start": "./bin/server"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
109
server.js
109
server.js
@@ -1,10 +1,12 @@
|
|||||||
var log = require('bookrc');
|
var log = require('bookrc');
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var bouncy = require('bouncy');
|
|
||||||
var tldjs = require('tldjs');
|
var tldjs = require('tldjs');
|
||||||
var on_finished = require('on-finished');
|
var on_finished = require('on-finished');
|
||||||
var debug = require('debug')('localtunnel-server');
|
var debug = require('debug')('localtunnel-server');
|
||||||
var http_proxy = require('http-proxy');
|
var http_proxy = require('http-proxy');
|
||||||
|
var http = require('http');
|
||||||
|
|
||||||
|
var BindingAgent = require('./lib/BindingAgent');
|
||||||
|
|
||||||
var proxy = http_proxy.createProxyServer({
|
var proxy = http_proxy.createProxyServer({
|
||||||
target: 'http://localtunnel.github.io'
|
target: 'http://localtunnel.github.io'
|
||||||
@@ -34,7 +36,7 @@ var stats = {
|
|||||||
tunnels: 0
|
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
|
// without a hostname, we won't know who the request is for
|
||||||
var hostname = req.headers.host;
|
var hostname = req.headers.host;
|
||||||
if (!hostname) {
|
if (!hostname) {
|
||||||
@@ -58,17 +60,23 @@ function maybe_bounce(req, res, bounce) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// flag if we already finished before we get a socket
|
|
||||||
// we can't respond to these requests
|
|
||||||
var finished = false;
|
var finished = false;
|
||||||
on_finished(res, function(err) {
|
if (sock) {
|
||||||
if (req.headers['upgrade'] == 'websocket') {
|
sock.once('end', function() {
|
||||||
return;
|
finished = true;
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
finished = true;
|
if (res) {
|
||||||
req.connection.destroy();
|
// 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
|
// get client port
|
||||||
client.next_socket(function(socket, done) {
|
client.next_socket(function(socket, done) {
|
||||||
@@ -91,26 +99,46 @@ function maybe_bounce(req, res, bounce) {
|
|||||||
return;
|
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
|
||||||
stream.on('error', function(err) {
|
// avoids having to rebuild the request and handle upgrades via the http client
|
||||||
socket.destroy();
|
if (res === null) {
|
||||||
req.connection.destroy();
|
var arr = [req.method + ' ' + req.url + ' HTTP/' + req.httpVersion];
|
||||||
done();
|
for (var i=0 ; i < (req.rawHeaders.length-1) ; i+=2) {
|
||||||
});
|
arr.push(req.rawHeaders[i] + ': ' + req.rawHeaders[i+1]);
|
||||||
|
|
||||||
// return the socket to the client pool
|
|
||||||
stream.once('end', function() {
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
|
|
||||||
on_finished(res, function(err) {
|
|
||||||
if (err) {
|
|
||||||
req.connection.destroy();
|
|
||||||
socket.destroy();
|
|
||||||
done();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arr.push('');
|
||||||
|
arr.push('');
|
||||||
|
|
||||||
|
socket.pipe(sock).pipe(socket);
|
||||||
|
socket.write(arr.join('\r\n'));
|
||||||
|
socket.once('end', function() {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var agent = new BindingAgent({
|
||||||
|
socket: socket
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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;
|
return true;
|
||||||
@@ -187,7 +215,7 @@ module.exports = function(opt) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.get('/:req_id', function(req, res, next) {
|
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
|
// limit requested hostnames to 20 characters
|
||||||
if (! /^[a-z0-9]{4,20}$/.test(req_id)) {
|
if (! /^[a-z0-9]{4,20}$/.test(req_id)) {
|
||||||
@@ -216,20 +244,23 @@ module.exports = function(opt) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var app_port = 0;
|
var server = http.createServer();
|
||||||
var app_server = app.listen(app_port, function() {
|
|
||||||
app_port = app_server.address().port;
|
|
||||||
});
|
|
||||||
|
|
||||||
var server = bouncy(function(req, res, bounce) {
|
server.on('request', function(req, res) {
|
||||||
debug('request %s', req.url);
|
debug('request %s', req.url);
|
||||||
|
if (maybe_bounce(req, res, null, null)) {
|
||||||
// if we should bounce this request, then don't send to our server
|
|
||||||
if (maybe_bounce(req, res, bounce)) {
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
bounce(app_port);
|
app(req, res);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('upgrade', function(req, socket, head) {
|
||||||
|
if (maybe_bounce(req, null, socket, head)) {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ var localtunnel = require('localtunnel');
|
|||||||
|
|
||||||
var localtunnel_server = require('../server')();
|
var localtunnel_server = require('../server')();
|
||||||
|
|
||||||
|
suite('basic');
|
||||||
|
|
||||||
var lt_server_port
|
var lt_server_port
|
||||||
|
|
||||||
test('set up localtunnel server', function(done) {
|
before('set up localtunnel server', function(done) {
|
||||||
var server = localtunnel_server.listen(function() {
|
var server = localtunnel_server.listen(function() {
|
||||||
lt_server_port = server.address().port;
|
lt_server_port = server.address().port;
|
||||||
console.log('lt server on:', lt_server_port);
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -42,7 +43,7 @@ test('landing page', function(done) {
|
|||||||
req.end();
|
req.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('set up local http server', function(done) {
|
before('set up local http server', function(done) {
|
||||||
var server = http.createServer();
|
var server = http.createServer();
|
||||||
server.on('request', function(req, res) {
|
server.on('request', function(req, res) {
|
||||||
res.write('foo');
|
res.write('foo');
|
||||||
@@ -52,12 +53,11 @@ test('set up local http server', function(done) {
|
|||||||
var port = server.address().port;
|
var port = server.address().port;
|
||||||
|
|
||||||
test._fake_port = port;
|
test._fake_port = port;
|
||||||
console.log('local http on:', port);
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('set up localtunnel client', function(done) {
|
before('set up localtunnel client', function(done) {
|
||||||
var opt = {
|
var opt = {
|
||||||
host: 'http://localhost:' + lt_server_port,
|
host: 'http://localhost:' + lt_server_port,
|
||||||
};
|
};
|
||||||
@@ -144,6 +144,6 @@ test('request uppercase domain', function(done) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shutdown', function() {
|
after('shutdown', function() {
|
||||||
localtunnel_server.close();
|
localtunnel_server.close();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ var url = require('url');
|
|||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var localtunnel = require('localtunnel');
|
var localtunnel = require('localtunnel');
|
||||||
|
|
||||||
|
suite('queue');
|
||||||
|
|
||||||
var localtunnel_server = require('../server')({
|
var localtunnel_server = require('../server')({
|
||||||
max_tcp_sockets: 1
|
max_tcp_sockets: 1
|
||||||
});
|
});
|
||||||
@@ -10,15 +12,14 @@ var localtunnel_server = require('../server')({
|
|||||||
var server;
|
var server;
|
||||||
var lt_server_port;
|
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() {
|
var lt_server = localtunnel_server.listen(function() {
|
||||||
lt_server_port = lt_server.address().port;
|
lt_server_port = lt_server.address().port;
|
||||||
console.log('lt server on:', lt_server_port);
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('set up local http server', function(done) {
|
before('set up local http server', function(done) {
|
||||||
server = http.createServer();
|
server = http.createServer();
|
||||||
server.on('request', function(req, res) {
|
server.on('request', function(req, res) {
|
||||||
// respond sometime later
|
// respond sometime later
|
||||||
@@ -32,12 +33,11 @@ test('set up local http server', function(done) {
|
|||||||
var port = server.address().port;
|
var port = server.address().port;
|
||||||
|
|
||||||
test._fake_port = port;
|
test._fake_port = port;
|
||||||
console.log('local http on:', port);
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('set up localtunnel client', function(done) {
|
before('set up localtunnel client', function(done) {
|
||||||
var opt = {
|
var opt = {
|
||||||
host: 'http://localhost:' + lt_server_port,
|
host: 'http://localhost:' + lt_server_port,
|
||||||
};
|
};
|
||||||
@@ -97,7 +97,7 @@ test('query localtunnel server w/ ident', function(done) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shutdown', function() {
|
after('shutdown', function() {
|
||||||
localtunnel_server.close();
|
localtunnel_server.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
68
test/simple.js
Normal file
68
test/simple.js
Normal file
@@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -5,33 +5,43 @@ var localtunnel = require('localtunnel');
|
|||||||
var WebSocket = require('ws');
|
var WebSocket = require('ws');
|
||||||
var WebSocketServer = require('ws').Server;
|
var WebSocketServer = require('ws').Server;
|
||||||
|
|
||||||
var localtunnel_server = require('../server')();
|
var localtunnel_server = require('../server')({
|
||||||
|
max_tcp_sockets: 2
|
||||||
|
});
|
||||||
|
|
||||||
var lt_server_port
|
var lt_server_port
|
||||||
|
|
||||||
test('set up localtunnel server', function(done) {
|
suite('websocket');
|
||||||
|
|
||||||
|
before('set up localtunnel server', function(done) {
|
||||||
var server = localtunnel_server.listen(function() {
|
var server = localtunnel_server.listen(function() {
|
||||||
lt_server_port = server.address().port;
|
lt_server_port = server.address().port;
|
||||||
console.log('lt server on:', lt_server_port);
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('set up local websocket server', function(done) {
|
before('set up local websocket server', function(done) {
|
||||||
|
|
||||||
var wss = new WebSocketServer({ port: 0 }, function() {
|
var wss = new WebSocketServer({ port: 0 }, function() {
|
||||||
test._fake_port = wss._server.address().port;
|
test._fake_port = wss._server.address().port;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
wss.on('error', function(err) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
|
||||||
wss.on('connection', function connection(ws) {
|
wss.on('connection', function connection(ws) {
|
||||||
|
ws.on('error', function(err) {
|
||||||
|
done(err);
|
||||||
|
});
|
||||||
|
|
||||||
ws.on('message', function incoming(message) {
|
ws.on('message', function incoming(message) {
|
||||||
ws.send(message);
|
ws.send(message);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('set up localtunnel client', function(done) {
|
before('set up localtunnel client', function(done) {
|
||||||
var opt = {
|
var opt = {
|
||||||
host: 'http://localhost:' + lt_server_port,
|
host: 'http://localhost:' + lt_server_port,
|
||||||
};
|
};
|
||||||
@@ -45,13 +55,14 @@ test('set up localtunnel client', function(done) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test websocket server request', function(done) {
|
test('websocket server request', function(done) {
|
||||||
var hostname = url.parse(test._fake_url).hostname;
|
var hostname = url.parse(test._fake_url).hostname;
|
||||||
var ws = new WebSocket('http://localhost:' + lt_server_port, {
|
var ws = new WebSocket('http://localhost:' + lt_server_port, {
|
||||||
headers: {
|
headers: {
|
||||||
host: hostname + '.tld'
|
host: hostname + '.tld'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ws.on('message', function(msg) {
|
ws.on('message', function(msg) {
|
||||||
assert.equal(msg, 'something');
|
assert.equal(msg, 'something');
|
||||||
done();
|
done();
|
||||||
|
|||||||
Reference in New Issue
Block a user