set_config API will now report random port result (even tho for the time being nobody is using it)
Massimo Melina committed
Mar 1, 2023 at 11:21 UTC
07d21d60a48871a107ea79707a95bc3fe0fbd8b2
3 files changed
+38
-27
src/adminApis.ts
+13
-23
@@ -2,7 +2,7 @@
2
3
import { ApiError, ApiHandlers, SendListReadable } from './apiMiddleware'
4
import { defineConfig, getWholeConfig, setConfig } from './config'
5
-import { getStatus, getUrls, httpsPortCfg, portCfg } from './listen'
5
+import { getServerStatus, getUrls } from './listen'
6
import {
7
API_VERSION,
8
BUILD_TIMESTAMP,
@@ -11,9 +11,8 @@ import {
11
IS_WINDOWS,
12
VERSION,
13
HTTP_UNAUTHORIZED,
14
- HTTP_FORBIDDEN,
14
HTTP_NOT_FOUND,
16
- HTTP_BAD_REQUEST
15
+ HTTP_BAD_REQUEST, HTTP_SERVER_ERROR
16
} from './const'
17
import vfsApis from './api.vfs'
18
import accountsApis from './api.accounts'
@@ -21,8 +20,7 @@ import pluginsApis from './api.plugins'
20
import monitorApis from './api.monitor'
21
import langApis from './api.lang'
22
import { getConnections } from './connections'
24
-import { debounceAsync, isLocalHost, onOff, wait } from './misc'
25
-import _ from 'lodash'
23
+import { debounceAsync, isLocalHost, onOff, waitFor } from './misc'
24
import events from './events'
25
import { anyAccountCanLoginAdmin, getFromAccount } from './perm'
26
import Koa from 'koa'
@@ -44,12 +42,16 @@ export const adminApis: ApiHandlers = {
42
43
async set_config({ values: v }) {
44
if (v) {
47
- const st = getStatus()
48
- const noHttp = (v.port ?? portCfg.get()) < 0 || !st.httpSrv.listening
49
- const noHttps = (v.https_port ?? httpsPortCfg.get()) < 0 || !st.httpsSrv.listening
50
- if (noHttp && noHttps)
51
- return new ApiError(HTTP_FORBIDDEN, "You cannot switch off both http and https ports")
45
await setConfig(v)
46
+ if (v.port === 0 || v.https_port === 0)
47
+ return await waitFor(async () => {
48
+ const st = await getServerStatus()
49
+ // wait for all random ports to be done, so we communicate new numbers
50
+ if ((v.port !== 0 || st.http.listening)
51
+ && (v.https_port !== 0 || st.https.listening))
52
+ return st
53
+ }, { timeout: 1000 })
54
+ ?? new ApiError(HTTP_SERVER_ERROR, "something went wrong changing ports")
55
}
56
return {}
57
},
@@ -57,31 +59,19 @@ export const adminApis: ApiHandlers = {
59
get_config: getWholeConfig,
60
61
async get_status() {
60
- const st = getStatus()
62
return {
63
started: HFS_STARTED,
64
build: BUILD_TIMESTAMP,
65
version: VERSION,
66
apiVersion: API_VERSION,
67
compatibleApiVersion: COMPATIBLE_API_VERSION,
67
- http: await serverStatus(st.httpSrv, portCfg.get()),
68
- https: await serverStatus(st.httpsSrv, httpsPortCfg.get()),
68
+ ...await getServerStatus(),
69
urls: getUrls(),
70
proxyDetected: getProxyDetected(),
71
frpDetected: localhostAdmin.get() && !getProxyDetected()
72
&& getConnections().every(isLocalHost)
73
&& await frpDebounced(),
74
}
75
-
76
- async function serverStatus(h: typeof st.httpSrv, configuredPort?: number) {
77
- const busy = await h.busy
78
- await wait(0) // simple trick to wait for also .error to be updated. If this trickery becomes necessary elsewhere, then we should make also error a Promise.
79
- return {
80
- ..._.pick(h, ['listening', 'error']),
81
- busy,
82
- port: (h?.address() as any)?.port || configuredPort,
83
- }
84
- }
75
},
76
77
async save_pem({ cert, private_key, name='self' }) {
src/listen.ts
+13
-4
@@ -178,12 +178,21 @@ function stopServer(srv: http.Server) {
178
})
179
}
180
181
-export function getStatus() {
181
+export async function getServerStatus() {
182
return {
183
- httpSrv,
184
- httpsSrv,
183
+ http: await serverStatus(httpSrv, portCfg.get()),
184
+ https: await serverStatus(httpsSrv, httpsPortCfg.get()),
185
}
186
-}
186
+
187
+ async function serverStatus(h: typeof httpSrv, configuredPort?: number) {
188
+ const busy = await h.busy
189
+ await wait(0) // simple trick to wait for also .error to be updated. If this trickery becomes necessary elsewhere, then we should make also error a Promise.
190
+ return {
191
+ ..._.pick(h, ['listening', 'error']),
192
+ busy,
193
+ port: (h?.address() as any)?.port || configuredPort,
194
+ }
195
+ }}
196
197
const ignore = /^(lo|.*loopback.*|virtualbox.*|.*\(wsl\).*|llw\d|awdl\d|utun\d|anpi\d)$/i // avoid giving too much information
198
src/misc.ts
+12
@@ -54,6 +54,18 @@ export function wait(ms: number) {
54
return new Promise(res=> setTimeout(res,ms))
55
}
56
57
+export async function waitFor<T>(cb: ()=> T, { interval=200, timeout=Infinity }={}) {
58
+ const started = Date.now()
59
+ while (1) {
60
+ const res = await cb()
61
+ if (res)
62
+ return res
63
+ if (Date.now() - started >= timeout)
64
+ return
65
+ await wait(interval)
66
+ }
67
+}
68
+
69
export function wantArray<T>(x?: void | T | T[]) {
70
return x == null ? [] : Array.isArray(x) ? x : [x]
71
}