10 Commits

Author SHA1 Message Date
Roman Shtylman
7f6640a9d6 0.0.6 2015-01-10 21:00:46 -08:00
Roman Shtylman
386910032e fix websocket support 2015-01-10 20:58:11 -08:00
Roman Shtylman
cfdb42ee9c rename .env.development to .env 2015-01-10 20:58:11 -08:00
Roman Shtylman
8af4fe19b8 update on-finished 2015-01-10 20:58:11 -08:00
Roman Shtylman
8d91892ace listen after connecting error handler 2015-01-10 20:58:10 -08:00
Roman Shtylman
4b485fecda Merge pull request #20 from sidthekidder/master
disallow uppercase domains, with test
2015-01-03 11:39:48 -05:00
Siddhartha Sahai
fa4802d488 disallow uppercase domains, with test 2015-01-03 04:39:09 +05:30
Roman Shtylman
5046c360ba don't log econnreset or timeout errors 2014-12-11 19:34:08 -08:00
Roman Shtylman
57ebcc90e1 add devops folder to show how to easily run on a server 2014-12-11 10:54:53 -08:00
Roman Shtylman
dd7d8d6aa6 bin: fix module require 2014-12-11 10:07:06 -08:00
12 changed files with 219 additions and 96 deletions

View File

@@ -1,7 +1,8 @@
#!/usr/bin/env node
require('stackup');
var log = require('bookrc');
var dotenv = require('dotenv').load();
var localenv = require('localenv');
var optimist = require('optimist');
var debug = require('debug')('localtunnel');

View File

@@ -1,6 +1,5 @@
/// bookrc logging setup
var log = require('book').default();
require('stackup');
log.use(require('book-git')(__dirname));
log.use(require('book-raven')(process.env.SENTRY_DSN));

33
devops/nginx/nginx.conf Normal file
View File

