@reldens/server-utils 0.43.0 → 0.45.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.
@@ -10,6 +10,8 @@ const { ServerDefaultConfigurations } = require('./server-default-configurations
10
10
  const { ServerFactoryUtils } = require('./server-factory-utils');
11
11
  const { ServerHeaders } = require('./server-headers');
12
12
  const { ServerErrorHandler } = require('./server-error-handler');
13
+ const { RequestLogger } = require('./request-logger');
14
+ const { EventDispatcher } = require('./event-dispatcher');
13
15
  const { DevelopmentModeDetector } = require('./app-server-factory/development-mode-detector');
14
16
  const { ProtocolEnforcer } = require('./app-server-factory/protocol-enforcer');
15
17
  const { SecurityConfigurer } = require('./app-server-factory/security-configurer');
@@ -113,10 +115,14 @@ class AppServerFactory
113
115
  this.http2CdnCorsAllowAll = false;
114
116
  this.http2CdnMimeTypes = {};
115
117
  this.http2CdnCacheConfig = {};
118
+ this.http2CdnSecurityHeaders = {};
116
119
  this.http2CdnServer = false;
117
120
  this.reverseProxyEnabled = false;
118
121
  this.reverseProxyRules = [];
119
122
  this.onError = null;
123
+ this.onRequestSuccess = null;
124
+ this.onRequestError = null;
125
+ this.onEvent = null;
120
126
  }
121
127
 
122
128
  buildStaticHeaders(res, path)
@@ -148,6 +154,7 @@ class AppServerFactory
148
154
  this.setupCors();
149
155
  this.setupRateLimiting();
150
156
  this.setupRequestParsing();
157
+ this.setupRequestLogging();
151
158
  this.setupTrustedProxy();
152
159
  try {
153
160
  this.appServer = this.createServer();
@@ -161,6 +168,13 @@ class AppServerFactory
161
168
  }
162
169
  return false;
163
170
  }
171
+ EventDispatcher.dispatch(
172
+ this.onEvent,
173
+ 'app-server-created',
174
+ 'appServerFactory',
175
+ this,
176
+ {port: this.port, useHttps: this.useHttps, isDevelopmentMode: this.isDevelopmentMode}
177
+ );
164
178
  if(this.http2CdnEnabled){
165
179
  if(!this.createHttp2CdnServer()){
166
180
  this.error = {message: 'The createHttp2CdnServer() returned false.'};
@@ -190,7 +204,13 @@ class AppServerFactory
190
204
  }
191
205
  this.http2CdnServer.corsOrigins = this.http2CdnCorsOrigins;
192
206
  this.http2CdnServer.corsAllowAll = this.http2CdnCorsAllowAll;
207
+ if(this.http2CdnSecurityHeaders && 0 < Object.keys(this.http2CdnSecurityHeaders).length){
208
+ this.http2CdnServer.securityHeaders = this.http2CdnSecurityHeaders;
209
+ }
193
210
  this.http2CdnServer.onError = this.onError;
211
+ this.http2CdnServer.onRequestSuccess = this.onRequestSuccess;
212
+ this.http2CdnServer.onRequestError = this.onRequestError;
213
+ this.http2CdnServer.onEvent = this.onEvent;
194
214
  if(!this.http2CdnServer.create()){
195
215
  this.error = this.http2CdnServer.error;
196
216
  return false;
@@ -199,6 +219,13 @@ class AppServerFactory
199
219
  this.error = this.http2CdnServer.error;
200
220
  return false;
201
221
  }
222
+ EventDispatcher.dispatch(
223
+ this.onEvent,
224
+ 'http2-cdn-created',
225
+ 'appServerFactory',
226
+ this,
227
+ {port: this.http2CdnPort}
228
+ );
202
229
  return true;
203
230
  }
204
231
 
