put id in path
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
node_modules
|
||||
node_modules
|
||||
@@ -1,4 +0,0 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- "9.2"
|
||||
+7
-4
@@ -1,13 +1,16 @@
|
||||
FROM node:10.1.0-alpine
|
||||
FROM node:16.11.1-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package.json /app/
|
||||
COPY yarn.lock /app/
|
||||
COPY package-lock.json /app/
|
||||
|
||||
RUN yarn install --production && yarn cache clean
|
||||
RUN npm install
|
||||
|
||||
COPY . /app
|
||||
|
||||
ENV NODE_ENV production
|
||||
ENTRYPOINT ["node", "-r", "esm", "./bin/server"]
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD npm start
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# build image
|
||||
docker build -t lt-server .
|
||||
# localtunnel-server
|
||||
|
||||
[](https://travis-ci.org/localtunnel/server)
|
||||
|
||||
+9
-8
@@ -1,14 +1,14 @@
|
||||
#!/usr/bin/env node -r esm
|
||||
|
||||
import 'localenv';
|
||||
import optimist from 'optimist';
|
||||
require('localenv');
|
||||
const optimist = require('optimist');
|
||||
|
||||
import log from 'book';
|
||||
import Debug from 'debug';
|
||||
const log = require('book');
|
||||
// const Debug = require('debug');
|
||||
|
||||
import CreateServer from '../server';
|
||||
const CreateServer = require('../server');
|
||||
|
||||
const debug = Debug('localtunnel');
|
||||
// const debug = Debug('localtunnel');
|
||||
|
||||
const argv = optimist
|
||||
.usage('Usage: $0 --port [num]')
|
||||
@@ -44,8 +44,9 @@ const server = CreateServer({
|
||||
domain: argv.domain,
|
||||
});
|
||||
|
||||
server.listen(argv.port, argv.address, () => {
|
||||
debug('server listening on port: %d', server.address().port);
|
||||
server.listen(process.env.PORT || argv.port, argv.address, () => {
|
||||
// debug('server listening on port: %d', server.address().port);
|
||||
console.log(`server listening on : ${JSON.stringify(server.address())}`)
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
|
||||
+5
-5
@@ -1,7 +1,7 @@
|
||||
import http from 'http';
|
||||
import Debug from 'debug';
|
||||
import pump from 'pump';
|
||||
import EventEmitter from 'events';
|
||||
const http = require('http');
|
||||
const Debug = require('debug');
|
||||
const pump = require('pump');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
// A client encapsulates req/res handling using an agent
|
||||
//
|
||||
@@ -129,4 +129,4 @@ class Client extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
export default Client;
|
||||
module.exports = Client;
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
import assert from 'assert';
|
||||
import http from 'http';
|
||||
import { Duplex } from 'stream';
|
||||
import WebSocket from 'ws';
|
||||
import net from 'net';
|
||||
const assert = require('assert');
|
||||
const http = require('http');
|
||||
const { Duplex } = require('stream');
|
||||
const WebSocket = require('ws');
|
||||
const net = require('net');
|
||||
|
||||
import Client from './Client';
|
||||
const Client from './Client';
|
||||
|
||||
class DummySocket extends Duplex {
|
||||
constructor(options) {
|
||||
|
||||
+5
-10
@@ -1,8 +1,5 @@
|
||||
import { hri } from 'human-readable-ids';
|
||||
import Debug from 'debug';
|
||||
|
||||
import Client from './Client';
|
||||
import TunnelAgent from './TunnelAgent';
|
||||
const Client = require('./Client');
|
||||
const TunnelAgent = require('./TunnelAgent');
|
||||
|
||||
// Manage sets of clients
|
||||
//
|
||||
@@ -19,8 +16,6 @@ class ClientManager {
|
||||
tunnels: 0
|
||||
};
|
||||
|
||||
this.debug = Debug('lt:ClientManager');
|
||||
|
||||
// This is totally wrong :facepalm: this needs to be per-client...
|
||||
this.graceTimeout = null;
|
||||
}
|
||||
@@ -34,7 +29,7 @@ class ClientManager {
|
||||
|
||||
// can't ask for id already is use
|
||||
if (clients[id]) {
|
||||
id = hri.random();
|
||||
return;
|
||||
}
|
||||
|
||||
const maxSockets = this.opt.max_tcp_sockets;
|
||||
@@ -74,7 +69,7 @@ class ClientManager {
|
||||
}
|
||||
|
||||
removeClient(id) {
|
||||
this.debug('removing client: %s', id);
|
||||
console.debug('removing client: %s', id)
|
||||
const client = this.clients[id];
|
||||
if (!client) {
|
||||
return;
|
||||
@@ -93,4 +88,4 @@ class ClientManager {
|
||||
}
|
||||
}
|
||||
|
||||
export default ClientManager;
|
||||
module.exports = ClientManager;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import assert from 'assert';
|
||||
import net from 'net';
|
||||
const assert = require('assert');
|
||||
const net = require('net');
|
||||
|
||||
import ClientManager from './ClientManager';
|
||||
const ClientManager = require('./ClientManager');
|
||||
|
||||
describe('ClientManager', () => {
|
||||
it('should construct with no tunnels', () => {
|
||||
|
||||
+6
-6
@@ -1,8 +1,8 @@
|
||||
import { Agent } from 'http';
|
||||
import net from 'net';
|
||||
import assert from 'assert';
|
||||
import log from 'book';
|
||||
import Debug from 'debug';
|
||||
const { Agent } = require('http');
|
||||
const net = require('net');
|
||||
const assert = require('assert');
|
||||
const log = require('book');
|
||||
const Debug = require('debug');
|
||||
|
||||
const DEFAULT_MAX_SOCKETS = 10;
|
||||
|
||||
@@ -172,4 +172,4 @@ class TunnelAgent extends Agent {
|
||||
}
|
||||
}
|
||||
|
||||
export default TunnelAgent;
|
||||
module.exports = TunnelAgent;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import http from 'http';
|
||||
import net from 'net';
|
||||
import assert from 'assert';
|
||||
const http = require('http');
|
||||
const net = require('net');
|
||||
const assert = require('assert');
|
||||
|
||||
import TunnelAgent from './TunnelAgent';
|
||||
const TunnelAgent = require('./TunnelAgent');
|
||||
|
||||
describe('TunnelAgent', () => {
|
||||
it('should create an empty agent', async () => {
|
||||
|
||||
Generated
+4765
File diff suppressed because it is too large
Load Diff
+12
-11
@@ -10,25 +10,26 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"book": "1.3.3",
|
||||
"debug": "3.1.0",
|
||||
"esm": "3.0.34",
|
||||
"human-readable-ids": "1.0.3",
|
||||
"koa": "2.5.1",
|
||||
"koa-router": "7.4.0",
|
||||
"debug": "4.3.2",
|
||||
"esm": "3.2.25",
|
||||
"human-readable-ids": "1.0.4",
|
||||
"koa": "2.13.3",
|
||||
"koa-router": "10.1.1",
|
||||
"localenv": "0.2.2",
|
||||
"optimist": "0.6.1",
|
||||
"pump": "3.0.0",
|
||||
"tldjs": "2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "5.1.1",
|
||||
"node-dev": "3.1.3",
|
||||
"supertest": "3.1.0",
|
||||
"ws": "5.1.1"
|
||||
"mocha": "9.1.3",
|
||||
"node-dev": "7.0.0",
|
||||
"nodemon": "2.0.14",
|
||||
"supertest": "6.1.6",
|
||||
"ws": "8.2.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha --check-leaks --require esm './**/*.test.js'",
|
||||
"start": "./bin/server",
|
||||
"dev": "node-dev bin/server --port 3000"
|
||||
"start": "node bin/server --port 8080 --secure true",
|
||||
"dev": "nodemon bin/server --port 3000"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
import log from 'book';
|
||||
import Koa from 'koa';
|
||||
import tldjs from 'tldjs';
|
||||
import Debug from 'debug';
|
||||
import http from 'http';
|
||||
import { hri } from 'human-readable-ids';
|
||||
import Router from 'koa-router';
|
||||
const Koa = require('koa');
|
||||
const Debug = require('debug');
|
||||
const http = require('http');
|
||||
const Router = require('koa-router');
|
||||
|
||||
import ClientManager from './lib/ClientManager';
|
||||
const ClientManager = require('./lib/ClientManager');
|
||||
|
||||
const debug = Debug('localtunnel:server');
|
||||
|
||||
export default function(opt) {
|
||||
let counter = 0;
|
||||
module.exports = function(opt) {
|
||||
opt = opt || {};
|
||||
|
||||
const validHosts = (opt.domain) ? [opt.domain] : undefined;
|
||||
const myTldjs = tldjs.fromUserSettings({ validHosts });
|
||||
const landingPage = opt.landing || 'https://localtunnel.github.io/www/';
|
||||
|
||||
function GetClientIdFromHostname(hostname) {
|
||||
return myTldjs.getSubdomain(hostname);
|
||||
}
|
||||
|
||||
const manager = new ClientManager(opt);
|
||||
|
||||
const schema = opt.secure ? 'https' : 'http';
|
||||
@@ -29,10 +20,12 @@ export default function(opt) {
|
||||
const router = new Router();
|
||||
|
||||
router.get('/api/status', async (ctx, next) => {
|
||||
counter += 1;
|
||||
const stats = manager.stats;
|
||||
ctx.body = {
|
||||
tunnels: stats.tunnels,
|
||||
mem: process.memoryUsage(),
|
||||
counter,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -53,49 +46,25 @@ export default function(opt) {
|
||||
app.use(router.routes());
|
||||
app.use(router.allowedMethods());
|
||||
|
||||
// root endpoint
|
||||
app.use(async (ctx, next) => {
|
||||
const path = ctx.request.path;
|
||||
app.use(async (ctx) => {
|
||||
const newReqId = ctx.query['new'];
|
||||
const parts = ctx.originalUrl.split('/')
|
||||
const reqId = parts[1]
|
||||
|
||||
// skip anything not on the root path
|
||||
if (path !== '/') {
|
||||
await next();
|
||||
if(reqId && !newReqId) {
|
||||
ctx.status = 404;
|
||||
ctx.body = {
|
||||
message: `id: ${reqId}, not found`,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const isNewClientRequest = ctx.query['new'] !== undefined;
|
||||
if (isNewClientRequest) {
|
||||
const reqId = hri.random();
|
||||
debug('making new client with id %s', reqId);
|
||||
const info = await manager.newClient(reqId);
|
||||
|
||||
const url = schema + '://' + info.id + '.' + ctx.request.host;
|
||||
info.url = url;
|
||||
ctx.body = info;
|
||||
if (!newReqId) {
|
||||
ctx.redirect(landingPage);
|
||||
return;
|
||||
}
|
||||
|
||||
// no new client request, send to landing page
|
||||
ctx.redirect(landingPage);
|
||||
});
|
||||
|
||||
// anything after the / path is a request for a specific client name
|
||||
// This is a backwards compat feature
|
||||
app.use(async (ctx, next) => {
|
||||
const parts = ctx.request.path.split('/');
|
||||
|
||||
// any request with several layers of paths is not allowed
|
||||
// rejects /foo/bar
|
||||
// allow /foo
|
||||
if (parts.length !== 2) {
|
||||
await next();
|
||||
return;
|
||||
}
|
||||
|
||||
const reqId = parts[1];
|
||||
|
||||
// limit requested hostnames to 63 characters
|
||||
if (! /^(?:[a-z0-9][a-z0-9\-]{4,63}[a-z0-9]|[a-z0-9]{4,63})$/.test(reqId)) {
|
||||
if (! /^(?:[a-z0-9][a-z0-9\-]{4,63}[a-z0-9]|[a-z0-9]{4,63})$/.test(newReqId)) {
|
||||
const msg = 'Invalid subdomain. Subdomains must be lowercase and between 4 and 63 alphanumeric characters.';
|
||||
ctx.status = 403;
|
||||
ctx.body = {
|
||||
@@ -104,10 +73,16 @@ export default function(opt) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug('making new client with id %s', reqId);
|
||||
const info = await manager.newClient(reqId);
|
||||
|
||||
const url = schema + '://' + info.id + '.' + ctx.request.host;
|
||||
debug('making new client with id %s', newReqId);
|
||||
const info = await manager.newClient(newReqId);
|
||||
if(!info) {
|
||||
ctx.status = 500;
|
||||
ctx.body = {
|
||||
message: 'no new client info',
|
||||
};
|
||||
return;
|
||||
}
|
||||
const url = `${schema}://${ctx.request.host}/${info.id}`;
|
||||
info.url = url;
|
||||
ctx.body = info;
|
||||
return;
|
||||
@@ -117,16 +92,14 @@ export default function(opt) {
|
||||
|
||||
const appCallback = app.callback();
|
||||
|
||||
server.on('request', (req, res) => {
|
||||
// without a hostname, we won't know who the request is for
|
||||
const hostname = req.headers.host;
|
||||
if (!hostname) {
|
||||
res.statusCode = 400;
|
||||
res.end('Host header is required');
|
||||
server.on('request', (req, res) => {
|
||||
const parts = req.url.split('/');
|
||||
if(parts[1].indexOf('?new=') === 0){
|
||||
appCallback(req, res);
|
||||
return;
|
||||
}
|
||||
const clientId = parts[1];
|
||||
|
||||
const clientId = GetClientIdFromHostname(hostname);
|
||||
if (!clientId) {
|
||||
appCallback(req, res);
|
||||
return;
|
||||
@@ -134,27 +107,22 @@ export default function(opt) {
|
||||
|
||||
const client = manager.getClient(clientId);
|
||||
if (!client) {
|
||||
res.statusCode = 404;
|
||||
res.end('404');
|
||||
appCallback(req, res);
|
||||
return;
|
||||
}
|
||||
|
||||
client.handleRequest(req, res);
|
||||
});
|
||||
|
||||
server.on('upgrade', (req, socket, head) => {
|
||||
const hostname = req.headers.host;
|
||||
if (!hostname) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
server.on('upgrade', (req, socket) => {
|
||||
const parts = req.url.split('/').filter((part=>part != ''));
|
||||
const clientId = parts[0];
|
||||
|
||||
const clientId = GetClientIdFromHostname(hostname);
|
||||
if (!clientId) {
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const client = manager.getClient(clientId);
|
||||
if (!client) {
|
||||
socket.destroy();
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
GET http://localhost:8080/api/status
|
||||
###
|
||||
GET http://localhost:8080/api/tunnels/neat-vampirebat-44/status
|
||||
###
|
||||
GET http://localhost:8080?new=true
|
||||
###
|
||||
GET https://neat-vampirebat-44.localhost:8080
|
||||
###
|
||||
GET http://localhost:3000?new=adogijaiog
|
||||
###
|
||||
GET http://localhost:3000/adogijaiog
|
||||
Reference in New Issue
Block a user