ic-testkit 0.42.0

PocketIC-oriented test utilities for IC canister tests
Documentation
use super::*;

#[test]
fn request_framing_preserves_body_bounds_and_rejects_ambiguous_lengths() {
    assert_eq!(request_length(b"POST / HTTP/1.1\r\n", 32, 4).unwrap(), None);
    let request = b"POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\ntest";
    assert_eq!(request_length(request, 64, 4).unwrap(), Some(request.len()));
    assert_eq!(
        request_length(b"DELETE / HTTP/1.1\r\n\r\n", 64, 0).unwrap(),
        Some(21)
    );
    for request in [
        &b"POST / HTTP/1.1\r\nContent-Length: 5\r\n\r\n"[..],
        &b"POST / HTTP/1.1\r\nContent-Length: 1\r\ncontent-length: 1\r\n\r\n"[..],
        &b"POST / HTTP/1.1\r\nContent-Length: -1\r\n\r\n"[..],
        &b"POST / HTTP/1.1\r\nTransfer-Encoding: chunked\r\n\r\n"[..],
    ] {
        assert!(request_length(request, 128, 4).is_err());
    }
    assert!(request_length(b"POST / HTTP/1.1\r\n\r\n", 4, 4).is_err());
}

#[test]
fn line_phase_returns_before_headers_and_completion_preserves_original_bytes() {
    use std::{io::Write as _, net::TcpListener, sync::mpsc};
    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let (proceed, waiting) = mpsc::channel();
    let mut writer_socket = TcpStream::connect(listener.local_addr().unwrap()).unwrap();
    listener.set_nonblocking(true).unwrap();
    let mut socket = listener.accept().unwrap().0;
    let writer = std::thread::spawn(move || {
        let socket = &mut writer_socket;
        socket.write_all(b"POST /selected HTTP/1.1\r\n").unwrap();
        waiting.recv_timeout(Duration::from_secs(5)).unwrap();
        socket.write_all(b"Content-Length: 4\r\n\r\ntest").unwrap();
    });
    let deadline = Instant::now() + Duration::from_secs(5);
    let line = read_http_request_line(&mut socket, deadline, 128).unwrap();
    assert_eq!(line, b"POST /selected HTTP/1.1\r\n");
    proceed.send(()).unwrap();
    let request = read_http_request_with_line(&mut socket, &line, deadline, 128, 4).unwrap();
    assert_eq!(
        request,
        b"POST /selected HTTP/1.1\r\nContent-Length: 4\r\n\r\ntest"
    );
    writer.join().unwrap();
}

#[test]
fn prefetched_line_and_elapsed_deadline_refuse_before_waiting_for_headers() {
    use std::net::TcpListener;
    let listener = TcpListener::bind("127.0.0.1:0").unwrap();
    let _writer = TcpStream::connect(listener.local_addr().unwrap()).unwrap();
    let mut socket = listener.accept().unwrap().0;
    let deadline = Instant::now() + Duration::from_secs(1);
    for line in [
        b"\r\n".as_slice(),
        b"POST / HTTP/1.1\n",
        b"POST / HTTP/1.1\r\nInjected: 1\r\n",
        &[255, 13, 10],
    ] {
        assert_eq!(
            read_http_request_with_line(&mut socket, line, deadline, 128, 4)
                .unwrap_err()
                .kind(),
            io::ErrorKind::InvalidData
        );
    }
    let elapsed = Instant::now();
    assert_eq!(
        read_http_request_line(&mut socket, elapsed, 128)
            .unwrap_err()
            .kind(),
        io::ErrorKind::TimedOut
    );
    assert_eq!(
        read_http_request_with_line(&mut socket, b"POST / HTTP/1.1\r\n", elapsed, 128, 4)
            .unwrap_err()
            .kind(),
        io::ErrorKind::TimedOut
    );
}

#[test]
fn actual_capture_refuses_header_body_overflow_and_truncated_payloads() {
    use std::{io::Write as _, net::TcpListener};
    for (request, headers, body, expected) in [
        (
            b"POST / HTTP/1.1\r\n\r\n".as_slice(),
            4,
            4,
            io::ErrorKind::InvalidData,
        ),
        (
            b"POST / HTTP/1.1\r\nContent-Length: 5\r\n\r\n12345",
            128,
            4,
            io::ErrorKind::InvalidData,
        ),
        (
            b"POST / HTTP/1.1\r\nContent-Length: 4\r\n\r\nab",
            128,
            4,
            io::ErrorKind::UnexpectedEof,
        ),
    ] {
        let listener = TcpListener::bind("127.0.0.1:0").unwrap();
        let mut writer = TcpStream::connect(listener.local_addr().unwrap()).unwrap();
        writer.write_all(request).unwrap();
        drop(writer);
        let mut socket = listener.accept().unwrap().0;
        assert_eq!(
            read_http_request(&mut socket, Duration::from_secs(1), headers, body)
                .unwrap_err()
                .kind(),
            expected
        );
    }
}