Sub host options #65
@@ -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,7 +41,9 @@ 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
|
||||
Not 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,7 @@ docker run -d \
|
||||
--restart always \
|
||||
--name localtunnel \
|
||||
--net host \
|
||||
defunctzombie/localtunnel-server:latest --port 3000
|
||||
defunctzombie/localtunnel-server:latest --port 3000 --sub-host sub
|
||||
```
|
||||
|
||||
|
||||
|
||||
+6
-1
@@ -17,6 +17,9 @@ const argv = optimist
|
||||
default: '80',
|
||||
describe: 'listen on this port for outside requests'
|
||||
})
|
||||
.options('sub-host', {
|
||||
describe: 'additionally listen on this sub host for outside requests (can use this option multiple times)'
|
||||
})
|
||||
.options('max-sockets', {
|
||||
default: 10,
|
||||
describe: 'maximum number of tcp sockets each client is allowed to establish at one time (the tunnels)'
|
||||
@@ -30,7 +33,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, () => {
|
||||
|
||||
@@ -40,14 +40,27 @@ const stats = {
|
||||
|
||||
// handle proxying a request to a client
|
||||
// will wait for a tunnel socket to become available
|
||||
function maybe_bounce(req, res, sock, head) {
|
||||
function maybe_bounce(req, res, sock, head, opt) {
|
||||
// without a hostname, we won't know who the request is for
|
||||
const hostname = req.headers.host;
|
||||
if (!hostname) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const subdomain = tldjs.getSubdomain(hostname);
|
||||
let subdomain = tldjs.getSubdomain(hostname);
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
if (!subdomain) {
|
||||
return false;
|
||||
}
|
||||
@@ -271,9 +284,8 @@ module.exports = function(opt) {
|
||||
|
||||
app.get('/:req_id', function(req, res, next) {
|
||||
const req_id = req.params.req_id;
|
||||
|
||||
// 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 err = new Error('Invalid subdomain. Subdomains must be lowercase and between 4 and 63 alphanumeric characters.');
|
||||
err.statusCode = 403;
|
||||
return next(err);
|
||||
@@ -312,7 +324,7 @@ module.exports = function(opt) {
|
||||
});
|
||||
|
||||
debug('request %s', req.url);
|
||||
if (maybe_bounce(req, res, null, null)) {
|
||||
if (maybe_bounce(req, res, null, null, opt)) {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -328,7 +340,7 @@ module.exports = function(opt) {
|
||||
console.error('ws socket', err);
|
||||
});
|
||||
|
||||
if (maybe_bounce(req, null, socket, head)) {
|
||||
if (maybe_bounce(req, null, socket, head, opt)) {
|
||||
return;
|
||||
};
|
||||
|
||||
|
||||
+36
-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']
|
||||
});
|
||||
|
||||
var lt_server_port
|
||||
@@ -66,3 +67,37 @@ test('should respond to request', function(done) {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('set up localtunnel client (sub-host)', function (done) {
|
||||
var opt = {
|
||||
host: 'http://nested.sub.host.dev:' + lt_server_port,
|
||||
subdomain: 'txyz'
|
||||
};
|
||||
|
||||
localtunnel(test._fake_port, opt, function (err, tunnel) {
|
||||
assert.ifError(err);
|
||||
var url = tunnel.url;
|
||||
assert.ok(new RegExp('^http:\/\/.*nested.sub.host.dev:' + lt_server_port + '$').test(url));
|
||||
test._fake_url = url;
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('should respond to request (sub-host)', function (done) {
|
||||
var opt = {
|
||||
host: 'txyz.nested.sub.host.dev',
|
||||
port: lt_server_port,
|
||||
};
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+29
-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: ['nested.sub', 'sub']
|
||||
});
|
||||
|
||||
var lt_server_port
|
||||
@@ -72,3 +73,30 @@ test('websocket server request', function(done) {
|
||||
ws.send('something');
|
||||
});
|
||||
});
|
||||
|
||||
test('set up localtunnel client (sub host)', function (done) {
|
||||
var opt = {
|
||||
host: 'http://sub.host.dev:' + 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.dev:' + lt_server_port + '$').test(url));
|
||||
test._fake_url = url;
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
|
||||
test('websocket server request (sub-host)', function (done) {
|
||||
var ws = new WebSocket(test._fake_url);
|
||||
|
||||
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