fix(server): cap /mcp body size (SRV-03)

Add tests for the /mcp body cap and complete the task file.

- oversized POST /mcp with declared Content-Length > 8 MiB -> 413
  before any body read
- oversized chunked POST /mcp -> 413 (counting-stream cut mid-body;
  rmcp maps body errors to 500, so the middleware sources the status)
- normal-size initialize round-trip unchanged
- task file: status completed, Summary filled

Verified: cargo test (219), cargo test --features mcp --lib (257),
cargo test --all-features (269 + integration), clippy default and
--all-features (-D warnings), cargo fmt --check.
This commit is contained in:
2026-08-29 09:42:42 +00:00
parent e38eaf1cea
commit a4df859771
2 changed files with 80 additions and 25 deletions
+27 -16
View File
@@ -738,32 +738,43 @@ mod tests {
let _ = ProtocolHandler::handle(&adapter, conn, &auth).await;
});
let mut client = client;
client
let (mut reader_client, mut writer_client) = tokio::io::split(client);
writer_client
.write_all(
b"POST /mcp HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nAccept: application/json, text/event-stream\r\nTransfer-Encoding: chunked\r\nConnection: close\r\n\r\n",
)
.await
.unwrap();
let chunk_size = 64 * 1024;
for _ in 0..(MCP_BODY_LIMIT / chunk_size) + 1 {
let header = format!("{chunk_size:x}\r\n");
client.write_all(header.as_bytes()).await.unwrap();
let chunk = vec![b'a'; chunk_size];
client.write_all(&chunk).await.unwrap();
client.write_all(b"\r\n").await.unwrap();
}
client.write_all(b"0\r\n\r\n").await.unwrap();
let writer = tokio::spawn(async move {
let chunk = vec![b'a'; 64 * 1024];
let chunk_header = format!("{:x}\r\n", chunk.len());
for _ in 0..(MCP_BODY_LIMIT / chunk.len()) + 1 {
if writer_client
.write_all(chunk_header.as_bytes())
.await
.is_err()
{
return;
}
if writer_client.write_all(&chunk).await.is_err() {
return;
}
if writer_client.write_all(b"\r\n").await.is_err() {
return;
}
}
let _ = writer_client.write_all(b"0\r\n\r\n").await;
});
let mut response = Vec::new();
let _ = tokio::time::timeout(
let read = tokio::time::timeout(
std::time::Duration::from_secs(10),
client.read_to_end(&mut response),
reader_client.read_to_end(&mut response),
)
.await
.expect("read timed out")
.unwrap();
.await;
writer.abort();
read.expect("read timed out").unwrap();
let text = String::from_utf8_lossy(&response);
assert!(