feat(config): switch webPort to URI-style webListen address

- 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:<port> 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
This commit is contained in:
2026-08-26 19:42:26 +08:00
parent 9cea5ed493
commit 61868fe93e
11 changed files with 99 additions and 84 deletions
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -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"
},
+2 -2
View File
@@ -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
+4 -4
View File
@@ -112,7 +112,7 @@ 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
webapisettings=Web API 设置
enablewebapi=启用 Web API
weblistenaddr=Web API 监听地址:端口
invaildweblistenaddr=无效的 Web API 监听地址:端口
@@ -9,6 +9,7 @@ import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
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;
@@ -82,6 +83,11 @@ public class KLALBConfigItem {
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) {
@@ -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 MultiProtocolSocketAddress getWebListen() {
return webListen;
}
public void setWebPort(int webPort) {
this.webPort = webPort;
public void setWebListen(MultiProtocolSocketAddress webListen) {
this.webListen = webListen;
}
public String getPerformanceStrategy() {
@@ -400,6 +400,8 @@ public class KLALBControllerConfigItem extends KLALBConfigItem {
", DeviceName='" + DeviceName + '\'' +
", DeviceDescription='" + DeviceDescription + '\'' +
", NetworkInterfaceExcepts=" + NetworkInterfaceExcepts +
", webUI=" + webUI +
", webListen=" + webListen +
'}';
}
@@ -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);
}
}
@@ -112,8 +112,8 @@ public class KLALBMain {
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();
} else if(kpcje.getControllerConfig() != null && kpcje.getControllerConfig().getWebListen() != null) {
p = kpcje.getControllerConfig().getWebListen().getPort();
}
try {
kpcje.enableWebServer(p);
@@ -85,17 +85,19 @@ public class KLALBProxySystem {
}
public void enableWebServer() throws IOException {
int port = 4665;
KLALBControllerConfigItem cci = getControllerConfig();
if (cci != null && cci.getWebPort() > 0) {
port = cci.getWebPort();
}
enableWebServer(port);
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, port);
webServer = new KLALBWebServer(this, listen);
webServer.start();
} else {
throw new IllegalStateException("Web server already enabled!");
@@ -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"),
TextSettingItem webListen = new TextSettingItem(UIEnv.getRsb().getString("weblistenaddr"),
CONST.itemwidth, CONST.settingheight);
webPortSet = webPort.getTextField();
settings.getView().add(webPort);
webListenSet = webListen.getTextField();
settings.getView().add(webListen);
//性能设置标题
@@ -1160,19 +1160,15 @@ public class KLALBStateGUI3 extends XFrame {
// 保存Web API设置
kck.setWebUI(webApiEnabled.isSelected());
String webPortText = webPortSet.getText().trim();
if (webPortText.equals("")) {
kck.setWebPort(4665);
String webListenText = webListenSet.getText().trim();
if (webListenText.equals("")) {
kck.setWebListen(new MultiProtocolSocketAddress("http", "0.0.0.0", 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) {
kck.setWebListen(new MultiProtocolSocketAddress(webListenText));
} catch (RuntimeException e) {
e.printStackTrace();
JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("invaildwebport"),
JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("invaildweblistenaddr"),
UIEnv.getRsb().getString("warning"), JOptionPane.WARNING_MESSAGE);
return;
}
@@ -1580,7 +1576,8 @@ public class KLALBStateGUI3 extends XFrame {
// 加载Web API设置
webApiEnabled.setSelected(kck.isWebUI());
webPortSet.setText(Integer.toString(kck.getWebPort()));
webListenSet.setText(kck.getWebListen() != null ? kck.getWebListen().toString()
: "http://0.0.0.0:4665");
// 加载TCP监听
MultiProtocolSocketAddress mpat = kck.getTCPListen();
@@ -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")) {