kuzzle 2.16.10 → 2.17.1

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 (186) 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 +40 -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 +222 -48
  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/Policy.js +23 -0
  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 +517 -75
  184. package/package.json +5 -3
  185. package/lib/api/openApiGenerator.d.ts +0 -7
  186. package/lib/api/openApiGenerator.js +0 -197
@@ -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,11 +77,18 @@ 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
+ ];
83
+ const HTTP_HEADER_CONNECTION = Buffer.from('Connection');
79
84
  const HTTP_HEADER_CONTENT_LENGTH = Buffer.from('Content-Length');
80
85
  const HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN = Buffer.from('Access-Control-Allow-Origin');
81
86
  const HTTP_HEADER_VARY = Buffer.from('Vary');
87
+ const HTTP_HEADER_TRANSFER_ENCODING = Buffer.from('Transfer-Encoding');
88
+ const CHUNKED = Buffer.from('chunked');
82
89
  const WILDCARD = Buffer.from('*');
83
90
  const ORIGIN = Buffer.from('Origin');
91
+ const CLOSE = Buffer.from('close');
84
92
  const CHARSET_REGEX = /charset=([\w-]+)/i;
85
93
 
86
94
  /**
@@ -119,7 +127,7 @@ class HttpWsProtocol extends Protocol {
119
127
  this.wsConfig = this.parseWebSocketOptions();
120
128
  this.httpConfig = this.parseHttpOptions();
121
129
 
122
- if (!this.wsConfig.enabled && !this.httpConfig.enabled) {
130
+ if (! this.wsConfig.enabled && ! this.httpConfig.enabled) {
123
131
  return false;
124
132
  }
125
133
 
@@ -135,7 +143,7 @@ class HttpWsProtocol extends Protocol {
135
143
  }
136
144
 
137
145
  this.server.listen(entrypoint.config.port, socket => {
138
- if (!socket) {
146
+ if (! socket) {
139
147
  throw new Error(`[http/websocket] fatal: unable to listen to port ${entrypoint.config.port}`);
140
148
  }
141
149
  });
@@ -197,7 +205,7 @@ class HttpWsProtocol extends Protocol {
197
205
  const socket = this.socketByConnectionId.get(data.connectionId);
198
206
  debugWS('notify: %a', data);
199
207
 
200
- if (!socket) {
208
+ if (! socket) {
201
209
  return;
202
210
  }
203
211
 
@@ -212,7 +220,7 @@ class HttpWsProtocol extends Protocol {
212
220
  joinChannel (channel, connectionId) {
213
221
  const socket = this.socketByConnectionId.get(connectionId);
214
222
 
215
- if (!socket) {
223
+ if (! socket) {
216
224
  return;
217
225
  }
218
226
 
@@ -223,7 +231,7 @@ class HttpWsProtocol extends Protocol {
223
231
  leaveChannel (channel, connectionId) {
224
232
  const socket = this.socketByConnectionId.get(connectionId);
225
233
 
226
- if (!socket) {
234
+ if (! socket) {
227
235
  return;
228
236
  }
229
237
 
@@ -237,7 +245,7 @@ class HttpWsProtocol extends Protocol {
237
245
 
238
246
  const socket = this.socketByConnectionId.get(connectionId);
239
247
 
240
- if (!socket) {
248
+ if (! socket) {
241
249
  return;
242
250
  }
243
251
 
@@ -260,7 +268,7 @@ class HttpWsProtocol extends Protocol {
260
268
 
261
269
  wsOnOpenHandler (socket) {
262
270
  const ip = Buffer.from(socket.getRemoteAddressAsText()).toString();
263
- const connection = new ClientConnection(this.name, [ip], {cookie: socket.cookie});
271
+ const connection = new ClientConnection(this.name, [ip], { cookie: socket.cookie });
264
272
 
265
273
  this.entryPoint.newConnection(connection);
266
274
  this.connectionBySocket.set(socket, connection);
@@ -271,7 +279,7 @@ class HttpWsProtocol extends Protocol {
271
279
  wsOnCloseHandler (socket, code, message) {
272
280
  const connection = this.connectionBySocket.get(socket);
273
281
 
274
- if (!connection) {
282
+ if (! connection) {
275
283
  return;
276
284
  }
277
285
 
@@ -289,7 +297,7 @@ class HttpWsProtocol extends Protocol {
289
297
  }
290
298
 
291
299
  wsOnMessageHandler (socket, data) {
292
- if (!data || data.byteLength === 0) {
300
+ if (! data || data.byteLength === 0) {
293
301
  return;
294
302
  }
295
303
 
@@ -346,7 +354,7 @@ class HttpWsProtocol extends Protocol {
346
354
  request = new Request(parsed, { connection });
347
355
  }
348
356
  catch (e) {
349
- this.wsSendError(socket, connection, e);
357
+ this.wsSendError(socket, connection, e, parsed.requestId);
350
358
  return;
351
359
  }
352
360
 
@@ -387,9 +395,14 @@ class HttpWsProtocol extends Protocol {
387
395
  * @param {uWS.WebSocket} socket
388
396
  * @param {ClientConnection} connection
389
397
  * @param {Error} error
398
+ * @param {String} requestId @optional
390
399
  */
