proxy-protocol-codec 0.1.2

PROXY Protocol codec implementation in Rust. See HAProxy for the protocol specification.
Documentation

proxy-protocol-codec

License Crates.io Version Docs.rs Version

PROXY Protocol codec implementation in Rust. See HAProxy for the protocol specification.

Usage

Add the following to your Cargo.toml as a dependency:

proxy-protocol-codec = "0.1"

Encoding

// PROXY Protocol v1 (text format), TCP4
let address_pair = proxy_protocol_codec::v1::AddressPair::Inet {
    src_ip: "127.0.0.1".parse().unwrap(),
    dst_ip: "127.0.0.2".parse().unwrap(),
    src_port: 8080,
    dst_port: 80,
};
let header = proxy_protocol_codec::v1::Header::new(address_pair);

assert_eq!(header.encode(), "PROXY TCP4 127.0.0.1 127.0.0.2 8080 80\r\n");

// PROXY Protocol v1 (text format), TCP6
let address_pair = proxy_protocol_codec::v1::AddressPair::Inet6 {
    src_ip: "::1".parse().unwrap(),
    dst_ip: "::2".parse().unwrap(),
    src_port: 8080,
    dst_port: 80,
};
let header = proxy_protocol_codec::v1::Header::new(address_pair);

assert_eq!(header.encode(), "PROXY TCP6 ::1 ::2 8080 80\r\n");

// PROXY Protocol v1 (text format), UNKNOWN
let address_pair = proxy_protocol_codec::v1::AddressPair::Unspecified;
let header = proxy_protocol_codec::v1::Header::new(address_pair);

assert_eq!(header.encode(), "PROXY UNKNOWN\r\n");
// PROXY Protocol v1 (binary format)
let header = proxy_protocol_codec::v2::Header::new_proxy(
    proxy_protocol_codec::v2::Protocol::Stream,
    proxy_protocol_codec::v2::AddressPair::Inet {
        src_ip: "127.0.0.1".parse().unwrap(),
        dst_ip: "127.0.0.2".parse().unwrap(),
        src_port: 8080,
        dst_port: 80,
    },
);

let encoded = header
    .encode()
    .write_ext_authority(b"example.com")? // Optional, write extensions.
    .finish()?;

Decoding

// PROXY Protocol v1 (text format)
let encoded = ...;

proxy_protocol_codec::v1::Header::decode(encoded))?
// PROXY Protocol v2 (binary format)
let tcp_stream = ...;

let mut buf = Vec::with_capacity(proxy_protocol_codec::v2::HEADER_SIZE);

unsafe {
    // Safe op filling with 0 is OK
    buf.set_len(proxy_protocol_codec::v2::HEADER_SIZE);
}

let peeked = tcp_stream.peek(&mut buf[..])?;

if peeked != 16 {
    // Not enough data to decode the header, the sender should not fragment the header.
    return Err(std::io::Error::new(
        std::io::ErrorKind::UnexpectedEof,
        "Not enough data to decode PROXY Protocol v2 header",
    ));
}

match proxy_protocol_codec::v2::Header::decode(&buf[..])? {
    proxy_protocol_codec::v2::Decoded::Partial(remaining) => {
        let total_length = proxy_protocol_codec::v2::HEADER_SIZE + remaining.get();

        unsafe {
            // Safe op filling with 0 is OK
            buf.set_len(total_length);
        }

        // Partial header, need more data.
        tcp_stream.read_exact(
            &mut buf[proxy_protocol_codec::v2::HEADER_SIZE..proxy_protocol_codec::v2::HEADER_SIZE + remaining.get()]
        )?;

        // Decode the full header with the remaining bytes.
        let proxy_protocol_codec::v2::Decoded::Some(header) = proxy_protocol_codec::v2::Header::decode(&buf[..])? else {
            panic!("must be Some here");
        };

        header
    }
    proxy_protocol_codec::v2::Decoded::Some(header) => {
        // Successfully decoded the header (without address pair or extensions).

        header
    }
    proxy_protocol_codec::v2::Decoded::None => {
        // Partial header, need more data.
        
        return ...;
    }
}