Sub host options: ala whitecolor #82
@@ -2,3 +2,8 @@ language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- "9.2"
|
||||
addons:
|
||||
hosts:
|
||||
- host
|
||||
- sub.host
|
||||
- nested.sub.host
|
||||
@@ -1,12 +1,12 @@
|
||||
# localtunnel-server
|
||||
|
||||
[](https://travis-ci.org/localtunnel/server)
|
||||
[](https://travis-ci.org/gojisystems/gojiconnect-server)
|
||||
|
||||
|
|
||||
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.
|
||||
|
||||
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,29 @@ 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
|
||||
**Note** that to use sub-hosts you should pass `--sub-host` option during set up. Pass this argument for every sub-host level.
|
||||
Alternatively, `--sub-host` may be passed in as a single argument with the all sub hosts in a string `"nested.sub"`
|
||||
|
||||
```shell
|
||||
# server set to run on port 1234 w/ a sub host to allow for this URL to work "http://sub.example.com"
|
||||
bin/server --port 1234 \
|
||||
--sub-host sub
|
||||
```
|
||||
|
||||
```shell
|
||||
# server set to run on port 1234 w/ a sub host to allow for this URL to work "http://nested.sub.example.com"
|
||||
bin/server --port 1234 \
|
||||
--sub-host nested \
|
||||
--sub-host sub
|
||||
```
|
||||
**Alternative**
|
||||
```shell
|
||||
# server set to run on port 1234 w/ a sub host to allow for this URL to work "http://nested.sub.example.com"
|
||||
bin/server --port 1234 \
|
||||
--sub-host "nested.sub"
|
||||
```
|
||||
|
||||
## use your server
|
||||
|
||||
You can now use your domain with the `--host` flag for the `lt` client.
|
||||
|
||||
@@ -41,6 +63,7 @@ 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.
|
||||
|
||||
|
||||
## Deploy
|
||||
|
||||
You can deploy your own localtunnel server using the prebuilt docker image.
|
||||
@@ -54,5 +77,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);
|
||||
|
||||
if (subdomain && opt.subHost) {
|
||||
const _subdomain = subdomain.replace(/\./g,'');
|
||||
const subHosts = Array.isArray(opt.subHost)
|
||||
? opt.subHost : [opt.subHost]
|
||||
const subHost = subHosts.reduce((found, sub) => {
|
||||
sub = sub.replace(/\./g,'');
|
||||
return found + (_subdomain.includes(sub) ? sub : sub.includes(_subdomain) ? _subdomain : '');
|
||||
},'')
|
||||
subdomain = subHost.length > 0
|
||||
? _subdomain.slice(0, -(subHost.length))
|
||||
: subdomain
|
||||
}
|
||||
|
||||
return subdomain;
|
||||
}
|
||||
|
||||
module.exports = function(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 = {
|
||||
@@ -110,8 +124,9 @@ module.exports = function(opt) {
|
||||
res.end('Host header is required');
|
||||
return;
|
||||
}
|
||||
|
||||
const clientId = GetClientIdFromHostname(hostname,opt);
|
||||
|
||||
const clientId = GetClientIdFromHostname(hostname);
|
||||
if (!clientId) {
|
||||
appCallback(req, res);
|
||||
return;
|
||||
@@ -129,13 +144,13 @@ module.exports = function(opt) {
|
||||
server.on('upgrade', (req, socket, head) => {
|
||||
const hostname = req.headers.host;
|
||||
if (!hostname) {
|
||||
sock.destroy();
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
const clientId = GetClientIdFromHostname(hostname);
|
||||
const clientId = GetClientIdFromHostname(hostname,opt);
|
||||
if (!clientId) {
|
||||
sock.destroy();
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
subHost: ['nested','sub']
|
||||
});
|
||||
|
||||
var lt_server_port
|
||||
|
||||
suite('nested-sub');
|
||||
|
||||
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 (sub-host)', function (done) {
|
||||
var opt = {
|
||||
host: 'http://nested.sub.host:' + lt_server_port,
|
||||
subdomain: 'txy-z'
|
||||
};
|
||||
|
||||
localtunnel(test._fake_port, opt, function (err, tunnel) {
|
||||
assert.ifError(err);
|
||||
var url = tunnel.url;
|
||||
assert.ok(new RegExp('^http:\/\/.*nested.sub.host:' + 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.host',
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
subHost: ['nested.sub']
|
||||
});
|
||||
|
||||
var lt_server_port
|
||||
|
||||
suite('nested.sub');
|
||||
|
||||
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 (sub-host)', function (done) {
|
||||
var opt = {
|
||||
host: 'http://nested.sub.host:' + lt_server_port,
|
||||
subdomain: 'txy-z'
|
||||
};
|
||||
|
||||
localtunnel(test._fake_port, opt, function (err, tunnel) {
|
||||
assert.ifError(err);
|
||||
var url = tunnel.url;
|
||||
assert.ok(new RegExp('^http:\/\/.*nested.sub.host:' + 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.host',
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
+4
-2
@@ -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']
|
||||
});
|
||||
|
||||
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) {
|
||||
@@ -65,4 +67,4 @@ test('should respond to request', function(done) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+3
-2
@@ -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
|
||||
@@ -71,4 +72,4 @@ test('websocket server request', function(done) {
|
||||
ws.on('open', function open() {
|
||||
ws.send('something');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
var assert = require('assert');
|
||||
var localtunnel = require('localtunnel');
|
||||
var WebSocket = require('ws');
|
||||
var WebSocketServer = require('ws').Server;
|
||||
|
||||
var localtunnel_server = require('../server')({
|
||||
max_tcp_sockets: 2,
|
||||
subHost: ['sub']
|
||||
});
|
||||
|
||||
var lt_server_port
|
||||
|
||||
suite('websocket.sub');
|
||||
|
||||
before('set up localtunnel server', function(done) {
|
||||
var server = localtunnel_server.listen(function() {
|
||||
lt_server_port = server.address().port;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
before('set up local websocket server', function(done) {
|
||||
var wss = new WebSocketServer({ port: 0 }, function() {
|
||||
test._fake_port = wss._server.address().port;
|
||||
done();
|
||||
});
|
||||
|
||||
wss.on('error', function(err) {
|
||||
done(err);
|
||||
});
|
||||
|
||||
wss.on('connection', function connection(ws) {
|
||||
ws.on('error', function(err) {
|
||||
done(err);
|
||||
});
|
||||
|
||||
ws.on('message', function incoming(message) {
|
||||
ws.send(message);
|
||||
});
|
||||
});
|
||||
});
|
||||
test('set up localtunnel client (sub host)', function (done) {
|
||||
var opt = {
|
||||
host: 'http://sub.host:' + lt_server_port
|
||||
};
|
||||
|
||||
localtunnel(test._fake_port, opt, function (err, tunnel) {
|
||||
assert.ifError(err);
|
||||
var url = tunnel.url;
|
||||
assert.ok(new RegExp('^http:\/\/.*sub.host:' + 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.host:' + 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
When making a PR please don't alter files in this way as such a change can't be merged into this repo as-is. This is great for your repo but for the PR it should be removed.