mirror of
https://github.com/bitinflow/localtunnel.git
synced 2026-03-14 05:55:53 +00:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9b0274ff4 | ||
|
|
83ecb29eff | ||
|
|
21df257d16 | ||
|
|
18ada0854a | ||
|
|
34afd6537d | ||
|
|
2c38aefb9d | ||
|
|
aa488f6e76 | ||
|
|
f6618953f9 | ||
|
|
092d050fa0 | ||
|
|
0334ace20b | ||
|
|
13afcff1ae | ||
|
|
ed5aa3f16b | ||
|
|
2fcac1336c | ||
|
|
0568ae0bef | ||
|
|
585a8afad7 | ||
|
|
fbe841a1c5 | ||
|
|
929473913f |
@@ -1,3 +1,4 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
- 0.9
|
||||
|
||||
23
README.md
23
README.md
@@ -1,4 +1,4 @@
|
||||
# localtunnel [](http://travis-ci.org/shtylman/localtunnel) #
|
||||
# localtunnel #
|
||||
|
||||
localtunnel exposes your localhost to the world for easy testing and sharing! No need to mess with DNS or deploy just to have others test out your changes.
|
||||
|
||||
@@ -10,6 +10,8 @@ Great for working with browser testing tools like browserling or external api ca
|
||||
npm install -g localtunnel
|
||||
```
|
||||
|
||||
This will install the localtunnel module globally and add the 'lt' client cli tool to your PATH.
|
||||
|
||||
## use ##
|
||||
|
||||
Super Easy! Assuming your local server is running on port 8000, just use the ```lt``` command to start the tunnel.
|
||||
@@ -24,12 +26,12 @@ You can restart your local server all you want, ```lt``` is smart enough to dete
|
||||
|
||||
## API ##
|
||||
|
||||
The localtunnel client is also usable through an API (test integration, automation, etc)
|
||||
The localtunnel client is also usable through an API (for test integration, automation, etc)
|
||||
|
||||
```javascript
|
||||
var lt_client = require('localtunnel').client;
|
||||
var localtunnel = require('localtunnel');
|
||||
|
||||
var client = lt_client.connect({
|
||||
var client = localtunnel.connect({
|
||||
// the localtunnel server
|
||||
host: 'http://localtunnel.me',
|
||||
// your local application port
|
||||
@@ -46,3 +48,16 @@ client.on('error', function(err) {
|
||||
// uh oh!
|
||||
});
|
||||
```
|
||||
|
||||
## other clients ##
|
||||
|
||||
Clients in other languages
|
||||
|
||||
*go* [gotunnelme](https://github.com/NoahShen/gotunnelme)
|
||||
|
||||
## server ##
|
||||
|
||||
See shtylman/localtunnel-server for details on the server that powers localtunnel.
|
||||
|
||||
## License ##
|
||||
MIT
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
var lt_client = require(__dirname + '/../client');
|
||||
var lt_client = require('../client');
|
||||
|
||||
var argv = require('optimist')
|
||||
.usage('Usage: $0 --port [num]')
|
||||
@@ -11,12 +11,17 @@ var argv = require('optimist')
|
||||
.options('subdomain', {
|
||||
describe: 'request this subdomain'
|
||||
})
|
||||
.options('local-host', {
|
||||
describe: 'tunnel traffic to this host instead of localhost'
|
||||
})
|
||||
.default('local-host', 'localhost')
|
||||
.describe('port', 'internal http server port')
|
||||
.argv;
|
||||
|
||||
var opt = {
|
||||
host: argv.host,
|
||||
port: argv.port,
|
||||
local_host: argv['local-host'],
|
||||
subdomain: argv.subdomain,
|
||||
}
|
||||
|
||||
|
||||
35
bin/server
35
bin/server
@@ -1,35 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// vendor
|
||||
var log = require('book');
|
||||
var optimist = require('optimist');
|
||||
|
||||
var argv = optimist
|
||||
.usage('Usage: $0 --port [num]')
|
||||
.options('port', {
|
||||
default: '80',
|
||||
describe: 'listen on this port for outside requests'
|
||||
})
|
||||
.argv;
|
||||
|
||||
if (argv.help) {
|
||||
optimist.showHelp();
|
||||
process.exit();
|
||||
}
|
||||
|
||||
process.once('uncaughtException', function(err) {
|
||||
log.panic(err);
|
||||
process.exit(-1);
|
||||
return;
|
||||
});
|
||||
|
||||
var server = require('../server')({
|
||||
max_tcp_sockets: 5
|
||||
});
|
||||
|
||||
server.listen(argv.port, function() {
|
||||
log.info('server listening on port: %d', server.address().port);
|
||||
});
|
||||
|
||||
// vim: ft=javascript
|
||||
|
||||
74
client.js
74
client.js
@@ -1,14 +1,15 @@
|
||||
// builtin
|
||||
var net = require('net');
|
||||
var url = require('url');
|
||||
var request = require('request');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var after = require('after');
|
||||
var request = require('request');
|
||||
|
||||
// request upstream url and connection info
|
||||
var request_url = function(params, cb) {
|
||||
request(params, function(err, res, body) {
|
||||
if (err) {
|
||||
cb(err);
|
||||
return cb(err);
|
||||
}
|
||||
|
||||
cb(null, body);
|
||||
@@ -18,6 +19,9 @@ var request_url = function(params, cb) {
|
||||
var connect = function(opt) {
|
||||
var ev = new EventEmitter();
|
||||
|
||||
// local host
|
||||
var local_host = opt.local_host;
|
||||
|
||||
// local port
|
||||
var local_port = opt.port;
|
||||
|
||||
@@ -30,7 +34,7 @@ var connect = function(opt) {
|
||||
var assigned_domain = opt.subdomain;
|
||||
|
||||
// connect to upstream given connection parameters
|
||||
var tunnel = function (remote_host, remote_port) {
|
||||
var tunnel = function (remote_host, remote_port, dead) {
|
||||
|
||||
var remote_opt = {
|
||||
host: remote_host,
|
||||
@@ -38,7 +42,7 @@ var connect = function(opt) {
|
||||
};
|
||||
|
||||
var local_opt = {
|
||||
host: 'localhost',
|
||||
host: local_host,
|
||||
port: local_port
|
||||
};
|
||||
|
||||
@@ -49,9 +53,9 @@ var connect = function(opt) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we need a new tunnel
|
||||
if (++remote_attempts >= 3) {
|
||||
console.error('localtunnel server offline - try again');
|
||||
process.exit(-1);
|
||||
return dead();
|
||||
}
|
||||
|
||||
// connection to localtunnel server
|
||||
@@ -59,7 +63,7 @@ var connect = function(opt) {
|
||||
|
||||
remote.once('error', function(err) {
|
||||
if (err.code !== 'ECONNREFUSED') {
|
||||
local.emit('error', err);
|
||||
remote.emit('error', err);
|
||||
}
|
||||
|
||||
// retrying connection to local server
|
||||
@@ -108,35 +112,49 @@ var connect = function(opt) {
|
||||
// where to quest
|
||||
params.uri = base_uri + ((assigned_domain) ? assigned_domain : '?new');
|
||||
|
||||
// get an id from lt server and setup forwarding tcp connections
|
||||
request_url(params, function(err, body) {
|
||||
function init_tunnel() {
|
||||
// get an id from lt server and setup forwarding tcp connections
|
||||
request_url(params, function(err, body) {
|
||||
if (err) {
|
||||
ev.emit('error', new Error('tunnel server not available: ' + err.message + ', retry 1s'));
|
||||
|
||||
if (err) {
|
||||
ev.emit('error', new Error('tunnel server not available: %s, retry 1s', err.message));
|
||||
// retry interval for id request
|
||||
return setTimeout(function() {
|
||||
init_tunnel();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// store the id so we can try to get the same one
|
||||
assigned_domain = body.id;
|
||||
var max_conn = body.max_conn_count || 1;
|
||||
|
||||
var max_conn = body.max_conn_count || 1;
|
||||
for (var count = 0 ; count < max_conn ; ++count) {
|
||||
tunnel(host, port);
|
||||
}
|
||||
// after all our tunnels die, we ask for new ones
|
||||
// this might happen if the upstream server dies
|
||||
var dead = after(max_conn, function() {
|
||||
init_tunnel();
|
||||
});
|
||||
|
||||
ev.emit('url', body.url);
|
||||
});
|
||||
for (var count = 0 ; count < max_conn ; ++count) {
|
||||
tunnel(host, port, dead);
|
||||
}
|
||||
|
||||
ev.emit('url', body.url);
|
||||
});
|
||||
}
|
||||
|
||||
init_tunnel();
|
||||
|
||||
return ev;
|
||||
};
|
||||
|
||||
module.exports.connect = connect;
|
||||
|
||||
// for backwards compatibility
|
||||
// old localtunnel modules had server and client code in same module
|
||||
// so to keep .client working we expose it here
|
||||
module.exports.client = module.exports;
|
||||
|
||||
2
index.js
2
index.js
@@ -1,2 +0,0 @@
|
||||
module.exports.client = require('./client');
|
||||
module.exports.server = require('./server');
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
var chars = 'abcdefghiklmnopqrstuvwxyz';
|
||||
module.exports = function rand_id() {
|
||||
var randomstring = '';
|
||||
for (var i=0; i<4; ++i) {
|
||||
var rnum = Math.floor(Math.random() * chars.length);
|
||||
randomstring += chars[rnum];
|
||||
}
|
||||
|
||||
return randomstring;
|
||||
}
|
||||
|
||||
19
package.json
19
package.json
@@ -2,30 +2,19 @@
|
||||
"author": "Roman Shtylman <shtylman@gmail.com>",
|
||||
"name": "localtunnel",
|
||||
"description": "expose localhost to the world",
|
||||
"version": "0.0.4",
|
||||
"version": "0.1.3",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/shtylman/localtunnel.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"request": "2.11.4",
|
||||
"book": "1.2.0",
|
||||
"optimist": "0.3.4",
|
||||
"http-raw": "1.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "1.6.0"
|
||||
},
|
||||
"optionalDependencies": {},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha --ui qunit -- test",
|
||||
"start": "./bin/server"
|
||||
"after": "0.8.1"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"bin": {
|
||||
"lt": "./bin/client"
|
||||
},
|
||||
"main": "./index.js"
|
||||
"main": "./client.js"
|
||||
}
|
||||
|
||||
324
server.js
324
server.js
@@ -1,324 +0,0 @@
|
||||
|
||||
// builtin
|
||||
var http = require('http');
|
||||
var net = require('net');
|
||||
var url = require('url');
|
||||
|
||||
// here be dragons
|
||||
var HTTPParser = process.binding('http_parser').HTTPParser;
|
||||
|
||||
// vendor
|
||||
var log = require('book');
|
||||
var createRawServer = require('http-raw');
|
||||
|
||||
// local
|
||||
var rand_id = require('./lib/rand_id');
|
||||
|
||||
// id -> client http server
|
||||
var clients = {};
|
||||
|
||||
// available parsers
|
||||
var parsers = http.parsers;
|
||||
|
||||
// send this request to the appropriate client
|
||||
// in -> incoming request stream
|
||||
function proxy_request(client, req, res, rs, ws) {
|
||||
|
||||
rs = rs || req.createRawStream();
|
||||
ws = ws || res.createRawStream();
|
||||
|
||||
// socket is a tcp connection back to the user hosting the site
|
||||
var sock = client.sockets.shift();
|
||||
|
||||
// queue request
|
||||
if (!sock) {
|
||||
log.info('no more clients, queued: %s', req.url);
|
||||
rs.pause();
|
||||
client.waiting.push([req, res, rs, ws]);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info('handle req: %s', req.url);
|
||||
|
||||
// pipe incoming request into tcp socket
|
||||
// incoming request isn't allowed to end the socket back to lt client
|
||||
rs.pipe(sock, { end: false });
|
||||
|
||||
sock.ws = ws;
|
||||
sock.req = req;
|
||||
|
||||
// since tcp connection to upstream are kept open
|
||||
// invoke parsing so we know when the response is complete
|
||||
var parser = sock.parser;
|
||||
parser.reinitialize(HTTPParser.RESPONSE);
|
||||
parser.socket = sock;
|
||||
|
||||
// we have completed a response
|
||||
// the tcp socket is free again
|
||||
parser.onIncoming = function (res) {
|
||||
parser.onMessageComplete = function() {
|
||||
log.info('ended response: %s', req.url);
|
||||
|
||||
// any request we had going on is now done
|
||||
ws.end();
|
||||
|
||||
// no more forwarding
|
||||
delete sock.ws;
|
||||
delete parser.onIncoming;
|
||||
|
||||
// return socket to available pool
|
||||
client.sockets.push(sock);
|
||||
|
||||
var next = client.waiting.shift();
|
||||
if (next) {
|
||||
log.trace('popped');
|
||||
proxy_request(client, next[0], next[1], next[2], next[3]);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
rs.resume();
|
||||
}
|
||||
|
||||
function upstream_response(d, start, end) {
|
||||
var socket = this;
|
||||
|
||||
var ws = socket.ws;
|
||||
if (!ws) {
|
||||
log.warn('no stream set for req:', socket.req.url);
|
||||
return;
|
||||
}
|
||||
|
||||
ws.write(d.slice(start, end));
|
||||
|
||||
if (socket.upgraded) {
|
||||
return;
|
||||
}
|
||||
|
||||
var ret = socket.parser.execute(d, start, end - start);
|
||||
if (ret instanceof Error) {
|
||||
log.error(ret);
|
||||
parsers.free(parser);
|
||||
socket.destroy(ret);
|
||||
}
|
||||
}
|
||||
|
||||
var handle_req = function (req, res) {
|
||||
|
||||
var max_tcp_sockets = req.socket.server.max_tcp_sockets;
|
||||
|
||||
// ignore favicon
|
||||
if (req.url === '/favicon.ico') {
|
||||
res.writeHead(404);
|
||||
return res.end();
|
||||
}
|
||||
|
||||
var hostname = req.headers.host;
|
||||
if (!hostname) {
|
||||
log.trace('no hostname: %j', req.headers);
|
||||
return res.end();
|
||||
}
|
||||
|
||||
var match = hostname.match(/^([a-z]{4})[.].*/);
|
||||
if (match) {
|
||||
var client_id = match[1];
|
||||
var client = clients[client_id];
|
||||
|
||||
// no such subdomain
|
||||
if (!client) {
|
||||
log.trace('no client found for id: ' + client_id);
|
||||
res.statusCode = 404;
|
||||
return res.end();
|
||||
}
|
||||
|
||||
return proxy_request(client, req, res);
|
||||
}
|
||||
|
||||
var parsed = url.parse(req.url, true);
|
||||
|
||||
// redirect main page to github reference
|
||||
if (req.url === '/' && !parsed.query.new) {
|
||||
res.writeHead(301, { Location: 'http://shtylman.github.com/localtunnel/' });
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// at this point, the client is requesting a new tunnel setup
|
||||
// either generate an id or use the one they requested
|
||||
|
||||
var match = req.url.match(/\/([a-z]{4})?/);
|
||||
|
||||
// user can request a particular set of characters
|
||||
// will be given if not already taken
|
||||
// this is useful when the main server is restarted
|
||||
// users can keep testing with their expected ids
|
||||
var requested_id;
|
||||
if (match && match[1]) {
|
||||
requested_id = match[1];
|
||||
}
|
||||
|
||||
var id = requested_id || rand_id();
|
||||
|
||||
// if the id already exists, this client must use something else
|
||||
if (clients[id]) {
|
||||
id = rand_id();
|
||||
}
|
||||
|
||||
// sockets is a list of available sockets for the connection
|
||||
// waiting is?
|
||||
var client = clients[id] = {
|
||||
sockets: [],
|
||||
waiting: []
|
||||
};
|
||||
|
||||
var client_server = net.createServer();
|
||||
client_server.listen(function() {
|
||||
var port = client_server.address().port;
|
||||
log.info('tcp server listening on port: %d', port);
|
||||
|
||||
var url = 'http://' + id + '.' + req.headers.host;
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify({
|
||||
url: url,
|
||||
id: id,
|
||||
port: port,
|
||||
max_conn_count: max_tcp_sockets
|
||||
}));
|
||||
});
|
||||
|
||||
var conn_timeout;
|
||||
|
||||
// user has 5 seconds to connect before their slot is given up
|
||||
function maybe_tcp_close() {
|
||||
conn_timeout = setTimeout(client_server.close.bind(client_server), 5000);
|
||||
}
|
||||
|
||||
maybe_tcp_close();
|
||||
|
||||
// no longer accepting connections for this id
|
||||
client_server.on('close', function() {
|
||||
log.trace('closed tcp socket for client(%s)', id);
|
||||
clearTimeout(conn_timeout);
|
||||
delete clients[id];
|
||||
});
|
||||
|
||||
client_server.on('connection', function(socket) {
|
||||
|
||||
// no more socket connections allowed
|
||||
if (client.sockets.length >= max_tcp_sockets) {
|
||||
return socket.end();
|
||||
}
|
||||
|
||||
log.trace('new connection for id: %s', id);
|
||||
|
||||
// no need to close the client server
|
||||
clearTimeout(conn_timeout);
|
||||
|
||||
// allocate a response parser for the socket
|
||||
// it only needs one since it will reuse it
|
||||
socket.parser = parsers.alloc();
|
||||
|
||||
socket._orig_ondata = socket.ondata;
|
||||
socket.ondata = upstream_response;
|
||||
|
||||
client.sockets.push(socket);
|
||||
|
||||
socket.once('close', function(had_error) {
|
||||
log.trace('client %s closed socket', id);
|
||||
|
||||
// remove this socket
|
||||
var idx = client.sockets.indexOf(socket);
|
||||
client.sockets.splice(idx, 1);
|
||||
|
||||
log.trace('remaining client sockets: %s', client.sockets.length);
|
||||
|
||||
// no more sockets for this ident
|
||||
if (client.sockets.length === 0) {
|
||||
log.trace('all client(%s) sockets disconnected', id);
|
||||
maybe_tcp_close();
|
||||
}
|
||||
});
|
||||
|
||||
// close will be emitted after this
|
||||
socket.on('error', function(err) {
|
||||
log.error(err);
|
||||
socket.end();
|
||||
});
|
||||
});
|
||||
|
||||
client_server.on('error', function(err) {
|
||||
log.error(err);
|
||||
});
|
||||
};
|
||||
|
||||
var handle_upgrade = function(req, ws) {
|
||||
|
||||
if (req.headers.connection !== 'Upgrade') {
|
||||
return;
|
||||
}
|
||||
|
||||
var hostname = req.headers.host;
|
||||
if (!hostname) {
|
||||
return res.end();
|
||||
}
|
||||
|
||||
var match = hostname.match(/^([a-z]{4})[.].*/);
|
||||
|
||||
// not a valid client
|
||||
if (!match) {
|
||||
return res.end();
|
||||
}
|
||||
|
||||
var client_id = match[1];
|
||||
var client = clients[client_id];
|
||||
|
||||
if (!client) {
|
||||
// no such subdomain
|
||||
return res.end();
|
||||
}
|
||||
|
||||
var socket = client.sockets.shift();
|
||||
if (!socket) {
|
||||
// no available sockets to upgrade to
|
||||
return res.end();
|
||||
}
|
||||
|
||||
var stream = req.createRawStream();
|
||||
|
||||
socket.ws = ws;
|
||||
socket.upgraded = true;
|
||||
|
||||
stream.once('end', function() {
|
||||
delete socket.ws;
|
||||
|
||||
// when this ends, we just reset the socket to the lt client
|
||||
// this is easier than trying to figure anything else out
|
||||
socket.end();
|
||||
|
||||
// put socket back into available pool
|
||||
client.sockets.push(socket);
|
||||
|
||||
var next = client.waiting.shift();
|
||||
if (next) {
|
||||
log.trace('popped');
|
||||
proxy_request(client, next[0], next[1], next[2], next[3]);
|
||||
}
|
||||
});
|
||||
|
||||
stream.pipe(socket, {end: false});
|
||||
socket.once('end', ws.end.bind(ws));
|
||||
};
|
||||
|
||||
module.exports = function(opt) {
|
||||
opt = opt || {};
|
||||
|
||||
var server = createRawServer();
|
||||
|
||||
server.max_tcp_sockets = opt.max_tcp_sockets || 5;
|
||||
server.on('request', handle_req);
|
||||
server.on('upgrade', handle_upgrade);
|
||||
|
||||
return server;
|
||||
};
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
var assert = require('assert');
|
||||
|
||||
var localtunnel_server = require('../').server();
|
||||
var localtunnel_client = require('../').client;
|
||||
|
||||
test('setup localtunnel server', function(done) {
|
||||
localtunnel_server.listen(3000, function() {
|
||||
console.log('lt server on:', 3000);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('setup local http server', function(done) {
|
||||
var server = http.createServer();
|
||||
server.on('request', function(req, res) {
|
||||
res.write('foo');
|
||||
res.end();
|
||||
});
|
||||
server.listen(function() {
|
||||
var port = server.address().port;
|
||||
|
||||
test._fake_port = port;
|
||||
console.log('local http on:', port);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('setup localtunnel client', function(done) {
|
||||
var client = localtunnel_client.connect({
|
||||
host: 'http://localhost:' + 3000,
|
||||
port: test._fake_port
|
||||
});
|
||||
|
||||
client.on('url', function(url) {
|
||||
assert.ok(/^http:\/\/.*localhost:3000$/.test(url));
|
||||
test._fake_url = url;
|
||||
done();
|
||||
});
|
||||
|
||||
client.on('error', function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('query localtunnel server w/ ident', function(done) {
|
||||
var uri = test._fake_url;
|
||||
var hostname = url.parse(uri).hostname;
|
||||
|
||||
var opt = {
|
||||
host: 'localhost',
|
||||
port: 3000,
|
||||
headers: {
|
||||
host: hostname
|
||||
},
|
||||
path: '/'
|
||||
}
|
||||
|
||||
var req = http.request(opt, function(res) {
|
||||
res.setEncoding('utf8');
|
||||
var body = '';
|
||||
|
||||
res.on('data', function(chunk) {
|
||||
body += chunk;
|
||||
});
|
||||
|
||||
res.on('end', function() {
|
||||
assert.equal('foo', body);
|
||||
|
||||
// TODO(shtylman) shutdown client
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
req.end();
|
||||
});
|
||||
|
||||
test('request specific domain', function(done) {
|
||||
var client = localtunnel_client.connect({
|
||||
host: 'http://localhost:' + 3000,
|
||||
port: test._fake_port,
|
||||
subdomain: 'abcd'
|
||||
});
|
||||
|
||||
client.on('url', function(url) {
|
||||
assert.ok(/^http:\/\/abcd.localhost:3000$/.test(url));
|
||||
done();
|
||||
});
|
||||
|
||||
client.on('error', function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('shutdown', function() {
|
||||
localtunnel_server.close();
|
||||
});
|
||||
|
||||
105
test/queue.js
105
test/queue.js
@@ -1,105 +0,0 @@
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
var assert = require('assert');
|
||||
|
||||
var localtunnel_server = require('../').server({
|
||||
max_tcp_sockets: 1
|
||||
});
|
||||
|
||||
var localtunnel_client = require('../').client;
|
||||
|
||||
var server;
|
||||
|
||||
test('setup localtunnel server', function(done) {
|
||||
localtunnel_server.listen(3000, function() {
|
||||
console.log('lt server on:', 3000);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('setup local http server', function(done) {
|
||||
server = http.createServer();
|
||||
server.on('request', function(req, res) {
|
||||
// respond sometime later
|
||||
setTimeout(function() {
|
||||
res.setHeader('x-count', req.headers['x-count']);
|
||||
res.end('foo');
|
||||
}, 100);
|
||||
});
|
||||
|
||||
server.listen(function() {
|
||||
var port = server.address().port;
|
||||
|
||||
test._fake_port = port;
|
||||
console.log('local http on:', port);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
test('setup localtunnel client', function(done) {
|
||||
var client = localtunnel_client.connect({
|
||||
host: 'http://localhost:' + 3000,
|
||||
port: test._fake_port
|
||||
});
|
||||
|
||||
client.on('url', function(url) {
|
||||
assert.ok(/^http:\/\/.*localhost:3000$/.test(url));
|
||||
test._fake_url = url;
|
||||
done();
|
||||
});
|
||||
|
||||
client.on('error', function(err) {
|
||||
console.error(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('query localtunnel server w/ ident', function(done) {
|
||||
var uri = test._fake_url;
|
||||
var hostname = url.parse(uri).hostname;
|
||||
|
||||
var count = 0;
|
||||
var opt = {
|
||||
host: 'localhost',
|
||||
port: 3000,
|
||||
agent: false,
|
||||
headers: {
|
||||
host: hostname
|
||||
},
|
||||
path: '/'
|
||||
}
|
||||
|
||||
var num_requests = 2;
|
||||
var responses = 0;
|
||||
|
||||
function maybe_done() {
|
||||
if (++responses >= num_requests) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function make_req() {
|
||||
opt.headers['x-count'] = count++;
|
||||
http.get(opt, function(res) {
|
||||
res.setEncoding('utf8');
|
||||
var body = '';
|
||||
|
||||
res.on('data', function(chunk) {
|
||||
body += chunk;
|
||||
});
|
||||
|
||||
res.on('end', function() {
|
||||
assert.equal('foo', body);
|
||||
maybe_done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
for (var i=0 ; i<num_requests ; ++i) {
|
||||
make_req();
|
||||
}
|
||||
});
|
||||
|
||||
test('shutdown', function() {
|
||||
localtunnel_server.close();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user