@reldens/server-utils 0.32.0 → 0.34.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/README.md +40 -0
- package/lib/app-server-factory.js +68 -10
- package/package.json +13 -11
package/README.md
CHANGED
|
@@ -9,6 +9,8 @@ A Node.js server toolkit providing secure application server creation, file hand
|
|
|
9
9
|
### AppServerFactory
|
|
10
10
|
- Complete Express.js server configuration with modular security
|
|
11
11
|
- HTTPS/HTTP server creation with SSL certificate management
|
|
12
|
+
- HTTP/2 support via spdy with automatic HTTP/1.1 fallback
|
|
13
|
+
- Optimized static asset caching for CSS, JS, fonts, and images
|
|
12
14
|
- SNI (Server Name Indication) support for multi-domain hosting
|
|
13
15
|
- Virtual host management with domain mapping
|
|
14
16
|
- Development mode detection with appropriate configurations
|
|
@@ -99,6 +101,40 @@ if(serverResult){
|
|
|
99
101
|
}
|
|
100
102
|
```
|
|
101
103
|
|
|
104
|
+
### HTTP/2 Server with Optimized Caching
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
let appServerFactory = new AppServerFactory();
|
|
108
|
+
let serverResult = appServerFactory.createAppServer({
|
|
109
|
+
port: 443,
|
|
110
|
+
useHttps: true,
|
|
111
|
+
useHttp2: true,
|
|
112
|
+
keyPath: '/ssl/server.key',
|
|
113
|
+
certPath: '/ssl/server.crt',
|
|
114
|
+
autoListen: true
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Cache configuration is automatic with defaults:
|
|
119
|
+
- CSS/JS: 1 year
|
|
120
|
+
- Fonts: 1 year
|
|
121
|
+
- Images: 30 days
|
|
122
|
+
|
|
123
|
+
Override cache settings if needed:
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
let appServerFactory = new AppServerFactory();
|
|
127
|
+
appServerFactory.cacheConfig = {
|
|
128
|
+
'.css': 86400,
|
|
129
|
+
'.js': 86400,
|
|
130
|
+
'.png': 604800
|
|
131
|
+
};
|
|
132
|
+
let serverResult = appServerFactory.createAppServer({
|
|
133
|
+
useHttps: true,
|
|
134
|
+
useHttp2: true
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
102
138
|
### File Operations
|
|
103
139
|
|
|
104
140
|
```javascript
|
|
@@ -193,6 +229,7 @@ appServerFactory.addDomain({
|
|
|
193
229
|
|
|
194
230
|
let serverResult = appServerFactory.createAppServer({
|
|
195
231
|
useHttps: true,
|
|
232
|
+
useHttp2: true,
|
|
196
233
|
useVirtualHosts: true,
|
|
197
234
|
keyPath: '/ssl/default.key',
|
|
198
235
|
certPath: '/ssl/default.crt',
|
|
@@ -328,6 +365,9 @@ Configurable rate limiting with development mode detection for appropriate thres
|
|
|
328
365
|
### HTTPS Support
|
|
329
366
|
Full SSL/TLS support with SNI for multi-domain hosting and automatic certificate management.
|
|
330
367
|
|
|
368
|
+
### HTTP/2 Support
|
|
369
|
+
HTTP/2 support via spdy with multiplexing for improved performance and automatic HTTP/1.1 fallback for older clients.
|
|
370
|
+
|
|
331
371
|
### Input Validation
|
|
332
372
|
Built-in validators for common input types including email, username, strong passwords, alphanumeric strings, and IP addresses.
|
|
333
373
|
|
|
@@ -12,6 +12,7 @@ const { CorsConfigurer } = require('./app-server-factory/cors-configurer');
|
|
|
12
12
|
const { RateLimitConfigurer } = require('./app-server-factory/rate-limit-configurer');
|
|
13
13
|
const http = require('http');
|
|
14
14
|
const https = require('https');
|
|
15
|
+
const spdy = require('spdy');
|
|
15
16
|
const express = require('express');
|
|
16
17
|
const bodyParser = require('body-parser');
|
|
17
18
|
const session = require('express-session');
|
|
@@ -34,6 +35,7 @@ class AppServerFactory
|
|
|
34
35
|
this.useUrlencoded = true;
|
|
35
36
|
this.encoding = 'utf-8';
|
|
36
37
|
this.useHttps = false;
|
|
38
|
+
this.useHttp2 = false;
|
|
37
39
|
this.passphrase = '';
|
|
38
40
|
this.httpsChain = '';
|
|
39
41
|
this.keyPath = '';
|
|
@@ -61,15 +63,28 @@ class AppServerFactory
|
|
|
61
63
|
this.defaultDomain = '';
|
|
62
64
|
this.maxRequestSize = '10mb';
|
|
63
65
|
this.sanitizeOptions = {allowedTags: [], allowedAttributes: {}};
|
|
66
|
+
this.cacheConfig = {
|
|
67
|
+
'.css': 31536000,
|
|
68
|
+
'.js': 31536000,
|
|
69
|
+
'.woff': 31536000,
|
|
70
|
+
'.woff2': 31536000,
|
|
71
|
+
'.ttf': 31536000,
|
|
72
|
+
'.eot': 31536000,
|
|
73
|
+
'.jpg': 2592000,
|
|
74
|
+
'.jpeg': 2592000,
|
|
75
|
+
'.png': 2592000,
|
|
76
|
+
'.gif': 2592000,
|
|
77
|
+
'.webp': 2592000,
|
|
78
|
+
'.svg': 2592000,
|
|
79
|
+
'.ico': 2592000
|
|
80
|
+
};
|
|
64
81
|
this.staticOptions = {
|
|
65
82
|
maxAge: '1d',
|
|
83
|
+
immutable: false,
|
|
66
84
|
etag: true,
|
|
67
85
|
lastModified: true,
|
|
68
86
|
index: false,
|
|
69
|
-
setHeaders:
|
|
70
|
-
res.set('X-Content-Type-Options', 'nosniff');
|
|
71
|
-
res.set('X-Frame-Options', 'DENY');
|
|
72
|
-
}
|
|
87
|
+
setHeaders: this.buildStaticHeaders.bind(this)
|
|
73
88
|
};
|
|
74
89
|
this.isDevelopmentMode = false;
|
|
75
90
|
this.developmentDomains = [];
|
|
@@ -98,6 +113,28 @@ class AppServerFactory
|
|
|
98
113
|
};
|
|
99
114
|
}
|
|
100
115
|
|
|
116
|
+
buildStaticHeaders(res, path)
|
|
117
|
+
{
|
|
118
|
+
res.set('X-Content-Type-Options', 'nosniff');
|
|
119
|
+
res.set('X-Frame-Options', 'DENY');
|
|
120
|
+
res.set('Vary', 'Accept-Encoding');
|
|
121
|
+
let cacheMaxAge = this.getCacheConfigForPath(path);
|
|
122
|
+
if(cacheMaxAge){
|
|
123
|
+
res.set('Cache-Control', 'public, max-age='+cacheMaxAge+', immutable');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getCacheConfigForPath(path)
|
|
128
|
+
{
|
|
129
|
+
let cacheKeys = Object.keys(this.cacheConfig);
|
|
130
|
+
for(let ext of cacheKeys){
|
|
131
|
+
if(path.endsWith(ext)){
|
|
132
|
+
return this.cacheConfig[ext];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
101
138
|
createAppServer(appServerConfig)
|
|
102
139
|
{
|
|
103
140
|
if(appServerConfig){
|
|
@@ -114,9 +151,16 @@ class AppServerFactory
|
|
|
114
151
|
this.setupRateLimiting();
|
|
115
152
|
this.setupRequestParsing();
|
|
116
153
|
this.setupTrustedProxy();
|
|
117
|
-
|
|
154
|
+
try {
|
|
155
|
+
this.appServer = this.createServer();
|
|
156
|
+
} catch (error) {
|
|
157
|
+
this.error = {message: 'Server creation exception: '+error.message};
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
118
160
|
if(!this.appServer){
|
|
119
|
-
this.error
|
|
161
|
+
if(!this.error.message){
|
|
162
|
+
this.error = {message: 'The createServer() returned false - check certificate paths and permissions'};
|
|
163
|
+
}
|
|
120
164
|
return false;
|
|
121
165
|
}
|
|
122
166
|
if(this.autoListen){
|
|
@@ -165,12 +209,14 @@ class AppServerFactory
|
|
|
165
209
|
if(!this.isDevelopmentMode){
|
|
166
210
|
return;
|
|
167
211
|
}
|
|
168
|
-
this.staticOptions.
|
|
212
|
+
this.staticOptions.immutable = false;
|
|
213
|
+
this.staticOptions.setHeaders = (res, path) => {
|
|
169
214
|
res.set('X-Content-Type-Options', 'nosniff');
|
|
170
215
|
res.set('X-Frame-Options', 'SAMEORIGIN');
|
|
171
216
|
res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
|
|
172
217
|
res.set('Pragma', 'no-cache');
|
|
173
218
|
res.set('Expires', '0');
|
|
219
|
+
res.set('Vary', 'Accept-Encoding');
|
|
174
220
|
};
|
|
175
221
|
}
|
|
176
222
|
|
|
@@ -306,7 +352,7 @@ class AppServerFactory
|
|
|
306
352
|
req.domain = this.defaultDomain;
|
|
307
353
|
return next();
|
|
308
354
|
}
|
|
309
|
-
this.error = {message: 'Unknown domain: '
|
|
355
|
+
this.error = {message: 'Unknown domain: '+hostname};
|
|
310
356
|
return res.status(404).send('Domain not found');
|
|
311
357
|
}
|
|
312
358
|
req.domain = domain;
|
|
@@ -336,6 +382,10 @@ class AppServerFactory
|
|
|
336
382
|
createServer()
|
|
337
383
|
{
|
|
338
384
|
if(!this.useHttps){
|
|
385
|
+
if(this.useHttp2){
|
|
386
|
+
this.error = {message: 'HTTP/2 requires HTTPS to be enabled'};
|
|
387
|
+
return false;
|
|
388
|
+
}
|
|
339
389
|
return http.createServer(this.app);
|
|
340
390
|
}
|
|
341
391
|
if(this.useVirtualHosts && 0 < this.domains.length){
|
|
@@ -348,12 +398,12 @@ class AppServerFactory
|
|
|
348
398
|
{
|
|
349
399
|
let key = FileHandler.readFile(this.keyPath, 'Key');
|
|
350
400
|
if(!key){
|
|
351
|
-
this.error = {message: 'Could not read SSL key file: '
|
|
401
|
+
this.error = {message: 'Could not read SSL key file: '+this.keyPath};
|
|
352
402
|
return false;
|
|
353
403
|
}
|
|
354
404
|
let cert = FileHandler.readFile(this.certPath, 'Cert');
|
|
355
405
|
if(!cert){
|
|
356
|
-
this.error = {message: 'Could not read SSL certificate file: '
|
|
406
|
+
this.error = {message: 'Could not read SSL certificate file: '+this.certPath};
|
|
357
407
|
return false;
|
|
358
408
|
}
|
|
359
409
|
let credentials = {key, cert, passphrase: this.passphrase};
|
|
@@ -363,6 +413,10 @@ class AppServerFactory
|
|
|
363
413
|
credentials.ca = ca;
|
|
364
414
|
}
|
|
365
415
|
}
|
|
416
|
+
if(this.useHttp2){
|
|
417
|
+
credentials.spdy = {protocols: ['h2', 'http/1.1']};
|
|
418
|
+
return spdy.createServer(credentials, this.app);
|
|
419
|
+
}
|
|
366
420
|
return https.createServer(credentials, this.app);
|
|
367
421
|
}
|
|
368
422
|
|
|
@@ -391,6 +445,10 @@ class AppServerFactory
|
|
|
391
445
|
let ctx = tls.createSecureContext({key, cert});
|
|
392
446
|
callback(null, ctx);
|
|
393
447
|
};
|
|
448
|
+
if(this.useHttp2){
|
|
449
|
+
httpsOptions.spdy = {protocols: ['h2', 'http/1.1']};
|
|
450
|
+
return spdy.createServer(httpsOptions, this.app);
|
|
451
|
+
}
|
|
394
452
|
return https.createServer(httpsOptions, this.app);
|
|
395
453
|
}
|
|
396
454
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reldens/server-utils",
|
|
3
3
|
"scope": "@reldens",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.34.0",
|
|
5
5
|
"description": "Reldens - Server Utils",
|
|
6
6
|
"author": "Damian A. Pastorini",
|
|
7
7
|
"license": "MIT",
|
|
@@ -17,17 +17,18 @@
|
|
|
17
17
|
"utils",
|
|
18
18
|
"shortcuts",
|
|
19
19
|
"system",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
20
|
+
"server",
|
|
21
|
+
"http",
|
|
22
|
+
"https",
|
|
23
23
|
"dwd",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
24
|
+
"http2",
|
|
25
|
+
"file",
|
|
26
|
+
"encrypt",
|
|
27
|
+
"uploader",
|
|
28
|
+
"upload",
|
|
29
|
+
"cors",
|
|
27
30
|
"nodejs",
|
|
28
|
-
"
|
|
29
|
-
"multiplayer",
|
|
30
|
-
"rol",
|
|
31
|
+
"protocol",
|
|
31
32
|
"platform",
|
|
32
33
|
"framework"
|
|
33
34
|
],
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"express-session": "1.18.2",
|
|
44
45
|
"helmet": "8.1.0",
|
|
45
46
|
"multer": "2.0.2",
|
|
46
|
-
"sanitize-html": "2.17.0"
|
|
47
|
+
"sanitize-html": "2.17.0",
|
|
48
|
+
"spdy": "^4.0.2"
|
|
47
49
|
}
|
|
48
50
|
}
|