@@ -226,7 +253,8 @@ class AppServerFactory
226
253
  {
227
254
  let detectConfig = {
228
255
  developmentDomains: this.developmentDomains,
229
- domains: this.domains
256
+ domains: this.domains,
257
+ onEvent: this.onEvent
230
258
  };
231
259
  if(0 < this.developmentPatterns.length){
232
260
  detectConfig['developmentPatterns'] = this.developmentPatterns;
@@ -260,7 +288,8 @@ class AppServerFactory
260
288
  this.protocolEnforcer.setup(this.app, {
261
289
  isDevelopmentMode: this.isDevelopmentMode,
262
290
  useHttps: this.useHttps,
263
- enforceProtocol: this.enforceProtocol
291
+ enforceProtocol: this.enforceProtocol,
292
+ onEvent: this.onEvent
264
293
  });
265
294
  }
266
295
 
@@ -270,11 +299,13 @@ class AppServerFactory
270
299
  isDevelopmentMode: this.isDevelopmentMode,
271
300
  useHelmet: this.useHelmet,
272
301
  helmetConfig: this.helmetConfig,
273
- developmentExternalDomains: this.developmentExternalDomains
302
+ developmentExternalDomains: this.developmentExternalDomains,
303
+ onEvent: this.onEvent
274
304
  });
275
305
  this.securityConfigurer.setupXssProtection(this.app, {
276
306
  useXssProtection: this.useXssProtection,
277
- sanitizeOptions: this.sanitizeOptions
307
+ sanitizeOptions: this.sanitizeOptions,
308
+ onEvent: this.onEvent
278
309
  });
279
310
  }
280
311
 
@@ -291,7 +322,8 @@ class AppServerFactory
291
322
  let corsConfig = {
292
323
  isDevelopmentMode: this.isDevelopmentMode,
293
324
  useCors: this.useCors,
294
- corsOrigin: this.corsOrigin
325
+ corsOrigin: this.corsOrigin,
326
+ onEvent: this.onEvent
295
327
  };
296
328
  if(0 < this.corsMethods.length){
297
329
  corsConfig['corsMethods'] = this.corsMethods;
@@ -321,7 +353,8 @@ class AppServerFactory
321
353
  maxRequests: this.maxRequests,
322
354
  developmentMultiplier: this.developmentMultiplier,
323
355
  applyKeyGenerator: this.applyKeyGenerator,
324
- tooManyRequestsMessage: this.tooManyRequestsMessage
356
+ tooManyRequestsMessage: this.tooManyRequestsMessage,
357
+ onEvent: this.onEvent
325
358
  });
326
359
  }
327
360
 
@@ -345,6 +378,14 @@ class AppServerFactory
345
378
  }
346
379
  }
347
380
 
381
+ setupRequestLogging()
382
+ {
383
+ if(!this.onRequestSuccess && !this.onRequestError){
384
+ return;
385
+ }
386
+ this.app.use(RequestLogger.createMiddleware(this.onRequestSuccess, this.onRequestError));
387
+ }
388
+
348
389
  setupReverseProxy()
349
390
  {
350
391
  if(!this.reverseProxyEnabled || 0 === this.reverseProxyRules.length){
@@ -354,7 +395,8 @@ class AppServerFactory
354
395
  reverseProxyRules: this.reverseProxyRules,
355
396
  isDevelopmentMode: this.isDevelopmentMode,
356
397
  useVirtualHosts: this.useVirtualHosts,
357
- onError: this.onError
398
+ onError: this.onError,
399
+ onEvent: this.onEvent
358
400
  });
359
401
  }
360
402
 
@@ -446,7 +488,15 @@ class AppServerFactory
446
488
  createServer()
447
489
  {
448
490
  if(!this.useHttps){
449
- return http.createServer(this.app);
491
+ let httpServer = http.createServer(this.app);
492
+ EventDispatcher.dispatch(
493
+ this.onEvent,
494
+ 'http-server-created',
495
+ 'appServerFactory',
496
+ this,
497
+ {port: this.port}
498
+ );
499
+ return httpServer;
450
500
  }
451
501
  if(this.useVirtualHosts && 0 < this.domains.length){
452
502
  return this.createHttpsServerWithSNI();
@@ -473,7 +523,15 @@ class AppServerFactory
473
523
  credentials.ca = ca;
474
524
  }
475
525
  }
476
- return https.createServer(credentials, this.app);
526
+ let httpsServer = https.createServer(credentials, this.app);
527
+ EventDispatcher.dispatch(
528
+ this.onEvent,
529
+ 'https-server-created',
530
+ 'appServerFactory',
531
+ this,
532
+ {port: this.port}
533
+ );
534
+ return httpsServer;
477
535
  }
478
536
 
