This crate provides an HTTP client for interacting with the Rspamd service in Rust. It supports both synchronous and asynchronous operations using the attohttpc and reqwest libraries, respectively.
- Sync/Async: Choose between synchronous (
attohttpc) or asynchronous (reqwest) client - Mutually Exclusive: Async and sync features are mutually exclusive by design
- Encryption: Native HTTPCrypt encryption support
- Compression: ZSTD compression for requests and responses
- Local File Scanning: Scan files on the same host without transferring body (
Fileheader) - Body Rewriting: Receive rewritten message bodies (
body_blockflag) - Envelope Data: Configure sender, recipients, IP, HELO, hostname, and custom headers
- Proxy Support: HTTP proxy configuration
- TLS: Custom TLS settings
Add the following to your Cargo.toml:
[dependencies]
rspamd-client = { version = "0.4", features = ["async"] }Enable either the sync or async feature (but not both):
async(default): Usesreqwestandtokiosync: Usesattohttpc
use rspamd_client::{Config, EnvelopeData, scan_async};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::builder()
.base_url("http://localhost:11333".to_string())
.build();
let envelope = EnvelopeData::builder()
.from("[email protected]".to_string())
.rcpt(vec!["[email protected]".to_string()])
.ip("127.0.0.1".to_string())
.build();
let email = "From: [email protected]\nTo: [email protected]\nSubject: Test\n\nThis is a test email.";
let response = scan_async(&config, email, envelope).await?;
println!("Score: {}, Action: {}", response.score, response.action);
Ok(())
}use rspamd_client::{Config, EnvelopeData, scan_sync};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::builder()
.base_url("http://localhost:11333".to_string())
.build();
let envelope = EnvelopeData::builder()
.from("[email protected]".to_string())
.build();
let email = "From: [email protected]\nTo: [email protected]\nSubject: Test\n\nThis is a test email.";
let response = scan_sync(&config, email, envelope)?;
println!("Score: {}, Action: {}", response.score, response.action);
Ok(())
}When the client and Rspamd server are on the same host, you can scan files without transferring the body by using the file_path option. This is a significant performance optimization:
use rspamd_client::{Config, EnvelopeData, scan_async};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::builder()
.base_url("http://localhost:11333".to_string())
.build();
let envelope = EnvelopeData::builder()
.from("[email protected]".to_string())
.file_path("/var/mail/message.eml".to_string()) // Rspamd reads file directly
.build();
// Empty body - file is read by Rspamd from disk
let response = scan_async(&config, "", envelope).await?;
println!("Scanned file with score: {}", response.score);
Ok(())
}Request the rewritten message body from Rspamd when modifications are applied (e.g., subject rewriting, header changes):
use rspamd_client::{Config, EnvelopeData, scan_async};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::builder()
.base_url("http://localhost:11333".to_string())
.build();
let envelope = EnvelopeData::builder()
.from("[email protected]".to_string())
.body_block(true) // Request rewritten body if modified
.build();
let email = "From: [email protected]\nTo: [email protected]\nSubject: Test\n\nBody content.";
let response = scan_async(&config, email, envelope).await?;
if let Some(rewritten_body) = response.rewritten_body {
println!("Message was rewritten, new body size: {} bytes", rewritten_body.len());
// Use rewritten_body instead of original
}
Ok(())
}Use native Rspamd HTTPCrypt encryption:
let config = Config::builder()
.base_url("http://localhost:11333".to_string())
.encryption_key("k4nz984k36xmcynm1hr9kdbn6jhcxf4ggbrb1quay7f88rpm9kay".to_string())
.build();The encryption key must be in Rspamd base32 format and match the server's public key.
ZSTD compression is enabled by default. To disable:
let config = Config::builder()
.base_url("http://localhost:11333".to_string())
.zstd(false)
.build();use rspamd_client::{Config, ProxyConfig};
let proxy = ProxyConfig {
proxy_url: "http://proxy.example.com:8080".to_string(),
username: Some("user".to_string()),
password: Some("pass".to_string()),
};
let config = Config::builder()
.base_url("http://localhost:11333".to_string())
.proxy_config(proxy)
.build();base_url: Rspamd server URL (required)password: Optional authentication passwordtimeout: Request timeout in seconds (default: 30.0)retries: Number of retry attempts (default: 1)zstd: Enable ZSTD compression (default: true)encryption_key: HTTPCrypt encryption key (optional)proxy_config: HTTP proxy settings (optional)tls_settings: Custom TLS configuration (optional)
from: Sender email addressrcpt: List of recipient email addressesip: Sender IP addressuser: Authenticated usernamehelo: SMTP HELO stringhostname: Resolved hostnamefile_path: Local file path for scanning (instead of body transfer)body_block: Request rewritten body in responseadditional_headers: Custom HTTP headers
pub struct RspamdScanReply {
pub score: f64, // Spam score
pub action: String, // Action to take (e.g., "reject", "add header")
pub symbols: HashMap<String, Symbol>, // Detected symbols
pub messages: HashMap<String, String>, // Messages from Rspamd
pub urls: Vec<String>, // Extracted URLs
pub emails: Vec<String>, // Extracted emails
pub message_id: String, // Message ID
pub time_real: f64, // Scan time
pub milter: Option<Milter>, // Milter actions (headers to add/remove)
pub rewritten_body: Option<Vec<u8>>, // Rewritten message body (if body_block enabled)
// ... other fields
}This project is licensed under the Apache 2.0 License.
Contributions are welcome! Please open a pull request or issue on GitHub.