1
1
import Inspector from "inspector" ;
2
2
import * as kerror from "../../kerror" ;
3
3
import { JSONObject } from "kuzzle-sdk" ;
4
- import get from "lodash/get " ;
4
+ import HttpWsProtocol from "../../core/network/protocols/httpwsProtocol " ;
5
5
6
6
const DEBUGGER_EVENT = "kuzzle-debugger-event" ;
7
7
@@ -15,7 +15,11 @@ export class KuzzleDebugger {
15
15
*/
16
16
private events = new Map < string , Set < string > > ( ) ;
17
17
18
+ private httpWsProtocol ?: HttpWsProtocol ;
19
+
18
20
async init ( ) {
21
+ this . httpWsProtocol = global . kuzzle . entryPoint . protocols . get ( "websocket" ) ;
22
+
19
23
this . inspector = new Inspector . Session ( ) ;
20
24
21
25
// Remove connection id from the list of listeners for each event
@@ -97,6 +101,20 @@ export class KuzzleDebugger {
97
101
this . inspector . disconnect ( ) ;
98
102
this . debuggerStatus = false ;
99
103
await global . kuzzle . ask ( "cluster:node:preventEviction" , false ) ;
104
+
105
+ // Disable debug mode for all connected sockets that still have listeners
106
+ if ( this . httpWsProtocol ) {
107
+ for ( const eventName of this . events . keys ( ) ) {
108
+ for ( const connectionId of this . events . get ( eventName ) ) {
109
+ const socket =
110
+ this . httpWsProtocol . socketByConnectionId . get ( connectionId ) ;
111
+ if ( socket ) {
112
+ socket . internal . debugSession = false ;
113
+ }
114
+ }
115
+ }
116
+ }
117
+
100
118
this . events . clear ( ) ;
101
119
}
102
120
@@ -109,21 +127,33 @@ export class KuzzleDebugger {
109
127
throw kerror . get ( "core" , "debugger" , "not_enabled" ) ;
110
128
}
111
129
112
- if ( ! get ( global . kuzzle . config , "security.debug.native_debug_protocol" ) ) {
113
- throw kerror . get (
114
- "core" ,
115
- "debugger" ,
116
- "native_debug_protocol_usage_denied"
117
- ) ;
118
- }
119
-
120
- // Always disable report progress because this params causes a segfault.
130
+ // Always disable report progress because this parameter causes a segfault.
121
131
// The reason this happens is because the inspector is running inside the same thread
122
- // as the Kuzzle Process and reportProgress forces the inspector to send events
123
- // to the main thread, while it is being inspected by the HeapProfiler, which causes javascript code
124
- // to be executed as the HeapProfiler is running, which causes a segfault.
132
+ // as the Kuzzle Process and reportProgress forces the inspector to call function in the JS Heap
133
+ // while it is being inspected by the HeapProfiler, which causes a segfault.
125
134
// See: https://github.com/nodejs/node/issues/44634
126
- params . reportProgress = false ;
135
+ if ( params . reportProgress ) {
136
+ // We need to send a fake HeapProfiler.reportHeapSnapshotProgress event
137
+ // to the inspector to make Chrome think that the HeapProfiler is done
138
+ // otherwise, even though the Chrome Inspector did receive the whole snapshot, it will not be parsed.
139
+ //
140
+ // Chrome inspector is waiting for a HeapProfiler.reportHeapSnapshotProgress event with the finished property set to true
141
+ // The `done` and `total` properties are only used to show a progress bar, so there are not important.
142
+ // Sending this event before the HeapProfiler.addHeapSnapshotChunk event will not cause any problem,
143
+ // in fact, Chrome always do that when taking a snapshot, it receives the HeapProfiler.reportHeapSnapshotProgress event
144
+ // before the HeapProfiler.addHeapSnapshotChunk event.
145
+ // So this will have no impact and when receiving the HeapProfiler.addHeapSnapshotChunk event, Chrome will wait to receive
146
+ // a complete snapshot before parsing it if it has received the HeapProfiler.reportHeapSnapshotProgress event with the finished property set to true before.
147
+ this . inspector . emit ( "inspectorNotification" , {
148
+ method : "HeapProfiler.reportHeapSnapshotProgress" ,
149
+ params : {
150
+ done : 0 ,
151
+ finished : true ,
152
+ total : 0 ,
153
+ } ,
154
+ } ) ;
155
+ params . reportProgress = false ;
156
+ }
127
157
128
158
return this . inspectorPost ( method , params ) ;
129
159
}
@@ -137,6 +167,18 @@ export class KuzzleDebugger {
137
167
throw kerror . get ( "core" , "debugger" , "not_enabled" ) ;
138
168
}
139
169
170
+ if ( this . httpWsProtocol ) {
171
+ const socket = this . httpWsProtocol . socketByConnectionId . get ( connectionId ) ;
172
+ if ( socket ) {
173
+ /**
174
+ * Mark the socket as a debugging socket
175
+ * this will bypass some limitations like the max pressure buffer size,
176
+ * which could end the connection when the debugger is sending a lot of data.
177
+ */
178
+ socket . internal . debugSession = true ;
179
+ }
180
+ }
181
+
140
182
let listeners = this . events . get ( event ) ;
141
183
if ( ! listeners ) {
142
184
listeners = new Set ( ) ;
@@ -159,6 +201,32 @@ export class KuzzleDebugger {
159
201
if ( listeners ) {
160
202
listeners . delete ( connectionId ) ;
161
203
}
204
+
205
+ if ( ! this . httpWsProtocol ) {
206
+ return ;
207
+ }
208
+
209
+ const socket = this . httpWsProtocol . socketByConnectionId . get ( connectionId ) ;
210
+ if ( ! socket ) {
211
+ return ;
212
+ }
213
+
214
+ let removeDebugSessionMarker = true ;
215
+ /**
216
+ * If the connection doesn't listen to any other events
217
+ * we can remove the debugSession marker
218
+ */
219
+ for ( const eventName of this . events . keys ( ) ) {
220
+ const eventListener = this . events . get ( eventName ) ;
221
+ if ( eventListener && eventListener . has ( connectionId ) ) {
222
+ removeDebugSessionMarker = false ;
223
+ break ;
224
+ }
225
+ }
226
+
227
+ if ( removeDebugSessionMarker ) {
228
+ socket . internal . debugSession = false ;
229
+ }
162
230
}
163
231
164
232
/**
0 commit comments