pub struct ProxyProtocolClient { /* private fields */ }Expand description
Client for connecting to and communicating through a ap-proxy server.
This is the main client API for connecting to a proxy server, authenticating, discovering peers via rendezvous codes, and sending messages.
§Lifecycle
- Create client with
new() - Connect and authenticate with
connect() - Perform operations (send messages, request rendezvous codes, etc.)
- Disconnect with
disconnect()
§Examples
Basic usage:
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient, IncomingMessage};
// Create and connect
let config = ProxyClientConfig {
proxy_url: "ws://localhost:8080".to_string(),
identity_keypair: None,
};
let mut client = ProxyProtocolClient::new(config);
let mut incoming = client.connect().await?;
// Handle messages
tokio::spawn(async move {
while let Some(msg) = incoming.recv().await {
match msg {
IncomingMessage::Send { source, payload, .. } => {
println!("Got message from {:?}", source);
}
_ => {}
}
}
});
// Send a message
// client.send_to(target_fingerprint, b"Hello".to_vec()).await?;Implementations§
Source§impl ProxyProtocolClient
impl ProxyProtocolClient
Sourcepub fn new(config: ProxyClientConfig) -> Self
pub fn new(config: ProxyClientConfig) -> Self
Create a new proxy client with the given configuration.
This does not establish a connection - call connect()
to connect and authenticate.
If config.identity_keypair is None, a new random identity will be generated.
Otherwise, the provided identity will be used for authentication.
§Examples
Create client with new identity:
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient};
let config = ProxyClientConfig {
proxy_url: "ws://localhost:8080".to_string(),
identity_keypair: None, // Will generate new identity
};
let client = ProxyProtocolClient::new(config);
println!("Client fingerprint: {:?}", client.fingerprint());Create client with existing identity:
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient, IdentityKeyPair};
let keypair = IdentityKeyPair::generate();
let config = ProxyClientConfig {
proxy_url: "ws://localhost:8080".to_string(),
identity_keypair: Some(keypair),
};
let client = ProxyProtocolClient::new(config);Sourcepub async fn connect(
&mut self,
) -> Result<UnboundedReceiver<IncomingMessage>, ProxyError>
pub async fn connect( &mut self, ) -> Result<UnboundedReceiver<IncomingMessage>, ProxyError>
Connect to the proxy server and perform authentication.
Establishes a WebSocket connection, completes the challenge-response authentication, and returns a channel for receiving incoming messages.
§Authentication Flow
- Connect to WebSocket at the configured URL
- Receive authentication challenge from server
- Sign challenge with client’s private key
- Send signed response to server
- Server verifies signature and accepts connection
§Timeout
Authentication must complete within 5 seconds or this method returns
ProxyError::AuthenticationTimeout.
§Errors
ProxyError::AlreadyConnectedif already connectedProxyError::WebSocketif connection failsProxyError::AuthenticationFailedif signature verification failsProxyError::AuthenticationTimeoutif authentication takes too long
§Examples
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient, IncomingMessage};
let config = ProxyClientConfig {
proxy_url: "ws://localhost:8080".to_string(),
identity_keypair: None,
};
let mut client = ProxyProtocolClient::new(config);
// Connect and get incoming message channel
let mut incoming = client.connect().await?;
// Handle messages
while let Some(msg) = incoming.recv().await {
println!("Received: {:?}", msg);
}Sourcepub async fn send_to(
&self,
destination: IdentityFingerprint,
payload: Vec<u8>,
) -> Result<(), ProxyError>
pub async fn send_to( &self, destination: IdentityFingerprint, payload: Vec<u8>, ) -> Result<(), ProxyError>
Send a message to another authenticated client.
The message is routed through the proxy server to the destination client. The proxy validates the source identity but cannot inspect the payload.
§Authentication Required
This method requires an active authenticated connection. Call
connect() first.
§Payload Encryption
The proxy does not encrypt message payloads. Clients should implement end-to-end encryption (e.g., using the Noise protocol) before calling this method.
§Errors
ProxyError::NotConnectedif not connected or not authenticatedProxyError::DestinationNotFoundif the destination client is not connectedProxyError::Serializationif message encoding fails
§Examples
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient};
let mut client = ProxyProtocolClient::new(config);
client.connect().await?;
// Get destination fingerprint from rendezvous lookup
// let destination = ...; // from IncomingMessage::IdentityInfo
// Send encrypted message
// let payload = encrypt_message(b"Hello!")?;
// client.send_to(destination, payload).await?;Sourcepub async fn request_rendezvous(&self) -> Result<(), ProxyError>
pub async fn request_rendezvous(&self) -> Result<(), ProxyError>
Request a rendezvous code from the server.
The server will generate a temporary code (format: “ABC-DEF-GHI”) that maps to your
identity. The code will be delivered via IncomingMessage::RendevouzInfo on the
channel returned by connect().
§Rendezvous Code Properties
- Format: 9 alphanumeric characters (e.g., “ABC-DEF-GHI”)
- Lifetime: 5 minutes
- Single-use: deleted after lookup
- Enables peer discovery without sharing long-lived identifiers
§Usage Pattern
- Call this method to request a code
- Receive the code via
IncomingMessage::RendevouzInfo - Share the code with a peer (e.g., display as QR code)
- Peer uses
request_identity()to look up your identity
§Authentication Required
This method requires an active authenticated connection.
§Errors
ProxyError::NotConnectedif not connected or not authenticated
§Examples
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient, IncomingMessage};
let mut client = ProxyProtocolClient::new(config);
let mut incoming = client.connect().await?;
// Request a code
client.request_rendezvous().await?;
// Wait for response
if let Some(IncomingMessage::RendevouzInfo(code)) = incoming.recv().await {
println!("Share this code: {}", code.as_str());
// Display as QR code, send via messaging, etc.
}Sourcepub async fn request_identity(
&self,
rendezvous_code: RendevouzCode,
) -> Result<(), ProxyError>
pub async fn request_identity( &self, rendezvous_code: RendevouzCode, ) -> Result<(), ProxyError>
Look up a peer’s identity using a rendezvous code.
Queries the server for the identity associated with a rendezvous code.
If the code is valid and hasn’t expired, the server responds with
IncomingMessage::IdentityInfo containing the peer’s identity and fingerprint.
§Code Consumption
Rendezvous codes are single-use. After successful lookup, the server deletes the code and it cannot be used again.
§Authentication Required
This method requires an active authenticated connection.
§Errors
ProxyError::NotConnectedif not connected or not authenticated
The server may not respond if the code is invalid, expired, or already used. Implement a timeout when waiting for the response.
§Examples
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient, IncomingMessage, RendevouzCode};
let mut client = ProxyProtocolClient::new(config);
let mut incoming = client.connect().await?;
// Get code from user (e.g., QR scan, text input)
let code = RendevouzCode::from_string("ABC-DEF-GHI".to_string());
// Look up the identity
client.request_identity(code).await?;
// Wait for response with timeout
match tokio::time::timeout(
tokio::time::Duration::from_secs(5),
incoming.recv()
).await {
Ok(Some(IncomingMessage::IdentityInfo { fingerprint, identity })) => {
println!("Found peer: {:?}", fingerprint);
// Now you can send messages to this peer
// client.send_to(fingerprint, payload).await?;
}
_ => {
println!("Code not found or expired");
}
}Sourcepub fn fingerprint(&self) -> IdentityFingerprint
pub fn fingerprint(&self) -> IdentityFingerprint
Get this client’s identity fingerprint.
Returns the SHA256 fingerprint of the client’s public key. This is the identifier that other clients use to send messages to this client.
The fingerprint is available immediately after creating the client, before connecting.
§Examples
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient};
let config = ProxyClientConfig {
proxy_url: "ws://localhost:8080".to_string(),
identity_keypair: None,
};
let client = ProxyProtocolClient::new(config);
println!("My fingerprint: {:?}", client.fingerprint());Sourcepub async fn is_authenticated(&self) -> bool
pub async fn is_authenticated(&self) -> bool
Check if the client is currently authenticated.
Returns true if the client has completed authentication and can send/receive
messages. Returns false if disconnected or still connecting.
§Examples
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient};
let mut client = ProxyProtocolClient::new(config);
assert!(!client.is_authenticated().await);
client.connect().await?;
assert!(client.is_authenticated().await);
client.disconnect().await?;
assert!(!client.is_authenticated().await);Sourcepub async fn disconnect(&mut self) -> Result<(), ProxyError>
pub async fn disconnect(&mut self) -> Result<(), ProxyError>
Disconnect from the proxy server and clean up resources.
Aborts background tasks, closes the WebSocket connection, and resets state.
After disconnecting, you can call connect()
again to reconnect.
This method is automatically called when the client is dropped, but calling it explicitly allows you to handle errors.
§Examples
use ap_proxy_client::{ProxyClientConfig, ProxyProtocolClient};
let mut client = ProxyProtocolClient::new(config);
client.connect().await?;
// Do work...
// Clean disconnect
client.disconnect().await?;