http-proxy-client-async 0.3.1

Async I/O HTTP 1.1 CONNECT proxy client protocol implementation
Documentation
#![warn(missing_debug_implementations, rust_2018_idioms)]

use futures::{executor, io::Cursor, AsyncReadExt};
use http_proxy_client_async::*;
use merge_io::MergeIO;

#[test]
fn handshake_test() -> std::io::Result<()> {
    executor::block_on(async {
        let expected_req = "CONNECT 127.0.0.1:8080 HTTP/1.1\r\n\
                            Host: 127.0.0.1:8080\r\n\
                            proxy-authorization: Basic aGVsbG86d29ybGQ=\r\n\
                            \r\n";
        let sample_res = "HTTP/1.1 200 OK\r\n\
                          X-Custom: Sample Value\r\n\
                          \r\n\
                          this is already the proxied content";

        let reader = Cursor::new(sample_res);
        let writer = Cursor::new(vec![0u8; 1024]);

        let socket = MergeIO::new(reader, writer);

        let mut request_headers = HeaderMap::new();
        request_headers.insert(
            "Proxy-Authorization",
            HeaderValue::from_static("Basic aGVsbG86d29ybGQ="),
        );

        let mut read_buf = [0u8; 1024];
        let Outcome {
            stream: mut tunnel_socket,
            response_parts:
                ResponseParts {
                    status_code: code,
                    headers: response_headers,
                    ..
                },
        } = handshake_and_wrap(socket, "127.0.0.1", 8080, &request_headers, &mut read_buf).await?;

        // Verify the response was good.
        assert_eq!(code, 200);
        assert_eq!(response_headers.len(), 1);
        assert_eq!(response_headers.get("x-custom").unwrap(), &"Sample Value");

        // Read all data from the socket.
        let mut data_at_tunnel = vec![];
        tunnel_socket.read_to_end(&mut data_at_tunnel).await?;

        // Validate that the data that arrived as part of the handshake is not
        // lost or corrupted.
        assert_eq!(
            data_at_tunnel,
            "this is already the proxied content".as_bytes()
        );

        // Ensure that at this point we don't have leftover data.
        let pending_prepend_data = tunnel_socket.pending_prepend_data();
        assert!(
            pending_prepend_data.is_empty(),
            "we should've read everything that's left after the handshake"
        );

        // Deconstruct the tunnel into MergedIO socket and after-handshake-data
        // buffer.
        let (unwrapped_socket, _cursor_after_handshake) = tunnel_socket.into_inner();

        // Deconstruct the unwrapped socket into cursor pairs.
        let (reader, writer) = unwrapped_socket.into_inner();

        // Esnure reader (the ones client read from) is at the end.
        assert_eq!(
            reader.position(),
            reader.get_ref().len() as u64,
            "readed was not read till the end"
        );

        // Ensure writer (the one client writes to) has the expected sample
        // data.
        assert_eq!(
            &writer.get_ref()[..writer.position() as usize],
            expected_req.as_bytes(),
            "writer didn't have the expected content"
        );

        Ok(())
    })
}