added subdomain support
This commit is contained in:
@@ -6,7 +6,7 @@ localtunnel exposes your localhost to the world for easy testing and sharing! No
|
||||
|
||||
This repo is the server component. If you are just looking for the CLI localtunnel app, see (https://github.com/localtunnel/localtunnel).
|
||||
|
||||
## overview ##
|
||||
## overview
|
||||
|
||||
The default localtunnel client connects to the `localtunnel.me` server. You can, however, easily set up and run your own server. In order to run your own localtunnel server you must ensure that your server can meet the following requirements:
|
||||
|
||||
@@ -15,7 +15,7 @@ The default localtunnel client connects to the `localtunnel.me` server. You can,
|
||||
|
||||
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
|
||||
## setup
|
||||
|
||||
```shell
|
||||
# pick a place where the files will live
|
||||
@@ -29,7 +29,7 @@ bin/server --port 1234
|
||||
|
||||
The localtunnel server is now running and waiting for client requests on port 1234. You will most likely want to set up a reverse proxy to listen on port 80 (or start localtunnel on port 80 directly).
|
||||
|
||||
#### use your server
|
||||
## use your server
|
||||
|
||||
You can now use your domain with the `--host` flag for the `lt` client.
|
||||
|
||||
@@ -41,6 +41,8 @@ You will be assigned a URL similar to `qdci.sub.example.com:1234`.
|
||||
|
||||
If your server is acting as 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.
|
||||
|
||||
Note that to use sub-hosts you should pass `--sub-host` option to server
|
||||
|
||||
## Deploy
|
||||
|
||||
You can deploy your own localtunnel server using the prebuilt docker image.
|
||||
@@ -54,5 +56,5 @@ docker run -d \
|
||||
--restart always \
|
||||
--name localtunnel \
|
||||
--net host \
|
||||
defunctzombie/localtunnel-server:latest --port 3000
|
||||
defunctzombie/localtunnel-server:latest --port 3000 --sub-host sub
|
||||
```
|
||||
|
||||
+7
-1
@@ -17,6 +17,10 @@ const argv = optimist
|
||||
default: '80',
|
||||
describe: 'listen on this port for outside requests'
|
||||
})
|
||||
.options('sub-host', {
|
||||
describe: 'additionally listen on these sub hosts for outside requests',
|
||||
type: 'array'
|
||||
})
|
||||
.options('max-sockets', {
|
||||
default: 10,
|
||||
describe: 'maximum number of tcp sockets each client is allowed to establish at one time (the tunnels)'
|
||||
@@ -30,7 +34,9 @@ if (argv.help) {
|
||||
|
||||
const server = require('../server')({
|
||||
max_tcp_sockets: argv['max-sockets'],
|
||||
secure: argv.secure
|
||||
secure: argv.secure,
|
||||
port: argv.port,
|
||||
subHost: argv['sub-host']
|
||||
});
|
||||
|
||||
server.listen(argv.port, () => {
|
||||
|
||||
Generated
+2134
File diff suppressed because it is too large
Load Diff
@@ -10,11 +10,27 @@ import rand_id from './lib/rand_id';
|
||||
|
||||
const debug = Debug('localtunnel:server');
|
||||
|
||||
function GetClientIdFromHostname(hostname) {
|
||||
return tldjs.getSubdomain(hostname);
|
||||
function GetClientIdFromHostname(hostname,opt) {
|
||||
let subdomain = tldjs.getSubdomain(hostname);
|
||||
console.log(opt,'option');
|
||||
console.log(hostname,'hostname');
|
||||
console.log(subdomain,'subdomain');
|
||||
if (subdomain && opt.subHost) {
|
||||
const subHosts = Array.isArray(opt.subHost)
|
||||
? opt.subHost : [opt.subHost]
|
||||
const subHost = subHosts.reduce((found, sub) => {
|
||||
return found
|
||||
|| (subdomain.slice(-sub.length) == sub ? sub : '')
|
||||
}, '')
|
||||
subdomain = subHost
|
||||
? subdomain.slice(0, -(subHost.length + 1))
|
||||
: subdomain
|
||||
}
|
||||
return subdomain;
|
||||
}
|
||||
|
||||
module.exports = function(opt) {
|
||||
console.log(opt);
|
||||
opt = opt || {};
|
||||
|
||||
const manager = new ClientManager(opt);
|
||||
@@ -42,7 +58,6 @@ module.exports = function(opt) {
|
||||
// root endpoint
|
||||
app.use(async (ctx, next) => {
|
||||
const path = ctx.request.path;
|
||||
|
||||
// skip anything not on the root path
|
||||
if (path !== '/') {
|
||||
await next();
|
||||
@@ -54,7 +69,6 @@ module.exports = function(opt) {
|
||||
const req_id = rand_id();
|
||||
debug('making new client with id %s', req_id);
|
||||
const info = await manager.newClient(req_id);
|
||||
|
||||
const url = schema + '://' + info.id + '.' + ctx.request.host;
|
||||
info.url = url;
|
||||
ctx.body = info;
|
||||
@@ -81,7 +95,7 @@ module.exports = function(opt) {
|
||||
const req_id = parts[1];
|
||||
|
||||
// limit requested hostnames to 63 characters
|
||||
if (! /^[a-z0-9]{4,63}$/.test(req_id)) {
|
||||
if (! /^[a-z0-9-]{4,63}$/.test(req_id)) {
|
||||
const msg = 'Invalid subdomain. Subdomains must be lowercase and between 4 and 63 alphanumeric characters.';
|
||||
ctx.status = 403;
|
||||
ctx.body = {
|
||||
@@ -111,7 +125,8 @@ module.exports = function(opt) {
|
||||
return;
|
||||
}
|
||||
|
||||
const clientId = GetClientIdFromHostname(hostname);
|
||||
const clientId = GetClientIdFromHostname(hostname,opt);
|
||||
console.log(clientId,'clientId response');
|
||||
if (!clientId) {
|
||||
appCallback(req, res);
|
||||
return;
|
||||
@@ -133,7 +148,7 @@ module.exports = function(opt) {
|
||||
return;
|
||||
}
|
||||
|
||||
const clientId = GetClientIdFromHostname(hostname);
|
||||
const clientId = GetClientIdFromHostname(hostname,opt);
|
||||
if (!clientId) {
|
||||
sock.destroy();
|
||||
return;
|
||||
|
||||
+41
-1
@@ -4,7 +4,8 @@ var assert = require('assert');
|
||||
var localtunnel = require('localtunnel');
|
||||
|
||||
var localtunnel_server = require('../server')({
|
||||
max_tcp_sockets: 2
|
||||
max_tcp_sockets: 2,
|
||||
subHost: ['nested.sub','nested','sub']
|
||||
});
|
||||
|
||||
var lt_server_port
|
||||
@@ -32,6 +33,7 @@ test('set up local http server', function(done) {
|
||||
test('set up localtunnel client', function(done) {
|
||||
var opt = {
|
||||
host: 'http://localhost:' + lt_server_port,
|
||||
subdomain: 'txyz'
|
||||
};
|
||||
|
||||
localtunnel(test._fake_port, opt, function(err, tunnel) {
|
||||
@@ -66,3 +68,41 @@ test('should respond to request', function(done) {
|
||||
});
|
||||
});
|
||||
});
|
||||
test('set up localtunnel client (sub-host)', function (done) {
|
||||
var opt = {
|
||||
host: 'http://nested.sub.localhost:' + lt_server_port,
|
||||
subdomain: 'txyz'
|
||||
};
|
||||
|
||||
localtunnel(test._fake_port, opt, function (err, tunnel) {
|
||||
console.log(url);
|
||||
assert.ifError(err);
|
||||
var url = tunnel.url;
|
||||
assert.ok(new RegExp('^http:\/\/.*nested.sub.localhost:' + lt_server_port + '$').test(url));
|
||||
test._fake_url = url;
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('should respond to request (sub-host)', function (done) {
|
||||
var hostname = url.parse(test._fake_url).hostname;
|
||||
var opt = {
|
||||
host: 'nested.sub.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();
|
||||
});
|
||||
});
|
||||
});
|
||||
+33
-1
@@ -6,7 +6,8 @@ var WebSocket = require('ws');
|
||||
var WebSocketServer = require('ws').Server;
|
||||
|
||||
var localtunnel_server = require('../server')({
|
||||
max_tcp_sockets: 2
|
||||
max_tcp_sockets: 2,
|
||||
subHost: ['sub']
|
||||
});
|
||||
|
||||
var lt_server_port
|
||||
@@ -72,3 +73,34 @@ test('websocket server request', function(done) {
|
||||
ws.send('something');
|
||||
});
|
||||
});
|
||||
test('set up localtunnel client (sub host)', function (done) {
|
||||
var opt = {
|
||||
host: 'http://sub.localhost:' + lt_server_port
|
||||
};
|
||||
|
||||
localtunnel(test._fake_port, opt, function (err, tunnel) {
|
||||
assert.ifError(err);
|
||||
var url = tunnel.url;
|
||||
assert.ok(new RegExp('^http:\/\/.*sub.localhost:' + lt_server_port + '$').test(url));
|
||||
test._fake_url = url;
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('websocket server request (sub-host)', function (done) {
|
||||
var hostname = url.parse(test._fake_url).hostname;
|
||||
var ws = new WebSocket('http://sub.localhost:' + lt_server_port, {
|
||||
headers: {
|
||||
host: hostname + '.tld'
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('message', function (msg) {
|
||||
assert.equal(msg, 'something');
|
||||
done();
|
||||
});
|
||||
|
||||
ws.on('open', function open() {
|
||||
ws.send('something');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user