QuickDID is a high-performance AT Protocol identity resolution service written in Rust. It provides handle-to-DID resolution with Redis-backed caching and queue processing.
at main 4.0 kB view raw
1//! Base handle resolver implementation. 2//! 3//! This module provides the fundamental handle resolution implementation that 4//! performs actual DNS and HTTP lookups to resolve AT Protocol handles to DIDs. 5 6use super::errors::HandleResolverError; 7use super::traits::HandleResolver; 8use crate::metrics::SharedMetricsPublisher; 9use async_trait::async_trait; 10use atproto_identity::resolve::{DnsResolver, resolve_subject}; 11use reqwest::Client; 12use std::sync::Arc; 13use std::time::{SystemTime, UNIX_EPOCH}; 14 15/// Base handle resolver that performs actual resolution via DNS and HTTP. 16/// 17/// This resolver implements the core AT Protocol handle resolution logic: 18/// 1. DNS TXT record lookup for `_atproto.{handle}` 19/// 2. HTTP well-known endpoint query at `https://{handle}/.well-known/atproto-did` 20/// 3. DID document retrieval from PLC directory or web DIDs 21/// 22/// # Example 23/// 24/// ```no_run 25/// use std::sync::Arc; 26/// use reqwest::Client; 27/// use atproto_identity::resolve::HickoryDnsResolver; 28/// use quickdid::handle_resolver::{create_base_resolver, HandleResolver}; 29/// use quickdid::metrics::NoOpMetricsPublisher; 30/// 31/// # async fn example() { 32/// let dns_resolver = Arc::new(HickoryDnsResolver::create_resolver(&[])); 33/// let http_client = Client::new(); 34/// let metrics = Arc::new(NoOpMetricsPublisher); 35/// 36/// let resolver = create_base_resolver( 37/// dns_resolver, 38/// http_client, 39/// metrics, 40/// ); 41/// 42/// let (did, timestamp) = resolver.resolve("alice.bsky.social").await.unwrap(); 43/// println!("Resolved {} at {}", did, timestamp); 44/// # } 45/// ``` 46pub(super) struct BaseHandleResolver { 47 /// DNS resolver for handle-to-DID resolution via TXT records. 48 dns_resolver: Arc<dyn DnsResolver>, 49 50 /// HTTP client for DID document retrieval and well-known endpoint queries. 51 http_client: Client, 52 53 /// Metrics publisher for telemetry. 54 metrics: SharedMetricsPublisher, 55} 56 57#[async_trait] 58impl HandleResolver for BaseHandleResolver { 59 async fn resolve(&self, s: &str) -> Result<(String, u64), HandleResolverError> { 60 let start_time = std::time::Instant::now(); 61 62 // Perform DNS/HTTP resolution 63 let result = resolve_subject(&self.http_client, &*self.dns_resolver, s) 64 .await 65 .map_err(|e| HandleResolverError::ResolutionFailed(e.to_string())); 66 67 let duration_ms = start_time.elapsed().as_millis() as u64; 68 69 // Publish metrics 70 71 match result { 72 Ok(did) => { 73 self.metrics 74 .time_with_tags( 75 "resolver.base.duration_ms", 76 duration_ms, 77 &[("success", "1")], 78 ) 79 .await; 80 81 let timestamp = SystemTime::now() 82 .duration_since(UNIX_EPOCH) 83 .map_err(|e| { 84 HandleResolverError::ResolutionFailed(format!("System time error: {}", e)) 85 })? 86 .as_secs(); 87 88 Ok((did, timestamp)) 89 } 90 Err(e) => { 91 self.metrics 92 .time_with_tags( 93 "resolver.base.duration_ms", 94 duration_ms, 95 &[("success", "0")], 96 ) 97 .await; 98 Err(e) 99 } 100 } 101 } 102} 103 104/// Create a new base handle resolver. 105/// 106/// This factory function creates a resolver that performs actual DNS and HTTP 107/// lookups for handle resolution. 108/// 109/// # Arguments 110/// 111/// * `dns_resolver` - DNS resolver for TXT record lookups 112/// * `http_client` - HTTP client for well-known endpoint queries 113/// * `metrics` - Metrics publisher for telemetry 114pub fn create_base_resolver( 115 dns_resolver: Arc<dyn DnsResolver>, 116 http_client: Client, 117 metrics: SharedMetricsPublisher, 118) -> Arc<dyn HandleResolver> { 119 Arc::new(BaseHandleResolver { 120 dns_resolver, 121 http_client, 122 metrics, 123 }) 124}