use crate::error::{Error, SocketError};
use crate::transport::connection::Connection;
use crate::transport::tests::scripted_socket::{ReadStep, ScriptedSocket, WriteStep};
use alloc::string::ToString;
use alloc::vec;
use alloc::vec::Vec;
fn headers_and_body(
cl: usize,
body: &[u8],
) -> Vec<u8> {
let mut v = Vec::new();
v.extend_from_slice(b"HTTP/1.1 200 OK\r\nContent-Length: ");
v.extend_from_slice(cl.to_string().as_bytes());
v.extend_from_slice(b"\r\n\r\n");
v.extend_from_slice(body);
v
}
#[test]
fn eof_mid_content_length_fails_not_short_ok() {
let partial = b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\nhel";
let mut socket = ScriptedSocket::new();
socket
.push_read(ReadStep::Data(partial.to_vec()))
.push_read(ReadStep::Eof);
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::NotConnected));
}
#[test]
fn partial_body_then_eof_via_zero_then_data_still_fails() {
let headers = b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\n\r\n".to_vec();
let mut socket = ScriptedSocket::new();
socket
.push_read(ReadStep::Data(headers))
.push_read(ReadStep::ZeroThenData(b"helloworld".to_vec()));
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::NotConnected));
}
#[test]
fn short_reads_still_assemble_full_content_length() {
let body = b"HelloWorld";
let full = headers_and_body(10, body);
let mut socket = ScriptedSocket::new().with_max_io_calls(500);
for byte in full {
socket.push_read(ReadStep::Data(vec![byte]));
}
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let raw = conn.read_raw_response(true).unwrap();
assert_eq!(&raw.body_bytes[..], b"HelloWorld");
assert!(conn.is_reusable());
}
#[test]
fn timed_out_on_read_fails_with_socket_timed_out() {
let headers = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n".to_vec();
let mut socket = ScriptedSocket::new();
socket
.push_read(ReadStep::Data(headers))
.push_read(ReadStep::TimedOut);
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::TimedOut));
}
#[test]
fn interrupted_on_read_fails_immediately_no_retry() {
let headers = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n".to_vec();
let mut socket = ScriptedSocket::new();
socket
.push_read(ReadStep::Data(headers))
.push_read(ReadStep::Interrupted)
.push_read(ReadStep::Data(b"Hello".to_vec()));
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::Interrupted));
assert_eq!(socket.read_calls, 2);
}
#[test]
fn os_error_on_read_propagates() {
let mut socket = ScriptedSocket::new();
socket.push_read(ReadStep::Error(SocketError::OsError(5)));
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::OsError(5)));
}
#[test]
fn connection_refused_style_error_on_read() {
let mut socket = ScriptedSocket::new();
socket.push_read(ReadStep::Error(SocketError::ConnectionRefused));
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::ConnectionRefused));
}
#[test]
fn not_connected_on_read_propagates() {
let mut socket = ScriptedSocket::new();
socket.push_read(ReadStep::Error(SocketError::NotConnected));
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::NotConnected));
}
#[test]
fn fatal_read_error_caller_must_discard_connection() {
let mut socket = ScriptedSocket::new();
socket
.push_read(ReadStep::Data(b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n".to_vec()))
.push_read(ReadStep::Error(SocketError::OsError(104)));
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert!(matches!(err, Error::Socket(SocketError::OsError(104))));
assert!(conn.is_reusable());
}
#[test]
fn eof_mid_headers_does_not_panic() {
let mut socket = ScriptedSocket::new();
socket
.push_read(ReadStep::Data(b"HTTP/1.1 200 OK\r\nConten".to_vec()))
.push_read(ReadStep::Eof);
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert!(
matches!(err, Error::Socket(_) | Error::Parse(_)),
"unexpected error: {err:?}"
);
}
#[test]
fn write_failure_mid_request_reports_bytes_accepted() {
let request = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
let mut socket = ScriptedSocket::new();
socket
.push_write(WriteStep::Accept(10))
.push_write(WriteStep::Error(SocketError::OsError(32)));
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.send_request(request, &[]).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::OsError(32)));
assert_eq!(socket.written_len(), 10);
assert_eq!(socket.get_written(), &request[..10]);
}
#[test]
fn zero_byte_write_maps_to_not_connected() {
let request = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
let mut socket = ScriptedSocket::new();
socket.push_write(WriteStep::Zero);
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.send_request(request, &[]).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::NotConnected));
assert_eq!(socket.written_len(), 0);
}
#[test]
fn interrupted_on_write_no_connection_retry() {
let request = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
let mut socket = ScriptedSocket::new();
socket
.push_write(WriteStep::Accept(5))
.push_write(WriteStep::Interrupted)
.push_write(WriteStep::AcceptAll);
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.send_request(request, &[]).unwrap_err();
assert_eq!(err, Error::Socket(SocketError::Interrupted));
assert_eq!(socket.written_len(), 5);
assert_eq!(socket.write_calls, 2, "must not retry Interrupted at Connection layer");
}
#[test]
fn max_io_calls_guards_against_infinite_loop() {
let mut socket = ScriptedSocket::new().with_max_io_calls(50);
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let err = conn.read_raw_response(true).unwrap_err();
assert!(
matches!(err, Error::Socket(_) | Error::Parse(_)),
"unexpected error: {err:?}"
);
assert!(
socket.read_calls <= 50,
"too many reads ({}); possible infinite loop",
socket.read_calls
);
}
#[test]
fn data_step_splits_across_buffer_sized_reads() {
let body = b"ABCDEFGHIJ";
let full = headers_and_body(10, body);
let mut socket = ScriptedSocket::new();
socket.push_read(ReadStep::Data(full));
let mut conn = Connection::new(&mut socket, 8192, usize::MAX);
let raw = conn.read_raw_response(true).unwrap();
assert_eq!(&raw.body_bytes[..], body);
}