fix: a refused control connection reads what was sent before closing (#945)
Closing a socket with unread input resets the connection, and the reset could reach the client before the refusal did (requests_run_only_after_the_token failed 17 times in 100). The server now ends its side and drains, briefly and up to 64 KiB, what the client had sent before closing: 0 in 100. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
78d9aa0153
commit
abddd67d22
1 file changed
+13
-2
@@ -123,6 +123,17 @@ fn unauthorized() -> Value {
|
|||||||
json!({"ok": false, "error": "unauthorized: send the control token as \"token\" (see docs/control-protocol.md)"})
|
json!({"ok": false, "error": "unauthorized: send the control token as \"token\" (see docs/control-protocol.md)"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Refuse the connection: the reply, then the end of what we send, then what the client had
|
||||||
|
/// already sent read and dropped (a little, briefly). Closing a socket with unread input resets
|
||||||
|
/// the connection, and a reset can reach the client before it has read the refusal.
|
||||||
|
fn refuse(out: &mut TcpStream, reader: &mut BufReader<TcpStream>) {
|
||||||
|
use std::io::Read;
|
||||||
|
let _ = writeln!(out, "{}", unauthorized());
|
||||||
|
let _ = out.shutdown(std::net::Shutdown::Write);
|
||||||
|
let _ = reader.get_ref().set_read_timeout(Some(Duration::from_millis(500)));
|
||||||
|
let _ = std::io::copy(&mut reader.by_ref().take(64 * 1024), &mut std::io::sink());
|
||||||
|
}
|
||||||
|
|
||||||
fn serve(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: egui::Context) {
|
fn serve(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: egui::Context) {
|
||||||
let Ok(read) = stream.try_clone() else { return };
|
let Ok(read) = stream.try_clone() else { return };
|
||||||
let mut out = stream;
|
let mut out = stream;
|
||||||
@@ -148,7 +159,7 @@ fn serve(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: egui::
|
|||||||
match msg.get("token").and_then(Value::as_str) {
|
match msg.get("token").and_then(Value::as_str) {
|
||||||
Some(t) if control_auth::tokens_match(t, token) => authenticated = true,
|
Some(t) if control_auth::tokens_match(t, token) => authenticated = true,
|
||||||
_ => {
|
_ => {
|
||||||
let _ = writeln!(out, "{}", unauthorized());
|
refuse(&mut out, &mut reader);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +181,7 @@ fn serve(stream: TcpStream, token: &str, tx: Sender<ControlRequest>, ctx: egui::
|
|||||||
r
|
r
|
||||||
}
|
}
|
||||||
Err(_) if !authenticated => {
|
Err(_) if !authenticated => {
|
||||||
let _ = writeln!(out, "{}", unauthorized());
|
refuse(&mut out, &mut reader);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
Reference in new issue
Block a user