-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
get-port.ts
140 lines (116 loc) · 2.98 KB
/
get-port.ts
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
import net from 'net';
import {createLock} from './locks';
type PortStatus = 'available' | 'unavailable';
const isPortAvailableOnHost = ({
portToTry,
host,
}: {
portToTry: number;
host: string;
}): Promise<PortStatus> => {
return new Promise<PortStatus>((resolve) => {
let status: PortStatus = 'unavailable';
const socket = new net.Socket();
socket.on('connect', () => {
status = 'unavailable';
socket.destroy();
});
socket.setTimeout(3000);
socket.on('timeout', () => {
status = 'unavailable';
socket.destroy();
resolve(status);
});
socket.on('error', () => {
status = 'available';
});
socket.on('close', () => resolve(status));
socket.connect(portToTry, host);
});
};
export const testPortAvailableOnMultipleHosts = async ({
hosts,
port,
}: {
port: number;
hosts: string[];
}): Promise<PortStatus> => {
const results = await Promise.all(
hosts.map((host) => {
return isPortAvailableOnHost({portToTry: port, host});
}),
);
return results.every((r) => r === 'available') ? 'available' : 'unavailable';
};
const getPort = async ({
from,
to,
hostsToTest,
}: {
from: number;
to: number;
hostsToTest: string[];
}) => {
const ports = makeRange(from, to);
for (const port of ports) {
if (
(await testPortAvailableOnMultipleHosts({
port,
hosts: hostsToTest,
})) === 'available'
) {
return port;
}
}
throw new Error('No available ports found');
};
const portLocks = createLock({timeout: 10000});
export const getDesiredPort = async ({
desiredPort,
from,
hostsToTry,
to,
}: {
desiredPort: number | undefined;
from: number;
to: number;
hostsToTry: string[];
}) => {
await portLocks.waitForAllToBeDone();
const lockPortSelection = portLocks.lock();
const unlockPort = () => portLocks.unlock(lockPortSelection);
if (
typeof desiredPort !== 'undefined' &&
(await testPortAvailableOnMultipleHosts({
port: desiredPort,
hosts: hostsToTry,
})) === 'available'
) {
return {port: desiredPort, unlockPort};
}
const actualPort = await getPort({from, to, hostsToTest: hostsToTry});
// If did specify a port but did not get that one, fail hard.
if (desiredPort && desiredPort !== actualPort) {
throw new Error(
`You specified port ${desiredPort} to be used for the HTTP server, but it is not available. Choose a different port or remove the setting to let Remotion automatically select a free port.`,
);
}
return {port: actualPort, unlockPort};
};
const makeRange = (from: number, to: number): number[] => {
if (!Number.isInteger(from) || !Number.isInteger(to)) {
throw new TypeError('`from` and `to` must be integer numbers');
}
if (from < 1024 || from > 65535) {
throw new RangeError('`from` must be between 1024 and 65535');
}
if (to < 1024 || to > 65536) {
throw new RangeError('`to` must be between 1024 and 65536');
}
if (to < from) {
throw new RangeError('`to` must be greater than or equal to `from`');
}
return new Array(to - from + 1).fill(true).map((_, i) => {
return i + from;
});
};