@remotelinker/reverse-ws-tunnel 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +54 -0
- package/README.md +406 -0
- package/client/index.js +34 -0
- package/client/proxyServer.js +74 -0
- package/client/tunnelClient.js +196 -0
- package/client/utils.js +24 -0
- package/cookbook.md +595 -0
- package/package.json +47 -0
- package/server/constants.js +7 -0
- package/server/index.js +8 -0
- package/server/messageHandler.js +79 -0
- package/server/state.js +1 -0
- package/server/tcpServer.js +137 -0
- package/server/websocketServer.js +142 -0
- package/utils/index.js +8 -0
- package/utils/loadConfig.js +54 -0
- package/utils/logger.js +136 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.0.3] - 2025-11-29
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
- Updated `startClient` to return a client instance.
|
|
9
|
+
- Added `connected` and `disconnected` events to the client instance.
|
|
10
|
+
- Added `close()` method to the client instance to terminate the connection and stop reconnection.
|
|
11
|
+
|
|
12
|
+
## [1.0.2] - 2025-10-05
|
|
13
|
+
|
|
14
|
+
### Fixes
|
|
15
|
+
- Corrected a typo in the environment variable name for `allowInsicureCerts` in the client configuration loader.
|
|
16
|
+
|
|
17
|
+
## [1.0.1] - 2025-09-16
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
- Added a comprehensive example of a reverse tunnel to README.md.
|
|
21
|
+
- Included a minimal web server to act as the tunnel's target.
|
|
22
|
+
- Updated the client configuration to align with the new example.
|
|
23
|
+
- Generated a CHANGELOG.md file from the project's git history.
|
|
24
|
+
|
|
25
|
+
### Refactoring
|
|
26
|
+
- Refactored WebSocket connection state management.
|
|
27
|
+
|
|
28
|
+
## [1.0.0] - 2025-09-16
|
|
29
|
+
|
|
30
|
+
### Features
|
|
31
|
+
- Added dynamic environment configuration.
|
|
32
|
+
- Added client IP to server connection function.
|
|
33
|
+
- Added support for custom config path.
|
|
34
|
+
- Added unit tests with Jest.
|
|
35
|
+
- Added `config.toml` for configuration.
|
|
36
|
+
- Added logger.
|
|
37
|
+
- Added examples.
|
|
38
|
+
- Added dynamic allowance of insecure HTTPS certificates.
|
|
39
|
+
- Added support for multiple servers on different ports.
|
|
40
|
+
- Added return of the WebSocket server instance.
|
|
41
|
+
- Added dynamic TCP server.
|
|
42
|
+
- Initial working version.
|
|
43
|
+
|
|
44
|
+
### Fixes
|
|
45
|
+
- Fixed WebSocket server heartbeat.
|
|
46
|
+
- Fixed `allowInsicureCerts` option.
|
|
47
|
+
- Fixed logger path.
|
|
48
|
+
- Fixed heartbeat issue.
|
|
49
|
+
- Disabled secure proxy when set to false.
|
|
50
|
+
|
|
51
|
+
### Refactoring
|
|
52
|
+
- Refactored dist directory structure.
|
|
53
|
+
- Changed `tunnelIdHeaderName`.
|
|
54
|
+
- Refactored with ChatGPT suggestions.
|
package/README.md
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
# 🚀 Reverse WebSocket Tunnel
|
|
2
|
+
|
|
3
|
+
**A Node.js library for creating secure reverse tunnels over WebSocket connections.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@remotelinker/reverse-ws-tunnel)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## 📖 What is Reverse WebSocket Tunnel?
|
|
11
|
+
|
|
12
|
+
Reverse WebSocket Tunnel is a library that enables you to expose local services to the internet through a WebSocket tunnel, similar to tools like ngrok or localtunnel. It consists of two main components:
|
|
13
|
+
|
|
14
|
+
- **Server**: Runs on a publicly accessible server and accepts WebSocket connections from clients
|
|
15
|
+
- **Client**: Runs locally and creates a tunnel to expose local services through the server
|
|
16
|
+
|
|
17
|
+
### Use Cases
|
|
18
|
+
|
|
19
|
+
- **Development**: Expose local development servers for testing webhooks, APIs, or sharing work
|
|
20
|
+
- **IoT & Edge**: Connect devices behind NAT/firewalls to cloud services
|
|
21
|
+
- **Microservices**: Enable secure communication between services across different networks
|
|
22
|
+
- **CI/CD**: Create temporary endpoints for automated testing
|
|
23
|
+
|
|
24
|
+
### How It Works
|
|
25
|
+
|
|
26
|
+
1. The **server** listens for WebSocket connections and HTTP requests
|
|
27
|
+
2. The **client** connects to the server via WebSocket and registers a tunnel ID
|
|
28
|
+
3. When the server receives HTTP requests for a specific tunnel, it forwards them through the WebSocket to the client
|
|
29
|
+
4. The client proxies the requests to the local target service and sends responses back through the tunnel
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 📦 Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @remotelinker/reverse-ws-tunnel
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 🚀 Quick Start
|
|
40
|
+
|
|
41
|
+
### Server Setup
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const { startWebSocketServer } = require('@remotelinker/reverse-ws-tunnel/server');
|
|
45
|
+
|
|
46
|
+
// Start the WebSocket tunnel server
|
|
47
|
+
startWebSocketServer({
|
|
48
|
+
port: 443,
|
|
49
|
+
host: '0.0.0.0',
|
|
50
|
+
path: '/tunnel',
|
|
51
|
+
tunnelIdHeaderName: 'x-tunnel-id',
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Client Setup
|
|
56
|
+
|
|
57
|
+
```javascript
|
|
58
|
+
const { startClient } = require('@remotelinker/reverse-ws-tunnel/client');
|
|
59
|
+
|
|
60
|
+
// Connect to the tunnel server and expose local service
|
|
61
|
+
const client = startClient({
|
|
62
|
+
tunnelId: '1cf2755f-c151-4281-b3f0-55c399035f87',
|
|
63
|
+
wsUrl: 'wss://yourdomain.com/tunnel',
|
|
64
|
+
targetUrl: 'http://localhost:3000',
|
|
65
|
+
tunnelEntryPort: 4443,
|
|
66
|
+
allowInsicureCerts: false,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Listen for events
|
|
70
|
+
client.on('connected', () => {
|
|
71
|
+
console.log('Connected to tunnel');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
client.on('disconnected', () => {
|
|
75
|
+
console.log('Disconnected from tunnel');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Close connection
|
|
79
|
+
// client.close();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## ⚙️ Configuration
|
|
85
|
+
|
|
86
|
+
You can configure the library using:
|
|
87
|
+
|
|
88
|
+
1. **Environment variables**
|
|
89
|
+
2. **TOML configuration files** (`config.toml`)
|
|
90
|
+
3. **Direct JavaScript parameters**
|
|
91
|
+
|
|
92
|
+
_Configuration priority: JavaScript parameters > config.toml > environment variables_
|
|
93
|
+
|
|
94
|
+
### 🖥️ Server Configuration
|
|
95
|
+
|
|
96
|
+
#### JavaScript API
|
|
97
|
+
|
|
98
|
+
```javascript
|
|
99
|
+
const { startWebSocketServer } = require('@remotelinker/reverse-ws-tunnel/server');
|
|
100
|
+
|
|
101
|
+
startWebSocketServer({
|
|
102
|
+
port: 443, // WebSocket server port
|
|
103
|
+
host: '0.0.0.0', // Host to bind (optional)
|
|
104
|
+
path: '/tunnel', // WebSocket path (optional)
|
|
105
|
+
tunnelIdHeaderName: 'x-tunnel-id', // Header name for tunnel identification
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Environment Variables
|
|
110
|
+
|
|
111
|
+
| Variable | Description | Default | Example |
|
|
112
|
+
| ----------------------- | ------------------------- | ------------- | ------------- |
|
|
113
|
+
| `WS_PORT` | WebSocket server port | `443` | `8080` |
|
|
114
|
+
| `HOST` | Host address to bind | `undefined` | `0.0.0.0` |
|
|
115
|
+
| `PATH_URL` | WebSocket endpoint path | `undefined` | `/tunnel` |
|
|
116
|
+
| `TUNNEL_ID_HEADER_NAME` | HTTP header for tunnel ID | `x-tunnel-id` | `x-tunnel-id` |
|
|
117
|
+
| `LOG_LEVEL` | Logging verbosity | `info` | `debug` |
|
|
118
|
+
|
|
119
|
+
#### TOML Configuration (`config.toml`)
|
|
120
|
+
|
|
121
|
+
```toml
|
|
122
|
+
# WebSocket server configuration
|
|
123
|
+
wsPort = 443
|
|
124
|
+
host = "0.0.0.0"
|
|
125
|
+
path = "/tunnel"
|
|
126
|
+
tunnelIdHeaderName = "x-tunnel-id"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### Example Server
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Run the example server
|
|
133
|
+
npm run example:server
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 💻 Client Configuration
|
|
137
|
+
|
|
138
|
+
#### JavaScript API
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
const { startClient } = require('@remotelinker/reverse-ws-tunnel/client');
|
|
142
|
+
|
|
143
|
+
const client = startClient({
|
|
144
|
+
tunnelId: '1cf2755f-c151-4281-b3f0-55c399035f87', // Unique tunnel identifier (UUID)
|
|
145
|
+
wsUrl: 'wss://example.com/tunnel', // WebSocket server URL
|
|
146
|
+
targetUrl: 'http://localhost:3000', // Local service to expose
|
|
147
|
+
tunnelEntryUrl: 'http://localhost:4443', // Optional: tunnel entry URL
|
|
148
|
+
tunnelEntryPort: 4443, // TCP port for tunnel entry
|
|
149
|
+
allowInsicureCerts: false, // Allow insecure SSL certificates
|
|
150
|
+
headers: {
|
|
151
|
+
// Optional: custom headers
|
|
152
|
+
Authorization: 'Bearer token',
|
|
153
|
+
'X-Custom-Header': 'value',
|
|
154
|
+
},
|
|
155
|
+
autoReconnect: true, // Automatically reconnect on close (default: true)
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Event handling
|
|
159
|
+
client.on('connected', () => console.log('Tunnel connected'));
|
|
160
|
+
client.on('disconnected', () => console.log('Tunnel disconnected'));
|
|
161
|
+
|
|
162
|
+
// Close the tunnel
|
|
163
|
+
// client.close();
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Environment Variables
|
|
167
|
+
|
|
168
|
+
| Variable | Description | Required | Default | Example |
|
|
169
|
+
| ---------------------- | ---------------------------------- | -------- | ------- | -------------------------------------- |
|
|
170
|
+
| `TUNNEL_ID` | Unique tunnel identifier (UUID-v4) | ✅ | - | `1cf2755f-c151-4281-b3f0-55c399035f87` |
|
|
171
|
+
| `WS_URL` | WebSocket server URL | ✅ | - | `wss://example.com/tunnel` |
|
|
172
|
+
| `TARGET_URL` | Local service URL to expose | ✅ | - | `http://localhost:3000` |
|
|
173
|
+
| `TUNNEL_ENTRY_URL` | Tunnel entry point URL | ❌ | - | `http://localhost:4443` |
|
|
174
|
+
| `TUNNEL_ENTRY_PORT` | TCP port for tunnel entry | ❌ | - | `4443` |
|
|
175
|
+
| `HEADERS` | Custom headers (JSON string) | ❌ | - | `{"Authorization":"Bearer token"}` |
|
|
176
|
+
| `ALLOW_INSICURE_CERTS` | Allow insecure SSL certificates | ❌ | `false` | `true` |
|
|
177
|
+
| `AUTO_RECONNECT` | Automatically reconnect on close | ❌ | `true` | `false` |
|
|
178
|
+
| `LOG_LEVEL` | Logging level | ❌ | `info` | `debug` |
|
|
179
|
+
|
|
180
|
+
#### TOML Configuration (`config.toml`)
|
|
181
|
+
|
|
182
|
+
```toml
|
|
183
|
+
# Unique identifier of the tunnel (UUID-v4)
|
|
184
|
+
tunnelId = "1cf2755f-c151-4281-b3f0-55c399035f87"
|
|
185
|
+
|
|
186
|
+
# WebSocket server URL to connect to
|
|
187
|
+
wsUrl = "wss://example.com/tunnel"
|
|
188
|
+
|
|
189
|
+
# Target URL where the traffic will be forwarded
|
|
190
|
+
targetUrl = "http://localhost:3000"
|
|
191
|
+
|
|
192
|
+
# Optional URL for tunnel entry point
|
|
193
|
+
tunnelEntryUrl = "http://localhost:4443"
|
|
194
|
+
|
|
195
|
+
# TCP port to open for incoming tunnel connections
|
|
196
|
+
tunnelEntryPort = 4443
|
|
197
|
+
|
|
198
|
+
# Whether to allow insecure SSL certificates (dev/test only)
|
|
199
|
+
allowInsicureCerts = false
|
|
200
|
+
|
|
201
|
+
# Automatically reconnect on close
|
|
202
|
+
autoReconnect = true
|
|
203
|
+
|
|
204
|
+
# Log verbosity level: error, warn, info, debug, trace
|
|
205
|
+
logLevel = "info"
|
|
206
|
+
|
|
207
|
+
# Custom headers to send with requests
|
|
208
|
+
[headers]
|
|
209
|
+
Authorization = "Bearer your-token"
|
|
210
|
+
X-Custom-Header = "custom-value"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### Example Client
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Run the example client
|
|
217
|
+
npm run example:client
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## 🐳 Docker Deployment
|
|
223
|
+
|
|
224
|
+
### Building the Docker Image
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
npm run docker:build
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Running with Docker Compose
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
npm run docker:deploy
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
The service will start on port 4443 by default.
|
|
237
|
+
|
|
238
|
+
### Docker Environment Variables
|
|
239
|
+
|
|
240
|
+
When using Docker, you can pass all the environment variables mentioned above:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
docker run -e TUNNEL_ID=your-uuid -e WS_URL=wss://example.com -e TARGET_URL=http://localhost:3000 remotelinker/reverse-ws-tunnel
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 📝 Logging
|
|
249
|
+
|
|
250
|
+
The library uses Winston for logging with configurable levels:
|
|
251
|
+
|
|
252
|
+
- `error`: Only error messages
|
|
253
|
+
- `warn`: Warnings and errors
|
|
254
|
+
- `info`: General information (default)
|
|
255
|
+
- `debug`: Detailed debugging information
|
|
256
|
+
- `trace`: Very verbose output including message traces
|
|
257
|
+
|
|
258
|
+
Set the log level via:
|
|
259
|
+
|
|
260
|
+
- Environment variable: `LOG_LEVEL=debug`
|
|
261
|
+
- TOML config: `logLevel = "debug"`
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## 🔧 Advanced Usage
|
|
266
|
+
|
|
267
|
+
### Custom Headers
|
|
268
|
+
|
|
269
|
+
You can send custom headers with tunnel requests:
|
|
270
|
+
|
|
271
|
+
```javascript
|
|
272
|
+
// Environment variable (JSON string)
|
|
273
|
+
process.env.HEADERS = JSON.stringify({
|
|
274
|
+
'Authorization': 'Bearer your-token',
|
|
275
|
+
'X-API-Key': 'your-api-key'
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Or in config.toml
|
|
279
|
+
[headers]
|
|
280
|
+
Authorization = "Bearer your-token"
|
|
281
|
+
X-API-Key = "your-api-key"
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### SSL/TLS Configuration
|
|
285
|
+
|
|
286
|
+
For development, you might need to allow insecure certificates:
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
startClient({
|
|
290
|
+
// ... other config
|
|
291
|
+
allowInsicureCerts: true, // Only for development!
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Multiple Tunnels
|
|
296
|
+
|
|
297
|
+
Each client needs a unique `tunnelId` (UUID-v4). You can run multiple clients with different tunnel IDs to expose multiple services.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## 🧪 Examples
|
|
302
|
+
|
|
303
|
+
The repository includes working examples:
|
|
304
|
+
|
|
305
|
+
- **Server**: `examples/server/` - Shows how to set up a tunnel server.
|
|
306
|
+
- **Client**: `examples/client/` - Shows how to connect and expose a local service.
|
|
307
|
+
- **Web Server**: `examples/webserver/` - A minimal target web server.
|
|
308
|
+
|
|
309
|
+
### Complete Reverse Tunnel Example
|
|
310
|
+
|
|
311
|
+
This example demonstrates how to set up a complete reverse tunnel to expose a local web server to the internet.
|
|
312
|
+
|
|
313
|
+
**1. Start the Target Web Server**
|
|
314
|
+
|
|
315
|
+
First, start the minimal web server that will be the destination of our tunnel. This server will respond with "Hello, World!".
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Terminal 1: Start the web server
|
|
319
|
+
node examples/webserver/webserver-example.js
|
|
320
|
+
# Server running on http://localhost:3000/
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**2. Start the Tunnel Server**
|
|
324
|
+
|
|
325
|
+
Next, start the tunnel server. This server runs on a publicly accessible machine and listens for WebSocket connections from the tunnel client.
|
|
326
|
+
|
|
327
|
+
The example server is located in `examples/server/`.
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
# Terminal 2: Start the tunnel server
|
|
331
|
+
npm run example:server
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
This will start the server using the configuration from `examples/server/config.toml`. By default, it listens on port `3000` for WebSocket connections and port `4443` for public HTTP requests.
|
|
335
|
+
|
|
336
|
+
**3. Start the Tunnel Client**
|
|
337
|
+
|
|
338
|
+
Now, start the tunnel client. The client connects to the tunnel server and exposes the local web server.
|
|
339
|
+
|
|
340
|
+
We need to configure the client to connect to our tunnel server and point to our local web server. The example client configuration is in `examples/client/config.toml`. Let's modify it to match our setup.
|
|
341
|
+
|
|
342
|
+
**`examples/client/config.toml`**
|
|
343
|
+
|
|
344
|
+
```toml
|
|
345
|
+
# Unique identifier of the tunnel (UUID-v4)
|
|
346
|
+
tunnelId = "1cf2755f-c151-4281-b3f0-55c399035f87"
|
|
347
|
+
|
|
348
|
+
# WebSocket server URL to connect to
|
|
349
|
+
wsUrl = "ws://localhost:8080/tunnel"
|
|
350
|
+
|
|
351
|
+
# Target URL where the traffic will be forwarded
|
|
352
|
+
targetUrl = "http://localhost:8080"
|
|
353
|
+
|
|
354
|
+
# TCP port to open for incoming tunnel connections
|
|
355
|
+
tunnelEntryPort = 4443
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Now, run the client:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
# Terminal 3: Start the tunnel client
|
|
362
|
+
npm run example:client
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**4. Test the Tunnel**
|
|
366
|
+
|
|
367
|
+
The tunnel is now active. The tunnel server is listening for requests on port `4443` and will forward them to your local web server running on port `8080`.
|
|
368
|
+
|
|
369
|
+
You can test it by making a `curl` request to the tunnel server's public endpoint, including the `x-tunnel-id` header:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
# Terminal 4: Test the tunnel
|
|
373
|
+
curl -X GET http://localhost:8083 -H "x-tunnel-id: 1cf2755f-c151-4281-b3f0-55c399035f87"
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
You should see the "Hello, World!" response from your local web server.
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
Hello, World!
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## ⚠️ Security Considerations
|
|
385
|
+
|
|
386
|
+
- **Production Use**: Ready for production use
|
|
387
|
+
- **SSL/TLS**: Always use secure WebSocket connections (`wss://`) in production
|
|
388
|
+
- **Authentication**: Implement proper authentication mechanisms for your tunnels
|
|
389
|
+
- **Rate Limiting**: Consider implementing rate limiting on the server side
|
|
390
|
+
- **Firewall**: Ensure proper firewall rules are in place
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## 📄 License
|
|
395
|
+
|
|
396
|
+
ISC License - see [LICENSE](LICENSE) file for details.
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## 🤝 Contributing
|
|
401
|
+
|
|
402
|
+
This project is in active development. Contributions, issues, and feature requests are welcome!
|
|
403
|
+
|
|
404
|
+
## 📞 Support
|
|
405
|
+
|
|
406
|
+
For questions and support, please open an issue on the GitHub repository.
|
package/client/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const { startHttpProxyServer } = require('./proxyServer');
|
|
2
|
+
const { connectWebSocket } = require('./tunnelClient');
|
|
3
|
+
const { setLogContext } = require('../utils/logger');
|
|
4
|
+
|
|
5
|
+
function startClient({ targetUrl, allowInsicureCerts, wsUrl, tunnelId, tunnelEntryUrl, tunnelEntryPort, headers, environment, autoReconnect }) {
|
|
6
|
+
setLogContext('CLIENT');
|
|
7
|
+
environment = environment || 'production';
|
|
8
|
+
const proxy = startHttpProxyServer(targetUrl, allowInsicureCerts);
|
|
9
|
+
const TARGET_PORT = proxy.port;
|
|
10
|
+
|
|
11
|
+
const client = connectWebSocket({
|
|
12
|
+
wsUrl,
|
|
13
|
+
tunnelId,
|
|
14
|
+
targetUrl,
|
|
15
|
+
targetPort: TARGET_PORT,
|
|
16
|
+
tunnelEntryUrl,
|
|
17
|
+
tunnelEntryPort,
|
|
18
|
+
headers,
|
|
19
|
+
environment,
|
|
20
|
+
autoReconnect,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const originalClose = client.close;
|
|
24
|
+
client.close = () => {
|
|
25
|
+
originalClose.call(client);
|
|
26
|
+
proxy.close();
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return client;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
startClient,
|
|
34
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const http = require('http');
|
|
2
|
+
const httpProxy = require('http-proxy');
|
|
3
|
+
const { logger } = require('../utils/logger');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Starts an HTTP/WebSocket proxy server to forward to the target URL.
|
|
7
|
+
* @param {string} targetUrl - The URL to forward requests to.
|
|
8
|
+
* @param {boolean} allowInsecureCerts - If true, allows self-signed certs.
|
|
9
|
+
* @returns {number} - The port the proxy is listening on.
|
|
10
|
+
*/
|
|
11
|
+
function startHttpProxyServer(targetUrl, allowInsecureCerts = false) {
|
|
12
|
+
logger.info('Starting HTTP/WS proxy server...');
|
|
13
|
+
logger.debug(`Target URL: ${targetUrl}`);
|
|
14
|
+
logger.debug(`Allow insecure certs: ${allowInsecureCerts}`);
|
|
15
|
+
|
|
16
|
+
const proxy = httpProxy.createProxyServer({});
|
|
17
|
+
|
|
18
|
+
const server = http.createServer((req, res) => {
|
|
19
|
+
logger.trace(`Incoming HTTP request: ${req.method} ${req.url}`);
|
|
20
|
+
|
|
21
|
+
if (!targetUrl) {
|
|
22
|
+
logger.warn('Request received without targetUrl set');
|
|
23
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
24
|
+
return res.end('Missing TARGET_URL');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
proxy.web(req, res, { target: targetUrl, changeOrigin: true, secure: !allowInsecureCerts }, (err) => {
|
|
28
|
+
logger.error('Proxy web error:', err);
|
|
29
|
+
if (!res.headersSent) {
|
|
30
|
+
res.writeHead(502);
|
|
31
|
+
res.end('Bad gateway');
|
|
32
|
+
} else {
|
|
33
|
+
res.end();
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
server.on('upgrade', (req, socket, head) => {
|
|
39
|
+
logger.trace(`Incoming WebSocket upgrade: ${req.url}`);
|
|
40
|
+
proxy.ws(req, socket, head, { target: targetUrl, changeOrigin: false, secure: !allowInsecureCerts }, (err) => {
|
|
41
|
+
logger.error('Proxy WS upgrade error:', err);
|
|
42
|
+
socket.end();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
proxy.on('error', (err, req, res) => {
|
|
47
|
+
logger.error('Proxy internal error:', err);
|
|
48
|
+
if (res && !res.headersSent) {
|
|
49
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
50
|
+
res.end('Proxy error');
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
let assignedPort;
|
|
55
|
+
|
|
56
|
+
server.listen(0, () => {
|
|
57
|
+
assignedPort = server.address().port;
|
|
58
|
+
logger.info(`Proxy server is listening on port ${assignedPort}`);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
get port() {
|
|
63
|
+
return assignedPort || server.address()?.port;
|
|
64
|
+
},
|
|
65
|
+
close: () => {
|
|
66
|
+
logger.info('Closing proxy server');
|
|
67
|
+
server.close();
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
module.exports = {
|
|
73
|
+
startHttpProxyServer,
|
|
74
|
+
};
|