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.
@@ -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
+ ![Plugin Interface Operation Demo](https://user-images.githubusercontent.com/11450939/126302159-0c533ea7-3bc0-484a-bd30-698d5a7881df.gif)
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).