fix(mcp): detach SSE keep-alive so it cannot wedge the HTTP server
The MCP HTTP server serves connections sequentially on the single current-thread runtime (the browser session is !Send). The SSE branch (GET /mcp, Accept: text/event-stream) ran an infinite keep-alive ping loop inline, so handle_connection never returned and the accept loop never ran again — one open SSE stream hung every later POST /mcp tool call indefinitely (functional break + trivial unauthenticated DoS). The keep-alive carries no browser state and its write half is Send + 'static, so spawn the ping loop on its own task and return from the handler. POST handling stays serialized (correct for a single browser session), but a live SSE stream no longer blocks the accept loop. Fixes #489
This commit is contained in:
1 parent
e314b9b0f3
commit
ea417bcd11
2 files changed
+114
-8
No files matched your search
@@ -169,7 +169,14 @@ async fn handle_connection(
|
||||
}
|
||||
|
||||
"GET" if accept_sse => {
|
||||
// SSE stream: hold open and send periodic keep-alive comments
|
||||
// SSE stream: hold open and send periodic keep-alive comments.
|
||||
// Connections are served sequentially (the browser session is
|
||||
// `!Send`), so holding this infinite keep-alive loop inline would
|
||||
// never return to the accept loop and would wedge every later
|
||||
// request. The keep-alive touches no browser state and the write
|
||||
// half is `Send + 'static`, so detach the ping loop onto its own
|
||||
// task and return — the accept loop stays free while the stream
|
||||
// lives on independently.
|
||||
let hdr = format!(
|
||||
"HTTP/1.1 200 OK\r\n\
|
||||
Content-Type: text/event-stream\r\n\
|
||||
@@ -179,14 +186,16 @@ async fn handle_connection(
|
||||
\r\n"
|
||||
);
|
||||
writer.write_all(hdr.as_bytes()).await?;
|
||||
loop {
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(15)).await;
|
||||
if writer.write_all(b": ping\n\n").await.is_err() {
|
||||
break;
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(tokio::time::Duration::from_secs(15)).await;
|
||||
if writer.write_all(b": ping\n\n").await.is_err() {
|
||||
break;
|
||||
}
|
||||
let _ = writer.flush().await;
|
||||
}
|
||||
let _ = writer.flush().await;
|
||||
}
|
||||
break;
|
||||
});
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
"POST" => {
|
||||
|
||||
Reference in new issue
Block a user