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.
- package/lib/api/controllers/adminController.js +3 -3
- package/lib/api/controllers/authController.js +12 -12
- package/lib/api/controllers/baseController.js +60 -3
- package/lib/api/controllers/clusterController.js +1 -1
- package/lib/api/controllers/collectionController.js +7 -5
- package/lib/api/controllers/documentController.js +130 -17
- package/lib/api/controllers/indexController.js +1 -1
- package/lib/api/controllers/memoryStorageController.js +39 -38
- package/lib/api/controllers/realtimeController.js +1 -1
- package/lib/api/controllers/securityController.js +50 -50
- package/lib/api/controllers/serverController.js +73 -27
- package/lib/api/documentExtractor.js +3 -3
- package/lib/api/funnel.js +44 -21
- package/lib/api/httpRoutes.js +9 -4
- package/lib/api/openapi/OpenApiManager.d.ts +11 -0
- package/lib/api/openapi/OpenApiManager.js +96 -0
- package/lib/api/openapi/{document → components/document}/count.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/create.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/createOrReplace.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/delete.yaml +1 -1
- package/lib/api/openapi/{document → components/document}/deleteByQuery.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/exists.yaml +1 -1
- package/lib/api/openapi/{document → components/document}/get.yaml +1 -1
- package/lib/api/openapi/{document → components/document}/index.d.ts +2 -0
- package/lib/api/openapi/{document → components/document}/index.js +7 -2
- package/lib/api/openapi/{document → components/document}/replace.yaml +2 -2
- package/lib/api/openapi/{document → components/document}/scroll.yaml +1 -1
- package/lib/api/openapi/{document → components/document}/update.yaml +2 -2
- package/lib/api/openapi/components/document/validate.yaml +42 -0
- package/lib/api/openapi/components/index.d.ts +2 -0
- package/lib/api/openapi/components/index.js +18 -0
- package/lib/api/openapi/{payloads.yaml → components/payloads.yaml} +0 -0
- package/lib/api/openapi/index.d.ts +1 -2
- package/lib/api/openapi/index.js +1 -5
- package/lib/api/openapi/openApiGenerator.d.ts +7 -0
- package/lib/api/openapi/openApiGenerator.js +133 -0
- package/lib/api/request/kuzzleRequest.js +8 -6
- package/lib/cluster/node.js +9 -9
- package/lib/cluster/publisher.js +1 -1
- package/lib/cluster/state.js +20 -4
- package/lib/cluster/subscriber.js +1 -1
- package/lib/cluster/workers/IDCardRenewer.js +2 -2
- package/lib/config/default.config.js +1 -0
- package/lib/config/index.js +6 -6
- package/lib/core/auth/passportResponse.js +6 -6
- package/lib/core/auth/passportWrapper.js +5 -5
- package/lib/core/backend/backend.d.ts +11 -3
- package/lib/core/backend/backend.js +22 -17
- package/lib/core/backend/backendConfig.d.ts +5 -1
- package/lib/core/backend/backendConfig.js +25 -2
- package/lib/core/backend/backendController.js +21 -5
- package/lib/core/backend/backendErrors.d.ts +58 -0
- package/lib/core/backend/backendErrors.js +121 -0
- package/lib/core/backend/backendHook.js +21 -5
- package/lib/core/backend/backendImport.js +21 -5
- package/lib/core/backend/backendOpenApi.d.ts +9 -0
- package/lib/core/backend/backendOpenApi.js +69 -0
- package/lib/core/backend/backendPipe.js +21 -5
- package/lib/core/backend/backendPlugin.js +22 -3
- package/lib/core/backend/backendVault.js +21 -2
- package/lib/core/backend/index.d.ts +2 -0
- package/lib/core/backend/index.js +2 -0
- package/lib/core/network/accessLogger.js +6 -6
- package/lib/core/network/clientConnection.js +1 -1
- package/lib/core/network/entryPoint.js +5 -5
- package/lib/core/network/httpRouter/index.js +5 -5
- package/lib/core/network/httpRouter/routeHandler.js +3 -3
- package/lib/core/network/httpRouter/routePart.js +5 -5
- package/lib/core/network/protocolManifest.js +1 -1
- package/lib/core/network/protocols/httpMessage.js +2 -2
- package/lib/core/network/protocols/httpwsProtocol.js +228 -50
- package/lib/core/network/protocols/mqttProtocol.js +3 -3
- package/lib/core/network/protocols/protocol.js +3 -3
- package/lib/core/network/router.js +7 -6
- package/lib/core/plugin/plugin.js +38 -64
- package/lib/core/plugin/pluginContext.js +22 -3
- package/lib/core/plugin/pluginManifest.js +3 -3
- package/lib/core/plugin/pluginRepository.js +5 -5
- package/lib/core/plugin/pluginsManager.js +29 -28
- package/lib/core/realtime/channel.js +20 -4
- package/lib/core/realtime/hotelClerk.js +24 -5
- package/lib/core/realtime/notification/server.js +1 -1
- package/lib/core/realtime/notification/user.js +1 -1
- package/lib/core/realtime/notifier.js +5 -5
- package/lib/core/security/index.js +1 -1
- package/lib/core/security/profileRepository.d.ts +176 -0
- package/lib/core/security/profileRepository.js +445 -443
- package/lib/core/security/roleRepository.js +16 -16
- package/lib/core/security/securityLoader.js +2 -2
- package/lib/core/security/tokenRepository.js +11 -11
- package/lib/core/security/userRepository.js +8 -8
- package/lib/core/shared/abstractManifest.js +4 -4
- package/lib/core/shared/repository.js +5 -5
- package/lib/core/shared/sdk/embeddedSdk.js +21 -2
- package/lib/core/shared/sdk/funnelProtocol.js +1 -1
- package/lib/core/shared/sdk/impersonatedSdk.js +1 -1
- package/lib/core/shared/store.js +30 -23
- package/lib/core/statistics/statistics.js +17 -17
- package/lib/core/storage/clientAdapter.js +45 -10
- package/lib/core/storage/indexCache.js +20 -4
- package/lib/core/validation/baseType.js +5 -5
- package/lib/core/validation/types/anything.js +1 -1
- package/lib/core/validation/types/boolean.js +2 -2
- package/lib/core/validation/types/date.js +9 -9
- package/lib/core/validation/types/email.js +5 -5
- package/lib/core/validation/types/enum.js +6 -6
- package/lib/core/validation/types/geoPoint.js +2 -2
- package/lib/core/validation/types/geoShape.js +28 -25
- package/lib/core/validation/types/integer.js +4 -4
- package/lib/core/validation/types/ipAddress.js +7 -6
- package/lib/core/validation/types/numeric.js +4 -4
- package/lib/core/validation/types/object.js +5 -5
- package/lib/core/validation/types/string.js +5 -5
- package/lib/core/validation/types/url.js +7 -6
- package/lib/core/validation/validation.js +95 -84
- package/lib/kerror/codes/1-services.json +12 -0
- package/lib/kerror/codes/2-api.json +12 -0
- package/lib/kerror/codes/3-network.json +12 -0
- package/lib/kerror/codes/4-plugin.json +6 -0
- package/lib/kerror/codes/index.js +11 -11
- package/lib/kerror/errors/multipleErrorsError.d.ts +1 -1
- package/lib/kerror/errors/multipleErrorsError.js +3 -3
- package/lib/kerror/index.d.ts +82 -0
- package/lib/kerror/index.js +176 -143
- package/lib/kuzzle/dumpGenerator.js +3 -3
- package/lib/kuzzle/event/kuzzleEventEmitter.js +4 -4
- package/lib/kuzzle/event/pipeRunner.js +1 -1
- package/lib/kuzzle/event/waterfall.js +6 -6
- package/lib/kuzzle/kuzzle.js +59 -9
- package/lib/kuzzle/log.js +3 -3
- package/lib/kuzzle/vault.js +3 -3
- package/lib/model/security/profile.d.ts +54 -0
- package/lib/model/security/profile.js +192 -232
- package/lib/model/security/rights.js +1 -1
- package/lib/model/security/role.d.ts +40 -0
- package/lib/model/security/role.js +174 -190
- package/lib/model/security/user.d.ts +29 -0
- package/lib/model/security/user.js +103 -52
- package/lib/model/storage/apiKey.js +2 -2
- package/lib/model/storage/baseModel.js +3 -3
- package/lib/service/cache/redis.js +7 -7
- package/lib/service/storage/elasticsearch.js +152 -90
- package/lib/service/storage/esWrapper.js +2 -3
- package/lib/types/ControllerDefinition.d.ts +3 -3
- package/lib/types/ControllerRights.d.ts +22 -0
- package/lib/types/ControllerRights.js +23 -0
- package/lib/types/HttpStream.d.ts +32 -0
- package/lib/types/HttpStream.js +70 -0
- package/lib/types/OpenApiDefinition.d.ts +43 -0
- package/lib/types/{config/StorageService/StorageServiceElasticsearchConfiguration.js → OpenApiDefinition.js} +1 -1
- package/lib/types/Plugin.js +20 -4
- package/lib/types/Policy.d.ts +25 -0
- package/lib/types/{InternalLogger.js → Policy.js} +2 -2
- package/lib/types/PolicyRestrictions.d.ts +21 -0
- package/lib/types/PolicyRestrictions.js +23 -0
- package/lib/types/Target.d.ts +15 -0
- package/lib/types/Target.js +23 -0
- package/lib/types/config/KuzzleConfiguration.d.ts +4 -0
- package/lib/types/config/ServicesConfiguration.d.ts +2 -2
- package/lib/types/config/{StorageService/StorageServiceElasticsearchConfiguration.d.ts → storageEngine/StorageEngineElasticsearchConfiguration.d.ts} +10 -3
- package/lib/types/config/storageEngine/StorageEngineElasticsearchConfiguration.js +3 -0
- package/lib/types/errors/ErrorDefinition.d.ts +27 -0
- package/lib/types/errors/ErrorDefinition.js +3 -0
- package/lib/types/errors/ErrorDomains.d.ts +17 -0
- package/lib/types/errors/ErrorDomains.js +3 -0
- package/lib/types/index.d.ts +9 -1
- package/lib/types/index.js +9 -1
- package/lib/util/array.d.ts +11 -0
- package/lib/util/array.js +57 -0
- package/lib/util/assertType.js +6 -6
- package/lib/util/bufferedPassThrough.d.ts +76 -0
- package/lib/util/bufferedPassThrough.js +161 -0
- package/lib/util/deprecate.js +7 -5
- package/lib/util/didYouMean.js +1 -1
- package/lib/util/dump-collection.d.ts +3 -0
- package/lib/util/dump-collection.js +284 -0
- package/lib/util/extractFields.js +2 -2
- package/lib/util/inflector.d.ts +8 -0
- package/lib/util/inflector.js +16 -0
- package/lib/util/mutex.js +21 -2
- package/lib/util/requestAssertions.js +7 -7
- package/lib/util/wildcard.js +55 -0
- package/package-lock.json +535 -75
- package/package.json +5 -3
- package/lib/api/openApiGenerator.d.ts +0 -7
- package/lib/api/openApiGenerator.js +0 -197
- package/lib/types/InternalLogger.d.ts +0 -25
|
@@ -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(
|
|
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
|
-
|
|
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
|
-
}
|
|
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
|
-
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
52
53
|
this.connections.set(requestContext.connection.id, requestContext);
|
|
53
54
|
global.kuzzle.statistics.newConnection(requestContext);
|
|
54
55
|
}
|
|
@@ -62,7 +63,7 @@ class Router {
|
|
|
62
63
|
removeConnection (requestContext) {
|
|
63
64
|
const connId = requestContext.connection.id;
|
|
64
65
|
|
|
65
|
-
if (!connId || !requestContext.connection.protocol) {
|
|
66
|
+
if (! connId || ! requestContext.connection.protocol) {
|
|
66
67
|
global.kuzzle.log.error(kerror.get(
|
|
67
68
|
'protocol',
|
|
68
69
|
'runtime',
|
|
@@ -71,7 +72,7 @@ class Router {
|
|
|
71
72
|
return;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
if (!this.connections.has(connId)) {
|
|
75
|
+
if (! this.connections.has(connId)) {
|
|
75
76
|
global.kuzzle.log.error(kerror.get(
|
|
76
77
|
'protocol',
|
|
77
78
|
'runtime',
|
|
@@ -131,7 +132,7 @@ class Router {
|
|
|
131
132
|
request.input.action = route.action;
|
|
132
133
|
|
|
133
134
|
if (route.deprecated) {
|
|
134
|
-
const { deprecated
|
|
135
|
+
const { deprecated: { since, message } } = route;
|
|
135
136
|
request.addDeprecation(since, message);
|
|
136
137
|
}
|
|
137
138
|
|