http-type 22.0.2

A comprehensive Rust type library for HTTP operations and concurrent programming. Provides core HTTP types (Request/Response with builder patterns, Method, HttpStatus, HttpVersion, ContentType, FileExtension with MIME mapping, Cookie parsing/building, HttpUrl parsing, WebSocket frame/opcode, protocol upgrade types, stream/task management, panic handling), thread-safe concurrent wrappers (ArcMutex, ArcRwLock, BoxRwLock, RcRwLock), dynamic dispatch types (BoxAny, RcAny, ArcAny with Send/Sync variants), high-performance hash collections (HashMapXxHash3_64, HashSetXxHash3_64), and static lifetime utilities (BoxLeak, Lifetime trait).
Documentation
use super::*;

#[test]
fn test_socket_host_type_alias() {
    let socket_host: SocketHost = "127.0.0.1".parse().unwrap();
    assert!(socket_host.is_ipv4());
    let socket_host_v6: SocketHost = "::1".parse().unwrap();
    assert!(socket_host_v6.is_ipv6());
}

#[test]
fn test_socket_port_type_alias() {
    let socket_port: SocketPort = 8080;
    assert_eq!(socket_port, 8080_u16);
}

#[tokio::test]
async fn test_from_transport_wraps_duplex_as_transport() {
    let (mut client, server): (DuplexStream, DuplexStream) = duplex(64);
    let config: RequestConfig = RequestConfig::default();
    let mut stream: Stream = Stream::from_transport(TestTransport::new(server), config, false);
    assert!(!stream.get_closed());
    assert!(stream.is_keep_alive(true));
    assert!(!stream.is_keep_alive(false));
    let sent: Vec<u8> = b"ping".to_vec();
    assert!(stream.try_send(&sent).await.is_ok());
    assert!(stream.try_flush().await.is_ok());
    let mut received: Vec<u8> = vec![0_u8; 4];
    client.read_exact(&mut received).await.unwrap();
    assert_eq!(received, b"ping".to_vec());
}

#[tokio::test]
async fn test_from_transport_reads_http_request_over_duplex() {
    let (mut client, server): (DuplexStream, DuplexStream) = duplex(1024);
    let raw: Vec<u8> = b"GET /trans HTTP/1.1\r\nHost: localhost\r\n\r\n".to_vec();
    client.write_all(&raw).await.unwrap();
    let config: RequestConfig = RequestConfig::default();
    let mut stream: Stream = Stream::from_transport(TestTransport::new(server), config, false);
    let request: Request = stream.try_get_http_request().await.unwrap();
    assert_eq!(*request.get_method(), RequestMethod::Get);
    assert_eq!(*request.get_path(), "/trans");
    assert_eq!(*request.get_version(), RequestVersion::Http1_1);
    assert_eq!(*request.get_host(), "localhost");
    assert!(request.get_body().is_empty());
}

#[tokio::test]
async fn test_from_transport_send_list_over_duplex() {
    let (mut client, server): (DuplexStream, DuplexStream) = duplex(64);
    let config: RequestConfig = RequestConfig::default();
    let mut stream: Stream = Stream::from_transport(TestTransport::new(server), config, false);
    let parts: Vec<&[u8]> = vec![b"HTTP/1.1 200 OK\r\n", b"Content-Length: 0\r\n\r\n"];
    assert!(stream.try_send_list(parts).await.is_ok());
    assert!(stream.try_flush().await.is_ok());
    let mut received: Vec<u8> = vec![0_u8; 38];
    client.read_exact(&mut received).await.unwrap();
    assert_eq!(&received[..4], b"HTTP");
    assert_eq!(&received[36..], b"\r\n");
}

#[tokio::test]
async fn test_from_transport_closed_rejects_writes() {
    let (_client, server): (DuplexStream, DuplexStream) = duplex(64);
    let config: RequestConfig = RequestConfig::default();
    let mut stream: Stream = Stream::from_transport(TestTransport::new(server), config, true);
    assert!(stream.get_closed());
    assert!(!stream.is_keep_alive(true));
    assert!(stream.try_send(b"nope").await.is_err());
    assert!(stream.try_send_list(vec![b"nope"]).await.is_err());
    assert!(stream.try_flush().await.is_err());
    assert!(stream.try_get_http_request().await.is_err());
}