@@ -0,0 +1,33 @@
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 10240;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log off;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
gzip on;
gzip_min_length 1000;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites/*;
}

View File

@@ -0,0 +1,65 @@
proxy_http_version 1.1;
# http://nginx.org/en/docs/http/websocket.html
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream lt-server {
server 127.0.0.1:2000;
}
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name .localtunnel.me;
location / {
proxy_pass http://lt-server/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto http;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_redirect off;
}
}
server {
listen 443 default_server ssl spdy;
listen [::]:443 default_server ipv6only=on;
server_name .localtuunel.me;
ssl on;
ssl_certificate /etc/nginx/ssl/STAR.localtunnel.me.crt;
ssl_certificate_key /etc/nginx/ssl/STAR.localtunnel.me.key;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers RC4:HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://lt-server/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Connection $connection_upgrade;
proxy_redirect off;
}
}

5
devops/run.sh Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
docker run --restart always --name localtunnel -d --net host defunctzombie/localtunnel-server:0.0.5 bin/server --secure --port 2000 --max-sockets 5
docker run --restart always --name nginx -d --net host -v /home/core/nginx/nginx.conf:/etc/nginx/nginx.conf -v /home/core/nginx/sites:/etc/nginx/sites -v /home/core/nginx/ssl:/etc/nginx/ssl nginx:1.7.8

View File

@@ -1,65 +0,0 @@
proxy_http_version 1.1;
# http://nginx.org/en/docs/http/websocket.html
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream lt-server {
server 127.0.0.1:8000;
}
server {
listen 80 default_server;
listen [::]:80 default_server ipv6only=on;
server_name .localtunnel.me;
location / {
proxy_pass http://lt-server/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto http;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_redirect off;
}
}
server {
listen 443 default_server ssl spdy;
listen [::]:443 default_server ipv6only=on;
server_name .localtuunel.me;
ssl on;
ssl_certificate ...;
ssl_certificate_key ...;
ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers RC4:HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
location / {
proxy_pass http://lt-server/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-NginX-Proxy true;
proxy_set_header Connection $connection_upgrade;
proxy_redirect off;
}
}

View File

@@ -2,32 +2,33 @@
"author": "Roman Shtylman <shtylman@gmail.com>",
"name": "localtunnel-server",
"description": "expose localhost to the world",
"version": "0.0.5",
"version": "0.0.6",
"repository": {
"type": "git",
"url": "git://github.com/shtylman/localtunnel-server.git"
},
"dependencies": {
"book": "1.3.1",
"optimist": "0.6.1",
"debug": "2.1.0",
"bookrc": "0.0.1",
"book-git": "0.0.2",
"book-raven": "1.0.1",
"browserkthx": "0.0.2",
"hbs": "2.7.0",
"taters": "1.0.0",
"express": "4.10.5",
"makeup": "0.0.1",
"enchilada": "0.11.0",
"bookrc": "0.0.1",
"bouncy": "3.2.2",
"browserkthx": "0.0.2",
"debug": "2.1.0",
"enchilada": "0.11.0",
"express": "4.10.5",
"hbs": "2.7.0",
"localenv": "0.2.2",
"makeover": "0.0.1",
"makeup": "0.0.1",
"on-finished": "2.2.0",
"optimist": "0.6.1",
"serve-favicon": "2.1.7",
"stackup": "0.0.5",
"stylish": "0.5.0",
"makeover": "0.0.1",
"taters": "1.0.0",
"tldjs": "1.5.1",
"on-finished": "2.1.1",
"localenv": "0.2.2",
"serve-favicon": "2.1.7"
"ws": "0.6.5"
},
"devDependencies": {
"mocha": "2.0.1",

View File

@@ -21,16 +21,13 @@ var Proxy = function(opt, cb) {
// 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);
cb(null, {
// port for lt client tcp connections
port: port,
// maximum number of tcp connections allowed by lt client
max_conn_count: max_tcp_sockets
});
client_server.on('error', function(err) {
if (err.code == 'ECONNRESET' || err.code == 'ETIMEDOUT') {
return;
}
log.error(err);
});
// track initial user connection setup
@@ -71,7 +68,6 @@ var Proxy = function(opt, cb) {
// new tcp connection from lt client
client_server.on('connection', function(socket) {
// no more socket connections allowed
if (self.sockets.length >= max_tcp_sockets) {
return socket.end();
@@ -119,8 +115,16 @@ var Proxy = function(opt, cb) {
}
});
client_server.on('error', function(err) {
log.error(err);
client_server.listen(function() {
var port = client_server.address().port;
debug('tcp server listening on port: %d', port);
cb(null, {
// port for lt client tcp connections
port: port,
// maximum number of tcp connections allowed by lt client
max_conn_count: max_tcp_sockets
});
});
};

View File

@@ -53,6 +53,10 @@ function maybe_bounce(req, res, bounce) {
// we can't respond to these requests
var finished = false;
on_finished(res, function(err) {
if (req.headers['upgrade'] == 'websocket') {
return;
}
finished = true;
req.connection.destroy();
});
@@ -197,8 +201,8 @@ module.exports = function(opt) {
var req_id = req.param('req_id');
// limit requested hostnames to 20 characters
if (! /^[A-Za-z0-9]{4,20}$/.test(req_id)) {
var err = new Error('Invalid subdomain. Subdomains must be between 4 and 20 alphanumeric characters.');
if (! /^[a-z0-9]{4,20}$/.test(req_id)) {
var err = new Error('Invalid subdomain. Subdomains must be lowercase and between 4 and 20 alphanumeric characters.');
err.statusCode = 403;
return next(err);
}

View File

@@ -126,7 +126,20 @@ test('request domain that is too long', function(done) {
localtunnel(test._fake_port, opt, function(err, tunnel) {
assert(err);
assert.equal(err.message, 'Invalid subdomain. Subdomains must be between 4 and 20 alphanumeric characters.');
assert.equal(err.message, 'Invalid subdomain. Subdomains must be lowercase and between 4 and 20 alphanumeric characters.');
done();
});
});
test('request uppercase domain', function(done) {
var opt = {
host: 'http://localhost:' + lt_server_port,
subdomain: 'ABCD'
};
localtunnel(test._fake_port, opt, function(err, tunnel) {
assert(err);
assert.equal(err.message, 'Invalid subdomain. Subdomains must be lowercase and between 4 and 20 alphanumeric characters.');
done();
});
});

63
test/websocket.js Normal file
View File

@@ -0,0 +1,63 @@
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')();
var lt_server_port
test('setup localtunnel server', function(done) {
var server = localtunnel_server.listen(function() {
lt_server_port = server.address().port;
console.log('lt server on:', lt_server_port);
done();
});
});
test('setup local websocket server', function(done) {
var wss = new WebSocketServer({ port: 0 }, function() {
test._fake_port = wss._server.address().port;
done();
});
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(message) {
ws.send(message);
});
});
});
test('setup localtunnel client', function(done) {
var opt = {
host: 'http://localhost:' + lt_server_port,
};
localtunnel(test._fake_port, opt, function(err, tunnel) {
assert.ifError(err);
var url = tunnel.url;
assert.ok(new RegExp('^http:\/\/.*localhost:' + lt_server_port + '$').test(url));
test._fake_url = url;
done(err);
});
});
test('test websocket server request', function(done) {
var hostname = url.parse(test._fake_url).hostname;
var ws = new WebSocket('http://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');
});
});