REST APIRust
Rust
Sample code using Rust
Session-based HTTPS Request
This is an example of accessing a REST API endpoint using a session.
The application requires a custom TLS certificate chain PEM file and the
Cuica hostname as input arguments. Placeholders are indicated by <>
.
Example Cargo.toml*
[dependencies]
clap = { version = "4.2", features = ["derive"] }
rpassword = "5.1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
reqwest = { version = "0.12", features = ["json", "rustls-tls", "cookies"] }
tokio = { version = "1.29", features = ["rt-multi-thread", "macros"] }
use clap::Parser;
use rpassword::read_password;
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::{self, Write};
use std::path::PathBuf;
/// Command-line arguments
#[derive(Parser)]
struct Args {
/// The host of the Cuica device
#[arg(index = 1)]
cuica_host: String,
/// Path to the Root CA certificate file
#[arg(index = 2)]
root_ca_path: PathBuf,
}
/// Structure for authentication login data
#[derive(Debug, Serialize)]
pub struct AuthLogin {
pub username: String,
pub password: String,
}
/// Structure matching the expected JSON response schema
#[derive(Debug, Deserialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct SystemInfoResponse {
pub version: String,
pub build_version: String,
pub last_commit_time: String,
pub serial_number: String,
}
/// Prompt the user for a username
fn prompt_username() -> String {
print!("\u{25e6} Username: ");
io::stdout().flush().unwrap();
let mut input = String::new();
if let Err(e) = io::stdin().read_line(&mut input) {
eprintln!("Error reading username: {}", e);
std::process::exit(1);
}
input.trim().to_string()
}
/// Prompt the user for a password without echoing input
fn prompt_password() -> String {
print!("\u{25e6} Password: ");
io::stdout().flush().unwrap();
match read_password() {
Ok(pwd) => pwd.trim().to_string(),
Err(e) => {
eprintln!("Error reading password: {}", e);
std::process::exit(1);
}
}
}
#[tokio::main]
async fn main() {
let args = Args::parse();
let username = prompt_username();
let password = prompt_password();
// Ensure the specified Root CA file exists to validate
// TLS connections
if !args.root_ca_path.exists() {
eprintln!("Error: Root CA file was not found");
std::process::exit(1);
}
// Read and parse the Root CA certificate
let root_ca = match fs::read(&args.root_ca_path) {
Ok(ca) => ca,
Err(e) => {
eprintln!("Error reading Root CA file: {}", e);
std::process::exit(1);
}
};
let cert = match reqwest::Certificate::from_pem(&root_ca) {
Ok(cert) => cert,
Err(e) => {
eprintln!("Error parsing Root CA certificate: {}", e);
std::process::exit(1);
}
};
// Build the HTTP client with the custom Root CA certificate
let http_client = match reqwest::Client::builder()
.cookie_store(true)
.use_rustls_tls()
.add_root_certificate(cert)
.build()
{
Ok(client) => client,
Err(e) => {
eprintln!("Error building HTTP client: {}", e);
std::process::exit(1);
}
};
// Log into the Cuica device to get a valid session
let login_request = AuthLogin {
username: username.clone(),
password: password.clone(),
};
let login_response = match http_client
.post(format!("https://{}/login", args.cuica_host))
.json(&login_request)
.header(reqwest::header::CONTENT_TYPE, "application/json")
.send()
.await
{
Ok(response) => response,
Err(e) => {
eprintln!("Error sending login request: {}", e);
std::process::exit(1);
}
};
// Handle the login response
match login_response.status() {
reqwest::StatusCode::OK => {
println!("Logged in successfully.");
}
reqwest::StatusCode::UNAUTHORIZED => {
eprintln!("User login unsuccessful. \
Check username and password.");
std::process::exit(1);
}
reqwest::StatusCode::FORBIDDEN => {
eprintln!(
"Access forbidden: Max tokens reached, \
system setup required, \
or non-admin user."
);
std::process::exit(1);
}
_ => {
// Handle all other responses
let error_body = match login_response.text().await {
Ok(body) => body,
Err(e) => {
eprintln!("Error reading login error response: {}", e);
std::process::exit(1);
}
};
eprintln!("Login failed: {}", error_body);
std::process::exit(1);
}
}
// Perform a GET request to retrieve system information
let info_response = match http_client
.get(format!("https://{}/v1/system/info", args.cuica_host))
.header(reqwest::header::CONTENT_TYPE, "application/json")
.send()
.await
{
Ok(response) => response,
Err(e) => {
eprintln!("Error sending system info request: {}", e);
std::process::exit(1);
}
};
// Deserialize the response into the SystemInfoResponse struct
let system_info =
match info_response.json::<SystemInfoResponse>().await {
Ok(info) => info,
Err(e) => {
eprintln!("Error parsing system info response: {}", e);
std::process::exit(1);
}
};
// Print the retrieved system information
println!("Version: {}", system_info.version);
println!("Build Version: {}", system_info.build_version);
println!("Last Commit Time: {}", system_info.last_commit_time);
println!("Serial Number: {}", system_info.serial_number);
}
$ ./sample_code cuica-host ./my-certs.pem
◦ Username: <somebody>
◦ Password:
Logged in successfully.
Version: 1.2.3
Build Version: 8311830
Last Commit Time: 2025-10-26T00:37:38-04:00
Serial Number: CUIPRF-XXX
Next