feat: 新建 oxide_net crate 传输枚举层

This commit is contained in:
2026-10-11 12:30:02 +00:00
parent 5a7525ea88
commit 6fb57e5152
5 changed files with 231 additions and 20 deletions
Generated
+23 -19
View File
@@ -1160,7 +1160,7 @@ dependencies = [
[[package]]
name = "oxide_builtins"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"chrono",
"fast-float",
@@ -1185,7 +1185,7 @@ dependencies = [
[[package]]
name = "oxide_bytecode"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"num-bigint",
"oxide_log",
@@ -1193,7 +1193,7 @@ dependencies = [
[[package]]
name = "oxide_cfg"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"oxide_bytecode",
"oxide_emit",
@@ -1204,7 +1204,7 @@ dependencies = [
[[package]]
name = "oxide_cli"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"ansi_term",
"bumpalo",
@@ -1227,7 +1227,7 @@ dependencies = [
[[package]]
name = "oxide_code_cache"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"lru",
"oxide_bytecode",
@@ -1236,7 +1236,7 @@ dependencies = [
[[package]]
name = "oxide_compiler"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1256,7 +1256,7 @@ dependencies = [
[[package]]
name = "oxide_dce"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1270,7 +1270,7 @@ dependencies = [
[[package]]
name = "oxide_emit"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"num-bigint",
"oxc_allocator",
@@ -1285,7 +1285,7 @@ dependencies = [
[[package]]
name = "oxide_engine"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1297,7 +1297,7 @@ dependencies = [
[[package]]
name = "oxide_ir"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"oxide_bytecode",
"oxide_log",
@@ -1306,7 +1306,7 @@ dependencies = [
[[package]]
name = "oxide_kernel"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"bumpalo",
"dashmap",
@@ -1320,7 +1320,7 @@ dependencies = [
[[package]]
name = "oxide_liveness"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1332,16 +1332,20 @@ dependencies = [
[[package]]
name = "oxide_log"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"tracing",
"tracing-appender",
"tracing-subscriber",
]
[[package]]
name = "oxide_net"
version = "0.15.4"
[[package]]
name = "oxide_parser"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"oxc_allocator",
"oxc_ast",
@@ -1356,7 +1360,7 @@ dependencies = [
[[package]]
name = "oxide_regalloc"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"oxide_bytecode",
"oxide_cfg",
@@ -1372,7 +1376,7 @@ dependencies = [
[[package]]
name = "oxide_runtime_api"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"num-bigint",
"num-traits",
@@ -1385,7 +1389,7 @@ dependencies = [
[[package]]
name = "oxide_test262"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"oxide_bytecode",
"oxide_compiler",
@@ -1403,7 +1407,7 @@ dependencies = [
[[package]]
name = "oxide_types"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"bumpalo",
"dashmap",
@@ -1416,7 +1420,7 @@ dependencies = [
[[package]]
name = "oxide_vm"
version = "0.15.3"
version = "0.15.4"
dependencies = [
"bumpalo",
"chrono",
+2 -1
View File
@@ -5,6 +5,7 @@ members = [
"crates/oxide_bytecode",
"crates/oxide_code_cache",
"crates/oxide_log",
"crates/oxide_net",
"crates/oxide_compiler",
"crates/oxide_emit",
"crates/oxide_ir",
@@ -23,7 +24,7 @@ members = [
]
[workspace.package]
version = "0.15.3"
version = "0.15.4"
edition = "2021"
license = "MIT"
rust-version = "1.80"
+12
View File
@@ -0,0 +1,12 @@
[package]
name = "oxide_net"
version.workspace = true
edition.workspace = true
license.workspace = true
rust-version.workspace = true
description = "OxideJS - Transport enum layer (Unix and TCP)"
[dependencies]
[lints]
workspace = true
+14
View File
@@ -0,0 +1,14 @@
//! OxideJS 引擎无关传输层。
//!
//! `transport` 提供 `Conn` 与 `Listener` 两枚枚举,各包装 Unix 域套接字与 TCP 连接两臂,
//! 统一六个常用操作:非阻塞、accept、克隆、读超时、写超时、write_all。协议层完全
//! 传输无关,本 crate 只提供传输面。
//!
//! 构造面(bind / connect)不含在本 crate:调用方先用 std 原语构造句柄,再包进枚举。
//! 零外部依赖,纯 `std::net` 与 `std::os::unix::net`。
/// 传输枚举层:`Conn` 与 `Listener` 及统一操作。
pub mod transport;
/// 重导出 `transport` 模块的 `Conn` 与 `Listener` 枚举。
pub use transport::{Conn, Listener};
+180
View File
@@ -0,0 +1,180 @@
//! 传输枚举层:`Conn` 与 `Listener` 各包装 Unix 与 TCP 两臂,统一六个常用操作。
//!
//! 关键约定:
//! - 两臂映射到 std 同名同语义方法,无行为分叉。
//! - `Listener::accept` 返回 `Conn` 并丢弃对端地址,现有 accept 循环不消费地址。
//! - 构造面(bind / connect)不含在本模块,调用方先用 std 原语构造句柄再包进枚举。
use std::io;
use std::io::Write;
use std::net::{TcpListener, TcpStream};
use std::os::unix::net::{UnixListener, UnixStream};
use std::time::Duration;
/// 双向连接:Unix 域套接字或 TCP 连接。
///
/// 两臂共享同一操作面,调用方无需感知具体传输。
#[derive(Debug)]
pub enum Conn {
/// Unix 域套接字臂。
Unix(UnixStream),
/// TCP 连接臂。
Tcp(TcpStream),
}
/// 监听套接字:Unix 域套接字监听者或 TCP 监听者。
#[derive(Debug)]
pub enum Listener {
/// Unix 域套接字监听者臂。
Unix(UnixListener),
/// TCP 监听者臂。
Tcp(TcpListener),
}
impl Listener {
/// 设置监听者非阻塞模式。
///
/// # 注意事项
/// - 置非阻塞后,无待接受连接时 `accept` 返回 `io::ErrorKind::WouldBlock`。
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
match self {
Listener::Unix(l) => l.set_nonblocking(nonblocking),
Listener::Tcp(l) => l.set_nonblocking(nonblocking),
}
}
/// 接受一个连接并包装为 `Conn`,丢弃对端地址。
///
/// # 边界与前提
/// - Unix 与 TCP 臂的对端地址类型不同,本层不暴露;需要来源地址的调用方须扩展签名。
/// - 非阻塞模式下无待接受连接时返回 `WouldBlock`。
pub fn accept(&self) -> io::Result<Conn> {
match self {
// 对端地址按约定丢弃,只取连接句柄。
Listener::Unix(l) => Ok(Conn::Unix(l.accept()?.0)),
Listener::Tcp(l) => Ok(Conn::Tcp(l.accept()?.0)),
}
}
}
impl Conn {
/// 克隆连接,返回共享同一底层套接字的新 `Conn`。
///
/// # 副作用
/// - 新句柄与原句柄共享同一套接字:写侧共享,读侧相互独立(std `try_clone` 语义)。
pub fn try_clone(&self) -> io::Result<Conn> {
match self {
Conn::Unix(s) => Ok(Conn::Unix(s.try_clone()?)),
Conn::Tcp(s) => Ok(Conn::Tcp(s.try_clone()?)),
}
}
/// 设置读超时。
///
/// # 边界与前提
/// - `None` 表示不限时(可无限阻塞)。
pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
match self {
Conn::Unix(s) => s.set_read_timeout(timeout),
Conn::Tcp(s) => s.set_read_timeout(timeout),
}
}
/// 设置写超时。
///
/// # 边界与前提
/// - `None` 表示不限时(可无限阻塞)。
pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
match self {
Conn::Unix(s) => s.set_write_timeout(timeout),
Conn::Tcp(s) => s.set_write_timeout(timeout),
}
}
/// 顺序写入全部字节。
///
/// # 边界与前提
/// - 空切片为无操作,恒成功。
/// - 已设超时且单次写入超时时返回 `Elapsed`(std 语义)。
pub fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
match self {
Conn::Unix(s) => s.write_all(buf),
Conn::Tcp(s) => s.write_all(buf),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Read;
/// Unix 回环:临时目录 bind Unix 套接字,accept 后写读往返,六方法逐一可用。
#[test]
fn unix_loopback_roundtrip() {
let dir = std::env::temp_dir().join(format!("oxide_net_{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("unix.sock");
let _ = std::fs::remove_file(&path);
let listener = UnixListener::bind(&path).unwrap();
let mut client = UnixStream::connect(&path).unwrap();
// 六方法逐一过一遍:非阻塞、accept、克隆、读写超时、write_all。
let listener = Listener::Unix(listener);
listener.set_nonblocking(false).unwrap();
let mut conn = listener.accept().unwrap();
let _peer = conn.try_clone().unwrap();
conn.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
conn.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
conn.write_all(b"hello-oxide").unwrap();
let mut buf = [0u8; 11];
client.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"hello-oxide");
let _ = std::fs::remove_file(&path);
}
/// TCP 回环:bind `127.0.0.1:0`(端口 0 由操作系统分配空闲端口),
/// 经 `local_addr` 取端口,connect 后写读往返。
#[test]
fn tcp_loopback_roundtrip() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let mut client = TcpStream::connect(addr).unwrap();
let listener = Listener::Tcp(listener);
listener.set_nonblocking(false).unwrap();
let mut conn = listener.accept().unwrap();
let _peer = conn.try_clone().unwrap();
conn.set_read_timeout(Some(Duration::from_secs(5))).unwrap();
conn.set_write_timeout(Some(Duration::from_secs(5))).unwrap();
conn.write_all(b"hello-oxide").unwrap();
let mut buf = [0u8; 11];
client.read_exact(&mut buf).unwrap();
assert_eq!(&buf, b"hello-oxide");
}
/// 非阻塞 accept 空轮:置非阻塞后无连接时 accept 应返回 `WouldBlock`(两臂各验一次)。
#[test]
fn nonblocking_accept_would_block() {
let dir = std::env::temp_dir().join(format!("oxide_net_{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let path = dir.join("unix_nb.sock");
let _ = std::fs::remove_file(&path);
let unix = Listener::Unix(UnixListener::bind(&path).unwrap());
unix.set_nonblocking(true).unwrap();
let err = unix.accept().unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::WouldBlock);
let tcp = Listener::Tcp(TcpListener::bind("127.0.0.1:0").unwrap());
tcp.set_nonblocking(true).unwrap();
let err = tcp.accept().unwrap_err();
assert_eq!(err.kind(), io::ErrorKind::WouldBlock);
let _ = std::fs::remove_file(&path);
}
}