cloud storage client
git clone https://git.lucas.co/cce-cloud.git
refactor: read requests with cce_ui::ipc::read_request_line
Byte-identical to the copy (and its test) deleted here; both live in
cce-ui now (40c66d4).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/main.rs | 79 +------------------------------------------------------------
1 file changed, 1 insertion(+), 78 deletions(-)
diff --git a/src/main.rs b/src/main.rs
index 52623cc..f8e3782 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3913,7 +3913,7 @@ fn run_daemon(socket_path: &str) {
// Bounded: this runs on the launcher's main loop, so a client that
// connected and said nothing used to freeze the launcher outright.
// The line carries a dmenu list inline, hence the generous size.
- let Some(initial_line) = read_request_line(&stream, 16 * 1024 * 1024, std::time::Duration::from_secs(3)) else {
+ let Some(initial_line) = cce_ui::ipc::read_request_line(&stream, 16 * 1024 * 1024, std::time::Duration::from_secs(3)) else {
continue;
};
let t_request = std::time::Instant::now();
@@ -5190,80 +5190,3 @@ mod repeat_tests {
assert!(!key_repeats(K::Shift_L, None));
}
}
-
-/// One request line from a control-socket client, bounded in size and in
-/// TOTAL time. Until 2026-10-02 this was `BufReader::read_line` on a socket
-/// with no timeout at all, on the daemon's main loop, so a client that connected and said nothing (or trickled a
-/// byte at a time) held the launcher frozen —
-/// no popup, no app menu, no switcher — until the client went away. None on EOF before any byte, timeout,
-/// overflow or a read error.
-fn read_request_line(conn: &std::os::unix::net::UnixStream, limit: usize, deadline: std::time::Duration) -> Option<String> {
- use std::io::Read;
- let until = std::time::Instant::now() + deadline;
- let mut buf: Vec<u8> = Vec::new();
- let mut chunk = [0u8; 4096];
- let mut reader = conn;
- loop {
- let left = until.checked_duration_since(std::time::Instant::now()).filter(|d| !d.is_zero())?;
- conn.set_read_timeout(Some(left)).ok()?;
- let n = reader.read(&mut chunk).ok()?;
- if n == 0 {
- if buf.is_empty() {
- return None;
- }
- break;
- }
- buf.extend_from_slice(&chunk[..n]);
- if let Some(end) = buf.iter().position(|&b| b == b'\n') {
- buf.truncate(end + 1);
- break;
- }
- if buf.len() > limit {
- return None;
- }
- }
- let _ = conn.set_read_timeout(None);
- String::from_utf8(buf).ok()
-}
-
-#[cfg(test)]
-mod request_line_tests {
- use super::read_request_line;
- use std::io::Write;
- use std::os::unix::net::UnixStream;
- use std::time::{Duration, Instant};
-
- #[test]
- fn a_request_line_is_bounded_in_time_and_size() {
- let (mut client, server) = UnixStream::pair().unwrap();
- client.write_all(b"open note\nmore").unwrap();
- assert_eq!(read_request_line(&server, 1024, Duration::from_secs(1)).as_deref(), Some("open note\n"));
-
- // Silent: given up at the deadline, not held forever.
- let (_quiet, server) = UnixStream::pair().unwrap();
- let t = Instant::now();
- assert_eq!(read_request_line(&server, 1024, Duration::from_millis(100)), None);
- assert!(t.elapsed() < Duration::from_millis(500));
-
- // Trickling a byte at a time: the TOTAL deadline still ends it.
- let (mut client, server) = UnixStream::pair().unwrap();
- let trickle = std::thread::spawn(move || {
- for _ in 0..40 {
- if client.write_all(b"x").is_err() {
- break;
- }
- std::thread::sleep(Duration::from_millis(25));
- }
- });
- let t = Instant::now();
- assert_eq!(read_request_line(&server, 1024, Duration::from_millis(150)), None);
- assert!(t.elapsed() < Duration::from_millis(500), "took {:?}", t.elapsed());
- drop(server);
- trickle.join().unwrap();
-
- // Over the size cap.
- let (mut client, server) = UnixStream::pair().unwrap();
- client.write_all(&[b'z'; 2000]).unwrap();
- assert_eq!(read_request_line(&server, 1024, Duration::from_millis(200)), None);
- }
-}