kuzzle 2.16.8 → 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.
Files changed (167) hide show
  1. package/README.md +11 -0
  2. package/lib/api/controllers/adminController.js +7 -6
  3. package/lib/api/controllers/authController.js +11 -11
  4. package/lib/api/controllers/baseController.js +60 -3
  5. package/lib/api/controllers/clusterController.js +1 -1
  6. package/lib/api/controllers/collectionController.js +7 -5
  7. package/lib/api/controllers/documentController.js +130 -17
  8. package/lib/api/controllers/indexController.js +1 -1
  9. package/lib/api/controllers/memoryStorageController.js +39 -38
  10. package/lib/api/controllers/realtimeController.js +1 -1
  11. package/lib/api/controllers/securityController.js +49 -49
  12. package/lib/api/controllers/serverController.js +73 -27
  13. package/lib/api/documentExtractor.js +3 -3
  14. package/lib/api/funnel.js +40 -21
  15. package/lib/api/httpRoutes.js +9 -4
  16. package/lib/api/openapi/OpenApiManager.d.ts +11 -0
  17. package/lib/api/openapi/OpenApiManager.js +96 -0
  18. package/lib/api/openapi/{document → components/document}/count.yaml +2 -2
  19. package/lib/api/openapi/{document → components/document}/create.yaml +2 -2
  20. package/lib/api/openapi/{document → components/document}/createOrReplace.yaml +2 -2
  21. package/lib/api/openapi/{document → components/document}/delete.yaml +1 -1
  22. package/lib/api/openapi/{document → components/document}/deleteByQuery.yaml +2 -2
  23. package/lib/api/openapi/{document → components/document}/exists.yaml +1 -1
  24. package/lib/api/openapi/{document → components/document}/get.yaml +1 -1
  25. package/lib/api/openapi/{document → components/document}/index.d.ts +2 -0
  26. package/lib/api/openapi/{document → components/document}/index.js +7 -2
  27. package/lib/api/openapi/{document → components/document}/replace.yaml +2 -2
  28. package/lib/api/openapi/{document → components/document}/scroll.yaml +1 -1
  29. package/lib/api/openapi/{document → components/document}/update.yaml +2 -2
  30. package/lib/api/openapi/components/document/validate.yaml +42 -0
  31. package/lib/api/openapi/components/index.d.ts +2 -0
  32. package/lib/api/openapi/components/index.js +18 -0
  33. package/lib/api/openapi/{payloads.yaml → components/payloads.yaml} +0 -0
  34. package/lib/api/openapi/index.d.ts +1 -2
  35. package/lib/api/openapi/index.js +1 -5
  36. package/lib/api/openapi/openApiGenerator.d.ts +7 -0
  37. package/lib/api/openapi/openApiGenerator.js +133 -0
  38. package/lib/api/request/kuzzleRequest.d.ts +11 -11
  39. package/lib/api/request/kuzzleRequest.js +38 -48
  40. package/lib/cluster/node.js +9 -9
  41. package/lib/cluster/publisher.js +1 -1
  42. package/lib/cluster/subscriber.js +1 -1
  43. package/lib/cluster/workers/IDCardRenewer.js +13 -4
  44. package/lib/config/default.config.js +1 -0
  45. package/lib/config/index.js +6 -6
  46. package/lib/core/auth/passportResponse.js +6 -6
  47. package/lib/core/auth/passportWrapper.js +5 -5
  48. package/lib/core/backend/backend.d.ts +5 -1
  49. package/lib/core/backend/backend.js +12 -8
  50. package/lib/core/backend/backendConfig.d.ts +5 -1
  51. package/lib/core/backend/backendConfig.js +4 -0
  52. package/lib/core/backend/backendOpenApi.d.ts +9 -0
  53. package/lib/core/backend/backendOpenApi.js +69 -0
  54. package/lib/core/backend/index.d.ts +1 -0
  55. package/lib/core/backend/index.js +1 -0
  56. package/lib/core/network/accessLogger.js +6 -6
  57. package/lib/core/network/clientConnection.js +1 -1
  58. package/lib/core/network/entryPoint.js +5 -5
  59. package/lib/core/network/httpRouter/index.js +5 -5
  60. package/lib/core/network/httpRouter/routeHandler.js +3 -3
  61. package/lib/core/network/httpRouter/routePart.js +5 -5
  62. package/lib/core/network/protocolManifest.js +1 -1
  63. package/lib/core/network/protocols/httpMessage.js +2 -2
  64. package/lib/core/network/protocols/httpwsProtocol.js +207 -46
  65. package/lib/core/network/protocols/mqttProtocol.js +3 -3
  66. package/lib/core/network/protocols/protocol.js +3 -3
  67. package/lib/core/network/router.js +7 -6
  68. package/lib/core/plugin/plugin.js +38 -64
  69. package/lib/core/plugin/pluginContext.d.ts +10 -1
  70. package/lib/core/plugin/pluginContext.js +2 -0
  71. package/lib/core/plugin/pluginManifest.js +3 -3
  72. package/lib/core/plugin/pluginRepository.js +5 -5
  73. package/lib/core/plugin/pluginsManager.js +29 -28
  74. package/lib/core/realtime/notification/server.js +1 -1
  75. package/lib/core/realtime/notification/user.js +1 -1
  76. package/lib/core/realtime/notifier.js +5 -5
  77. package/lib/core/security/index.js +1 -1
  78. package/lib/core/security/profileRepository.d.ts +176 -0
  79. package/lib/core/security/profileRepository.js +426 -443
  80. package/lib/core/security/roleRepository.js +16 -16
  81. package/lib/core/security/securityLoader.js +3 -3
  82. package/lib/core/security/tokenRepository.js +18 -21
  83. package/lib/core/security/userRepository.js +8 -8
  84. package/lib/core/shared/abstractManifest.js +4 -4
  85. package/lib/core/shared/repository.js +6 -6
  86. package/lib/core/shared/sdk/funnelProtocol.js +1 -1
  87. package/lib/core/shared/sdk/impersonatedSdk.js +1 -1
  88. package/lib/core/shared/store.js +30 -23
  89. package/lib/core/statistics/statistics.js +17 -17
  90. package/lib/core/storage/clientAdapter.js +45 -10
  91. package/lib/core/validation/baseType.js +5 -5
  92. package/lib/core/validation/types/anything.js +1 -1
  93. package/lib/core/validation/types/boolean.js +2 -2
  94. package/lib/core/validation/types/date.js +9 -9
  95. package/lib/core/validation/types/email.js +5 -5
  96. package/lib/core/validation/types/enum.js +6 -6
  97. package/lib/core/validation/types/geoPoint.js +2 -2
  98. package/lib/core/validation/types/geoShape.js +28 -25
  99. package/lib/core/validation/types/integer.js +4 -4
  100. package/lib/core/validation/types/ipAddress.js +7 -6
  101. package/lib/core/validation/types/numeric.js +4 -4
  102. package/lib/core/validation/types/object.js +5 -5
  103. package/lib/core/validation/types/string.js +5 -5
  104. package/lib/core/validation/types/url.js +7 -6
  105. package/lib/core/validation/validation.js +95 -84
  106. package/lib/kerror/codes/1-services.json +12 -0
  107. package/lib/kerror/codes/2-api.json +12 -0
  108. package/lib/kerror/codes/3-network.json +12 -0
  109. package/lib/kerror/codes/4-plugin.json +6 -0
  110. package/lib/kerror/codes/index.js +11 -11
  111. package/lib/kerror/index.js +1 -1
  112. package/lib/kuzzle/dumpGenerator.js +3 -3
  113. package/lib/kuzzle/event/kuzzleEventEmitter.js +4 -4
  114. package/lib/kuzzle/event/pipeRunner.js +1 -1
  115. package/lib/kuzzle/event/waterfall.js +6 -6
  116. package/lib/kuzzle/kuzzle.js +36 -5
  117. package/lib/kuzzle/log.js +3 -3
  118. package/lib/kuzzle/vault.js +3 -3
  119. package/lib/model/security/profile.d.ts +54 -0
  120. package/lib/model/security/profile.js +174 -233
  121. package/lib/model/security/rights.js +1 -1
  122. package/lib/model/security/role.d.ts +40 -0
  123. package/lib/model/security/role.js +159 -191
  124. package/lib/model/security/user.d.ts +29 -0
  125. package/lib/model/security/user.js +84 -52
  126. package/lib/model/storage/apiKey.js +2 -2
  127. package/lib/model/storage/baseModel.js +3 -3
  128. package/lib/service/cache/redis.js +7 -7
  129. package/lib/service/storage/elasticsearch.js +152 -90
  130. package/lib/service/storage/esWrapper.js +2 -3
  131. package/lib/types/ControllerDefinition.d.ts +3 -3
  132. package/lib/types/ControllerRights.d.ts +22 -0
  133. package/lib/types/ControllerRights.js +23 -0
  134. package/lib/types/HttpStream.d.ts +32 -0
  135. package/lib/types/HttpStream.js +70 -0
  136. package/lib/types/OpenApiDefinition.d.ts +43 -0
  137. package/lib/types/{config/StorageService/StorageServiceElasticsearchConfiguration.js → OpenApiDefinition.js} +1 -1
  138. package/lib/types/Policy.d.ts +25 -0
  139. package/lib/types/Policy.js +23 -0
  140. package/lib/types/PolicyRestrictions.d.ts +21 -0
  141. package/lib/types/PolicyRestrictions.js +23 -0
  142. package/lib/types/Target.d.ts +15 -0
  143. package/lib/types/Target.js +23 -0
  144. package/lib/types/config/KuzzleConfiguration.d.ts +4 -0
  145. package/lib/types/config/ServicesConfiguration.d.ts +2 -2
  146. package/lib/types/config/{StorageService/StorageServiceElasticsearchConfiguration.d.ts → storageEngine/StorageEngineElasticsearchConfiguration.d.ts} +10 -3
  147. package/lib/types/config/storageEngine/StorageEngineElasticsearchConfiguration.js +3 -0
  148. package/lib/types/index.d.ts +7 -1
  149. package/lib/types/index.js +7 -1
  150. package/lib/util/array.d.ts +11 -0
  151. package/lib/util/array.js +57 -0
  152. package/lib/util/assertType.js +6 -6
  153. package/lib/util/bufferedPassThrough.d.ts +76 -0
  154. package/lib/util/bufferedPassThrough.js +161 -0
  155. package/lib/util/deprecate.js +7 -5
  156. package/lib/util/didYouMean.js +1 -1
  157. package/lib/util/dump-collection.d.ts +3 -0
  158. package/lib/util/dump-collection.js +265 -0
  159. package/lib/util/extractFields.js +2 -2
  160. package/lib/util/inflector.d.ts +8 -0
  161. package/lib/util/inflector.js +16 -0
  162. package/lib/util/requestAssertions.js +7 -7
  163. package/lib/util/wildcard.js +55 -0
  164. package/package-lock.json +881 -431
  165. package/package.json +23 -20
  166. package/lib/api/openApiGenerator.d.ts +0 -7
  167. package/lib/api/openApiGenerator.js +0 -197
