kuzzle 2.21.0 → 2.22.0
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.js +19 -0
- package/lib/api/funnel.js +16 -2
- package/lib/cluster/node.js +9 -0
- package/lib/cluster/subscriber.js +9 -1
- package/lib/cluster/workers/IDCardRenewer.js +4 -0
- package/lib/core/backend/backend.d.ts +2 -1
- package/lib/core/backend/backend.js +15 -13
- package/lib/core/backend/backendSubscription.d.ts +25 -0
- package/lib/core/backend/backendSubscription.js +90 -0
- package/lib/core/backend/index.d.ts +1 -0
- package/lib/core/backend/index.js +1 -0
- package/lib/core/debug/kuzzleDebugger.d.ts +1 -0
- package/lib/core/debug/kuzzleDebugger.js +70 -9
- package/lib/core/network/clientConnection.js +6 -1
- package/lib/core/network/protocols/httpwsProtocol.js +18 -3
- package/lib/kuzzle/kuzzle.js +1 -1
- package/package.json +1 -1
|
@@ -42,10 +42,14 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
42
42
|
__setModuleDefault(result, mod);
|
|
43
43
|
return result;
|
|
44
44
|
};
|
|
45
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
46
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
47
|
+
};
|
|
45
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
49
|
exports.DebugController = void 0;
|
|
47
50
|
const baseController_1 = require("./baseController");
|
|
48
51
|
const kerror = __importStar(require("../../kerror"));
|
|
52
|
+
const get_1 = __importDefault(require("lodash/get"));
|
|
49
53
|
/**
|
|
50
54
|
* @class DebugController
|
|
51
55
|
*/
|
|
@@ -70,12 +74,18 @@ class DebugController extends baseController_1.NativeController {
|
|
|
70
74
|
* Connect the debugger
|
|
71
75
|
*/
|
|
72
76
|
async enable() {
|
|
77
|
+
if (!(0, get_1.default)(global.kuzzle.config, "security.debug.native_debug_protocol")) {
|
|
78
|
+
throw kerror.get("core", "debugger", "native_debug_protocol_usage_denied");
|
|
79
|
+
}
|
|
73
80
|
await global.kuzzle.ask("core:debugger:enable");
|
|
74
81
|
}
|
|
75
82
|
/**
|
|
76
83
|
* Disconnect the debugger and clears all the events listeners
|
|
77
84
|
*/
|
|
78
85
|
async disable() {
|
|
86
|
+
if (!(0, get_1.default)(global.kuzzle.config, "security.debug.native_debug_protocol")) {
|
|
87
|
+
throw kerror.get("core", "debugger", "native_debug_protocol_usage_denied");
|
|
88
|
+
}
|
|
79
89
|
await global.kuzzle.ask("core:debugger:disable");
|
|
80
90
|
}
|
|
81
91
|
/**
|
|
@@ -83,6 +93,9 @@ class DebugController extends baseController_1.NativeController {
|
|
|
83
93
|
* See: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
84
94
|
*/
|
|
85
95
|
async post(request) {
|
|
96
|
+
if (!(0, get_1.default)(global.kuzzle.config, "security.debug.native_debug_protocol")) {
|
|
97
|
+
throw kerror.get("core", "debugger", "native_debug_protocol_usage_denied");
|
|
98
|
+
}
|
|
86
99
|
const method = request.getBodyString("method");
|
|
87
100
|
const params = request.getBodyObject("params", {});
|
|
88
101
|
return global.kuzzle.ask("core:debugger:post", method, params);
|
|
@@ -92,6 +105,9 @@ class DebugController extends baseController_1.NativeController {
|
|
|
92
105
|
* See events from: https://chromedevtools.github.io/devtools-protocol/v8/
|
|
93
106
|
*/
|
|
94
107
|
async addListener(request) {
|
|
108
|
+
if (!(0, get_1.default)(global.kuzzle.config, "security.debug.native_debug_protocol")) {
|
|
109
|
+
throw kerror.get("core", "debugger", "native_debug_protocol_usage_denied");
|
|
110
|
+
}
|
|
95
111
|
if (request.context.connection.protocol !== "websocket") {
|
|
96
112
|
throw kerror.get("api", "assert", "unsupported_protocol", request.context.connection.protocol, "debug:addListener");
|
|
97
113
|
}
|
|
@@ -102,6 +118,9 @@ class DebugController extends baseController_1.NativeController {
|
|
|
102
118
|
* Remove the websocket connection from the events' listeners
|
|
103
119
|
*/
|
|
104
120
|
async removeListener(request) {
|
|
121
|
+
if (!(0, get_1.default)(global.kuzzle.config, "security.debug.native_debug_protocol")) {
|
|
122
|
+
throw kerror.get("core", "debugger", "native_debug_protocol_usage_denied");
|
|
123
|
+
}
|
|
105
124
|
if (request.context.connection.protocol !== "websocket") {
|
|
106
125
|
throw kerror.get("api", "assert", "unsupported_protocol", request.context.connection.protocol, "debug:removeListener");
|
|
107
126
|
}
|
package/lib/api/funnel.js
CHANGED
|
@@ -51,6 +51,7 @@ const debug = require("../util/debug")("kuzzle:funnel");
|
|
|
51
51
|
const processError = kerror.wrap("api", "process");
|
|
52
52
|
const { has } = require("../util/safeObject");
|
|
53
53
|
const { HttpStream } = require("../types");
|
|
54
|
+
const get = require("lodash/get");
|
|
54
55
|
|
|
55
56
|
// Actions of the auth controller that does not necessite to verify the token
|
|
56
57
|
// when cookie auth is active
|
|
@@ -179,6 +180,12 @@ class Funnel {
|
|
|
179
180
|
throw processError.get("not_enough_nodes");
|
|
180
181
|
}
|
|
181
182
|
|
|
183
|
+
const isRequestFromDebugSession = get(
|
|
184
|
+
request,
|
|
185
|
+
"context.connection.misc.internal.debugSession",
|
|
186
|
+
false
|
|
187
|
+
);
|
|
188
|
+
|
|
182
189
|
if (this.overloaded) {
|
|
183
190
|
const now = Date.now();
|
|
184
191
|
|
|
@@ -226,7 +233,8 @@ class Funnel {
|
|
|
226
233
|
*/
|
|
227
234
|
if (
|
|
228
235
|
this.pendingRequestsQueue.length >=
|
|
229
|
-
|
|
236
|
+
global.kuzzle.config.limits.requestsBufferSize &&
|
|
237
|
+
!isRequestFromDebugSession
|
|
230
238
|
) {
|
|
231
239
|
const error = processError.get("overloaded");
|
|
232
240
|
global.kuzzle.emit("log:error", error);
|
|
@@ -239,7 +247,13 @@ class Funnel {
|
|
|
239
247
|
request.internalId,
|
|
240
248
|
new PendingRequest(request, fn, context)
|
|
241
249
|
);
|
|
242
|
-
|
|
250
|
+
|
|
251
|
+
if (isRequestFromDebugSession) {
|
|
252
|
+
// Push at the front to prioritize debug requests
|
|
253
|
+
this.pendingRequestsQueue.unshift(request.internalId);
|
|
254
|
+
} else {
|
|
255
|
+
this.pendingRequestsQueue.push(request.internalId);
|
|
256
|
+
}
|
|
243
257
|
|
|
244
258
|
if (!this.overloaded) {
|
|
245
259
|
this.overloaded = true;
|
package/lib/cluster/node.js
CHANGED
|
@@ -256,6 +256,15 @@ class ClusterNode {
|
|
|
256
256
|
*/
|
|
257
257
|
preventEviction(evictionPrevented) {
|
|
258
258
|
this.publisher.sendNodePreventEviction(evictionPrevented);
|
|
259
|
+
// This node is subscribed to the other node and might not receive their heartbeat while debugging
|
|
260
|
+
// so this node should not have the responsability of evicting others when his own eviction is prevented
|
|
261
|
+
// when debugging.
|
|
262
|
+
// Otherwise when recovering from a debug session, all the other nodes will be evicted.
|
|
263
|
+
for (const subscriber of this.remoteNodes.values()) {
|
|
264
|
+
subscriber.handleNodePreventEviction({
|
|
265
|
+
evictionPrevented,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
259
268
|
}
|
|
260
269
|
|
|
261
270
|
/**
|
|
@@ -680,7 +680,15 @@ class ClusterSubscriber {
|
|
|
680
680
|
* to recover, otherwise we evict it from the cluster.
|
|
681
681
|
*/
|
|
682
682
|
async checkHeartbeat() {
|
|
683
|
-
if (this.
|
|
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
|
+
|
|
691
|
+
if (this.state === stateEnum.EVICTED) {
|
|
684
692
|
return;
|
|
685
693
|
}
|
|
686
694
|
|
|
@@ -29,6 +29,10 @@ class IDCardRenewer {
|
|
|
29
29
|
const redisConf = config.redis || {};
|
|
30
30
|
await this.initRedis(redisConf.config, redisConf.name);
|
|
31
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
|
+
);
|
|
32
36
|
this.parentPort.postMessage({
|
|
33
37
|
error: `Failed to connect to redis, could not refresh ID card: ${error.message}`,
|
|
34
38
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EmbeddedSDK } from "../shared/sdk/embeddedSdk";
|
|
2
|
-
import { EventDefinition, JSONObject } from "../../../index";
|
|
2
|
+
import { BackendSubscription, EventDefinition, JSONObject } from "../../../index";
|
|
3
3
|
import { BackendCluster, BackendConfig, BackendController, BackendHook, BackendImport, BackendPipe, BackendPlugin, BackendStorage, BackendVault, BackendOpenApi, InternalLogger, BackendErrors } from "./index";
|
|
4
4
|
export declare class Backend {
|
|
5
5
|
private _kuzzle;
|
|
@@ -121,6 +121,7 @@ export declare class Backend {
|
|
|
121
121
|
* Standard errors
|
|
122
122
|
*/
|
|
123
123
|
errors: BackendErrors;
|
|
124
|
+
subscription: BackendSubscription;
|
|
124
125
|
/**
|
|
125
126
|
* @deprecated
|
|
126
127
|
*
|
|
@@ -51,7 +51,8 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
51
51
|
const kuzzle_1 = __importDefault(require("../../kuzzle"));
|
|
52
52
|
const embeddedSdk_1 = require("../shared/sdk/embeddedSdk");
|
|
53
53
|
const kerror = __importStar(require("../../kerror"));
|
|
54
|
-
const index_1 = require("
|
|
54
|
+
const index_1 = require("../../../index");
|
|
55
|
+
const index_2 = require("./index");
|
|
55
56
|
const assertionError = kerror.wrap("plugin", "assert");
|
|
56
57
|
const runtimeError = kerror.wrap("plugin", "runtime");
|
|
57
58
|
let _app = null;
|
|
@@ -139,18 +140,19 @@ class Backend {
|
|
|
139
140
|
// Silent if no version can be found
|
|
140
141
|
}
|
|
141
142
|
global.app = this;
|
|
142
|
-
this.pipe = new
|
|
143
|
-
this.hook = new
|
|
144
|
-
this.config = new
|
|
145
|
-
this.vault = new
|
|
146
|
-
this.controller = new
|
|
147
|
-
this.plugin = new
|
|
148
|
-
this.storage = new
|
|
149
|
-
this.import = new
|
|
150
|
-
this.log = new
|
|
151
|
-
this.cluster = new
|
|
152
|
-
this.openApi = new
|
|
153
|
-
this.errors = new
|
|
143
|
+
this.pipe = new index_2.BackendPipe(this);
|
|
144
|
+
this.hook = new index_2.BackendHook(this);
|
|
145
|
+
this.config = new index_2.BackendConfig(this);
|
|
146
|
+
this.vault = new index_2.BackendVault(this);
|
|
147
|
+
this.controller = new index_2.BackendController(this);
|
|
148
|
+
this.plugin = new index_2.BackendPlugin(this);
|
|
149
|
+
this.storage = new index_2.BackendStorage(this);
|
|
150
|
+
this.import = new index_2.BackendImport(this);
|
|
151
|
+
this.log = new index_2.InternalLogger(this);
|
|
152
|
+
this.cluster = new index_2.BackendCluster();
|
|
153
|
+
this.openApi = new index_2.BackendOpenApi(this);
|
|
154
|
+
this.errors = new index_2.BackendErrors(this);
|
|
155
|
+
this.subscription = new index_1.BackendSubscription(this);
|
|
154
156
|
this.kerror = kerror;
|
|
155
157
|
try {
|
|
156
158
|
this.commit = this._readCommit();
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { JSONObject } from "kuzzle-sdk";
|
|
2
|
+
import { Connection } from "../../api/request";
|
|
3
|
+
import { ApplicationManager } from "./index";
|
|
4
|
+
export declare class BackendSubscription extends ApplicationManager {
|
|
5
|
+
/**
|
|
6
|
+
* Registers a new realtime subscription on the specified connection
|
|
7
|
+
*
|
|
8
|
+
* @param connection Connection to register the subscription on
|
|
9
|
+
* @param index Index name
|
|
10
|
+
* @param collection Collection name
|
|
11
|
+
* @param filters Subscription filters
|
|
12
|
+
* @param options.volatile Volatile data
|
|
13
|
+
* @param options.scope Subscription scope
|
|
14
|
+
* @param options.users Option for users notifications
|
|
15
|
+
*/
|
|
16
|
+
add(connection: Connection, index: string, collection: string, filters: JSONObject, { volatile, scope, users, }?: {
|
|
17
|
+
volatile?: JSONObject;
|
|
18
|
+
scope?: "in" | "out" | "all" | "none";
|
|
19
|
+
users?: "in" | "out" | "all" | "none";
|
|
20
|
+
}): Promise<{
|
|
21
|
+
roomId: string;
|
|
22
|
+
channel: string;
|
|
23
|
+
}>;
|
|
24
|
+
remove(connection: Connection, roomId: string): Promise<void>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Kuzzle, a backend software, self-hostable and ready to use
|
|
4
|
+
* to power modern apps
|
|
5
|
+
*
|
|
6
|
+
* Copyright 2015-2022 Kuzzle
|
|
7
|
+
* mailto: support AT kuzzle.io
|
|
8
|
+
* website: http://kuzzle.io
|
|
9
|
+
*
|
|
10
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
11
|
+
* you may not use this file except in compliance with the License.
|
|
12
|
+
* You may obtain a copy of the License at
|
|
13
|
+
*
|
|
14
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
15
|
+
*
|
|
16
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
17
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
18
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
19
|
+
* See the License for the specific language governing permissions and
|
|
20
|
+
* limitations under the License.
|
|
21
|
+
*/
|
|
22
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
25
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
26
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
27
|
+
}
|
|
28
|
+
Object.defineProperty(o, k2, desc);
|
|
29
|
+
}) : (function(o, m, k, k2) {
|
|
30
|
+
if (k2 === undefined) k2 = k;
|
|
31
|
+
o[k2] = m[k];
|
|
32
|
+
}));
|
|
33
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
34
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
35
|
+
}) : function(o, v) {
|
|
36
|
+
o["default"] = v;
|
|
37
|
+
});
|
|
38
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.BackendSubscription = void 0;
|
|
47
|
+
const request_1 = require("../../api/request");
|
|
48
|
+
const kerror = __importStar(require("../../kerror"));
|
|
49
|
+
const index_1 = require("./index");
|
|
50
|
+
const runtimeError = kerror.wrap("plugin", "runtime");
|
|
51
|
+
class BackendSubscription extends index_1.ApplicationManager {
|
|
52
|
+
/**
|
|
53
|
+
* Registers a new realtime subscription on the specified connection
|
|
54
|
+
*
|
|
55
|
+
* @param connection Connection to register the subscription on
|
|
56
|
+
* @param index Index name
|
|
57
|
+
* @param collection Collection name
|
|
58
|
+
* @param filters Subscription filters
|
|
59
|
+
* @param options.volatile Volatile data
|
|
60
|
+
* @param options.scope Subscription scope
|
|
61
|
+
* @param options.users Option for users notifications
|
|
62
|
+
*/
|
|
63
|
+
async add(connection, index, collection, filters, { volatile, scope, users, } = {}) {
|
|
64
|
+
if (!this._application.started) {
|
|
65
|
+
throw runtimeError.get("unavailable_before_start", "subscriptions.add");
|
|
66
|
+
}
|
|
67
|
+
const subscriptionRequest = new request_1.KuzzleRequest({
|
|
68
|
+
action: "subscribe",
|
|
69
|
+
body: filters,
|
|
70
|
+
collection,
|
|
71
|
+
controller: "realtime",
|
|
72
|
+
index,
|
|
73
|
+
scope,
|
|
74
|
+
users,
|
|
75
|
+
}, {
|
|
76
|
+
connectionId: connection.id,
|
|
77
|
+
volatile,
|
|
78
|
+
});
|
|
79
|
+
const { channel, roomId } = await global.kuzzle.ask("core:realtime:subscribe", subscriptionRequest);
|
|
80
|
+
return { channel, roomId };
|
|
81
|
+
}
|
|
82
|
+
async remove(connection, roomId) {
|
|
83
|
+
if (!this._application.started) {
|
|
84
|
+
throw runtimeError.get("unavailable_before_start", "subscriptions.remove");
|
|
85
|
+
}
|
|
86
|
+
await global.kuzzle.ask("core:realtime:unsubscribe", connection.id, roomId);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.BackendSubscription = BackendSubscription;
|
|
90
|
+
//# sourceMappingURL=backendSubscription.js.map
|
|
@@ -28,4 +28,5 @@ __exportStar(require("./backendVault"), exports);
|
|
|
28
28
|
__exportStar(require("./backendOpenApi"), exports);
|
|
29
29
|
__exportStar(require("./internalLogger"), exports);
|
|
30
30
|
__exportStar(require("./backendErrors"), exports);
|
|
31
|
+
__exportStar(require("./backendSubscription"), exports);
|
|
31
32
|
//# sourceMappingURL=index.js.map
|
|
@@ -29,7 +29,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
29
29
|
exports.KuzzleDebugger = void 0;
|
|
30
30
|
const inspector_1 = __importDefault(require("inspector"));
|
|
31
31
|
const kerror = __importStar(require("../../kerror"));
|
|
32
|
-
const get_1 = __importDefault(require("lodash/get"));
|
|
33
32
|
const DEBUGGER_EVENT = "kuzzle-debugger-event";
|
|
34
33
|
class KuzzleDebugger {
|
|
35
34
|
constructor() {
|
|
@@ -40,6 +39,7 @@ class KuzzleDebugger {
|
|
|
40
39
|
this.events = new Map();
|
|
41
40
|
}
|
|
42
41
|
async init() {
|
|
42
|
+
this.httpWsProtocol = global.kuzzle.entryPoint.protocols.get("websocket");
|
|
43
43
|
this.inspector = new inspector_1.default.Session();
|
|
44
44
|
// Remove connection id from the list of listeners for each event
|
|
45
45
|
global.kuzzle.on("connection:remove", (connectionId) => {
|
|
@@ -100,6 +100,17 @@ class KuzzleDebugger {
|
|
|
100
100
|
this.inspector.disconnect();
|
|
101
101
|
this.debuggerStatus = false;
|
|
102
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
|
+
}
|
|
103
114
|
this.events.clear();
|
|
104
115
|
}
|
|
105
116
|
/**
|
|
@@ -110,16 +121,33 @@ class KuzzleDebugger {
|
|
|
110
121
|
if (!this.debuggerStatus) {
|
|
111
122
|
throw kerror.get("core", "debugger", "not_enabled");
|
|
112
123
|
}
|
|
113
|
-
|
|
114
|
-
throw kerror.get("core", "debugger", "native_debug_protocol_usage_denied");
|
|
115
|
-
}
|
|
116
|
-
// Always disable report progress because this params causes a segfault.
|
|
124
|
+
// Always disable report progress because this parameter causes a segfault.
|
|
117
125
|
// The reason this happens is because the inspector is running inside the same thread
|
|
118
|
-
// as the Kuzzle Process and reportProgress forces the inspector to
|
|
119
|
-
//
|
|
120
|
-
// to be executed as the HeapProfiler is running, which causes a segfault.
|
|
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.
|
|
121
128
|
// See: https://github.com/nodejs/node/issues/44634
|
|
122
|
-
params.reportProgress
|
|
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
|
+
}
|
|
123
151
|
return this.inspectorPost(method, params);
|
|
124
152
|
}
|
|
125
153
|
/**
|
|
@@ -130,6 +158,17 @@ class KuzzleDebugger {
|
|
|
130
158
|
if (!this.debuggerStatus) {
|
|
131
159
|
throw kerror.get("core", "debugger", "not_enabled");
|
|
132
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
|
+
}
|
|
133
172
|
let listeners = this.events.get(event);
|
|
134
173
|
if (!listeners) {
|
|
135
174
|
listeners = new Set();
|
|
@@ -148,6 +187,28 @@ class KuzzleDebugger {
|
|
|
148
187
|
if (listeners) {
|
|
149
188
|
listeners.delete(connectionId);
|
|
150
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
|
+
}
|
|
151
212
|
}
|
|
152
213
|
/**
|
|
153
214
|
* Execute a method using the Chrome Debug Protocol
|
|
@@ -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
|
}
|
package/lib/kuzzle/kuzzle.js
CHANGED
|
@@ -148,7 +148,6 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
|
|
|
148
148
|
regExpEngine: this.config.realtime.pcreSupport ? "js" : "re2",
|
|
149
149
|
seed: this.config.internal.hash.seed,
|
|
150
150
|
});
|
|
151
|
-
await this.debugger.init();
|
|
152
151
|
await new cacheEngine_1.default().init();
|
|
153
152
|
await new storageEngine_1.default().init();
|
|
154
153
|
await new realtime_1.default().init();
|
|
@@ -167,6 +166,7 @@ class Kuzzle extends kuzzleEventEmitter_1.default {
|
|
|
167
166
|
// must be initialized before plugins to allow API requests from plugins
|
|
168
167
|
// before opening connections to external users
|
|
169
168
|
await this.entryPoint.init();
|
|
169
|
+
await this.debugger.init();
|
|
170
170
|
this.pluginsManager.application = application;
|
|
171
171
|
const pluginImports = await this.pluginsManager.init(options.plugins);
|
|
172
172
|
this.log.info(`[✔] Successfully loaded ${this.pluginsManager.loadedPlugins.length} plugins: ${this.pluginsManager.loadedPlugins.join(", ")}`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kuzzle",
|
|
3
3
|
"author": "The Kuzzle Team <support@kuzzle.io>",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.22.0",
|
|
5
5
|
"description": "Kuzzle is an open-source solution that handles all the data management through a secured API, with a large choice of protocols.",
|
|
6
6
|
"bin": "bin/start-kuzzle-server",
|
|
7
7
|
"scripts": {
|