479
537
  createHttpsServerWithSNI()
@@ -517,7 +575,15 @@ class AppServerFactory
517
575
  let ctx = tls.createSecureContext({key, cert});
518
576
  callback(null, ctx);
519
577
  };
520
- return https.createServer(httpsOptions, this.app);
578
+ let sniServer = https.createServer(httpsOptions, this.app);
579
+ EventDispatcher.dispatch(
580
+ this.onEvent,
581
+ 'sni-server-created',
582
+ 'appServerFactory',
583
+ this,
584
+ {port: this.port, domainsCount: this.domains.length}
585
+ );
586
+ return sniServer;
521
587
  }
522
588
 
523
589
  loadDefaultCredentials()
@@ -543,6 +609,13 @@ class AppServerFactory
543
609
  return false;
544
610
  }
545
611
  this.appServer.listen(listenPort);
612
+ EventDispatcher.dispatch(
613
+ this.onEvent,
614
+ 'app-server-listening',
615
+ 'appServerFactory',
616
+ this,
617
+ {port: listenPort}
618
+ );
546
619
  return true;
547
620
  }
548
621
 
@@ -604,6 +677,13 @@ class AppServerFactory
604
677
  return false;
605
678
  }
606
679
  this.domains.push(domainConfig);
680
+ EventDispatcher.dispatch(
681
+ this.onEvent,
682
+ 'domain-added',
683
+ 'appServerFactory',
684
+ this,
685
+ {hostname: domainConfig.hostname}
686
+ );
607
687
  return true;
608
688
  }
609
689
 
@@ -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;
@@ -10,6 +10,8 @@ const { ServerDefaultConfigurations } = require('./server-default-configurations
10
10
  const { ServerFactoryUtils } = require('./server-factory-utils');
11
11
  const { ServerHeaders } = require('./server-headers');
12
12
  const { ServerErrorHandler } = require('./server-error-handler');
13
+ const { EventDispatcher } = require('./event-dispatcher');
14
+ const { CdnRequestHandler } = require('./cdn-request-handler');
13
15
 
14
16
  class Http2CdnServer
15
17
  {
@@ -35,6 +37,10 @@ class Http2CdnServer
35
37
  this.securityHeaders = this.serverHeaders.http2SecurityHeaders;
36
38
  this.varyHeader = this.serverHeaders.http2VaryHeader;
37
39
  this.onError = null;
40
+ this.onRequestSuccess = null;
41
+ this.onRequestError = null;
42
+ this.onEvent = null;
43
+ this.requestHandler = new CdnRequestHandler(this);
38
44
  }
39
45
 
40
46
  create()
@@ -62,6 +68,13 @@ class Http2CdnServer
62
68
  }
63
69
  this.http2Server = http2.createSecureServer(options);
64
70
  this.setupEventHandlers();
71
+ EventDispatcher.dispatch(
72
+ this.onEvent,
73
+ 'cdn-server-created',
74
+ 'http2CdnServer',
75
+ this,
76
+ {port: this.port, allowHTTP1: this.allowHTTP1}
77
+ );
65
78
  return true;
66
79
  }
67
80
 
@@ -103,160 +116,111 @@ class Http2CdnServer
103
116
  {port: this.port}
104
117
  );
105
118
  });
119
+ EventDispatcher.dispatch(
120
+ this.onEvent,
121
+ 'cdn-handlers-setup',
122
+ 'http2CdnServer',
123
+ this,
124
+ {port: this.port}
125
+ );
126
+ }
127
+
128
+ invokeRequestSuccess(requestData)
129
+ {
130
+ if('function' !== typeof this.onRequestSuccess){
131
+ return;
132
+ }
133
+ this.onRequestSuccess(requestData);
134
+ }
135
+
136
+ invokeRequestError(errorData)
137
+ {
138
+ if('function' !== typeof this.onRequestError){
139
+ return;
140
+ }
141
+ this.onRequestError(errorData);
106
142
  }
107
143
 
108
144
  handleStream(stream, headers)
