kuzzle 2.16.11 → 2.17.2

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 (187) hide show
  1. package/lib/api/controllers/adminController.js +3 -3
  2. package/lib/api/controllers/authController.js +12 -12
  3. package/lib/api/controllers/baseController.js +60 -3
  4. package/lib/api/controllers/clusterController.js +1 -1
  5. package/lib/api/controllers/collectionController.js +7 -5
  6. package/lib/api/controllers/documentController.js +130 -17
  7. package/lib/api/controllers/indexController.js +1 -1
  8. package/lib/api/controllers/memoryStorageController.js +39 -38
  9. package/lib/api/controllers/realtimeController.js +1 -1
  10. package/lib/api/controllers/securityController.js +50 -50
  11. package/lib/api/controllers/serverController.js +73 -27
  12. package/lib/api/documentExtractor.js +3 -3
  13. package/lib/api/funnel.js +44 -21
  14. package/lib/api/httpRoutes.js +9 -4
  15. package/lib/api/openapi/OpenApiManager.d.ts +11 -0
  16. package/lib/api/openapi/OpenApiManager.js +96 -0
  17. package/lib/api/openapi/{document → components/document}/count.yaml +2 -2
  18. package/lib/api/openapi/{document → components/document}/create.yaml +2 -2
  19. package/lib/api/openapi/{document → components/document}/createOrReplace.yaml +2 -2
  20. package/lib/api/openapi/{document → components/document}/delete.yaml +1 -1
  21. package/lib/api/openapi/{document → components/document}/deleteByQuery.yaml +2 -2
  22. package/lib/api/openapi/{document → components/document}/exists.yaml +1 -1
  23. package/lib/api/openapi/{document → components/document}/get.yaml +1 -1
  24. package/lib/api/openapi/{document → components/document}/index.d.ts +2 -0
  25. package/lib/api/openapi/{document → components/document}/index.js +7 -2
  26. package/lib/api/openapi/{document → components/document}/replace.yaml +2 -2
  27. package/lib/api/openapi/{document → components/document}/scroll.yaml +1 -1
  28. package/lib/api/openapi/{document → components/document}/update.yaml +2 -2
  29. package/lib/api/openapi/components/document/validate.yaml +42 -0
  30. package/lib/api/openapi/components/index.d.ts +2 -0
  31. package/lib/api/openapi/components/index.js +18 -0
  32. package/lib/api/openapi/{payloads.yaml → components/payloads.yaml} +0 -0
  33. package/lib/api/openapi/index.d.ts +1 -2
  34. package/lib/api/openapi/index.js +1 -5
  35. package/lib/api/openapi/openApiGenerator.d.ts +7 -0
  36. package/lib/api/openapi/openApiGenerator.js +133 -0
  37. package/lib/api/request/kuzzleRequest.js +8 -6
  38. package/lib/cluster/node.js +9 -9
  39. package/lib/cluster/publisher.js +1 -1
  40. package/lib/cluster/state.js +20 -4
  41. package/lib/cluster/subscriber.js +1 -1
  42. package/lib/cluster/workers/IDCardRenewer.js +2 -2
  43. package/lib/config/default.config.js +1 -0
  44. package/lib/config/index.js +6 -6
  45. package/lib/core/auth/passportResponse.js +6 -6
  46. package/lib/core/auth/passportWrapper.js +5 -5
  47. package/lib/core/backend/backend.d.ts +11 -3
  48. package/lib/core/backend/backend.js +22 -17
  49. package/lib/core/backend/backendConfig.d.ts +5 -1
  50. package/lib/core/backend/backendConfig.js +25 -2
  51. package/lib/core/backend/backendController.js +21 -5
  52. package/lib/core/backend/backendErrors.d.ts +58 -0
  53. package/lib/core/backend/backendErrors.js +121 -0
  54. package/lib/core/backend/backendHook.js +21 -5
  55. package/lib/core/backend/backendImport.js +21 -5
  56. package/lib/core/backend/backendOpenApi.d.ts +9 -0
  57. package/lib/core/backend/backendOpenApi.js +69 -0
  58. package/lib/core/backend/backendPipe.js +21 -5
  59. package/lib/core/backend/backendPlugin.js +22 -3
  60. package/lib/core/backend/backendVault.js +21 -2
  61. package/lib/core/backend/index.d.ts +2 -0
  62. package/lib/core/backend/index.js +2 -0
  63. package/lib/core/network/accessLogger.js +6 -6
  64. package/lib/core/network/clientConnection.js +1 -1
  65. package/lib/core/network/entryPoint.js +5 -5
  66. package/lib/core/network/httpRouter/index.js +5 -5
  67. package/lib/core/network/httpRouter/routeHandler.js +3 -3
  68. package/lib/core/network/httpRouter/routePart.js +5 -5
  69. package/lib/core/network/protocolManifest.js +1 -1
  70. package/lib/core/network/protocols/httpMessage.js +2 -2
  71. package/lib/core/network/protocols/httpwsProtocol.js +228 -50
  72. package/lib/core/network/protocols/mqttProtocol.js +3 -3
  73. package/lib/core/network/protocols/protocol.js +3 -3
  74. package/lib/core/network/router.js +7 -6
  75. package/lib/core/plugin/plugin.js +38 -64
  76. package/lib/core/plugin/pluginContext.js +22 -3
  77. package/lib/core/plugin/pluginManifest.js +3 -3
  78. package/lib/core/plugin/pluginRepository.js +5 -5
  79. package/lib/core/plugin/pluginsManager.js +29 -28
  80. package/lib/core/realtime/channel.js +20 -4
  81. package/lib/core/realtime/hotelClerk.js +24 -5
  82. package/lib/core/realtime/notification/server.js +1 -1
  83. package/lib/core/realtime/notification/user.js +1 -1
  84. package/lib/core/realtime/notifier.js +5 -5
  85. package/lib/core/security/index.js +1 -1
  86. package/lib/core/security/profileRepository.d.ts +176 -0
  87. package/lib/core/security/profileRepository.js +445 -443
  88. package/lib/core/security/roleRepository.js +16 -16
  89. package/lib/core/security/securityLoader.js +2 -2
  90. package/lib/core/security/tokenRepository.js +11 -11
  91. package/lib/core/security/userRepository.js +8 -8
  92. package/lib/core/shared/abstractManifest.js +4 -4
  93. package/lib/core/shared/repository.js +5 -5
  94. package/lib/core/shared/sdk/embeddedSdk.js +21 -2
  95. package/lib/core/shared/sdk/funnelProtocol.js +1 -1
  96. package/lib/core/shared/sdk/impersonatedSdk.js +1 -1
  97. package/lib/core/shared/store.js +30 -23
  98. package/lib/core/statistics/statistics.js +17 -17
  99. package/lib/core/storage/clientAdapter.js +45 -10
  100. package/lib/core/storage/indexCache.js +20 -4
  101. package/lib/core/validation/baseType.js +5 -5
  102. package/lib/core/validation/types/anything.js +1 -1
  103. package/lib/core/validation/types/boolean.js +2 -2
  104. package/lib/core/validation/types/date.js +9 -9
  105. package/lib/core/validation/types/email.js +5 -5
  106. package/lib/core/validation/types/enum.js +6 -6
  107. package/lib/core/validation/types/geoPoint.js +2 -2
  108. package/lib/core/validation/types/geoShape.js +28 -25
  109. package/lib/core/validation/types/integer.js +4 -4
  110. package/lib/core/validation/types/ipAddress.js +7 -6
  111. package/lib/core/validation/types/numeric.js +4 -4
  112. package/lib/core/validation/types/object.js +5 -5
  113. package/lib/core/validation/types/string.js +5 -5
  114. package/lib/core/validation/types/url.js +7 -6
  115. package/lib/core/validation/validation.js +95 -84
  116. package/lib/kerror/codes/1-services.json +12 -0
  117. package/lib/kerror/codes/2-api.json +12 -0
  118. package/lib/kerror/codes/3-network.json +12 -0
  119. package/lib/kerror/codes/4-plugin.json +6 -0
  120. package/lib/kerror/codes/index.js +11 -11
  121. package/lib/kerror/errors/multipleErrorsError.d.ts +1 -1
  122. package/lib/kerror/errors/multipleErrorsError.js +3 -3
  123. package/lib/kerror/index.d.ts +82 -0
  124. package/lib/kerror/index.js +176 -143
  125. package/lib/kuzzle/dumpGenerator.js +3 -3
  126. package/lib/kuzzle/event/kuzzleEventEmitter.js +4 -4
  127. package/lib/kuzzle/event/pipeRunner.js +1 -1
  128. package/lib/kuzzle/event/waterfall.js +6 -6
  129. package/lib/kuzzle/kuzzle.js +59 -9
  130. package/lib/kuzzle/log.js +3 -3
  131. package/lib/kuzzle/vault.js +3 -3
  132. package/lib/model/security/profile.d.ts +54 -0
  133. package/lib/model/security/profile.js +192 -232
  134. package/lib/model/security/rights.js +1 -1
  135. package/lib/model/security/role.d.ts +40 -0
  136. package/lib/model/security/role.js +174 -190
  137. package/lib/model/security/user.d.ts +29 -0
  138. package/lib/model/security/user.js +103 -52
  139. package/lib/model/storage/apiKey.js +2 -2
  140. package/lib/model/storage/baseModel.js +3 -3
  141. package/lib/service/cache/redis.js +7 -7
  142. package/lib/service/storage/elasticsearch.js +152 -90
  143. package/lib/service/storage/esWrapper.js +2 -3
  144. package/lib/types/ControllerDefinition.d.ts +3 -3
  145. package/lib/types/ControllerRights.d.ts +22 -0
  146. package/lib/types/ControllerRights.js +23 -0
  147. package/lib/types/HttpStream.d.ts +32 -0
  148. package/lib/types/HttpStream.js +70 -0
  149. package/lib/types/OpenApiDefinition.d.ts +43 -0
  150. package/lib/types/{config/StorageService/StorageServiceElasticsearchConfiguration.js → OpenApiDefinition.js} +1 -1
  151. package/lib/types/Plugin.js +20 -4
  152. package/lib/types/Policy.d.ts +25 -0
  153. package/lib/types/{InternalLogger.js → Policy.js} +2 -2
  154. package/lib/types/PolicyRestrictions.d.ts +21 -0
  155. package/lib/types/PolicyRestrictions.js +23 -0
  156. package/lib/types/Target.d.ts +15 -0
  157. package/lib/types/Target.js +23 -0
  158. package/lib/types/config/KuzzleConfiguration.d.ts +4 -0
  159. package/lib/types/config/ServicesConfiguration.d.ts +2 -2
  160. package/lib/types/config/{StorageService/StorageServiceElasticsearchConfiguration.d.ts → storageEngine/StorageEngineElasticsearchConfiguration.d.ts} +10 -3
  161. package/lib/types/config/storageEngine/StorageEngineElasticsearchConfiguration.js +3 -0
  162. package/lib/types/errors/ErrorDefinition.d.ts +27 -0
  163. package/lib/types/errors/ErrorDefinition.js +3 -0
  164. package/lib/types/errors/ErrorDomains.d.ts +17 -0
  165. package/lib/types/errors/ErrorDomains.js +3 -0
  166. package/lib/types/index.d.ts +9 -1
  167. package/lib/types/index.js +9 -1
  168. package/lib/util/array.d.ts +11 -0
  169. package/lib/util/array.js +57 -0
  170. package/lib/util/assertType.js +6 -6
  171. package/lib/util/bufferedPassThrough.d.ts +76 -0
  172. package/lib/util/bufferedPassThrough.js +161 -0
  173. package/lib/util/deprecate.js +7 -5
  174. package/lib/util/didYouMean.js +1 -1
  175. package/lib/util/dump-collection.d.ts +3 -0
  176. package/lib/util/dump-collection.js +284 -0
  177. package/lib/util/extractFields.js +2 -2
  178. package/lib/util/inflector.d.ts +8 -0
  179. package/lib/util/inflector.js +16 -0
  180. package/lib/util/mutex.js +21 -2
  181. package/lib/util/requestAssertions.js +7 -7
  182. package/lib/util/wildcard.js +55 -0
  183. package/package-lock.json +535 -75
  184. package/package.json +5 -3
  185. package/lib/api/openApiGenerator.d.ts +0 -7
  186. package/lib/api/openApiGenerator.js +0 -197
  187. package/lib/types/InternalLogger.d.ts +0 -25
