use std::io::{self, BufReader, Read, Write};
use std::net::{Shutdown, TcpListener, TcpStream};
use std::time::Duration;
use super::{LINGER_TIMEOUT, Response, head, linger, write_error};
use crate::mcp::http::fixtures::{Reply, TestResult, refusal};
#[test]
fn an_elapsed_write_deadline_refuses_before_writing_bytes() -> TestResult {
let listener = TcpListener::bind("127.0.0.1:0")?;
let caller = TcpStream::connect(listener.local_addr()?)?;
let (mut socket, _) = listener.accept()?;
let response = Response {
status: 200,
body: b"{}".to_vec(),
headers: Vec::new(),
};
let error = response
.write(&mut socket, Duration::ZERO)
.err()
.ok_or("write should refuse")?;
assert_eq!(error.status, 408);
assert_eq!(error.code, "mcp_http_write_timeout");
drop(caller);
Ok(())
}
#[test]
fn socket_write_timeouts_keep_their_name_and_io_cause() {
for kind in [io::ErrorKind::TimedOut, io::ErrorKind::WouldBlock] {
let error = write_error(&io::Error::from(kind));
assert_eq!(error.status, 408);
assert_eq!(error.code, "mcp_http_write_timeout");
assert!(error.to_string().contains(&format!("io:{kind:?}")));
}
let error = write_error(&io::Error::from(io::ErrorKind::BrokenPipe));
assert_eq!(error.code, "mcp_http_write_failed");
assert!(error.to_string().contains("BrokenPipe"));
}
#[test]
fn lingering_close_drains_every_byte_after_an_oversized_body() -> TestResult {
let cap = 4096;
let listener = TcpListener::bind("127.0.0.1:0")?;
let mut caller = TcpStream::connect(listener.local_addr()?)?;
let body = vec![b'x'; cap * 4];
write!(
caller,
"POST /mcp HTTP/1.1\r\nHost: fixture\r\nContent-Length: {}\r\n\r\n",
body.len()
)?;
caller.write_all(&body)?;
caller.shutdown(Shutdown::Write)?;
let (socket, _) = listener.accept()?;
let mut socket = BufReader::new(socket);
let error = head(&mut socket, LINGER_TIMEOUT, cap)
.err()
.ok_or("oversized body should refuse")?;
assert_eq!(error.status, 413);
assert_eq!(error.code, "mcp_http_body_refused");
Response::refused(&error, None)?.write(socket.get_mut(), LINGER_TIMEOUT)?;
linger(&mut socket)?;
let mut unread = Vec::new();
socket.read_to_end(&mut unread)?;
drop(socket);
refusal(&Reply::read(caller)?, 413, "mcp_http_body_refused")?;
assert!(
unread.is_empty(),
"lingering close left {} unread request bytes",
unread.len()
);
Ok(())
}