mirror of
https://github.com/bitinflow/localtunnel.git
synced 2026-03-19 00:15:55 +00:00
remove server components
moved to localtunnel-server repo
This commit is contained in:
45
README.md
45
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.
|
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.
|
||||||
|
|
||||||
@@ -24,43 +24,9 @@ Thats it! It will connect to the tunnel server, setup the tunnel, and tell you w
|
|||||||
|
|
||||||
You can restart your local server all you want, ```lt``` is smart enough to detect this and reconnect once it is back.
|
You can restart your local server all you want, ```lt``` is smart enough to detect this and reconnect once it is back.
|
||||||
|
|
||||||
### custom server
|
|
||||||
|
|
||||||
The default localtunnel client connects to the ```localtunnel.me``` server. You can however easily setup and run your own server. In order to run your own localtunnel server you must ensure that your server can meet the following requirements:
|
|
||||||
|
|
||||||
* You can setup DNS entries for your domain.tld and for *.domain.tld (or sub.domain.tld and *.sub.domain.tld)
|
|
||||||
* The server can accept incoming TCP connections for any non-root TCP port (ports over 1000).
|
|
||||||
|
|
||||||
The above are important as the client will ask the server for a subdomain under a particular domain. The server will listen on any OS assigned TCP port for client connections
|
|
||||||
|
|
||||||
#### setup
|
|
||||||
|
|
||||||
```shell
|
|
||||||
// pick a place where the files will live
|
|
||||||
git clone git://github.com/shtylman/localtunnel.git
|
|
||||||
cd localtunnel
|
|
||||||
npm install
|
|
||||||
|
|
||||||
// server set to run on port 1234
|
|
||||||
bin/server --port 1324
|
|
||||||
```
|
|
||||||
|
|
||||||
The localtunnel server is now running and waiting for client requests on port 1234. You will most likely want to setup a reverse proxy to listen on port 80 (or start localtunnel on port 80 directly).
|
|
||||||
|
|
||||||
#### use your server
|
|
||||||
|
|
||||||
You can now use your domain with the ```--host``` flag for the ```lt``` client.
|
|
||||||
```shell
|
|
||||||
lt --host http://sub.example.tld:1234 --port 9000
|
|
||||||
```
|
|
||||||
|
|
||||||
You will be assigned a url similar to ```qdci.sub.example.com:1234```
|
|
||||||
|
|
||||||
If your server is being a reverse proxy (i.e. nginx) and is able to listen on port 80, then you do not need the ```:1234``` part of the hostname for the ```lt``` client
|
|
||||||
|
|
||||||
## API ##
|
## 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
|
```javascript
|
||||||
var lt_client = require('localtunnel').client;
|
var lt_client = require('localtunnel').client;
|
||||||
@@ -82,3 +48,10 @@ client.on('error', function(err) {
|
|||||||
// uh oh!
|
// uh oh!
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## server ##
|
||||||
|
|
||||||
|
See shtylman/localtunnel-server for details on the server that powers localtunnel.
|
||||||
|
|
||||||
|
## License ##
|
||||||
|
MIT
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
var lt_client = require(__dirname + '/../client');
|
var lt_client = require('../client');
|
||||||
|
|
||||||
var argv = require('optimist')
|
var argv = require('optimist')
|
||||||
.usage('Usage: $0 --port [num]')
|
.usage('Usage: $0 --port [num]')
|
||||||
|
|||||||
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
|
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// builtin
|
|
||||||
var net = require('net');
|
var net = require('net');
|
||||||
var url = require('url');
|
var url = require('url');
|
||||||
var request = require('request');
|
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
|
||||||
|
var request = require('request');
|
||||||
|
|
||||||
// request upstream url and connection info
|
// request upstream url and connection info
|
||||||
var request_url = function(params, cb) {
|
var request_url = function(params, cb) {
|
||||||
request(params, function(err, res, body) {
|
request(params, function(err, res, body) {
|
||||||
@@ -140,3 +140,7 @@ var connect = function(opt) {
|
|||||||
|
|
||||||
module.exports.connect = connect;
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
16
package.json
16
package.json
@@ -9,24 +9,12 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"request": "2.11.4",
|
"request": "2.11.4",
|
||||||
"book": "1.2.0",
|
"optimist": "0.3.4"
|
||||||
"optimist": "0.3.4",
|
|
||||||
"http-raw": "1.1.0",
|
|
||||||
"debug": "0.7.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mocha": "1.6.0"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {},
|
|
||||||
"engines": {
|
|
||||||
"node": "*"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "mocha --ui qunit -- test",
|
|
||||||
"start": "./bin/server"
|
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"lt": "./bin/client"
|
"lt": "./bin/client"
|
||||||
},
|
},
|
||||||
"main": "./index.js"
|
"main": "./client.js"
|
||||||
}
|
}
|
||||||
|
|||||||
339
server.js
339
server.js
@@ -1,339 +0,0 @@
|
|||||||
var http = require('http');
|
|
||||||
var net = require('net');
|
|
||||||
var url = require('url');
|
|
||||||
|
|
||||||
var log = require('book');
|
|
||||||
var debug = require('debug')('localtunnel-server');
|
|
||||||
var createRawServer = require('http-raw');
|
|
||||||
|
|
||||||
var rand_id = require('./lib/rand_id');
|
|
||||||
|
|
||||||
// here be dragons, understanding of node http internals will be required
|
|
||||||
var HTTPParser = process.binding('http_parser').HTTPParser;
|
|
||||||
|
|
||||||
// id -> client http server
|
|
||||||
var clients = {};
|
|
||||||
|
|
||||||
// available parsers for requests
|
|
||||||
// this is borrowed from how node does things by preallocating parsers
|
|
||||||
var parsers = http.parsers;
|
|
||||||
|
|
||||||
// send this request to the appropriate client
|
|
||||||
// in -> incoming request stream
|
|
||||||
function proxy_request(client, req, res, rs, ws) {
|
|
||||||
|
|
||||||
// socket is a tcp connection back to the user hosting the site
|
|
||||||
var sock = client.sockets.shift();
|
|
||||||
|
|
||||||
// queue request
|
|
||||||
if (!sock) {
|
|
||||||
debug('no more clients, queued: %s', req.url);
|
|
||||||
rs.pause();
|
|
||||||
client.waiting.push([req, res, rs, ws]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug('handle req: %s', req.url);
|
|
||||||
|
|
||||||
// pipe incoming request into tcp socket
|
|
||||||
// incoming request will close the socket when done
|
|
||||||
// lt client should establish a new socket once request is finished
|
|
||||||
// we do this instead of keeping socket open to make things easier
|
|
||||||
rs.pipe(sock);
|
|
||||||
|
|
||||||
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() {
|
|
||||||
debug('ended response: %s', req.url);
|
|
||||||
|
|
||||||
// any request we had going on is now done
|
|
||||||
ws.end();
|
|
||||||
sock.end();
|
|
||||||
|
|
||||||
// no more forwarding
|
|
||||||
delete sock.ws;
|
|
||||||
delete sock.req;
|
|
||||||
delete parser.onIncoming;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
rs.resume();
|
|
||||||
}
|
|
||||||
|
|
||||||
function upstream_response(d, start, end) {
|
|
||||||
var socket = this;
|
|
||||||
|
|
||||||
var ws = socket.ws;
|
|
||||||
if (!ws) {
|
|
||||||
return log.warn('no stream set for req:', socket.req.url);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// without a hostname, we won't know who the request is for
|
|
||||||
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
|
|
||||||
// we use 502 error to the client to signify we can't service the request
|
|
||||||
if (!client) {
|
|
||||||
debug('no client found for id: ' + client_id);
|
|
||||||
res.statusCode = 502;
|
|
||||||
return res.end('localtunnel error: no active client for \'' + client_id + '\'');
|
|
||||||
}
|
|
||||||
|
|
||||||
var rs = req.createRawStream();
|
|
||||||
var ws = res.createRawStream();
|
|
||||||
|
|
||||||
return proxy_request(client, req, res, rs, ws);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// NOTE: everything below is for new client setup (not proxied request)
|
|
||||||
|
|
||||||
// ignore favicon requests
|
|
||||||
if (req.url === '/favicon.ico') {
|
|
||||||
res.writeHead(404);
|
|
||||||
return res.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
var parsed = url.parse(req.url, true);
|
|
||||||
|
|
||||||
// redirect main page to github reference for root requests
|
|
||||||
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 is assigned a random id
|
|
||||||
if (clients[id]) {
|
|
||||||
id = rand_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
// sockets is a list of available sockets for the connection
|
|
||||||
// waiting is a list of requests still needing to be handled
|
|
||||||
var client = clients[id] = {
|
|
||||||
sockets: [],
|
|
||||||
waiting: []
|
|
||||||
};
|
|
||||||
|
|
||||||
// new tcp server to service requests for this client
|
|
||||||
var client_server = net.createServer();
|
|
||||||
client_server.listen(function() {
|
|
||||||
var port = client_server.address().port;
|
|
||||||
debug('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({
|
|
||||||
// full url for internet facing requests
|
|
||||||
url: url,
|
|
||||||
// "subdomain" part
|
|
||||||
id: id,
|
|
||||||
// port for lt client tcp connections
|
|
||||||
port: port,
|
|
||||||
// maximum number of tcp connections allowed by lt client
|
|
||||||
max_conn_count: max_tcp_sockets
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
// track initial user connection setup
|
|
||||||
var conn_timeout;
|
|
||||||
|
|
||||||
// user has 5 seconds to connect before their slot is given up
|
|
||||||
function maybe_tcp_close() {
|
|
||||||
clearTimeout(conn_timeout);
|
|
||||||
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];
|
|
||||||
|
|
||||||
// clear waiting by ending responses, (requests?)
|
|
||||||
client.waiting.forEach(function(waiting) {
|
|
||||||
waiting[1].end();
|
|
||||||
waiting[3].end(); // write stream
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// new tcp connection from lt client
|
|
||||||
client_server.on('connection', function(socket) {
|
|
||||||
|
|
||||||
// no more socket connections allowed
|
|
||||||
if (client.sockets.length >= max_tcp_sockets) {
|
|
||||||
return socket.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
debug('new connection for id: %s', id);
|
|
||||||
|
|
||||||
// a single connection is enough to keep client id slot open
|
|
||||||
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;
|
|
||||||
|
|
||||||
socket.once('close', function(had_error) {
|
|
||||||
debug('client %s closed socket (error: %s)', id, had_error);
|
|
||||||
|
|
||||||
// what if socket was servicing a request at this time?
|
|
||||||
// then it will be put back in available after right?
|
|
||||||
|
|
||||||
// remove this socket
|
|
||||||
var idx = client.sockets.indexOf(socket);
|
|
||||||
if (idx >= 0) {
|
|
||||||
client.sockets.splice(idx, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to track total sockets, not just active available
|
|
||||||
|
|
||||||
debug('remaining client sockets: %s', client.sockets.length);
|
|
||||||
|
|
||||||
// no more sockets for this ident
|
|
||||||
if (client.sockets.length === 0) {
|
|
||||||
debug('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.sockets.push(socket);
|
|
||||||
|
|
||||||
var next = client.waiting.shift();
|
|
||||||
if (next) {
|
|
||||||
debug('handling queued request');
|
|
||||||
proxy_request(client, next[0], next[1], next[2], next[3]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
|
|
||||||
stream.pipe(socket);
|
|
||||||
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