Files
markbase/vendor/smb-server/src/conn/reader.rs
Warren 7eb528d35f
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled
SMB Server Phase 2: VFS backend build fix + integration test
- Add VfsFile: Send supertrait for Mutex compatibility
- Fix SmbServerCommand: struct → Subcommand enum with Start variant
- Fix tracing_subscriber::init() → try_init() to avoid panic when
  logger already initialized
- Fix CLI subcommand name: smb-server → smb-start (flatten naming)
- Add #[command(name = "smb-start")] for CLI disambiguation
- Fix unused variable warnings (smb_fs.rs, smb_server_backend.rs)
- Remove unused VfsFile imports (webdav.rs, scp_handler.rs)
- Integration test: Docker smbclient verified (list, upload, read)
2026-06-20 19:42:29 +08:00

81 lines
2.7 KiB
Rust

//! Per-connection frame reader: pulls bytes off the socket, frames them,
//! hands each frame to the dispatcher.
use std::io;
use std::sync::Arc;
use crate::proto::framing::{FRAME_HEADER_LEN, decode_frame_header};
use tokio::io::{AsyncReadExt, ReadHalf};
use tokio::net::TcpStream;
use tracing::{debug, error};
use crate::conn::state::Connection;
use crate::server::ServerState;
/// Read one frame's payload (without the 4-byte length prefix).
///
/// Returns `Ok(None)` on a clean EOF, `Ok(Some(bytes))` on a complete frame,
/// `Err` on partial/garbled data.
pub async fn read_one_frame(reader: &mut ReadHalf<TcpStream>) -> io::Result<Option<Vec<u8>>> {
let mut hdr = [0u8; FRAME_HEADER_LEN];
match reader.read_exact(&mut hdr).await {
Ok(_) => {}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(None),
Err(e) => return Err(e),
}
let len = match decode_frame_header(&hdr) {
Ok(n) => n,
Err(e) => {
return Err(io::Error::new(io::ErrorKind::InvalidData, e.to_string()));
}
};
let mut payload = vec![0u8; len as usize];
reader.read_exact(&mut payload).await?;
Ok(Some(payload))
}
/// Continuously read frames; for each, await `dispatch_one`'s response and
/// route it to the writer.
///
/// Sequential dispatch keeps v1 simple and matches the spec's "single writer
/// task / per-frame dispatch" pattern. We process one frame at a time per
/// connection in v1 — a follow-up can spawn dispatch tasks if a workload
/// proves to need credit-window concurrency.
pub async fn reader_task(
mut reader: ReadHalf<TcpStream>,
server: Arc<ServerState>,
conn: Arc<Connection>,
tx: tokio::sync::mpsc::Sender<crate::conn::writer::FramePayload>,
) -> io::Result<()> {
loop {
let frame = match read_one_frame(&mut reader).await {
Ok(Some(b)) => b,
Ok(None) => {
debug!("client closed connection");
return Ok(());
}
Err(e) => {
error!(error = %e, "frame read error");
return Err(e);
}
};
// Check shutdown after every frame.
if server
.shutting_down
.load(std::sync::atomic::Ordering::Acquire)
{
debug!("server shutting down; dropping connection");
return Ok(());
}
// The dispatcher is async but we await it inline — order-preserving and
// good enough for v1.
let response = crate::dispatch::dispatch_frame(&server, &conn, &frame).await;
if let Some(bytes) = response
&& tx.send(bytes).await.is_err()
{
debug!("writer channel closed; reader exiting");
return Ok(());
}
}
}