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).
|
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:
|
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.
|
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
|
```shell
|
||||||
# pick a place where the files will live
|
# 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).
|
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.
|
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.
|
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.
|
You can deploy your own localtunnel server using the prebuilt docker image.
|
||||||
|
|
||||||
@@ -54,5 +56,7 @@ docker run -d \
|
|||||||
--restart always \
|
--restart always \
|
||||||
--name localtunnel \
|
--name localtunnel \
|
||||||
--net host \
|
--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',
|
default: '80',
|
||||||
describe: 'listen on this port for outside requests'
|
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', {
|
.options('max-sockets', {
|
||||||
default: 10,
|
default: 10,
|
||||||
describe: 'maximum number of tcp sockets each client is allowed to establish at one time (the tunnels)'
|
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')({
|
const server = require('../server')({
|
||||||
max_tcp_sockets: argv['max-sockets'],
|
max_tcp_sockets: argv['max-sockets'],
|
||||||
secure: argv.secure
|
secure: argv.secure,
|
||||||
|
port: argv.port,
|
||||||
|
subHost: argv['sub-host']
|
||||||
});
|
});
|
||||||
|
|
||||||
server.listen(argv.port, () => {
|
server.listen(argv.port, () => {
|
||||||
|
|||||||
@@ -40,14 +40,27 @@ const stats = {
|
|||||||
|
|
||||||
// handle proxying a request to a client
|
// handle proxying a request to a client
|
||||||
// will wait for a tunnel socket to become available
|
// 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
|
// without a hostname, we won't know who the request is for
|
||||||
const hostname = req.headers.host;
|
const hostname = req.headers.host;
|
||||||
if (!hostname) {
|
if (!hostname) {
|
||||||
return false;
|
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) {
|
if (!subdomain) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -271,9 +284,8 @@ module.exports = function(opt) {
|
|||||||
|
|
||||||
app.get('/:req_id', function(req, res, next) {
|
app.get('/:req_id', function(req, res, next) {
|
||||||
const req_id = req.params.req_id;
|
const req_id = req.params.req_id;
|
||||||
|
|
||||||
// limit requested hostnames to 63 characters
|
// 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.');
|
const err = new Error('Invalid subdomain. Subdomains must be lowercase and between 4 and 63 alphanumeric characters.');
|
||||||
err.statusCode = 403;
|
err.statusCode = 403;
|
||||||
return next(err);
|
return next(err);
|
||||||
@@ -312,7 +324,7 @@ module.exports = function(opt) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
debug('request %s', req.url);
|
debug('request %s', req.url);
|
||||||
if (maybe_bounce(req, res, null, null)) {
|
if (maybe_bounce(req, res, null, null, opt)) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -328,7 +340,7 @@ module.exports = function(opt) {
|
|||||||
console.error('ws socket', err);
|
console.error('ws socket', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (maybe_bounce(req, null, socket, head)) {
|
if (maybe_bounce(req, null, socket, head, opt)) {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+36
-1
@@ -4,7 +4,8 @@ var assert = require('assert');
|
|||||||
var localtunnel = require('localtunnel');
|
var localtunnel = require('localtunnel');
|
||||||
|
|
||||||
var localtunnel_server = require('../server')({
|
var localtunnel_server = require('../server')({
|
||||||
max_tcp_sockets: 2
|
max_tcp_sockets: 2,
|
||||||
|
subHost: ['nested.sub']
|
||||||
});
|
});
|
||||||
|
|
||||||
var lt_server_port
|
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 WebSocketServer = require('ws').Server;
|
||||||
|
|
||||||
var localtunnel_server = require('../server')({
|
var localtunnel_server = require('../server')({
|
||||||
max_tcp_sockets: 2
|
max_tcp_sockets: 2,
|
||||||
|
subHost: ['nested.sub', 'sub']
|
||||||
});
|
});
|
||||||
|
|
||||||
var lt_server_port
|
var lt_server_port
|
||||||
@@ -72,3 +73,30 @@ test('websocket server request', function(done) {
|
|||||||
ws.send('something');
|
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