Skip to content
This repository was archived by the owner on Dec 15, 2025. It is now read-only.

Commit 4c5b668

Browse files
committed
fixed bug where client kept hanging
1 parent 95ae127 commit 4c5b668

3 files changed

Lines changed: 504 additions & 10 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ clap = "2.33"
2121
git-version = "0.3.5"
2222
command-group = "1.0.8"
2323

24+
[dev-dependencies]
25+
tokio = { version = "1.14.0", features = ["full", "test-util"] }
26+
tempfile = "3.3.0"
2427
[lib]
2528
name = "zinit"
2629
path = "src/lib.rs"

src/app/api.rs

Lines changed: 101 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -260,11 +260,65 @@ impl Api {
260260

261261
// If it starts with '{', it's likely JSON-RPC
262262
if peek_buffer[0] == b'{' || peek_buffer[0] == b'[' {
263-
// Read the rest of the request
264-
if let Err(err) = stream.read_to_end(&mut buffer).await {
265-
error!("failed to read request: {}", err);
266-
let _ = stream.write_all("bad request".as_ref()).await;
267-
return;
263+
// Read the request until we find a newline or a certain amount of data
264+
// This avoids waiting for EOF which would require the client to close the connection
265+
let mut temp_buf = [0u8; 1024];
266+
loop {
267+
match stream.read(&mut temp_buf).await {
268+
Ok(0) => break, // Connection closed
269+
Ok(n) => {
270+
buffer.extend_from_slice(&temp_buf[..n]);
271+
272+
// Check if we have a complete JSON object/array
273+
// This is a simple heuristic - we count opening and closing braces/brackets
274+
let mut open_braces = 0;
275+
let mut open_brackets = 0;
276+
let mut in_string = false;
277+
let mut escape_next = false;
278+
279+
for &b in &buffer {
280+
if escape_next {
281+
escape_next = false;
282+
continue;
283+
}
284+
285+
match b {
286+
b'\\' if in_string => escape_next = true,
287+
b'"' => in_string = !in_string,
288+
b'{' if !in_string => open_braces += 1,
289+
b'}' if !in_string => {
290+
if open_braces > 0 {
291+
open_braces -= 1;
292+
}
293+
},
294+
b'[' if !in_string => open_brackets += 1,
295+
b']' if !in_string => {
296+
if open_brackets > 0 {
297+
open_brackets -= 1;
298+
}
299+
},
300+
_ => {}
301+
}
302+
}
303+
304+
// If all braces/brackets are balanced, we have a complete JSON
305+
if open_braces == 0 && open_brackets == 0 && !in_string {
306+
break;
307+
}
308+
309+
// Safety check to prevent buffer from growing too large
310+
if buffer.len() > 1024 * 1024 { // 1MB limit
311+
error!("request too large");
312+
let _ = stream.write_all("request too large".as_ref()).await;
313+
return;
314+
}
315+
},
316+
Err(err) => {
317+
error!("failed to read request: {}", err);
318+
let _ = stream.write_all("bad request".as_ref()).await;
319+
return;
320+
}
321+
}
268322
}
269323

270324
// First, try to parse as a batch request
@@ -634,9 +688,28 @@ impl Client {
634688
con.flush().await?;
635689

636690
// Read and parse the response
637-
// Use the same approach as the command method, which is known to work
638-
let mut data = String::new();
639-
con.read_to_string(&mut data).await?;
691+
// The server sends a JSON response followed by a newline character
692+
// We need to read the entire response until we find the terminating newline
693+
let mut buffer = Vec::new();
694+
let mut temp_buf = [0u8; 1024];
695+
696+
loop {
697+
let n = con.read(&mut temp_buf).await?;
698+
if n == 0 {
699+
break; // Connection closed
700+
}
701+
702+
buffer.extend_from_slice(&temp_buf[..n]);
703+
704+
// Check if the buffer ends with a newline
705+
if buffer.ends_with(b"\n") {
706+
break;
707+
}
708+
}
709+
710+
// Convert to string and trim the trailing newline
711+
let data = String::from_utf8(buffer)?;
712+
let data = data.trim_end();
640713

641714
// Parse the JSON-RPC response
642715
let response: JsonRpcResponse = encoder::from_str(&data)?;
@@ -660,8 +733,26 @@ impl Client {
660733
let _ = con.write(b"\n").await?;
661734
con.flush().await?;
662735

663-
let mut data = String::new();
664-
con.read_to_string(&mut data).await?;
736+
let mut buffer = Vec::new();
737+
let mut temp_buf = [0u8; 1024];
738+
739+
loop {
740+
let n = con.read(&mut temp_buf).await?;
741+
if n == 0 {
742+
break; // Connection closed
743+
}
744+
745+
buffer.extend_from_slice(&temp_buf[..n]);
746+
747+
// Check if the buffer ends with a newline
748+
if buffer.ends_with(b"\n") {
749+
break;
750+
}
751+
}
752+
753+
// Convert to string and trim the trailing newline
754+
let data = String::from_utf8(buffer)?;
755+
let data = data.trim_end();
665756

666757
let response: Response = encoder::from_str(&data)?;
667758

0 commit comments

Comments
 (0)