control: close the connection on malformed input before auth (#1259)
control::serve documents that the first request on a connection must be `auth` and that anything else closes the connection. A first line that wasn't valid JSON got a parse error but left the connection open, unlike a wrong token or a non-auth request. Close it in that case too; after a successful auth a malformed line still just gets the parse error. Adds control::transport_tests::malformed_input_before_auth_closes_the_connection, which fails before this change. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9c8cf1a701
commit
486b1bbcb1
|
|
@ -788,7 +788,8 @@ pub fn serve(client: ControlClient) -> std::io::Result<Endpoint> {
|
|||
continue;
|
||||
}
|
||||
let (reply, close) = match serde_json::from_str::<Value>(&line) {
|
||||
Err(e) => (rpc_error(Value::Null, -32700, &format!("parse error: {e}")), false),
|
||||
// Before `auth`, like any other first request, this closes the connection.
|
||||
Err(e) => (rpc_error(Value::Null, -32700, &format!("parse error: {e}")), !authed),
|
||||
Ok(msg) => {
|
||||
let id = msg.get("id").cloned().unwrap_or(Value::Null);
|
||||
let method = msg.get("method").and_then(Value::as_str).unwrap_or("");
|
||||
|
|
@ -839,3 +840,31 @@ fn random_token() -> std::io::Result<String> {
|
|||
getrandom::fill(&mut bytes).map_err(|e| std::io::Error::other(e.to_string()))?;
|
||||
Ok(bytes.iter().map(|b| format!("{b:02x}")).collect())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod transport_tests {
|
||||
/// Before authenticating, a line that isn't JSON gets a parse error and closes the
|
||||
/// connection, as [`super::serve`] documents for any first request other than `auth`.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
fn malformed_input_before_auth_closes_the_connection() {
|
||||
use std::io::{BufRead, BufReader, ErrorKind, Write};
|
||||
let (_control, client) = super::attach(&egui::Context::default());
|
||||
let ep = super::serve(client).unwrap();
|
||||
let s = std::net::TcpStream::connect(("127.0.0.1", ep.port)).unwrap();
|
||||
s.set_read_timeout(Some(std::time::Duration::from_secs(5))).unwrap();
|
||||
writeln!(s.try_clone().unwrap(), "not json").unwrap();
|
||||
let mut r = BufReader::new(s);
|
||||
let mut first = String::new();
|
||||
r.read_line(&mut first).unwrap();
|
||||
let reply: serde_json::Value = serde_json::from_str(&first).unwrap();
|
||||
assert_eq!(reply["error"]["code"], -32700, "{first}");
|
||||
// Closed: end of stream (or a reset), not a connection left waiting for more input.
|
||||
let mut next = String::new();
|
||||
let closed = match r.read_line(&mut next) {
|
||||
Ok(n) => n == 0,
|
||||
Err(e) => matches!(e.kind(), ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted),
|
||||
};
|
||||
assert!(closed, "the connection stayed open: {next:?}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue