jinaga-server 3.5.1 → 3.5.2
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/dist/http/router.js +183 -24
- package/dist/http/router.js.map +1 -1
- package/dist/http/stream.d.ts +3 -0
- package/dist/http/stream.js +79 -5
- package/dist/http/stream.js.map +1 -1
- package/docs/analysis/DIAGNOSTIC_LOGGING_README.md +201 -0
- package/docs/analysis/security-audit-feeds-hash-handler.md +407 -0
- package/package.json +2 -2
- package/src/http/router.ts +232 -29
- package/src/http/stream.ts +97 -5
- package/test-concurrent-streams.js +219 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test script to simulate multiple concurrent clients connecting to streaming feeds
|
|
5
|
+
* This will help validate the diagnostic logging and reproduce the push notification issues
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const http = require('http');
|
|
9
|
+
const { performance } = require('perf_hooks');
|
|
10
|
+
|
|
11
|
+
// Configuration
|
|
12
|
+
const SERVER_HOST = 'localhost';
|
|
13
|
+
const SERVER_PORT = 8080;
|
|
14
|
+
const FEED_HASH = 'your-feed-hash-here'; // Replace with actual feed hash
|
|
15
|
+
const NUM_CLIENTS = 5;
|
|
16
|
+
const CONNECTION_DURATION = 30000; // 30 seconds
|
|
17
|
+
const STAGGER_DELAY = 1000; // 1 second between client connections
|
|
18
|
+
|
|
19
|
+
class StreamingClient {
|
|
20
|
+
constructor(clientId, feedHash) {
|
|
21
|
+
this.clientId = clientId;
|
|
22
|
+
this.feedHash = feedHash;
|
|
23
|
+
this.messageCount = 0;
|
|
24
|
+
this.connected = false;
|
|
25
|
+
this.startTime = null;
|
|
26
|
+
this.lastMessageTime = null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
connect() {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
console.log(`[Client-${this.clientId}] Connecting to feed: ${this.feedHash}`);
|
|
32
|
+
this.startTime = performance.now();
|
|
33
|
+
|
|
34
|
+
const options = {
|
|
35
|
+
hostname: SERVER_HOST,
|
|
36
|
+
port: SERVER_PORT,
|
|
37
|
+
path: `/feeds/${this.feedHash}`,
|
|
38
|
+
method: 'GET',
|
|
39
|
+
headers: {
|
|
40
|
+
'Accept': 'application/x-jinaga-feed-stream',
|
|
41
|
+
'Connection': 'keep-alive',
|
|
42
|
+
'Cache-Control': 'no-cache'
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const req = http.request(options, (res) => {
|
|
47
|
+
console.log(`[Client-${this.clientId}] Connected - Status: ${res.statusCode}`);
|
|
48
|
+
|
|
49
|
+
if (res.statusCode !== 200) {
|
|
50
|
+
console.error(`[Client-${this.clientId}] Connection failed with status: ${res.statusCode}`);
|
|
51
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
this.connected = true;
|
|
56
|
+
resolve();
|
|
57
|
+
|
|
58
|
+
res.on('data', (chunk) => {
|
|
59
|
+
const now = performance.now();
|
|
60
|
+
this.messageCount++;
|
|
61
|
+
this.lastMessageTime = now;
|
|
62
|
+
|
|
63
|
+
const timeSinceStart = Math.round(now - this.startTime);
|
|
64
|
+
const timeSinceLastMessage = this.lastMessageTime ? Math.round(now - this.lastMessageTime) : 0;
|
|
65
|
+
|
|
66
|
+
console.log(`[Client-${this.clientId}] Message #${this.messageCount} received - Time: ${timeSinceStart}ms, Gap: ${timeSinceLastMessage}ms, Size: ${chunk.length} bytes`);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const data = chunk.toString().trim();
|
|
70
|
+
if (data) {
|
|
71
|
+
const parsed = JSON.parse(data);
|
|
72
|
+
console.log(`[Client-${this.clientId}] Parsed message - References: ${parsed.references?.length || 0}, Bookmark: ${parsed.bookmark?.substring(0, 8) || 'none'}...`);
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.warn(`[Client-${this.clientId}] Failed to parse message: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
res.on('end', () => {
|
|
80
|
+
const duration = Math.round(performance.now() - this.startTime);
|
|
81
|
+
console.log(`[Client-${this.clientId}] Stream ended - Duration: ${duration}ms, Messages: ${this.messageCount}`);
|
|
82
|
+
this.connected = false;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
res.on('error', (error) => {
|
|
86
|
+
console.error(`[Client-${this.clientId}] Stream error: ${error.message}`);
|
|
87
|
+
this.connected = false;
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
req.on('error', (error) => {
|
|
92
|
+
console.error(`[Client-${this.clientId}] Request error: ${error.message}`);
|
|
93
|
+
reject(error);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
req.setTimeout(CONNECTION_DURATION + 5000, () => {
|
|
97
|
+
console.log(`[Client-${this.clientId}] Request timeout`);
|
|
98
|
+
req.destroy();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
req.end();
|
|
102
|
+
|
|
103
|
+
// Auto-disconnect after specified duration
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
if (this.connected) {
|
|
106
|
+
console.log(`[Client-${this.clientId}] Auto-disconnecting after ${CONNECTION_DURATION}ms`);
|
|
107
|
+
req.destroy();
|
|
108
|
+
}
|
|
109
|
+
}, CONNECTION_DURATION);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
getStats() {
|
|
114
|
+
const duration = this.startTime ? Math.round(performance.now() - this.startTime) : 0;
|
|
115
|
+
return {
|
|
116
|
+
clientId: this.clientId,
|
|
117
|
+
messageCount: this.messageCount,
|
|
118
|
+
connected: this.connected,
|
|
119
|
+
duration: duration
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async function runConcurrentTest() {
|
|
125
|
+
console.log(`Starting concurrent streaming test with ${NUM_CLIENTS} clients`);
|
|
126
|
+
console.log(`Server: ${SERVER_HOST}:${SERVER_PORT}`);
|
|
127
|
+
console.log(`Feed Hash: ${FEED_HASH}`);
|
|
128
|
+
console.log(`Connection Duration: ${CONNECTION_DURATION}ms`);
|
|
129
|
+
console.log(`Stagger Delay: ${STAGGER_DELAY}ms`);
|
|
130
|
+
console.log('---');
|
|
131
|
+
|
|
132
|
+
const clients = [];
|
|
133
|
+
const connectionPromises = [];
|
|
134
|
+
|
|
135
|
+
// Create and connect clients with staggered timing
|
|
136
|
+
for (let i = 1; i <= NUM_CLIENTS; i++) {
|
|
137
|
+
const client = new StreamingClient(i, FEED_HASH);
|
|
138
|
+
clients.push(client);
|
|
139
|
+
|
|
140
|
+
// Stagger the connections
|
|
141
|
+
setTimeout(async () => {
|
|
142
|
+
try {
|
|
143
|
+
await client.connect();
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error(`[Client-${i}] Connection failed: ${error.message}`);
|
|
146
|
+
}
|
|
147
|
+
}, (i - 1) * STAGGER_DELAY);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Monitor client statistics
|
|
151
|
+
const statsInterval = setInterval(() => {
|
|
152
|
+
console.log('\n=== CLIENT STATISTICS ===');
|
|
153
|
+
clients.forEach(client => {
|
|
154
|
+
const stats = client.getStats();
|
|
155
|
+
console.log(`Client-${stats.clientId}: Messages=${stats.messageCount}, Connected=${stats.connected}, Duration=${stats.duration}ms`);
|
|
156
|
+
});
|
|
157
|
+
console.log('========================\n');
|
|
158
|
+
}, 10000); // Every 10 seconds
|
|
159
|
+
|
|
160
|
+
// Wait for test completion
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
clearInterval(statsInterval);
|
|
163
|
+
|
|
164
|
+
console.log('\n=== FINAL STATISTICS ===');
|
|
165
|
+
let totalMessages = 0;
|
|
166
|
+
let connectedClients = 0;
|
|
167
|
+
|
|
168
|
+
clients.forEach(client => {
|
|
169
|
+
const stats = client.getStats();
|
|
170
|
+
totalMessages += stats.messageCount;
|
|
171
|
+
if (stats.connected) connectedClients++;
|
|
172
|
+
console.log(`Client-${stats.clientId}: Messages=${stats.messageCount}, Connected=${stats.connected}, Duration=${stats.duration}ms`);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
console.log(`\nSummary:`);
|
|
176
|
+
console.log(`- Total Messages Received: ${totalMessages}`);
|
|
177
|
+
console.log(`- Still Connected: ${connectedClients}/${NUM_CLIENTS}`);
|
|
178
|
+
console.log(`- Average Messages per Client: ${(totalMessages / NUM_CLIENTS).toFixed(1)}`);
|
|
179
|
+
|
|
180
|
+
// Check for potential issues
|
|
181
|
+
const messageCounts = clients.map(c => c.getStats().messageCount);
|
|
182
|
+
const minMessages = Math.min(...messageCounts);
|
|
183
|
+
const maxMessages = Math.max(...messageCounts);
|
|
184
|
+
const variance = maxMessages - minMessages;
|
|
185
|
+
|
|
186
|
+
if (variance > 5) {
|
|
187
|
+
console.log(`\n⚠️ POTENTIAL ISSUE DETECTED:`);
|
|
188
|
+
console.log(` Message count variance: ${variance} (min: ${minMessages}, max: ${maxMessages})`);
|
|
189
|
+
console.log(` This suggests some clients may not be receiving all notifications`);
|
|
190
|
+
} else {
|
|
191
|
+
console.log(`\n✅ Message distribution appears consistent`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log('\nTest completed. Check server logs for detailed diagnostic information.');
|
|
195
|
+
process.exit(0);
|
|
196
|
+
}, CONNECTION_DURATION + (NUM_CLIENTS * STAGGER_DELAY) + 5000);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Handle command line arguments
|
|
200
|
+
if (process.argv.length > 2) {
|
|
201
|
+
const feedHash = process.argv[2];
|
|
202
|
+
if (feedHash && feedHash !== 'your-feed-hash-here') {
|
|
203
|
+
FEED_HASH = feedHash;
|
|
204
|
+
console.log(`Using feed hash from command line: ${FEED_HASH}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (FEED_HASH === 'your-feed-hash-here') {
|
|
209
|
+
console.error('Please provide a valid feed hash as a command line argument:');
|
|
210
|
+
console.error('node test-concurrent-streams.js <feed-hash>');
|
|
211
|
+
console.error('\nOr edit the FEED_HASH constant in this script.');
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Start the test
|
|
216
|
+
runConcurrentTest().catch(error => {
|
|
217
|
+
console.error('Test failed:', error);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
});
|