mirror of
https://github.com/Corfirean/coa-server-manager.git
synced 2026-11-13 04:21:02 +00:00
Console shutdown request reads the reply before closing
send_without_answer closed the socket right after writing "server shutdown". With the reply still unread, Windows resets the connection, and the world can drop the command: a world supervised by coa-launcher.exe ignored the shutdown and the stop timed out after 90 seconds. The reply is now read up to the next prompt, the end of the connection or 10 seconds. The E2E also checks which supervisor runs the world and that manage.py stops a world the Manager started. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a822bfc99b
commit
a9fb982ab0
2 files changed
+27
-2
No files matched your search
@@ -85,10 +85,21 @@ fn main() {
|
||||
assert_eq!(std::fs::read(&manage_py).unwrap(), original, "manage.py is not rewritten");
|
||||
assert_eq!(realm_name(root), "AzerothCore", "no profile: the launcher's own realm name");
|
||||
|
||||
let shipped = root.join("Scripts/coa-launcher.exe");
|
||||
let supervisor = state::running(root, "supervisor").expect("a supervisor");
|
||||
println!("[supervisor] {}", supervisor.exe);
|
||||
let expected = if shipped.is_file() { "coa-launcher.exe" } else { "python.exe" };
|
||||
assert!(supervisor.exe.to_lowercase().ends_with(expected), "the world is supervised by {expected}");
|
||||
|
||||
// 2
|
||||
assert!(drive(root, Verb::StopAll).ok, "stop-all");
|
||||
assert!(nothing_running(root), "1-2 left something running");
|
||||
|
||||
// 2b: the repack's Python launcher stops a world the Manager started
|
||||
assert!(drive(root, Verb::StartAll).ok);
|
||||
assert!(manage(root, "stop-all").0, "manage.py stops what the Manager started");
|
||||
assert!(nothing_running(root), "2b left something running");
|
||||
|
||||
// 3
|
||||
assert!(manage(root, "start-all").0);
|
||||
assert!(drive(root, Verb::StopAll).ok, "driver stops what manage.py started");
|
||||
|
||||
@@ -88,12 +88,26 @@ impl Ra {
|
||||
self.command(c)
|
||||
}
|
||||
|
||||
/// Send a command the server does not answer before it closes the console (`server shutdown`), and return at
|
||||
/// once.
|
||||
/// Send a command whose answer does not matter (`server shutdown`). The reply is still read (up to the next
|
||||
/// prompt, the end of the connection, or a few seconds): closing a socket that holds unread data resets the
|
||||
/// connection, and the server can then drop the command it has not processed yet.
|
||||
pub fn send_without_answer(mut self, cmd: &str) -> Result<()> {
|
||||
let c = crate::console::check_command(cmd)?;
|
||||
self.stream.write_all(format!("{c}\r\n").as_bytes())?;
|
||||
self.stream.flush()?;
|
||||
self.stream.set_read_timeout(Some(Duration::from_secs(10)))?;
|
||||
let mut seen = Vec::new();
|
||||
let mut buf = [0u8; 4096];
|
||||
while let Ok(n) = self.stream.read(&mut buf) {
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
seen.extend_from_slice(&buf[..n]);
|
||||
if seen.ends_with(b"AC>") || seen.len() > 1 << 20 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let _ = self.stream.shutdown(std::net::Shutdown::Both);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user