Skip to content

Commit 7d68c53

Browse files
committed
feat: init server
1 parent d3e819a commit 7d68c53

File tree

13 files changed

+905
-1
lines changed

13 files changed

+905
-1
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.DS_Store
2+
node_modules

Dockerfile

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
FROM node:8
2+
3+
# Create app directory
4+
RUN mkdir -p /usr/src/app
5+
WORKDIR /usr/src/app
6+
7+
# Install app dependencies
8+
COPY package.json /usr/src/app/
9+
RUN npm install
10+
11+
# Bundle app source
12+
COPY . /usr/src/app
13+
14+
EXPOSE 3000
15+
CMD [ "node", "bin/server" ]

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License
2+
3+
Copyright (c) 2016 Noop Labs LLC http://nooplabs.com
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,56 @@
1-
# nport
1+
# **NPort**
2+
3+
NPort is a **Node.js-based tool** that tunnels HTTP connections through **Socket.IO** streams, enabling you to expose local servers via public URLs easily and securely. It is particularly useful for **development environments**, testing webhooks, and sharing projects on local servers.
4+
5+
---
6+
7+
## **Features**
8+
9+
- **HTTP Tunneling**: Expose your local HTTP server to the internet using Socket.IO-based tunnels.
10+
- **Secure and Lightweight**: A minimal, fast, and secure way to share your server without requiring complicated infrastructure.
11+
- **Custom Subdomains**: Access your local server using easy-to-read public URLs.
12+
- **WebSocket Support**: Handles WebSocket connections seamlessly.
13+
- **Cross-Platform**: Works on Linux, macOS, and Windows systems.
14+
15+
---
16+
17+
## **Install**
18+
19+
```sh
20+
# install nport from npm
21+
22+
npm i nport # local install
23+
24+
npm i -g nport # global install
25+
26+
# alternative: install from github
27+
28+
npm install git+https://github.com/tuanngocptn/nport.git # local install
29+
30+
npm install -g git+https://github.com/tuanngocptn/nport.git # global install
31+
```
32+
33+
---
34+
35+
## **How to use**
36+
37+
```sh
38+
npx nport -s xxx -p 3000 # https://xxx.nport.link (local install)
39+
40+
nport -s xxx -p 3000 # https://xxx.nport.link (global install)
41+
```
42+
**OR**
43+
44+
```sh
45+
npx nport --server https://nport.link --subdomain xxx --hostname 127.0.0.1 --port 3000 # https://xxx.nport.link (local install)
46+
47+
nport --server https://nport.link --subdomain xxx --hostname 127.0.0.1 --port 3000 # https://xxx.nport.link (global install)
48+
```
49+
50+
# Source from socket-tunnel
51+
52+
Tunnel HTTP connections via socket.io streams. Inspired by [localtunnel](https://github.com/localtunnel/localtunnel).
53+
54+
## Blog Post
55+
56+
[Read all about it](https://ericbarch.com/post/sockettunnel/)

bin/client

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env node
2+
const optimist = require('optimist');
3+
4+
let argv = optimist
5+
.usage('Usage: $0 --server [string] --subdomain [string] --hostname [string] --port [number]')
6+
.options('se', {
7+
alias: 'server',
8+
default: 'https://nport.link',
9+
describe: 'Tunnel server endpoint'
10+
})
11+
.options('s', {
12+
alias: 'subdomain',
13+
describe: '(Required) Public URL the tunnel server is forwarding to us'
14+
})
15+
.options('h', {
16+
alias: 'hostname',
17+
default: '127.0.0.1',
18+
describe: 'Address of local server for forwarding over socket-tunnel'
19+
})
20+
.options('p', {
21+
alias: 'port',
22+
describe: '(Required) Port of local server for forwarding over socket-tunnel'
23+
})
24+
.argv;
25+
26+
if (argv.help) {
27+
optimist.showHelp();
28+
process.exit();
29+
}
30+
31+
if (!argv['server'] || !argv['subdomain'] || !argv['port']) {
32+
for (var key in ['server', 'subdomain', 'port']) {
33+
if (argv[key]) continue;
34+
35+
console.log('Error: Required option, but nothing found');
36+
37+
optimist.showHelp();
38+
39+
process.exit();
40+
}
41+
}
42+
43+
require('../client.js')(argv);

bin/server

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env node
2+
var optimist = require('optimist');
3+
4+
var argv = optimist
5+
.usage('Usage: $0 --hostname [string] --port [number] --subdomain [string]')
6+
.options('h', {
7+
alias: 'hostname',
8+
default: '0.0.0.0',
9+
describe: 'Accept connections on this hostname'
10+
})
11+
.options('p', {
12+
alias: 'port',
13+
default: 3000,
14+
describe: 'Server daemon port'
15+
})
16+
.options('s', {
17+
alias: 'subdomain',
18+
default: '',
19+
describe: 'Name of subdomain used. Required when server listens on a subdomain (leave blank otherwise)'
20+
})
21+
.argv;
22+
23+
if (argv.help) {
24+
optimist.showHelp();
25+
process.exit();
26+
}
27+
28+
require('../server.js')(argv);

client.js

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
'use strict';
2+
3+
const IDLE_SOCKET_TIMEOUT_MILLISECONDS = 1000 * 30;
4+
5+
module.exports = (options) => {
6+
return new Promise((resolve, reject) => {
7+
// require the things we need
8+
const net = require('net');
9+
const ss = require('socket.io-stream');
10+
let socket = require('socket.io-client')(options['server']);
11+
12+
socket.on('connect', () => {
13+
console.log(new Date() + ': connected');
14+
console.log(new Date() + ': requesting subdomain ' + options['subdomain'] + ' via ' + options['server']);
15+
16+
socket.emit('createTunnel', options['subdomain'], (err) => {
17+
if (err) {
18+
console.log(new Date() + ': [error] ' + err);
19+
20+
reject(err);
21+
} else {
22+
console.log(new Date() + ': registered with server successfully');
23+
console.log(new Date() + ': your domain is: https://' + options['subdomain'] + '.nport.link');
24+
25+
// clean and concat requested url
26+
let url;
27+
let subdomain = options['subdomain'].toString();
28+
let server = options['server'].toString();
29+
30+
if (server.includes('https://')) {
31+
url = `https://${subdomain}.${server.slice(8)}`;
32+
} else if (server.includes('http://')) {
33+
url = `http://${subdomain}.${server.slice(7)}`;
34+
} else {
35+
url = `https://${subdomain}.${server}`;
36+
}
37+
38+
// resolve promise with requested URL
39+
resolve(url);
40+
}
41+
});
42+
});
43+
44+
socket.on('incomingClient', (clientId) => {
45+
let client = net.connect(options['port'], options['hostname'], () => {
46+
let s = ss.createStream();
47+
s.pipe(client).pipe(s);
48+
49+
s.on('end', () => {
50+
client.destroy();
51+
});
52+
53+
ss(socket).emit(clientId, s);
54+
});
55+
56+
client.setTimeout(IDLE_SOCKET_TIMEOUT_MILLISECONDS);
57+
client.on('timeout', () => {
58+
client.end();
59+
});
60+
61+
client.on('error', () => {
62+
// handle connection refusal (create a stream and immediately close it)
63+
let s = ss.createStream();
64+
ss(socket).emit(clientId, s);
65+
s.end();
66+
});
67+
});
68+
});
69+
};

examples/api.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const socketTunnel = require('../lib/api');
2+
3+
socketTunnel.connect('https://domain.example', 'deviceSubdomain', '2222')
4+
.then((url) => {
5+
console.log(url);
6+
})
7+
.catch((err) => {
8+
console.error(err);
9+
});

lib/api.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// client api
2+
const client = require('../client');
3+
4+
let api = {
5+
connect: (server, subdomain, port, hostname = '127.0.0.1') => {
6+
if (!server || !subdomain || !port || !hostname) {
7+
return Promise.reject(new Error('One or more options were not provided'));
8+
}
9+
10+
let options = {
11+
server: server,
12+
subdomain: subdomain.toString(),
13+
port: port.toString(),
14+
hostname: hostname
15+
};
16+
17+
// client returns a promise
18+
return client(options);
19+
}
20+
};
21+
22+
module.exports = api;

nginx.conf.sample

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Example of nginx config for proxying requests to socket-tunnel server
2+
3+
server {
4+
listen *:80;
5+
server_name subdomain.example.com *.subdomain.example.com;
6+
rewrite ^ https://$host$request_uri? permanent;
7+
}
8+
9+
server {
10+
listen *:443;
11+
12+
server_name subdomain.example.com *.subdomain.example.com;
13+
14+
ssl on;
15+
ssl_certificate /path/to/certificate/file.crt;
16+
ssl_certificate_key /path/to/key/file.key;
17+
ssl_prefer_server_ciphers on;
18+
19+
location / {
20+
proxy_pass http://127.0.0.1:3000/;
21+
22+
proxy_set_header X-Real-IP $remote_addr;
23+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
24+
proxy_set_header Host $http_host;
25+
proxy_http_version 1.1;
26+
proxy_set_header X-Forwarded-Proto https;
27+
proxy_set_header X-NginX-Proxy true;
28+
proxy_set_header Upgrade $http_upgrade;
29+
proxy_set_header Connection 'upgrade';
30+
31+
proxy_redirect off;
32+
}
33+
}

0 commit comments

Comments
 (0)