@@ -38,7 +38,7 @@ const kerror = require('../../../kerror').wrap('network', 'http');
38
38
  * in JSON format
39
39
  */
40
40
  class RouteHandler {
41
- constructor(url, query, message) {
41
+ constructor (url, query, message) {
42
42
  this.handler = null;
43
43
  this._request = null;
44
44
  this.url = url;
@@ -92,7 +92,7 @@ class RouteHandler {
92
92
  * @param {string} name
93
93
  * @param {string} value
94
94
  */
95
- addArgument(name, value) {
95
+ addArgument (name, value) {
96
96
  this.data[name] = value;
97
97
  }
98
98
 
@@ -101,7 +101,7 @@ class RouteHandler {
101
101
  *
102
102
  * @param {Function} callback
103
103
  */
104
- invokeHandler(callback) {
104
+ invokeHandler (callback) {
105
105
  this.handler(this.request, callback);
106
106
  }
107
107
  }
@@ -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,10 +77,15 @@ const HTTP_ALLOWED_CONTENT_TYPES = [
76
77
  'application/x-www-form-urlencoded',
77
78
  'multipart/form-data',
78
79
  ];
80
+ const HTTP_SKIPPED_HEADERS = [
81
+ 'content-length',
82
+ ];
79
83
  const HTTP_HEADER_CONNECTION = Buffer.from('Connection');
80
84
  const HTTP_HEADER_CONTENT_LENGTH = Buffer.from('Content-Length');
81
85
  const HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = Buffer.from('Access-Control-Allow-Origin');
82
86
  const HTTP_HEADER_VARY = Buffer.from('Vary');
87
+ const HTTP_HEADER_TRANSFER_ENCODING = Buffer.from('Transfer-Encoding');
88
+ const CHUNKED = Buffer.from('chunked');
83
89
  const WILDCARD = Buffer.from('*');
84
90
  const ORIGIN = Buffer.from('Origin');
85
91
  const CLOSE = Buffer.from('close');
@@ -121,7 +127,7 @@ class HttpWsProtocol extends Protocol {
121
127
  this.wsConfig = this.parseWebSocketOptions();
122
128
  this.httpConfig = this.parseHttpOptions();
123
129
 
124
- if (!this.wsConfig.enabled && !this.httpConfig.enabled) {
130
+ if (! this.wsConfig.enabled && ! this.httpConfig.enabled) {
125
131
  return false;
126
132
  }
127
133
 
@@ -137,7 +143,7 @@ class HttpWsProtocol extends Protocol {
137
143
  }
138
144
 
139
145
  this.server.listen(entrypoint.config.port, socket => {
140
- if (!socket) {
146
+ if (! socket) {
141
147
  throw new Error(`[http/websocket] fatal: unable to listen to port ${entrypoint.config.port}`);
142
148
  }
143
149
  });
@@ -199,7 +205,7 @@ class HttpWsProtocol extends Protocol {
199
205
  const socket = this.socketByConnectionId.get(data.connectionId);
200
206
  debugWS('notify: %a', data);
201
207
 
202
- if (!socket) {
208
+ if (! socket) {
203
209
  return;
204
210
  }
205
211
 
@@ -214,7 +220,7 @@ class HttpWsProtocol extends Protocol {
214
220
  joinChannel (channel, connectionId) {
215
221
  const socket = this.socketByConnectionId.get(connectionId);
216
222
 
217
- if (!socket) {
223
+ if (! socket) {
218
224
  return;
219
225
  }
220
226
 
@@ -225,7 +231,7 @@ class HttpWsProtocol extends Protocol {
225
231
  leaveChannel (channel, connectionId) {
226
232
  const socket = this.socketByConnectionId.get(connectionId);
227
233
 
228
- if (!socket) {
234
+ if (! socket) {
229
235
  return;
230
236
  }
231
237
 
@@ -239,7 +245,7 @@ class HttpWsProtocol extends Protocol {
239
245
 
240
246
  const socket = this.socketByConnectionId.get(connectionId);
241
247
 
242
- if (!socket) {
248
+ if (! socket) {
243
249
  return;
244
250
  }
245
251
 
@@ -252,6 +258,7 @@ class HttpWsProtocol extends Protocol {
252
258
  res.upgrade(
253
259
  {
254
260
  cookie: req.getHeader('cookie'),
261
+ origin: req.getHeader('origin'),
255
262
  },
256
263
  req.getHeader('sec-websocket-key'),
257
264
  req.getHeader('sec-websocket-protocol'),
@@ -262,7 +269,14 @@ class HttpWsProtocol extends Protocol {
262
269
 
263
270
  wsOnOpenHandler (socket) {
264
271
  const ip = Buffer.from(socket.getRemoteAddressAsText()).toString();
265
- const connection = new ClientConnection(this.name, [ip], {cookie: socket.cookie});
272
+ const connection = new ClientConnection(
273
+ this.name,
274
+ [ip],
275
+ {
276
+ cookie: socket.cookie,
277
+ origin: socket.origin
278
+ }
279
+ );
266
280
 
267
281
  this.entryPoint.newConnection(connection);
268
282
  this.connectionBySocket.set(socket, connection);
@@ -273,7 +287,7 @@ class HttpWsProtocol extends Protocol {
273
287
  wsOnCloseHandler (socket, code, message) {
274
288
  const connection = this.connectionBySocket.get(socket);
275
289
 
276
- if (!connection) {
290
+ if (! connection) {
277
291
  return;
278
292
  }
279
293
 
@@ -291,7 +305,7 @@ class HttpWsProtocol extends Protocol {
291
305
  }
292
306
 
293
307
  wsOnMessageHandler (socket, data) {
294
- if (!data || data.byteLength === 0) {
308
+ if (! data || data.byteLength === 0) {
295
309
  return;
296
310
  }
297
311
 
@@ -348,7 +362,7 @@ class HttpWsProtocol extends Protocol {
348
362
  request = new Request(parsed, { connection });
349
363
  }
350
364
  catch (e) {
351
- this.wsSendError(socket, connection, e);
365
+ this.wsSendError(socket, connection, e, parsed.requestId);
352
366
  return;
353
367
  }
354
368
 
@@ -389,9 +403,14 @@ class HttpWsProtocol extends Protocol {
389
403
  * @param {uWS.WebSocket} socket
390
404
  * @param {ClientConnection} connection
391
405
  * @param {Error} error
406
+ * @param {String} requestId @optional
392
407
  */
393
- wsSendError (socket, connection, error) {
408
+ wsSendError (socket, connection, error, requestId) {
394
409
  const request = new Request({}, { connection, error });
410
+
411
+ // If a requestId is provided we use it instead of the generated one
412
+ request.id = requestId || request.id;
413
+
395
414
  const sanitized = removeErrorStack(request.response.toJSON()).content;
396
415
 
397
416
  this.wsSend(socket, Buffer.from(JSON.stringify(sanitized)));
@@ -405,7 +424,7 @@ class HttpWsProtocol extends Protocol {
405
424
  * @param {Buffer} payload
406
425
  */
407
426
  wsSend (socket, payload) {
408
- if (!this.connectionBySocket.has(socket)) {
427
+ if (! this.connectionBySocket.has(socket)) {
409
428
  return;
410
429
  }
411
430
 
@@ -444,7 +463,7 @@ class HttpWsProtocol extends Protocol {
444
463
  const contentType = message.headers['content-type'];
445
464
 
446
465
  if ( contentType
447
- && !HTTP_ALLOWED_CONTENT_TYPES.some(allowed => contentType.includes(allowed))
466
+ && ! HTTP_ALLOWED_CONTENT_TYPES.some(allowed => contentType.includes(allowed))
448
467
  ) {
449
468
  this.httpSendError(
450
469
  message,
@@ -501,7 +520,7 @@ class HttpWsProtocol extends Protocol {
501
520
  return;
502
521
  }
503
522
 
504
- if (!isLast) {
523
+ if (! isLast) {
505
524
  return;
506
525
  }
507
526
 
@@ -584,6 +603,13 @@ class HttpWsProtocol extends Protocol {
584
603
  return;
585
604
  }
586
605
 
606
+ if (request.result instanceof HttpStream) {
607
+ this.httpSendStream(request, response, request.result, message);
608
+ return;
609
+ }
610
+
611
+ // Send the response in one go
612
+
587
613
  const data = this.httpRequestToResponse(request, message);
588
614
 
589
615
  this.httpCompress(message, data, result => {
@@ -594,38 +620,13 @@ class HttpWsProtocol extends Protocol {
594
620
  request.response.setHeader('Content-Encoding', result.encoding);
595
621
 
596
622
  response.cork(() => {
597
- response.writeStatus(Buffer.from(request.response.status.toString()));
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
- }
623
+ this.httpWriteRequestHeaders(request, response, message);
623
624
 
624
625
  const [ success ] = response.tryEnd(
625
626
  result.compressed,
626
627
  result.compressed.length);
627
628
 
628
- if (!success) {
629
+ if (! success) {
629
630
  response.onWritable(offset => {
630
631
  const retryData = result.compressed.subarray(offset);
631
632
  const [ retrySuccess ] = response.tryEnd(
@@ -640,6 +641,182 @@ class HttpWsProtocol extends Protocol {
640
641
  });
641
642
  }
642
643
 
644
+ /**
645
+ *
646
+ * @param {uWS.HttpRequest} request
647
+ * @param {uWS.HttpResponse} response
648
+ * @param {HttpMessage} message
649
+ */
650
+ httpWriteRequestHeaders (request, response, message) {
651
+ response.writeStatus(Buffer.from(request.response.status.toString()));
652
+
653
+ response.writeHeader(HTTP_HEADER_CONNECTION, CLOSE);
654
+
655
+ for (const header of this.httpConfig.headers) {
656
+ // If header is missing, add the default one
657
+ if (request.response.headers[header[2]] === undefined) {
658
+ response.writeHeader(header[0], header[1]);
659
+ }
660
+ }
661
+
662
+ // Access-Control-Allow-Origin Logic
663
+ if ( request.response.headers['Access-Control-Allow-Origin'] === undefined
664
+ && message.headers
665
+ && message.headers.origin
666
+ ) {
667
+ response.writeHeader(
668
+ HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
669
+ Buffer.from(message.headers.origin));
670
+
671
+ response.writeHeader(HTTP_HEADER_VARY, ORIGIN);
672
+ }
673
+
674
+ for (const [key, value] of Object.entries(request.response.headers)) {
675
+ // Skip some headers that are not allowed to be sent or modified
676
+ if (HTTP_SKIPPED_HEADERS.includes(key.toLowerCase())) {
677
+ continue;
678
+ }
679
+
680
+ response.writeHeader(Buffer.from(key), Buffer.from(value.toString()));
681
+ }
682
+ }
683
+
684
+ /**
685
+ * Send stream data thorugh the HTTP response
686
+ * @param {HttpRequest} request
687
+ * @param {HttpStream} httpStream - The data stream to send
688
+ * @param {uWS.HttpResponse} response
689
+ * @param {HttpMessage} message
690
+ */
691
+ httpSendStream (request, response, httpStream, message) {
692
+ const streamSizeFixed = typeof httpStream.totalBytes === 'number' && httpStream.totalBytes > 0;
693
+
694
+ if (httpStream.errored) {
695
+ this.httpSendError(
696
+ message,
697
+ response,
698
+ kerror.get('network', 'http', 'stream_errored', httpStream.error.message)
699
+ );
700
+ return;
701
+ }
702
+
703
+ if (httpStream.destroyed) {
704
+ this.httpSendError(
705
+ message,
706
+ response,
707
+ kerror.get('network', 'http', 'stream_closed')
708
+ );
709
+ return;
710
+ }
711
+
712
+ // Remove Content-Length header to avoid conflic with Transfer-Encoding header
713
+ request.response.setHeader('Content-Length', null);
714
+
715
+ // Send Headers in one go
716
+ response.cork(() => {
717
+ this.httpWriteRequestHeaders(request, response, message);
718
+
719
+ if (streamSizeFixed) {
720
+ response.writeHeader(HTTP_HEADER_CONTENT_LENGTH, Buffer.from(httpStream.totalBytes.toString()));
721
+ }
722
+ else {
723
+ response.writeHeader(HTTP_HEADER_TRANSFER_ENCODING, CHUNKED);
724
+ }
725
+ });
726
+
727
+ httpStream.stream.on('data', chunk => {
728
+ // Make a copy of the array buffer
729
+ const arrayBuffer = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
730
+
731
+ const arrayBufferOffset = response.getWriteOffset();
732
+
733
+ let backpressure = false;
734
+
735
+ /**
736
+ * Switch method of writing data to the response
737
+ * based on the size of the stream.
738
+ * If the stream has a fixed size we can use the tryEnd method
739
+ */
740
+ if (streamSizeFixed) {
741
+ const [ success, done ] = response.tryEnd(arrayBuffer, httpStream.totalBytes);
742
+ backpressure = ! success;
743
+
744
+ if (done) {
745
+ httpStream.destroy();
746
+ return;
747
+ }
748
+ }
749
+ else {
750
+ const success = response.write(arrayBuffer);
751
+
752
+ backpressure = ! success;
753
+ }
754
+
755
+ // In case of backpressure we need to wait for drainage before sending more data
756
+ if (backpressure) {
757
+ response.arrayBufferOffset = arrayBufferOffset;
758
+ response.arrayBuffer = arrayBuffer;
759
+ httpStream.stream.pause();
760
+
761
+ /**
762
+ * When the stream is drained we can resume sending data,
763
+ * only if the is no backpressure after we wrote the missing chunk data.
764
+ */
765
+ response.onWritable(offset => {
766
+ if (! streamSizeFixed && offset - response.arrayBufferOffset === 0) {
767
+ httpStream.stream.resume();
768
+ return true;
769
+ }
770
+
771
+ let retryBackpressure = false;
772
+ const remainingChunkData = response.arrayBuffer.slice(offset - response.arrayBufferOffset);
773
+
774
+ if (streamSizeFixed) {
775
+ const [ success ] = response.tryEnd(remainingChunkData, httpStream.totalBytes);
776
+
777
+ retryBackpressure = ! success;
778
+ }
779
+ else {
780
+ const success = response.write(remainingChunkData);
781
+
782
+ retryBackpressure = ! success;
783
+ }
784
+
785
+ if (! retryBackpressure) {
786
+ httpStream.stream.resume();
787
+ }
788
+
789
+ return ! retryBackpressure;
790
+ });
791
+ }
792
+ });
793
+
794
+ httpStream.stream.on('end', () => {
795
+ if (httpStream.destroy()) {
796
+ response.end();
797
+ }
798
+ });
799
+
800
+ httpStream.stream.on('close', () => {
801
+ if (httpStream.destroy()) {
802
+ response.end();
803
+ }
804
+ });
805
+
806
+ httpStream.stream.on('error', err => {
807
+ if (httpStream.destroy()) {
808
+ response.end();
809
+ }
810
+
811
+ debugHTTP('[%s] httpSendStream: %s', httpStream.connection.id, err.message);
812
+ });
813
+
814
+ response.onAborted(() => {
815
+ httpStream.destroy();
816
+ });
817
+
818
+ }
819
+
643
820
  /**
644
821
  * Forward an error response to the client
645
822
  *
@@ -680,7 +857,8 @@ class HttpWsProtocol extends Protocol {
680
857
  if (message.headers && message.headers.origin) {
681
858
  if (global.kuzzle.config.internal.allowAllOrigins) {
682
859
  response.writeHeader(HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, WILDCARD);
683
- } else {
860
+ }
861
+ else {
684
862
  response.writeHeader(HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, Buffer.from(message.headers.origin));
685
863
  response.writeHeader(HTTP_HEADER_VARY, ORIGIN);
686
864
  }
@@ -703,13 +881,13 @@ class HttpWsProtocol extends Protocol {
703
881
  * @param {HttpMessage} message
704
882
  * @returns {Buffer}
705
883
  */
706
- httpRequestToResponse(request, message) {
884
+ httpRequestToResponse (request, message) {
707
885
  let data = removeErrorStack(request.response.toJSON());
708
886
 
709
887
  if (message.requestId !== data.requestId) {
710
888
  data.requestId = message.requestId;
711
889
 
712
- if (!data.raw) {
890
+ if (! data.raw) {
713
891
  data.content.requestId = message.requestId;
714
892
  }
715
893
  }
@@ -763,7 +941,7 @@ class HttpWsProtocol extends Protocol {
763
941
  * @param {Buffer} data
764
942
  * @param {Function} callback
765
943
  */
766
- httpCompress(message, data, callback) {
944
+ httpCompress (message, data, callback) {
767
945
  if (message.headers['accept-encoding']) {
768
946
  const encodings = message.headers['accept-encoding']
769
947
  .split(',')
@@ -792,15 +970,15 @@ class HttpWsProtocol extends Protocol {
792
970
 
793
971
  if (algorithm === 'gzip') {
794
972
  zlib.gzip(data, (err, compressed) => callback({
795
- compressed: !err ? compressed : data,
796
- encoding: !err ? 'gzip' : 'identity',
973
+ compressed: ! err ? compressed : data,
974
+ encoding: ! err ? 'gzip' : 'identity',
797
975
  }));
798
976
  return;
799
977
  }
800
978
  else if (algorithm === 'deflate') {
801
979
  zlib.deflate(data, (err, compressed) => callback({
802
- compressed: !err ? compressed : data,
803
- encoding: !err ? 'deflate' : 'identity',
980
+ compressed: ! err ? compressed : data,
981
+ encoding: ! err ? 'deflate' : 'identity',
804
982
  }));
805
983
  return;
806
984
  }
@@ -814,7 +992,7 @@ class HttpWsProtocol extends Protocol {
814
992
 
815
993
  httpUncompress (message, payload, cb) {
816
994
  let encodings = message.headers['content-encoding'];
817
- if (!encodings) {
995
+ if (! encodings) {
818
996
  cb(null, payload);
819
997
  return;
820
998
  }
@@ -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