use super::*;
use tokio::io::AsyncWriteExt;
async fn connected_pair() -> (TcpStream, TcpStream) {
let listener = TcpListener::bind(("127.0.0.1", 0)).await.unwrap();
let addr = listener.local_addr().unwrap();
let client = TcpStream::connect(addr).await.unwrap();
let (server_side, _) = listener.accept().await.unwrap();
(server_side, client)
}
#[tokio::test]
async fn reads_exactly_up_to_and_including_the_terminating_blank_line() {
let (mut server_side, mut client) = connected_pair().await;
client
.write_all(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
.await
.unwrap();
let prefix = read_header_prefix(&mut server_side)
.await
.unwrap_or_else(|_| {
panic!("expected a complete header block to be read");
});
assert_eq!(prefix, b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
}
#[tokio::test]
async fn assembles_a_header_block_split_across_multiple_writes() {
let (mut server_side, mut client) = connected_pair().await;
client
.write_all(b"GET /page HTTP/1.1\r\nHost: localhost\r")
.await
.unwrap();
client.write_all(b"\n\r\n").await.unwrap();
let prefix = read_header_prefix(&mut server_side)
.await
.unwrap_or_else(|_| {
panic!("expected a complete header block to be read across multiple writes");
});
assert_eq!(prefix, b"GET /page HTTP/1.1\r\nHost: localhost\r\n\r\n");
}
#[tokio::test]
async fn preserves_bytes_sent_past_the_header_block() {
let (mut server_side, mut client) = connected_pair().await;
let first = b"GET /a HTTP/1.1\r\nHost: localhost\r\n\r\n";
let second = b"GET /b HTTP/1.1\r\nHost: localhost\r\n\r\n";
let mut sent = Vec::new();
sent.extend_from_slice(first);
sent.extend_from_slice(second);
client.write_all(&sent).await.unwrap();
let prefix = read_header_prefix(&mut server_side)
.await
.unwrap_or_else(|_| {
panic!("expected a complete header block to be read");
});
assert_eq!(
&prefix, &sent,
"pipelined bytes past the first header block must survive intact"
);
}
#[tokio::test]
async fn errors_with_connection_closed_when_client_disconnects_before_headers_complete() {
let (mut server_side, client) = connected_pair().await;
drop(client);
match read_header_prefix(&mut server_side).await {
Err(HeaderReadError::ConnectionClosed) => {}
Err(_) => panic!("expected ConnectionClosed, got a different error variant"),
Ok(_) => {
panic!("expected an error, got a complete header block from a closed connection")
}
}
}
#[tokio::test]
async fn errors_with_too_large_once_max_header_bytes_is_exceeded_without_a_terminator() {
let (mut server_side, mut client) = connected_pair().await;
let garbage = vec![b'a'; MAX_HEADER_BYTES + 1];
client.write_all(&garbage).await.unwrap();
match read_header_prefix(&mut server_side).await {
Err(HeaderReadError::TooLarge) => {}
Err(_) => panic!("expected TooLarge, got a different error variant"),
Ok(_) => {
panic!("expected an error, got a complete header block from unterminated garbage")
}
}
}
#[tokio::test]
async fn prefixed_io_replays_the_prefix_before_reading_from_the_live_socket() {
let (server_side, mut client) = connected_pair().await;
let mut io = PrefixedIo::new(b"buffered-prefix".to_vec(), server_side);
client.write_all(b"-live-bytes").await.unwrap();
let mut collected = Vec::new();
let mut chunk = [0u8; 8];
while collected.len() < b"buffered-prefix-live-bytes".len() {
let n = io.read(&mut chunk).await.unwrap();
assert!(n > 0, "read returned 0 before all expected bytes arrived");
collected.extend_from_slice(&chunk[..n]);
}
assert_eq!(collected, b"buffered-prefix-live-bytes");
}