From 61868fe93e2953da6e2974396cc57200ad7d7322 Mon Sep 17 00:00:00 2001 From: SerinaNya <34389622+SerinaNya@users.noreply.github.com> Date: Wed, 26 Aug 2026 19:42:26 +0800 Subject: [PATCH] feat(config): switch webPort to URI-style webListen address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - replace integer webPort with MultiProtocolSocketAddress webListen (default: http://0.0.0.0:4665) in controller config - rename Swing UI item to "Web API 监听地址" and validate as full URI - update WebServer binding to honor configured host and port - normalize legacy webPort integers to http://0.0.0.0: on load and keep JSON API backward compatibility - update i18n keys in zh_CN and en_US resource bundles - update root klalb-config.json and AGENTS.md documentation --- AGENTS.md | 2 +- dashboard | 2 +- klalb-config.json | 4 +- src/klalb_en_US.properties | 4 +- src/klalb_zh_CN.properties | 14 +++--- .../cloud/network/klalb/KLALBConfigItem.java | 20 +++++--- .../klalb/KLALBControllerConfigItem.java | 28 ++++++----- .../kne/cloud/network/klalb/KLALBMain.java | 12 ++--- .../cloud/network/klalb/KLALBProxySystem.java | 26 +++++----- .../network/klalb/ui/KLALBStateGUI3.java | 49 +++++++++---------- .../network/klalb/web/KLALBWebServer.java | 22 ++++++--- 11 files changed, 99 insertions(+), 84 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e83049e..5c9ebac 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -82,7 +82,7 @@ Key controller config fields: - `ntpServers`: time server list (replaces `ntpServerTable`). - `denyExternalEndpointQuery` / `denyExternalEndpointBroadcast`: safety flags — the query flag hides ONLY the external-endpoint list in full node-info responses (device name/description still answer; Tiny queries are never gated), the broadcast flag disables LAN multicast discovery (renamed from `denyConnectionQuery` / `denyConnectionBroadcast`, which replaced `denyLineTableQuery` / `denyLineTableBroadcast`). - `enableTUN`: boolean flag for TUN interface creation (`"TUNName"` configures device name). -- `webPort`: default `4665`. +- `webListen`: Web API listen address, normally `http://0.0.0.0:4665`; legacy `webPort` is accepted on load/API input. Legacy JSON keys are still accepted on load: `KLALBConfigItem.getDefaultJsonDeserializer()` normalizes old key names (`openConnections`/`LineTable`, `denyConnectionQuery`, `denyLineTable*`, ...) before reflective deserialization (manual rewrite because gson-2.1 has no `@SerializedName(alternate=...)`), and `handleConfig` in the web server accepts them too. New saves always write canonical names. diff --git a/dashboard b/dashboard index d4250fc..f38d640 160000 --- a/dashboard +++ b/dashboard @@ -1 +1 @@ -Subproject commit d4250fce313700ffa806881527c425c81aac9f70 +Subproject commit f38d640ac43f67cb1063e6a8af9f026637a74616 diff --git a/klalb-config.json b/klalb-config.json index d2e9e65..27ecebc 100644 --- a/klalb-config.json +++ b/klalb-config.json @@ -56,7 +56,7 @@ "DeviceName": "Device Name", "DeviceDescription": "The Description of Device", "webUI": true, - "webPort": 4665, + "webListen": "http://0.0.0.0:4665", "NetworkInterfaceExcepts": [], "Type": "KLALBController" }, @@ -110,4 +110,4 @@ }, "Type": "SocketBridge" } -] \ No newline at end of file +] diff --git a/src/klalb_en_US.properties b/src/klalb_en_US.properties index fa87769..66af474 100644 --- a/src/klalb_en_US.properties +++ b/src/klalb_en_US.properties @@ -114,5 +114,5 @@ multifill=Multi-Core - Fill Cores Sequentially (Recommended for general-purpose multiscatter=Multi-Core - Spread Load Evenly (Optimized for multi-socket NUMA architectures) webapisettings=Web API settings enablewebapi=Enable Web API -webport=Web port -invaildwebport=Invalid web port number +weblistenaddr=Web API listen address +invaildweblistenaddr=Invalid Web API listen address diff --git a/src/klalb_zh_CN.properties b/src/klalb_zh_CN.properties index 96e4c40..cce2c5d 100644 --- a/src/klalb_zh_CN.properties +++ b/src/klalb_zh_CN.properties @@ -4,9 +4,9 @@ language=语言 basicsettings=基本设置 ipv6addr=IPv6地址 devicename=设备名称 -devicedescription=设备描述 +devicedescription=设备描述 dnsserver=DNS服务器 -extraroutes=额外路由 +extraroutes=额外路由 asnumber=AS号码 tcplistening=TCP监听端口 udplistening=UDP监听端口 @@ -111,8 +111,8 @@ performancesettings=性能设置 performancestrategy=性能策略 singlecore=单核-缓存命中率优先(高能耗比,适合低功耗设备、云机) multifill=多核-负载按顺序填充(适合大多数物理服务器、电脑) -multiscatter=多核-负载均匀打散分配(适合特殊的多路NUMA服务器) -webapisettings=Web API \u8bbe\u7f6e -enablewebapi=\u542f\u7528 Web API -webport=Web \u7aef\u53e3 -invaildwebport=\u65e0\u6548\u7684 Web \u7aef\u53e3\u53f7 +multiscatter=多核-负载均匀打散分配(适合特殊的多路NUMA服务器) +webapisettings=Web API 设置 +enablewebapi=启用 Web API +weblistenaddr=Web API 监听地址:端口 +invaildweblistenaddr=无效的 Web API 监听地址:端口 diff --git a/src/org/kne/cloud/network/klalb/KLALBConfigItem.java b/src/org/kne/cloud/network/klalb/KLALBConfigItem.java index d468df8..a1da3f7 100644 --- a/src/org/kne/cloud/network/klalb/KLALBConfigItem.java +++ b/src/org/kne/cloud/network/klalb/KLALBConfigItem.java @@ -7,8 +7,9 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import com.google.gson.reflect.TypeToken; @@ -78,11 +79,16 @@ public class KLALBConfigItem { * denyConnectionQuery/denyLineTableQuery → denyExternalEndpointQuery, * denyConnectionBroadcast/denyLineTableBroadcast → denyExternalEndpointBroadcast。 */ - private void normalizeLegacyControllerKeys(JsonObject jobj) { - renameLegacyKey(jobj,"externalEndpoints","openConnections","OpenConnections","lineTable","LineTable"); - renameLegacyKey(jobj,"denyExternalEndpointQuery","denyConnectionQuery","denyLineTableQuery"); - renameLegacyKey(jobj,"denyExternalEndpointBroadcast","denyConnectionBroadcast","denyLineTableBroadcast"); - } + private void normalizeLegacyControllerKeys(JsonObject jobj) { + renameLegacyKey(jobj,"externalEndpoints","openConnections","OpenConnections","lineTable","LineTable"); + renameLegacyKey(jobj,"denyExternalEndpointQuery","denyConnectionQuery","denyLineTableQuery"); + renameLegacyKey(jobj,"denyExternalEndpointBroadcast","denyConnectionBroadcast","denyLineTableBroadcast"); + if (!jobj.has("webListen") && jobj.has("webPort") && !jobj.get("webPort").isJsonNull()) { + int port = jobj.get("webPort").getAsInt(); + jobj.add("webListen", new JsonPrimitive("http://0.0.0.0:" + port)); + } + jobj.remove("webPort"); + } private void renameLegacyKey(JsonObject jobj, String newKey, String... legacyKeys) { if (jobj.has(newKey)) { diff --git a/src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java b/src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java index 4e4ba31..2d911d2 100644 --- a/src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java +++ b/src/org/kne/cloud/network/klalb/KLALBControllerConfigItem.java @@ -35,7 +35,7 @@ public class KLALBControllerConfigItem extends KLALBConfigItem { private String DeviceName; private String DeviceDescription; private boolean webUI = false; - private int webPort = 4665; + private MultiProtocolSocketAddress webListen = new MultiProtocolSocketAddress("http", "0.0.0.0", 4665); public boolean isEnableTUN() { return enableTUN; @@ -53,12 +53,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem { this.webUI = webUI; } - public int getWebPort() { - return webPort; - } - - public void setWebPort(int webPort) { - this.webPort = webPort; + public MultiProtocolSocketAddress getWebListen() { + return webListen; + } + + public void setWebListen(MultiProtocolSocketAddress webListen) { + this.webListen = webListen; } public String getPerformanceStrategy() { @@ -397,10 +397,12 @@ public class KLALBControllerConfigItem extends KLALBConfigItem { ", enableTUN=" + enableTUN + ", TUNName='" + TUNName + '\'' + ", performanceStrategy='" + performanceStrategy + '\'' + - ", DeviceName='" + DeviceName + '\'' + - ", DeviceDescription='" + DeviceDescription + '\'' + - ", NetworkInterfaceExcepts=" + NetworkInterfaceExcepts + - '}'; + ", DeviceName='" + DeviceName + '\'' + + ", DeviceDescription='" + DeviceDescription + '\'' + + ", NetworkInterfaceExcepts=" + NetworkInterfaceExcepts + + ", webUI=" + webUI + + ", webListen=" + webListen + + '}'; } @Override @@ -408,11 +410,11 @@ public class KLALBControllerConfigItem extends KLALBConfigItem { if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; KLALBControllerConfigItem that = (KLALBControllerConfigItem) o; - return nogui == that.nogui && enableTUN == that.enableTUN && webUI == that.webUI && denyExternalEndpointQuery == that.denyExternalEndpointQuery && denyExternalEndpointBroadcast == that.denyExternalEndpointBroadcast && Double.compare(burstLimit, that.burstLimit) == 0 && Double.compare(delayUpperBound, that.delayUpperBound) == 0 && Double.compare(delayLowerBound, that.delayLowerBound) == 0 && nagleDelayTime == that.nagleDelayTime && linkNagleDelayTime == that.linkNagleDelayTime && linkConnectionsCount == that.linkConnectionsCount && webPort == that.webPort && Objects.equals(language, that.language) && Objects.equals(VirtualAddress, that.VirtualAddress) && Objects.equals(VirtualASN, that.VirtualASN) && Objects.equals(DNS, that.DNS) && Objects.equals(TCPListen, that.TCPListen) && Objects.equals(UDPListen, that.UDPListen) && Objects.equals(VirtualSocketName, that.VirtualSocketName) && Objects.equals(externalEndpoints, that.externalEndpoints) && Objects.equals(autoConnections, that.autoConnections) && Objects.equals(ntpServers, that.ntpServers) && Objects.equals(ExtraRoutes, that.ExtraRoutes) && Objects.equals(congestionAlgorithm, that.congestionAlgorithm) && Objects.equals(TUNName, that.TUNName) && Objects.equals(performanceStrategy, that.performanceStrategy) && Objects.equals(DeviceName, that.DeviceName) && Objects.equals(DeviceDescription, that.DeviceDescription) && Objects.equals(NetworkInterfaceExcepts, that.NetworkInterfaceExcepts); + return nogui == that.nogui && enableTUN == that.enableTUN && webUI == that.webUI && denyExternalEndpointQuery == that.denyExternalEndpointQuery && denyExternalEndpointBroadcast == that.denyExternalEndpointBroadcast && Double.compare(burstLimit, that.burstLimit) == 0 && Double.compare(delayUpperBound, that.delayUpperBound) == 0 && Double.compare(delayLowerBound, that.delayLowerBound) == 0 && nagleDelayTime == that.nagleDelayTime && linkNagleDelayTime == that.linkNagleDelayTime && linkConnectionsCount == that.linkConnectionsCount && Objects.equals(webListen, that.webListen) && Objects.equals(language, that.language) && Objects.equals(VirtualAddress, that.VirtualAddress) && Objects.equals(VirtualASN, that.VirtualASN) && Objects.equals(DNS, that.DNS) && Objects.equals(TCPListen, that.TCPListen) && Objects.equals(UDPListen, that.UDPListen) && Objects.equals(VirtualSocketName, that.VirtualSocketName) && Objects.equals(externalEndpoints, that.externalEndpoints) && Objects.equals(autoConnections, that.autoConnections) && Objects.equals(ntpServers, that.ntpServers) && Objects.equals(ExtraRoutes, that.ExtraRoutes) && Objects.equals(congestionAlgorithm, that.congestionAlgorithm) && Objects.equals(TUNName, that.TUNName) && Objects.equals(performanceStrategy, that.performanceStrategy) && Objects.equals(DeviceName, that.DeviceName) && Objects.equals(DeviceDescription, that.DeviceDescription) && Objects.equals(NetworkInterfaceExcepts, that.NetworkInterfaceExcepts); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), language, nogui, VirtualAddress, VirtualASN, DNS, TCPListen, UDPListen, VirtualSocketName, externalEndpoints, autoConnections, ntpServers, ExtraRoutes, denyExternalEndpointQuery, denyExternalEndpointBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, enableTUN, TUNName, performanceStrategy, DeviceName, DeviceDescription, NetworkInterfaceExcepts, webUI, webPort); + return Objects.hash(super.hashCode(), language, nogui, VirtualAddress, VirtualASN, DNS, TCPListen, UDPListen, VirtualSocketName, externalEndpoints, autoConnections, ntpServers, ExtraRoutes, denyExternalEndpointQuery, denyExternalEndpointBroadcast, congestionAlgorithm, burstLimit, delayUpperBound, delayLowerBound, nagleDelayTime, linkNagleDelayTime, linkConnectionsCount, enableTUN, TUNName, performanceStrategy, DeviceName, DeviceDescription, NetworkInterfaceExcepts, webUI, webListen); } } diff --git a/src/org/kne/cloud/network/klalb/KLALBMain.java b/src/org/kne/cloud/network/klalb/KLALBMain.java index 6e67abb..bbcac9f 100644 --- a/src/org/kne/cloud/network/klalb/KLALBMain.java +++ b/src/org/kne/cloud/network/klalb/KLALBMain.java @@ -108,12 +108,12 @@ public class KLALBMain { case "web": if(sc.length >= 2) { String action = sc[1].toLowerCase(); - if("start".equals(action)) { - int p = 4665; - if(sc.length >= 3) { - try { p = Integer.parseInt(sc[2]); } catch (NumberFormatException ignored) {} - } else if(kpcje.getControllerConfig() != null && kpcje.getControllerConfig().getWebPort() > 0) { - p = kpcje.getControllerConfig().getWebPort(); + if("start".equals(action)) { + int p = 4665; + if(sc.length >= 3) { + try { p = Integer.parseInt(sc[2]); } catch (NumberFormatException ignored) {} + } else if(kpcje.getControllerConfig() != null && kpcje.getControllerConfig().getWebListen() != null) { + p = kpcje.getControllerConfig().getWebListen().getPort(); } try { kpcje.enableWebServer(p); diff --git a/src/org/kne/cloud/network/klalb/KLALBProxySystem.java b/src/org/kne/cloud/network/klalb/KLALBProxySystem.java index cbce896..85dda3c 100644 --- a/src/org/kne/cloud/network/klalb/KLALBProxySystem.java +++ b/src/org/kne/cloud/network/klalb/KLALBProxySystem.java @@ -84,18 +84,20 @@ public class KLALBProxySystem { public KLALBProxySystem() { } - public void enableWebServer() throws IOException { - int port = 4665; - KLALBControllerConfigItem cci = getControllerConfig(); - if (cci != null && cci.getWebPort() > 0) { - port = cci.getWebPort(); - } - enableWebServer(port); - } - - public void enableWebServer(int port) throws IOException { - if (webServer == null) { - webServer = new KLALBWebServer(this, port); + public void enableWebServer() throws IOException { + KLALBControllerConfigItem cci = getControllerConfig(); + MultiProtocolSocketAddress listen = cci == null || cci.getWebListen() == null + ? new MultiProtocolSocketAddress("http", "0.0.0.0", 4665) : cci.getWebListen(); + enableWebServer(listen); + } + + public void enableWebServer(int port) throws IOException { + enableWebServer(new MultiProtocolSocketAddress("http", "0.0.0.0", port)); + } + + public void enableWebServer(MultiProtocolSocketAddress listen) throws IOException { + if (webServer == null) { + webServer = new KLALBWebServer(this, listen); webServer.start(); } else { throw new IllegalStateException("Web server already enabled!"); diff --git a/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java b/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java index 51295bb..6085371 100644 --- a/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java +++ b/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java @@ -76,7 +76,7 @@ public class KLALBStateGUI3 extends XFrame { private JTextField addressFieldSet; // 地址设置框 private JTextField asnFieldSet; // ASN设置框 private JTextField tunDeviceName; // 虚拟网卡名称设置框 - private JTextField webPortSet; // Web API 端口设置框 + private JTextField webListenSet; // Web API 监听地址设置框 private NetworkGraphPanel graph; // 网络图面板 private JTextField textFieldLocate; // 定位地址输入框 private JTextField asnField2; // ASN显示框 @@ -917,10 +917,10 @@ public class KLALBStateGUI3 extends XFrame { webApiEnabled=webApic.getCheckBox(); settings.getView().add(webApic); - TextSettingItem webPort = new TextSettingItem(UIEnv.getRsb().getString("webport"), - CONST.itemwidth, CONST.settingheight); - webPortSet = webPort.getTextField(); - settings.getView().add(webPort); + TextSettingItem webListen = new TextSettingItem(UIEnv.getRsb().getString("weblistenaddr"), + CONST.itemwidth, CONST.settingheight); + webListenSet = webListen.getTextField(); + settings.getView().add(webListen); //性能设置标题 @@ -1158,23 +1158,19 @@ public class KLALBStateGUI3 extends XFrame { kck.setTUNName(tunNameText); } - // 保存Web API设置 - kck.setWebUI(webApiEnabled.isSelected()); - String webPortText = webPortSet.getText().trim(); - if (webPortText.equals("")) { - kck.setWebPort(4665); - } else { - try { - int wp = Integer.parseInt(webPortText); - if (wp < 0 || wp > 65535) { - throw new NumberFormatException("out of range"); - } - kck.setWebPort(wp); - } catch (NumberFormatException e) { - e.printStackTrace(); - JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("invaildwebport"), - UIEnv.getRsb().getString("warning"), JOptionPane.WARNING_MESSAGE); - return; + // 保存Web API设置 + kck.setWebUI(webApiEnabled.isSelected()); + String webListenText = webListenSet.getText().trim(); + if (webListenText.equals("")) { + kck.setWebListen(new MultiProtocolSocketAddress("http", "0.0.0.0", 4665)); + } else { + try { + kck.setWebListen(new MultiProtocolSocketAddress(webListenText)); + } catch (RuntimeException e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("invaildweblistenaddr"), + UIEnv.getRsb().getString("warning"), JOptionPane.WARNING_MESSAGE); + return; } } @@ -1578,9 +1574,10 @@ public class KLALBStateGUI3 extends XFrame { String tunname=kck.getTUNName(); tunDeviceName.setText(tunname!=null?tunname:""); - // 加载Web API设置 - webApiEnabled.setSelected(kck.isWebUI()); - webPortSet.setText(Integer.toString(kck.getWebPort())); + // 加载Web API设置 + webApiEnabled.setSelected(kck.isWebUI()); + webListenSet.setText(kck.getWebListen() != null ? kck.getWebListen().toString() + : "http://0.0.0.0:4665"); // 加载TCP监听 MultiProtocolSocketAddress mpat = kck.getTCPListen(); @@ -1768,4 +1765,4 @@ public class KLALBStateGUI3 extends XFrame { KLALBStateGUI3 kcg = new KLALBStateGUI3(new KLALBController()); kcg.setVisible(true); } -} \ No newline at end of file +} diff --git a/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java b/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java index 3161944..e473d75 100644 --- a/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java +++ b/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java @@ -31,7 +31,7 @@ import org.kne.cloud.network.srv6.SRv6Router; */ public class KLALBWebServer { private final KLALBProxySystem proxySystem; - private final int port; + private final MultiProtocolSocketAddress listen; private HttpServer server; private final Gson gson; private final ScheduledExecutorService sseExecutor = Executors.newSingleThreadScheduledExecutor(r -> { @@ -43,15 +43,21 @@ public class KLALBWebServer { private final AtomicBoolean running = new AtomicBoolean(false); public KLALBWebServer(KLALBProxySystem proxySystem, int port) { + this(proxySystem, new MultiProtocolSocketAddress("http", "0.0.0.0", port)); + } + + public KLALBWebServer(KLALBProxySystem proxySystem, MultiProtocolSocketAddress listen) { this.proxySystem = proxySystem; - this.port = port; + this.listen = listen; this.gson = proxySystem.getGson(); } public synchronized void start() throws IOException { if (running.get()) return; - server = HttpServer.create(new InetSocketAddress(port), 0); + InetSocketAddress socketAddress = "0.0.0.0".equals(listen.getHost()) + ? new InetSocketAddress(listen.getPort()) : listen.getSocketAddress(); + server = HttpServer.create(socketAddress, 0); server.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); // API Contexts @@ -72,7 +78,7 @@ public class KLALBWebServer { running.set(true); startSseBroadcaster(); - System.out.println("KLALB Web Dashboard started at http://localhost:" + port); + System.out.println("KLALB Web Dashboard started at " + listen); } public synchronized void stop() { @@ -97,7 +103,7 @@ public class KLALBWebServer { } public int getPort() { - return port; + return listen.getPort(); } private void setCorsHeaders(HttpExchange exchange) { @@ -605,8 +611,10 @@ public class KLALBWebServer { current.setWebUI(json.get("webUI").getAsBoolean()); } - if (json.has("webPort")) { - current.setWebPort(json.get("webPort").getAsInt()); + if (json.has("webListen") && !json.get("webListen").isJsonNull()) { + current.setWebListen(new MultiProtocolSocketAddress(json.get("webListen").getAsString())); + } else if (json.has("webPort")) { + current.setWebListen(new MultiProtocolSocketAddress("http", "0.0.0.0", json.get("webPort").getAsInt())); } if (json.has("nogui")) {