Answer
Whistle supports processing requests and responses through scripts, which provides developers with great flexibility to implement complex network request processing logic.
Script Processing Basics
1. Script File Format
Whistle scripts use CommonJS module format:
javascriptmodule.exports = function(req, res) { // Processing logic };
2. Script Types
- reqScript: Process requests
- resScript: Process responses
- plugin: Plugin scripts
reqScript Usage
1. Basic Usage
Create script file: request-handler.js
javascriptmodule.exports = function(req, res) { console.log('Request URL:', req.url); console.log('Request Method:', req.method); console.log('Request Headers:', req.headers); };
Configure rule:
shellwww.example.com reqScript://{request-handler.js}
2. Modify Request Headers
javascriptmodule.exports = function(req, res) { // Add custom request header req.headers['X-Custom-Header'] = 'Custom Value'; // Modify existing request header req.headers['User-Agent'] = 'Custom User Agent'; // Delete request header delete req.headers['Authorization']; };
3. Modify Request Body
javascriptmodule.exports = function(req, res) { let body = ''; req.on('data', function(chunk) { body += chunk.toString(); }); req.on('end', function() { // Modify request body const modifiedBody = body.replace(/old/g, 'new'); // Resend modified request // Note: This requires special handling }); };
4. Request Redirection
javascriptmodule.exports = function(req, res) { // Modify request URL if (req.url.includes('/old-path')) { req.url = req.url.replace('/old-path', '/new-path'); } };
5. Conditional Processing
javascriptmodule.exports = function(req, res) { // Process based on request method if (req.method === 'POST') { req.headers['X-Request-Type'] = 'POST'; } // Process based on request path if (req.url.includes('/api/')) { req.headers['X-API-Request'] = 'true'; } // Process based on request headers if (req.headers['authorization']) { req.headers['X-Authenticated'] = 'true'; } };
resScript Usage
1. Basic Usage
Create script file: response-handler.js
javascriptmodule.exports = function(req, res) { console.log('Response Status:', res.statusCode); console.log('Response Headers:', res.headers); };
Configure rule:
shellwww.example.com resScript://{response-handler.js}
2. Modify Response Headers
javascriptmodule.exports = function(req, res) { // Add custom response header res.setHeader('X-Custom-Header', 'Custom Value'); // Modify existing response header res.setHeader('Content-Type', 'application/json'); // Delete response header res.removeHeader('X-Powered-By'); };
3. Modify Response Body
javascriptmodule.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { let body = chunk.toString(); // Modify response content body = body.replace(/old/g, 'new'); body = body.replace(/error/g, 'success'); // End response with modified content originalEnd.call(res, body, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };
4. Response Data Transformation
javascriptmodule.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { const body = chunk.toString(); // JSON data transformation if (res.headers['content-type'] && res.headers['content-type'].includes('application/json')) { const jsonData = JSON.parse(body); jsonData.timestamp = Date.now(); jsonData.modified = true; originalEnd.call(res, JSON.stringify(jsonData), encoding); } else { originalEnd.call(res, chunk, encoding); } } else { originalEnd.call(res, chunk, encoding); } }; };
5. Response Caching
javascriptconst cache = {}; module.exports = function(req, res) { const cacheKey = req.url; // Check cache if (cache[cacheKey]) { console.log('Using cached response for:', cacheKey); res.end(cache[cacheKey]); return; } // Cache response const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { cache[cacheKey] = chunk.toString(); originalEnd.call(res, chunk, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };
Advanced Script Processing
1. Combine reqScript and resScript
request-handler.js:
javascriptmodule.exports = function(req, res) { // Request start time req.startTime = Date.now(); req.headers['X-Request-Start'] = req.startTime; };
response-handler.js:
javascriptmodule.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { // Calculate request duration const duration = Date.now() - (req.startTime || 0); res.setHeader('X-Request-Duration', duration); originalEnd.call(res, chunk, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };
Configure rule:
shellwww.example.com reqScript://{request-handler.js} resScript://{response-handler.js}
2. Using External Modules
javascriptconst fs = require('fs'); const path = require('path'); module.exports = function(req, res) { // Read external file const filePath = path.join(__dirname, 'data.json'); const data = JSON.parse(fs.readFileSync(filePath, 'utf8')); // Use data to modify response const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { const body = chunk.toString(); const modifiedBody = body.replace('{{data}}', JSON.stringify(data)); originalEnd.call(res, modifiedBody, encoding); } else { originalEnd.call(res, chunk, encoding); } }; };
3. Async Processing
javascriptconst https = require('https'); module.exports = function(req, res) { const originalEnd = res.end; res.end = function(chunk, encoding) { if (chunk) { // Async processing https.get('https://api.example.com/data', (response) => { let data = ''; response.on('data', (chunk) => { data += chunk; }); response.on('end', () => { // Use external data to modify response const body = chunk.toString(); const modifiedBody = body.replace('{{external-data}}', data); originalEnd.call(res, modifiedBody, encoding); }); }).on('error', (err) => { console.error('Error fetching external data:', err); originalEnd.call(res, chunk, encoding); }); } else { originalEnd.call(res, chunk, encoding); } }; };
Script Debugging
1. Using console.log
javascriptmodule.exports = function(req, res) { console.log('Request URL:', req.url); console.log('Request Method:', req.method); console.log('Request Headers:', JSON.stringify(req.headers, null, 2)); };
2. Error Handling
javascriptmodule.exports = function(req, res) { try { // Processing logic const result = processData(req); res.end(JSON.stringify(result)); } catch (error) { console.error('Script error:', error); res.statusCode = 500; res.end(JSON.stringify({ error: error.message })); } };
Best Practices
1. Script Organization
- Organize scripts by functional modules
- Use meaningful filenames
- Add comments to explain script purpose
2. Performance Optimization
- Avoid complex calculations in scripts
- Use caching to reduce repeated operations
- Use async processing appropriately
3. Error Handling
- Add appropriate error handling
- Log errors
- Provide friendly error messages
4. Security Considerations
- Validate input data
- Prevent injection attacks
- Don't expose sensitive information