Initial Dever 0.0.1

This commit is contained in:
Codex committed 2026-11-03 03:32:07 +00:00
commit 81233d098c
1479 files changed
+163782

No files matched your search

@@ -0,0 +1,212 @@
// 独立 Rust 共享会话参照,不调用 Dever 运行库;覆盖收发、期限、关闭和所有权退出。
// TCP/WebSocket 握手在计时外完成;此参照不代表 TLS、HTTP 升级或整个 Scope 系统。
mod wire;
use futures_util::{SinkExt, StreamExt};
use std::{
borrow::Cow,
future::Future,
sync::{
Arc, Mutex,
atomic::{AtomicBool, Ordering},
},
time::Duration,
};
use tokio::{net::TcpStream, sync::Mutex as Direction};
use tokio_tungstenite::{
WebSocketStream,
tungstenite::{
Message,
protocol::{CloseFrame, frame::coding::CloseCode},
},
};
use tokio_util::sync::CancellationToken;
use wire::Wire;
pub type Error = Cow<'static, str>;
#[derive(Clone, Copy)]
pub struct Limits {
pub message: usize,
pub idle: Duration,
pub write: Duration,
}
struct State {
socket: Mutex<Option<WebSocketStream<TcpStream>>>,
reader: Direction<()>,
writer: Direction<()>,
closing: CancellationToken,
ended: AtomicBool,
limits: Limits,
}
impl State {
fn abort(&self) {
self.closing.cancel();
self.socket.lock().unwrap().take();
}
async fn active<Value>(
&self,
work: impl Future<Output = Result<Value, Error>>,
) -> Result<Value, Error> {
tokio::select! {
biased;
() = self.closing.cancelled() => Err("WebSocket is closing".into()),
result = work => result,
}
}
}
// 所有者退出同步释放协议连接;保留别名不能延长连接生命。
pub struct Owner(Arc<State>);
impl Drop for Owner {
fn drop(&mut self) {
self.0.abort();
}
}
struct Writing<'socket>(Option<&'socket State>);
impl Drop for Writing<'_> {
fn drop(&mut self) {
if let Some(state) = self.0 {
state.abort();
}
}
}
#[derive(Clone)]
pub struct Socket(Arc<State>);
impl Socket {
pub fn new(socket: WebSocketStream<TcpStream>, limits: Limits) -> (Self, Owner) {
let state = Arc::new(State {
socket: Mutex::new(Some(socket)),
reader: Direction::new(()),
writer: Direction::new(()),
closing: CancellationToken::new(),
ended: AtomicBool::new(false),
limits,
});
(Self(state.clone()), Owner(state))
}
pub async fn send(&self, message: Message) -> Result<(), Error> {
let limit = match message {
Message::Text(_) | Message::Binary(_) => self.0.limits.message,
Message::Ping(_) | Message::Pong(_) => 125,
_ => return Err("use close for the closing handshake".into()),
};
if message.len() > limit {
return Err("WebSocket message exceeds byte limit".into());
}
self.0
.active(async {
tokio::time::timeout(self.0.limits.write, async {
let _writer = self.0.writer.lock().await;
let mut writing = Writing(Some(&self.0));
Wire(&self.0.socket).send(message).await.map_err(failure)?;
writing.0 = None;
Ok(())
})
.await
.map_err(|_| Error::from("WebSocket write timed out"))?
})
.await
}
pub async fn receive(&self) -> Result<Option<Message>, Error> {
if self.0.ended.load(Ordering::Acquire) {
return Ok(None);
}
let result = self
.0
.active(async {
tokio::time::timeout(self.0.limits.idle, async {
let _reader = self.0.reader.lock().await;
if self.0.ended.load(Ordering::Acquire) {
return Ok(None);
}
let message = Wire(&self.0.socket)
.next()
.await
.transpose()
.map_err(failure)?;
match message {
Some(Message::Ping(_)) => {
self.reply().await?;
Ok(message)
}
Some(Message::Close(_)) | None => {
let _closing = Writing(Some(&self.0));
self.reply().await?;
self.0.ended.store(true, Ordering::Release);
Ok(None)
}
_ => Ok(message),
}
})
.await
.map_err(|_| Error::from("WebSocket receive timed out"))?
})
.await;
if result.is_err() && !self.0.closing.is_cancelled() {
self.0.abort();
}
result
}
async fn reply(&self) -> Result<(), Error> {
let _writer = self.0.writer.lock().await;
let mut writing = Writing(Some(&self.0));
tokio::time::timeout(self.0.limits.write, Wire(&self.0.socket).flush())
.await
.map_err(|_| Error::from("WebSocket reply timed out"))?
.map_err(failure)?;
writing.0 = None;
Ok(())
}
pub async fn close(&self, code: u16, reason: &str) -> Result<(), Error> {
let code = CloseCode::from(code);
if !code.is_allowed() || reason.len() > 123 {
return Err("invalid WebSocket close frame".into());
}
if self.0.closing.is_cancelled() {
return Err("WebSocket is closed".into());
}
self.0.closing.cancel();
let _closing = Writing(Some(&self.0));
tokio::time::timeout(self.0.limits.write, async {
let writer = self.0.writer.lock().await;
Wire(&self.0.socket)
.send(Message::Close(Some(CloseFrame {
code,
reason: reason.to_owned().into(),
})))
.await
.map_err(failure)?;
drop(writer);
let _reader = self.0.reader.lock().await;
loop {
let message = Wire(&self.0.socket)
.next()
.await
.transpose()
.map_err(failure)?;
Wire(&self.0.socket).flush().await.map_err(failure)?;
if matches!(message, Some(Message::Close(_)) | None) {
self.0.ended.store(true, Ordering::Release);
return Ok(());
}
}
})
.await
.map_err(|_| Error::from("WebSocket close timed out"))?
}
}
fn failure(error: tokio_tungstenite::tungstenite::Error) -> Error {
error.to_string().into()
}
@@ -0,0 +1,58 @@
use futures_util::{Sink, Stream};
use std::{
pin::Pin,
sync::Mutex,
task::{Context, Poll},
};
use tokio::net::TcpStream;
use tokio_tungstenite::{
WebSocketStream,
tungstenite::{Error, Message},
};
// 共享引用仅在一次协议调用内借用连接;send/next 的状态机由 futures-util 提供。
pub(super) struct Wire<'socket>(pub &'socket Mutex<Option<WebSocketStream<TcpStream>>>);
impl Wire<'_> {
fn with<Value>(
&self,
call: impl FnOnce(Pin<&mut WebSocketStream<TcpStream>>) -> Value,
) -> Result<Value, Error> {
let mut socket = self.0.lock().unwrap();
socket
.as_mut()
.map(|socket| call(Pin::new(socket)))
.ok_or(Error::ConnectionClosed)
}
}
impl Sink<Message> for Wire<'_> {
type Error = Error;
fn poll_ready(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Result<(), Error>> {
self.with(|socket| socket.poll_ready(context))?
}
fn start_send(self: Pin<&mut Self>, message: Message) -> Result<(), Error> {
self.with(|socket| socket.start_send(message))?
}
fn poll_flush(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Result<(), Error>> {
self.with(|socket| socket.poll_flush(context))?
}
fn poll_close(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Result<(), Error>> {
self.with(|socket| socket.poll_close(context))?
}
}
impl Stream for Wire<'_> {
type Item = Result<Message, Error>;
fn poll_next(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match self.with(|socket| socket.poll_next(context)) {
Ok(result) => result,
Err(error) => Poll::Ready(Some(Err(error))),
}
}
}