Properly handle Result types
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -26,6 +26,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.98"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
@@ -656,6 +662,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
name = "xmpp-blackbox-exporter"
|
name = "xmpp-blackbox-exporter"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
|
@@ -4,6 +4,7 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.98"
|
||||||
axum = "0.8.4"
|
axum = "0.8.4"
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
78
src/main.rs
78
src/main.rs
@@ -1,5 +1,16 @@
|
|||||||
|
use anyhow::{
|
||||||
|
anyhow,
|
||||||
|
Context,
|
||||||
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::Query,
|
extract::Query,
|
||||||
|
http::{
|
||||||
|
StatusCode,
|
||||||
|
},
|
||||||
|
response::{
|
||||||
|
IntoResponse,
|
||||||
|
Response,
|
||||||
|
},
|
||||||
routing::get,
|
routing::get,
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
@@ -40,11 +51,42 @@ impl ProbeFacts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct AppError(anyhow::Error);
|
||||||
|
|
||||||
|
impl std::fmt::Display for AppError {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
std::fmt::Display::fmt(&self.0, formatter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for AppError {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
std::fmt::Debug::fmt(&self.0, formatter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoResponse for AppError {
|
||||||
|
fn into_response(self) -> Response {
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("Internal server error:\n{:?}", self),
|
||||||
|
)
|
||||||
|
.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> From<E> for AppError where E: Into<anyhow::Error> {
|
||||||
|
fn from(err: E) -> Self {
|
||||||
|
Self(err.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let mut args = std::env::args();
|
let mut args = std::env::args();
|
||||||
let _name = args.next().unwrap();
|
let _name = args.next().ok_or(anyhow!(""))?;
|
||||||
|
|
||||||
let mut listen = String::from("[::]:3000");
|
let mut listen = String::from("[::]:3000");
|
||||||
|
|
||||||
@@ -57,7 +99,7 @@ async fn main() {
|
|||||||
|
|
||||||
match arg.as_str() {
|
match arg.as_str() {
|
||||||
"--listen" => {
|
"--listen" => {
|
||||||
listen = args.next().unwrap();
|
listen = args.next().ok_or(anyhow!(""))?;
|
||||||
}
|
}
|
||||||
unknown => {
|
unknown => {
|
||||||
println!("unknown option: {}", unknown);
|
println!("unknown option: {}", unknown);
|
||||||
@@ -70,40 +112,45 @@ async fn main() {
|
|||||||
.route("/", get(route_index))
|
.route("/", get(route_index))
|
||||||
.route("/probe-client-to-server", get(route_probe_client_to_server));
|
.route("/probe-client-to-server", get(route_probe_client_to_server));
|
||||||
|
|
||||||
let listener = tokio::net::TcpListener::bind(listen).await.unwrap();
|
let listener = tokio::net::TcpListener::bind(listen).await?;
|
||||||
println!("Server listening on: http://{}", listener.local_addr().unwrap());
|
println!("Server listening on: http://{}", listener.local_addr()?);
|
||||||
println!("Probe with:");
|
println!("Probe with:");
|
||||||
println!(" http://{}/probe-client-to-server?domain=fem-net.de&hostname=xmpp-2.fem-net.de&port=5222", listener.local_addr().unwrap());
|
println!(" http://{}/probe-client-to-server?domain=fem-net.de&hostname=xmpp-2.fem-net.de&port=5222", listener.local_addr()?);
|
||||||
|
|
||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn route_index() -> String {
|
async fn route_index() -> String {
|
||||||
return String::from("Prometheus exporter for checking XMPP server availability");
|
return String::from("Prometheus exporter for checking XMPP server availability");
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn probe_client_to_server(domain: &str, hostname: &str, port: u16) -> Result<ProbeFacts, String> {
|
async fn probe_client_to_server(domain: &str, hostname: &str, port: u16) -> anyhow::Result<ProbeFacts> {
|
||||||
let mut probe_facts = ProbeFacts::new();
|
let mut probe_facts = ProbeFacts::new();
|
||||||
|
|
||||||
let mut stream = TcpStream::connect((hostname, port)).await.unwrap();
|
let mut stream = TcpStream::connect((hostname, port)).await
|
||||||
|
.context("Couldn't connect to XMPP server")?;
|
||||||
|
|
||||||
let connect_string = format!("<stream:stream to='{}' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' xml:lang='en' version='1.0'></stream:stream>", domain);
|
let connect_string = format!("<stream:stream to='{}' xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' xml:lang='en' version='1.0'></stream:stream>", domain);
|
||||||
|
|
||||||
stream.write_all(connect_string.as_bytes()).await.unwrap();
|
stream.write_all(connect_string.as_bytes()).await
|
||||||
|
.context("Failed to write XMPP connection test payload")?;
|
||||||
|
|
||||||
let mut stream = BufReader::new(stream);
|
let mut stream = BufReader::new(stream);
|
||||||
|
|
||||||
let mut response = String::new();
|
let mut response = String::new();
|
||||||
stream.read_line(&mut response).await.unwrap();
|
stream.read_line(&mut response).await
|
||||||
|
.context("Failed to read XMPP connection response")?;
|
||||||
|
|
||||||
println!("{}", response);
|
println!("{}", response);
|
||||||
|
|
||||||
let re_match_xmpp_stream = Regex::new(r"http://etherx\.jabber\.org/streams").unwrap();
|
let re_match_xmpp_stream = Regex::new(r"http://etherx\.jabber\.org/streams")?;
|
||||||
if re_match_xmpp_stream.is_match(&response) {
|
if re_match_xmpp_stream.is_match(&response) {
|
||||||
probe_facts.is_xmpp_stream = true;
|
probe_facts.is_xmpp_stream = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let re_match_xmpp_client = Regex::new(r"jabber:client").unwrap();
|
let re_match_xmpp_client = Regex::new(r"jabber:client")?;
|
||||||
if re_match_xmpp_client.is_match(&response) {
|
if re_match_xmpp_client.is_match(&response) {
|
||||||
probe_facts.is_xmpp_client = true;
|
probe_facts.is_xmpp_client = true;
|
||||||
}
|
}
|
||||||
@@ -113,9 +160,10 @@ async fn probe_client_to_server(domain: &str, hostname: &str, port: u16) -> Resu
|
|||||||
|
|
||||||
async fn route_probe_client_to_server(
|
async fn route_probe_client_to_server(
|
||||||
Query(query): Query<ProbeClientToServerQuery>,
|
Query(query): Query<ProbeClientToServerQuery>,
|
||||||
) -> Result<String, String> {
|
) -> Result<impl IntoResponse, AppError> {
|
||||||
|
|
||||||
let probe_facts = probe_client_to_server(&query.domain, &query.hostname, query.port).await.unwrap();
|
let probe_facts = probe_client_to_server(&query.domain, &query.hostname, query.port).await
|
||||||
|
.context("Failed probing XMPP connection")?;
|
||||||
|
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user