agentic-flow 1.6.4 → 1.6.5
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/CHANGELOG.md +20 -0
- package/dist/cli-proxy.js +37 -77
- package/dist/mcp/fastmcp/tools/swarm/init.js +5 -18
- package/dist/reasoningbank/agentdb-adapter.js +125 -0
- package/dist/reasoningbank/index.js +4 -0
- package/dist/transport/quic.js +58 -581
- package/dist/utils/agentdbCommands.js +175 -0
- package/dist/utils/cli.js +17 -27
- package/docs/AGENTDB_INTEGRATION.md +379 -0
- package/docs/plans/QUIC/quic-tutorial.md +66 -38
- package/docs/quic/FINAL-VALIDATION.md +0 -0
- package/docs/quic/IMPLEMENTATION-COMPLETE-SUMMARY.md +0 -0
- package/docs/quic/PERFORMANCE-VALIDATION.md +0 -0
- package/docs/quic/QUIC-STATUS-OLD.md +0 -0
- package/docs/quic/QUIC-STATUS.md +3 -3
- package/docs/quic/QUIC-VALIDATION-REPORT.md +0 -0
- package/docs/quic/WASM-INTEGRATION-COMPLETE.md +0 -0
- package/package.json +3 -1
- package/wasm/reasoningbank/reasoningbank_wasm_bg.js +2 -2
- package/wasm/reasoningbank/reasoningbank_wasm_bg.wasm +0 -0
package/dist/transport/quic.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// QUIC Transport Layer for Agentic Flow
|
|
2
2
|
// WebAssembly-based QUIC client/server with connection pooling and stream multiplexing
|
|
3
3
|
import { logger } from '../utils/logger.js';
|
|
4
|
-
import { QuicHandshakeManager } from './quic-handshake.js';
|
|
5
4
|
/**
|
|
6
5
|
* QUIC Client - Manages outbound QUIC connections and stream multiplexing
|
|
7
6
|
*/
|
|
@@ -10,8 +9,6 @@ export class QuicClient {
|
|
|
10
9
|
connections;
|
|
11
10
|
wasmModule; // WASM module reference
|
|
12
11
|
initialized;
|
|
13
|
-
udpSocket; // UDP socket for QUIC transport
|
|
14
|
-
handshakeManager; // QUIC handshake protocol
|
|
15
12
|
constructor(config = {}) {
|
|
16
13
|
this.config = {
|
|
17
14
|
host: config.host || '0.0.0.0',
|
|
@@ -32,8 +29,6 @@ export class QuicClient {
|
|
|
32
29
|
};
|
|
33
30
|
this.connections = new Map();
|
|
34
31
|
this.initialized = false;
|
|
35
|
-
this.udpSocket = null;
|
|
36
|
-
this.handshakeManager = new QuicHandshakeManager();
|
|
37
32
|
}
|
|
38
33
|
/**
|
|
39
34
|
* Initialize QUIC client with WASM module
|
|
@@ -61,98 +56,7 @@ export class QuicClient {
|
|
|
61
56
|
}
|
|
62
57
|
}
|
|
63
58
|
/**
|
|
64
|
-
*
|
|
65
|
-
*/
|
|
66
|
-
async createUdpSocket() {
|
|
67
|
-
const dgram = await import('dgram');
|
|
68
|
-
this.udpSocket = dgram.createSocket('udp4');
|
|
69
|
-
return new Promise((resolve, reject) => {
|
|
70
|
-
this.udpSocket.on('error', (err) => {
|
|
71
|
-
logger.error('UDP socket error', { error: err });
|
|
72
|
-
reject(err);
|
|
73
|
-
});
|
|
74
|
-
this.udpSocket.on('message', (msg, rinfo) => {
|
|
75
|
-
this.handleIncomingPacket(msg, rinfo);
|
|
76
|
-
});
|
|
77
|
-
this.udpSocket.on('listening', () => {
|
|
78
|
-
const address = this.udpSocket.address();
|
|
79
|
-
logger.info('UDP socket listening', {
|
|
80
|
-
address: address.address,
|
|
81
|
-
port: address.port
|
|
82
|
-
});
|
|
83
|
-
resolve();
|
|
84
|
-
});
|
|
85
|
-
this.udpSocket.bind();
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Send UDP packet to remote host
|
|
90
|
-
*/
|
|
91
|
-
async sendUdpPacket(packet, host, port) {
|
|
92
|
-
if (!this.udpSocket) {
|
|
93
|
-
throw new Error('UDP socket not created');
|
|
94
|
-
}
|
|
95
|
-
return new Promise((resolve, reject) => {
|
|
96
|
-
this.udpSocket.send(packet, port, host, (err) => {
|
|
97
|
-
if (err) {
|
|
98
|
-
logger.error('Failed to send UDP packet', { error: err, host, port });
|
|
99
|
-
reject(err);
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
logger.debug('UDP packet sent', {
|
|
103
|
-
bytes: packet.length,
|
|
104
|
-
host,
|
|
105
|
-
port
|
|
106
|
-
});
|
|
107
|
-
resolve();
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Handle incoming QUIC packet from UDP
|
|
114
|
-
* Uses packet bridge to convert UDP packets to WASM messages
|
|
115
|
-
*/
|
|
116
|
-
async handleIncomingPacket(packet, rinfo) {
|
|
117
|
-
try {
|
|
118
|
-
logger.debug('Received UDP packet', {
|
|
119
|
-
bytes: packet.length,
|
|
120
|
-
from: `${rinfo.address}:${rinfo.port}`
|
|
121
|
-
});
|
|
122
|
-
if (this.wasmModule?.client && this.wasmModule?.createMessage) {
|
|
123
|
-
// Convert raw UDP packet to QUIC message for WASM processing
|
|
124
|
-
const addr = `${rinfo.address}:${rinfo.port}`;
|
|
125
|
-
const message = this.wasmModule.createMessage(`packet-${Date.now()}`, 'data', packet, {
|
|
126
|
-
source: addr,
|
|
127
|
-
timestamp: Date.now(),
|
|
128
|
-
bytes: packet.length
|
|
129
|
-
});
|
|
130
|
-
try {
|
|
131
|
-
// Send to WASM for processing
|
|
132
|
-
await this.wasmModule.client.sendMessage(addr, message);
|
|
133
|
-
// Receive response (if any)
|
|
134
|
-
const response = await this.wasmModule.client.recvMessage(addr);
|
|
135
|
-
if (response && response.payload) {
|
|
136
|
-
// Send response packet back to sender
|
|
137
|
-
const responsePacket = new Uint8Array(response.payload);
|
|
138
|
-
await this.sendUdpPacket(responsePacket, rinfo.address, rinfo.port);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
catch (wasmError) {
|
|
142
|
-
// WASM processing error (expected for incomplete QUIC handshakes)
|
|
143
|
-
logger.debug('WASM packet processing skipped', {
|
|
144
|
-
reason: 'Requires full QUIC handshake',
|
|
145
|
-
error: wasmError instanceof Error ? wasmError.message : String(wasmError)
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
catch (error) {
|
|
151
|
-
logger.error('Error handling incoming packet', { error });
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Connect to QUIC server with 0-RTT support
|
|
59
|
+
* Connect to QUIC server
|
|
156
60
|
*/
|
|
157
61
|
async connect(host, port) {
|
|
158
62
|
if (!this.initialized) {
|
|
@@ -161,11 +65,11 @@ export class QuicClient {
|
|
|
161
65
|
const targetHost = host || this.config.serverHost;
|
|
162
66
|
const targetPort = port || this.config.serverPort;
|
|
163
67
|
const connectionId = `${targetHost}:${targetPort}`;
|
|
164
|
-
// Check if connection already exists
|
|
68
|
+
// Check if connection already exists
|
|
165
69
|
if (this.connections.has(connectionId)) {
|
|
166
70
|
const conn = this.connections.get(connectionId);
|
|
167
71
|
conn.lastActivity = new Date();
|
|
168
|
-
logger.debug('Reusing existing QUIC connection
|
|
72
|
+
logger.debug('Reusing existing QUIC connection', { connectionId });
|
|
169
73
|
return conn;
|
|
170
74
|
}
|
|
171
75
|
// Check connection pool limit
|
|
@@ -173,14 +77,9 @@ export class QuicClient {
|
|
|
173
77
|
throw new Error(`Maximum connections (${this.config.maxConnections}) reached`);
|
|
174
78
|
}
|
|
175
79
|
try {
|
|
176
|
-
|
|
177
|
-
if (!this.udpSocket) {
|
|
178
|
-
logger.info('Creating UDP socket for QUIC transport...');
|
|
179
|
-
await this.createUdpSocket();
|
|
180
|
-
}
|
|
181
|
-
logger.info('Establishing QUIC connection with 0-RTT', { host: targetHost, port: targetPort });
|
|
80
|
+
logger.info('Establishing QUIC connection', { host: targetHost, port: targetPort });
|
|
182
81
|
// Establish QUIC connection via WASM
|
|
183
|
-
//
|
|
82
|
+
// This is a placeholder - actual implementation will use WASM bindings
|
|
184
83
|
const connection = {
|
|
185
84
|
id: connectionId,
|
|
186
85
|
remoteAddr: `${targetHost}:${targetPort}`,
|
|
@@ -189,19 +88,7 @@ export class QuicClient {
|
|
|
189
88
|
lastActivity: new Date()
|
|
190
89
|
};
|
|
191
90
|
this.connections.set(connectionId, connection);
|
|
192
|
-
|
|
193
|
-
if (this.wasmModule?.client && this.wasmModule?.createMessage) {
|
|
194
|
-
await this.handshakeManager.initiateHandshake(connectionId, `${targetHost}:${targetPort}`, this.wasmModule.client, this.wasmModule.createMessage);
|
|
195
|
-
}
|
|
196
|
-
// Connection is established immediately with 0-RTT if enabled
|
|
197
|
-
const rttMode = this.config.enableEarlyData ? '0-RTT' : '1-RTT';
|
|
198
|
-
const handshakeState = this.handshakeManager.getHandshakeState(connectionId);
|
|
199
|
-
logger.info(`QUIC connection established (${rttMode})`, {
|
|
200
|
-
connectionId,
|
|
201
|
-
mode: rttMode,
|
|
202
|
-
handshakeState,
|
|
203
|
-
maxStreams: this.config.maxConcurrentStreams
|
|
204
|
-
});
|
|
91
|
+
logger.info('QUIC connection established', { connectionId });
|
|
205
92
|
return connection;
|
|
206
93
|
}
|
|
207
94
|
catch (error) {
|
|
@@ -210,7 +97,7 @@ export class QuicClient {
|
|
|
210
97
|
}
|
|
211
98
|
}
|
|
212
99
|
/**
|
|
213
|
-
* Create bidirectional stream on connection
|
|
100
|
+
* Create bidirectional stream on connection
|
|
214
101
|
*/
|
|
215
102
|
async createStream(connectionId) {
|
|
216
103
|
const connection = this.connections.get(connectionId);
|
|
@@ -222,41 +109,24 @@ export class QuicClient {
|
|
|
222
109
|
}
|
|
223
110
|
const streamId = connection.streamCount++;
|
|
224
111
|
connection.lastActivity = new Date();
|
|
225
|
-
logger.debug('Creating QUIC
|
|
226
|
-
|
|
227
|
-
streamId,
|
|
228
|
-
totalStreams: connection.streamCount,
|
|
229
|
-
maxStreams: this.config.maxConcurrentStreams
|
|
230
|
-
});
|
|
231
|
-
// Create stream via WASM - uses bidirectional stream multiplexing
|
|
232
|
-
const wasmClient = this.wasmModule?.client;
|
|
233
|
-
if (!wasmClient) {
|
|
234
|
-
throw new Error('WASM client not initialized');
|
|
235
|
-
}
|
|
112
|
+
logger.debug('Creating QUIC stream', { connectionId, streamId });
|
|
113
|
+
// Create stream via WASM
|
|
236
114
|
const stream = {
|
|
237
115
|
id: streamId,
|
|
238
116
|
connectionId,
|
|
239
117
|
send: async (data) => {
|
|
240
|
-
logger.debug('Sending data on
|
|
241
|
-
//
|
|
242
|
-
const message = this.wasmModule.createMessage(`stream-${streamId}`, 'data', data, { streamId, connectionId, timestamp: Date.now() });
|
|
243
|
-
// Send via WASM client (multiplexed stream)
|
|
244
|
-
await wasmClient.sendMessage(connectionId, message);
|
|
118
|
+
logger.debug('Sending data on stream', { connectionId, streamId, bytes: data.length });
|
|
119
|
+
// WASM call to send data
|
|
245
120
|
connection.lastActivity = new Date();
|
|
246
121
|
},
|
|
247
122
|
receive: async () => {
|
|
248
|
-
logger.debug('Receiving data on
|
|
249
|
-
//
|
|
250
|
-
const response = await wasmClient.recvMessage(connectionId);
|
|
123
|
+
logger.debug('Receiving data on stream', { connectionId, streamId });
|
|
124
|
+
// WASM call to receive data
|
|
251
125
|
connection.lastActivity = new Date();
|
|
252
|
-
|
|
253
|
-
if (response && response.payload) {
|
|
254
|
-
return new Uint8Array(response.payload);
|
|
255
|
-
}
|
|
256
|
-
return new Uint8Array();
|
|
126
|
+
return new Uint8Array(); // Placeholder
|
|
257
127
|
},
|
|
258
128
|
close: async () => {
|
|
259
|
-
logger.debug('Closing
|
|
129
|
+
logger.debug('Closing stream', { connectionId, streamId });
|
|
260
130
|
connection.streamCount--;
|
|
261
131
|
connection.lastActivity = new Date();
|
|
262
132
|
}
|
|
@@ -264,30 +134,17 @@ export class QuicClient {
|
|
|
264
134
|
return stream;
|
|
265
135
|
}
|
|
266
136
|
/**
|
|
267
|
-
* Send HTTP/3 request over QUIC
|
|
137
|
+
* Send HTTP/3 request over QUIC
|
|
268
138
|
*/
|
|
269
139
|
async sendRequest(connectionId, method, path, headers, body) {
|
|
270
140
|
const stream = await this.createStream(connectionId);
|
|
271
141
|
try {
|
|
272
|
-
|
|
273
|
-
connectionId,
|
|
274
|
-
method,
|
|
275
|
-
path,
|
|
276
|
-
streamId: stream.id,
|
|
277
|
-
bodySize: body?.length || 0
|
|
278
|
-
});
|
|
279
|
-
// Encode HTTP/3 request with QPACK compression
|
|
142
|
+
// Encode HTTP/3 request
|
|
280
143
|
const request = this.encodeHttp3Request(method, path, headers, body);
|
|
281
144
|
await stream.send(request);
|
|
282
|
-
// Receive HTTP/3 response
|
|
145
|
+
// Receive HTTP/3 response
|
|
283
146
|
const responseData = await stream.receive();
|
|
284
147
|
const response = this.decodeHttp3Response(responseData);
|
|
285
|
-
logger.debug('Received HTTP/3 response', {
|
|
286
|
-
connectionId,
|
|
287
|
-
status: response.status,
|
|
288
|
-
streamId: stream.id,
|
|
289
|
-
bodySize: response.body.length
|
|
290
|
-
});
|
|
291
148
|
return response;
|
|
292
149
|
}
|
|
293
150
|
finally {
|
|
@@ -315,238 +172,51 @@ export class QuicClient {
|
|
|
315
172
|
for (const connectionId of this.connections.keys()) {
|
|
316
173
|
await this.closeConnection(connectionId);
|
|
317
174
|
}
|
|
318
|
-
// Close UDP socket
|
|
319
|
-
if (this.udpSocket) {
|
|
320
|
-
this.udpSocket.close();
|
|
321
|
-
this.udpSocket = null;
|
|
322
|
-
logger.info('UDP socket closed');
|
|
323
|
-
}
|
|
324
175
|
this.initialized = false;
|
|
325
176
|
}
|
|
326
177
|
/**
|
|
327
|
-
* Get connection statistics
|
|
178
|
+
* Get connection statistics
|
|
328
179
|
*/
|
|
329
|
-
|
|
330
|
-
const wasmClient = this.wasmModule?.client;
|
|
331
|
-
// Get stats from WASM if available
|
|
332
|
-
let wasmStats = null;
|
|
333
|
-
if (wasmClient) {
|
|
334
|
-
try {
|
|
335
|
-
wasmStats = await wasmClient.poolStats();
|
|
336
|
-
}
|
|
337
|
-
catch (error) {
|
|
338
|
-
logger.warn('Failed to get WASM stats', { error });
|
|
339
|
-
}
|
|
340
|
-
}
|
|
180
|
+
getStats() {
|
|
341
181
|
return {
|
|
342
182
|
totalConnections: this.connections.size,
|
|
343
183
|
activeConnections: this.connections.size,
|
|
344
184
|
totalStreams: Array.from(this.connections.values()).reduce((sum, c) => sum + c.streamCount, 0),
|
|
345
185
|
activeStreams: Array.from(this.connections.values()).reduce((sum, c) => sum + c.streamCount, 0),
|
|
346
|
-
bytesReceived:
|
|
347
|
-
bytesSent:
|
|
348
|
-
packetsLost:
|
|
349
|
-
rttMs:
|
|
186
|
+
bytesReceived: 0, // From WASM
|
|
187
|
+
bytesSent: 0, // From WASM
|
|
188
|
+
packetsLost: 0, // From WASM
|
|
189
|
+
rttMs: 0 // From WASM
|
|
350
190
|
};
|
|
351
191
|
}
|
|
352
192
|
/**
|
|
353
|
-
* Load WASM module
|
|
193
|
+
* Load WASM module (placeholder)
|
|
354
194
|
*/
|
|
355
195
|
async loadWasmModule() {
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
const { fileURLToPath } = await import('url');
|
|
361
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
362
|
-
// Load WASM module from wasm/quic directory
|
|
363
|
-
const wasmModulePath = path.join(__dirname, '../../wasm/quic/agentic_flow_quic.js');
|
|
364
|
-
const { WasmQuicClient, defaultConfig, createQuicMessage } = await import(wasmModulePath);
|
|
365
|
-
// Create WASM client instance with config
|
|
366
|
-
const config = defaultConfig();
|
|
367
|
-
config.server_addr = `${this.config.serverHost}:${this.config.serverPort}`;
|
|
368
|
-
config.verify_peer = this.config.verifyPeer;
|
|
369
|
-
config.max_connections = this.config.maxConnections;
|
|
370
|
-
config.connection_timeout = this.config.connectionTimeout;
|
|
371
|
-
config.idle_timeout = this.config.idleTimeout;
|
|
372
|
-
config.max_concurrent_streams = this.config.maxConcurrentStreams;
|
|
373
|
-
config.initial_congestion_window = this.config.initialCongestionWindow;
|
|
374
|
-
config.max_datagram_size = this.config.maxDatagramSize;
|
|
375
|
-
config.enable_early_data = this.config.enableEarlyData;
|
|
376
|
-
const wasmClient = new WasmQuicClient(config);
|
|
377
|
-
logger.info('QUIC WASM module loaded successfully', {
|
|
378
|
-
serverAddr: config.server_addr,
|
|
379
|
-
maxStreams: config.max_concurrent_streams,
|
|
380
|
-
enableEarlyData: config.enable_early_data
|
|
381
|
-
});
|
|
382
|
-
return {
|
|
383
|
-
client: wasmClient,
|
|
384
|
-
createMessage: createQuicMessage,
|
|
385
|
-
config
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
catch (error) {
|
|
389
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
390
|
-
logger.error('Failed to load QUIC WASM module', { error });
|
|
391
|
-
throw new Error(`WASM module loading failed: ${errorMessage}`);
|
|
392
|
-
}
|
|
196
|
+
// This will be implemented to load the actual WASM module
|
|
197
|
+
// For now, return a mock object
|
|
198
|
+
logger.debug('Loading QUIC WASM module...');
|
|
199
|
+
return {};
|
|
393
200
|
}
|
|
394
201
|
/**
|
|
395
|
-
* Encode HTTP/3 request
|
|
396
|
-
* QPACK is HTTP/3's header compression format (successor to HTTP/2's HPACK)
|
|
202
|
+
* Encode HTTP/3 request (placeholder)
|
|
397
203
|
*/
|
|
398
204
|
encodeHttp3Request(method, path, headers, body) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
// - Frame length (variable)
|
|
403
|
-
// - Frame payload (HEADERS + DATA frames)
|
|
404
|
-
const encoder = new TextEncoder();
|
|
405
|
-
// Encode pseudo-headers (HTTP/3 required fields with ':' prefix)
|
|
406
|
-
const pseudoHeaders = [
|
|
407
|
-
`:method ${method}`,
|
|
408
|
-
`:path ${path}`,
|
|
409
|
-
`:scheme https`,
|
|
410
|
-
`:authority ${this.config.serverHost}`
|
|
411
|
-
];
|
|
412
|
-
// Encode regular headers
|
|
413
|
-
const regularHeaders = Object.entries(headers).map(([key, value]) => `${key}: ${value}`);
|
|
414
|
-
// Combine all headers
|
|
415
|
-
const allHeaders = [...pseudoHeaders, ...regularHeaders].join('\r\n');
|
|
416
|
-
const headersBytes = encoder.encode(allHeaders + '\r\n\r\n');
|
|
417
|
-
// Create HEADERS frame (type 0x01)
|
|
418
|
-
const headersFrame = new Uint8Array([
|
|
419
|
-
0x01, // HEADERS frame type
|
|
420
|
-
...this.encodeVarint(headersBytes.length), // Frame length
|
|
421
|
-
...headersBytes // Frame payload
|
|
422
|
-
]);
|
|
423
|
-
// Add DATA frame if body exists (type 0x00)
|
|
424
|
-
if (body && body.length > 0) {
|
|
425
|
-
const dataFrame = new Uint8Array([
|
|
426
|
-
0x00, // DATA frame type
|
|
427
|
-
...this.encodeVarint(body.length), // Frame length
|
|
428
|
-
...body // Frame payload
|
|
429
|
-
]);
|
|
430
|
-
// Combine HEADERS and DATA frames
|
|
431
|
-
const combined = new Uint8Array(headersFrame.length + dataFrame.length);
|
|
432
|
-
combined.set(headersFrame, 0);
|
|
433
|
-
combined.set(dataFrame, headersFrame.length);
|
|
434
|
-
return combined;
|
|
435
|
-
}
|
|
436
|
-
return headersFrame;
|
|
205
|
+
// HTTP/3 QPACK encoding will be implemented
|
|
206
|
+
logger.debug('Encoding HTTP/3 request', { method, path, headers });
|
|
207
|
+
return new Uint8Array();
|
|
437
208
|
}
|
|
438
209
|
/**
|
|
439
|
-
* Decode HTTP/3 response
|
|
210
|
+
* Decode HTTP/3 response (placeholder)
|
|
440
211
|
*/
|
|
441
212
|
decodeHttp3Response(data) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const headers = {};
|
|
450
|
-
let body = new Uint8Array();
|
|
451
|
-
// Parse HTTP/3 frames
|
|
452
|
-
while (offset < data.length) {
|
|
453
|
-
// Read frame type
|
|
454
|
-
const frameType = data[offset++];
|
|
455
|
-
// Read frame length (varint)
|
|
456
|
-
const { value: frameLength, bytesRead } = this.decodeVarint(data, offset);
|
|
457
|
-
offset += bytesRead;
|
|
458
|
-
// Extract frame payload
|
|
459
|
-
const frameData = data.slice(offset, offset + frameLength);
|
|
460
|
-
offset += frameLength;
|
|
461
|
-
if (frameType === 0x01) {
|
|
462
|
-
// HEADERS frame
|
|
463
|
-
const headersText = decoder.decode(frameData);
|
|
464
|
-
const lines = headersText.split('\r\n');
|
|
465
|
-
for (const line of lines) {
|
|
466
|
-
if (line.startsWith(':status ')) {
|
|
467
|
-
status = parseInt(line.substring(8));
|
|
468
|
-
}
|
|
469
|
-
else if (line.includes(': ')) {
|
|
470
|
-
const [key, ...valueParts] = line.split(': ');
|
|
471
|
-
headers[key.toLowerCase()] = valueParts.join(': ');
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
else if (frameType === 0x00) {
|
|
476
|
-
// DATA frame
|
|
477
|
-
body = frameData;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
return { status, headers, body };
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* Encode variable-length integer (QUIC varint format)
|
|
484
|
-
*/
|
|
485
|
-
encodeVarint(value) {
|
|
486
|
-
if (value < 64) {
|
|
487
|
-
return new Uint8Array([value]);
|
|
488
|
-
}
|
|
489
|
-
else if (value < 16384) {
|
|
490
|
-
return new Uint8Array([0x40 | (value >> 8), value & 0xff]);
|
|
491
|
-
}
|
|
492
|
-
else if (value < 1073741824) {
|
|
493
|
-
return new Uint8Array([
|
|
494
|
-
0x80 | (value >> 24),
|
|
495
|
-
(value >> 16) & 0xff,
|
|
496
|
-
(value >> 8) & 0xff,
|
|
497
|
-
value & 0xff
|
|
498
|
-
]);
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
return new Uint8Array([
|
|
502
|
-
0xc0 | (value >> 56),
|
|
503
|
-
(value >> 48) & 0xff,
|
|
504
|
-
(value >> 40) & 0xff,
|
|
505
|
-
(value >> 32) & 0xff,
|
|
506
|
-
(value >> 24) & 0xff,
|
|
507
|
-
(value >> 16) & 0xff,
|
|
508
|
-
(value >> 8) & 0xff,
|
|
509
|
-
value & 0xff
|
|
510
|
-
]);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
/**
|
|
514
|
-
* Decode variable-length integer (QUIC varint format)
|
|
515
|
-
*/
|
|
516
|
-
decodeVarint(data, offset) {
|
|
517
|
-
const firstByte = data[offset];
|
|
518
|
-
const prefix = firstByte >> 6;
|
|
519
|
-
if (prefix === 0) {
|
|
520
|
-
return { value: firstByte, bytesRead: 1 };
|
|
521
|
-
}
|
|
522
|
-
else if (prefix === 1) {
|
|
523
|
-
return {
|
|
524
|
-
value: ((firstByte & 0x3f) << 8) | data[offset + 1],
|
|
525
|
-
bytesRead: 2
|
|
526
|
-
};
|
|
527
|
-
}
|
|
528
|
-
else if (prefix === 2) {
|
|
529
|
-
return {
|
|
530
|
-
value: ((firstByte & 0x3f) << 24) |
|
|
531
|
-
(data[offset + 1] << 16) |
|
|
532
|
-
(data[offset + 2] << 8) |
|
|
533
|
-
data[offset + 3],
|
|
534
|
-
bytesRead: 4
|
|
535
|
-
};
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
return {
|
|
539
|
-
value: ((firstByte & 0x3f) << 56) |
|
|
540
|
-
(data[offset + 1] << 48) |
|
|
541
|
-
(data[offset + 2] << 40) |
|
|
542
|
-
(data[offset + 3] << 32) |
|
|
543
|
-
(data[offset + 4] << 24) |
|
|
544
|
-
(data[offset + 5] << 16) |
|
|
545
|
-
(data[offset + 6] << 8) |
|
|
546
|
-
data[offset + 7],
|
|
547
|
-
bytesRead: 8
|
|
548
|
-
};
|
|
549
|
-
}
|
|
213
|
+
// HTTP/3 QPACK decoding will be implemented
|
|
214
|
+
logger.debug('Decoding HTTP/3 response', { bytes: data.length });
|
|
215
|
+
return {
|
|
216
|
+
status: 200,
|
|
217
|
+
headers: {},
|
|
218
|
+
body: new Uint8Array()
|
|
219
|
+
};
|
|
550
220
|
}
|
|
551
221
|
}
|
|
552
222
|
/**
|
|
@@ -558,7 +228,6 @@ export class QuicServer {
|
|
|
558
228
|
wasmModule;
|
|
559
229
|
initialized;
|
|
560
230
|
listening;
|
|
561
|
-
udpSocket; // UDP socket for QUIC transport
|
|
562
231
|
constructor(config = {}) {
|
|
563
232
|
this.config = {
|
|
564
233
|
host: config.host || '0.0.0.0',
|
|
@@ -580,7 +249,6 @@ export class QuicServer {
|
|
|
580
249
|
this.connections = new Map();
|
|
581
250
|
this.initialized = false;
|
|
582
251
|
this.listening = false;
|
|
583
|
-
this.udpSocket = null;
|
|
584
252
|
}
|
|
585
253
|
/**
|
|
586
254
|
* Initialize QUIC server
|
|
@@ -607,107 +275,7 @@ export class QuicServer {
|
|
|
607
275
|
}
|
|
608
276
|
}
|
|
609
277
|
/**
|
|
610
|
-
*
|
|
611
|
-
*/
|
|
612
|
-
async sendUdpPacket(packet, host, port) {
|
|
613
|
-
if (!this.udpSocket) {
|
|
614
|
-
throw new Error('UDP socket not created');
|
|
615
|
-
}
|
|
616
|
-
return new Promise((resolve, reject) => {
|
|
617
|
-
this.udpSocket.send(packet, port, host, (err) => {
|
|
618
|
-
if (err) {
|
|
619
|
-
logger.error('Failed to send UDP packet', { error: err, host, port });
|
|
620
|
-
reject(err);
|
|
621
|
-
}
|
|
622
|
-
else {
|
|
623
|
-
logger.debug('UDP packet sent', {
|
|
624
|
-
bytes: packet.length,
|
|
625
|
-
host,
|
|
626
|
-
port
|
|
627
|
-
});
|
|
628
|
-
resolve();
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
/**
|
|
634
|
-
* Handle incoming QUIC connection from UDP
|
|
635
|
-
* Uses packet bridge to convert UDP packets to WASM messages
|
|
636
|
-
*/
|
|
637
|
-
async handleIncomingConnection(packet, rinfo) {
|
|
638
|
-
try {
|
|
639
|
-
logger.debug('Received QUIC packet', {
|
|
640
|
-
bytes: packet.length,
|
|
641
|
-
from: `${rinfo.address}:${rinfo.port}`
|
|
642
|
-
});
|
|
643
|
-
const connectionId = `${rinfo.address}:${rinfo.port}`;
|
|
644
|
-
let connection = this.connections.get(connectionId);
|
|
645
|
-
if (!connection) {
|
|
646
|
-
if (this.connections.size >= this.config.maxConnections) {
|
|
647
|
-
logger.warn('Max connections reached, rejecting new connection', {
|
|
648
|
-
connectionId
|
|
649
|
-
});
|
|
650
|
-
return;
|
|
651
|
-
}
|
|
652
|
-
connection = {
|
|
653
|
-
id: connectionId,
|
|
654
|
-
remoteAddr: `${rinfo.address}:${rinfo.port}`,
|
|
655
|
-
streamCount: 0,
|
|
656
|
-
createdAt: new Date(),
|
|
657
|
-
lastActivity: new Date()
|
|
658
|
-
};
|
|
659
|
-
this.connections.set(connectionId, connection);
|
|
660
|
-
logger.info('New QUIC connection established', { connectionId });
|
|
661
|
-
}
|
|
662
|
-
connection.lastActivity = new Date();
|
|
663
|
-
if (this.wasmModule?.client && this.wasmModule?.createMessage) {
|
|
664
|
-
// Convert raw UDP packet to QUIC message for WASM processing
|
|
665
|
-
const message = this.wasmModule.createMessage(`conn-${Date.now()}`, 'data', packet, {
|
|
666
|
-
connectionId,
|
|
667
|
-
source: `${rinfo.address}:${rinfo.port}`,
|
|
668
|
-
timestamp: Date.now()
|
|
669
|
-
});
|
|
670
|
-
try {
|
|
671
|
-
// Send to WASM for processing
|
|
672
|
-
await this.wasmModule.client.sendMessage(connectionId, message);
|
|
673
|
-
// Receive response (if any)
|
|
674
|
-
const response = await this.wasmModule.client.recvMessage(connectionId);
|
|
675
|
-
if (response && response.payload) {
|
|
676
|
-
// Send response packet back to sender
|
|
677
|
-
const responsePacket = new Uint8Array(response.payload);
|
|
678
|
-
await this.sendUdpPacket(responsePacket, rinfo.address, rinfo.port);
|
|
679
|
-
}
|
|
680
|
-
if (response && response.metadata?.streamData) {
|
|
681
|
-
this.handleStreamData(connectionId, response.metadata.streamData);
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
catch (wasmError) {
|
|
685
|
-
// WASM processing error (expected for incomplete QUIC handshakes)
|
|
686
|
-
logger.debug('WASM packet processing skipped', {
|
|
687
|
-
connectionId,
|
|
688
|
-
reason: 'Requires full QUIC handshake',
|
|
689
|
-
error: wasmError instanceof Error ? wasmError.message : String(wasmError)
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
catch (error) {
|
|
695
|
-
logger.error('Error handling incoming connection', { error });
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
/**
|
|
699
|
-
* Handle stream data from QUIC connection
|
|
700
|
-
*/
|
|
701
|
-
handleStreamData(connectionId, streamData) {
|
|
702
|
-
logger.debug('Received stream data', {
|
|
703
|
-
connectionId,
|
|
704
|
-
bytes: streamData.length
|
|
705
|
-
});
|
|
706
|
-
// Process stream data (application layer)
|
|
707
|
-
}
|
|
708
|
-
/**
|
|
709
|
-
* Start listening for QUIC connections over UDP
|
|
710
|
-
* Supports connection migration and stream multiplexing
|
|
278
|
+
* Start listening for connections
|
|
711
279
|
*/
|
|
712
280
|
async listen() {
|
|
713
281
|
if (!this.initialized) {
|
|
@@ -718,55 +286,17 @@ export class QuicServer {
|
|
|
718
286
|
return;
|
|
719
287
|
}
|
|
720
288
|
try {
|
|
721
|
-
logger.info('Starting QUIC server
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
});
|
|
727
|
-
// Create and bind UDP socket
|
|
728
|
-
const dgram = await import('dgram');
|
|
729
|
-
this.udpSocket = dgram.createSocket('udp4');
|
|
730
|
-
return new Promise((resolve, reject) => {
|
|
731
|
-
this.udpSocket.on('error', (err) => {
|
|
732
|
-
logger.error('UDP socket error', { error: err });
|
|
733
|
-
this.listening = false;
|
|
734
|
-
reject(err);
|
|
735
|
-
});
|
|
736
|
-
this.udpSocket.on('message', async (msg, rinfo) => {
|
|
737
|
-
await this.handleIncomingConnection(msg, rinfo);
|
|
738
|
-
});
|
|
739
|
-
this.udpSocket.on('listening', () => {
|
|
740
|
-
const address = this.udpSocket.address();
|
|
741
|
-
this.listening = true;
|
|
742
|
-
logger.info(`QUIC server listening on UDP ${address.address}:${address.port}`, {
|
|
743
|
-
features: [
|
|
744
|
-
'UDP transport',
|
|
745
|
-
'Stream multiplexing',
|
|
746
|
-
'Connection migration',
|
|
747
|
-
'0-RTT support',
|
|
748
|
-
`Max ${this.config.maxConcurrentStreams} concurrent streams`
|
|
749
|
-
]
|
|
750
|
-
});
|
|
751
|
-
resolve();
|
|
752
|
-
});
|
|
753
|
-
this.udpSocket.bind(this.config.port, this.config.host);
|
|
754
|
-
});
|
|
289
|
+
logger.info('Starting QUIC server', { host: this.config.host, port: this.config.port });
|
|
290
|
+
// Start QUIC server via WASM
|
|
291
|
+
// This will be implemented with actual WASM bindings
|
|
292
|
+
this.listening = true;
|
|
293
|
+
logger.info(`QUIC server listening on ${this.config.host}:${this.config.port}`);
|
|
755
294
|
}
|
|
756
295
|
catch (error) {
|
|
757
296
|
logger.error('Failed to start QUIC server', { error });
|
|
758
297
|
throw error;
|
|
759
298
|
}
|
|
760
299
|
}
|
|
761
|
-
/**
|
|
762
|
-
* Setup connection handler for incoming QUIC connections
|
|
763
|
-
*/
|
|
764
|
-
setupConnectionHandler() {
|
|
765
|
-
logger.debug('Setting up QUIC connection handler');
|
|
766
|
-
// Connection handler processes incoming connections
|
|
767
|
-
// Each connection can have multiple bidirectional streams
|
|
768
|
-
// QUIC handles connection migration automatically (e.g., WiFi -> Cellular)
|
|
769
|
-
}
|
|
770
300
|
/**
|
|
771
301
|
* Stop server and close all connections
|
|
772
302
|
*/
|
|
@@ -780,12 +310,6 @@ export class QuicServer {
|
|
|
780
310
|
for (const connectionId of this.connections.keys()) {
|
|
781
311
|
await this.closeConnection(connectionId);
|
|
782
312
|
}
|
|
783
|
-
// Close UDP socket
|
|
784
|
-
if (this.udpSocket) {
|
|
785
|
-
this.udpSocket.close();
|
|
786
|
-
this.udpSocket = null;
|
|
787
|
-
logger.info('UDP socket closed');
|
|
788
|
-
}
|
|
789
313
|
// Stop listening via WASM
|
|
790
314
|
this.listening = false;
|
|
791
315
|
logger.info('QUIC server stopped');
|
|
@@ -803,73 +327,26 @@ export class QuicServer {
|
|
|
803
327
|
this.connections.delete(connectionId);
|
|
804
328
|
}
|
|
805
329
|
/**
|
|
806
|
-
* Get server statistics
|
|
330
|
+
* Get server statistics
|
|
807
331
|
*/
|
|
808
|
-
|
|
809
|
-
const wasmClient = this.wasmModule?.client;
|
|
810
|
-
// Get stats from WASM if available
|
|
811
|
-
let wasmStats = null;
|
|
812
|
-
if (wasmClient) {
|
|
813
|
-
try {
|
|
814
|
-
wasmStats = await wasmClient.poolStats();
|
|
815
|
-
}
|
|
816
|
-
catch (error) {
|
|
817
|
-
logger.warn('Failed to get WASM stats', { error });
|
|
818
|
-
}
|
|
819
|
-
}
|
|
332
|
+
getStats() {
|
|
820
333
|
return {
|
|
821
334
|
totalConnections: this.connections.size,
|
|
822
335
|
activeConnections: this.connections.size,
|
|
823
336
|
totalStreams: Array.from(this.connections.values()).reduce((sum, c) => sum + c.streamCount, 0),
|
|
824
337
|
activeStreams: Array.from(this.connections.values()).reduce((sum, c) => sum + c.streamCount, 0),
|
|
825
|
-
bytesReceived:
|
|
826
|
-
bytesSent:
|
|
827
|
-
packetsLost:
|
|
828
|
-
rttMs:
|
|
338
|
+
bytesReceived: 0,
|
|
339
|
+
bytesSent: 0,
|
|
340
|
+
packetsLost: 0,
|
|
341
|
+
rttMs: 0
|
|
829
342
|
};
|
|
830
343
|
}
|
|
831
344
|
/**
|
|
832
|
-
* Load WASM module
|
|
345
|
+
* Load WASM module (placeholder)
|
|
833
346
|
*/
|
|
834
347
|
async loadWasmModule() {
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
// Import WASM bindings (same as client)
|
|
838
|
-
const path = await import('path');
|
|
839
|
-
const { fileURLToPath } = await import('url');
|
|
840
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
841
|
-
// Load WASM module from wasm/quic directory
|
|
842
|
-
const wasmModulePath = path.join(__dirname, '../../wasm/quic/agentic_flow_quic.js');
|
|
843
|
-
const { WasmQuicClient, defaultConfig, createQuicMessage } = await import(wasmModulePath);
|
|
844
|
-
// Create server config
|
|
845
|
-
const config = defaultConfig();
|
|
846
|
-
config.server_addr = `${this.config.host}:${this.config.port}`;
|
|
847
|
-
config.verify_peer = this.config.verifyPeer;
|
|
848
|
-
config.max_connections = this.config.maxConnections;
|
|
849
|
-
config.connection_timeout = this.config.connectionTimeout;
|
|
850
|
-
config.idle_timeout = this.config.idleTimeout;
|
|
851
|
-
config.max_concurrent_streams = this.config.maxConcurrentStreams;
|
|
852
|
-
config.initial_congestion_window = this.config.initialCongestionWindow;
|
|
853
|
-
config.max_datagram_size = this.config.maxDatagramSize;
|
|
854
|
-
config.enable_early_data = this.config.enableEarlyData;
|
|
855
|
-
// Note: Server uses the same WASM client for bidirectional communication
|
|
856
|
-
const wasmClient = new WasmQuicClient(config);
|
|
857
|
-
logger.info('QUIC server WASM module loaded successfully', {
|
|
858
|
-
serverAddr: config.server_addr,
|
|
859
|
-
maxConnections: config.max_connections,
|
|
860
|
-
maxStreams: config.max_concurrent_streams
|
|
861
|
-
});
|
|
862
|
-
return {
|
|
863
|
-
client: wasmClient,
|
|
864
|
-
createMessage: createQuicMessage,
|
|
865
|
-
config
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
catch (error) {
|
|
869
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
870
|
-
logger.error('Failed to load QUIC server WASM module', { error });
|
|
871
|
-
throw new Error(`WASM module loading failed: ${errorMessage}`);
|
|
872
|
-
}
|
|
348
|
+
logger.debug('Loading QUIC server WASM module...');
|
|
349
|
+
return {};
|
|
873
350
|
}
|
|
874
351
|
}
|
|
875
352
|
/**
|
|
@@ -970,7 +447,7 @@ export class QuicTransport {
|
|
|
970
447
|
/**
|
|
971
448
|
* Get connection statistics
|
|
972
449
|
*/
|
|
973
|
-
|
|
974
|
-
return
|
|
450
|
+
getStats() {
|
|
451
|
+
return this.client.getStats();
|
|
975
452
|
}
|
|
976
453
|
}
|