@aikidosec/broker-client 1.0.7 → 1.0.9
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 +1 -1
- package/app/client.js +57 -45
- package/app/streaming/handlers.js +1 -1
- package/app/streaming/initStreamingResponse.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ docker compose up -d
|
|
|
21
21
|
## Quick Start
|
|
22
22
|
|
|
23
23
|
1. **Generate CLIENT_SECRET in Aikido UI**:
|
|
24
|
-
- Navigate to: Settings → Broker Clients → Add New Client
|
|
24
|
+
- Navigate to: Settings → [Broker Clients](https://app.aikido.dev/settings/integrations/broker/clients) → Add New Client
|
|
25
25
|
- Copy the generated `CLIENT_SECRET`
|
|
26
26
|
|
|
27
27
|
2. **Configure environment** (`.env`):
|
package/app/client.js
CHANGED
|
@@ -13,7 +13,6 @@ import dns from 'native-dns';
|
|
|
13
13
|
import { ResourceManager } from './resourceManager.js';
|
|
14
14
|
import { HttpsProxyAgent } from 'https-proxy-agent';
|
|
15
15
|
import { getClientId, setClientIdCache, getServerUrl, getClientSecret } from './config.js';
|
|
16
|
-
import { STREAMING_THRESHOLD } from './streaming/state.js';
|
|
17
16
|
import { registerStreamingHandlers } from './streaming/handlers.js';
|
|
18
17
|
import { initStreamingResponse } from './streaming/initStreamingResponse.js';
|
|
19
18
|
import { startStaleStreamCleanup, cleanupAllStreams } from './streaming/cleanup.js';
|
|
@@ -33,7 +32,7 @@ const DNS_SERVERS = process.env.DNS_SERVERS
|
|
|
33
32
|
: null;
|
|
34
33
|
|
|
35
34
|
// Configure axios defaults
|
|
36
|
-
const MAX_RESPONSE_SIZE =
|
|
35
|
+
const MAX_RESPONSE_SIZE = 50 * 1024 * 1024; // 50 MB (falls back to streaming if exceeded)
|
|
37
36
|
const axiosConfig = {
|
|
38
37
|
timeout: 300000,
|
|
39
38
|
maxRedirects: 5,
|
|
@@ -105,28 +104,44 @@ async function resolveInternalHostname(hostname) {
|
|
|
105
104
|
}
|
|
106
105
|
|
|
107
106
|
/**
|
|
108
|
-
*
|
|
109
|
-
* @param {
|
|
110
|
-
* @param {
|
|
111
|
-
* @
|
|
107
|
+
* Make an internal HTTP request with automatic fallback to streaming if buffered response is too large.
|
|
108
|
+
* @param {object} options - Request options
|
|
109
|
+
* @param {string} options.method - HTTP method
|
|
110
|
+
* @param {string} options.url - Target URL
|
|
111
|
+
* @param {object} options.headers - Request headers
|
|
112
|
+
* @param {*} options.body - Request body
|
|
113
|
+
* @param {string} options.requestId - Request ID for logging
|
|
114
|
+
* @returns {Promise<{response: object, streaming: boolean}>} Response and whether streaming was used
|
|
112
115
|
*/
|
|
113
|
-
async function
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
async function forwardRequestToInternalResource ({ method, url, headers, body, requestId }) {
|
|
117
|
+
const makeRequest = async (streaming) => {
|
|
118
|
+
let requestData = {
|
|
119
|
+
method,
|
|
120
|
+
url,
|
|
116
121
|
headers,
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
122
|
+
data: body,
|
|
123
|
+
validateStatus: () => true, // Accept all status codes
|
|
124
|
+
responseType: streaming ? 'stream' : 'arraybuffer'
|
|
125
|
+
}
|
|
126
|
+
if (streaming) {
|
|
127
|
+
requestData.maxContentLength = Infinity;
|
|
128
|
+
}
|
|
129
|
+
// if streaming, this returns a stream instantly, it does not wait for the full response
|
|
130
|
+
return await internalHttpClient.request(requestData);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Try buffered request first, fallback to streaming if response too large
|
|
134
|
+
try {
|
|
135
|
+
return { response: await makeRequest(false), useStreaming: false };
|
|
136
|
+
} catch (reqError) {
|
|
137
|
+
// Fallback to streaming if buffered request exceeded maxContentLength (axios ERR_BAD_RESPONSE)
|
|
138
|
+
if (axios.isAxiosError(reqError) && // axios does not have a specific error for maxcontent length, so we have to check message (this is about the MAX_RESPONSE_SIZE we set above)
|
|
139
|
+
reqError.code === 'ERR_BAD_RESPONSE' &&
|
|
140
|
+
reqError.message?.includes('maxContentLength')) {
|
|
141
|
+
log.warn(`Buffered request exceeded maxContentLength for ${requestId}, falling back to streaming`);
|
|
142
|
+
return { response: await makeRequest(true), useStreaming: true };
|
|
143
|
+
}
|
|
144
|
+
throw reqError;
|
|
130
145
|
}
|
|
131
146
|
}
|
|
132
147
|
|
|
@@ -238,9 +253,20 @@ const socket = io(SERVER_URL, {
|
|
|
238
253
|
pingTimeout: 60000, // 60s (default 20s) - time to wait for pong before considering connection dead
|
|
239
254
|
});
|
|
240
255
|
|
|
241
|
-
// Socket.IO event handlers
|
|
256
|
+
// Socket.IO event handlers - listen for engine open to catch transport before upgrade
|
|
257
|
+
socket.io.on('open', () => {
|
|
258
|
+
const transport = socket.io?.engine?.transport?.name;
|
|
259
|
+
log.info(`🔌 Engine opened (initial transport: ${transport})`);
|
|
260
|
+
|
|
261
|
+
// Log transport upgrades (e.g., polling -> websocket)
|
|
262
|
+
socket.io.engine.on('upgrade', (newTransport) => {
|
|
263
|
+
log.info(`🔄 Transport upgraded to: ${newTransport?.name}`);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
242
267
|
socket.on('connect', async () => {
|
|
243
|
-
|
|
268
|
+
const transport = socket.io?.engine?.transport?.name;
|
|
269
|
+
log.info(`✓ Connected to broker server (transport: ${transport})`);
|
|
244
270
|
|
|
245
271
|
const clientId = getClientId();
|
|
246
272
|
|
|
@@ -342,26 +368,13 @@ socket.on('forward_request', async (data, callback) => {
|
|
|
342
368
|
}
|
|
343
369
|
}
|
|
344
370
|
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
let contentLength = null;
|
|
348
|
-
|
|
349
|
-
if (method === 'GET') {
|
|
350
|
-
contentLength = await getContentLengthViaHead(resolvedUrl, headers);
|
|
351
|
-
if (contentLength !== null && contentLength > STREAMING_THRESHOLD) {
|
|
352
|
-
useStreaming = true;
|
|
353
|
-
log.info(`Large response detected (${(contentLength / (1024 * 1024)).toFixed(2)} MB) - using streaming`);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Make the request with appropriate response type
|
|
358
|
-
const response = await internalHttpClient.request({
|
|
371
|
+
// Make the request (with automatic fallback to streaming if buffered response is too large)
|
|
372
|
+
const { response, useStreaming } = await forwardRequestToInternalResource ({
|
|
359
373
|
method,
|
|
360
374
|
url: resolvedUrl,
|
|
361
375
|
headers,
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
responseType: useStreaming ? 'stream' : 'arraybuffer',
|
|
376
|
+
body,
|
|
377
|
+
requestId
|
|
365
378
|
});
|
|
366
379
|
|
|
367
380
|
if (useStreaming) {
|
|
@@ -409,17 +422,16 @@ socket.on('forward_request', async (data, callback) => {
|
|
|
409
422
|
* @param {string} requestId - Request ID for tracking
|
|
410
423
|
* @param {number} statusCode - HTTP status code
|
|
411
424
|
* @param {object} headers - Response headers
|
|
412
|
-
* @param {Buffer|null} responseData - Raw response data (
|
|
425
|
+
* @param {Buffer|null} responseData - Raw response data (sent as binary)
|
|
413
426
|
* @param {function} callback - Socket.IO callback
|
|
414
427
|
*/
|
|
415
428
|
function sendDirectResponse(requestId, statusCode, headers, responseData, callback) {
|
|
416
|
-
const responseBody = responseData ? responseData.toString('base64') : null;
|
|
417
429
|
callback({
|
|
418
430
|
request_id: requestId,
|
|
419
431
|
status_code: statusCode,
|
|
420
432
|
headers: headers,
|
|
421
|
-
body:
|
|
422
|
-
version:
|
|
433
|
+
body: responseData || null, // Send raw Buffer - Socket.IO handles binary natively
|
|
434
|
+
version: 3
|
|
423
435
|
});
|
|
424
436
|
}
|
|
425
437
|
|
|
@@ -64,7 +64,7 @@ export async function handleGetNextChunk(data, callback) {
|
|
|
64
64
|
|
|
65
65
|
callback({
|
|
66
66
|
request_id: requestId,
|
|
67
|
-
data: chunkData.
|
|
67
|
+
data: chunkData, // Send raw Buffer - Socket.IO handles binary natively
|
|
68
68
|
complete: isComplete,
|
|
69
69
|
chunk_index: state.chunkIndex
|
|
70
70
|
});
|
package/package.json
CHANGED