9.2 KiB
socktop_connector
A WebSocket connector library for communicating with socktop agents.
Overview
socktop_connector provides a high-level, type-safe interface for connecting to socktop agents over WebSocket connections. It handles connection management, TLS certificate pinning, compression, and protocol buffer decoding automatically.
Features
- WebSocket Communication: Support for both
ws://andwss://connections - TLS Security: Certificate pinning for secure connections with self-signed certificates
- Hostname Verification: Configurable hostname verification for TLS connections
- Type Safety: Strongly typed requests and responses
- Automatic Compression: Handles gzip compression/decompression transparently
- Protocol Buffer Support: Decodes binary process data automatically
- Error Handling: Comprehensive error handling with detailed error messages
Connection Types
Non-TLS Connections (ws://)
Use connect_to_socktop_agent() for unencrypted WebSocket connections.
TLS Connections (wss://)
Use connect_to_socktop_agent_with_tls() for encrypted connections with certificate pinning. You can control hostname verification with the verify_hostname parameter.
Quick Start
Add this to your Cargo.toml:
[dependencies]
socktop_connector = "0.1"
tokio = { version = "1", features = ["full"] }
Basic Usage
use socktop_connector::{connect_to_socktop_agent, AgentRequest, AgentResponse};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to a socktop agent (non-TLS connections are always unverified)
let mut connector = connect_to_socktop_agent("ws://localhost:3000/ws").await?;
// Request metrics
match connector.request(AgentRequest::Metrics).await? {
AgentResponse::Metrics(metrics) => {
println!("CPU: {}%, Memory: {}/{}MB",
metrics.cpu_total,
metrics.mem_used / 1024 / 1024,
metrics.mem_total / 1024 / 1024
);
}
_ => unreachable!(),
}
// Request process list
match connector.request(AgentRequest::Processes).await? {
AgentResponse::Processes(processes) => {
println!("Total processes: {}", processes.process_count);
for process in processes.top_processes.iter().take(5) {
println!(" {} (PID: {}) - CPU: {}%",
process.name, process.pid, process.cpu_usage);
}
}
_ => unreachable!(),
}
Ok(())
}
TLS with Certificate Pinning
use socktop_connector::{connect_to_socktop_agent_with_tls, AgentRequest};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect with TLS certificate pinning and hostname verification
let mut connector = connect_to_socktop_agent_with_tls(
"wss://remote-host:8443/ws",
"/path/to/cert.pem",
false // Enable hostname verification
).await?;
let response = connector.request(AgentRequest::Disks).await?;
println!("Got disk info: {:?}", response);
Ok(())
}
Advanced Configuration
use socktop_connector::{ConnectorConfig, SocktopConnector, AgentRequest};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create a custom configuration
let config = ConnectorConfig::new("wss://remote-host:8443/ws")
.with_tls_ca("/path/to/cert.pem")
.with_hostname_verification(false);
// Create and connect
let mut connector = SocktopConnector::new(config);
connector.connect().await?;
// Make requests
let response = connector.request(AgentRequest::Metrics).await?;
// Clean disconnect
connector.disconnect().await?;
Ok(())
}
Continuous Updates
The socktop agent provides real-time system metrics. Each request returns the current snapshot, but you can implement continuous monitoring by making requests in a loop:
use socktop_connector::{connect_to_socktop_agent, AgentRequest, AgentResponse};
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut connector = connect_to_socktop_agent("ws://localhost:3000/ws").await?;
// Monitor system metrics every 2 seconds
loop {
match connector.request(AgentRequest::Metrics).await {
Ok(AgentResponse::Metrics(metrics)) => {
// Calculate total network activity across all interfaces
let total_rx: u64 = metrics.networks.iter().map(|n| n.received).sum();
let total_tx: u64 = metrics.networks.iter().map(|n| n.transmitted).sum();
println!("CPU: {:.1}%, Memory: {:.1}%, Network: ↓{} ↑{}",
metrics.cpu_total,
(metrics.mem_used as f64 / metrics.mem_total as f64) * 100.0,
format_bytes(total_rx),
format_bytes(total_tx)
);
}
Err(e) => {
eprintln!("Error getting metrics: {}", e);
break;
}
_ => unreachable!(),
}
sleep(Duration::from_secs(2)).await;
}
Ok(())
}
fn format_bytes(bytes: u64) -> String {
const UNITS: &[&str] = &["B", "KB", "MB", "GB"];
let mut size = bytes as f64;
let mut unit_index = 0;
while size >= 1024.0 && unit_index < UNITS.len() - 1 {
size /= 1024.0;
unit_index += 1;
}
format!("{:.1}{}", size, UNITS[unit_index])
}
Understanding Data Freshness
The socktop agent implements intelligent caching to avoid overwhelming the system:
- Metrics: Cached for ~250ms by default (fast-changing data like CPU, memory)
- Processes: Cached for ~1500ms by default (moderately changing data)
- Disks: Cached for ~1000ms by default (slowly changing data)
This means:
- Multiple rapid requests for the same data type will return cached results
- Different data types have independent cache timers
- Fresh data is automatically retrieved when cache expires
use socktop_connector::{connect_to_socktop_agent, AgentRequest, AgentResponse};
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut connector = connect_to_socktop_agent("ws://localhost:3000/ws").await?;
// This demonstrates cache behavior
println!("Requesting metrics twice quickly...");
// First request - fresh data from system
let start = std::time::Instant::now();
connector.request(AgentRequest::Metrics).await?;
println!("First request took: {:?}", start.elapsed());
// Second request immediately - cached data
let start = std::time::Instant::now();
connector.request(AgentRequest::Metrics).await?;
println!("Second request took: {:?}", start.elapsed()); // Much faster!
// Wait for cache to expire, then request again
sleep(Duration::from_millis(300)).await;
let start = std::time::Instant::now();
connector.request(AgentRequest::Metrics).await?;
println!("Third request (after cache expiry): {:?}", start.elapsed());
Ok(())
}
The WebSocket connection remains open between requests, providing efficient real-time monitoring without connection overhead.
Request Types
The library supports three types of requests:
AgentRequest::Metrics- Get current system metrics (CPU, memory, network, etc.)AgentRequest::Disks- Get disk usage informationAgentRequest::Processes- Get running process information
Response Types
Responses are automatically parsed into strongly-typed structures:
AgentResponse::Metrics(Metrics)- System metrics with CPU, memory, network dataAgentResponse::Disks(Vec<DiskInfo>)- List of disk usage informationAgentResponse::Processes(ProcessesPayload)- Process list with CPU and memory usage
Configuration Options
The library provides flexible configuration through the ConnectorConfig builder:
with_tls_ca(path)- Enable TLS with certificate pinningwith_hostname_verification(bool)- Control hostname verification for TLS connectionstrue(recommended): Verify the server hostname matches the certificatefalse: Skip hostname verification (useful for localhost or IP-based connections)
Note: Hostname verification only applies to TLS connections (wss://). Non-TLS connections (ws://) don't use certificates, so hostname verification is not applicable.
Security Considerations
- Production TLS: Always enable hostname verification (
verify_hostname: true) for production - Development/Testing: You may disable hostname verification for localhost or IP addresses
- Certificate Pinning: Use
with_tls_ca()for self-signed certificates - Non-TLS: Use only for development or trusted networks
Environment Variables
Currently no environment variables are used. All configuration is done through the API.
Error Handling
The library uses anyhow::Error for error handling, providing detailed error messages for common failure scenarios:
- Connection failures
- TLS certificate validation errors
- Protocol errors
- Parsing errors
License
MIT License - see the LICENSE file for details.