whistle.script 1.2.9 β 1.3.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-en_US.md +249 -0
- package/README.md +224 -239
- package/lib/scripts.js +4 -1
- package/package.json +2 -2
- package/public/index.html +1 -1
- package/public/index.js +2 -2
package/README-en_US.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# π§© whistle.script - Script Extension Plugin
|
|
2
|
+
|
|
3
|
+
[δΈζ](./README.md) Β· English
|
|
4
|
+
|
|
5
|
+
> Environment Requirement: Whistle version must be 2.10.0 or higher.
|
|
6
|
+
|
|
7
|
+
whistle.script is a script extension plugin for [Whistle](https://github.com/avwo/whistle). By writing Node.js scripts in the Web interface, you can inject dynamic logic into Whistle, achieving **programmatic deep control** over network requests, responses, and protocols like WebSocket.
|
|
8
|
+
|
|
9
|
+
## π― Core Features
|
|
10
|
+
|
|
11
|
+
### HTTP/HTTPS Processing
|
|
12
|
+
- **Dynamic Rule Generation** - Generate and inject Whistle matching rules in real-time based on request URL, headers, and other information.
|
|
13
|
+
- **Request/Response Interception & Modification** - Fully intercept HTTP(S) request and response streams, supporting modification of URL, method, headers, status code, and response body.
|
|
14
|
+
- **Debugging & Logging** - Outputs from `console.log` within scripts are displayed in the plugin console in real-time, facilitating debugging.
|
|
15
|
+
|
|
16
|
+
### WebSocket Processing
|
|
17
|
+
- **Bidirectional Communication Interception** - Intercept WebSocket handshakes and data frames between client and server.
|
|
18
|
+
- **Dynamic Message Processing** - View, modify, or forward `ping`, `pong`, `message`, and control frames in real-time.
|
|
19
|
+
- **Direct Data Transmission** - Can actively send data to or disconnect from either end.
|
|
20
|
+
|
|
21
|
+
### Tunnel / Raw Socket Processing
|
|
22
|
+
- **Transparent Pipeline Operation** - Handle raw TCP connections like HTTPS tunnels, enabling low-level data stream forwarding or modification.
|
|
23
|
+
- **High Flexibility** - Provides an API similar to WebSocket for handling non-HTTP protocols.
|
|
24
|
+
|
|
25
|
+
## π¦ Installation Guide
|
|
26
|
+
|
|
27
|
+
### 1. Install the Whistle Runtime
|
|
28
|
+
**Recommended Method (Desktop Users)**: Download and install the visual client for easier management.
|
|
29
|
+
π [Whistle Client Download](https://github.com/avwo/whistle-client)
|
|
30
|
+
|
|
31
|
+
**Command Line Method**:
|
|
32
|
+
1. **Install Node.js (>= 8.8)**
|
|
33
|
+
Download and install the latest LTS version from the [Node.js official website](https://nodejs.org/).
|
|
34
|
+
2. **Install Whistle Globally**
|
|
35
|
+
```bash
|
|
36
|
+
npm install -g whistle
|
|
37
|
+
```
|
|
38
|
+
> **Note**: If you encounter permission issues during installation, you can try using `sudo` (not recommended) or refer to the official documentation to configure the npm global installation path.
|
|
39
|
+
|
|
40
|
+
### 2. Install the whistle.script Plugin
|
|
41
|
+
After Whistle is running, execute the following command:
|
|
42
|
+
```bash
|
|
43
|
+
w2 i whistle.script
|
|
44
|
+
```
|
|
45
|
+
**Or install via the Management Interface**:
|
|
46
|
+
1. Start Whistle and open the management interface (default is `http://127.0.0.1:8899`).
|
|
47
|
+
2. Go to the **Plugins** page.
|
|
48
|
+
3. Click the `Install` button at the top.
|
|
49
|
+
4. Enter `whistle.script` and confirm installation.
|
|
50
|
+
|
|
51
|
+
## π Quick Start
|
|
52
|
+
|
|
53
|
+
### 1. Open the Plugin Interface
|
|
54
|
+
- In the Whistle management interface, navigate via the menu `Plugins -> script`.
|
|
55
|
+
- Or directly visit: [http://local.whistlejs.com/plugin.script/](http://local.whistlejs.com/plugin.script/).
|
|
56
|
+
|
|
57
|
+
### 2. Create and Associate Your First Script
|
|
58
|
+
1. Click **Create** in the plugin interface to create a script named `test`.
|
|
59
|
+
2. In the editor on the right, enter the following sample code:
|
|
60
|
+
```javascript
|
|
61
|
+
exports.handleRequestRules = (ctx) => {
|
|
62
|
+
console.log('Request received:', ctx.fullUrl);
|
|
63
|
+
ctx.rules = ['www.example.com 127.0.0.1:8080']; // Forward request to local port 8080
|
|
64
|
+
};
|
|
65
|
+
```
|
|
66
|
+
3. In Whistle's **Rules** configuration page, add the rule:
|
|
67
|
+
```txt
|
|
68
|
+
www.example.com whistle.script://test
|
|
69
|
+
```
|
|
70
|
+
4. Now, requests to `http://www.example.com` will be processed by the script, and logs can be viewed in the plugin's **Console** tab.
|
|
71
|
+
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
## π Feature Details
|
|
75
|
+
|
|
76
|
+
### 1. Dynamic Rule Setting
|
|
77
|
+
This mode allows the script to dynamically return the Whistle rules (string or array) to be executed based on request information. These rules will be merged and executed with the original rules configured via `whistle.script://`.
|
|
78
|
+
|
|
79
|
+
#### HTTP/HTTPS Rules
|
|
80
|
+
> **Important**: To intercept HTTPS requests, you must first [enable and install Whistle's HTTPS root certificate](https://wproxy.org/docs/gui/https.html).
|
|
81
|
+
|
|
82
|
+
**Script Example (`test`)**:
|
|
83
|
+
```javascript
|
|
84
|
+
exports.handleRequestRules = (ctx) => {
|
|
85
|
+
// Dynamically return a local file based on the request path
|
|
86
|
+
if (ctx.fullUrl.includes('/api/test')) {
|
|
87
|
+
ctx.rules = ['api.example.com/api/test file://{mockData.json}'];
|
|
88
|
+
ctx.values = {
|
|
89
|
+
'mockData.json': JSON.stringify({ code: 200, data: 'mocked' })
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
```
|
|
94
|
+
**Whistle Rule Configuration**:
|
|
95
|
+
```txt
|
|
96
|
+
# Handles requests for multiple domains to the `test` script
|
|
97
|
+
whistle.script://test www.test.com api.example.com
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### Passing Parameters to Scripts
|
|
101
|
+
You can pass parameters to scripts within rules (avoid spaces within parameters).
|
|
102
|
+
```txt
|
|
103
|
+
whistle.script://test(prod,env1) www.example.com
|
|
104
|
+
```
|
|
105
|
+
Access them within the script as follows:
|
|
106
|
+
```javascript
|
|
107
|
+
exports.handleRequestRules = (ctx) => {
|
|
108
|
+
console.log(process.args); // Output: ["prod", "env1"]
|
|
109
|
+
console.log(ctx.scriptValue); // Output (v1.3.0+): "prod,env1"
|
|
110
|
+
// Execute different logic based on parameters
|
|
111
|
+
ctx.rules = 'www.test.com 127.0.0.1:8080';
|
|
112
|
+
};
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### WebSocket Rule Setting
|
|
116
|
+
```javascript
|
|
117
|
+
exports.handleWebSocketRules = (ctx) => {
|
|
118
|
+
// Dynamically decide which WebSocket connections should be processed by this plugin
|
|
119
|
+
this.rules = 'echo.websocket.org statusCode://101';
|
|
120
|
+
};
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 2. Direct Manipulation of Requests and Responses
|
|
124
|
+
This mode grants the script full control over network traffic, allowing it to manually initiate requests, read, and modify data.
|
|
125
|
+
|
|
126
|
+
#### HTTP/HTTPS Request Handling
|
|
127
|
+
Trigger this mode using the `script://` protocol.
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
exports.handleRequest = (ctx, request) => {
|
|
131
|
+
const { req, res } = ctx;
|
|
132
|
+
|
|
133
|
+
req.passThrough({
|
|
134
|
+
// Optional
|
|
135
|
+
transformReq: function(req, next) {
|
|
136
|
+
// getBuffer, getText, getJson can all be used to get the request body, with the same parameter and callback usage
|
|
137
|
+
req.getJson(function(err, data) {
|
|
138
|
+
if (err) {
|
|
139
|
+
return next();
|
|
140
|
+
}
|
|
141
|
+
// data.a.b.c = 'test';
|
|
142
|
+
next(JSON.stringify(data));
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
// Optional
|
|
146
|
+
transformRes: function(svrRes, next) {
|
|
147
|
+
// getBuffer, getText, getJson can all be used to get the request body, with the same parameter and callback usage
|
|
148
|
+
svrRes.getText(function(err, text) {
|
|
149
|
+
if (err) {
|
|
150
|
+
return next();
|
|
151
|
+
}
|
|
152
|
+
next('[' + text + ', 123' + ']');
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
```
|
|
158
|
+
**Association Rule**:
|
|
159
|
+
```txt
|
|
160
|
+
# Note: Use script:// here to trigger the handleRequest method
|
|
161
|
+
www.example.com/api script://test
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### WebSocket Connection Handling
|
|
165
|
+
```javascript
|
|
166
|
+
exports.handleWebSocket = async (socket, connect) => {
|
|
167
|
+
console.log('WebSocket connection established');
|
|
168
|
+
|
|
169
|
+
// Connect to the original backend server
|
|
170
|
+
const serverSocket = await connect();
|
|
171
|
+
|
|
172
|
+
// Listen for client messages, forward to server
|
|
173
|
+
socket.on('message', (data, opts) => {
|
|
174
|
+
console.log('<< From client:', data);
|
|
175
|
+
// Data can be modified here
|
|
176
|
+
serverSocket.send(`[Relay] ${data}`, opts);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Listen for server messages, forward to client
|
|
180
|
+
serverSocket.on('message', (data, opts) => {
|
|
181
|
+
console.log('>> From server:', data);
|
|
182
|
+
socket.send(data, opts);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Handle connection closure
|
|
186
|
+
socket.on('disconnect', (code, reason) => {
|
|
187
|
+
console.log(`Client disconnected [${code}]: ${reason}`);
|
|
188
|
+
serverSocket.disconnect(code, reason);
|
|
189
|
+
});
|
|
190
|
+
};
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
#### Tunnel (Raw TCP) Handling
|
|
194
|
+
|
|
195
|
+
Used to handle tunnels established by the `CONNECT` method (e.g., HTTPS).
|
|
196
|
+
```javascript
|
|
197
|
+
exports.handleTunnel = async (clientSocket, connect) => {
|
|
198
|
+
const targetSocket = await connect();
|
|
199
|
+
// Establish a bidirectional transparent pipeline
|
|
200
|
+
clientSocket.pipe(targetSocket).pipe(clientSocket);
|
|
201
|
+
|
|
202
|
+
// Can listen to the data event for lower-level binary data operations
|
|
203
|
+
};
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### 3. Advanced Features
|
|
207
|
+
|
|
208
|
+
#### Request Authentication (`auth`)
|
|
209
|
+
Perform identity verification before a request enters other processing stages.
|
|
210
|
+
```javascript
|
|
211
|
+
exports.auth = async (req, options) => {
|
|
212
|
+
const token = req.headers['x-auth-token'];
|
|
213
|
+
// 1. Add internal passthrough headers (starting with x-whistle-)
|
|
214
|
+
req.setHeader('x-whistle-req-id', Date.now());
|
|
215
|
+
|
|
216
|
+
// 2. Perform asynchronous verification
|
|
217
|
+
// const isValid = await verifyToken(token);
|
|
218
|
+
// return isValid; // Returning false will directly respond with 403 Forbidden
|
|
219
|
+
|
|
220
|
+
// 3. Allow to pass by default
|
|
221
|
+
return true;
|
|
222
|
+
};
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### Pipeline Hooks
|
|
226
|
+
Perform lightweight interception at different lifecycle stages of request/response.
|
|
227
|
+
```javascript
|
|
228
|
+
// Process before the request body is read by the Whistle rule engine
|
|
229
|
+
exports.handleReqRead = (req, res, options) => {
|
|
230
|
+
// Can be used to log the original request body or perform early modifications
|
|
231
|
+
req.pipe(res); // Usually direct pipe transmission
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
// Process after the request body is processed by the Whistle rule engine, before being sent to the target server
|
|
235
|
+
exports.handleReqWrite = (req, res, options) => {
|
|
236
|
+
// Can be used for final modifications based on rule results
|
|
237
|
+
req.pipe(res);
|
|
238
|
+
};
|
|
239
|
+
// Similar hooks: handleResRead, handleResWrite, handleWsReqRead, etc.
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## π More Resources
|
|
243
|
+
- [Whistle Core Documentation](https://wproxy.org/)
|
|
244
|
+
- [Whistle Rule Configuration Syntax](https://wproxy.org/docs/rules/rule.html)
|
|
245
|
+
- [Whistle GitHub Repository](https://github.com/avwo/whistle)
|
|
246
|
+
- [Plugin Development Type Definition Reference](https://github.com/avwo/lack/blob/master/assets/ts/src/types/global.d.ts)
|
|
247
|
+
|
|
248
|
+
## π License
|
|
249
|
+
This project is open source under the [MIT License](./LICENSE).
|