rtjscomp 0.8.1 → 0.8.3

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.
Files changed (2) hide show
  1. package/package.json +16 -9
  2. package/rtjscomp.js +241 -170
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "rtjscomp",
3
- "author": "L3P3 <dev@l3p3.de> (https://l3p3.de)",
4
- "license": "Zlib",
5
- "version": "0.8.1",
3
+ "version": "0.8.3",
6
4
  "description": "php-like server but with javascript",
7
- "keywords": [
8
- "http",
9
- "javascript",
10
- "server"
11
- ],
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/L3P3/rtjscomp"
8
+ },
9
+ "bugs": {
10
+ "url": "https://github.com/L3P3/rtjscomp/issues"
11
+ },
12
12
  "dependencies": {
13
13
  "ipware": "latest",
14
14
  "parse-multipart-data": "latest",
@@ -19,5 +19,12 @@
19
19
  },
20
20
  "bin": {
21
21
  "rtjscomp": "./rtjscomp.js"
22
- }
22
+ },
23
+ "keywords": [
24
+ "http",
25
+ "javascript",
26
+ "server"
27
+ ],
28
+ "author": "L3P3 <dev@l3p3.de> (https://l3p3.de)",
29
+ "license": "Zlib"
23
30
  }
package/rtjscomp.js CHANGED
@@ -25,7 +25,9 @@ const GZIP_OPTIONS = {level: 9};
25
25
  const AGENT_CHECK_BOT = /bot|googlebot|crawler|spider|robot|crawling|favicon/i;
26
26
  const AGENT_CHECK_MOBIL = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
27
27
  const HTTP_LIST_REG = /,\s*/;
