-
-
Notifications
You must be signed in to change notification settings - Fork 201
Expand file tree
/
Copy pathnet-service.ts
More file actions
158 lines (134 loc) · 3.49 KB
/
net-service.ts
File metadata and controls
158 lines (134 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import * as net from "net";
import * as _ from "lodash";
import { sleep } from "../helpers";
import {
IErrors,
INet,
IChildProcess,
IOsInfo,
IWaitForPortListenData,
IDictionary,
} from "../declarations";
import { injector } from "../yok";
export class Net implements INet {
private static DEFAULT_INTERVAL = 1000;
constructor(
private $errors: IErrors,
private $childProcess: IChildProcess,
private $logger: ILogger,
private $osInfo: IOsInfo,
) {}
public async getFreePort(): Promise<number> {
const server = net.createServer((sock: net.Socket) => {
/* empty - noone will connect here */
});
return new Promise<number>((resolve, reject) => {
let isResolved = false;
server.listen(0, () => {
const portUsed = (<net.AddressInfo>server.address()).port;
server.close();
if (!isResolved) {
isResolved = true;
resolve(portUsed);
}
});
server.on("error", (err: Error) => {
if (!isResolved) {
isResolved = true;
reject(err);
}
});
});
}
public async isPortAvailable(port: number): Promise<boolean> {
return new Promise<boolean>((resolve, reject) => {
let isResolved = false;
const server = net.createServer();
server.on("error", (err: Error) => {
if (!isResolved) {
isResolved = true;
resolve(false);
}
});
server.once("close", () => {
if (!isResolved) {
// "close" will be emitted right after "error"
isResolved = true;
resolve(true);
}
});
server.on("listening", (err: Error) => {
if (err && !isResolved) {
isResolved = true;
resolve(true);
}
server.close();
});
server.listen(port, "127.0.0.1");
});
}
public async getAvailablePortInRange(
startPort: number,
endPort?: number,
): Promise<number> {
endPort = endPort || 65534;
while (!(await this.isPortAvailable(startPort))) {
startPort++;
if (startPort > endPort) {
this.$errors.fail("Unable to find free local port.");
}
}
return startPort;
}
public async waitForPortToListen(
waitForPortListenData: IWaitForPortListenData,
): Promise<boolean> {
if (!waitForPortListenData) {
this.$errors.fail("You must pass port and timeout for check.");
}
const { timeout, port } = waitForPortListenData;
const interval = waitForPortListenData.interval || Net.DEFAULT_INTERVAL;
const endTime = new Date().getTime() + timeout;
const platformData: IDictionary<{ command: string; regex: RegExp }> = {
darwin: {
command: "netstat -f inet -p tcp -anL",
regex: new RegExp(`\\.${port}\\b`, "g"),
},
linux: {
command: "netstat -tnl",
regex: new RegExp(`:${port}\\s`, "g"),
},
win32: {
command: "netstat -ant -p tcp",
regex: new RegExp(`TCP\\s+(\\d+\\.){3}\\d+:${port}.*?LISTEN`, "g"),
},
};
const platform = this.$osInfo.platform();
const currentPlatformData = platformData[platform];
if (!currentPlatformData) {
this.$errors.fail(
`Unable to check for free ports on ${platform}. Supported platforms are: ${_.keys(
platformData,
).join(", ")}`,
);
}
while (true) {
const { command, regex } = currentPlatformData;
try {
const result = await this.$childProcess.exec(command);
if (result && !!result.match(regex)) {
return true;
}
} catch (err) {
this.$logger.trace(`Error while calling '${command}': ${err}`);
}
const currentTime = new Date().getTime();
if (currentTime >= endTime) {
break;
}
await sleep(interval);
}
return false;
}
}
injector.register("net", Net);