fix: no warning was given if ipv4 port is busy while v6 is available
Massimo Melina committed
May 31, 2022 at 23:04 UTC
225a1eaefa1cd6003ec93fc25b97f566e16fb9d7
2 files changed
+59
-42
server/src/listen.ts
+58
-42
@@ -8,7 +8,7 @@ import { watchLoad } from './watchLoad'
8
import { networkInterfaces } from 'os';
9
import { newConnection } from './connections'
10
import open from 'open'
11
-import { onlyTruthy, prefix, wait } from './misc'
11
+import { debounceAsync, onlyTruthy, prefix, wait } from './misc'
12
import { ADMIN_URI, DEV } from './const'
13
import findProcess from 'find-process'
14
@@ -32,33 +32,7 @@ portCfg.sub(async port => {
32
open('http://localhost' + (port === 80 ? '' : ':' + port) + ADMIN_URI).then()
33
})
34
35
-const cert = defineConfig<string>('cert')
36
-const privateKey = defineConfig<string>('private_key')
37
-const httpsNeeds = [cert, privateKey]
38
-const httpsNeedsNames = { cert: 'certificate', private_key: 'private key' }
39
-const httpsOptions = { key: '', cert: '' }
40
-for (const cfg of httpsNeeds) {
41
- let unwatch: ReturnType<typeof watchLoad>['unwatch']
42
- cfg.sub(async v => {
43
- unwatch?.()
44
- const k = cfg.key() === 'private_key' ? 'key' : 'cert'
45
- httpsOptions[k] = v
46
- if (!v || v.includes('\n'))
47
- return considerHttps()
48
- // v is a path
49
- httpsOptions[k] = ''
50
- unwatch = watchLoad(v, data => {
51
- httpsOptions[k] = data
52
- considerHttps()
53
- }).unwatch
54
- await considerHttps()
55
- })
56
-}
57
-
58
-export const httpsPortCfg = defineConfig('https_port', -1)
59
-httpsPortCfg.sub(considerHttps)
60
-
61
-async function considerHttps() {
35
+const considerHttps = debounceAsync(async () => {
36
stopServer(httpsSrv).then()
37
let port = httpsPortCfg.get()
38
try {
@@ -84,15 +58,62 @@ async function considerHttps() {
58
httpsSrv.on('connection', socket =>
59
newConnection(socket, true))
60
printUrls(port, 'https')
61
+})
62
+
63
+
64
+const cert = defineConfig<string>('cert')
65
+const privateKey = defineConfig<string>('private_key')
66
+const httpsNeeds = [cert, privateKey]
67
+const httpsNeedsNames = { cert: 'certificate', private_key: 'private key' }
68
+const httpsOptions = { key: '', cert: '' }
69
+for (const cfg of httpsNeeds) {
70
+ let unwatch: ReturnType<typeof watchLoad>['unwatch']
71
+ cfg.sub(async v => {
72
+ unwatch?.()
73
+ const k = cfg.key() === 'private_key' ? 'key' : 'cert'
74
+ httpsOptions[k] = v
75
+ if (!v || v.includes('\n'))
76
+ return considerHttps()
77
+ // v is a path
78
+ httpsOptions[k] = ''
79
+ unwatch = watchLoad(v, data => {
80
+ httpsOptions[k] = data
81
+ considerHttps()
82
+ }).unwatch
83
+ await considerHttps()
84
+ })
85
}
86
89
-interface StartServer { port: number, net?:string }
90
-function startServer(srv: typeof httpSrv, { port, net }: StartServer) {
91
- return new Promise<number>((resolve, reject) => {
87
+export const httpsPortCfg = defineConfig('https_port', -1)
88
+httpsPortCfg.sub(considerHttps)
89
+
90
+interface StartServer { port: number, host?:string }
91
+function startServer(srv: typeof httpSrv, { port, host }: StartServer) {
92
+ return new Promise<number>(async resolve => {
93
try {
93
- if (port < 0)
94
+ if (port < 0 || !host && !await testIpV4()) // !host means ipV4+6, and if v4 port alone is busy we won't be notified of the failure, so we'll first test it on its own
95
return resolve(0)
95
- srv.listen(port, net, () => {
96
+ port = await listen(host)
97
+ if (port)
98
+ console.log(srv.name, "serving on", host||"any network", ':', port)
99
+ resolve(port)
100
+ }
101
+ catch(e) {
102
+ srv.error = String(e)
103
+ console.error(srv.name, "couldn't listen on port", port, srv.error)
104
+ resolve(0)
105
+ }
106
+ })
107
+
108
+ async function testIpV4() {
109
+ const res = await listen('0.0.0.0')
110
+ await new Promise(res => srv.close(res))
111
+ return res > 0
112
+ }
113
+
114
+ function listen(host?: string) {
115
+ return new Promise<number>(async (resolve, reject) => {
116
+ srv.listen({ port, host }, () => {
117
const ad = srv.address()
118
if (!ad)
119
return reject('no address')
@@ -100,7 +121,6 @@ function startServer(srv: typeof httpSrv, { port, net }: StartServer) {
121
srv.close()
122
return reject('type of socket not supported')
123
}
103
- console.log(srv.name, "serving on", net||"any network", ':', ad.port)
124
resolve(ad.port)
125
}).on('error', async e => {
126
srv.error = String(e)
@@ -111,16 +131,12 @@ function startServer(srv: typeof httpSrv, { port, net }: StartServer) {
131
srv.error = `couldn't listen on port ${port} used by ${srv.busy}`
132
}
133
console.error(srv.name, srv.error)
114
- console.log(" >> try specifying a different port like: --port 8011")
134
+ const k = (srv === httpSrv? portCfg : httpsPortCfg).key()
135
+ console.log(` >> try specifying a different port like: --${k} 8011`)
136
resolve(0)
137
})
117
- }
118
- catch(e) {
119
- srv.error = String(e)
120
- console.error(srv.name, "couldn't listen on port", port, srv.error)
121
- resolve(0)
122
- }
123
- })
138
+ })
139
+ }
140
}
141
142
function stopServer(srv: http.Server) {
todo.md
+1
@@ -1,4 +1,5 @@
1
# To do
2
+- fix: exe is not using native crc32 lib
3
- plugin api to read hfs config
4
- plugin: config to be able to declare a file field (real-path) https://github.com/rejetto/hfs/issues/46
5
- fix: root without can_download breaks