Skip to content

Commit 6a61ecc

Browse files
committed
feat: https server configuration
1 parent 5995d7a commit 6a61ecc

File tree

6 files changed

+138
-80
lines changed

6 files changed

+138
-80
lines changed

src/app/client/HostTracker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export class HostTracker extends ManagerClient<ParamsBase, HostTrackerEvents> {
6767
// this.emit('hosts', msg.data);
6868
if (msg.data.local) {
6969
msg.data.local.forEach(({ type }) => {
70-
const secure = location.protocol === 'https';
70+
const secure = location.protocol === 'https:';
7171
const port = location.port ? parseInt(location.port, 10) : secure ? 443 : 80;
7272
const { hostname } = location;
7373
if (type !== 'android' && type !== 'ios') {

src/server/Config.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import * as process from 'process';
22
import * as fs from 'fs';
33
import * as path from 'path';
4-
import { Configuration, HostItem } from '../types/Configuration';
4+
import { Configuration, HostItem, ServerItem } from '../types/Configuration';
55
import { EnvName } from './EnvName';
66

7+
const DEFAULT_PORT = 8000;
8+
79
export class Config {
810
private static instance?: Config;
911
public static getInstance(defaultConfig?: Configuration): Config {
@@ -35,19 +37,16 @@ export class Config {
3537
if (!configPath) {
3638
return;
3739
}
38-
const isAbsolute = configPath.startsWith('/');
39-
const absolutePath = isAbsolute ? configPath : path.resolve(process.cwd(), configPath);
40+
this.fullConfig = JSON.parse(this.readFile(configPath));
41+
}
42+
43+
public readFile(pathString: string): string {
44+
const isAbsolute = pathString.startsWith('/');
45+
const absolutePath = isAbsolute ? pathString : path.resolve(process.cwd(), pathString);
4046
if (!fs.existsSync(absolutePath)) {
41-
console.error(`Can't find configuration file "${absolutePath}"`);
42-
return;
43-
}
44-
try {
45-
const configString = fs.readFileSync(absolutePath).toString();
46-
this.fullConfig = JSON.parse(configString);
47-
} catch (e) {
48-
console.error(`Failed to load configuration from file "${absolutePath}"`);
49-
console.error(`Error: ${e.message}`);
47+
throw Error(`Can't find configuration file "${absolutePath}"`);
5048
}
49+
return fs.readFileSync(absolutePath).toString();
5150
}
5251

5352
public getHostList(): HostItem[] {
@@ -78,4 +77,16 @@ export class Config {
7877
}
7978
return this.fullConfig.runApplTracker === true;
8079
}
80+
81+
public getServers(): ServerItem[] {
82+
if (!Array.isArray(this.fullConfig.server)) {
83+
return [
84+
{
85+
secure: false,
86+
port: DEFAULT_PORT,
87+
},
88+
];
89+
}
90+
return this.fullConfig.server;
91+
}
8192
}

src/server/index.ts

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -87,35 +87,40 @@ async function loadApplModules() {
8787
loadPlatformModulesPromises.push(loadApplModules());
8888
/// #endif
8989

90-
Promise.all(loadPlatformModulesPromises).then(() => {
91-
servicesToStart.forEach((serviceClass: ServiceClass) => {
92-
const service = serviceClass.getInstance();
93-
runningServices.push(service);
94-
service.start();
90+
Promise.all(loadPlatformModulesPromises)
91+
.then(() => {
92+
servicesToStart.forEach((serviceClass: ServiceClass) => {
93+
const service = serviceClass.getInstance();
94+
runningServices.push(service);
95+
service.start();
96+
});
97+
98+
const wsService = WebSocketServer.getInstance();
99+
mwList.forEach((mwFactory: MwFactory) => {
100+
wsService.registerMw(mwFactory);
101+
});
102+
103+
mw2List.forEach((mwFactory: MwFactory) => {
104+
WebsocketMultiplexer.registerMw(mwFactory);
105+
});
106+
107+
if (process.platform === 'win32') {
108+
readline
109+
.createInterface({
110+
input: process.stdin,
111+
output: process.stdout,
112+
})
113+
.on('SIGINT', exit);
114+
}
115+
116+
process.on('SIGINT', exit);
117+
process.on('SIGTERM', exit);
118+
})
119+
.catch((error) => {
120+
console.error(error.message);
121+
exit('1');
95122
});
96123

97-
const wsService = WebSocketServer.getInstance();
98-
mwList.forEach((mwFactory: MwFactory) => {
99-
wsService.registerMw(mwFactory);
100-
});
101-
102-
mw2List.forEach((mwFactory: MwFactory) => {
103-
WebsocketMultiplexer.registerMw(mwFactory);
104-
});
105-
106-
if (process.platform === 'win32') {
107-
readline
108-
.createInterface({
109-
input: process.stdin,
110-
output: process.stdout,
111-
})
112-
.on('SIGINT', exit);
113-
}
114-
115-
process.on('SIGINT', exit);
116-
process.on('SIGTERM', exit);
117-
});
118-
119124
let interrupted = false;
120125
function exit(signal: string) {
121126
console.log(`\nReceived signal ${signal}`);

src/server/services/HttpServer.ts

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import * as http from 'http';
2+
import * as https from 'https';
23
import path from 'path';
34
import { Service } from './Service';
45
import { Utils } from '../Utils';
56
import express, { Express } from 'express';
7+
import { Config } from '../Config';
68

7-
const proto = 'http';
8-
const DEFAULT_PORT = 8000;
99
const DEFAULT_STATIC_DIR = path.join(__dirname, './public');
1010

11+
export type ServerAndPort = {
12+
server: https.Server | http.Server;
13+
port: number;
14+
};
15+
1116
export class HttpServer implements Service {
1217
private static instance: HttpServer;
13-
private static PORT = DEFAULT_PORT;
1418
private static PUBLIC_DIR = DEFAULT_STATIC_DIR;
1519
private static SERVE_STATIC = true;
16-
private server?: http.Server;
20+
private servers: ServerAndPort[] = [];
1721
private app?: Express;
1822

1923
protected constructor() {
@@ -31,13 +35,6 @@ export class HttpServer implements Service {
3135
return !!this.instance;
3236
}
3337

34-
public static setPort(port: number): void {
35-
if (HttpServer.instance) {
36-
throw Error('Unable to change value after instantiation');
37-
}
38-
HttpServer.PORT = port;
39-
}
40-
4138
public static setPublicDir(dir: string): void {
4239
if (HttpServer.instance) {
4340
throw Error('Unable to change value after instantiation');
@@ -52,29 +49,60 @@ export class HttpServer implements Service {
5249
HttpServer.SERVE_STATIC = enabled;
5350
}
5451

55-
public getPort(): number {
56-
return HttpServer.PORT;
57-
}
58-
59-
public getServer(): http.Server | undefined {
60-
return this.server;
52+
public getServers(): ServerAndPort[] {
53+
return [...this.servers];
6154
}
6255

6356
public getName(): string {
64-
return `HTTP Server {tcp:${HttpServer.PORT}}`;
57+
return `HTTP(s) Server Service`;
6558
}
6659

6760
public start(): void {
6861
this.app = express();
6962
if (HttpServer.SERVE_STATIC && HttpServer.PUBLIC_DIR) {
7063
this.app.use(express.static(HttpServer.PUBLIC_DIR));
7164
}
72-
this.server = http.createServer(this.app).listen(HttpServer.PORT, () => {
73-
Utils.printListeningMsg(proto, HttpServer.PORT);
65+
const config = Config.getInstance();
66+
config.getServers().forEach((serverItem) => {
67+
const { secure, port } = serverItem;
68+
let proto: string;
69+
let server: http.Server | https.Server;
70+
if (secure) {
71+
if (!serverItem.options) {
72+
throw Error('Must provide option for secure server configuration');
73+
}
74+
let { key, cert } = serverItem.options;
75+
const { keyPath, certPath } = serverItem.options;
76+
if (!key) {
77+
if (typeof keyPath !== 'string') {
78+
throw Error('Must provide parameter "key" or "keyPath"');
79+
}
80+
key = config.readFile(keyPath);
81+
}
82+
if (!cert) {
83+
if (typeof certPath !== 'string') {
84+
throw Error('Must provide parameter "cert" or "certPath"');
85+
}
86+
cert = config.readFile(certPath);
87+
}
88+
const options = { ...serverItem.options, cert, key };
89+
server = https.createServer(options, this.app);
90+
proto = 'https';
91+
} else {
92+
const options = serverItem.options ? { ...serverItem.options } : {};
93+
server = http.createServer(options, this.app);
94+
proto = 'http';
95+
}
96+
this.servers.push({ server, port });
97+
server.listen(port, () => {
98+
Utils.printListeningMsg(proto, port);
99+
});
74100
});
75101
}
76102

77103
public release(): void {
78-
this.server?.close();
104+
this.servers.forEach((item) => {
105+
item.server.close();
106+
});
79107
}
80108
}
Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,14 @@
1-
import * as http from 'http';
21
import { Server as WSServer } from 'ws';
32
import WS from 'ws';
43
import querystring from 'querystring';
54
import url from 'url';
65
import { Service } from './Service';
7-
import { HttpServer } from './HttpServer';
6+
import { HttpServer, ServerAndPort } from './HttpServer';
87
import { MwFactory } from '../mw/Mw';
98

109
export class WebSocketServer implements Service {
1110
private static instance?: WebSocketServer;
12-
private server?: WSServer;
13-
private port = 0;
11+
private servers: WSServer[] = [];
1412
private mwFactories: Set<MwFactory> = new Set();
1513

1614
protected constructor() {
@@ -32,11 +30,13 @@ export class WebSocketServer implements Service {
3230
this.mwFactories.add(mwFactory);
3331
}
3432

35-
public attachToServer(httpServer: http.Server): WSServer {
36-
const wss = new WSServer({ server: httpServer });
33+
public attachToServer(item: ServerAndPort): WSServer {
34+
const { server, port } = item;
35+
const TAG = `WebSocket Server {tcp:${port}}`;
36+
const wss = new WSServer({ server });
3737
wss.on('connection', async (ws: WS, request) => {
3838
if (!request.url) {
39-
ws.close(4001, `[${this.getName()}] Invalid url`);
39+
ws.close(4001, `[${TAG}] Invalid url`);
4040
return;
4141
}
4242
const parsedUrl = url.parse(request.url);
@@ -49,35 +49,35 @@ export class WebSocketServer implements Service {
4949
}
5050
}
5151
if (!processed) {
52-
ws.close(4002, `[${this.getName()}] Unsupported request`);
52+
ws.close(4002, `[${TAG}] Unsupported request`);
5353
}
5454
return;
5555
});
5656
wss.on('close', () => {
57-
console.log(`${this.getName()} stopped`);
57+
console.log(`${TAG} stopped`);
5858
});
59-
this.server = wss;
59+
this.servers.push(wss);
6060
return wss;
6161
}
6262

63-
public getServer(): WSServer | undefined {
64-
return this.server;
63+
public getServers(): WSServer[] {
64+
return this.servers;
6565
}
6666

6767
public getName(): string {
68-
return `WebSocket Server {tcp:${this.port}}`;
68+
return `WebSocket Server Service`;
6969
}
7070

7171
public start(): void {
7272
const service = HttpServer.getInstance();
73-
const server = service.getServer();
74-
this.port = service.getPort();
75-
if (server) {
76-
this.attachToServer(server);
77-
}
73+
service.getServers().forEach((item) => {
74+
this.attachToServer(item);
75+
});
7876
}
7977

8078
public release(): void {
81-
this.server?.close();
79+
this.servers.forEach((server) => {
80+
server.close();
81+
});
8282
}
8383
}

src/types/Configuration.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as https from 'https';
2+
13
export interface HostItem {
24
type: 'android' | 'ios';
35
secure: boolean;
@@ -6,7 +8,19 @@ export interface HostItem {
68
useProxy?: boolean;
79
}
810

11+
export type ExtendedServerOption = https.ServerOptions & {
12+
certPath?: string;
13+
keyPath?: string;
14+
};
15+
16+
export interface ServerItem {
17+
secure: boolean;
18+
port: number;
19+
options?: ExtendedServerOption;
20+
}
21+
922
export interface Configuration {
23+
server?: ServerItem[];
1024
runApplTracker?: boolean;
1125
announceApplTracker?: boolean;
1226
runGoogTracker?: boolean;

0 commit comments

Comments
 (0)