kuzzle 2.16.11 → 2.17.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/adminController.js +3 -3
- package/lib/api/controllers/authController.js +11 -11
- package/lib/api/controllers/baseController.js +60 -3
- package/lib/api/controllers/clusterController.js +1 -1
- package/lib/api/controllers/collectionController.js +7 -5
- package/lib/api/controllers/documentController.js +130 -17
- package/lib/api/controllers/indexController.js +1 -1
- package/lib/api/controllers/memoryStorageController.js +39 -38
- package/lib/api/controllers/realtimeController.js +1 -1
- package/lib/api/controllers/securityController.js +49 -49
- package/lib/api/controllers/serverController.js +73 -27
- package/lib/api/documentExtractor.js +3 -3
- package/lib/api/funnel.js +40 -21
- package/lib/api/httpRoutes.js +9 -4
- package/lib/api/openapi/OpenApiManager.d.ts +11 -0
- package/lib/api/openapi/OpenApiManager.js +96 -0
- package/lib/api/openapi/{document → components/document}/count.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/create.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/createOrReplace.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/delete.yaml +1 -1
- package/lib/api/openapi/{document → components/document}/deleteByQuery.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/exists.yaml +1 -1
- package/lib/api/openapi/{document → components/document}/get.yaml +1 -1
- package/lib/api/openapi/{document → components/document}/index.d.ts +2 -0
- package/lib/api/openapi/{document → components/document}/index.js +7 -2
- package/lib/api/openapi/{document → components/document}/replace.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/scroll.yaml +1 -1
- package/lib/api/openapi/{document → components/document}/update.yaml +2 -2
- package/lib/api/openapi/components/document/validate.yaml +42 -0
- package/lib/api/openapi/components/index.d.ts +2 -0
- package/lib/api/openapi/components/index.js +18 -0
- package/lib/api/openapi/{payloads.yaml → components/payloads.yaml} +0 -0
- package/lib/api/openapi/index.d.ts +1 -2
- package/lib/api/openapi/index.js +1 -5
- package/lib/api/openapi/openApiGenerator.d.ts +7 -0
- package/lib/api/openapi/openApiGenerator.js +133 -0
- package/lib/api/request/kuzzleRequest.js +4 -0
- package/lib/cluster/node.js +9 -9
- package/lib/cluster/publisher.js +1 -1
- package/lib/cluster/subscriber.js +1 -1
- package/lib/cluster/workers/IDCardRenewer.js +2 -2
- package/lib/config/default.config.js +1 -0
- package/lib/config/index.js +6 -6
- package/lib/core/auth/passportResponse.js +6 -6
- package/lib/core/auth/passportWrapper.js +5 -5
- package/lib/core/backend/backend.d.ts +5 -1
- package/lib/core/backend/backend.js +12 -8
- package/lib/core/backend/backendConfig.d.ts +5 -1
- package/lib/core/backend/backendConfig.js +4 -0
- package/lib/core/backend/backendOpenApi.d.ts +9 -0
- package/lib/core/backend/backendOpenApi.js +69 -0
- package/lib/core/backend/index.d.ts +1 -0
- package/lib/core/backend/index.js +1 -0
- package/lib/core/network/accessLogger.js +6 -6
- package/lib/core/network/clientConnection.js +1 -1
- package/lib/core/network/entryPoint.js +5 -5
- package/lib/core/network/httpRouter/index.js +5 -5
- package/lib/core/network/httpRouter/routeHandler.js +3 -3
- package/lib/core/network/httpRouter/routePart.js +5 -5
- package/lib/core/network/protocolManifest.js +1 -1
- package/lib/core/network/protocols/httpMessage.js +2 -2
- package/lib/core/network/protocols/httpwsProtocol.js +205 -48
- package/lib/core/network/protocols/mqttProtocol.js +3 -3
- package/lib/core/network/protocols/protocol.js +3 -3
- package/lib/core/network/router.js +7 -6
- package/lib/core/plugin/plugin.js +38 -64
- package/lib/core/plugin/pluginManifest.js +3 -3
- package/lib/core/plugin/pluginRepository.js +5 -5
- package/lib/core/plugin/pluginsManager.js +29 -28
- package/lib/core/realtime/notification/server.js +1 -1
- package/lib/core/realtime/notification/user.js +1 -1
- package/lib/core/realtime/notifier.js +5 -5
- package/lib/core/security/index.js +1 -1
- package/lib/core/security/profileRepository.d.ts +176 -0
- package/lib/core/security/profileRepository.js +426 -443
- package/lib/core/security/roleRepository.js +16 -16
- package/lib/core/security/securityLoader.js +2 -2
- package/lib/core/security/tokenRepository.js +11 -11
- package/lib/core/security/userRepository.js +8 -8
- package/lib/core/shared/abstractManifest.js +4 -4
- package/lib/core/shared/repository.js +5 -5
- package/lib/core/shared/sdk/funnelProtocol.js +1 -1
- package/lib/core/shared/sdk/impersonatedSdk.js +1 -1
- package/lib/core/shared/store.js +30 -23
- package/lib/core/statistics/statistics.js +17 -17
- package/lib/core/storage/clientAdapter.js +45 -10
- package/lib/core/validation/baseType.js +5 -5
- package/lib/core/validation/types/anything.js +1 -1
- package/lib/core/validation/types/boolean.js +2 -2
- package/lib/core/validation/types/date.js +9 -9
- package/lib/core/validation/types/email.js +5 -5
- package/lib/core/validation/types/enum.js +6 -6
- package/lib/core/validation/types/geoPoint.js +2 -2
- package/lib/core/validation/types/geoShape.js +28 -25
- package/lib/core/validation/types/integer.js +4 -4
- package/lib/core/validation/types/ipAddress.js +7 -6
- package/lib/core/validation/types/numeric.js +4 -4
- package/lib/core/validation/types/object.js +5 -5
- package/lib/core/validation/types/string.js +5 -5
- package/lib/core/validation/types/url.js +7 -6
- package/lib/core/validation/validation.js +95 -84
- package/lib/kerror/codes/1-services.json +12 -0
- package/lib/kerror/codes/2-api.json +12 -0
- package/lib/kerror/codes/3-network.json +12 -0
- package/lib/kerror/codes/4-plugin.json +6 -0
- package/lib/kerror/codes/index.js +11 -11
- package/lib/kerror/index.js +1 -1
- package/lib/kuzzle/dumpGenerator.js +3 -3
- package/lib/kuzzle/event/kuzzleEventEmitter.js +4 -4
- package/lib/kuzzle/event/pipeRunner.js +1 -1
- package/lib/kuzzle/event/waterfall.js +6 -6
- package/lib/kuzzle/kuzzle.js +36 -5
- package/lib/kuzzle/log.js +3 -3
- package/lib/kuzzle/vault.js +3 -3
- package/lib/model/security/profile.d.ts +54 -0
- package/lib/model/security/profile.js +174 -233
- package/lib/model/security/rights.js +1 -1
- package/lib/model/security/role.d.ts +40 -0
- package/lib/model/security/role.js +159 -191
- package/lib/model/security/user.d.ts +29 -0
- package/lib/model/security/user.js +84 -52
- package/lib/model/storage/apiKey.js +2 -2
- package/lib/model/storage/baseModel.js +3 -3
- package/lib/service/cache/redis.js +7 -7
- package/lib/service/storage/elasticsearch.js +152 -90
- package/lib/service/storage/esWrapper.js +2 -3
- package/lib/types/ControllerDefinition.d.ts +3 -3
- package/lib/types/ControllerRights.d.ts +22 -0
- package/lib/types/ControllerRights.js +23 -0
- package/lib/types/HttpStream.d.ts +32 -0
- package/lib/types/HttpStream.js +70 -0
- package/lib/types/OpenApiDefinition.d.ts +43 -0
- package/lib/types/{config/StorageService/StorageServiceElasticsearchConfiguration.js → OpenApiDefinition.js} +1 -1
- package/lib/types/Policy.d.ts +25 -0
- package/lib/types/{InternalLogger.js → Policy.js} +2 -2
- package/lib/types/PolicyRestrictions.d.ts +21 -0
- package/lib/types/PolicyRestrictions.js +23 -0
- package/lib/types/Target.d.ts +15 -0
- package/lib/types/Target.js +23 -0
- package/lib/types/config/KuzzleConfiguration.d.ts +4 -0
- package/lib/types/config/ServicesConfiguration.d.ts +2 -2
- package/lib/types/config/{StorageService/StorageServiceElasticsearchConfiguration.d.ts → storageEngine/StorageEngineElasticsearchConfiguration.d.ts} +10 -3
- package/lib/types/config/storageEngine/StorageEngineElasticsearchConfiguration.js +3 -0
- package/lib/types/index.d.ts +7 -1
- package/lib/types/index.js +7 -1
- package/lib/util/array.d.ts +11 -0
- package/lib/util/array.js +57 -0
- package/lib/util/assertType.js +6 -6
- package/lib/util/bufferedPassThrough.d.ts +76 -0
- package/lib/util/bufferedPassThrough.js +161 -0
- package/lib/util/deprecate.js +7 -5
- package/lib/util/didYouMean.js +1 -1
- package/lib/util/dump-collection.d.ts +3 -0
- package/lib/util/dump-collection.js +265 -0
- package/lib/util/extractFields.js +2 -2
- package/lib/util/inflector.d.ts +8 -0
- package/lib/util/inflector.js +16 -0
- package/lib/util/requestAssertions.js +7 -7
- package/lib/util/wildcard.js +55 -0
- package/package-lock.json +538 -78
- package/package.json +5 -3
- package/lib/api/openApiGenerator.d.ts +0 -7
- package/lib/api/openApiGenerator.js +0 -197
- package/lib/types/InternalLogger.d.ts +0 -25
|
@@ -27,6 +27,7 @@ const zlib = require('zlib');
|
|
|
27
27
|
|
|
28
28
|
const uWS = require('uWebSockets.js');
|
|
29
29
|
|
|
30
|
+
const { HttpStream } = require('../../../types');
|
|
30
31
|
const { Request } = require('../../../api/request');
|
|
31
32
|
const { KuzzleError } = require('../../../kerror/errors');
|
|
32
33
|
const Protocol = require('./protocol');
|
|
@@ -80,6 +81,8 @@ const HTTP_HEADER_CONNECTION = Buffer.from('Connection');
|
|
|
80
81
|
const HTTP_HEADER_CONTENT_LENGTH = Buffer.from('Content-Length');
|
|
81
82
|
const HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = Buffer.from('Access-Control-Allow-Origin');
|
|
82
83
|
const HTTP_HEADER_VARY = Buffer.from('Vary');
|
|
84
|
+
const HTTP_HEADER_TRANSFER_ENCODING = Buffer.from('Transfer-Encoding');
|
|
85
|
+
const CHUNKED = Buffer.from('chunked');
|
|
83
86
|
const WILDCARD = Buffer.from('*');
|
|
84
87
|
const ORIGIN = Buffer.from('Origin');
|
|
85
88
|
const CLOSE = Buffer.from('close');
|
|
@@ -121,7 +124,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
121
124
|
this.wsConfig = this.parseWebSocketOptions();
|
|
122
125
|
this.httpConfig = this.parseHttpOptions();
|
|
123
126
|
|
|
124
|
-
if (!this.wsConfig.enabled && !this.httpConfig.enabled) {
|
|
127
|
+
if (! this.wsConfig.enabled && ! this.httpConfig.enabled) {
|
|
125
128
|
return false;
|
|
126
129
|
}
|
|
127
130
|
|
|
@@ -137,7 +140,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
this.server.listen(entrypoint.config.port, socket => {
|
|
140
|
-
if (!socket) {
|
|
143
|
+
if (! socket) {
|
|
141
144
|
throw new Error(`[http/websocket] fatal: unable to listen to port ${entrypoint.config.port}`);
|
|
142
145
|
}
|
|
143
146
|
});
|
|
@@ -199,7 +202,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
199
202
|
const socket = this.socketByConnectionId.get(data.connectionId);
|
|
200
203
|
debugWS('notify: %a', data);
|
|
201
204
|
|
|
202
|
-
if (!socket) {
|
|
205
|
+
if (! socket) {
|
|
203
206
|
return;
|
|
204
207
|
}
|
|
205
208
|
|
|
@@ -214,7 +217,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
214
217
|
joinChannel (channel, connectionId) {
|
|
215
218
|
const socket = this.socketByConnectionId.get(connectionId);
|
|
216
219
|
|
|
217
|
-
if (!socket) {
|
|
220
|
+
if (! socket) {
|
|
218
221
|
return;
|
|
219
222
|
}
|
|
220
223
|
|
|
@@ -225,7 +228,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
225
228
|
leaveChannel (channel, connectionId) {
|
|
226
229
|
const socket = this.socketByConnectionId.get(connectionId);
|
|
227
230
|
|
|
228
|
-
if (!socket) {
|
|
231
|
+
if (! socket) {
|
|
229
232
|
return;
|
|
230
233
|
}
|
|
231
234
|
|
|
@@ -239,7 +242,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
239
242
|
|
|
240
243
|
const socket = this.socketByConnectionId.get(connectionId);
|
|
241
244
|
|
|
242
|
-
if (!socket) {
|
|
245
|
+
if (! socket) {
|
|
243
246
|
return;
|
|
244
247
|
}
|
|
245
248
|
|
|
@@ -262,7 +265,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
262
265
|
|
|
263
266
|
wsOnOpenHandler (socket) {
|
|
264
267
|
const ip = Buffer.from(socket.getRemoteAddressAsText()).toString();
|
|
265
|
-
const connection = new ClientConnection(this.name, [ip], {cookie: socket.cookie});
|
|
268
|
+
const connection = new ClientConnection(this.name, [ip], { cookie: socket.cookie });
|
|
266
269
|
|
|
267
270
|
this.entryPoint.newConnection(connection);
|
|
268
271
|
this.connectionBySocket.set(socket, connection);
|
|
@@ -273,7 +276,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
273
276
|
wsOnCloseHandler (socket, code, message) {
|
|
274
277
|
const connection = this.connectionBySocket.get(socket);
|
|
275
278
|
|
|
276
|
-
if (!connection) {
|
|
279
|
+
if (! connection) {
|
|
277
280
|
return;
|
|
278
281
|
}
|
|
279
282
|
|
|
@@ -291,7 +294,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
291
294
|
}
|
|
292
295
|
|
|
293
296
|
wsOnMessageHandler (socket, data) {
|
|
294
|
-
if (!data || data.byteLength === 0) {
|
|
297
|
+
if (! data || data.byteLength === 0) {
|
|
295
298
|
return;
|
|
296
299
|
}
|
|
297
300
|
|
|
@@ -405,7 +408,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
405
408
|
* @param {Buffer} payload
|
|
406
409
|
*/
|
|
407
410
|
wsSend (socket, payload) {
|
|
408
|
-
if (!this.connectionBySocket.has(socket)) {
|
|
411
|
+
if (! this.connectionBySocket.has(socket)) {
|
|
409
412
|
return;
|
|
410
413
|
}
|
|
411
414
|
|
|
@@ -444,7 +447,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
444
447
|
const contentType = message.headers['content-type'];
|
|
445
448
|
|
|
446
449
|
if ( contentType
|
|
447
|
-
&& !HTTP_ALLOWED_CONTENT_TYPES.some(allowed => contentType.includes(allowed))
|
|
450
|
+
&& ! HTTP_ALLOWED_CONTENT_TYPES.some(allowed => contentType.includes(allowed))
|
|
448
451
|
) {
|
|
449
452
|
this.httpSendError(
|
|
450
453
|
message,
|
|
@@ -501,7 +504,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
501
504
|
return;
|
|
502
505
|
}
|
|
503
506
|
|
|
504
|
-
if (!isLast) {
|
|
507
|
+
if (! isLast) {
|
|
505
508
|
return;
|
|
506
509
|
}
|
|
507
510
|
|
|
@@ -584,6 +587,13 @@ class HttpWsProtocol extends Protocol {
|
|
|
584
587
|
return;
|
|
585
588
|
}
|
|
586
589
|
|
|
590
|
+
if (request.result instanceof HttpStream) {
|
|
591
|
+
this.httpSendStream(request, response, request.result, message);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Send the response in one go
|
|
596
|
+
|
|
587
597
|
const data = this.httpRequestToResponse(request, message);
|
|
588
598
|
|
|
589
599
|
this.httpCompress(message, data, result => {
|
|
@@ -594,38 +604,13 @@ class HttpWsProtocol extends Protocol {
|
|
|
594
604
|
request.response.setHeader('Content-Encoding', result.encoding);
|
|
595
605
|
|
|
596
606
|
response.cork(() => {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
response.writeHeader(HTTP_HEADER_CONNECTION, CLOSE);
|
|
600
|
-
|
|
601
|
-
for (const header of this.httpConfig.headers) {
|
|
602
|
-
// If header is missing, add the default one
|
|
603
|
-
if (request.response.headers[header[2]] === undefined) {
|
|
604
|
-
response.writeHeader(header[0], header[1]);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// Access-Control-Allow-Origin Logic
|
|
609
|
-
if ( request.response.headers['Access-Control-Allow-Origin'] === undefined
|
|
610
|
-
&& message.headers
|
|
611
|
-
&& message.headers.origin
|
|
612
|
-
) {
|
|
613
|
-
response.writeHeader(
|
|
614
|
-
HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
|
615
|
-
Buffer.from(message.headers.origin));
|
|
616
|
-
|
|
617
|
-
response.writeHeader(HTTP_HEADER_VARY, ORIGIN);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
for (const [key, value] of Object.entries(request.response.headers)) {
|
|
621
|
-
response.writeHeader(Buffer.from(key), Buffer.from(value.toString()));
|
|
622
|
-
}
|
|
607
|
+
this.httpWriteRequestHeaders(request, response, message);
|
|
623
608
|
|
|
624
609
|
const [ success ] = response.tryEnd(
|
|
625
610
|
result.compressed,
|
|
626
611
|
result.compressed.length);
|
|
627
612
|
|
|
628
|
-
if (!success) {
|
|
613
|
+
if (! success) {
|
|
629
614
|
response.onWritable(offset => {
|
|
630
615
|
const retryData = result.compressed.subarray(offset);
|
|
631
616
|
const [ retrySuccess ] = response.tryEnd(
|
|
@@ -640,6 +625,177 @@ class HttpWsProtocol extends Protocol {
|
|
|
640
625
|
});
|
|
641
626
|
}
|
|
642
627
|
|
|
628
|
+
/**
|
|
629
|
+
*
|
|
630
|
+
* @param {uWS.HttpRequest} request
|
|
631
|
+
* @param {uWS.HttpResponse} response
|
|
632
|
+
* @param {HttpMessage} message
|
|
633
|
+
*/
|
|
634
|
+
httpWriteRequestHeaders (request, response, message) {
|
|
635
|
+
response.writeStatus(Buffer.from(request.response.status.toString()));
|
|
636
|
+
|
|
637
|
+
response.writeHeader(HTTP_HEADER_CONNECTION, CLOSE);
|
|
638
|
+
|
|
639
|
+
for (const header of this.httpConfig.headers) {
|
|
640
|
+
// If header is missing, add the default one
|
|
641
|
+
if (request.response.headers[header[2]] === undefined) {
|
|
642
|
+
response.writeHeader(header[0], header[1]);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// Access-Control-Allow-Origin Logic
|
|
647
|
+
if ( request.response.headers['Access-Control-Allow-Origin'] === undefined
|
|
648
|
+
&& message.headers
|
|
649
|
+
&& message.headers.origin
|
|
650
|
+
) {
|
|
651
|
+
response.writeHeader(
|
|
652
|
+
HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
|
|
653
|
+
Buffer.from(message.headers.origin));
|
|
654
|
+
|
|
655
|
+
response.writeHeader(HTTP_HEADER_VARY, ORIGIN);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
for (const [key, value] of Object.entries(request.response.headers)) {
|
|
659
|
+
response.writeHeader(Buffer.from(key), Buffer.from(value.toString()));
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Send stream data thorugh the HTTP response
|
|
665
|
+
* @param {HttpRequest} request
|
|
666
|
+
* @param {HttpStream} httpStream - The data stream to send
|
|
667
|
+
* @param {uWS.HttpResponse} response
|
|
668
|
+
* @param {HttpMessage} message
|
|
669
|
+
*/
|
|
670
|
+
httpSendStream (request, response, httpStream, message) {
|
|
671
|
+
const streamSizeFixed = typeof httpStream.totalBytes === 'number' && httpStream.totalBytes > 0;
|
|
672
|
+
|
|
673
|
+
if (httpStream.errored) {
|
|
674
|
+
this.httpSendError(
|
|
675
|
+
message,
|
|
676
|
+
response,
|
|
677
|
+
kerror.get('network', 'http', 'stream_errored', httpStream.error.message)
|
|
678
|
+
);
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
if (httpStream.destroyed) {
|
|
683
|
+
this.httpSendError(
|
|
684
|
+
message,
|
|
685
|
+
response,
|
|
686
|
+
kerror.get('network', 'http', 'stream_closed')
|
|
687
|
+
);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// Remove Content-Length header to avoid conflic with Transfer-Encoding header
|
|
692
|
+
request.response.setHeader('Content-Length', null);
|
|
693
|
+
|
|
694
|
+
// Send Headers in one go
|
|
695
|
+
response.cork(() => {
|
|
696
|
+
this.httpWriteRequestHeaders(request, response, message);
|
|
697
|
+
|
|
698
|
+
if (streamSizeFixed) {
|
|
699
|
+
response.writeHeader(HTTP_HEADER_CONTENT_LENGTH, Buffer.from(httpStream.totalBytes.toString()));
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
response.writeHeader(HTTP_HEADER_TRANSFER_ENCODING, CHUNKED);
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
httpStream.stream.on('data', chunk => {
|
|
707
|
+
// Make a copy of the array buffer
|
|
708
|
+
const arrayBuffer = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
|
|
709
|
+
|
|
710
|
+
const arrayBufferOffset = response.getWriteOffset();
|
|
711
|
+
|
|
712
|
+
let backpressure = false;
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Switch method of writing data to the response
|
|
716
|
+
* based on the size of the stream.
|
|
717
|
+
* If the stream has a fixed size we can use the tryEnd method
|
|
718
|
+
*/
|
|
719
|
+
if (streamSizeFixed) {
|
|
720
|
+
const [ success, done ] = response.tryEnd(arrayBuffer, httpStream.totalBytes);
|
|
721
|
+
backpressure = ! success;
|
|
722
|
+
|
|
723
|
+
if (done) {
|
|
724
|
+
httpStream.destroy();
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
const success = response.write(arrayBuffer);
|
|
730
|
+
|
|
731
|
+
backpressure = ! success;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// In case of backpressure we need to wait for drainage before sending more data
|
|
735
|
+
if (backpressure) {
|
|
736
|
+
response.arrayBufferOffset = arrayBufferOffset;
|
|
737
|
+
response.arrayBuffer = arrayBuffer;
|
|
738
|
+
httpStream.stream.pause();
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* When the stream is drained we can resume sending data,
|
|
742
|
+
* only if the is no backpressure after we wrote the missing chunk data.
|
|
743
|
+
*/
|
|
744
|
+
response.onWritable(offset => {
|
|
745
|
+
if (! streamSizeFixed && offset - response.arrayBufferOffset === 0) {
|
|
746
|
+
httpStream.stream.resume();
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
let retryBackpressure = false;
|
|
751
|
+
const remainingChunkData = response.arrayBuffer.slice(offset - response.arrayBufferOffset);
|
|
752
|
+
|
|
753
|
+
if (streamSizeFixed) {
|
|
754
|
+
const [ success ] = response.tryEnd(remainingChunkData, httpStream.totalBytes);
|
|
755
|
+
|
|
756
|
+
retryBackpressure = ! success;
|
|
757
|
+
}
|
|
758
|
+
else {
|
|
759
|
+
const success = response.write(remainingChunkData);
|
|
760
|
+
|
|
761
|
+
retryBackpressure = ! success;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (! retryBackpressure) {
|
|
765
|
+
httpStream.stream.resume();
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return ! retryBackpressure;
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
httpStream.stream.on('end', () => {
|
|
774
|
+
if (httpStream.destroy()) {
|
|
775
|
+
response.end();
|
|
776
|
+
}
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
httpStream.stream.on('close', () => {
|
|
780
|
+
if (httpStream.destroy()) {
|
|
781
|
+
response.end();
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
httpStream.stream.on('error', err => {
|
|
786
|
+
if (httpStream.destroy()) {
|
|
787
|
+
response.end();
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
debugHTTP('[%s] httpSendStream: %s', httpStream.connection.id, err.message);
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
response.onAborted(() => {
|
|
794
|
+
httpStream.destroy();
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
}
|
|
798
|
+
|
|
643
799
|
/**
|
|
644
800
|
* Forward an error response to the client
|
|
645
801
|
*
|
|
@@ -680,7 +836,8 @@ class HttpWsProtocol extends Protocol {
|
|
|
680
836
|
if (message.headers && message.headers.origin) {
|
|
681
837
|
if (global.kuzzle.config.internal.allowAllOrigins) {
|
|
682
838
|
response.writeHeader(HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, WILDCARD);
|
|
683
|
-
}
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
684
841
|
response.writeHeader(HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, Buffer.from(message.headers.origin));
|
|
685
842
|
response.writeHeader(HTTP_HEADER_VARY, ORIGIN);
|
|
686
843
|
}
|
|
@@ -703,13 +860,13 @@ class HttpWsProtocol extends Protocol {
|
|
|
703
860
|
* @param {HttpMessage} message
|
|
704
861
|
* @returns {Buffer}
|
|
705
862
|
*/
|
|
706
|
-
httpRequestToResponse(request, message) {
|
|
863
|
+
httpRequestToResponse (request, message) {
|
|
707
864
|
let data = removeErrorStack(request.response.toJSON());
|
|
708
865
|
|
|
709
866
|
if (message.requestId !== data.requestId) {
|
|
710
867
|
data.requestId = message.requestId;
|
|
711
868
|
|
|
712
|
-
if (!data.raw) {
|
|
869
|
+
if (! data.raw) {
|
|
713
870
|
data.content.requestId = message.requestId;
|
|
714
871
|
}
|
|
715
872
|
}
|
|
@@ -763,7 +920,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
763
920
|
* @param {Buffer} data
|
|
764
921
|
* @param {Function} callback
|
|
765
922
|
*/
|
|
766
|
-
httpCompress(message, data, callback) {
|
|
923
|
+
httpCompress (message, data, callback) {
|
|
767
924
|
if (message.headers['accept-encoding']) {
|
|
768
925
|
const encodings = message.headers['accept-encoding']
|
|
769
926
|
.split(',')
|
|
@@ -792,15 +949,15 @@ class HttpWsProtocol extends Protocol {
|
|
|
792
949
|
|
|
793
950
|
if (algorithm === 'gzip') {
|
|
794
951
|
zlib.gzip(data, (err, compressed) => callback({
|
|
795
|
-
compressed: !err ? compressed : data,
|
|
796
|
-
encoding: !err ? 'gzip' : 'identity',
|
|
952
|
+
compressed: ! err ? compressed : data,
|
|
953
|
+
encoding: ! err ? 'gzip' : 'identity',
|
|
797
954
|
}));
|
|
798
955
|
return;
|
|
799
956
|
}
|
|
800
957
|
else if (algorithm === 'deflate') {
|
|
801
958
|
zlib.deflate(data, (err, compressed) => callback({
|
|
802
|
-
compressed: !err ? compressed : data,
|
|
803
|
-
encoding: !err ? 'deflate' : 'identity',
|
|
959
|
+
compressed: ! err ? compressed : data,
|
|
960
|
+
encoding: ! err ? 'deflate' : 'identity',
|
|
804
961
|
}));
|
|
805
962
|
return;
|
|
806
963
|
}
|
|
@@ -814,7 +971,7 @@ class HttpWsProtocol extends Protocol {
|
|
|
814
971
|
|
|
815
972
|
httpUncompress (message, payload, cb) {
|
|
816
973
|
let encodings = message.headers['content-encoding'];
|
|
817
|
-
if (!encodings) {
|
|
974
|
+
if (! encodings) {
|
|
818
975
|
cb(null, payload);
|
|
819
976
|
return;
|
|
820
977
|
}
|
|
@@ -99,7 +99,7 @@ class MqttProtocol extends Protocol {
|
|
|
99
99
|
const payload = JSON.stringify(data.payload);
|
|
100
100
|
|
|
101
101
|
for (const channel of data.channels) {
|
|
102
|
-
this.aedes.publish({payload, topic: channel}, this.publishCallback);
|
|
102
|
+
this.aedes.publish({ payload, topic: channel }, this.publishCallback);
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -126,14 +126,14 @@ class MqttProtocol extends Protocol {
|
|
|
126
126
|
|
|
127
127
|
const client = this.connectionsById.get(data.connectionId);
|
|
128
128
|
|
|
129
|
-
if (!client) {
|
|
129
|
+
if (! client) {
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
const payload = Buffer.from(JSON.stringify(data.payload));
|
|
134
134
|
|
|
135
135
|
data.channels.forEach(topic => {
|
|
136
|
-
client.publish({payload, topic}, this.publishCallback);
|
|
136
|
+
client.publish({ payload, topic }, this.publishCallback);
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
139
|
|
|
@@ -46,7 +46,7 @@ class Protocol {
|
|
|
46
46
|
*
|
|
47
47
|
* @returns {Promise<boolean>}
|
|
48
48
|
*/
|
|
49
|
-
async init(name, entryPoint) {
|
|
49
|
+
async init (name, entryPoint) {
|
|
50
50
|
this.entryPoint = entryPoint;
|
|
51
51
|
|
|
52
52
|
// name should be passed in the constructor
|
|
@@ -83,12 +83,12 @@ class Protocol {
|
|
|
83
83
|
|
|
84
84
|
joinChannel (channel, connectionId) {
|
|
85
85
|
// do nothing by default
|
|
86
|
-
return {channel, connectionId};
|
|
86
|
+
return { channel, connectionId };
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
leaveChannel (channel, connectionId) {
|
|
90
90
|
// do nothing by default
|
|
91
|
-
return {channel, connectionId};
|
|
91
|
+
return { channel, connectionId };
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
notify () {
|
|
@@ -31,7 +31,7 @@ const HttpRouter = require('./httpRouter');
|
|
|
31
31
|
* @param {Kuzzle} kuzzle
|
|
32
32
|
*/
|
|
33
33
|
class Router {
|
|
34
|
-
constructor() {
|
|
34
|
+
constructor () {
|
|
35
35
|
this.connections = new Map();
|
|
36
36
|
this.http = new HttpRouter();
|
|
37
37
|
}
|
|
@@ -42,13 +42,14 @@ class Router {
|
|
|
42
42
|
* @param {RequestContext} requestContext
|
|
43
43
|
*/
|
|
44
44
|
newConnection (requestContext) {
|
|
45
|
-
if (!requestContext.connection.id || !requestContext.connection.protocol) {
|
|
45
|
+
if (! requestContext.connection.id || ! requestContext.connection.protocol) {
|
|
46
46
|
global.kuzzle.log.error(kerror.get(
|
|
47
47
|
'protocol',
|
|
48
48
|
'runtime',
|
|
49
49
|
'invalid_connection',
|
|
50
50
|
JSON.stringify(requestContext)));
|
|
51
|
-
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
52
53
|
this.connections.set(requestContext.connection.id, requestContext);
|
|
53
54
|
global.kuzzle.statistics.newConnection(requestContext);
|
|
54
55
|
}
|
|
@@ -62,7 +63,7 @@ class Router {
|
|
|
62
63
|
removeConnection (requestContext) {
|
|
63
64
|
const connId = requestContext.connection.id;
|
|
64
65
|
|
|
65
|
-
if (!connId || !requestContext.connection.protocol) {
|
|
66
|
+
if (! connId || ! requestContext.connection.protocol) {
|
|
66
67
|
global.kuzzle.log.error(kerror.get(
|
|
67
68
|
'protocol',
|
|
68
69
|
'runtime',
|
|
@@ -71,7 +72,7 @@ class Router {
|
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
if (!this.connections.has(connId)) {
|
|
75
|
+
if (! this.connections.has(connId)) {
|
|
75
76
|
global.kuzzle.log.error(kerror.get(
|
|
76
77
|
'protocol',
|
|
77
78
|
'runtime',
|
|
@@ -131,7 +132,7 @@ class Router {
|
|
|
131
132
|
request.input.action = route.action;
|
|
132
133
|
|
|
133
134
|
if (route.deprecated) {
|
|
134
|
-
const { deprecated
|
|
135
|
+
const { deprecated: { since, message } } = route;
|
|
135
136
|
request.addDeprecation(since, message);
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
const path = require('path');
|
|
25
25
|
const fs = require('fs');
|
|
26
26
|
const semver = require('semver');
|
|
27
|
-
const enforcer = require('openapi-enforcer');
|
|
28
27
|
|
|
29
28
|
const { PluginContext } = require('./pluginContext');
|
|
30
29
|
const PrivilegedPluginContext = require('./privilegedContext');
|
|
@@ -34,7 +33,6 @@ const Manifest = require('./pluginManifest');
|
|
|
34
33
|
const { has, isPlainObject } = require('../../util/safeObject');
|
|
35
34
|
|
|
36
35
|
const assertionError = kerror.wrap('plugin', 'assert');
|
|
37
|
-
const controllerError = kerror.wrap('plugin', 'controller');
|
|
38
36
|
const runtimeError = kerror.wrap('plugin', 'runtime');
|
|
39
37
|
|
|
40
38
|
const PLUGIN_NAME_REGEX = /^[a-z-\d]+$/;
|
|
@@ -43,7 +41,7 @@ const HTTP_VERBS = ['get', 'head', 'post', 'put', 'delete', 'patch', 'options'];
|
|
|
43
41
|
class Plugin {
|
|
44
42
|
constructor (
|
|
45
43
|
instance,
|
|
46
|
-
{ name, application=false, deprecationWarning=true } = {}
|
|
44
|
+
{ name, application = false, deprecationWarning = true } = {}
|
|
47
45
|
) {
|
|
48
46
|
this._instance = instance;
|
|
49
47
|
|
|
@@ -154,24 +152,46 @@ class Plugin {
|
|
|
154
152
|
|
|
155
153
|
// getters/setters ===========================================================
|
|
156
154
|
|
|
157
|
-
get instance () {
|
|
155
|
+
get instance () {
|
|
156
|
+
return this._instance;
|
|
157
|
+
}
|
|
158
158
|
|
|
159
|
-
get context () {
|
|
159
|
+
get context () {
|
|
160
|
+
return this._context;
|
|
161
|
+
}
|
|
160
162
|
|
|
161
|
-
get application () {
|
|
163
|
+
get application () {
|
|
164
|
+
return this._application;
|
|
165
|
+
}
|
|
162
166
|
|
|
163
|
-
get logPrefix () {
|
|
167
|
+
get logPrefix () {
|
|
168
|
+
return `[${this.name}]`;
|
|
169
|
+
}
|
|
164
170
|
|
|
165
|
-
get config () {
|
|
166
|
-
|
|
171
|
+
get config () {
|
|
172
|
+
return this._config;
|
|
173
|
+
}
|
|
174
|
+
set config (config) {
|
|
175
|
+
this._config = config;
|
|
176
|
+
}
|
|
167
177
|
|
|
168
|
-
get version () {
|
|
169
|
-
|
|
178
|
+
get version () {
|
|
179
|
+
return this._version;
|
|
180
|
+
}
|
|
181
|
+
set version (version) {
|
|
182
|
+
this._version = version;
|
|
183
|
+
}
|
|
170
184
|
|
|
171
|
-
get deprecationWarning () {
|
|
172
|
-
|
|
185
|
+
get deprecationWarning () {
|
|
186
|
+
return this._deprecationWarning;
|
|
187
|
+
}
|
|
188
|
+
set deprecationWarning (value) {
|
|
189
|
+
this._deprecationWarning = value;
|
|
190
|
+
}
|
|
173
191
|
|
|
174
|
-
get name () {
|
|
192
|
+
get name () {
|
|
193
|
+
return this._name;
|
|
194
|
+
}
|
|
175
195
|
set name (name) {
|
|
176
196
|
if (! this.constructor.checkName(name)) {
|
|
177
197
|
this.printDeprecation('Plugin names should be in kebab-case. This behavior will be enforced in futur versions of Kuzzle.');
|
|
@@ -180,7 +200,9 @@ class Plugin {
|
|
|
180
200
|
this._name = name;
|
|
181
201
|
}
|
|
182
202
|
|
|
183
|
-
get manifest () {
|
|
203
|
+
get manifest () {
|
|
204
|
+
return this._manifest;
|
|
205
|
+
}
|
|
184
206
|
set manifest (manifest) {
|
|
185
207
|
this._manifest = manifest;
|
|
186
208
|
}
|
|
@@ -264,7 +286,7 @@ class Plugin {
|
|
|
264
286
|
return PLUGIN_NAME_REGEX.test(name);
|
|
265
287
|
}
|
|
266
288
|
|
|
267
|
-
static
|
|
289
|
+
static checkControllerDefinition (name, definition, { application = false } = {}) {
|
|
268
290
|
if (typeof name !== 'string') {
|
|
269
291
|
throw assertionError.get(
|
|
270
292
|
'invalid_controller_definition',
|
|
@@ -342,10 +364,6 @@ class Plugin {
|
|
|
342
364
|
name,
|
|
343
365
|
`action "${action}" has invalid http properties: ${routeProperties.join(', ')}`);
|
|
344
366
|
}
|
|
345
|
-
|
|
346
|
-
if (route.openapi) {
|
|
347
|
-
await checkOpenAPISpecification(name, action, route);
|
|
348
|
-
}
|
|
349
367
|
}
|
|
350
368
|
}
|
|
351
369
|
}
|
|
@@ -365,48 +383,4 @@ function checkHttpRouteProperties (route, action, name, application) {
|
|
|
365
383
|
}
|
|
366
384
|
}
|
|
367
385
|
|
|
368
|
-
async function checkOpenAPISpecification (controller, action, route) {
|
|
369
|
-
if (! isPlainObject(route.openapi)) {
|
|
370
|
-
throw assertionError.get(
|
|
371
|
-
'invalid_controller_definition',
|
|
372
|
-
controller,
|
|
373
|
-
'The "openapi" property of the route definition must be an object');
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const routePath = route.path.charAt(0) === '/'
|
|
377
|
-
? route.path
|
|
378
|
-
: `/_/${route.path}`;
|
|
379
|
-
|
|
380
|
-
// Set :param notation to {param}
|
|
381
|
-
const formattedPath = routePath.replace(/\/:([^/]*)/g,'/{$1}');
|
|
382
|
-
|
|
383
|
-
const { error, warning } = await enforcer({
|
|
384
|
-
/* eslint-disable sort-keys */
|
|
385
|
-
openapi: '3.0.0',
|
|
386
|
-
info: {
|
|
387
|
-
title: 'Kuzzle API',
|
|
388
|
-
version: require('../../../package').version
|
|
389
|
-
},
|
|
390
|
-
paths: {
|
|
391
|
-
[formattedPath]: {
|
|
392
|
-
[route.verb]: route.openapi
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
/* eslint-enable sort-keys */
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
if (warning) {
|
|
399
|
-
global.kuzzle.log.warn(`Warning for OpenAPI specification in "${controller}:${action}", ${route.openapi} : ${warning}`);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
if (error) {
|
|
403
|
-
throw controllerError.get(
|
|
404
|
-
'invalid_openapi_schema',
|
|
405
|
-
controller,
|
|
406
|
-
action,
|
|
407
|
-
route.openapi,
|
|
408
|
-
error);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
|
|
412
386
|
module.exports = Plugin;
|
|
@@ -27,7 +27,7 @@ const kerror = require('../../kerror').wrap('plugin', 'manifest');
|
|
|
27
27
|
const AbstractManifest = require('../shared/abstractManifest');
|
|
28
28
|
|
|
29
29
|
class PluginManifest extends AbstractManifest {
|
|
30
|
-
constructor(pluginPath) {
|
|
30
|
+
constructor (pluginPath) {
|
|
31
31
|
super(pluginPath);
|
|
32
32
|
this.privileged = false;
|
|
33
33
|
}
|
|
@@ -36,11 +36,11 @@ class PluginManifest extends AbstractManifest {
|
|
|
36
36
|
super.load();
|
|
37
37
|
|
|
38
38
|
// Ensure ES will accept the plugin name as index
|
|
39
|
-
if (
|
|
39
|
+
if (! /^[\w-]+$/.test(this.name)) {
|
|
40
40
|
throw kerror.get('invalid_name', this.path);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
if (!_.isNil(this.raw) && !_.isNil(this.raw.privileged)) {
|
|
43
|
+
if (! _.isNil(this.raw) && ! _.isNil(this.raw.privileged)) {
|
|
44
44
|
if (typeof this.raw.privileged !== 'boolean') {
|
|
45
45
|
throw kerror.get(
|
|
46
46
|
'invalid_privileged',
|