391
- wsSendError (socket, connection, error) {
400
+ wsSendError (socket, connection, error, requestId) {
392
401
  const request = new Request({}, { connection, error });
402
+
403
+ // If a requestId is provided we use it instead of the generated one
404
+ request.id = requestId || request.id;
405
+
393
406
  const sanitized = removeErrorStack(request.response.toJSON()).content;
394
407
 
395
408
  this.wsSend(socket, Buffer.from(JSON.stringify(sanitized)));
@@ -403,7 +416,7 @@ class HttpWsProtocol extends Protocol {
403
416
  * @param {Buffer} payload
404
417
  */
405
418
  wsSend (socket, payload) {
406
- if (!this.connectionBySocket.has(socket)) {
419
+ if (! this.connectionBySocket.has(socket)) {
407
420
  return;
408
421
  }
409
422
 
@@ -442,7 +455,7 @@ class HttpWsProtocol extends Protocol {
442
455
  const contentType = message.headers['content-type'];
443
456
 
444
457
  if ( contentType
445
- && !HTTP_ALLOWED_CONTENT_TYPES.some(allowed => contentType.includes(allowed))
458
+ && ! HTTP_ALLOWED_CONTENT_TYPES.some(allowed => contentType.includes(allowed))
446
459
  ) {
447
460
  this.httpSendError(
448
461
  message,
@@ -499,7 +512,7 @@ class HttpWsProtocol extends Protocol {
499
512
  return;
500
513
  }
501
514
 
502
- if (!isLast) {
515
+ if (! isLast) {
503
516
  return;
504
517
  }
505
518
 
@@ -582,6 +595,13 @@ class HttpWsProtocol extends Protocol {
582
595
  return;
583
596
  }
584
597
 
598
+ if (request.result instanceof HttpStream) {
599
+ this.httpSendStream(request, response, request.result, message);
600
+ return;
601
+ }
602
+
603
+ // Send the response in one go
604
+
585
605
  const data = this.httpRequestToResponse(request, message);
586
606
 
587
607
  this.httpCompress(message, data, result => {
@@ -592,36 +612,13 @@ class HttpWsProtocol extends Protocol {
592
612
  request.response.setHeader('Content-Encoding', result.encoding);
593
613
 
594
614
  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
- }
615
+ this.httpWriteRequestHeaders(request, response, message);
619
616
 
620
617
  const [ success ] = response.tryEnd(
621
618
  result.compressed,
622
619
  result.compressed.length);
623
620
 
624
- if (!success) {
621
+ if (! success) {
625
622
  response.onWritable(offset => {
626
623
  const retryData = result.compressed.subarray(offset);
627
624
  const [ retrySuccess ] = response.tryEnd(
@@ -636,6 +633,182 @@ class HttpWsProtocol extends Protocol {
636
633
  });
637
634
  }
638
635
 
636
+ /**
637
+ *
638
+ * @param {uWS.HttpRequest} request
639
+ * @param {uWS.HttpResponse} response
640
+ * @param {HttpMessage} message
641
+ */
642
+ httpWriteRequestHeaders (request, response, message) {
643
+ response.writeStatus(Buffer.from(request.response.status.toString()));
644
+
645
+ response.writeHeader(HTTP_HEADER_CONNECTION, CLOSE);
646
+
647
+ for (const header of this.httpConfig.headers) {
648
+ // If header is missing, add the default one
649
+ if (request.response.headers[header[2]] === undefined) {
650
+ response.writeHeader(header[0], header[1]);
651
+ }
652
+ }
653
+
654
+ // Access-Control-Allow-Origin Logic
655
+ if ( request.response.headers['Access-Control-Allow-Origin'] === undefined
656
+ && message.headers
657
+ && message.headers.origin
658
+ ) {
659
+ response.writeHeader(
660
+ HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
661
+ Buffer.from(message.headers.origin));
662
+
663
+ response.writeHeader(HTTP_HEADER_VARY, ORIGIN);
664
+ }
665
+
666
+ for (const [key, value] of Object.entries(request.response.headers)) {
667
+ // Skip some headers that are not allowed to be sent or modified
668
+ if (HTTP_SKIPPED_HEADERS.includes(key.toLowerCase())) {
669
+ continue;
670
+ }
671
+
672
+ response.writeHeader(Buffer.from(key), Buffer.from(value.toString()));
673
+ }
674
+ }
675
+
676
+ /**
677
+ * Send stream data thorugh the HTTP response
678
+ * @param {HttpRequest} request
679
+ * @param {HttpStream} httpStream - The data stream to send
680
+ * @param {uWS.HttpResponse} response
681
+ * @param {HttpMessage} message
682
+ */
683
+ httpSendStream (request, response, httpStream, message) {
684
+ const streamSizeFixed = typeof httpStream.totalBytes === 'number' && httpStream.totalBytes > 0;
685
+
686
+ if (httpStream.errored) {
687
+ this.httpSendError(
688
+ message,
689
+ response,
690
+ kerror.get('network', 'http', 'stream_errored', httpStream.error.message)
691
+ );
692
+ return;
693
+ }
694
+
695
+ if (httpStream.destroyed) {
696
+ this.httpSendError(
697
+ message,
698
+ response,
699
+ kerror.get('network', 'http', 'stream_closed')
700
+ );
701
+ return;
702
+ }
703
+
704
+ // Remove Content-Length header to avoid conflic with Transfer-Encoding header
705
+ request.response.setHeader('Content-Length', null);
706
+
707
+ // Send Headers in one go
708
+ response.cork(() => {
709
+ this.httpWriteRequestHeaders(request, response, message);
710
+
711
+ if (streamSizeFixed) {
712
+ response.writeHeader(HTTP_HEADER_CONTENT_LENGTH, Buffer.from(httpStream.totalBytes.toString()));
713
+ }
714
+ else {
715
+ response.writeHeader(HTTP_HEADER_TRANSFER_ENCODING, CHUNKED);
716
+ }
717
+ });
718
+
719
+ httpStream.stream.on('data', chunk => {
720
+ // Make a copy of the array buffer
721
+ const arrayBuffer = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
722
+
723
+ const arrayBufferOffset = response.getWriteOffset();
724
+
725
+ let backpressure = false;
726
+
727
+ /**
728
+ * Switch method of writing data to the response
729
+ * based on the size of the stream.
730
+ * If the stream has a fixed size we can use the tryEnd method
731
+ */
732
+ if (streamSizeFixed) {
733
+ const [ success, done ] = response.tryEnd(arrayBuffer, httpStream.totalBytes);
734
+ backpressure = ! success;
735
+
736
+ if (done) {
737
+ httpStream.destroy();
738
+ return;
739
+ }
740
+ }
741
+ else {
742
+ const success = response.write(arrayBuffer);
743
+
744
+ backpressure = ! success;
745
+ }
746
+
747
+ // In case of backpressure we need to wait for drainage before sending more data
748
+ if (backpressure) {
749
+ response.arrayBufferOffset = arrayBufferOffset;
750
+ response.arrayBuffer = arrayBuffer;
751
+ httpStream.stream.pause();
752
+
753
+ /**
754
+ * When the stream is drained we can resume sending data,
755
+ * only if the is no backpressure after we wrote the missing chunk data.
756
+ */
757
+ response.onWritable(offset => {
758
+ if (! streamSizeFixed && offset - response.arrayBufferOffset === 0) {
759
+ httpStream.stream.resume();
760
+ return true;
761
+ }
762
+
763
+ let retryBackpressure = false;
764
+ const remainingChunkData = response.arrayBuffer.slice(offset - response.arrayBufferOffset);
765
+
766
+ if (streamSizeFixed) {
767
+ const [ success ] = response.tryEnd(remainingChunkData, httpStream.totalBytes);
768
+
769
+ retryBackpressure = ! success;
770
+ }
771
+ else {
772
+ const success = response.write(remainingChunkData);
773
+
774
+ retryBackpressure = ! success;
775
+ }
776
+
777
+ if (! retryBackpressure) {
778
+ httpStream.stream.resume();
779
+ }
780
+
781
+ return ! retryBackpressure;
782
+ });
783
+ }
784
+ });
785
+
786
+ httpStream.stream.on('end', () => {
787
+ if (httpStream.destroy()) {
788
+ response.end();
789
+ }
790
+ });
791
+
792
+ httpStream.stream.on('close', () => {
793
+ if (httpStream.destroy()) {
794
+ response.end();
795
+ }
796
+ });
797
+
798
+ httpStream.stream.on('error', err => {
799
+ if (httpStream.destroy()) {
800
+ response.end();
801
+ }
802
+
803
+ debugHTTP('[%s] httpSendStream: %s', httpStream.connection.id, err.message);
804
+ });
805
+
806
+ response.onAborted(() => {
807
+ httpStream.destroy();
808
+ });
809
+
810
+ }
811
+
639
812
  /**
640
813
  * Forward an error response to the client
641
814
  *
@@ -676,7 +849,8 @@ class HttpWsProtocol extends Protocol {
676
849
  if (message.headers && message.headers.origin) {
677
850
  if (global.kuzzle.config.internal.allowAllOrigins) {
678
851
  response.writeHeader(HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, WILDCARD);
679
- } else {
852
+ }
853
+ else {
680
854
  response.writeHeader(HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, Buffer.from(message.headers.origin));
681
855
  response.writeHeader(HTTP_HEADER_VARY, ORIGIN);
682
856
  }
@@ -699,13 +873,13 @@ class HttpWsProtocol extends Protocol {
699
873
  * @param {HttpMessage} message
700
874
  * @returns {Buffer}
701
875
  */
702
- httpRequestToResponse(request, message) {
876
+ httpRequestToResponse (request, message) {
703
877
  let data = removeErrorStack(request.response.toJSON());
704
878
 
705
879
  if (message.requestId !== data.requestId) {
706
880
  data.requestId = message.requestId;
707
881
 
708
- if (!data.raw) {
882
+ if (! data.raw) {
709
883
  data.content.requestId = message.requestId;
710
884
  }
711
885
  }
@@ -759,7 +933,7 @@ class HttpWsProtocol extends Protocol {
759
933
  * @param {Buffer} data
760
934
  * @param {Function} callback
761
935
  */
762
- httpCompress(message, data, callback) {
936
+ httpCompress (message, data, callback) {
763
937
  if (message.headers['accept-encoding']) {
764
938
  const encodings = message.headers['accept-encoding']
765
939
  .split(',')
@@ -788,15 +962,15 @@ class HttpWsProtocol extends Protocol {
788
962
 
789
963
  if (algorithm === 'gzip') {
790
964
  zlib.gzip(data, (err, compressed) => callback({
791
- compressed: !err ? compressed : data,
792
- encoding: !err ? 'gzip' : 'identity',
965
+ compressed: ! err ? compressed : data,
966
+ encoding: ! err ? 'gzip' : 'identity',
793
967
  }));
794
968
  return;
795
969
  }
796
970
  else if (algorithm === 'deflate') {
797
971
  zlib.deflate(data, (err, compressed) => callback({
798
- compressed: !err ? compressed : data,
799
- encoding: !err ? 'deflate' : 'identity',
972
+ compressed: ! err ? compressed : data,
973
+ encoding: ! err ? 'deflate' : 'identity',
800
974
  }));
801
975
  return;
802
976
  }
@@ -810,7 +984,7 @@ class HttpWsProtocol extends Protocol {
810
984
 
811
985
  httpUncompress (message, payload, cb) {
812
986
  let encodings = message.headers['content-encoding'];
813
- if (!encodings) {
987
+ if (! encodings) {
814
988
  cb(null, payload);
815
989
  return;
816
990
  }
@@ -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