use reqwest::header::{CONTENT_LENGTH, TRANSFER_ENCODING};
use serde_json::json;
use std::time::Duration;
mod mcp_test_helpers;
use mcp_test_helpers::with_mcp_test_server;
#[tokio::test]
async fn test_http_headers_no_dual_encoding() {
with_mcp_test_server("http_headers_compliance", |server| async move {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(5))
.build()?;
let init_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1.0"}
}
});
let response = client
.post(&server.http_url())
.json(&init_request)
.send()
.await?;
let has_content_length = response.headers().contains_key(CONTENT_LENGTH);
let has_transfer_encoding = response.headers().contains_key(TRANSFER_ENCODING);
assert!(
!(has_content_length && has_transfer_encoding),
"HTTP protocol violation: Response has both Content-Length and Transfer-Encoding headers"
);
let progress_request = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "echo",
"arguments": {"message": "test"},
"_meta": {
"progressToken": "test-token-123"
}
}
});
let response = client
.post(&server.http_url())
.json(&progress_request)
.send()
.await?;
let has_content_length = response.headers().contains_key(CONTENT_LENGTH);
let has_transfer_encoding = response.headers().contains_key(TRANSFER_ENCODING);
assert!(
!(has_content_length && has_transfer_encoding),
"HTTP protocol violation in progress response: Has both Content-Length and Transfer-Encoding"
);
assert!(
has_content_length || has_transfer_encoding,
"Response must have either Content-Length or Transfer-Encoding"
);
Ok(())
})
.await
.unwrap();
}
#[tokio::test]
async fn test_chunked_encoding_for_progress_tokens() {
with_mcp_test_server("chunked_encoding_progress", |server| async move {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(5))
.build()?;
let init_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1.0"}
}
});
let init_response = client
.post(&server.http_url())
.json(&init_request)
.send()
.await?;
assert_eq!(init_response.status(), 200);
let progress_request = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "echo",
"arguments": {"message": "test"},
"_meta": {
"progressToken": "test-progress-123"
}
}
});
let response = client
.post(&server.http_url())
.json(&progress_request)
.send()
.await?;
println!("Response headers: {:?}", response.headers());
let transfer_encoding = response
.headers()
.get(TRANSFER_ENCODING)
.and_then(|v| v.to_str().ok());
assert_eq!(
transfer_encoding,
Some("chunked"),
"Progress token requests should use chunked encoding"
);
assert!(
!response.headers().contains_key(CONTENT_LENGTH),
"Chunked responses must not have Content-Length header"
);
Ok(())
})
.await
.unwrap();
}
#[tokio::test]
async fn test_regular_requests_use_content_length() {
with_mcp_test_server("content_length_regular", |server| async move {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(5))
.build()?;
let init_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1.0"}
}
});
let response = client
.post(&server.http_url())
.json(&init_request)
.send()
.await?;
assert!(
response.headers().contains_key(CONTENT_LENGTH),
"Regular responses should have Content-Length header"
);
assert!(
!response.headers().contains_key(TRANSFER_ENCODING),
"Regular responses should not have Transfer-Encoding header"
);
Ok(())
})
.await
.unwrap();
}