@yousolution/node-red-contrib-you-tunnel-websocket 0.0.1 β 1.1.0
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/README.md +90 -5
- package/docker-compose.yml +1 -1
- package/nodes/wsTunnel.html +154 -78
- package/nodes/wsTunnel.js +87 -432
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -1,11 +1,96 @@
|
|
|
1
|
-
#
|
|
1
|
+
# node-red-contrib-you-tunnel-websocket
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/js/%40yousolution%2Fnode-red-contrib-you-tunnel-websocket)
|
|
4
|
+
[](https://www.npmjs.com/package/@yousolution/node-red-contrib-you-tunnel-websocket)
|
|
5
|
+
|
|
6
|
+
A Node-RED node collection for creating WebSocket tunnels that enable HTTP traffic routing over WebSocket connections.
|
|
4
7
|
|
|
5
8
|
## Install
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
Install via Node-RED palette manager, or:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @yousolution/node-red-contrib-you-tunnel-websocket
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Nodes
|
|
17
|
+
|
|
18
|
+
### wstunnel
|
|
19
|
+
|
|
20
|
+
Configuration node for WebSocket tunnel server settings.
|
|
21
|
+
|
|
22
|
+
#### Properties
|
|
23
|
+
|
|
24
|
+
- **Name**: Descriptive name for the tunnel configuration *(required)*
|
|
25
|
+
- **Host**: Hostname or IP address to bind the WebSocket server (default: `localhost`)
|
|
26
|
+
- **Port**: Port number for the WebSocket server (default: `443`)
|
|
27
|
+
- **Path**: Optional path component for the WebSocket URL
|
|
28
|
+
- **Tunnel ID Header**: HTTP header name used to identify tunnel connections (default: `x-tunnel-id`)
|
|
29
|
+
- **Log Level**: Logging verbosity level (`trace`, `debug`, `info`, `warning`, `error`)
|
|
30
|
+
|
|
31
|
+
### wstunnel server
|
|
32
|
+
|
|
33
|
+
Creates a WebSocket server that can tunnel HTTP traffic over WebSocket connections.
|
|
34
|
+
|
|
35
|
+
#### Properties
|
|
36
|
+
|
|
37
|
+
- **Name**: Optional name for the node
|
|
38
|
+
- **wstunnel**: Reference to a wstunnel configuration node *(required)*
|
|
39
|
+
|
|
40
|
+
#### Inputs
|
|
41
|
+
|
|
42
|
+
*none*
|
|
43
|
+
|
|
44
|
+
#### Outputs
|
|
45
|
+
|
|
46
|
+
*none*
|
|
47
|
+
|
|
48
|
+
#### Details
|
|
49
|
+
|
|
50
|
+
The server node creates a WebSocket server that listens for client connections. HTTP traffic can be tunneled through established WebSocket connections using tunnel ID headers.
|
|
51
|
+
|
|
52
|
+
Status indicators:
|
|
53
|
+
- π΄ **disconnected**: Server is not running
|
|
54
|
+
- π’ **listening on port [port]**: Server is active and listening
|
|
55
|
+
- π’ **clients [count]**: Number of connected WebSocket clients
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
### Basic Setup
|
|
60
|
+
|
|
61
|
+
1. Add a **wstunnel** configuration node:
|
|
62
|
+
- Set Name, Host, Port, and other options
|
|
63
|
+
|
|
64
|
+
2. Add a **wstunnel server** node:
|
|
65
|
+
- Select the configuration node
|
|
66
|
+
- Deploy the flow
|
|
67
|
+
|
|
68
|
+
3. The server will start listening for WebSocket connections
|
|
69
|
+
|
|
70
|
+
### Connection URLs
|
|
71
|
+
|
|
72
|
+
WebSocket servers are accessible at:
|
|
73
|
+
```
|
|
74
|
+
ws://[host]:[port][/path]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For secure connections:
|
|
78
|
+
```
|
|
79
|
+
wss://[host]:[port][/path]
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Requirements
|
|
83
|
+
|
|
84
|
+
- **Node-RED**: >= 2.0.0
|
|
85
|
+
- **Node.js**: >= 12.0.0
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
Apache-2.0
|
|
90
|
+
|
|
91
|
+
## Author
|
|
8
92
|
|
|
9
|
-
|
|
93
|
+
Andrea Trentin (<andrea.trentin@yousolution.cloud>)
|
|
10
94
|
|
|
11
|
-
|
|
95
|
+
Repository: [yousolution-cloud/node-red-contrib-you-tunnel-websocket](https://github.com/yousolution-cloud/node-red-contrib-you-tunnel-websocket)</content>
|
|
96
|
+
<parameter name="filePath">README.md
|
package/docker-compose.yml
CHANGED
package/nodes/wsTunnel.html
CHANGED
|
@@ -13,20 +13,41 @@
|
|
|
13
13
|
|
|
14
14
|
<script type="text/x-red" data-template-name="wstunnel">
|
|
15
15
|
<div class="form-row">
|
|
16
|
-
<label for="node-config-input-name"><i class="fa fa-bookmark"></i>
|
|
16
|
+
<label for="node-config-input-name"><i class="fa fa-bookmark"></i> Name</label>
|
|
17
17
|
<input type="text" id="node-config-input-name" placeholder="tunnel name" />
|
|
18
18
|
</div>
|
|
19
19
|
<div class="form-row">
|
|
20
20
|
<label for="node-config-input-host"><i class="fa fa-bookmark"></i> Host</label>
|
|
21
21
|
<input type="text" id="node-config-input-host" placeholder="localhost" style="width: 40%;" />
|
|
22
22
|
<label for="node-config-input-port" style="margin-left: 10px; width: 35px; "> Port</label>
|
|
23
|
-
<input type="text" id="node-config-input-port" placeholder="
|
|
23
|
+
<input type="text" id="node-config-input-port" placeholder="443" style="width:45px">
|
|
24
24
|
</div>
|
|
25
25
|
<div class="form-row">
|
|
26
|
-
<label for="node-config-input-
|
|
27
|
-
<input type="text" id="node-config-input-
|
|
26
|
+
<label for="node-config-input-path"><i class="fa fa-gear"></i> Path </label>
|
|
27
|
+
<input type="text" id="node-config-input-path" placeholder="">
|
|
28
28
|
</div>
|
|
29
29
|
<div class="form-row">
|
|
30
|
+
<span><b>Tunnel Id</b></span>
|
|
31
|
+
</div>
|
|
32
|
+
<div class="form-row" style="padding-left: 10px">
|
|
33
|
+
<span>Name of the http custom header</span>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="form-row">
|
|
36
|
+
<label for="node-config-input-tunnelIdHeaderName"><i class="fa fa-gear"></i> Header </label>
|
|
37
|
+
<input type="text" id="node-config-input-tunnelIdHeaderName" placeholder="x-tunnel-id">
|
|
38
|
+
</div>
|
|
39
|
+
<div class="form-row">
|
|
40
|
+
<label for="node-config-input-logLevel"><i class="fa fa-list"></i> Log Level</label>
|
|
41
|
+
<select id="node-config-input-logLevel" style="width: 150px;">
|
|
42
|
+
<option value="trace">Trace</option>
|
|
43
|
+
<option value="debug">Debug</option>
|
|
44
|
+
<option value="info">Info</option>
|
|
45
|
+
<option value="warning">Warning</option>
|
|
46
|
+
<option value="error">Error</option>
|
|
47
|
+
</select>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- <div class="form-row">
|
|
30
51
|
<label for="node-config-input-srcPort"><i class="fa fa-gear"></i> Source Port</label>
|
|
31
52
|
<input type="text" id="node-config-input-srcPort">
|
|
32
53
|
</div>
|
|
@@ -41,7 +62,7 @@
|
|
|
41
62
|
<div class="form-row">
|
|
42
63
|
<label for="node-config-input-timeout"><i class="fa fa-gear"></i> Timeout</label>
|
|
43
64
|
<input type="text" id="node-config-input-timeout">
|
|
44
|
-
</div>
|
|
65
|
+
</div> -->
|
|
45
66
|
</script>
|
|
46
67
|
|
|
47
68
|
<script type="text/javascript">
|
|
@@ -52,11 +73,9 @@
|
|
|
52
73
|
name: { value: '', required: true },
|
|
53
74
|
host: { value: '' },
|
|
54
75
|
port: { value: '' },
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
dstPort: { value: '' },
|
|
59
|
-
timeout: { value: '' },
|
|
76
|
+
path: { value: '' },
|
|
77
|
+
tunnelIdHeaderName: { value: '' },
|
|
78
|
+
logLevel: { value: 'info' },
|
|
60
79
|
},
|
|
61
80
|
label: function () {
|
|
62
81
|
return this.name;
|
|
@@ -65,14 +84,90 @@
|
|
|
65
84
|
});
|
|
66
85
|
</script>
|
|
67
86
|
|
|
68
|
-
<script type="text/
|
|
87
|
+
<script type="text/html" data-help-name="wstunnel">
|
|
88
|
+
<p>Configuration node for WebSocket tunnel settings. This node defines the connection parameters and logging configuration for WebSocket tunnel servers.</p>
|
|
89
|
+
|
|
90
|
+
<h3>Configuration</h3>
|
|
91
|
+
<dl class="message-properties">
|
|
92
|
+
<dt>Name <span class="property-type">string</span></dt>
|
|
93
|
+
<dd>A descriptive name for this tunnel configuration. This is required and will be displayed in the node list.</dd>
|
|
94
|
+
|
|
95
|
+
<dt>Host <span class="property-type">string</span></dt>
|
|
96
|
+
<dd>The hostname or IP address where the WebSocket server will bind. Defaults to <code>localhost</code> if not specified.</dd>
|
|
97
|
+
|
|
98
|
+
<dt>Port <span class="property-type">number</span></dt>
|
|
99
|
+
<dd>The port number on which the WebSocket server will listen for connections. Defaults to <code>443</code> if not specified.</dd>
|
|
100
|
+
|
|
101
|
+
<dt>Path <span class="property-type">string</span></dt>
|
|
102
|
+
<dd>Optional path component for the WebSocket server URL. Can be left empty for root path connections.</dd>
|
|
103
|
+
|
|
104
|
+
<dt>Tunnel ID Header <span class="property-type">string</span></dt>
|
|
105
|
+
<dd>Name of the HTTP custom header used to identify tunnel connections. Defaults to <code>x-tunnel-id</code> if not specified.</dd>
|
|
106
|
+
|
|
107
|
+
<dt>Log Level <span class="property-type">string</span></dt>
|
|
108
|
+
<dd>
|
|
109
|
+
Sets the verbosity level for logging output. Available options:
|
|
110
|
+
<ul>
|
|
111
|
+
<li><strong>trace</strong> - Most verbose, includes all debug information</li>
|
|
112
|
+
<li><strong>debug</strong> - Debug information for troubleshooting</li>
|
|
113
|
+
<li><strong>info</strong> - General informational messages (default)</li>
|
|
114
|
+
<li><strong>warning</strong> - Warning messages only</li>
|
|
115
|
+
<li><strong>error</strong> - Error messages only</li>
|
|
116
|
+
</ul>
|
|
117
|
+
</dd>
|
|
118
|
+
</dl>
|
|
119
|
+
|
|
120
|
+
<h3>Usage</h3>
|
|
121
|
+
<p>
|
|
122
|
+
This configuration node is used by <code>wstunnel server</code> nodes to define connection parameters. Create one configuration node for each unique set of
|
|
123
|
+
connection settings you need.
|
|
124
|
+
</p>
|
|
125
|
+
|
|
126
|
+
<h3>Connection URL</h3>
|
|
127
|
+
<p>The WebSocket server will be accessible at:</p>
|
|
128
|
+
<pre>ws://[host]:[port][/path]</pre>
|
|
129
|
+
|
|
130
|
+
<h3>Tunnel ID Header</h3>
|
|
131
|
+
<p>
|
|
132
|
+
The tunnel ID header is used to associate incoming HTTP requests with specific WebSocket connections. Clients should include this header in their requests
|
|
133
|
+
to identify which tunnel to use.
|
|
134
|
+
</p>
|
|
135
|
+
|
|
136
|
+
<h3>Log Level Impact</h3>
|
|
137
|
+
<p>The selected log level affects all logging output from the reverse-ws-tunnel library:</p>
|
|
138
|
+
<ul>
|
|
139
|
+
<li><strong>trace/debug</strong> - Useful for development and troubleshooting connection issues</li>
|
|
140
|
+
<li><strong>info</strong> - Recommended for production environments</li>
|
|
141
|
+
<li><strong>warning/error</strong> - Minimal logging for performance-critical environments</li>
|
|
142
|
+
</ul>
|
|
143
|
+
|
|
144
|
+
<h3>Example Configuration</h3>
|
|
145
|
+
<ul>
|
|
146
|
+
<li><strong>Name:</strong> "Production Tunnel"</li>
|
|
147
|
+
<li><strong>Host:</strong> "0.0.0.0" (to accept connections from any interface)</li>
|
|
148
|
+
<li><strong>Port:</strong> "8080"</li>
|
|
149
|
+
<li><strong>Path:</strong> "/api/tunnel"</li>
|
|
150
|
+
<li><strong>Header:</strong> "x-tunnel-id"</li>
|
|
151
|
+
<li><strong>Log Level:</strong> "info"</li>
|
|
152
|
+
</ul>
|
|
153
|
+
|
|
154
|
+
<h3>Notes</h3>
|
|
155
|
+
<ul>
|
|
156
|
+
<li>Each configuration can be shared by multiple <code>wstunnel server</code> nodes</li>
|
|
157
|
+
<li>Changing the port or host requires redeploying affected server nodes</li>
|
|
158
|
+
<li>Log level changes are applied when server nodes are deployed or restarted</li>
|
|
159
|
+
<li>Ensure the specified port is available and not blocked by firewall rules</li>
|
|
160
|
+
</ul>
|
|
161
|
+
</script>
|
|
162
|
+
|
|
163
|
+
<!-- <script type="text/x-red" data-template-name="wstunnel client">
|
|
69
164
|
<div class="form-row">
|
|
70
165
|
<label for="node-input-url"><i class="fa fa-bookmark"></i> Url</label>
|
|
71
166
|
<input type="text" id="node-input-url" placeholder="Url tunnel" />
|
|
72
167
|
</div>
|
|
73
|
-
</script>
|
|
168
|
+
</script> -->
|
|
74
169
|
|
|
75
|
-
<script type="text/x-red" data-help-name="wstunnel client">
|
|
170
|
+
<!-- <script type="text/x-red" data-help-name="wstunnel client">
|
|
76
171
|
<p>
|
|
77
172
|
This node establishes a WebSocket client connection to a remote WebSocket server,
|
|
78
173
|
enabling the creation of an HTTP tunnel over the WebSocket connection. It can be
|
|
@@ -115,9 +210,9 @@
|
|
|
115
210
|
<li>If the connection is closed or fails, the node will attempt to reconnect based on internal reconnection logic.</li>
|
|
116
211
|
<li>Ensure the WebSocket server you're connecting to supports tunneling or is configured to handle this type of traffic.</li>
|
|
117
212
|
</ul>
|
|
118
|
-
</script>
|
|
213
|
+
</script> -->
|
|
119
214
|
|
|
120
|
-
<script type="text/javascript">
|
|
215
|
+
<!-- <script type="text/javascript">
|
|
121
216
|
RED.nodes.registerType('wstunnel client', {
|
|
122
217
|
category: 'network',
|
|
123
218
|
color: '#a7c957',
|
|
@@ -134,84 +229,65 @@
|
|
|
134
229
|
return this.name ? 'node_label_italic' : '';
|
|
135
230
|
},
|
|
136
231
|
});
|
|
137
|
-
</script>
|
|
232
|
+
</script> -->
|
|
138
233
|
|
|
139
234
|
<script type="text/x-red" data-template-name="wstunnel server">
|
|
140
235
|
<div class="form-row">
|
|
141
|
-
<label for="node-input-
|
|
236
|
+
<label for="node-input-wstunnel"><i class="fa fa-gears"></i> Cofigure Tunnel Websocket server</label>
|
|
142
237
|
<input type="text" id="node-input-wstunnel">
|
|
143
238
|
</div>
|
|
144
239
|
</script>
|
|
145
240
|
|
|
146
|
-
<script type="text/
|
|
147
|
-
<p>
|
|
148
|
-
This node is used to configure and manage a WebSocket server tunnel. It allows the establishment
|
|
149
|
-
of WebSocket-based communication between a server and a client. You can define the source and destination
|
|
150
|
-
addresses and ports to tunnel the traffic through WebSocket connections.
|
|
151
|
-
</p>
|
|
241
|
+
<script type="text/html" data-help-name="wstunnel server">
|
|
242
|
+
<p>This node starts a WebSocket server for tunneling HTTP traffic over WebSocket connections.</p>
|
|
152
243
|
|
|
153
|
-
<h3>
|
|
154
|
-
<
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
<td>The host address to which the WebSocket server should connect. This is typically the address of your server hosting the WebSocket server.</td>
|
|
162
|
-
</tr>
|
|
163
|
-
<tr>
|
|
164
|
-
<td><strong>Port</strong></td>
|
|
165
|
-
<td>The port on which the WebSocket server will listen. This is the port used to accept WebSocket connections from clients.</td>
|
|
166
|
-
</tr>
|
|
167
|
-
<tr>
|
|
168
|
-
<td><strong>Source Address</strong></td>
|
|
169
|
-
<td>The source address (IP or hostname) for the WebSocket connection. This is typically set to the address of the service initiating the WebSocket connection.</td>
|
|
170
|
-
</tr>
|
|
171
|
-
<tr>
|
|
172
|
-
<td><strong>Source Port</strong></td>
|
|
173
|
-
<td>The source port that the WebSocket server will use. This is required to define the source of the communication.</td>
|
|
174
|
-
</tr>
|
|
175
|
-
<tr>
|
|
176
|
-
<td><strong>Destination Address</strong></td>
|
|
177
|
-
<td>The destination address where the WebSocket connection should forward the tunneled HTTP traffic. This is usually set to the IP or hostname of the remote service.</td>
|
|
178
|
-
</tr>
|
|
179
|
-
<tr>
|
|
180
|
-
<td><strong>Destination Port</strong></td>
|
|
181
|
-
<td>The destination port where the tunneled HTTP traffic will be sent. This corresponds to the port of the destination service.</td>
|
|
182
|
-
</tr>
|
|
183
|
-
<tr>
|
|
184
|
-
<td><strong>Timeout</strong></td>
|
|
185
|
-
<td>Timeout in milliseconds for the WebSocket connection. If the WebSocket connection does not establish within this time, the connection attempt will be aborted.</td>
|
|
186
|
-
</tr>
|
|
187
|
-
</table>
|
|
244
|
+
<h3>Configuration</h3>
|
|
245
|
+
<dl class="message-properties">
|
|
246
|
+
<dt>wstunnel <span class="property-type">config</span></dt>
|
|
247
|
+
<dd>
|
|
248
|
+
Select a <code>wstunnel</code> configuration node that provides server options such as <code>host</code>, <code>port</code>, <code>path</code>,
|
|
249
|
+
<code>tunnel ID header name</code>, and <code>log level</code>.
|
|
250
|
+
</dd>
|
|
251
|
+
</dl>
|
|
188
252
|
|
|
189
|
-
<h3>
|
|
253
|
+
<h3>Details</h3>
|
|
254
|
+
<p>When deployed, the node starts a WebSocket server using the port and options defined in the selected <code>wstunnel</code> config node.</p>
|
|
255
|
+
<p>
|
|
256
|
+
The node updates its status to indicate when it is listening and displays the number of connected clients. Connections are automatically closed if the node
|
|
257
|
+
is redeployed or the port is changed.
|
|
258
|
+
</p>
|
|
259
|
+
<p>The server listens for WebSocket connections and uses the configured tunnel ID header to associate incoming connections.</p>
|
|
190
260
|
<p>
|
|
191
|
-
|
|
192
|
-
|
|
261
|
+
The log level configured in the <code>wstunnel</code> config node is applied when the server starts, controlling the verbosity of logging output from the
|
|
262
|
+
tunnel library.
|
|
193
263
|
</p>
|
|
194
264
|
|
|
195
|
-
<h3>
|
|
196
|
-
<
|
|
197
|
-
[
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
265
|
+
<h3>Runtime Behavior</h3>
|
|
266
|
+
<ul>
|
|
267
|
+
<li>Status will show <code>listening on port [port]</code> when active.</li>
|
|
268
|
+
<li>Each client connection updates the status with the current number of connected clients.</li>
|
|
269
|
+
<li>All client connections are properly closed when the node is stopped or removed.</li>
|
|
270
|
+
</ul>
|
|
271
|
+
|
|
272
|
+
<h3>Logging</h3>
|
|
273
|
+
<dl class="message-properties">
|
|
274
|
+
<dt>Listening</dt>
|
|
275
|
+
<dd>Logs when the WebSocket server is up and listening on the configured port.</dd>
|
|
276
|
+
<dt>Connection</dt>
|
|
277
|
+
<dd>Logs the client IP and port when a new WebSocket client connects.</dd>
|
|
278
|
+
<dt>Disconnection</dt>
|
|
279
|
+
<dd>Logs when a client disconnects and shows remaining connected clients.</dd>
|
|
280
|
+
</dl>
|
|
209
281
|
|
|
210
282
|
<h3>Notes</h3>
|
|
211
283
|
<ul>
|
|
212
|
-
<li>
|
|
213
|
-
|
|
214
|
-
|
|
284
|
+
<li>
|
|
285
|
+
Ensure the <code>wstunnel</code> config node is correctly configured with a valid <strong>port</strong>, <strong>tunnel ID header</strong>, and
|
|
286
|
+
appropriate <strong>log level</strong>.
|
|
287
|
+
</li>
|
|
288
|
+
<li>Redeploying the flow will gracefully shut down existing WebSocket servers and restart them with updated configuration.</li>
|
|
289
|
+
<li>Log level changes require redeployment to take effect.</li>
|
|
290
|
+
<li>This node does not have input or output wires β it works autonomously based on its configuration.</li>
|
|
215
291
|
</ul>
|
|
216
292
|
</script>
|
|
217
293
|
|
package/nodes/wsTunnel.js
CHANGED
|
@@ -13,16 +13,11 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
**/
|
|
16
|
-
const WebSocket = require('ws');
|
|
17
|
-
const { Buffer } = require('buffer');
|
|
18
|
-
const net = require('net');
|
|
19
|
-
const { v4: uuidv4 } = require('uuid');
|
|
20
|
-
const tls = require('tls');
|
|
21
16
|
|
|
22
|
-
const
|
|
17
|
+
const { startWebSocketServer } = require('@remotelinker/reverse-ws-tunnel/server');
|
|
18
|
+
const { setLogLevel } = require('@remotelinker/reverse-ws-tunnel/utils');
|
|
19
|
+
|
|
23
20
|
const instances = {};
|
|
24
|
-
const HTTP_TIMEOUT = 1000 * 30; // in seconds
|
|
25
|
-
const RECONNECT_INTERVAL = 1000 * 5;
|
|
26
21
|
|
|
27
22
|
module.exports = function (RED) {
|
|
28
23
|
'use strict';
|
|
@@ -30,471 +25,131 @@ module.exports = function (RED) {
|
|
|
30
25
|
function WSTunnelNode(n) {
|
|
31
26
|
RED.nodes.createNode(this, n);
|
|
32
27
|
|
|
33
|
-
const [dstProtocol, dstHost, dstPort] = getProtocolAndHostAndDefautlPort(n.dstAddr);
|
|
34
|
-
|
|
35
|
-
// console.log('{}{}{}{}{}{}{}{}');
|
|
36
|
-
// console.log(dstProtocol);
|
|
37
|
-
// console.log(dstHost);
|
|
38
|
-
// console.log(dstPort);
|
|
39
|
-
// console.log('{}{}{}{}{}{}{}{}');
|
|
40
|
-
|
|
41
28
|
this.options = {
|
|
42
29
|
name: n.name || 'websocket tunnel',
|
|
43
30
|
host: n.host || 'localhost',
|
|
44
|
-
port: n.port ||
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
dstAddr: dstHost || 'localhost',
|
|
49
|
-
dstPort: n.dstPort || dstPort || 80,
|
|
50
|
-
timeout: n.timeout || HTTP_TIMEOUT,
|
|
31
|
+
port: n.port || 443,
|
|
32
|
+
path: n.path || '',
|
|
33
|
+
tunnelIdHeaderName: n.tunnelIdHeaderName || 'x-tunnel-id',
|
|
34
|
+
logLevel: n.logLevel || 'info',
|
|
51
35
|
};
|
|
52
36
|
}
|
|
53
37
|
|
|
54
38
|
RED.nodes.registerType('wstunnel', WSTunnelNode);
|
|
55
39
|
|
|
56
|
-
function
|
|
40
|
+
function WSTunnelServer(n) {
|
|
57
41
|
RED.nodes.createNode(this, n);
|
|
58
|
-
|
|
59
|
-
this
|
|
60
|
-
|
|
61
|
-
const websocketURL = n.url;
|
|
62
|
-
|
|
63
|
-
if (!websocketURL) {
|
|
64
|
-
node.status({ fill: 'red', shape: 'dot', text: 'The URL is invalid or not defined.' });
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function connectWebSocket() {
|
|
69
|
-
let options = undefined;
|
|
70
|
-
// Crea una connessione al server WebSocket
|
|
71
|
-
const ws = new WebSocket(websocketURL, options);
|
|
72
|
-
|
|
73
|
-
// Quando la connessione Γ¨ aperta, invia un messaggio al server
|
|
74
|
-
ws.on('open', () => {
|
|
75
|
-
console.log('Connected to the WebSocket server');
|
|
76
|
-
node.status({ fill: 'green', shape: 'dot', text: 'WebSocket tunnel established' });
|
|
77
|
-
});
|
|
42
|
+
this.tunnelConfig = RED.nodes.getNode(n.wstunnel);
|
|
43
|
+
const node = this;
|
|
44
|
+
const id = node.id;
|
|
78
45
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
// console.log('Received Base64 encoded HTTP RAW request:');
|
|
46
|
+
// Set the log level dynamically based on configuration
|
|
47
|
+
const logLevel = node.tunnelConfig.options.logLevel || 'info';
|
|
48
|
+
setLogLevel(logLevel);
|
|
83
49
|
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
// const request = JSON.parse(Buffer.from(data, 'base64').toString('utf-8'));
|
|
87
|
-
// const rawHttpRequest = Buffer.from(request.httpRequest, 'base64').toString('utf-8');
|
|
88
|
-
// console.log('Decoded HTTP RAW Request:', request.httpRequest);
|
|
50
|
+
node.status({ fill: 'red', shape: 'ring', text: 'disconnected' });
|
|
89
51
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
// console.log(res.toString('utf-8'));
|
|
95
|
-
// console.log('======');
|
|
96
|
-
ws.send(Buffer.concat([extractedUUID, res]));
|
|
97
|
-
} catch (error) {
|
|
98
|
-
console.log(error);
|
|
99
|
-
}
|
|
100
|
-
});
|
|
52
|
+
const port = node.tunnelConfig.options.port;
|
|
53
|
+
const host = node.tunnelConfig.options.host ? '' : undefined;
|
|
54
|
+
const path = node.tunnelConfig.options.path ? '' : undefined;
|
|
55
|
+
const tunnelIdHeaderName = node.tunnelConfig.options.tunnelIdHeaderName;
|
|
101
56
|
|
|
102
|
-
|
|
103
|
-
ws.on('error', (error) => {
|
|
104
|
-
node.status({ fill: 'red', shape: 'dot', text: 'Websocket error.' });
|
|
105
|
-
console.error('Error occurred: ', error);
|
|
106
|
-
});
|
|
57
|
+
instances[id] = instances[id] || { state: {} };
|
|
107
58
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
59
|
+
if (instances[id].state[port]) {
|
|
60
|
+
console.warn(`Server already running on port ${port} for node ${id}. Skipping creation.`);
|
|
61
|
+
node.status({
|
|
62
|
+
fill: 'green',
|
|
63
|
+
shape: 'dot',
|
|
64
|
+
text: `listening on port ${port}`,
|
|
113
65
|
});
|
|
66
|
+
return;
|
|
114
67
|
}
|
|
115
68
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
69
|
+
instances[id].state = startWebSocketServer({
|
|
70
|
+
port,
|
|
71
|
+
host,
|
|
72
|
+
path,
|
|
73
|
+
tunnelIdHeaderName,
|
|
74
|
+
});
|
|
120
75
|
|
|
121
|
-
|
|
122
|
-
return await tlsRequest(rawHttpRequest);
|
|
123
|
-
}
|
|
76
|
+
console.log(`Started WebSocketServer for node ${id} on port ${port}`);
|
|
124
77
|
|
|
125
|
-
|
|
78
|
+
if (instances[id].state[port].webSocketServer) {
|
|
79
|
+
onListening();
|
|
126
80
|
}
|
|
127
81
|
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
const { hostname, port } = extractHostAndPort(rawHttpRequest);
|
|
131
|
-
|
|
132
|
-
let responseData = Buffer.alloc(0); // Inizializza un buffer vuoto per accumulare i dati
|
|
82
|
+
function onListening() {
|
|
83
|
+
const server = instances[id].state[port].webSocketServer;
|
|
133
84
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
// Ricevi i dati dalla risposta del server
|
|
142
|
-
client.on('data', (data) => {
|
|
143
|
-
// console.log('Received response from server:');
|
|
144
|
-
// console.log(data.toString()); // Converte i dati ricevuti in stringa e li stampa
|
|
145
|
-
// return resolve(data);
|
|
146
|
-
responseData = Buffer.concat([responseData, data]); // Accumula i dati ricevuti
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Quando la connessione si chiude
|
|
150
|
-
client.on('end', () => {
|
|
151
|
-
console.log('Connection closed');
|
|
152
|
-
resolve(responseData);
|
|
153
|
-
// client.destroy()
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Gestione degli errori
|
|
157
|
-
client.on('error', (err) => {
|
|
158
|
-
reject(err);
|
|
159
|
-
console.error('Error:', err);
|
|
85
|
+
server.on('listening', () => {
|
|
86
|
+
console.log('Listening ' + id);
|
|
87
|
+
node.status({
|
|
88
|
+
fill: 'green',
|
|
89
|
+
shape: 'dot',
|
|
90
|
+
text: `listening on port ${port}`,
|
|
160
91
|
});
|
|
161
92
|
});
|
|
162
|
-
}
|
|
163
93
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const
|
|
94
|
+
server.on('connection', (ws) => {
|
|
95
|
+
const clientAddress = ws._socket.remoteAddress;
|
|
96
|
+
const clientPort = ws._socket.remotePort;
|
|
97
|
+
const totalClients = server.clients.size;
|
|
167
98
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// Creazione di un socket TCP
|
|
174
|
-
const client = net.createConnection(port, hostname, () => {
|
|
175
|
-
client.write(rawHttpRequest); // Invia la richiesta HTTP raw
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// Ricevi i dati dalla risposta del server
|
|
179
|
-
client.on('data', (data) => {
|
|
180
|
-
responseData = Buffer.concat([responseData, data]); // Accumula i dati ricevuti
|
|
99
|
+
node.status({
|
|
100
|
+
fill: 'green',
|
|
101
|
+
shape: 'dot',
|
|
102
|
+
text: `clients ${totalClients}`,
|
|
181
103
|
});
|
|
182
104
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
105
|
+
console.log(`WebSocket connection from ${clientAddress}:${clientPort}`);
|
|
106
|
+
console.log(`Total connected clients: ${totalClients}`);
|
|
107
|
+
|
|
108
|
+
ws.on('close', () => {
|
|
109
|
+
const remainingClients = server.clients.size;
|
|
110
|
+
node.status({
|
|
111
|
+
fill: 'green',
|
|
112
|
+
shape: 'dot',
|
|
113
|
+
text: `clients ${remainingClients}`,
|
|
114
|
+
});
|
|
115
|
+
console.log(`Client disconnected: ${clientAddress}:${clientPort}`);
|
|
116
|
+
console.log(`Remaining clients: ${remainingClients}`);
|
|
193
117
|
});
|
|
194
118
|
});
|
|
195
119
|
}
|
|
196
120
|
|
|
197
|
-
function
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
if (!match) {
|
|
203
|
-
throw new Error('Host non trovato nella stringa HTTP raw');
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const host = match[1];
|
|
207
|
-
const [hostname, port = 443] = host.split(':'); // Porta di default 443 per HTTPS
|
|
208
|
-
|
|
209
|
-
return { hostname, port: parseInt(port, 10) };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
this.url = n.url;
|
|
213
|
-
// this.operation = n.operation;
|
|
214
|
-
// this.filename = n.filename;
|
|
215
|
-
// this.fileExtension = n.fileExtension;
|
|
216
|
-
// this.workdir = n.workdir;
|
|
217
|
-
// this.tunnelConfig = RED.nodes.getNode(this.tunnel);
|
|
218
|
-
|
|
219
|
-
// const globalContext = this.context().global;
|
|
220
|
-
|
|
221
|
-
// if (!this.tunnelConfig) {
|
|
222
|
-
// this.error('missing tunnel Websocket configuration');
|
|
223
|
-
// return;
|
|
224
|
-
// }
|
|
225
|
-
|
|
226
|
-
// Hack for async function
|
|
227
|
-
// setTimeout(async () => {
|
|
228
|
-
// await connect(node);
|
|
229
|
-
// });
|
|
230
|
-
|
|
231
|
-
// node.on('input', async (msg, send, done) => {
|
|
232
|
-
// setTimeout(async () => {
|
|
233
|
-
// await connect(node);
|
|
234
|
-
// }, node.tunnelConfig.options.reconnectTimeout);
|
|
235
|
-
// });
|
|
236
|
-
}
|
|
237
|
-
RED.nodes.registerType('wstunnel client', WSTunnelClient);
|
|
238
|
-
|
|
239
|
-
function WSTunnelServer(n) {
|
|
240
|
-
instances[n.id] = instances[n.id] || {};
|
|
241
|
-
instances[n.id].server = instances[n.id].server || null;
|
|
242
|
-
instances[n.id].wss = instances[n.id].wss || null;
|
|
243
|
-
instances[n.id].websocketServer = instances[n.id].websocketServer || null;
|
|
244
|
-
|
|
245
|
-
RED.nodes.createNode(this, n);
|
|
246
|
-
this.tunnelConfig = RED.nodes.getNode(n.wstunnel);
|
|
247
|
-
let node = this;
|
|
248
|
-
const id = n.id;
|
|
249
|
-
|
|
250
|
-
node.status({ fill: 'red', shape: 'ring', text: 'disconnected' });
|
|
251
|
-
|
|
252
|
-
const port = node.tunnelConfig.options.port;
|
|
253
|
-
const srcAddr = node.tunnelConfig.options.srcAddr || 'localhost';
|
|
254
|
-
const srcPort = node.tunnelConfig.options.srcPort || 8083;
|
|
255
|
-
const dstAddr = node.tunnelConfig.options.dstAddr || 'localhost';
|
|
256
|
-
const dstPort = node.tunnelConfig.options.dstPort || 80;
|
|
257
|
-
const timeout = node.tunnelConfig.options.timeout || HTTP_TIMEOUT;
|
|
258
|
-
// const port = 4443;
|
|
259
|
-
|
|
260
|
-
// console.log('+++++++++ OPTIONS ++++++++');
|
|
261
|
-
// console.log(port);
|
|
262
|
-
// console.log(dstPort);
|
|
263
|
-
// console.log(dstAddr);
|
|
264
|
-
// console.log(timeout);
|
|
265
|
-
// console.log('+++++++++ OPTIONS ++++++++');
|
|
266
|
-
const options = {
|
|
267
|
-
id,
|
|
268
|
-
port,
|
|
269
|
-
srcAddr,
|
|
270
|
-
srcPort,
|
|
271
|
-
dstAddr,
|
|
272
|
-
dstPort,
|
|
273
|
-
timeout,
|
|
274
|
-
};
|
|
275
|
-
|
|
276
|
-
if (instances[options.id].server) {
|
|
277
|
-
instances[options.id].server.close(() => {
|
|
278
|
-
TCPServer(options);
|
|
279
|
-
});
|
|
280
|
-
} else {
|
|
281
|
-
TCPServer(options);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (instances[n.id].wss) {
|
|
285
|
-
instances[n.id].wss.close(() => {
|
|
286
|
-
webSocketServer(node, options);
|
|
287
|
-
});
|
|
288
|
-
} else {
|
|
289
|
-
webSocketServer(node, options);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function requestFromServer(message, options) {
|
|
294
|
-
return new Promise((resolve, reject) => {
|
|
295
|
-
if (!instances[options.id].websocketServer) {
|
|
296
|
-
return reject(new WebsocketTunnelError('WebSocket connection not established.', 500));
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const uuid = uuidv4();
|
|
300
|
-
const uuidBuffer = Buffer.from(uuid);
|
|
301
|
-
const resultBuffer = Buffer.concat([uuidBuffer, Buffer.from(message)]);
|
|
302
|
-
|
|
303
|
-
const timeoutId = setTimeout(() => {
|
|
304
|
-
clearTimeout(timeoutId);
|
|
305
|
-
reject('Timeout: No response from the client.');
|
|
306
|
-
}, options.timeout);
|
|
307
|
-
|
|
308
|
-
const onMessage = (response) => {
|
|
309
|
-
// console.log(response.toString());
|
|
310
|
-
const extractedUUID = response.slice(0, 36);
|
|
311
|
-
if (Buffer.compare(uuidBuffer, extractedUUID) === 0) {
|
|
312
|
-
resolve(response.slice(36));
|
|
313
|
-
instances[options.id].websocketServer.removeListener('message', onMessage);
|
|
314
|
-
clearTimeout(timeoutId);
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
instances[options.id].websocketServer.on('message', onMessage);
|
|
318
|
-
|
|
319
|
-
instances[options.id].websocketServer.send(resultBuffer);
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function webSocketServer(node, options) {
|
|
324
|
-
try {
|
|
325
|
-
instances[node.id].wss = new WebSocket.Server({ port: options.port });
|
|
326
|
-
} catch (error) {
|
|
327
|
-
console.log(error);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
instances[node.id].wss.on('listening', () => {
|
|
331
|
-
console.log(`WebSocket server is listening on port ${options.port}`);
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
instances[node.id].wss.on('connection', (ws) => {
|
|
335
|
-
if (instances[node.id].websocketServer) {
|
|
336
|
-
console.log('WebSocket tunnel is already active');
|
|
337
|
-
ws.close();
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
console.log('WebSocket tunnel established');
|
|
342
|
-
node.status({ fill: 'green', shape: 'dot', text: 'WebSocket tunnel established' });
|
|
343
|
-
instances[node.id].websocketServer = ws;
|
|
344
|
-
|
|
345
|
-
// Invia un messaggio di benvenuto al client appena connesso
|
|
346
|
-
// ws.send('Ciao dal server WebSocket!');
|
|
347
|
-
|
|
348
|
-
const interval = setInterval(() => {
|
|
349
|
-
if (ws.readyState === WebSocket.OPEN) {
|
|
350
|
-
ws.ping(); // invia un ping al client
|
|
351
|
-
console.log('Ping inviato al client');
|
|
352
|
-
}
|
|
353
|
-
}, PING_INTERVAL);
|
|
354
|
-
|
|
355
|
-
// Quando si verifica un errore
|
|
356
|
-
ws.on('error', function error(err) {
|
|
357
|
-
instances[node.id].websocketServer = null;
|
|
358
|
-
console.error('WebSocket error:', err);
|
|
359
|
-
clearInterval(interval); // pulisci l'intervallo quando la connessione Γ¨ chiusa
|
|
360
|
-
node.error(err, { payload: 'Open websockeet tunnel error', message: err.message });
|
|
361
|
-
node.status({ fill: 'red', shape: 'dot', text: 'Error' });
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
// Gestiamo i messaggi ricevuti dal client
|
|
365
|
-
// ws.on('message', (message) => {
|
|
366
|
-
// console.log(`Messaggio ricevuto dal client: ${message}`);
|
|
367
|
-
// // Rispondiamo al client
|
|
368
|
-
// // ws.send(`Hai detto: ${message}`);
|
|
369
|
-
// });
|
|
370
|
-
|
|
371
|
-
// Gestiamo la chiusura della connessione
|
|
372
|
-
ws.on('close', () => {
|
|
373
|
-
console.log('WebSocket tunnel closed');
|
|
374
|
-
instances[node.id].websocketServer = null;
|
|
375
|
-
node.status({ fill: 'red', shape: 'ring', text: 'disconnected' });
|
|
376
|
-
clearInterval(interval); // pulisci l'intervallo quando la connessione Γ¨ chiusa
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
// Quando ricevi un pong dal client
|
|
380
|
-
ws.on('pong', () => {
|
|
381
|
-
console.log('Pong ricevuto dal client');
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
instances[node.id].wss.on('error', (error) => {
|
|
386
|
-
console.log(error);
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function TCPServer(options) {
|
|
391
|
-
instances[options.id].server = net.createServer((socket) => {
|
|
392
|
-
// console.log('Client connesso');
|
|
393
|
-
|
|
394
|
-
// Variabile per memorizzare i dati grezzi della richiesta
|
|
395
|
-
let rawRequestData = '';
|
|
396
|
-
|
|
397
|
-
// Leggi i dati grezzi dal socket
|
|
398
|
-
socket.on('data', async (data) => {
|
|
399
|
-
rawRequestData += data.toString(); // Aggiungi i dati ricevuti alla variabile rawRequestData
|
|
400
|
-
|
|
401
|
-
// Puoi scegliere di processare la richiesta solo quando hai ricevuto tutto il contenuto, ad esempio quando la richiesta termina
|
|
402
|
-
if (rawRequestData.includes('\r\n\r\n')) {
|
|
403
|
-
// Quando i dati contengono l'header completo, puoi trattarli come una richiesta completa
|
|
404
|
-
|
|
405
|
-
// Sostituire il campo Host con il nuovo valore
|
|
406
|
-
rawRequestData = rawRequestData.replace(/Host:\s*([^\r\n]*)/, `Host: ${options.dstAddr}:${options.dstPort}`);
|
|
407
|
-
console.log('Richiesta HTTP raw ricevuta:');
|
|
408
|
-
console.log(rawRequestData);
|
|
409
|
-
|
|
121
|
+
this.on('close', function (removed, done) {
|
|
122
|
+
if (instances[id] && instances[id].state && instances[id].state[port]) {
|
|
123
|
+
const server = instances[id].state[port].webSocketServer;
|
|
124
|
+
if (server) {
|
|
410
125
|
try {
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
// Chiudi la connessione (opzionale)
|
|
419
|
-
} catch (error) {
|
|
420
|
-
console.log(error);
|
|
421
|
-
let errorRespose =
|
|
422
|
-
`HTTP/1.1 408 Request Timeout\r\n` +
|
|
423
|
-
`Content-Type: text/plain; charset=utf-8\r\n` +
|
|
424
|
-
// `Content-Length: 29\r\n` +
|
|
425
|
-
`Connection: close\r\n` +
|
|
426
|
-
`Date: ${new Date().toUTCString()}\r\n\r\n` + // usa toUTCString() invece di toGMTString
|
|
427
|
-
`Request Timeout: The server timed out waiting for the request.`;
|
|
428
|
-
|
|
429
|
-
if (error instanceof WebsocketTunnelError) {
|
|
430
|
-
errorRespose =
|
|
431
|
-
`HTTP/1.1 ${error.code} ${error.name}\r\n` +
|
|
432
|
-
`Content-Type: text/plain; charset=utf-8\r\n` +
|
|
433
|
-
// `Content-Length: 29\r\n` +
|
|
434
|
-
`Connection: close\r\n` +
|
|
435
|
-
`Date: ${new Date().toUTCString()}\r\n\r\n` + // usa toUTCString() invece di toGMTString
|
|
436
|
-
`${error.message}`;
|
|
126
|
+
for (const client of server.clients) {
|
|
127
|
+
try {
|
|
128
|
+
client.removeAllListeners();
|
|
129
|
+
client.close();
|
|
130
|
+
} catch (clientErr) {
|
|
131
|
+
console.error(`Error terminating client on port ${port}:`, clientErr);
|
|
132
|
+
}
|
|
437
133
|
}
|
|
438
134
|
|
|
439
|
-
|
|
135
|
+
server.close(() => {
|
|
136
|
+
console.log(`Closed WebSocketServer on port ${port} during node shutdown`);
|
|
137
|
+
});
|
|
138
|
+
} catch (err) {
|
|
139
|
+
console.error(`Error closing WebSocketServer on port ${port}:`, err);
|
|
440
140
|
}
|
|
441
|
-
socket.end();
|
|
442
141
|
}
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
socket.on('end', () => {
|
|
446
|
-
console.log('Client disconnected');
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
socket.on('error', (err) => {
|
|
450
|
-
console.error('Connection error:', err);
|
|
451
|
-
});
|
|
452
|
-
});
|
|
453
142
|
|
|
454
|
-
|
|
455
|
-
instances[options.id].server.listen(options.srcPort, () => {
|
|
456
|
-
console.log(`HTTP server listening on port ${options.srcPort}`);
|
|
457
|
-
});
|
|
458
|
-
instances[options.id].server.on('error', (error) => {
|
|
459
|
-
console.log(error);
|
|
460
|
-
});
|
|
461
|
-
} catch (error) {
|
|
462
|
-
console.log(error);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
143
|
+
delete instances[id].state[port];
|
|
465
144
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
https: 443,
|
|
471
|
-
ftp: 21,
|
|
472
|
-
ssh: 22,
|
|
473
|
-
};
|
|
474
|
-
// Add "http://" as a default protocol
|
|
475
|
-
if (!/^https?:\/\//i.test(url)) {
|
|
476
|
-
url = 'http://' + url;
|
|
145
|
+
// Se non ci sono piΓΉ porte attive per questo nodo, cancella del tutto
|
|
146
|
+
if (Object.keys(instances[id].state).length === 0) {
|
|
147
|
+
delete instances[id];
|
|
148
|
+
}
|
|
477
149
|
}
|
|
478
|
-
const parsedUrl = new URL(url);
|
|
479
|
-
const protocol = parsedUrl.protocol.replace(':', '');
|
|
480
|
-
const host = parsedUrl.hostname;
|
|
481
|
-
|
|
482
|
-
const port = parsedUrl.port || protocolPorts[protocol] || null;
|
|
483
150
|
|
|
484
|
-
|
|
485
|
-
}
|
|
486
|
-
console.error('Invalid URL:', error);
|
|
487
|
-
return null;
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
class WebsocketTunnelError extends Error {
|
|
492
|
-
constructor(message, code) {
|
|
493
|
-
super(message);
|
|
494
|
-
this.name = this.constructor.name;
|
|
495
|
-
this.code = code;
|
|
496
|
-
Error.captureStackTrace(this, this.constructor);
|
|
497
|
-
}
|
|
151
|
+
done();
|
|
152
|
+
});
|
|
498
153
|
}
|
|
499
154
|
|
|
500
155
|
RED.nodes.registerType('wstunnel server', WSTunnelServer);
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yousolution/node-red-contrib-you-tunnel-websocket",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Module to create websockeet tunnel for NODE-RED",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"update": "npm pack && mv
|
|
7
|
+
"update": "PACKAGE_NAME=$(npm pack) && mv $PACKAGE_NAME ./data && cd data && rm -rf node_modules && npm i $PACKAGE_NAME && docker-compose restart",
|
|
8
8
|
"test": "mocha 'test/**/*.spec.js'",
|
|
9
9
|
"coverage": "nyc npm run test"
|
|
10
10
|
},
|
|
@@ -19,11 +19,19 @@
|
|
|
19
19
|
"node-red": {
|
|
20
20
|
"version": ">=2.0.0",
|
|
21
21
|
"nodes": {
|
|
22
|
-
"wsTunnel": "
|
|
22
|
+
"wsTunnel": "./nodes/wsTunnel.js"
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"
|
|
26
|
+
"@remotelinker/reverse-ws-tunnel": "^1.0.7",
|
|
27
|
+
"cookie": "^1.0.2",
|
|
28
|
+
"cross-conf-env": "^1.3.0",
|
|
29
|
+
"dotenv": "^17.2.1",
|
|
30
|
+
"express": "^4.21.2",
|
|
31
|
+
"http-parser-js": "^0.5.10",
|
|
32
|
+
"http-proxy": "^1.18.1",
|
|
33
|
+
"uuid": "^11.1.0",
|
|
34
|
+
"ws": "^8.18.0"
|
|
27
35
|
},
|
|
28
36
|
"repository": {
|
|
29
37
|
"type": "git",
|