kingfisher/src/validation/mongodb.rs

72 lines
2.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/validation/mongodb.rs
use std::time::Duration;
use anyhow::Result;
use bson::doc;
use mongodb::{options::ClientOptions, Client};
pub fn looks_like_mongodb_uri(uri: &str) -> bool {
// quick scheme check first
if !(uri.starts_with("mongodb://") || uri.starts_with("mongodb+srv://")) {
return false;
}
// pure string-level parse no network, even for +srv
mongodb::options::ConnectionString::parse(uri).is_ok()
}
const FAST_CONNECT_MS: u64 = 700; // direct single-host URIs
const FAST_SELECT_MS: u64 = 300;
const SRV_CONNECT_MS: u64 = 15_000; // gives Atlas a fighting chance
const SRV_SELECT_MS: u64 = 15_000;
/// Validates a MongoDB URI in ≤ 2 s. Returns `(bool, String)` where the
/// boolean indicates success and the string provides a status message.
pub async fn validate_mongodb(uri: &str) -> Result<(bool, String)> {
// ---- quick reject without touching the network
if !looks_like_mongodb_uri(uri) {
return Ok((false, "Invalid MongoDB URI".to_string()));
}
let is_srv = uri.starts_with("mongodb+srv://");
if is_srv {
// Skip SRV URIs to avoid slow DNS lookups and topology discovery.
return Ok((
false,
"Validation skipped for mongodb+srv:// URI (performance reasons)".to_string(),
));
}
// ---- build client opts
let mut opts = ClientOptions::parse(uri).await?;
if !is_srv {
// one socket, skip cluster discovery for plain 'mongodb://'
opts.direct_connection = Some(true);
opts.connect_timeout = Some(Duration::from_millis(FAST_CONNECT_MS));
opts.server_selection_timeout = Some(Duration::from_millis(FAST_SELECT_MS));
} else {
// SRV needs DNS and replica-set discovery; give it a couple seconds
opts.connect_timeout = Some(Duration::from_millis(SRV_CONNECT_MS));
opts.server_selection_timeout = Some(Duration::from_millis(SRV_SELECT_MS));
// leave direct_connection = None (driver decides)
}
opts.max_pool_size = Some(1);
opts.min_pool_size = Some(0);
// ---- dial and ping
let client = Client::with_options(opts)?;
let ok = client.database("admin").run_command(doc! { "ping": 1 }).await.is_ok();
let msg = if ok {
"MongoDB connection is valid.".to_string()
} else {
"MongoDB connection failed.".to_string()
};
Ok((ok, msg))
}
// pub fn generate_mongodb_cache_key(mongodb_uri: &str) -> String {
// use sha1::{Digest, Sha1};
// let mut hasher = Sha1::new();
// hasher.update(mongodb_uri.as_bytes());
// format!("MongoDB:{:x}", hasher.finalize())
// }