@@ -32,7 +32,7 @@ const { has } = require('../../../util/safeObject');
32
32
  * @class RoutePart
33
33
  */
34
34
  class RoutePart {
35
- constructor() {
35
+ constructor () {
36
36
  this.subparts = {};
37
37
  this.placeholders = null;
38
38
 
@@ -45,7 +45,7 @@ class RoutePart {
45
45
  * @param {string} part
46
46
  * @returns {boolean}
47
47
  */
48
- exists(part) {
48
+ exists (part) {
49
49
  return this.subparts[part] !== undefined && this.subparts[part].handler !== null;
50
50
  }
51
51
 
@@ -56,8 +56,8 @@ class RoutePart {
56
56
  * @param {string} part
57
57
  * @returns {RoutePart}
58
58
  */
59
- getNext(part) {
60
- if (!has(this.subparts, part)) {
59
+ getNext (part) {
60
+ if (! has(this.subparts, part)) {
61
61
  this.subparts[part] = new RoutePart();
62
62
  }
63
63
 
@@ -71,7 +71,7 @@ class RoutePart {
71
71
  * @param {HttpMessage} message
72
72
  * @returns {RouteHandler} registered function handler
73
73
  */
74
- getHandler(message) {
74
+ getHandler (message) {
75
75
  // Do not use WHATWG API yet, stick with the legacy (and deprecated) URL
76
76
  // There are two issues:
77
77
  // - Heavy performance impact: https://github.com/nodejs/node/issues/30334
@@ -24,7 +24,7 @@
24
24
  const AbstractManifest = require('../shared/abstractManifest');
25
25
 
26
26
  class ProtocolManifest extends AbstractManifest {
27
- constructor(protocolPath, protocol) {
27
+ constructor (protocolPath, protocol) {
28
28
  super(protocolPath);
29
29
 
30
30
  this.protocol = protocol;
@@ -29,7 +29,7 @@ class HttpMessage {
29
29
  * @param {ClientConnection} connection
30
30
  * @param {uWS.HttpRequest} request
31
31
  */
32
- constructor(connection, request) {
32
+ constructor (connection, request) {
33
33
  this.connection = connection;
34
34
  this._content = null;
35
35
  this.ips = connection.ips;
@@ -54,7 +54,7 @@ class HttpMessage {
54
54
  }
55
55
 
56
56
  set content (value) {
57
- if (!value || value.length === 0) {
57
+ if (! value || value.length === 0) {
58
58
  this._content = null;
59
59
  }
60
60
  else {
@@ -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');
@@ -76,11 +77,15 @@ const HTTP_ALLOWED_CONTENT_TYPES = [
76
77
  'application/x-www-form-urlencoded',
77
78
  'multipart/form-data',
78
79
  ];
80
+ const HTTP_HEADER_CONNECTION = Buffer.from('Connection');
79
81
  const HTTP_HEADER_CONTENT_LENGTH = Buffer.from('Content-Length');
80
82
  const HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = Buffer.from('Access-Control-Allow-Origin');
81
83
  const HTTP_HEADER_VARY = Buffer.from('Vary');
84
+ const HTTP_HEADER_TRANSFER_ENCODING = Buffer.from('Transfer-Encoding');
85
+ const CHUNKED = Buffer.from('chunked');
82
86
  const WILDCARD = Buffer.from('*');
83
87
  const ORIGIN = Buffer.from('Origin');
88
+ const CLOSE = Buffer.from('close');
84
89
  const CHARSET_REGEX = /charset=([\w-]+)/i;
85
90
 
86
91
  /**
@@ -119,7 +124,7 @@ class HttpWsProtocol extends Protocol {
119
124
  this.wsConfig = this.parseWebSocketOptions();
120
125
  this.httpConfig = this.parseHttpOptions();
121
126
 
122
- if (!this.wsConfig.enabled && !this.httpConfig.enabled) {
127
+ if (! this.wsConfig.enabled && ! this.httpConfig.enabled) {
123
128
  return false;
124
129
  }
125
130
 
@@ -135,7 +140,7 @@ class HttpWsProtocol extends Protocol {
135
140
  }
136
141
 
137
142
  this.server.listen(entrypoint.config.port, socket => {
138
- if (!socket) {
143
+ if (! socket) {
139
144
  throw new Error(`[http/websocket] fatal: unable to listen to port ${entrypoint.config.port}`);
140
145
  }
141
146
  });
@@ -197,7 +202,7 @@ class HttpWsProtocol extends Protocol {
197
202
  const socket = this.socketByConnectionId.get(data.connectionId);
198
203
  debugWS('notify: %a', data);
199
204
 
200
- if (!socket) {
205
+ if (! socket) {
201
206
  return;
202
207
  }
203
208
 
@@ -212,7 +217,7 @@ class HttpWsProtocol extends Protocol {
212
217
  joinChannel (channel, connectionId) {
213
218
  const socket = this.socketByConnectionId.get(connectionId);
214
219
 
215
- if (!socket) {
220
+ if (! socket) {
216
221
  return;
217
222
  }
218
223
 
@@ -223,7 +228,7 @@ class HttpWsProtocol extends Protocol {
223
228
  leaveChannel (channel, connectionId) {
224
229
  const socket = this.socketByConnectionId.get(connectionId);
225
230
 
226
- if (!socket) {
231
+ if (! socket) {
227
232
  return;
228
233
  }
229
234
 
@@ -237,7 +242,7 @@ class HttpWsProtocol extends Protocol {
237
242
 
238
243
  const socket = this.socketByConnectionId.get(connectionId);
239
244
 
240
- if (!socket) {
245
+ if (! socket) {
241
246
  return;
242
247
  }
243
248
 
@@ -260,7 +265,7 @@ class HttpWsProtocol extends Protocol {
260
265
 
261
266
  wsOnOpenHandler (socket) {
262
267
  const ip = Buffer.from(socket.getRemoteAddressAsText()).toString();
263
- const connection = new ClientConnection(this.name, [ip], {cookie: socket.cookie});
268
+ const connection = new ClientConnection(this.name, [ip], { cookie: socket.cookie });
264
269
 
265
270
  this.entryPoint.newConnection(connection);
266
271
  this.connectionBySocket.set(socket, connection);
@@ -271,7 +276,7 @@ class HttpWsProtocol extends Protocol {
271
276
  wsOnCloseHandler (socket, code, message) {
272
277
  const connection = this.connectionBySocket.get(socket);
273
278
 
274
- if (!connection) {
279
+ if (! connection) {
275
280
  return;
276
281
  }
277
282
 
@@ -289,7 +294,7 @@ class HttpWsProtocol extends Protocol {
289
294
  }
290
295
 
291
296
  wsOnMessageHandler (socket, data) {
292
- if (!data || data.byteLength === 0) {
297
+ if (! data || data.byteLength === 0) {
293
298
  return;
294
299
  }
295
300
 
@@ -403,7 +408,7 @@ class HttpWsProtocol extends Protocol {
403
408
  * @param {Buffer} payload
404
409
  */
405
410
  wsSend (socket, payload) {
406
- if (!this.connectionBySocket.has(socket)) {
411
+ if (! this.connectionBySocket.has(socket)) {
407
412
  return;
408
413
  }
409
414
 
@@ -442,7 +447,7 @@ class HttpWsProtocol extends Protocol {
442
447
  const contentType = message.headers['content-type'];
443
448
 
444
449
  if ( contentType
445
- && !HTTP_ALLOWED_CONTENT_TYPES.some(allowed => contentType.includes(allowed))
450
+ && ! HTTP_ALLOWED_CONTENT_TYPES.some(allowed => contentType.includes(allowed))
446
451
  ) {
447
452
  this.httpSendError(
448
453
  message,
@@ -499,7 +504,7 @@ class HttpWsProtocol extends Protocol {
499
504
  return;
500
505
  }
501
506
 
502
- if (!isLast) {
507
+ if (! isLast) {
503
508
  return;
504
509
  }
505
510
 
@@ -582,6 +587,13 @@ class HttpWsProtocol extends Protocol {
582
587
  return;
583
588
  }
584
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
+
585
597
  const data = this.httpRequestToResponse(request, message);
586
598
 
587
599
  this.httpCompress(message, data, result => {
@@ -592,36 +604,13 @@ class HttpWsProtocol extends Protocol {
592
604
  request.response.setHeader('Content-Encoding', result.encoding);
593
605
 
594
606
  response.cork(() => {
595
- response.writeStatus(Buffer.from(request.response.status.toString()));
596
-
597
- for (const header of this.httpConfig.headers) {
598
- // If header is missing, add the default one
599
- if (request.response.headers[header[2]] === undefined) {
600
- response.writeHeader(header[0], header[1]);
601
- }
602
- }
603
-
604
- // Access-Control-Allow-Origin Logic
605
- if ( request.response.headers['Access-Control-Allow-Origin'] === undefined
606
- && message.headers
607
- && message.headers.origin
608
- ) {
609
- response.writeHeader(
610
- HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
611
- Buffer.from(message.headers.origin));
612
-
613
- response.writeHeader(HTTP_HEADER_VARY, ORIGIN);
614
- }
615
-
616
- for (const [key, value] of Object.entries(request.response.headers)) {
617
- response.writeHeader(Buffer.from(key), Buffer.from(value.toString()));
618
- }
607
+ this.httpWriteRequestHeaders(request, response, message);
619
608
 
620
609
  const [ success ] = response.tryEnd(
621
610
  result.compressed,
622
611
  result.compressed.length);
623
612
 
624
- if (!success) {
613
+ if (! success) {
625
614
  response.onWritable(offset => {
626
615
  const retryData = result.compressed.subarray(offset);
627
616
  const [ retrySuccess ] = response.tryEnd(
@@ -636,6 +625,177 @@ class HttpWsProtocol extends Protocol {
636
625
  });
637
626
  }
638
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
+
639
799
  /**
640
800
  * Forward an error response to the client
641
801
  *
@@ -676,7 +836,8 @@ class HttpWsProtocol extends Protocol {
676
836
  if (message.headers && message.headers.origin) {
677
837
  if (global.kuzzle.config.internal.allowAllOrigins) {
678
838
  response.writeHeader(HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, WILDCARD);
679
- } else {
839
+ }
840
+ else {
680
841
  response.writeHeader(HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, Buffer.from(message.headers.origin));
681
842
  response.writeHeader(HTTP_HEADER_VARY, ORIGIN);
682
843
  }
@@ -699,13 +860,13 @@ class HttpWsProtocol extends Protocol {
699
860
  * @param {HttpMessage} message
700
861
  * @returns {Buffer}
701
862
  */
702
- httpRequestToResponse(request, message) {
863
+ httpRequestToResponse (request, message) {
703
864
  let data = removeErrorStack(request.response.toJSON());
704
865
 
705
866
  if (message.requestId !== data.requestId) {
706
867
  data.requestId = message.requestId;
707
868
 
708
- if (!data.raw) {
869
+ if (! data.raw) {
709
870
  data.content.requestId = message.requestId;
710
871
  }
711
872
  }
@@ -759,7 +920,7 @@ class HttpWsProtocol extends Protocol {
759
920
  * @param {Buffer} data
760
921
  * @param {Function} callback
761
922
  */
762
- httpCompress(message, data, callback) {
923
+ httpCompress (message, data, callback) {
763
924
  if (message.headers['accept-encoding']) {
764
925
  const encodings = message.headers['accept-encoding']
765
926
  .split(',')
@@ -788,15 +949,15 @@ class HttpWsProtocol extends Protocol {
788
949
 
789
950
  if (algorithm === 'gzip') {
790
951
  zlib.gzip(data, (err, compressed) => callback({
791
- compressed: !err ? compressed : data,
792
- encoding: !err ? 'gzip' : 'identity',
952
+ compressed: ! err ? compressed : data,
953
+ encoding: ! err ? 'gzip' : 'identity',
793
954
  }));
794
955
  return;
795
956
  }
796
957
  else if (algorithm === 'deflate') {
797
958
  zlib.deflate(data, (err, compressed) => callback({
798
- compressed: !err ? compressed : data,
799
- encoding: !err ? 'deflate' : 'identity',
959
+ compressed: ! err ? compressed : data,
960
+ encoding: ! err ? 'deflate' : 'identity',
800
961
  }));
801
962
  return;
802
963
  }
@@ -810,7 +971,7 @@ class HttpWsProtocol extends Protocol {
810
971
 
811
972
  httpUncompress (message, payload, cb) {
812
973
  let encodings = message.headers['content-encoding'];
813
- if (!encodings) {
974
+ if (! encodings) {
814
975
  cb(null, payload);
815
976
  return;
816
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
- } else {
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 : { since, message } } = route;
135
+ const { deprecated: { since, message } } = route;
135
136
  request.addDeprecation(since, message);
136
137
  }
137
138