#[tokio::test]
async fn test_from_transport_transport_without_socket_address_reports_error() {
    let (_client, server): (DuplexStream, DuplexStream) = duplex(64);
    let config: RequestConfig = RequestConfig::default();
    let stream: Stream = Stream::from_transport(TestTransport::new(server), config, false);
    assert!(stream.get_peer_addr().is_err());
    assert!(stream.get_local_addr().is_err());
    assert!(stream.get_stream().peer_addr().is_err());
    assert!(stream.get_stream().local_addr().is_err());
}

#[tokio::test]
async fn test_new_tcp_stream_reports_peer_and_local_address() {
    let listener: TcpListener = TcpListener::bind("127.0.0.1:0").await.unwrap();
    let bound: SocketAddr = listener.local_addr().unwrap();
    let client: TcpStream = TcpStream::connect(bound).await.unwrap();
    let (server, peer): (TcpStream, SocketAddr) = listener.accept().await.unwrap();
    assert_ne!(peer, bound);
    let config: RequestConfig = RequestConfig::default();
    let stream: Stream = Stream::new(server, config, false);
    assert_eq!(stream.get_peer_addr().unwrap(), peer);
    assert_eq!(stream.get_local_addr().unwrap(), bound);
    assert_eq!(stream.get_stream().peer_addr().unwrap(), peer);
    drop(client);
}

#[tokio::test]
async fn test_new_tcp_stream_parses_request_over_socket() {
    let listener: TcpListener = TcpListener::bind("127.0.0.1:0").await.unwrap();
    let bound: SocketAddr = listener.local_addr().unwrap();
    let mut client: TcpStream = TcpStream::connect(bound).await.unwrap();
    let (server, peer): (TcpStream, SocketAddr) = listener.accept().await.unwrap();
    let raw: Vec<u8> =
        b"POST /tcp HTTP/1.1\r\nHost: localhost\r\nContent-Length: 4\r\n\r\nbody".to_vec();
    client.write_all(&raw).await.unwrap();
    let config: RequestConfig = RequestConfig::default();
    let mut stream: Stream = Stream::new(server, config, false);
    let request: Request = stream.try_get_http_request().await.unwrap();
    assert_eq!(*request.get_method(), RequestMethod::Post);
    assert_eq!(*request.get_path(), "/tcp");
    assert_eq!(request.get_body(), b"body");
    assert_eq!(stream.get_peer_addr().unwrap(), peer);
    assert_eq!(stream.get_local_addr().unwrap(), bound);
    let response: Vec<u8> = b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n".to_vec();
    assert!(stream.try_send(&response).await.is_ok());
    assert!(stream.try_flush().await.is_ok());
    let mut echoed: Vec<u8> = vec![0_u8; 38];
    client.read_exact(&mut echoed).await.unwrap();
    assert_eq!(&echoed[..4], b"HTTP");
}

#[tokio::test]
async fn test_from_transport_keeps_pipelined_request_in_leftover() {
    let (mut client, server): (DuplexStream, DuplexStream) = duplex(2048);
    let first: Vec<u8> = b"GET /one HTTP/1.1\r\nHost: localhost\r\n\r\n".to_vec();
    let second: Vec<u8> = b"GET /two HTTP/1.1\r\nHost: localhost\r\n\r\n".to_vec();
    client.write_all(&[first, second].concat()).await.unwrap();
    let config: RequestConfig = RequestConfig::default();
    let mut stream: Stream = Stream::from_transport(TestTransport::new(server), config, false);
    let one: Request = stream.try_get_http_request().await.unwrap();
    assert_eq!(*one.get_path(), "/one");
    let two: Request = stream.try_get_http_request().await.unwrap();
    assert_eq!(*two.get_path(), "/two");
}