109
145
  {
110
- try {
111
- let requestPath = headers[':path'];
112
- if(!requestPath){
113
- stream.respond({':status': 400});
114
- stream.end();
115
- return;
116
- }
117
- let requestOrigin = headers['origin'] || '';
118
- let allowedOrigin = ServerFactoryUtils.validateOrigin(requestOrigin, this.corsOrigins, this.corsAllowAll);
119
- if('OPTIONS' === headers[':method']){
120
- let optionsHeaders = {':status': 200};
121
- if(allowedOrigin){
122
- optionsHeaders['access-control-allow-origin'] = allowedOrigin;
146
+ let requestContext = {
147
+ isHttp2: true,
148
+ method: headers[':method'],
149
+ path: headers[':path'],
150
+ hostname: headers['x-forwarded-host'] || headers[':authority'],
151
+ origin: headers['origin'] || '',
152
+ ip: null,
153
+ userAgent: null,
154
+ sendResponse: (statusCode, responseHeaders) => {
155
+ if(responseHeaders){
156
+ stream.respond(responseHeaders);
157
+ stream.end();
158
+ return;
123
159
  }
124
- optionsHeaders['access-control-allow-methods'] = this.corsMethods;
125
- optionsHeaders['access-control-allow-headers'] = this.corsHeaders;
126
- stream.respond(optionsHeaders);
127
- stream.end();
128
- return;
129
- }
130
- let filePath = this.resolveFilePath(requestPath);
131
- if(!filePath){
132
- stream.respond({':status': 404});
160
+ stream.respond({':status': statusCode});
133
161
  stream.end();
134
- return;
162
+ },
163
+ sendFile: (filePath, responseHeaders) => {
164
+ stream.on('error', (err) => {
165
+ requestContext.errorCallback(err);
166
+ });
167
+ stream.on('finish', () => {
168
+ requestContext.successCallback();
169
+ });
170
+ stream.respondWithFile(filePath, responseHeaders);
171
+ },
172
+ onSuccess: (callback) => {
173
+ requestContext.successCallback = callback;
174
+ },
175
+ onError: (callback) => {
176
+ requestContext.errorCallback = callback;
135
177
  }
136
- let ext = FileHandler.extension(filePath);
137
- let cacheAge = ServerFactoryUtils.getCacheConfigForPath(filePath, this.cacheConfig);
138
- let responseHeaders = {':status': 200};
139
- let securityKeys = Object.keys(this.securityHeaders);
140
- for(let headerKey of securityKeys){
141
- responseHeaders[headerKey] = this.securityHeaders[headerKey];
142
- }
143
- if(allowedOrigin){
144
- responseHeaders['access-control-allow-origin'] = allowedOrigin;
145
- }
146
- if(this.varyHeader){
147
- responseHeaders['vary'] = this.varyHeader;
148
- }
149
- if(cacheAge){
150
- responseHeaders['cache-control'] = 'public, max-age='+cacheAge+', immutable';
151
- }
152
- if(this.mimeTypes[ext]){
153
- responseHeaders['content-type'] = this.mimeTypes[ext];
154
- }
155
- stream.on('error', (err) => {
156
- ServerErrorHandler.handleError(
157
- this.onError,
158
- 'http2CdnServer',
159
- this,
160
- 'stream-error',
161
- err,
162
- {port: this.port, path: requestPath}
163
- );
164
- });
165
- stream.respondWithFile(filePath, responseHeaders);
166
- } catch (err) {
167
- ServerErrorHandler.handleError(
168
- this.onError,
169
- 'http2CdnServer',
170
- this,
171
- 'handle-stream-error',
172
- err,
173
- {port: this.port, path: headers[':path']}
174
- );
175
- if(!stream.destroyed){
176
- stream.respond({':status': 500});
177
- stream.end();
178
- }
179
- }
178
+ };
179
+ this.requestHandler.handleRequest(requestContext);
180
180
  }
181
181
 
182
182
  handleHttp1Request(req, res)
183
183
  {
184
- try {
185
- let requestPath = req.url;
186
- if(!requestPath){
187
- res.writeHead(400);
188
- res.end();
189
- return;
190
- }
191
- let requestOrigin = req.headers['origin'] || '';
192
- let allowedOrigin = ServerFactoryUtils.validateOrigin(requestOrigin, this.corsOrigins, this.corsAllowAll);
193
- if('OPTIONS' === req.method){
194
- let optionsHeaders = {};
195
- if(allowedOrigin){
196
- optionsHeaders['access-control-allow-origin'] = allowedOrigin;
197
- }
198
- optionsHeaders['access-control-allow-methods'] = this.corsMethods;
199
- optionsHeaders['access-control-allow-headers'] = this.corsHeaders;
200
- res.writeHead(200, optionsHeaders);
201
- res.end();
202
- return;
203
- }
204
- let filePath = this.resolveFilePath(requestPath);
205
- if(!filePath){
206
- res.writeHead(404);
207
- res.end();
208
- return;
209
- }
210
- let ext = FileHandler.extension(filePath);
211
- let cacheAge = ServerFactoryUtils.getCacheConfigForPath(filePath, this.cacheConfig);
212
- let responseHeaders = {};
213
- let securityKeys = Object.keys(this.securityHeaders);
214
- for(let headerKey of securityKeys){
215
- responseHeaders[headerKey] = this.securityHeaders[headerKey];
216
- }
217
- if(allowedOrigin){
218
- responseHeaders['access-control-allow-origin'] = allowedOrigin;
219
- }
220
- if(this.varyHeader){
221
- responseHeaders['vary'] = this.varyHeader;
222
- }
223
- if(cacheAge){
224
- responseHeaders['cache-control'] = 'public, max-age='+cacheAge+', immutable';
225
- }
226
- if(this.mimeTypes[ext]){
227
- responseHeaders['content-type'] = this.mimeTypes[ext];
228
- }
229
- let fileStream = FileHandler.createReadStream(filePath);
230
- fileStream.on('error', (err) => {
231
- ServerErrorHandler.handleError(
232
- this.onError,
233
- 'http2CdnServer',
234
- this,
235
- 'http1-stream-error',
236
- err,
237
- {port: this.port, path: requestPath}
238
- );
239
- if(!res.headersSent){
240
- res.writeHead(500);
184
+ let requestContext = {
185
+ isHttp2: false,
186
+ method: req.method,
187
+ path: req.url,
188
+ hostname: req.headers['x-forwarded-host'] || req.headers.host,
189
+ origin: req.headers['origin'] || '',
190
+ ip: req.headers['x-forwarded-for'] || req.socket.remoteAddress,
191
+ userAgent: req.headers['user-agent'],
192
+ sendResponse: (statusCode, responseHeaders) => {
193
+ if(responseHeaders){
194
+ res.writeHead(statusCode, responseHeaders);
241
195
  res.end();
196
+ return;
242
197
  }
243
- });
244
- res.writeHead(200, responseHeaders);
245
- fileStream.pipe(res);
246
- } catch (err) {
247
- ServerErrorHandler.handleError(
248
- this.onError,
249
- 'http2CdnServer',
250
- this,
251
- 'handle-http1-request-error',
252
- err,
253
- {port: this.port, path: req.url}
254
- );
255
- if(!res.headersSent){
256
- res.writeHead(500);
198
+ res.writeHead(statusCode);
257
199
  res.end();
200
+ },
201
+ sendFile: (filePath, responseHeaders) => {
202
+ let fileStream = FileHandler.createReadStream(filePath);
203
+ fileStream.on('error', (err) => {
204
+ if(!res.headersSent){
205
+ res.writeHead(500);
206
+ res.end();
207
+ }
208
+ requestContext.errorCallback(err);
209
+ });
210
+ res.writeHead(200, responseHeaders);
211
+ fileStream.pipe(res);
212
+ res.on('finish', () => {
213
+ requestContext.successCallback();
214
+ });
215
+ },
216
+ onSuccess: (callback) => {
217
+ requestContext.successCallback = callback;
218
+ },
219
+ onError: (callback) => {
220
+ requestContext.errorCallback = callback;
258
221
  }
259
- }
222
+ };
223
+ this.requestHandler.handleRequest(requestContext);
260
224
  }
261
225
 
262
226
  resolveFilePath(requestPath)
@@ -282,6 +246,13 @@ class Http2CdnServer
282
246
  return false;
283
247
  }
284
248
  this.http2Server.listen(this.port);
249
+ EventDispatcher.dispatch(
250
+ this.onEvent,
251
+ 'cdn-server-listening',
252
+ 'http2CdnServer',
253
+ this,
254
+ {port: this.port}
255
+ );
285
256
  return true;
286
257
  }
287
258