diff --git a/Cargo.lock b/Cargo.lock index 5f83936c..8ee4cd6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.15.9" +version = "0.15.10" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.15.9" +version = "0.15.10" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.15.9" +version = "0.15.10" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.15.9" +version = "0.15.10" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.15.9" +version = "0.15.10" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.15.9" +version = "0.15.10" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1257,7 +1257,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.15.9" +version = "0.15.10" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.15.9" +version = "0.15.10" dependencies = [ "num-bigint", "oxc_allocator", @@ -1286,7 +1286,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.15.9" +version = "0.15.10" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1298,7 +1298,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.15.9" +version = "0.15.10" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1307,7 +1307,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.15.9" +version = "0.15.10" dependencies = [ "bumpalo", "dashmap", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.15.9" +version = "0.15.10" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.15.9" +version = "0.15.10" dependencies = [ "tracing", "tracing-appender", @@ -1342,11 +1342,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.15.9" +version = "0.15.10" [[package]] name = "oxide_parser" -version = "0.15.9" +version = "0.15.10" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1361,7 +1361,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.15.9" +version = "0.15.10" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1377,7 +1377,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.15.9" +version = "0.15.10" dependencies = [ "num-bigint", "num-traits", @@ -1390,7 +1390,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.15.9" +version = "0.15.10" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1408,7 +1408,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.15.9" +version = "0.15.10" dependencies = [ "bumpalo", "dashmap", @@ -1421,7 +1421,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.15.9" +version = "0.15.10" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index d29e83cc..17158a10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ members = [ ] [workspace.package] -version = "0.15.9" +version = "0.15.10" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_cli/src/main.rs b/crates/oxide_cli/src/main.rs index 1223a3e5..82400f68 100644 --- a/crates/oxide_cli/src/main.rs +++ b/crates/oxide_cli/src/main.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] use std::fs; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::net::{IpAddr, SocketAddr}; use std::process::ExitCode; use std::time::{Duration, Instant}; @@ -129,6 +129,9 @@ enum ServerCommands { /// well-known 端口。 #[arg(long)] port: Option, + /// TCP 绑定地址;缺省 loopback(127.0.0.1),两臂(--rm 与持久)共享。 + #[arg(long, default_value = "127.0.0.1")] + host: IpAddr, /// 前台形态:当前进程直接运行持久 server(守护形态 spawn 的子进程用, /// 不向用户暴露)。 #[arg(long, hide = true)] @@ -331,6 +334,7 @@ fn server_command(command: ServerCommands) -> ExitCode { idle_timeout, workers, port, + host, foreground, } => { if rm { @@ -341,17 +345,18 @@ fn server_command(command: ServerCommands) -> ExitCode { if let Some(n) = workers { config.worker_count = n as usize; } - // 显式端口映射 TCP 端点(回环地址硬编码):0 为动态端口, + // 显式端口映射 TCP 端点(绑定地址取 --host):0 为动态端口, // 由操作系统在绑定后分配空闲端口;不带端口旗标时保持 Unix // 缺省。 if let Some(port) = port { - config.endpoint = Endpoint::Tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port)); + config.endpoint = Endpoint::Tcp(SocketAddr::new(host, port)); } start_result(run_server_rm(&config)) } else if foreground { let port = port.unwrap_or(sidecar::WELL_KNOWN_PORT); let mut config = ServerConfig { - endpoint: Endpoint::Tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port)), + endpoint: Endpoint::Tcp(SocketAddr::new(host, port)), + bind_addr: host, ..ServerConfig::well_known() }; if let Some(n) = workers { @@ -359,7 +364,7 @@ fn server_command(command: ServerCommands) -> ExitCode { } start_result(run_server(&config)) } else { - start_daemon(workers, port.unwrap_or(sidecar::WELL_KNOWN_PORT)) + start_daemon(workers, host, port.unwrap_or(sidecar::WELL_KNOWN_PORT)) } } ServerCommands::Cleanup => match well_known_cleanup() { @@ -491,7 +496,7 @@ fn control_arm(request: &ServerRequest, render: fn(&ServerResponse) -> Option Option, port: u16) -> ExitCode { +fn start_daemon(workers: Option, host: IpAddr, port: u16) -> ExitCode { // spawn:脱离子进程,标准流全空,不等待。 - if let Err(err) = spawn::spawn_detached_server(workers, port) { + if let Err(err) = spawn::spawn_detached_server(workers, host, port) { eprintln!("{}", Red.paint(format!("spawn server 进程失败:{err}"))); return ExitCode::FAILURE; } @@ -563,8 +568,8 @@ fn restart_command() -> ExitCode { } // 拉起新 server:脱离进程,标准流全空,不等待。新 server 按缺省配置拉起 - // (well-known 端口,不继承旧旗标)。 - if let Err(err) = spawn::spawn_detached_server(None, sidecar::WELL_KNOWN_PORT) { + // (well-known 绑定地址与端口,不继承旧旗标)。 + if let Err(err) = spawn::spawn_detached_server(None, sidecar::WELL_KNOWN_BIND_ADDR, sidecar::WELL_KNOWN_PORT) { eprintln!("{}", Red.paint(format!("拉起新 server 失败:{err}"))); return ExitCode::FAILURE; } @@ -855,3 +860,41 @@ fn not_implemented(command: &str) -> ExitCode { // 2 = 未实现,与 0=成功、1=运行失败区分,供脚本与 CI 识别。 ExitCode::from(2) } + +#[cfg(test)] +mod tests { + use super::*; + use std::net::{IpAddr, Ipv4Addr}; + + /// 取解析结果的 start 臂 host 字段(两臂共享的 --host 旗标)。 + fn parsed_host(args: &[&str]) -> IpAddr { + let cli = Cli::try_parse_from(args).expect("解析应成功"); + let Some(Commands::Server { command }) = cli.command else { + panic!("应解析出 server 臂"); + }; + let ServerCommands::Start { host, .. } = command else { + panic!("应解析出 start 臂"); + }; + host + } + + /// --host 缺省取 loopback:不带旗标解析得 127.0.0.1,显式旗标原样解析, + /// 非法取值走 clap 错误。 + #[test] + fn start_host_flag_parses() { + assert_eq!( + parsed_host(&["oxide", "server", "start"]), + IpAddr::V4(Ipv4Addr::LOCALHOST), + "缺省 host 应为 loopback" + ); + assert_eq!( + parsed_host(&["oxide", "server", "start", "--host", "0.0.0.0"]), + "0.0.0.0".parse::().unwrap(), + "显式 host 应原样解析" + ); + assert!( + Cli::try_parse_from(["oxide", "server", "start", "--host", "not-an-ip"]).is_err(), + "非法 host 应走 clap 错误" + ); + } +} diff --git a/crates/oxide_cli/src/server/cleanup.rs b/crates/oxide_cli/src/server/cleanup.rs index 95ed5c2f..efecf6d4 100644 --- a/crates/oxide_cli/src/server/cleanup.rs +++ b/crates/oxide_cli/src/server/cleanup.rs @@ -211,7 +211,10 @@ pub fn cleanup_with_wait(sidecar_path: &Path, endpoint: &Endpoint, kill_wait: Du /// # 注意事项 /// - CLI 分派臂与启动 liveness 扫描的稳定入口。 pub fn well_known_cleanup() -> CleanupOutcome { - cleanup(&sidecar::well_known_sidecar_path(), &sidecar::well_known_endpoint()) + cleanup( + &sidecar::well_known_sidecar_path(), + &sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR), + ) } /// 发送 SIGTERM:`kill -TERM`,与 `pid_alive` 的 `kill -0` 同一 `Command` 路径。 @@ -302,6 +305,7 @@ mod tests { version: "0.0.0".into(), started_at: 0, starttime, + port: 0, } } diff --git a/crates/oxide_cli/src/server/liveness.rs b/crates/oxide_cli/src/server/liveness.rs index 8a65dfb9..3efc9000 100644 --- a/crates/oxide_cli/src/server/liveness.rs +++ b/crates/oxide_cli/src/server/liveness.rs @@ -258,6 +258,7 @@ mod tests { version: "0.0.0".into(), started_at: 0, starttime, + port: 0, } } diff --git a/crates/oxide_cli/src/server/server.rs b/crates/oxide_cli/src/server/server.rs index e45a6937..065609bd 100644 --- a/crates/oxide_cli/src/server/server.rs +++ b/crates/oxide_cli/src/server/server.rs @@ -21,7 +21,7 @@ use std::fs; use std::io; -use std::net::{TcpListener, TcpStream}; +use std::net::{IpAddr, TcpListener, TcpStream}; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -86,17 +86,21 @@ pub struct ServerConfig { pub worker_count: usize, /// 本进程构建版本,版本比对与 sidecar 写入用;生产默认取构建期版本。 pub version: String, + /// TCP 绑定地址(loopback 缺省);端点由 CLI 层按本字段加端口构造, + /// Unix 臂不受影响。 + pub bind_addr: IpAddr, } impl ServerConfig { - /// 生产默认配置:endpoint 取 well-known TCP 端口,sidecar 取 well-known - /// 位置,worker 数取宿主核数,版本取构建期版本。 + /// 生产默认配置:endpoint 取 well-known TCP 端口(loopback 绑定地址), + /// sidecar 取 well-known 位置,worker 数取宿主核数,版本取构建期版本。 pub fn well_known() -> Self { ServerConfig { - endpoint: sidecar::well_known_endpoint(), + endpoint: sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR), sidecar_path: sidecar::well_known_sidecar_path(), worker_count: default_worker_count(), version: env!("CARGO_PKG_VERSION").to_string(), + bind_addr: sidecar::WELL_KNOWN_BIND_ADDR, } } } @@ -842,6 +846,7 @@ mod tests { sidecar_path: dir.join("sidecar.json"), worker_count: 2, version: env!("CARGO_PKG_VERSION").to_string(), + bind_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), } } @@ -1419,6 +1424,7 @@ mod tests { .join(format!("oxide_server_tcp_yield_{}_sidecar.json", std::process::id())), worker_count: 2, version: env!("CARGO_PKG_VERSION").to_string(), + bind_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), }; yield_and_takeover(&config).expect("TCP 臂交接应成功"); } diff --git a/crates/oxide_cli/src/server/sidecar.rs b/crates/oxide_cli/src/server/sidecar.rs index 4b6d1db7..910f851c 100644 --- a/crates/oxide_cli/src/server/sidecar.rs +++ b/crates/oxide_cli/src/server/sidecar.rs @@ -33,6 +33,8 @@ use serde::{Deserialize, Serialize}; /// - `started_at`:Unix 秒,人读展示用。 /// - `starttime`:`/proc//stat` 的启动时刻刻度(自开机单调递增), /// PID 复用判定用。 +/// - `port`:TCP 监听端口(机器可读形态),Unix 臂为 0;旧 sidecar 无该 +/// 字段时读回为 0(serde 缺省),探活裁决仍走 `socket_path` 文本。 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ServerIdentity { pub pid: u32, @@ -40,27 +42,37 @@ pub struct ServerIdentity { pub version: String, pub started_at: u64, pub starttime: u64, + /// TCP 监听端口(机器可读形态);Unix 臂为 0。旧 sidecar 无该字段时 + /// 读回为 0(serde 缺省),探活裁决走 socket_path 文本,行为不变。 + #[serde(default)] + pub port: u16, } impl ServerIdentity { - /// 构造当前进程的身份(pid、版本、启动时间、启动时刻刻度)。 + /// 构造当前进程的身份(pid、版本、启动时间、启动时刻刻度、监听端口)。 /// /// # 边界与前提 /// - 版本由调用方注入(生产默认取构建期版本,测试注入可区分值), /// 本函数不读取构建期版本。 /// - `socket_path` 字段记监听端点的文本形态(`endpoint.describe()`), /// 字段名与 JSON 模式不变,旧 sidecar 可读。 + /// - `port` 取端点的 TCP 端口(Unix 臂无端口,记 0)。 /// - 非 Linux 宿主(`/proc` 缺位)时 `starttime` 填 0,交叉核对退化, /// liveness 由 endpoint 探活裁决。 pub fn new(endpoint: &Endpoint, version: &str) -> Self { let pid = std::process::id(); let started_at = SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0); + let port = match endpoint { + Endpoint::Tcp(addr) => addr.port(), + Endpoint::Unix(_) => 0, + }; ServerIdentity { pid, socket_path: endpoint.describe(), version: version.to_string(), started_at, starttime: process_starttime(pid).unwrap_or(0), + port, } } } @@ -87,6 +99,9 @@ pub fn current_uid() -> u32 { /// well-known TCP 端口:持久 server 缺省端口。 pub const WELL_KNOWN_PORT: u16 = 4242; +/// well-known TCP 绑定地址:缺省 loopback(持久 server 缺省只接受本机连接)。 +pub const WELL_KNOWN_BIND_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::LOCALHOST); + /// well-known sidecar 路径:`$TMPDIR/oxide-.json`。 pub fn well_known_sidecar_path() -> PathBuf { std::env::temp_dir().join(format!("oxide-{}.json", current_uid())) @@ -100,9 +115,9 @@ pub fn well_known_socket_path() -> PathBuf { std::env::temp_dir().join(format!("oxide-{}.sock", current_uid())) } -/// well-known TCP 端点:127.0.0.1:WELL_KNOWN_PORT(绑定地址固定 loopback)。 -pub fn well_known_endpoint() -> Endpoint { - Endpoint::Tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), WELL_KNOWN_PORT)) +/// well-known TCP 端点:bind_addr:WELL_KNOWN_PORT(缺省取 loopback)。 +pub fn well_known_endpoint(bind_addr: IpAddr) -> Endpoint { + Endpoint::Tcp(SocketAddr::new(bind_addr, WELL_KNOWN_PORT)) } /// 解析端点文本形态:先试 `SocketAddr`(host:port)得 TCP 臂,失败回 @@ -408,6 +423,7 @@ mod tests { version: "0.0.0".into(), started_at: 0, starttime: 0, + port: 0, } } @@ -422,6 +438,42 @@ mod tests { assert_eq!(read, id, "五字段应逐字段一致"); } + /// 端口字段:TCP 端点记端点端口(机器可读形态),Unix 臂记 0。 + #[test] + fn identity_port_per_arm() { + let tcp = ServerIdentity::new(&Endpoint::Tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 4242)), "0.0.0"); + assert_eq!(tcp.port, 4242, "TCP 臂应记端点端口"); + let unix = ServerIdentity::new(&Endpoint::Unix(PathBuf::from("/tmp/oxide-test.sock")), "0.0.0"); + assert_eq!(unix.port, 0, "Unix 臂应记 0"); + } + + /// 写读回环(TCP 臂):sidecar 含机器可读 port 字段,读回逐字段一致。 + #[test] + fn identity_roundtrip_records_tcp_port() { + let dir = TestDir::new(); + let path = dir.path("sidecar.json"); + let endpoint = Endpoint::Tcp(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 4242)); + let id = ServerIdentity::new(&endpoint, "0.0.0"); + write_exclusive(&id, &path).expect("写 sidecar 应成功"); + let read = read_identity(&path).expect("读回 sidecar 应成功"); + assert_eq!(read, id, "字段应逐字段一致"); + assert_eq!(read.port, 4242, "port 字段应记端点端口"); + } + + /// 旧 sidecar(无 port 字段)读回:serde 缺省补 0,读回成功不判损坏。 + #[test] + fn legacy_sidecar_without_port_reads_back() { + let dir = TestDir::new(); + let path = dir.path("sidecar.json"); + fs::write( + &path, + r#"{"pid":1,"socket_path":"127.0.0.1:4242","version":"0.0.0","started_at":0,"starttime":0}"#, + ) + .expect("写旧 sidecar 应成功"); + let id = read_identity(&path).expect("旧 sidecar 应可读回"); + assert_eq!(id.port, 0, "无 port 字段应读回 0"); + } + /// O_EXCL 排他:同路径二次 create_new 必得 AlreadyExists。 #[test] fn write_exclusive_second_create_fails() { diff --git a/crates/oxide_cli/src/server/spawn.rs b/crates/oxide_cli/src/server/spawn.rs index 825b2ecb..fae42d19 100644 --- a/crates/oxide_cli/src/server/spawn.rs +++ b/crates/oxide_cli/src/server/spawn.rs @@ -4,31 +4,41 @@ //! spawn 逻辑。`current_exe()` 在 lib 上下文与二进制目标返回同一路径(lib //! 链接进同一个 oxide 二进制),spawn 行为逐字节不变。 +use std::net::IpAddr; use std::process::{Command, Stdio}; use std::time::{Duration, Instant}; use super::client::send_control_request; use super::protocol::{ServerRequest, ServerResponse}; -/// spawn 脱离的前台 server 子进程(`server start --foreground --port N`): +/// spawn 脱离的前台 server 子进程(`server start --foreground --host H --port N`): /// 标准流全空,不等待。 /// /// # 步骤 /// 1. 取当前可执行文件路径。 -/// 2. 构造命令 `server start --foreground --port N` 加可选 `--workers N`。 +/// 2. 构造命令 `server start --foreground --host H --port N` 加可选 `--workers N`。 /// 3. 三个标准流置空并 spawn。 /// /// # 边界与前提 /// - 必须带 `--foreground`:守护形态 spawn 的是前台 server 入口,不带该旗标会 /// 再次进入守护形态形成递归 spawn。 -/// - `port` 是 TCP 监听端口;缺省取 well-known 端口(调用方传入)。 +/// - `host` 是 TCP 绑定地址(调用方传入,缺省 loopback);`port` 是 TCP 监听 +/// 端口,缺省取 well-known 端口(调用方传入)。 /// /// # 副作用 /// - 创建脱离的子进程,调用方退出后由 init 收领继续运行。 -pub fn spawn_detached_server(workers: Option, port: u16) -> std::io::Result { +pub fn spawn_detached_server(workers: Option, host: IpAddr, port: u16) -> std::io::Result { let exe = std::env::current_exe()?; let mut cmd = Command::new(exe); - cmd.args(["server", "start", "--foreground", "--port", &port.to_string()]); + cmd.args([ + "server", + "start", + "--foreground", + "--host", + &host.to_string(), + "--port", + &port.to_string(), + ]); if let Some(n) = workers { cmd.args(["--workers", &n.to_string()]); } diff --git a/crates/oxide_cli/src/server/watchdog.rs b/crates/oxide_cli/src/server/watchdog.rs index e9e2c55f..c3c55dcb 100644 --- a/crates/oxide_cli/src/server/watchdog.rs +++ b/crates/oxide_cli/src/server/watchdog.rs @@ -159,8 +159,8 @@ pub fn run_watchdog(workers: Option) -> ExitCode { } // 初始 spawn 与就绪探测:失败即退 1,不进入监控循环。watchdog 子进程用 - // well-known 端口(范围控制:不加 --port 旗标)。 - let mut child = match spawn_detached_server(workers, sidecar::WELL_KNOWN_PORT) { + // well-known 绑定地址与端口(范围控制:不加 --host / --port 旗标)。 + let mut child = match spawn_detached_server(workers, sidecar::WELL_KNOWN_BIND_ADDR, sidecar::WELL_KNOWN_PORT) { Ok(child) => child, Err(err) => { eprintln!("spawn server 进程失败:{err}"); @@ -246,7 +246,7 @@ pub fn run_watchdog(workers: Option) -> ExitCode { return ExitCode::FAILURE; } std::thread::sleep(CRASH_BACKOFF); - match spawn_detached_server(workers, sidecar::WELL_KNOWN_PORT) { + match spawn_detached_server(workers, sidecar::WELL_KNOWN_BIND_ADDR, sidecar::WELL_KNOWN_PORT) { Ok(new_child) => { child = new_child; child_exited = None; @@ -308,6 +308,7 @@ mod tests { version: "0.0.0".into(), started_at: 0, starttime: 0, + port: 0, } } diff --git a/crates/oxide_cli/tests/server_cli.rs b/crates/oxide_cli/tests/server_cli.rs index ed4dd588..be4b7ecd 100644 --- a/crates/oxide_cli/tests/server_cli.rs +++ b/crates/oxide_cli/tests/server_cli.rs @@ -142,7 +142,7 @@ fn server_help_lists_subcommands() { fn server_log_no_file_exit_1() { let _guard = lock_well_known(); assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -162,7 +162,7 @@ fn server_log_no_file_exit_1() { fn server_control_arms_no_server_exit_1() { let _guard = lock_well_known(); assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -181,7 +181,7 @@ fn server_control_arms_no_server_exit_1() { fn server_forge_no_server_exit_1() { let _guard = lock_well_known(); assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -196,7 +196,7 @@ fn server_forge_no_server_exit_1() { fn server_cleanup_exit_0() { let _guard = lock_well_known(); assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -216,7 +216,7 @@ fn server_start_e2e() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -266,7 +266,7 @@ fn server_start_when_running() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -305,7 +305,7 @@ fn server_restart_e2e() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -387,7 +387,7 @@ fn server_log_e2e() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -469,7 +469,7 @@ fn server_forge_e2e() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -529,7 +529,7 @@ fn server_watchdog_e2e() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -634,7 +634,7 @@ fn server_watchdog_sigint_stops_server() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -682,7 +682,7 @@ fn server_start_port_e2e() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -761,7 +761,7 @@ fn server_start_port_multi_client() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); @@ -824,7 +824,7 @@ fn server_start_port_second_instance_refused() { // 探活先行:存活 server 占用全局路径时 panic,不抢占。 assert!( - !sidecar::is_server_alive(&sidecar::well_known_endpoint()), + !sidecar::is_server_alive(&sidecar::well_known_endpoint(sidecar::WELL_KNOWN_BIND_ADDR)), "存活 server 占用 well-known 路径,测试不抢占" ); diff --git a/crates/oxide_cli/tests/server_client.rs b/crates/oxide_cli/tests/server_client.rs index c5843c0f..02a1f4b8 100644 --- a/crates/oxide_cli/tests/server_client.rs +++ b/crates/oxide_cli/tests/server_client.rs @@ -5,6 +5,7 @@ //! 全局单例。 use std::fs; +use std::net::{IpAddr, Ipv4Addr}; use std::time::{Duration, Instant}; use oxide_cli::server::client::send_control_request_from; @@ -21,6 +22,7 @@ fn test_config(test_name: &str) -> ServerConfig { sidecar_path: dir.join("sidecar.json"), worker_count: 2, version: env!("CARGO_PKG_VERSION").to_string(), + bind_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), } } @@ -117,6 +119,7 @@ fn control_client_tcp_arm_end_to_end() { sidecar_path: dir.join("sidecar.json"), worker_count: 2, version: env!("CARGO_PKG_VERSION").to_string(), + bind_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), }; // 后台线程起真实 run_server,等 sidecar 出现(TCP 臂无 socket 文件, diff --git a/crates/oxide_cli/tests/server_signal_int.rs b/crates/oxide_cli/tests/server_signal_int.rs index c50f3b53..c3bf4951 100644 --- a/crates/oxide_cli/tests/server_signal_int.rs +++ b/crates/oxide_cli/tests/server_signal_int.rs @@ -4,6 +4,7 @@ //! 进程级一次性的,信号投递无歧义,测试确定。SIGTERM 路径见 //! `server_signal_term.rs`(同型,独立进程)。 +use std::net::{IpAddr, Ipv4Addr}; use std::process::Command; use std::thread::JoinHandle; use std::time::{Duration, Instant}; @@ -21,6 +22,7 @@ fn unique_paths(test_name: &str) -> ServerConfig { sidecar_path: dir.join("sidecar.json"), worker_count: 2, version: env!("CARGO_PKG_VERSION").to_string(), + bind_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), } } diff --git a/crates/oxide_cli/tests/server_signal_term.rs b/crates/oxide_cli/tests/server_signal_term.rs index 9be7f2fb..269eac3d 100644 --- a/crates/oxide_cli/tests/server_signal_term.rs +++ b/crates/oxide_cli/tests/server_signal_term.rs @@ -4,6 +4,7 @@ //! 进程级一次性的,信号投递无歧义,测试确定。SIGINT 路径见 //! `server_signal_int.rs`(同型,独立进程)。 +use std::net::{IpAddr, Ipv4Addr}; use std::process::Command; use std::thread::JoinHandle; use std::time::{Duration, Instant}; @@ -21,6 +22,7 @@ fn unique_paths(test_name: &str) -> ServerConfig { sidecar_path: dir.join("sidecar.json"), worker_count: 2, version: env!("CARGO_PKG_VERSION").to_string(), + bind_addr: IpAddr::V4(Ipv4Addr::LOCALHOST), } }