- Simplified token type assignment in OAuth service. - Removed unused session_lock module and re-exported Session from zesdex_entities. - Cleaned up session entity by removing unnecessary comments and code. - Consolidated session handling in HTTP handlers for better readability. - Improved formatting and readability in OAuth repository tests. - Enhanced session lock repository with clearer match statements. - Streamlined session repository error handling. - Refined RNG tests for better clarity. - Adjusted module visibility and organization in lib.rs. - Updated IPC client and connection code for better error handling and clarity. - Improved frame handling in IPC for better readability. - Organized module imports and added test utilities for IPC. - Enhanced database connection error handling. - Simplified JWT token creation error handling. - Improved password verification error handling. - Cleaned up state management code for better readability. - Refactored middleware for session authentication and rate limiting. - Simplified clipboard utility for better error handling. - Enhanced logging initialization for better error reporting. - Improved pagination utility with clearer method annotations. - Cleaned up sanitization functions for filenames and paths. - Enhanced slug generation functions for better clarity and usability.
111 lines
3.7 KiB
Rust
111 lines
3.7 KiB
Rust
//! IPC client — connects to the daemon's Unix socket and sends/receives
|
|
//! framed JSON messages.
|
|
//!
|
|
//! [`IpcClient`] wraps a [`Connection`] behind a [`Mutex`] so it can be
|
|
//! shared across threads (e.g. the TUI event loop and the render task).
|
|
|
|
use crate::conn::Connection;
|
|
use anyhow::{Context, Result};
|
|
use serde::de::DeserializeOwned;
|
|
use serde::Serialize;
|
|
use std::os::unix::net::UnixStream;
|
|
use std::sync::Mutex;
|
|
|
|
/// A thread-safe IPC client connected to a Zesdex daemon over a Unix
|
|
/// socket.
|
|
pub struct IpcClient {
|
|
/// Inner connection protected by a mutex for shared access.
|
|
conn: Mutex<Connection>,
|
|
}
|
|
|
|
impl IpcClient {
|
|
/// Connect to the daemon listening at `path` (a Unix socket path).
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns an error if the socket path does not exist, the connection
|
|
/// is refused, or the caller lacks permission.
|
|
pub fn connect_unix(path: &str) -> Result<Self> {
|
|
let stream = UnixStream::connect(path)
|
|
.with_context(|| format!("failed to connect to Unix socket at {path:?}"))?;
|
|
let conn = Connection::new(stream);
|
|
Ok(Self {
|
|
conn: Mutex::new(conn),
|
|
})
|
|
}
|
|
|
|
/// Serialise `msg` to JSON and send it as a length-prefixed frame.
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if the internal mutex is poisoned (a previous operation
|
|
/// panicked while holding the lock).
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Delegates to the underlying [`Connection::send`].
|
|
pub fn send<T: Serialize>(&self, msg: &T) -> Result<()> {
|
|
let mut guard = self
|
|
.conn
|
|
.lock()
|
|
.expect("IpcClient mutex poisoned — the previous operation panicked");
|
|
guard.send(msg)
|
|
}
|
|
|
|
/// Read one framed JSON message and deserialise it.
|
|
///
|
|
/// Returns `Ok(None)` on clean EOF (daemon closed the connection).
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if the internal mutex is poisoned (a previous operation
|
|
/// panicked while holding the lock).
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Delegates to the underlying [`Connection::receive`].
|
|
pub fn receive<T: DeserializeOwned>(&self) -> Result<Option<T>> {
|
|
let mut guard = self
|
|
.conn
|
|
.lock()
|
|
.expect("IpcClient mutex poisoned — the previous operation panicked");
|
|
guard.receive()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::os::unix::net::UnixListener;
|
|
use crate::test_utils::Ping;
|
|
|
|
#[test]
|
|
fn connect_and_round_trip() {
|
|
let id = crate::test_utils::TEST_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
|
|
let dir = std::env::temp_dir().join(format!("zesdex-ipc-test-{}-{}", std::process::id(), id));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
let sock_path = dir.join("test.sock");
|
|
let sock_path_str = sock_path.to_string_lossy().to_string();
|
|
|
|
// Start a minimal echo server in a background thread.
|
|
let listener = UnixListener::bind(&sock_path).unwrap();
|
|
let server_handle = std::thread::spawn(move || {
|
|
let (stream, _) = listener.accept().unwrap();
|
|
let mut conn = Connection::new(stream);
|
|
// Echo one message back.
|
|
let req: Ping = conn.receive().unwrap().unwrap();
|
|
conn.send(&req).unwrap();
|
|
});
|
|
|
|
// Client connects and sends a ping, then receives the echo.
|
|
let client = IpcClient::connect_unix(&sock_path_str).unwrap();
|
|
client.send(&Ping { seq: 7 }).unwrap();
|
|
let resp: Ping = client.receive().unwrap().expect("expected a response");
|
|
assert_eq!(resp, Ping { seq: 7 });
|
|
|
|
server_handle.join().unwrap();
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|
|
}
|