28
+ const IMPORT_REG = /import\(/g;
28
29
 
30
+ let log_verbose = process.argv.includes('-v');
29
31
  let port_http = 0;
30
32
  let port_https = 0;
31
33
  const file_type_mimes = new Map;
@@ -61,7 +63,7 @@ if (!Object.fromEntries) {
61
63
  global.globals = rtjscomp;
62
64
  global.actions = rtjscomp.actions;
63
65
  global.data_load = name => {
64
- log('[deprecated!] load: ' + name);
66
+ log('[deprecated] synchronous load: ' + PATH_DATA + name);
65
67
  try {
66
68
  return fs.readFileSync(PATH_DATA + name, 'utf8');
67
69
  }
@@ -70,7 +72,7 @@ global.data_load = name => {
70
72
  }
71
73
  }
72
74
  global.data_save = (name, data) => (
73
- log('[deprecated!] save: ' + name),
75
+ log('[deprecated] synchronous save: ' + PATH_DATA + name),
74
76
  fs.writeFileSync(PATH_DATA + name, data, 'utf8')
75
77
  )
76
78
  global.number_check_int = number => (
@@ -81,13 +83,12 @@ global.number_check_uint = number => (
81
83
  )
82
84
 
83
85
  rtjscomp.data_load = async name => {
84
- log('load: ' + name);
86
+ if (log_verbose) log('load: ' + PATH_DATA + name);
85
87
  const data = await fsp.readFile(PATH_DATA + name, 'utf8').catch(() => null);
86
88
  return name.endsWith('.json') ? JSON.parse(data || null) : data;
87
89
  }
88
90
  rtjscomp.data_load_watch = (name, callback) => (
89
91
  file_keep_new(PATH_DATA + name, data => (
90
- log('load: ' + name),
91
92
  callback(
92
93
  name.endsWith('.json')
93
94
  ? JSON.parse(data || null)
@@ -96,7 +97,7 @@ rtjscomp.data_load_watch = (name, callback) => (
96
97
  ))
97
98
  )
98
99
  rtjscomp.data_save = (name, data) => (
99
- log('save: ' + name),
100
+ log_verbose && log('save: ' + PATH_DATA + name),
100
101
  fsp.writeFile(
101
102
  PATH_DATA + name,
102
103
  name.endsWith('.json') ? JSON.stringify(data) : data,
@@ -104,83 +105,143 @@ rtjscomp.data_save = (name, data) => (
104
105
  )
105
106
  )
106
107
 
108
+ const custom_require_paths = new Set;
107
109
  const custom_require_cache = new Map;
110
+ const custom_import_cache = new Map;
108
111
  const custom_require = path => {
109
112
  let result = custom_require_cache.get(path);
110
113
  if (result != null) return result;
111
114
 
115
+ log_verbose && log('require: ' + path);
116
+ const path_real = require.resolve(path, resolve_options);
117
+ custom_require_paths.add(path_real);
112
118
  custom_require_cache.set(
113
119
  path,
114
- result = require(
115
- require.resolve(path, resolve_options)
120
+ result = require(path_real)
121
+ );
122
+ return result;
123
+ }
124
+ const custom_import = async path => {
125
+ let result = custom_import_cache.get(path);
126
+ if (result != null) return result;
127
+
128
+ log_verbose && log('import: ' + path);
129
+ custom_import_cache.set(
130
+ path,
131
+ result = await import(
132
+ 'file://' + require.resolve(path, resolve_options)
116
133
  )
117
134
  );
118
135
  return result;
119
136
  }
137
+ actions.module_cache_clear = () => {
138
+ for (const path of custom_require_paths) {
139
+ delete require.cache[path];
140
+ }
141
+ custom_require_cache.clear();
142
+ custom_import_cache.clear();
143
+ }
120
144
 
121
145
  const services_active = new Map;
146
+ const services_loading = new Set;
122
147
  const services_list_react = async () => {
123
148
  await Promise.all(
124
149
  Array.from(services_active.entries())
125
150
  .filter(([path, _]) => !services.has(path))
126
151
  .map(([_, service_object]) => service_stop(service_object, true))
127
152
  );
128
- for (const path of services) {
129
- if (!services_active.has(path)) await service_start(path);
153
+ for (const path of services)
154
+ if (!services_active.has(path)) {
155
+ await service_start(path);
130
156
  }
131
157
  }
132
158
  const service_start = async path => {
133
159
  const service_object = {
160
+ content: null,
161
+ handler_stop: null,
134
162
  path,
135
- start: null,
136
- started: false,
137
- stop: null,
138
163
  stopped: false,
139
164
  };
140
165
 
141
166
  await file_keep_new(PATH_PUBLIC + path + '.service.js', async file_content => {
142
167
  if (file_content === null) {
143
- log('error, service file not found: ' + path);
144
- await service_stop(service_object, true);
145
- return;
168
+ log('[error] service file not found: ' + path);
169
+ return service_stop(service_object, true);
146
170
  }
147
- await service_stop_handler(service_object);
148
- await service_start_inner(path, service_object, file_content);
171
+ if (services_loading.size > 0) {
172
+ await Promise.all(Array.from(services_loading));
173
+ }
174
+ const start_promise = service_start_inner(path, service_object, file_content);
175
+ services_loading.add(start_promise);
176
+ await start_promise;
177
+ services_loading.delete(start_promise);
149
178
  });
150
179
  }
151
180
  const service_start_inner = async (path, service_object, file_content) => {
181
+ if (services_active.has(path)) {
182
+ await service_stop_handler(service_object);
183
+ }
184
+ const content_object = service_object.content = {};
185
+ log('start service: ' + path);
186
+
187
+ const start_interval = setInterval(() => {
188
+ log(`[warning] ${path}: still starting`);
189
+ }, 1e3);
190
+
191
+ if (file_content.includes('globals.')) {
192
+ log(`[deprecated] ${path}: uses globals object`);
193
+ }
194
+
152
195
  try {
153
- const fun = new Function(
154
- 'require',
155
- `const log=a=>rtjscomp.log(${
196
+ const fun = (0, eval)(
197
+ `(async function(require,custom_import){const log=a=>rtjscomp.log(${
156
198
  JSON.stringify(path + ': ')
157
- }+a);${file_content}`
199
+ }+a);${
200
+ file_content.replace(IMPORT_REG, 'custom_import(') + '\n'
201
+ }})`
158
202
  );
159
- fun.call(service_object, custom_require);
203
+ const result = await fun.call(content_object, custom_require, custom_import);
204
+ if (service_object.stopped) {
205
+ clearInterval(start_interval);
206
+ return;
207
+ }
208
+ if (typeof result === 'function') {
209
+ service_object.handler_stop = result;
210
+ }
160
211
  }
161
212
  catch (err) {
162
- log(`error in service ${path}: ${err.message}`);
163
- await service_stop(service_object, false);
164
- return;
213
+ clearInterval(start_interval);
214
+ log(`[error] ${path}: ${err.message}`);
215
+ if (service_object.stopped) return;
216
+ return service_stop(service_object, false);
165
217
  }
166
218
 
167
- if (service_object.start) {
219
+ const handler_start = content_object.start;
220
+ if (handler_start) {
221
+ log(`[deprecated] ${path}: has start method`);
222
+ delete content_object.start;
168
223
  try {
169
- await service_object.start();
170
- service_object.start = null;
171
- service_object.started = true;
224
+ await handler_start();
172
225
  }
173
226
  catch (err) {
174
- services_active.delete(path);
175
- log(`error while starting ${path}: ${err.message}`);
176
- return;
227
+ clearInterval(start_interval);
228
+ log(`[error] ${path} start: ${err.message}`);
229
+ return service_stop(service_object, false);
177
230
  }
178
231
  }
232
+ clearInterval(start_interval);
233
+
234
+ if (content_object.stop) {
235
+ log(`[deprecated] ${path}: has stop method`);
236
+ service_object.handler_stop = content_object.stop;
237
+ delete content_object.stop;
238
+ }
239
+
179
240
  services_active.set(path, service_object);
180
- log('service started: ' + path);
241
+ if (log_verbose) log('started service: ' + path);
181
242
  }
182
243
  const services_shutdown = () => (
183
- log('shutdown services...'),
244
+ log_verbose && log('shutdown services...'),
184
245
  Promise.all(
185
246
  Array.from(services_active.values())
186
247
  .map(service_object => service_stop(service_object, true))
@@ -190,28 +251,39 @@ const service_stop = async (service_object, forget) => {
190
251
  service_object.stopped = true;
191
252
  if (forget) fs.unwatchFile(PATH_PUBLIC + service_object.path + '.service.js');
192
253
  await service_stop_handler(service_object);
193
- services_active.delete(service_object.path);
194
- log('service stopped: ' + service_object.path);
195
254
  }
196
255
  const service_stop_handler = async service_object => {
197
- if (service_object.stop) {
256
+ services_active.delete(service_object.path);
257
+ log('stop service: ' + service_object.path);
258
+ const handler_stop = service_object.handler_stop;
259
+ if (handler_stop) {
260
+ const stop_interval = setInterval(() => {
261
+ log(`[warning] ${service_object.path}: still stopping`);
262
+ }, 1e3);
198
263
  try {
199
- await service_object.stop();
200
- service_object.stop = null;
264
+ service_object.handler_stop = null;
265
+ await handler_stop();
201
266
  }
202
267
  catch (err) {
203
- log(`error while stopping ${service_object.path}: ${err.message}`);
268
+ log(`[error] ${service_object.path} stop: ${err.message}`);
204
269
  }
270
+ clearInterval(stop_interval);
205
271
  }
272
+ if (log_verbose) log('stopped service: ' + service_object.path);
206
273
  }
207
274
  global.service_require = path => {
208
275
  const service = services_active.get(path);
209
- if (service) return service;
276
+ if (service != null) return service.content;
210
277
  throw new Error('service required: ' + path);
211
278
  }
212
- global.service_require_try = path => (
213
- services_active.get(path) || null
214
- )
279
+ global.service_require_try = path => {
280
+ const service = services_active.get(path);
281
+ return (
282
+ service != null
283
+ ? service.content
284
+ : null
285
+ );
286
+ }
215
287
 
216
288
  const map_generate_bol = (set, data) => {
217
289
  set.clear();
@@ -237,7 +309,7 @@ const map_generate_equ = (map, data) => {
237
309
 
238
310
  const file_compare = (curr, prev, path) => (
239
311
  curr.mtime > prev.mtime && (
240
- log('file changed: ' + path),
312
+ log_verbose && log('file changed: ' + path),
241
313
  true
242
314
  )
243
315
  )
@@ -251,7 +323,9 @@ const file_watch = (path, callback) => (
251
323
  )
252
324
  const file_keep_new = async (path, callback) => {
253
325
  try {
254
- await callback(await fsp.readFile(path, 'utf8'));
326
+ const data = await fsp.readFile(path, 'utf8');
327
+ if (log_verbose) log('load file: ' + path);
328
+ await callback(data);
255
329
  fs.watchFile(path, async (curr, prev) => {
256
330
  if (file_compare(curr, prev, path)) {
257
331
  await callback(
@@ -272,7 +346,7 @@ actions.log_clear = () => {
272
346
  const log = rtjscomp.log = msg => (
273
347
  console.log(msg),
274
348
  log_history.push(msg),
275
- spam('log', [msg])
349
+ spam_enabled ? spam('log', [msg]) : undefined
276
350
  )
277
351
 
278
352
  const spam_enabled = fs.existsSync('spam.csv');
@@ -281,17 +355,16 @@ actions.spam_save = async (muted = false) => {
281
355
  if (!spam_enabled) return;
282
356
 
283
357
  try {
284
- fsp.appendFile('spam.csv', rtjscomp.spam_history, 'utf8');
358
+ const tmp = rtjscomp.spam_history;
285
359
  rtjscomp.spam_history = '';
286
- muted || log('spam.csv saved');
360
+ await fsp.appendFile('spam.csv', tmp, 'utf8');
361
+ if (log_verbose && !muted) log('spam.csv saved');
287
362
  }
288
363
  catch (err) {
289
- log('error saving spam.csv: ' + err.message);
364
+ log('[error] cannot save spam.csv: ' + err.message);
290
365
  }
291
366
  }
292
367
  const spam = (type, data) => {
293
- if (!spam_enabled) return;
294
-
295
368
  rtjscomp.spam_history += (
296
369
  Date.now() +
297
370
  ',' +
@@ -315,7 +388,7 @@ const request_handle = async (request, response, https) => {
315
388
  https = request_headers['x-forwarded-proto'] === 'https';
316
389
  }
317
390
 
318
- spam('request', [https, request.url, request_ip]);
391
+ if (spam_enabled) spam('request', [https, request.url, request_ip]);
319
392
 
320
393
  try {
321
394
  const request_url_parsed = url.parse(request.url, false);
@@ -422,11 +495,11 @@ const request_handle = async (request, response, https) => {
422
495
  file_function = file_cache_functions.get(path);
423
496
  }
424
497
  else {
425
- log(`load ${
498
+ if (log_verbose) log(`load ${
426
499
  file_dyn_enabled
427
500
  ? 'dynam'
428
501
  : 'stat'
429
- }ic file: ${path}`);
502
+ }ic file: ${path_real}`);
430
503
 
431
504
  if (
432
505
  file_privates.has(path) ||
@@ -442,9 +515,12 @@ const request_handle = async (request, response, https) => {
442
515
  if (file_content.includes('\r')) {
443
516
  throw 'illegal line break, must be unix';
444
517
  }
518
+ if (file_content.includes('globals.')) {
519
+ log(`[deprecated] ${path}: uses globals object`);
520
+ }
445
521
  const file_content_length = file_content.length;
446
522
 
447
- let code = `async (input,output,request,response,require)=>{const log=a=>rtjscomp.log(${
523
+ let code = `async (input,output,request,response,require,custom_import)=>{const log=a=>rtjscomp.log(${
448
524
  JSON.stringify(path + ': ')
449
525
  }+a);`;
450
526
 
@@ -469,19 +545,23 @@ const request_handle = async (request, response, https) => {
469
545
  // `<?`?
470
546
  if (file_content.charCodeAt(index_start) !== 61) {
471
547
  code += (
472
- file_content.substring(
473
- index_start,
474
- index_end
475
- ) +
548
+ file_content
549
+ .substring(
550
+ index_start,
551
+ index_end
552
+ )
553
+ .replace(IMPORT_REG, 'custom_import(') +
476
554
  ';'
477
555
  );
478
556
  }
479
557
  else { // `<?=`?
480
558
  code += `output.write(''+(${
481
- file_content.substring(
482
- ++index_start,
483
- index_end
484
- )
559
+ file_content
560
+ .substring(
561
+ ++index_start,
562
+ index_end
563
+ )
564
+ .replace(IMPORT_REG, 'custom_import(')
485
565
  }));`;
486
566
  }
487
567
  }
@@ -516,14 +596,14 @@ const request_handle = async (request, response, https) => {
516
596
  }
517
597
 
518
598
  try {
519
- file_function = eval(code += '}');
599
+ file_function = (0, eval)(code += '}');
520
600
  }
521
601
  catch (err) {
522
602
  throw err.message;
523
603
  }
524
604
  }
525
605
  catch (err) {
526
- log('compile error: ' + err);
606
+ log(`[error] ${path} compile: ${err}`);
527
607
  throw 500;
528
608
  }
529
609
 
@@ -578,7 +658,7 @@ const request_handle = async (request, response, https) => {
578
658
  );
579
659
  }
580
660
  catch (err) {
581
- log('request query error: ' + err.message);
661
+ log(`[error] ${path} request query: ${err.message}`);
582
662
  throw 400;
583
663
  }
584
664
  }
@@ -613,7 +693,7 @@ const request_handle = async (request, response, https) => {
613
693
  }
614
694
  }
615
695
  catch (err) {
616
- log('request body error: ' + err.message);
696
+ log(`[error] ${path} request body: ${err.message}`);
617
697
  throw 400;
618
698
  }
619
699
  }
@@ -641,7 +721,7 @@ const request_handle = async (request, response, https) => {
641
721
  file_function_output = response;
642
722
  }
643
723
 
644
- spam('execute', [
724
+ if (spam_enabled) spam('execute', [
645
725
  path,
646
726
  Object.fromEntries(
647
727
  Object.entries(file_function_input)
@@ -653,19 +733,24 @@ const request_handle = async (request, response, https) => {
653
733
  )
654
734
  ]);
655
735
 
736
+ if (services_loading.size > 0) {
737
+ await Promise.all(Array.from(services_loading));
738
+ }
739
+
656
740
  try {
657
741
  await file_function(
658
742
  file_function_input,
659
743
  file_function_output,
660
744
  request,
661
745
  response,
662
- custom_require
746
+ custom_require,
747
+ custom_import
663
748
  );
664
749
  file_function_output.end();
665
750
  }
666
751
  catch (err) {
667
752
  if (err instanceof Error) {
668
- log(`error in file ${path}: ${err.message}`);
753
+ log(`[error] ${path}: ${err.message}`);
669
754
 
670
755
  if (err.message.startsWith('service required: ')) {
671
756
  err = 503;
@@ -698,7 +783,7 @@ const request_handle = async (request, response, https) => {
698
783
  file_data = fs.createReadStream(path_real);
699
784
  }
700
785
 
701
- spam('static_send', [path, file_gz_enabled]);
786
+ if (spam_enabled) spam('static_send', [path, file_gz_enabled]);
702
787
  response.setHeader('Cache-Control', 'public, max-age=600');
703
788
 
704
789
  if (file_gz_enabled) {
@@ -718,8 +803,8 @@ const request_handle = async (request, response, https) => {
718
803
  err = 500;
719
804
  }
720
805
 
721
- if (err >= 400) {
722
- log(`error ${err} at request: ${request_ip}; ${request.url}`);
806
+ if (err >= 400 && log_verbose) {
807
+ log(`[error] request failed: ${err}; ${request_ip}; ${request.url}`);
723
808
  }
724
809
 
725
810
  response.writeHead(err, {
@@ -733,32 +818,33 @@ const request_handle = async (request, response, https) => {
733
818
  let exiting = false;
734
819
  actions.halt = async () => {
735
820
  await Promise.all([
736
- actions.http_stop(),
821
+ actions.http_stop && actions.http_stop(),
737
822
  actions.https_stop && actions.https_stop(),
738
823
  services_shutdown(),
739
- ].filter(Boolean));
824
+ ]);
740
825
  await actions.spam_save();
741
826
  log('stopped everything');
742
827
  }
743
828
  actions.exit = async status => {
744
829
  if (exiting) return;
830
+ exiting = true;
745
831
  if (typeof status !== 'number') status = 0;
746
832
  await actions.halt();
747
833
  log('exiting...');
748
- exiting = true;
749
834
  process.exit(status);
750
835
  }
751
836
 
752
837
  process.on('uncaughtException', err => {
753
- err = err.message || err;
754
838
  if (typeof err === 'symbol') err = err.toString();
755
- log('error uncaughtException: ' + err);
756
- console.log(err);
839
+ log('[error] uncaughtException: ' + (err.message || err));
840
+ console.error(err);
841
+ if (exiting) process.exit(1);
757
842
  actions.exit(1);
758
843
  });
759
844
  process.on('unhandledRejection', err => {
760
- log('error unhandledRejection: ' + (err.message || err));
761
- console.log(err);
845
+ log('[error] unhandledRejection: ' + (err.message || err));
846
+ console.error(err);
847
+ if (exiting) process.exit(1);
762
848
  actions.exit(1);
763
849
  });
764
850
  process.on('exit', actions.exit);
@@ -776,7 +862,7 @@ await Promise.all([
776
862
  fsp.stat(PATH_PUBLIC).catch(_ => null),
777
863
  ]).then(([stat_config, stat_data, stat_public]) => {
778
864
  if (!stat_config) {
779
- log('creating config template directory');
865
+ log('create config template directory');
780
866
  fs.mkdirSync(PATH_CONFIG);
781
867
  fs.mkdirSync(PATH_CONFIG + 'ssl');
782
868
  for (const file of 'file_type_dyns,file_type_mimes,file_type_nocompress,path_aliases,port_http,port_https,services'.split(',')) {
@@ -798,32 +884,30 @@ await Promise.all([
798
884
  }
799
885
  });
800
886
 
887
+ file_keep_new(PATH_CONFIG + 'services.txt', data => (
888
+ map_generate_bol(services, data),
889
+ services_list_react()
890
+ ));
891
+
801
892
  await Promise.all([
802
893
  file_keep_new(PATH_CONFIG + 'init.js', data => {
803
894
  if (!data) return;
804
- log('[deprecated!] run global init script');
895
+ log('[deprecated] run global init script');
805
896
  try {
806
897
  var require = custom_require;
807
- eval(data);
898
+ (0, eval)(data);
808
899
  }
809
900
  catch (err) {
810
- log('error in init.js: ' + err.message);
901
+ log('[error] init.js: ' + err.message);
811
902
  }
812
903
  }),
813
- file_keep_new(PATH_CONFIG + 'services.txt', async data => {
814
- log('load service list');
815
- map_generate_bol(services, data);
816
- await services_list_react();
817
- }),
818
904
  file_keep_new(PATH_CONFIG + 'file_type_mimes.txt', data => {
819
- log('load file type map');
820
905
  map_generate_equ(file_type_mimes, data);
821
906
  if (!file_type_mimes.has('txt')) {
822
907
  file_type_mimes.set('txt', 'text/plain; charset=utf-8');
823
908
  }
824
909
  }),
825
910
  file_keep_new(PATH_CONFIG + 'path_aliases.txt', data => {
826
- log('load path aliases map');
827
911
  map_generate_equ(path_aliases, data);
828
912
  path_aliases_templates.clear();
829
913
  for (const [key, value] of path_aliases.entries()) {
@@ -843,38 +927,37 @@ await Promise.all([
843
927
  }
844
928
  }),
845
929
  file_keep_new(PATH_CONFIG + 'file_type_dyns.txt', data => {
846
- log('load dynamic file type list');
847
930
  map_generate_bol(file_type_dyns, data);
848
931
  }),
849
932
  file_keep_new(PATH_CONFIG + 'file_type_nocompress.txt', data => {
850
- log('load non-compressable file list');
851
933
  map_generate_bol(file_type_nocompress, data);
852
934
  }),
853
935
  file_keep_new(PATH_CONFIG + 'file_raws.txt', data => {
854
936
  if (!data) return;
855
- log('load static file list');
856
937
  map_generate_bol(file_raws, data);
857
938
  }),
858
939
  file_keep_new(PATH_CONFIG + 'file_privates.txt', data => {
859
940
  if (!data) return;
860
- log('load private file list');
861
941
  map_generate_bol(file_privates, data);
862
942
  }),
863
943
  file_keep_new(PATH_CONFIG + 'file_blocks.txt', data => {
864
944
  if (!data) return;
865
- log('load blocked file list');
866
945
  map_generate_bol(file_blocks, data);
867
946
  }),
868
947
  ]);
869
948
 
870
949
  let connections_count = 0;
871
- const server_http = http.createServer(
872
- (request, response) => request_handle(request, response, false)
873
- );
874
950
  let http_status = false;
875
951
  let http_status_target = false;
952
+ let http_listened_resolve = null;
876
953
  const http_connections = new Map;
877
-
954
+ const server_http = http.createServer(
955
+ (request, response) => request_handle(request, response, false)
956
+ );
957
+ server_http.on('error', err => {
958
+ log('[error] http: ' + err.message);
959
+ http_listened_resolve && http_listened_resolve();
960
+ });
878
961
  server_http.on('connection', connection => {
879
962
  const id = ++connections_count;
880
963
  http_connections.set(id, connection);
@@ -883,71 +966,67 @@ server_http.on('connection', connection => {
883
966
  });
884
967
  });
885
968
 
886
- actions.http_start = () => {
887
- if (http_status) return;
888
- try {
889
- server_http.listen(port_http);
890
- http_status = http_status_target = true;
891
- log('http started at port ' + port_http);
892
- }
893
- catch (err) {
894
- log('error while starting http: ' + err.message);
895
- }
896
- }
897
- actions.http_restart = () => {
898
- if (!http_status) actions.http_start();
899
- else if (http_status_target) {
900
- http_status_target = false;
901
- log('http is restarting...');
902
- server_http.close(() => {
903
- http_status = false;
904
- log('http stopped');
905
- actions.http_start();
906
- });
969
+ actions.http_start = async () => {
970
+ await actions.http_stop();
971
+ http_status_target = true;
972
+ log('start http: http://localhost:' + port_http);
973
+ await new Promise(resolve => server_http.listen(port_http, http_listened_resolve = resolve));
974
+ if (http_listened_resolve) http_listened_resolve = null;
975
+ else{
976
+ http_status = true;
977
+ if (log_verbose) log('started http');
907
978
  }
908
979
  }
909
980
  actions.http_stop = async () => {
910
- if (!http_status_target || !http_status) return;
981
+ if (!http_status_target) return;
911
982
  http_status_target = false;
912
- log('http is stopping...');
983
+ log('stop http');
984
+ const kill_timeout = setTimeout(actions.http_kill, 5e3);
913
985
  await new Promise(resolve => server_http.close(resolve));
986
+ clearTimeout(kill_timeout);
914
987
  http_status = false;
915
- log('http stopped');
988
+ if (log_verbose) log('stopped http');
916
989
  }
917
- actions.http_kill = () => {
918
- if (http_status_target || !http_status) return;
919
- log('killing http...');
920
- for (const connection of http_connections.values()) connection.destroy();
990
+ actions.http_kill = async () => {
991
+ if (http_status_target) return;
992
+ log('kill http');
993
+ await Promise.all(
994
+ Array.from(http_connections.values())
995
+ .map(connection => connection.destroy())
996
+ );
997
+ if (log_verbose) log('killed http');
921
998
  http_connections.clear();
922
999
  }
923
1000
 
924
1001
  file_keep_new(PATH_CONFIG + 'port_http.txt', data => {
925
- log('load http port number');
926
1002
  if (
927
1003
  !data ||
928
1004
  isNaN(data = Number(data)) ||
929
1005
  !number_check_uint(data)
930
1006
  ) {
931
- log('error: invalid http port number');
1007
+ log('[error] http: invalid port number');
932
1008
  }
933
1009
  else if (data !== port_http) {
934
1010
  port_http = data;
935
- actions.http_restart();
1011
+ actions.http_start();
936
1012
  }
937
1013
  });
938
1014
 
939
1015
  try {
940
1016
  const https_key = fs.readFileSync(PATH_CONFIG + 'ssl/domain.key');
941
1017
  const https_cert = fs.readFileSync(PATH_CONFIG + 'ssl/chained.pem');
1018
+ let https_status = false;
1019
+ let https_status_target = false;
1020
+ let https_listened_resolve = null;
1021
+ const https_connections = new Map;
942
1022
  const server_https = require('https').createServer(
943
1023
  {key: https_key, cert: https_cert},
944
1024
  (request, response) => request_handle(request, response, true)
945
1025
  );
946
-
947
- let https_status = false;
948
- let https_status_target = false;
949
- const https_connections = new Map;
950
-
1026
+ server_https.on('error', err => {
1027
+ log('[error] https: ' + err.message);
1028
+ https_listened_resolve && https_listened_resolve();
1029
+ });
951
1030
  server_https.on('connection', connection => {
952
1031
  const id = ++connections_count;
953
1032
  https_connections.set(id, connection);
@@ -956,61 +1035,53 @@ try {
956
1035
  });
957
1036
  });
958
1037
 
959
- actions.https_start = () => {
960
- if (https_status) return;
961
- try {
962
- server_https.listen(port_https);
963
- https_status = https_status_target = true;
964
- log('https started at port ' + port_https);
965
- }
966
- catch (err) {
967
- log('error while starting https: ' + err.message);
968
- }
969
- }
970
- actions.https_restart = () => {
971
- if (!https_status) actions.https_start();
972
- else if (https_status_target) {
973
- https_status_target = false;
974
- log('https is restarting...');
975
- server_https.close(function () {
976
- https_status = false;
977
- log('https stopped');
978
- actions.https_start();
979
- });
1038
+ actions.https_start = async () => {
1039
+ await actions.https_stop();
1040
+ https_status_target = true;
1041
+ log('start https: https://localhost:' + port_https);
1042
+ await new Promise(resolve => server_https.listen(port_https, https_listened_resolve = resolve));
1043
+ if (https_listened_resolve) https_listened_resolve = null;
1044
+ else{
1045
+ https_status = true;
1046
+ if (log_verbose) log('started https');
980
1047
  }
981
1048
  }
982
1049
  actions.https_stop = async () => {
983
- if (!https_status_target || !https_status) return;
1050
+ if (!https_status_target) return;
984
1051
  https_status_target = false;
985
- log('https is stopping...');
1052
+ log('stop https');
1053
+ const kill_timeout = setTimeout(actions.https_kill, 5000);
986
1054
  await new Promise(resolve => server_https.close(resolve));
1055
+ clearTimeout(kill_timeout);
987
1056
  https_status = false;
988
- log('https stopped');
1057
+ if (log_verbose) log('stopped https');
989
1058
  }
990
- actions.https_kill = () => {
991
- if (https_status_target || !https_status) return;
992
- log('killing https...');
993
- for (const connection of https_connections.values()) connection.destroy();
1059
+ actions.https_kill = async () => {
1060
+ if (https_status_target) return;
1061
+ log('kill https');
1062
+ await Promise.all(
1063
+ Array.from(https_connections.values()).map(connection => connection.destroy())
1064
+ );
1065
+ if (log_verbose) log('killed https');
994
1066
  https_connections.clear();
995
1067
  }
996
1068
 
997
1069
  file_keep_new(PATH_CONFIG + 'port_https.txt', data => {
998
- log('load https port number');
999
1070
  if (
1000
1071
  !data ||
1001
1072
  isNaN(data = Number(data)) ||
1002
1073
  !number_check_uint(data)
1003
1074
  ) {
1004
- log('error: invalid https port number');
1075
+ log('[error] https: invalid port number');
1005
1076
  }
1006
1077
  else if (data !== port_https) {
1007
1078
  port_https = data;
1008
- actions.https_restart();
1079
+ actions.https_start();
1009
1080
  }
1010
1081
  });
1011
1082
  }
1012
1083
  catch (err) {
1013
- log('https is disabled');
1084
+ if (log_verbose) log('https: no cert, disabled');
1014
1085
  }
1015
1086
 
1016
1087
  })();