@aetherframework/websocket 1.0.0 → 1.0.1
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/LICENSE +21 -0
- package/package.json +1 -2
- package/src/core/ConnectionManager.js +0 -213
- package/src/core/FrameParser.js +0 -115
- package/src/core/HandshakeHandler.js +0 -93
- package/src/core/ProtocolHandler.js +0 -186
- package/src/core/WebSocketFactory.js +0 -293
- package/src/drivers/http-driver.js +0 -576
- package/src/drivers/index.js +0 -29
- package/src/drivers/memory-driver.js +0 -422
- package/src/drivers/tcp-driver.js +0 -471
- package/src/drivers/tls-driver.js +0 -502
- package/src/middleware/auth-middleware.js +0 -37
- package/src/middleware/broadcast-manager.js +0 -173
- package/src/middleware/compression.js +0 -194
- package/src/middleware/message-logger.js +0 -322
- package/src/middleware/rate-limiter.js +0 -142
- package/src/utils/config-loader.js +0 -183
- package/src/utils/connection-pool.js +0 -110
- package/src/utils/error-handler.js +0 -59
- package/src/utils/frame-encoder.js +0 -211
- package/src/utils/heartbeat-manager.js +0 -91
|
@@ -1,422 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license MIT
|
|
3
|
-
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
-
* SPDX-License-Identifier: MIT
|
|
5
|
-
* @module @aetherframework/src/drivers/memory-driver
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import EventEmitter from 'events';
|
|
9
|
-
import crypto from 'crypto';
|
|
10
|
-
|
|
11
|
-
class MemoryDriver extends EventEmitter {
|
|
12
|
-
constructor(config = {}) {
|
|
13
|
-
super();
|
|
14
|
-
this.config = {
|
|
15
|
-
maxPayload: config.maxPayload || 1024 * 1024,
|
|
16
|
-
latency: config.latency || 0, // Simulated network latency in ms
|
|
17
|
-
dropRate: config.dropRate || 0, // Simulated packet drop rate (0-1)
|
|
18
|
-
...config
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
this.connections = new Map();
|
|
22
|
-
this.messageQueue = new Map();
|
|
23
|
-
this.isServer = false;
|
|
24
|
-
this.serverInstance = null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Create an in-memory WebSocket server
|
|
29
|
-
* @param {Object} options - Server options
|
|
30
|
-
* @returns {Promise<Object>} Server instance
|
|
31
|
-
*/
|
|
32
|
-
async createServer(options = {}) {
|
|
33
|
-
this.isServer = true;
|
|
34
|
-
this.serverInstance = {
|
|
35
|
-
type: 'memory',
|
|
36
|
-
address: { address: 'memory', port: 0, family: 'memory' },
|
|
37
|
-
connections: new Map(),
|
|
38
|
-
broadcast: (message) => this._broadcast(message),
|
|
39
|
-
close: () => this.closeServer(),
|
|
40
|
-
getStats: () => this.getStats()
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
// Simulate server listening
|
|
44
|
-
setTimeout(() => {
|
|
45
|
-
this.emit('server:listening', {
|
|
46
|
-
host: options.host || 'memory',
|
|
47
|
-
port: options.port || 0,
|
|
48
|
-
family: 'memory'
|
|
49
|
-
});
|
|
50
|
-
}, 0);
|
|
51
|
-
|
|
52
|
-
return this.serverInstance;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Create an in-memory WebSocket client
|
|
57
|
-
* @param {string} url - WebSocket URL (ignored for memory driver)
|
|
58
|
-
* @param {Object} options - Client options
|
|
59
|
-
* @returns {Promise<Object>} Client connection
|
|
60
|
-
*/
|
|
61
|
-
async createClient(url, options = {}) {
|
|
62
|
-
const connectionId = crypto.randomUUID();
|
|
63
|
-
|
|
64
|
-
// Create mock connection
|
|
65
|
-
const connection = {
|
|
66
|
-
id: connectionId,
|
|
67
|
-
readyState: 1, // OPEN
|
|
68
|
-
protocol: options.protocol || null,
|
|
69
|
-
remoteAddress: 'memory',
|
|
70
|
-
remotePort: 0,
|
|
71
|
-
url: url,
|
|
72
|
-
options: options,
|
|
73
|
-
send: (data) => this._sendToServer(connectionId, data),
|
|
74
|
-
sendBinary: (data) => this._sendBinaryToServer(connectionId, data),
|
|
75
|
-
sendJSON: (obj) => this._sendToServer(connectionId, JSON.stringify(obj)),
|
|
76
|
-
close: (code, reason) => this._closeConnection(connectionId, code, reason),
|
|
77
|
-
terminate: () => this._terminateConnection(connectionId),
|
|
78
|
-
ping: () => this._sendPing(connectionId),
|
|
79
|
-
getStats: () => this._getConnectionStats(connectionId)
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
this.connections.set(connectionId, connection);
|
|
83
|
-
|
|
84
|
-
// Simulate connection handshake
|
|
85
|
-
setTimeout(() => {
|
|
86
|
-
if (Math.random() > this.config.dropRate) {
|
|
87
|
-
this.emit('connection', connection);
|
|
88
|
-
|
|
89
|
-
// Simulate server accepting connection
|
|
90
|
-
if (this.isServer) {
|
|
91
|
-
this.serverInstance.connections.set(connectionId, connection);
|
|
92
|
-
|
|
93
|
-
// Send welcome message
|
|
94
|
-
setTimeout(() => {
|
|
95
|
-
this._deliverMessage(connectionId, {
|
|
96
|
-
type: 'welcome',
|
|
97
|
-
message: 'Connected to memory WebSocket server',
|
|
98
|
-
connectionId: connectionId,
|
|
99
|
-
timestamp: Date.now()
|
|
100
|
-
});
|
|
101
|
-
}, this.config.latency);
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
// Simulate connection failure
|
|
105
|
-
this.emit('error', {
|
|
106
|
-
type: 'connection_failed',
|
|
107
|
-
message: 'Simulated connection failure',
|
|
108
|
-
connectionId
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
}, this.config.latency);
|
|
112
|
-
|
|
113
|
-
return connection;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Send message to server (from client)
|
|
118
|
-
* @param {string} connectionId - Connection ID
|
|
119
|
-
* @param {string|Buffer} data - Data to send
|
|
120
|
-
* @private
|
|
121
|
-
*/
|
|
122
|
-
_sendToServer(connectionId, data) {
|
|
123
|
-
const connection = this.connections.get(connectionId);
|
|
124
|
-
if (!connection || connection.readyState !== 1) {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Simulate network latency
|
|
129
|
-
setTimeout(() => {
|
|
130
|
-
if (Math.random() > this.config.dropRate) {
|
|
131
|
-
// Deliver to server
|
|
132
|
-
this.emit('message', connection, data, false);
|
|
133
|
-
|
|
134
|
-
// Update connection stats
|
|
135
|
-
connection.stats = connection.stats || {
|
|
136
|
-
messagesSent: 0,
|
|
137
|
-
messagesReceived: 0,
|
|
138
|
-
bytesSent: 0,
|
|
139
|
-
bytesReceived: 0
|
|
140
|
-
};
|
|
141
|
-
connection.stats.messagesSent++;
|
|
142
|
-
connection.stats.bytesSent += Buffer.byteLength(data);
|
|
143
|
-
} else {
|
|
144
|
-
// Simulate packet loss
|
|
145
|
-
this.emit('error', {
|
|
146
|
-
type: 'packet_loss',
|
|
147
|
-
message: 'Simulated packet loss',
|
|
148
|
-
connectionId
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}, this.config.latency);
|
|
152
|
-
|
|
153
|
-
return true;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Send binary data to server
|
|
158
|
-
* @param {string} connectionId - Connection ID
|
|
159
|
-
* @param {Buffer} data - Binary data
|
|
160
|
-
* @private
|
|
161
|
-
*/
|
|
162
|
-
_sendBinaryToServer(connectionId, data) {
|
|
163
|
-
const connection = this.connections.get(connectionId);
|
|
164
|
-
if (!connection || connection.readyState !== 1) {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
setTimeout(() => {
|
|
169
|
-
if (Math.random() > this.config.dropRate) {
|
|
170
|
-
this.emit('message', connection, data, true);
|
|
171
|
-
|
|
172
|
-
connection.stats = connection.stats || {
|
|
173
|
-
messagesSent: 0,
|
|
174
|
-
messagesReceived: 0,
|
|
175
|
-
bytesSent: 0,
|
|
176
|
-
bytesReceived: 0
|
|
177
|
-
};
|
|
178
|
-
connection.stats.messagesSent++;
|
|
179
|
-
connection.stats.bytesSent += data.length;
|
|
180
|
-
}
|
|
181
|
-
}, this.config.latency);
|
|
182
|
-
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Send ping frame
|
|
188
|
-
* @param {string} connectionId - Connection ID
|
|
189
|
-
* @private
|
|
190
|
-
*/
|
|
191
|
-
_sendPing(connectionId) {
|
|
192
|
-
const connection = this.connections.get(connectionId);
|
|
193
|
-
if (!connection || connection.readyState !== 1) {
|
|
194
|
-
return false;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
setTimeout(() => {
|
|
198
|
-
if (Math.random() > this.config.dropRate) {
|
|
199
|
-
// Simulate pong response
|
|
200
|
-
setTimeout(() => {
|
|
201
|
-
this.emit('pong', connection);
|
|
202
|
-
}, this.config.latency / 2);
|
|
203
|
-
}
|
|
204
|
-
}, this.config.latency);
|
|
205
|
-
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Deliver message to connection (from server to client)
|
|
211
|
-
* @param {string} connectionId - Connection ID
|
|
212
|
-
* @param {any} message - Message to deliver
|
|
213
|
-
* @private
|
|
214
|
-
*/
|
|
215
|
-
_deliverMessage(connectionId, message) {
|
|
216
|
-
const connection = this.connections.get(connectionId);
|
|
217
|
-
if (!connection || connection.readyState !== 1) {
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Queue message for delivery
|
|
222
|
-
if (!this.messageQueue.has(connectionId)) {
|
|
223
|
-
this.messageQueue.set(connectionId, []);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
this.messageQueue.get(connectionId).push({
|
|
227
|
-
message,
|
|
228
|
-
timestamp: Date.now()
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
// Process queue with simulated latency
|
|
232
|
-
setTimeout(() => {
|
|
233
|
-
const queue = this.messageQueue.get(connectionId);
|
|
234
|
-
if (queue && queue.length > 0) {
|
|
235
|
-
const item = queue.shift();
|
|
236
|
-
if (Math.random() > this.config.dropRate) {
|
|
237
|
-
// Emit message event on connection
|
|
238
|
-
connection.emit('message', item.message);
|
|
239
|
-
|
|
240
|
-
// Update stats
|
|
241
|
-
connection.stats = connection.stats || {
|
|
242
|
-
messagesSent: 0,
|
|
243
|
-
messagesReceived: 0,
|
|
244
|
-
bytesSent: 0,
|
|
245
|
-
bytesReceived: 0
|
|
246
|
-
};
|
|
247
|
-
connection.stats.messagesReceived++;
|
|
248
|
-
connection.stats.bytesReceived += Buffer.byteLength(
|
|
249
|
-
typeof item.message === 'string' ? item.message : JSON.stringify(item.message)
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}, this.config.latency);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Close connection
|
|
258
|
-
* @param {string} connectionId - Connection ID
|
|
259
|
-
* @param {number} code - Close code
|
|
260
|
-
* @param {string} reason - Close reason
|
|
261
|
-
* @private
|
|
262
|
-
*/
|
|
263
|
-
_closeConnection(connectionId, code = 1000, reason = '') {
|
|
264
|
-
const connection = this.connections.get(connectionId);
|
|
265
|
-
if (!connection) return;
|
|
266
|
-
|
|
267
|
-
connection.readyState = 3; // CLOSED
|
|
268
|
-
|
|
269
|
-
setTimeout(() => {
|
|
270
|
-
this.emit('close', connection, code, reason);
|
|
271
|
-
|
|
272
|
-
if (this.isServer) {
|
|
273
|
-
this.serverInstance.connections.delete(connectionId);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
this.connections.delete(connectionId);
|
|
277
|
-
this.messageQueue.delete(connectionId);
|
|
278
|
-
}, this.config.latency);
|
|
279
|
-
|
|
280
|
-
return true;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/**
|
|
284
|
-
* Terminate connection (force close)
|
|
285
|
-
* @param {string} connectionId - Connection ID
|
|
286
|
-
* @private
|
|
287
|
-
*/
|
|
288
|
-
_terminateConnection(connectionId) {
|
|
289
|
-
const connection = this.connections.get(connectionId);
|
|
290
|
-
if (!connection) return;
|
|
291
|
-
|
|
292
|
-
connection.readyState = 3; // CLOSED
|
|
293
|
-
|
|
294
|
-
this.emit('close', connection, 1006, 'Connection terminated');
|
|
295
|
-
|
|
296
|
-
if (this.isServer) {
|
|
297
|
-
this.serverInstance.connections.delete(connectionId);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
this.connections.delete(connectionId);
|
|
301
|
-
this.messageQueue.delete(connectionId);
|
|
302
|
-
|
|
303
|
-
return true;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Broadcast message to all connections
|
|
308
|
-
* @param {any} message - Message to broadcast
|
|
309
|
-
* @private
|
|
310
|
-
*/
|
|
311
|
-
_broadcast(message) {
|
|
312
|
-
if (!this.isServer) return;
|
|
313
|
-
|
|
314
|
-
this.serverInstance.connections.forEach((connection, connectionId) => {
|
|
315
|
-
this._deliverMessage(connectionId, message);
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Send message to specific connection
|
|
321
|
-
* @param {string} connectionId - Connection ID
|
|
322
|
-
* @param {any} message - Message to send
|
|
323
|
-
*/
|
|
324
|
-
sendTo(connectionId, message) {
|
|
325
|
-
if (!this.isServer) return false;
|
|
326
|
-
|
|
327
|
-
const connection = this.serverInstance.connections.get(connectionId);
|
|
328
|
-
if (!connection) return false;
|
|
329
|
-
|
|
330
|
-
this._deliverMessage(connectionId, message);
|
|
331
|
-
return true;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* Get connection statistics
|
|
336
|
-
* @param {string} connectionId - Connection ID
|
|
337
|
-
* @returns {Object} Connection statistics
|
|
338
|
-
* @private
|
|
339
|
-
*/
|
|
340
|
-
_getConnectionStats(connectionId) {
|
|
341
|
-
const connection = this.connections.get(connectionId);
|
|
342
|
-
if (!connection) return null;
|
|
343
|
-
|
|
344
|
-
return {
|
|
345
|
-
...(connection.stats || {}),
|
|
346
|
-
readyState: connection.readyState,
|
|
347
|
-
protocol: connection.protocol,
|
|
348
|
-
remoteAddress: connection.remoteAddress,
|
|
349
|
-
remotePort: connection.remotePort,
|
|
350
|
-
uptime: Date.now() - (connection.createdAt || Date.now())
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Close the memory server
|
|
356
|
-
* @returns {Promise<void>}
|
|
357
|
-
*/
|
|
358
|
-
async closeServer() {
|
|
359
|
-
return new Promise((resolve) => {
|
|
360
|
-
// Close all connections
|
|
361
|
-
this.connections.forEach((connection, connectionId) => {
|
|
362
|
-
this._closeConnection(connectionId, 1001, 'Server shutting down');
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
// Clear all data
|
|
366
|
-
this.connections.clear();
|
|
367
|
-
this.messageQueue.clear();
|
|
368
|
-
this.serverInstance = null;
|
|
369
|
-
this.isServer = false;
|
|
370
|
-
|
|
371
|
-
setTimeout(() => {
|
|
372
|
-
this.emit('server:closed');
|
|
373
|
-
resolve();
|
|
374
|
-
}, this.config.latency);
|
|
375
|
-
});
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
/**
|
|
379
|
-
* Get server statistics
|
|
380
|
-
* @returns {Object} Statistics object
|
|
381
|
-
*/
|
|
382
|
-
getStats() {
|
|
383
|
-
return {
|
|
384
|
-
connections: this.connections.size,
|
|
385
|
-
isServer: this.isServer,
|
|
386
|
-
config: {
|
|
387
|
-
latency: this.config.latency,
|
|
388
|
-
dropRate: this.config.dropRate,
|
|
389
|
-
maxPayload: this.config.maxPayload
|
|
390
|
-
},
|
|
391
|
-
memoryUsage: process.memoryUsage(),
|
|
392
|
-
timestamp: Date.now()
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Simulate network conditions
|
|
398
|
-
* @param {Object} conditions - Network conditions to simulate
|
|
399
|
-
*/
|
|
400
|
-
simulateNetwork(conditions = {}) {
|
|
401
|
-
this.config = {
|
|
402
|
-
...this.config,
|
|
403
|
-
...conditions
|
|
404
|
-
};
|
|
405
|
-
|
|
406
|
-
this.emit('network:changed', this.config);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Reset all connections and state
|
|
411
|
-
*/
|
|
412
|
-
reset() {
|
|
413
|
-
this.connections.clear();
|
|
414
|
-
this.messageQueue.clear();
|
|
415
|
-
this.serverInstance = null;
|
|
416
|
-
this.isServer = false;
|
|
417
|
-
|
|
418
|
-
this.emit('reset');
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
export default MemoryDriver;
|