@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 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: function(res){
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
- this.appServer = this.createServer();
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 = {message: 'Failed to create app server'};
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.setHeaders = (res) => {
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: ' + hostname};
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: ' + this.keyPath};
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: ' + this.certPath};
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.32.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
- "game",
21
- "mmorpg",
22
- "rpg",
20
+ "server",
21
+ "http",
22
+ "https",
23
23
  "dwd",
24
- "colyseus",
25
- "phaser",
26
- "parcel",
24
+ "http2",
25
+ "file",
26
+ "encrypt",
27
+ "uploader",
28
+ "upload",
29
+ "cors",
27
30
  "nodejs",
28
- "mmo",
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
  }