kuzzle 2.20.3 → 2.21.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/lib/api/controllers/debugController.d.ts +2 -27
- package/lib/api/controllers/debugController.js +20 -175
- package/lib/api/controllers/documentController.js +122 -10
- package/lib/api/funnel.js +16 -2
- package/lib/api/request/kuzzleRequest.d.ts +8 -0
- package/lib/api/request/kuzzleRequest.js +12 -0
- package/lib/cluster/idCardHandler.js +19 -6
- package/lib/cluster/node.js +27 -1
- package/lib/cluster/protobuf/sync.proto +5 -0
- package/lib/cluster/publisher.js +10 -0
- package/lib/cluster/subscriber.js +15 -0
- package/lib/cluster/workers/IDCardRenewer.js +27 -26
- package/lib/core/debug/kuzzleDebugger.d.ts +46 -0
- package/lib/core/debug/kuzzleDebugger.js +266 -0
- package/lib/core/network/clientConnection.js +6 -1
- package/lib/core/network/protocols/httpwsProtocol.js +18 -3
- package/lib/kuzzle/dumpGenerator.js +1 -1
- package/lib/kuzzle/kuzzle.js +3 -0
- package/lib/service/storage/elasticsearch.js +44 -30
- package/lib/types/events/EventGenericDocument.d.ts +21 -1
- package/package.json +1 -1
- package/lib/types/DebugModule.d.ts +0 -24
- package/lib/types/DebugModule.js +0 -39
package/lib/cluster/publisher.js
CHANGED
|
@@ -270,6 +270,16 @@ class ClusterPublisher {
|
|
|
270
270
|
return this.send("NodeShutdown", { nodeId });
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Publishes an event about the node being in debug mode
|
|
275
|
+
* @param {bool} evictionPrevented
|
|
276
|
+
*
|
|
277
|
+
* @returns {Long} ID of the message sent
|
|
278
|
+
*/
|
|
279
|
+
sendNodePreventEviction(evictionPrevented) {
|
|
280
|
+
return this.send("NodePreventEviction", { evictionPrevented });
|
|
281
|
+
}
|
|
282
|
+
|
|
273
283
|
/**
|
|
274
284
|
* Publishes an event about a node being evicted
|
|
275
285
|
*
|
|
@@ -55,6 +55,8 @@ class ClusterSubscriber {
|
|
|
55
55
|
this.remoteNodeIP = remoteNodeIP;
|
|
56
56
|
this.remoteNodeAddress = `tcp://${remoteNodeIP}:${this.localNode.config.ports.sync}`;
|
|
57
57
|
this.remoteNodeId = remoteNodeId;
|
|
58
|
+
// Used in debug mode when the node might be slower
|
|
59
|
+
this.remoteNodeEvictionPrevented = false;
|
|
58
60
|
this.socket = null;
|
|
59
61
|
this.protoroot = null;
|
|
60
62
|
|
|
@@ -87,6 +89,7 @@ class ClusterSubscriber {
|
|
|
87
89
|
NewAuthStrategy: this.handleNewAuthStrategy,
|
|
88
90
|
NewRealtimeRoom: this.handleNewRealtimeRoom,
|
|
89
91
|
NodeEvicted: this.handleNodeEviction,
|
|
92
|
+
NodePreventEviction: this.handleNodePreventEviction,
|
|
90
93
|
NodeShutdown: this.handleNodeShutdown,
|
|
91
94
|
RefreshIndexCache: this.handleRefreshIndexCache,
|
|
92
95
|
RefreshValidators: this.handleRefreshValidators,
|
|
@@ -229,6 +232,10 @@ class ClusterSubscriber {
|
|
|
229
232
|
}
|
|
230
233
|
}
|
|
231
234
|
|
|
235
|
+
async handleNodePreventEviction(message) {
|
|
236
|
+
this.remoteNodeEvictionPrevented = message.evictionPrevented;
|
|
237
|
+
}
|
|
238
|
+
|
|
232
239
|
/**
|
|
233
240
|
* Handles a heartbeat from the remote node
|
|
234
241
|
*
|
|
@@ -673,6 +680,14 @@ class ClusterSubscriber {
|
|
|
673
680
|
* to recover, otherwise we evict it from the cluster.
|
|
674
681
|
*/
|
|
675
682
|
async checkHeartbeat() {
|
|
683
|
+
if (this.remoteNodeEvictionPrevented) {
|
|
684
|
+
// Fake the heartbeat while the node eviction prevention is enabled
|
|
685
|
+
// otherwise when the node eviction prevention is disabled
|
|
686
|
+
// the node will be evicted if it did not send a heartbeat before disabling the protection.
|
|
687
|
+
this.lastHeartbeat = Date.now();
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
676
691
|
if (this.state === stateEnum.EVICTED) {
|
|
677
692
|
return;
|
|
678
693
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const { isMainThread, parentPort } = require("worker_threads");
|
|
4
3
|
const Redis = require("../../service/cache/redis");
|
|
5
4
|
|
|
6
5
|
class IDCardRenewer {
|
|
7
6
|
constructor() {
|
|
8
|
-
this.parentPort = parentPort;
|
|
9
7
|
this.redis = null;
|
|
10
8
|
this.refreshTimer = null;
|
|
11
9
|
this.nodeIdKey = null;
|
|
@@ -31,6 +29,10 @@ class IDCardRenewer {
|
|
|
31
29
|
const redisConf = config.redis || {};
|
|
32
30
|
await this.initRedis(redisConf.config, redisConf.name);
|
|
33
31
|
} catch (error) {
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
console.error(
|
|
34
|
+
`Failed to connect to redis, could not refresh ID card: ${error.message}`
|
|
35
|
+
);
|
|
34
36
|
this.parentPort.postMessage({
|
|
35
37
|
error: `Failed to connect to redis, could not refresh ID card: ${error.message}`,
|
|
36
38
|
});
|
|
@@ -50,7 +52,7 @@ class IDCardRenewer {
|
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
// Notify that the worker is running and updating the ID Card
|
|
53
|
-
|
|
55
|
+
process.send({ initialized: true });
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
async initRedis(config, name) {
|
|
@@ -73,13 +75,13 @@ class IDCardRenewer {
|
|
|
73
75
|
// => this node is too slow, we need to remove it from the cluster
|
|
74
76
|
if (refreshed === 0) {
|
|
75
77
|
await this.dispose();
|
|
76
|
-
|
|
78
|
+
process.send({
|
|
77
79
|
error: "Node too slow: ID card expired",
|
|
78
80
|
});
|
|
79
81
|
}
|
|
80
82
|
} catch (error) {
|
|
81
83
|
await this.dispose();
|
|
82
|
-
|
|
84
|
+
process.send({
|
|
83
85
|
error: `Failed to refresh ID Card: ${error.message}`,
|
|
84
86
|
});
|
|
85
87
|
}
|
|
@@ -112,26 +114,25 @@ class IDCardRenewer {
|
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
117
|
+
const idCardRenewer = new IDCardRenewer();
|
|
118
|
+
|
|
119
|
+
process.on("message", async (message) => {
|
|
120
|
+
if (message.action === "start") {
|
|
121
|
+
// Simulate basic global Kuzzle Context
|
|
122
|
+
global.kuzzle = { ...message.kuzzle };
|
|
123
|
+
global.kuzzle.log = {
|
|
124
|
+
debug: console.debug, // eslint-disable-line no-console
|
|
125
|
+
error: console.error, // eslint-disable-line no-console
|
|
126
|
+
info: console.info, // eslint-disable-line no-console
|
|
127
|
+
warn: console.warn, // eslint-disable-line no-console
|
|
128
|
+
};
|
|
129
|
+
// Should never throw
|
|
130
|
+
await idCardRenewer.init(message);
|
|
131
|
+
} else if (message.action === "dispose") {
|
|
132
|
+
// Should never throw
|
|
133
|
+
await idCardRenewer.dispose();
|
|
134
|
+
process.exit(0);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
136
137
|
|
|
137
138
|
module.exports = { IDCardRenewer };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { JSONObject } from "kuzzle-sdk";
|
|
2
|
+
export declare class KuzzleDebugger {
|
|
3
|
+
private inspector;
|
|
4
|
+
private debuggerStatus;
|
|
5
|
+
/**
|
|
6
|
+
* Map<eventName, Set<connectionId>>
|
|
7
|
+
*/
|
|
8
|
+
private events;
|
|
9
|
+
private httpWsProtocol?;
|
|
10
|
+
init(): Promise<void>;
|
|
11
|
+
registerAsks(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Connect the debugger
|
|
14
|
+
*/
|
|
15
|
+
enable(): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Disconnect the debugger and clears all the events listeners
|
|
18
|
+
*/
|
|
19
|
+
disable(): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Trigger action from debugger directly following the Chrome Debug Protocol
|
|
22
|
+
* See: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
23
|
+
*/
|
|
24
|
+
post(method: string, params?: JSONObject): Promise<JSONObject>;
|
|
25
|
+
/**
|
|
26
|
+
* Make the websocket connection listen and receive events from Chrome Debug Protocol
|
|
27
|
+
* See events from: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
28
|
+
*/
|
|
29
|
+
addListener(event: string, connectionId: string): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Remove the websocket connection from the events" listeners
|
|
32
|
+
*/
|
|
33
|
+
removeListener(event: string, connectionId: string): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Execute a method using the Chrome Debug Protocol
|
|
36
|
+
* @param method Chrome Debug Protocol method to execute
|
|
37
|
+
* @param params
|
|
38
|
+
* @returns
|
|
39
|
+
*/
|
|
40
|
+
private inspectorPost;
|
|
41
|
+
/**
|
|
42
|
+
* Sends a direct notification to a websocket connection without having to listen to a specific room
|
|
43
|
+
*/
|
|
44
|
+
private notifyConnection;
|
|
45
|
+
private notifyGlobalListeners;
|
|
46
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.KuzzleDebugger = void 0;
|
|
30
|
+
const inspector_1 = __importDefault(require("inspector"));
|
|
31
|
+
const kerror = __importStar(require("../../kerror"));
|
|
32
|
+
const DEBUGGER_EVENT = "kuzzle-debugger-event";
|
|
33
|
+
class KuzzleDebugger {
|
|
34
|
+
constructor() {
|
|
35
|
+
this.debuggerStatus = false;
|
|
36
|
+
/**
|
|
37
|
+
* Map<eventName, Set<connectionId>>
|
|
38
|
+
*/
|
|
39
|
+
this.events = new Map();
|
|
40
|
+
}
|
|
41
|
+
async init() {
|
|
42
|
+
this.httpWsProtocol = global.kuzzle.entryPoint.protocols.get("websocket");
|
|
43
|
+
this.inspector = new inspector_1.default.Session();
|
|
44
|
+
// Remove connection id from the list of listeners for each event
|
|
45
|
+
global.kuzzle.on("connection:remove", (connectionId) => {
|
|
46
|
+
if (!this.debuggerStatus) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const listener of this.events.values()) {
|
|
50
|
+
listener.delete(connectionId);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
this.inspector.on("inspectorNotification", async (payload) => {
|
|
54
|
+
if (!this.debuggerStatus) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await this.notifyGlobalListeners(payload.method, payload);
|
|
58
|
+
const listeners = this.events.get(payload.method);
|
|
59
|
+
if (!listeners) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const promises = [];
|
|
63
|
+
for (const connectionId of listeners) {
|
|
64
|
+
promises.push(this.notifyConnection(connectionId, DEBUGGER_EVENT, {
|
|
65
|
+
event: payload.method,
|
|
66
|
+
result: payload,
|
|
67
|
+
}));
|
|
68
|
+
}
|
|
69
|
+
// No need to catch, notify is already try-catched
|
|
70
|
+
await Promise.all(promises);
|
|
71
|
+
});
|
|
72
|
+
await this.registerAsks();
|
|
73
|
+
}
|
|
74
|
+
async registerAsks() {
|
|
75
|
+
global.kuzzle.onAsk("core:debugger:enable", () => this.enable());
|
|
76
|
+
global.kuzzle.onAsk("core:debugger:disable", () => this.disable());
|
|
77
|
+
global.kuzzle.onAsk("core:debugger:post", (method, params) => this.post(method, params));
|
|
78
|
+
global.kuzzle.onAsk("core:debugger:isEnabled", () => this.debuggerStatus);
|
|
79
|
+
global.kuzzle.onAsk("core:debugger:removeListener", (event, connectionId) => this.removeListener(event, connectionId));
|
|
80
|
+
global.kuzzle.onAsk("core:debugger:addListener", (event, connectionId) => this.addListener(event, connectionId));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Connect the debugger
|
|
84
|
+
*/
|
|
85
|
+
async enable() {
|
|
86
|
+
if (this.debuggerStatus) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.inspector.connect();
|
|
90
|
+
this.debuggerStatus = true;
|
|
91
|
+
await global.kuzzle.ask("cluster:node:preventEviction", true);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Disconnect the debugger and clears all the events listeners
|
|
95
|
+
*/
|
|
96
|
+
async disable() {
|
|
97
|
+
if (!this.debuggerStatus) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.inspector.disconnect();
|
|
101
|
+
this.debuggerStatus = false;
|
|
102
|
+
await global.kuzzle.ask("cluster:node:preventEviction", false);
|
|
103
|
+
// Disable debug mode for all connected sockets that still have listeners
|
|
104
|
+
if (this.httpWsProtocol) {
|
|
105
|
+
for (const eventName of this.events.keys()) {
|
|
106
|
+
for (const connectionId of this.events.get(eventName)) {
|
|
107
|
+
const socket = this.httpWsProtocol.socketByConnectionId.get(connectionId);
|
|
108
|
+
if (socket) {
|
|
109
|
+
socket.internal.debugSession = false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
this.events.clear();
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Trigger action from debugger directly following the Chrome Debug Protocol
|
|
118
|
+
* See: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
119
|
+
*/
|
|
120
|
+
async post(method, params = {}) {
|
|
121
|
+
if (!this.debuggerStatus) {
|
|
122
|
+
throw kerror.get("core", "debugger", "not_enabled");
|
|
123
|
+
}
|
|
124
|
+
// Always disable report progress because this parameter causes a segfault.
|
|
125
|
+
// The reason this happens is because the inspector is running inside the same thread
|
|
126
|
+
// as the Kuzzle Process and reportProgress forces the inspector to call function in the JS Heap
|
|
127
|
+
// while it is being inspected by the HeapProfiler, which causes a segfault.
|
|
128
|
+
// See: https://github.com/nodejs/node/issues/44634
|
|
129
|
+
if (params.reportProgress) {
|
|
130
|
+
// We need to send a fake HeapProfiler.reportHeapSnapshotProgress event
|
|
131
|
+
// to the inspector to make Chrome think that the HeapProfiler is done
|
|
132
|
+
// otherwise, even though the Chrome Inspector did receive the whole snapshot, it will not be parsed.
|
|
133
|
+
//
|
|
134
|
+
// Chrome inspector is waiting for a HeapProfiler.reportHeapSnapshotProgress event with the finished property set to true
|
|
135
|
+
// The `done` and `total` properties are only used to show a progress bar, so there are not important.
|
|
136
|
+
// Sending this event before the HeapProfiler.addHeapSnapshotChunk event will not cause any problem,
|
|
137
|
+
// in fact, Chrome always do that when taking a snapshot, it receives the HeapProfiler.reportHeapSnapshotProgress event
|
|
138
|
+
// before the HeapProfiler.addHeapSnapshotChunk event.
|
|
139
|
+
// So this will have no impact and when receiving the HeapProfiler.addHeapSnapshotChunk event, Chrome will wait to receive
|
|
140
|
+
// a complete snapshot before parsing it if it has received the HeapProfiler.reportHeapSnapshotProgress event with the finished property set to true before.
|
|
141
|
+
this.inspector.emit("inspectorNotification", {
|
|
142
|
+
method: "HeapProfiler.reportHeapSnapshotProgress",
|
|
143
|
+
params: {
|
|
144
|
+
done: 0,
|
|
145
|
+
finished: true,
|
|
146
|
+
total: 0,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
params.reportProgress = false;
|
|
150
|
+
}
|
|
151
|
+
return this.inspectorPost(method, params);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Make the websocket connection listen and receive events from Chrome Debug Protocol
|
|
155
|
+
* See events from: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
156
|
+
*/
|
|
157
|
+
async addListener(event, connectionId) {
|
|
158
|
+
if (!this.debuggerStatus) {
|
|
159
|
+
throw kerror.get("core", "debugger", "not_enabled");
|
|
160
|
+
}
|
|
161
|
+
if (this.httpWsProtocol) {
|
|
162
|
+
const socket = this.httpWsProtocol.socketByConnectionId.get(connectionId);
|
|
163
|
+
if (socket) {
|
|
164
|
+
/**
|
|
165
|
+
* Mark the socket as a debugging socket
|
|
166
|
+
* this will bypass some limitations like the max pressure buffer size,
|
|
167
|
+
* which could end the connection when the debugger is sending a lot of data.
|
|
168
|
+
*/
|
|
169
|
+
socket.internal.debugSession = true;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
let listeners = this.events.get(event);
|
|
173
|
+
if (!listeners) {
|
|
174
|
+
listeners = new Set();
|
|
175
|
+
this.events.set(event, listeners);
|
|
176
|
+
}
|
|
177
|
+
listeners.add(connectionId);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Remove the websocket connection from the events" listeners
|
|
181
|
+
*/
|
|
182
|
+
async removeListener(event, connectionId) {
|
|
183
|
+
if (!this.debuggerStatus) {
|
|
184
|
+
throw kerror.get("core", "debugger", "not_enabled");
|
|
185
|
+
}
|
|
186
|
+
const listeners = this.events.get(event);
|
|
187
|
+
if (listeners) {
|
|
188
|
+
listeners.delete(connectionId);
|
|
189
|
+
}
|
|
190
|
+
if (!this.httpWsProtocol) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const socket = this.httpWsProtocol.socketByConnectionId.get(connectionId);
|
|
194
|
+
if (!socket) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
let removeDebugSessionMarker = true;
|
|
198
|
+
/**
|
|
199
|
+
* If the connection doesn't listen to any other events
|
|
200
|
+
* we can remove the debugSession marker
|
|
201
|
+
*/
|
|
202
|
+
for (const eventName of this.events.keys()) {
|
|
203
|
+
const eventListener = this.events.get(eventName);
|
|
204
|
+
if (eventListener && eventListener.has(connectionId)) {
|
|
205
|
+
removeDebugSessionMarker = false;
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (removeDebugSessionMarker) {
|
|
210
|
+
socket.internal.debugSession = false;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Execute a method using the Chrome Debug Protocol
|
|
215
|
+
* @param method Chrome Debug Protocol method to execute
|
|
216
|
+
* @param params
|
|
217
|
+
* @returns
|
|
218
|
+
*/
|
|
219
|
+
async inspectorPost(method, params) {
|
|
220
|
+
if (!this.debuggerStatus) {
|
|
221
|
+
throw kerror.get("core", "debugger", "not_enabled");
|
|
222
|
+
}
|
|
223
|
+
let resolve;
|
|
224
|
+
const promise = new Promise((res) => {
|
|
225
|
+
resolve = res;
|
|
226
|
+
});
|
|
227
|
+
this.inspector.post(method, params, (err, res) => {
|
|
228
|
+
if (err) {
|
|
229
|
+
resolve({
|
|
230
|
+
error: JSON.stringify(Object.getOwnPropertyDescriptors(err)),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
resolve(res);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
return promise;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Sends a direct notification to a websocket connection without having to listen to a specific room
|
|
241
|
+
*/
|
|
242
|
+
async notifyConnection(connectionId, event, payload) {
|
|
243
|
+
global.kuzzle.entryPoint._notify({
|
|
244
|
+
channels: [event],
|
|
245
|
+
connectionId,
|
|
246
|
+
payload,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
async notifyGlobalListeners(event, payload) {
|
|
250
|
+
const listeners = this.events.get("*");
|
|
251
|
+
if (!listeners) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const promises = [];
|
|
255
|
+
for (const connectionId of listeners) {
|
|
256
|
+
promises.push(this.notifyConnection(connectionId, DEBUGGER_EVENT, {
|
|
257
|
+
event,
|
|
258
|
+
result: payload,
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
// No need to catch, notify is already try-catched
|
|
262
|
+
await Promise.all(promises);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
exports.KuzzleDebugger = KuzzleDebugger;
|
|
266
|
+
//# sourceMappingURL=kuzzleDebugger.js.map
|
|
@@ -31,10 +31,11 @@ const uuid = require("uuid");
|
|
|
31
31
|
* @param {object} [headers] - Optional extra key-value object. I.e., for http, will receive the request headers
|
|
32
32
|
*/
|
|
33
33
|
class ClientConnection {
|
|
34
|
-
constructor(protocol, ips, headers = null) {
|
|
34
|
+
constructor(protocol, ips, headers = null, internal = null) {
|
|
35
35
|
this.id = uuid.v4();
|
|
36
36
|
this.protocol = protocol;
|
|
37
37
|
this.headers = {};
|
|
38
|
+
this.internal = {};
|
|
38
39
|
|
|
39
40
|
if (!Array.isArray(ips)) {
|
|
40
41
|
throw new TypeError(`Expected ips to be an Array, got ${typeof ips}`);
|
|
@@ -45,6 +46,10 @@ class ClientConnection {
|
|
|
45
46
|
this.headers = headers;
|
|
46
47
|
}
|
|
47
48
|
|
|
49
|
+
if (isPlainObject(internal)) {
|
|
50
|
+
this.internal = internal;
|
|
51
|
+
}
|
|
52
|
+
|
|
48
53
|
Object.freeze(this);
|
|
49
54
|
}
|
|
50
55
|
}
|
|
@@ -282,6 +282,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
282
282
|
res.upgrade(
|
|
283
283
|
{
|
|
284
284
|
headers,
|
|
285
|
+
internal: {},
|
|
285
286
|
},
|
|
286
287
|
req.getHeader("sec-websocket-key"),
|
|
287
288
|
req.getHeader("sec-websocket-protocol"),
|
|
@@ -292,7 +293,12 @@ class HttpWsProtocol extends Protocol {
|
|
|
292
293
|
|
|
293
294
|
wsOnOpenHandler(socket) {
|
|
294
295
|
const ip = Buffer.from(socket.getRemoteAddressAsText()).toString();
|
|
295
|
-
const connection = new ClientConnection(
|
|
296
|
+
const connection = new ClientConnection(
|
|
297
|
+
this.name,
|
|
298
|
+
[ip],
|
|
299
|
+
socket.headers,
|
|
300
|
+
socket.internal
|
|
301
|
+
);
|
|
296
302
|
|
|
297
303
|
this.entryPoint.newConnection(connection);
|
|
298
304
|
this.connectionBySocket.set(socket, connection);
|
|
@@ -457,8 +463,17 @@ class HttpWsProtocol extends Protocol {
|
|
|
457
463
|
const buffer = this.backpressureBuffer.get(socket);
|
|
458
464
|
buffer.push(payload);
|
|
459
465
|
|
|
460
|
-
|
|
461
|
-
|
|
466
|
+
/**
|
|
467
|
+
* Client socket too slow: we need to close it
|
|
468
|
+
*
|
|
469
|
+
* If the socket is marked as a debugSession, we don't close it
|
|
470
|
+
* the debugger might send a lot of messages and we don't want to
|
|
471
|
+
* loose the connection while debugging and loose important information.
|
|
472
|
+
*/
|
|
473
|
+
if (
|
|
474
|
+
!socket.internal.debugSession &&
|
|
475
|
+
buffer.length > WS_BACKPRESSURE_BUFFER_MAX_LENGTH
|
|
476
|
+
) {
|
|
462
477
|
socket.end(WS_FORCED_TERMINATION_CODE, WS_BACKPRESSURE_MESSAGE);
|
|
463
478
|
}
|
|
464
479
|
}
|
|
@@ -228,7 +228,7 @@ class DumpGenerator {
|
|
|
228
228
|
while (dumps.length >= config.history.reports) {
|
|
229
229
|
const dir = dumps.shift().path;
|
|
230
230
|
|
|
231
|
-
fs.
|
|
231
|
+
fs.rmdirSync(dir, { recursive: true });
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
for (let i = 0; i < dumps.length - config.history.coredump; i++) {
|
package/lib/kuzzle/kuzzle.js
CHANGED
|
@@ -80,6 +80,7 @@ const package_json_1 = require("../../package.json");
|
|
|
80
80
|
const name_generator_1 = require("../util/name-generator");
|
|
81
81
|
const openapi_1 = require("../api/openapi");
|
|
82
82
|
const crypto_1 = require("../util/crypto");
|
|
83
|
+
const kuzzleDebugger_1 = require("../core/debug/kuzzleDebugger");
|
|
83
84
|
exports.BACKEND_IMPORT_KEY = "backend:init:import";
|
|
84
85
|
let _kuzzle = null;
|
|
85
86
|
Reflect.defineProperty(global, "kuzzle", {
|
|
@@ -119,6 +120,7 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
|
|
|
119
120
|
this.dumpGenerator = new dumpGenerator_1.default();
|
|
120
121
|
this.vault = null;
|
|
121
122
|
this.asyncStore = new asyncStore_1.default();
|
|
123
|
+
this.debugger = new kuzzleDebugger_1.KuzzleDebugger();
|
|
122
124
|
this.version = package_json_1.version;
|
|
123
125
|
this.importTypes = {
|
|
124
126
|
fixtures: this.importFixtures.bind(this),
|
|
@@ -164,6 +166,7 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
|
|
|
164
166
|
// must be initialized before plugins to allow API requests from plugins
|
|
165
167
|
// before opening connections to external users
|
|
166
168
|
await this.entryPoint.init();
|
|
169
|
+
await this.debugger.init();
|
|
167
170
|
this.pluginsManager.application = application;
|
|
168
171
|
const pluginImports = await this.pluginsManager.init(options.plugins);
|
|
169
172
|
this.log.info(`[✔] Successfully loaded ${this.pluginsManager.loadedPlugins.length} plugins: ${this.pluginsManager.loadedPlugins.join(", ")}`);
|