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 }