@reldens/server-utils 0.42.0 → 0.44.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/api-reference.md +333 -0
- package/.claude/package-architecture.md +177 -0
- package/CLAUDE.md +61 -202
- package/README.md +81 -2
- package/lib/app-server-factory/cors-configurer.js +10 -0
- package/lib/app-server-factory/development-mode-detector.js +25 -0
- package/lib/app-server-factory/protocol-enforcer.js +11 -0
- package/lib/app-server-factory/rate-limit-configurer.js +10 -0
- package/lib/app-server-factory/reverse-proxy-configurer.js +26 -1
- package/lib/app-server-factory/security-configurer.js +18 -0
- package/lib/app-server-factory.js +122 -10
- package/lib/cdn-request-handler.js +130 -0
- package/lib/event-dispatcher.js +26 -0
- package/lib/file-handler.js +13 -0
- package/lib/http2-cdn-server.js +156 -46
- package/lib/request-logger.js +42 -0
- package/lib/server-error-handler.js +25 -0
- package/package.json +2 -2
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const helmet = require('helmet');
|
|
8
8
|
const sanitizeHtml = require('sanitize-html');
|
|
9
|
+
const { EventDispatcher } = require('../event-dispatcher');
|
|
9
10
|
|
|
10
11
|
class SecurityConfigurer
|
|
11
12
|
{
|
|
@@ -18,15 +19,24 @@ class SecurityConfigurer
|
|
|
18
19
|
this.helmetConfig = false;
|
|
19
20
|
this.helmetOptions = {};
|
|
20
21
|
this.sanitizeOptions = {allowedTags: [], allowedAttributes: {}};
|
|
22
|
+
this.onEvent = null;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
setupHelmet(app, config)
|
|
24
26
|
{
|
|
25
27
|
this.useHelmet = config.useHelmet !== false;
|
|
28
|
+
this.onEvent = config.onEvent || null;
|
|
26
29
|
if(!this.useHelmet){
|
|
27
30
|
return;
|
|
28
31
|
}
|
|
29
32
|
app.use(helmet(this.mapHelmetOptions(config)));
|
|
33
|
+
EventDispatcher.dispatch(
|
|
34
|
+
this.onEvent,
|
|
35
|
+
'helmet-configured',
|
|
36
|
+
'securityConfigurer',
|
|
37
|
+
this,
|
|
38
|
+
{useHelmet: this.useHelmet, isDevelopmentMode: this.isDevelopmentMode}
|
|
39
|
+
);
|
|
30
40
|
}
|
|
31
41
|
|
|
32
42
|
mapHelmetOptions(config)
|
|
@@ -131,6 +141,7 @@ class SecurityConfigurer
|
|
|
131
141
|
{
|
|
132
142
|
this.useXssProtection = config.useXssProtection !== false;
|
|
133
143
|
this.sanitizeOptions = config.sanitizeOptions || this.sanitizeOptions;
|
|
144
|
+
this.onEvent = config.onEvent || null;
|
|
134
145
|
if(!this.useXssProtection){
|
|
135
146
|
return;
|
|
136
147
|
}
|
|
@@ -143,6 +154,13 @@ class SecurityConfigurer
|
|
|
143
154
|
}
|
|
144
155
|
next();
|
|
145
156
|
});
|
|
157
|
+
EventDispatcher.dispatch(
|
|
158
|
+
this.onEvent,
|
|
159
|
+
'xss-protection-enabled',
|
|
160
|
+
'securityConfigurer',
|
|
161
|
+
this,
|
|
162
|
+
{useXssProtection: this.useXssProtection}
|
|
163
|
+
);
|
|
146
164
|
}
|
|
147
165
|
|
|
148
166
|
sanitizeRequestBody(body)
|
|
@@ -9,6 +9,9 @@ const { Http2CdnServer } = require('./http2-cdn-server');
|
|
|
9
9
|
const { ServerDefaultConfigurations } = require('./server-default-configurations');
|
|
10
10
|
const { ServerFactoryUtils } = require('./server-factory-utils');
|
|
11
11
|
const { ServerHeaders } = require('./server-headers');
|
|
12
|
+
const { ServerErrorHandler } = require('./server-error-handler');
|
|
13
|
+
const { RequestLogger } = require('./request-logger');
|
|
14
|
+
const { EventDispatcher } = require('./event-dispatcher');
|
|
12
15
|
const { DevelopmentModeDetector } = require('./app-server-factory/development-mode-detector');
|
|
13
16
|
const { ProtocolEnforcer } = require('./app-server-factory/protocol-enforcer');
|
|
14
17
|
const { SecurityConfigurer } = require('./app-server-factory/security-configurer');
|
|
@@ -115,6 +118,10 @@ class AppServerFactory
|
|
|
115
118
|
this.http2CdnServer = false;
|
|
116
119
|
this.reverseProxyEnabled = false;
|
|
117
120
|
this.reverseProxyRules = [];
|
|
121
|
+
this.onError = null;
|
|
122
|
+
this.onRequestSuccess = null;
|
|
123
|
+
this.onRequestError = null;
|
|
124
|
+
this.onEvent = null;
|
|
118
125
|
}
|
|
119
126
|
|
|
120
127
|
buildStaticHeaders(res, path)
|
|
@@ -146,6 +153,7 @@ class AppServerFactory
|
|
|
146
153
|
this.setupCors();
|
|
147
154
|
this.setupRateLimiting();
|
|
148
155
|
this.setupRequestParsing();
|
|
156
|
+
this.setupRequestLogging();
|
|
149
157
|
this.setupTrustedProxy();
|
|
150
158
|
try {
|
|
151
159
|
this.appServer = this.createServer();
|
|
@@ -159,6 +167,13 @@ class AppServerFactory
|
|
|
159
167
|
}
|
|
160
168
|
return false;
|
|
161
169
|
}
|
|
170
|
+
EventDispatcher.dispatch(
|
|
171
|
+
this.onEvent,
|
|
172
|
+
'app-server-created',
|
|
173
|
+
'appServerFactory',
|
|
174
|
+
this,
|
|
175
|
+
{port: this.port, useHttps: this.useHttps, isDevelopmentMode: this.isDevelopmentMode}
|
|
176
|
+
);
|
|
162
177
|
if(this.http2CdnEnabled){
|
|
163
178
|
if(!this.createHttp2CdnServer()){
|
|
164
179
|
this.error = {message: 'The createHttp2CdnServer() returned false.'};
|
|
@@ -188,6 +203,10 @@ class AppServerFactory
|
|
|
188
203
|
}
|
|
189
204
|
this.http2CdnServer.corsOrigins = this.http2CdnCorsOrigins;
|
|
190
205
|
this.http2CdnServer.corsAllowAll = this.http2CdnCorsAllowAll;
|
|
206
|
+
this.http2CdnServer.onError = this.onError;
|
|
207
|
+
this.http2CdnServer.onRequestSuccess = this.onRequestSuccess;
|
|
208
|
+
this.http2CdnServer.onRequestError = this.onRequestError;
|
|
209
|
+
this.http2CdnServer.onEvent = this.onEvent;
|
|
191
210
|
if(!this.http2CdnServer.create()){
|
|
192
211
|
this.error = this.http2CdnServer.error;
|
|
193
212
|
return false;
|
|
@@ -196,6 +215,13 @@ class AppServerFactory
|
|
|
196
215
|
this.error = this.http2CdnServer.error;
|
|
197
216
|
return false;
|
|
198
217
|
}
|
|
218
|
+
EventDispatcher.dispatch(
|
|
219
|
+
this.onEvent,
|
|
220
|
+
'http2-cdn-created',
|
|
221
|
+
'appServerFactory',
|
|
222
|
+
this,
|
|
223
|
+
{port: this.http2CdnPort}
|
|
224
|
+
);
|
|
199
225
|
return true;
|
|
200
226
|
}
|
|
201
227
|
|
|
@@ -223,7 +249,8 @@ class AppServerFactory
|
|
|
223
249
|
{
|
|
224
250
|
let detectConfig = {
|
|
225
251
|
developmentDomains: this.developmentDomains,
|
|
226
|
-
domains: this.domains
|
|
252
|
+
domains: this.domains,
|
|
253
|
+
onEvent: this.onEvent
|
|
227
254
|
};
|
|
228
255
|
if(0 < this.developmentPatterns.length){
|
|
229
256
|
detectConfig['developmentPatterns'] = this.developmentPatterns;
|
|
@@ -257,7 +284,8 @@ class AppServerFactory
|
|
|
257
284
|
this.protocolEnforcer.setup(this.app, {
|
|
258
285
|
isDevelopmentMode: this.isDevelopmentMode,
|
|
259
286
|
useHttps: this.useHttps,
|
|
260
|
-
enforceProtocol: this.enforceProtocol
|
|
287
|
+
enforceProtocol: this.enforceProtocol,
|
|
288
|
+
onEvent: this.onEvent
|
|
261
289
|
});
|
|
262
290
|
}
|
|
263
291
|
|
|
@@ -267,11 +295,13 @@ class AppServerFactory
|
|
|
267
295
|
isDevelopmentMode: this.isDevelopmentMode,
|
|
268
296
|
useHelmet: this.useHelmet,
|
|
269
297
|
helmetConfig: this.helmetConfig,
|
|
270
|
-
developmentExternalDomains: this.developmentExternalDomains
|
|
298
|
+
developmentExternalDomains: this.developmentExternalDomains,
|
|
299
|
+
onEvent: this.onEvent
|
|
271
300
|
});
|
|
272
301
|
this.securityConfigurer.setupXssProtection(this.app, {
|
|
273
302
|
useXssProtection: this.useXssProtection,
|
|
274
|
-
sanitizeOptions: this.sanitizeOptions
|
|
303
|
+
sanitizeOptions: this.sanitizeOptions,
|
|
304
|
+
onEvent: this.onEvent
|
|
275
305
|
});
|
|
276
306
|
}
|
|
277
307
|
|
|
@@ -288,7 +318,8 @@ class AppServerFactory
|
|
|
288
318
|
let corsConfig = {
|
|
289
319
|
isDevelopmentMode: this.isDevelopmentMode,
|
|
290
320
|
useCors: this.useCors,
|
|
291
|
-
corsOrigin: this.corsOrigin
|
|
321
|
+
corsOrigin: this.corsOrigin,
|
|
322
|
+
onEvent: this.onEvent
|
|
292
323
|
};
|
|
293
324
|
if(0 < this.corsMethods.length){
|
|
294
325
|
corsConfig['corsMethods'] = this.corsMethods;
|
|
@@ -318,7 +349,8 @@ class AppServerFactory
|
|
|
318
349
|
maxRequests: this.maxRequests,
|
|
319
350
|
developmentMultiplier: this.developmentMultiplier,
|
|
320
351
|
applyKeyGenerator: this.applyKeyGenerator,
|
|
321
|
-
tooManyRequestsMessage: this.tooManyRequestsMessage
|
|
352
|
+
tooManyRequestsMessage: this.tooManyRequestsMessage,
|
|
353
|
+
onEvent: this.onEvent
|
|
322
354
|
});
|
|
323
355
|
}
|
|
324
356
|
|
|
@@ -342,6 +374,14 @@ class AppServerFactory
|
|
|
342
374
|
}
|
|
343
375
|
}
|
|
344
376
|
|
|
377
|
+
setupRequestLogging()
|
|
378
|
+
{
|
|
379
|
+
if(!this.onRequestSuccess && !this.onRequestError){
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
this.app.use(RequestLogger.createMiddleware(this.onRequestSuccess, this.onRequestError));
|
|
383
|
+
}
|
|
384
|
+
|
|
345
385
|
setupReverseProxy()
|
|
346
386
|
{
|
|
347
387
|
if(!this.reverseProxyEnabled || 0 === this.reverseProxyRules.length){
|
|
@@ -350,7 +390,9 @@ class AppServerFactory
|
|
|
350
390
|
this.reverseProxyConfigurer.setup(this.app, {
|
|
351
391
|
reverseProxyRules: this.reverseProxyRules,
|
|
352
392
|
isDevelopmentMode: this.isDevelopmentMode,
|
|
353
|
-
useVirtualHosts: this.useVirtualHosts
|
|
393
|
+
useVirtualHosts: this.useVirtualHosts,
|
|
394
|
+
onError: this.onError,
|
|
395
|
+
onEvent: this.onEvent
|
|
354
396
|
});
|
|
355
397
|
}
|
|
356
398
|
|
|
@@ -388,6 +430,14 @@ class AppServerFactory
|
|
|
388
430
|
return next();
|
|
389
431
|
}
|
|
390
432
|
this.error = {message: 'No hostname provided and no default domain configured'};
|
|
433
|
+
ServerErrorHandler.handleError(
|
|
434
|
+
this.onError,
|
|
435
|
+
'appServerFactory',
|
|
436
|
+
this,
|
|
437
|
+
'virtual-host-no-hostname',
|
|
438
|
+
this.error,
|
|
439
|
+
{request: req, response: res}
|
|
440
|
+
);
|
|
391
441
|
return res.status(400).send('Bad Request');
|
|
392
442
|
}
|
|
393
443
|
let domain = this.findDomainConfig(hostname);
|
|
@@ -397,6 +447,14 @@ class AppServerFactory
|
|
|
397
447
|
return next();
|
|
398
448
|
}
|
|
399
449
|
this.error = {message: 'Unknown domain: '+hostname};
|
|
450
|
+
ServerErrorHandler.handleError(
|
|
451
|
+
this.onError,
|
|
452
|
+
'appServerFactory',
|
|
453
|
+
this,
|
|
454
|
+
'virtual-host-unknown-domain',
|
|
455
|
+
this.error,
|
|
456
|
+
{hostname: hostname, request: req, response: res}
|
|
457
|
+
);
|
|
400
458
|
return res.status(404).send('Domain not found');
|
|
401
459
|
}
|
|
402
460
|
req.domain = domain;
|
|
@@ -426,7 +484,15 @@ class AppServerFactory
|
|
|
426
484
|
createServer()
|
|
427
485
|
{
|
|
428
486
|
if(!this.useHttps){
|
|
429
|
-
|
|
487
|
+
let httpServer = http.createServer(this.app);
|
|
488
|
+
EventDispatcher.dispatch(
|
|
489
|
+
this.onEvent,
|
|
490
|
+
'http-server-created',
|
|
491
|
+
'appServerFactory',
|
|
492
|
+
this,
|
|
493
|
+
{port: this.port}
|
|
494
|
+
);
|
|
495
|
+
return httpServer;
|
|
430
496
|
}
|
|
431
497
|
if(this.useVirtualHosts && 0 < this.domains.length){
|
|
432
498
|
return this.createHttpsServerWithSNI();
|
|
@@ -453,7 +519,15 @@ class AppServerFactory
|
|
|
453
519
|
credentials.ca = ca;
|
|
454
520
|
}
|
|
455
521
|
}
|
|
456
|
-
|
|
522
|
+
let httpsServer = https.createServer(credentials, this.app);
|
|
523
|
+
EventDispatcher.dispatch(
|
|
524
|
+
this.onEvent,
|
|
525
|
+
'https-server-created',
|
|
526
|
+
'appServerFactory',
|
|
527
|
+
this,
|
|
528
|
+
{port: this.port}
|
|
529
|
+
);
|
|
530
|
+
return httpsServer;
|
|
457
531
|
}
|
|
458
532
|
|
|
459
533
|
createHttpsServerWithSNI()
|
|
@@ -471,17 +545,41 @@ class AppServerFactory
|
|
|
471
545
|
let key = FileHandler.readFile(domain.keyPath, 'Domain Key');
|
|
472
546
|
if(!key){
|
|
473
547
|
this.error = {message: 'Could not read domain SSL key: '+domain.keyPath};
|
|
548
|
+
ServerErrorHandler.handleError(
|
|
549
|
+
this.onError,
|
|
550
|
+
'appServerFactory',
|
|
551
|
+
this,
|
|
552
|
+
'sni-key-read-failure',
|
|
553
|
+
this.error,
|
|
554
|
+
{hostname: hostname, domain: domain, keyPath: domain.keyPath}
|
|
555
|
+
);
|
|
474
556
|
return callback(null, null);
|
|
475
557
|
}
|
|
476
558
|
let cert = FileHandler.readFile(domain.certPath, 'Domain Cert');
|
|
477
559
|
if(!cert){
|
|
478
560
|
this.error = {message: 'Could not read domain SSL certificate: '+domain.certPath};
|
|
561
|
+
ServerErrorHandler.handleError(
|
|
562
|
+
this.onError,
|
|
563
|
+
'appServerFactory',
|
|
564
|
+
this,
|
|
565
|
+
'sni-cert-read-failure',
|
|
566
|
+
this.error,
|
|
567
|
+
{hostname: hostname, domain: domain, certPath: domain.certPath}
|
|
568
|
+
);
|
|
479
569
|
return callback(null, null);
|
|
480
570
|
}
|
|
481
571
|
let ctx = tls.createSecureContext({key, cert});
|
|
482
572
|
callback(null, ctx);
|
|
483
573
|
};
|
|
484
|
-
|
|
574
|
+
let sniServer = https.createServer(httpsOptions, this.app);
|
|
575
|
+
EventDispatcher.dispatch(
|
|
576
|
+
this.onEvent,
|
|
577
|
+
'sni-server-created',
|
|
578
|
+
'appServerFactory',
|
|
579
|
+
this,
|
|
580
|
+
{port: this.port, domainsCount: this.domains.length}
|
|
581
|
+
);
|
|
582
|
+
return sniServer;
|
|
485
583
|
}
|
|
486
584
|
|
|
487
585
|
loadDefaultCredentials()
|
|
@@ -507,6 +605,13 @@ class AppServerFactory
|
|
|
507
605
|
return false;
|
|
508
606
|
}
|
|
509
607
|
this.appServer.listen(listenPort);
|
|
608
|
+
EventDispatcher.dispatch(
|
|
609
|
+
this.onEvent,
|
|
610
|
+
'app-server-listening',
|
|
611
|
+
'appServerFactory',
|
|
612
|
+
this,
|
|
613
|
+
{port: listenPort}
|
|
614
|
+
);
|
|
510
615
|
return true;
|
|
511
616
|
}
|
|
512
617
|
|
|
@@ -568,6 +673,13 @@ class AppServerFactory
|
|
|
568
673
|
return false;
|
|
569
674
|
}
|
|
570
675
|
this.domains.push(domainConfig);
|
|
676
|
+
EventDispatcher.dispatch(
|
|
677
|
+
this.onEvent,
|
|
678
|
+
'domain-added',
|
|
679
|
+
'appServerFactory',
|
|
680
|
+
this,
|
|
681
|
+
{hostname: domainConfig.hostname}
|
|
682
|
+
);
|
|
571
683
|
return true;
|
|
572
684
|
}
|
|
573
685
|
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - CdnRequestHandler
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { FileHandler } = require('./file-handler');
|
|
8
|
+
const { ServerFactoryUtils } = require('./server-factory-utils');
|
|
9
|
+
const { ServerErrorHandler } = require('./server-error-handler');
|
|
10
|
+
|
|
11
|
+
class CdnRequestHandler
|
|
12
|
+
{
|
|
13
|
+
|
|
14
|
+
constructor(cdnServer)
|
|
15
|
+
{
|
|
16
|
+
this.cdnServer = cdnServer;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
handleRequest(requestContext)
|
|
20
|
+
{
|
|
21
|
+
let startTime = Date.now();
|
|
22
|
+
let requestData = {
|
|
23
|
+
serverType: 'cdn',
|
|
24
|
+
method: requestContext.method,
|
|
25
|
+
path: requestContext.path,
|
|
26
|
+
hostname: requestContext.hostname,
|
|
27
|
+
statusCode: 200,
|
|
28
|
+
responseTime: 0,
|
|
29
|
+
ip: requestContext.ip,
|
|
30
|
+
userAgent: requestContext.userAgent,
|
|
31
|
+
timestamp: new Date().toISOString()
|
|
32
|
+
};
|
|
33
|
+
try {
|
|
34
|
+
if(!requestData.path){
|
|
35
|
+
requestContext.sendResponse(400);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let requestOrigin = requestContext.origin || '';
|
|
39
|
+
let allowedOrigin = ServerFactoryUtils.validateOrigin(
|
|
40
|
+
requestOrigin,
|
|
41
|
+
this.cdnServer.corsOrigins,
|
|
42
|
+
this.cdnServer.corsAllowAll
|
|
43
|
+
);
|
|
44
|
+
if('OPTIONS' === requestData.method){
|
|
45
|
+
this.handleOptionsRequest(requestContext, allowedOrigin);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let filePath = this.cdnServer.resolveFilePath(requestData.path);
|
|
49
|
+
if(!filePath){
|
|
50
|
+
requestData.statusCode = 404;
|
|
51
|
+
requestData.responseTime = Date.now()-startTime;
|
|
52
|
+
requestContext.sendResponse(404);
|
|
53
|
+
this.cdnServer.invokeRequestError(requestData);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
let responseHeaders = this.buildResponseHeaders(allowedOrigin, filePath, requestContext.isHttp2);
|
|
57
|
+
requestContext.onSuccess(() => {
|
|
58
|
+
requestData.responseTime = Date.now()-startTime;
|
|
59
|
+
this.cdnServer.invokeRequestSuccess(requestData);
|
|
60
|
+
});
|
|
61
|
+
requestContext.onError((err) => {
|
|
62
|
+
requestData.statusCode = 500;
|
|
63
|
+
requestData.responseTime = Date.now()-startTime;
|
|
64
|
+
requestData.error = err;
|
|
65
|
+
ServerErrorHandler.handleError(
|
|
66
|
+
this.cdnServer.onError,
|
|
67
|
+
'http2CdnServer',
|
|
68
|
+
this.cdnServer,
|
|
69
|
+
requestContext.isHttp2 ? 'stream-error' : 'http1-stream-error',
|
|
70
|
+
err,
|
|
71
|
+
{port: this.cdnServer.port, path: requestData.path}
|
|
72
|
+
);
|
|
73
|
+
this.cdnServer.invokeRequestError(requestData);
|
|
74
|
+
});
|
|
75
|
+
requestContext.sendFile(filePath, responseHeaders);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
requestData.statusCode = 500;
|
|
78
|
+
requestData.responseTime = Date.now()-startTime;
|
|
79
|
+
requestData.error = err;
|
|
80
|
+
ServerErrorHandler.handleError(
|
|
81
|
+
this.cdnServer.onError,
|
|
82
|
+
'http2CdnServer',
|
|
83
|
+
this.cdnServer,
|
|
84
|
+
requestContext.isHttp2 ? 'handle-stream-error' : 'handle-http1-request-error',
|
|
85
|
+
err,
|
|
86
|
+
{port: this.cdnServer.port, path: requestData.path}
|
|
87
|
+
);
|
|
88
|
+
this.cdnServer.invokeRequestError(requestData);
|
|
89
|
+
requestContext.sendResponse(500);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
handleOptionsRequest(requestContext, allowedOrigin)
|
|
94
|
+
{
|
|
95
|
+
let optionsHeaders = requestContext.isHttp2 ? {':status': 200} : {};
|
|
96
|
+
if(allowedOrigin){
|
|
97
|
+
optionsHeaders['access-control-allow-origin'] = allowedOrigin;
|
|
98
|
+
}
|
|
99
|
+
optionsHeaders['access-control-allow-methods'] = this.cdnServer.corsMethods;
|
|
100
|
+
optionsHeaders['access-control-allow-headers'] = this.cdnServer.corsHeaders;
|
|
101
|
+
requestContext.sendResponse(200, optionsHeaders);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
buildResponseHeaders(allowedOrigin, filePath, isHttp2)
|
|
105
|
+
{
|
|
106
|
+
let ext = FileHandler.extension(filePath);
|
|
107
|
+
let cacheAge = ServerFactoryUtils.getCacheConfigForPath(filePath, this.cdnServer.cacheConfig);
|
|
108
|
+
let responseHeaders = isHttp2 ? {':status': 200} : {};
|
|
109
|
+
let securityKeys = Object.keys(this.cdnServer.securityHeaders);
|
|
110
|
+
for(let headerKey of securityKeys){
|
|
111
|
+
responseHeaders[headerKey] = this.cdnServer.securityHeaders[headerKey];
|
|
112
|
+
}
|
|
113
|
+
if(allowedOrigin){
|
|
114
|
+
responseHeaders['access-control-allow-origin'] = allowedOrigin;
|
|
115
|
+
}
|
|
116
|
+
if(this.cdnServer.varyHeader){
|
|
117
|
+
responseHeaders['vary'] = this.cdnServer.varyHeader;
|
|
118
|
+
}
|
|
119
|
+
if(cacheAge){
|
|
120
|
+
responseHeaders['cache-control'] = 'public, max-age='+cacheAge+', immutable';
|
|
121
|
+
}
|
|
122
|
+
if(this.cdnServer.mimeTypes[ext]){
|
|
123
|
+
responseHeaders['content-type'] = this.cdnServer.mimeTypes[ext];
|
|
124
|
+
}
|
|
125
|
+
return responseHeaders;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports.CdnRequestHandler = CdnRequestHandler;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
*
|
|
3
|
+
* Reldens - EventDispatcher
|
|
4
|
+
*
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class EventDispatcher
|
|
8
|
+
{
|
|
9
|
+
|
|
10
|
+
static dispatch(onEventCallback, eventType, instanceName, instance, data = {})
|
|
11
|
+
{
|
|
12
|
+
if('function' !== typeof onEventCallback){
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
onEventCallback({
|
|
16
|
+
eventType: eventType,
|
|
17
|
+
instanceName: instanceName,
|
|
18
|
+
instance: instance,
|
|
19
|
+
data: data,
|
|
20
|
+
timestamp: new Date().toISOString()
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports.EventDispatcher = EventDispatcher;
|
package/lib/file-handler.js
CHANGED
|
@@ -765,6 +765,19 @@ class FileHandler
|
|
|
765
765
|
return this.writeFile(filePath, content.replace(searchValue, replaceValue));
|
|
766
766
|
}
|
|
767
767
|
|
|
768
|
+
createReadStream(filePath, options)
|
|
769
|
+
{
|
|
770
|
+
if(!this.isValidPath(filePath)){
|
|
771
|
+
this.error = {message: 'Invalid file path.', filePath};
|
|
772
|
+
return false;
|
|
773
|
+
}
|
|
774
|
+
if(!this.isFile(filePath)){
|
|
775
|
+
this.error = {message: 'File does not exist or is not a file.', filePath};
|
|
776
|
+
return false;
|
|
777
|
+
}
|
|
778
|
+
return fs.createReadStream(filePath, options);
|
|
779
|
+
}
|
|
780
|
+
|
|
768
781
|
}
|
|
769
782
|
|
|
770
783
|
module.exports.FileHandler = new FileHandler();
|