use std::future::Future;
use std::pin::Pin;
pub trait ChallengeResolver: Send + Sync + 'static {
fn resolve<'a>(
&'a self,
proxy_url: &'a str,
body: &'a [u8],
) -> Pin<Box<dyn Future<Output = Option<String>> + Send + 'a>>;
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
struct AlwaysSolves;
impl ChallengeResolver for AlwaysSolves {
fn resolve<'a>(
&'a self,
_proxy_url: &'a str,
_body: &'a [u8],
) -> Pin<Box<dyn Future<Output = Option<String>> + Send + 'a>> {
Box::pin(async { Some("cookie123".to_string()) })
}
}
struct AlwaysFails;
impl ChallengeResolver for AlwaysFails {
fn resolve<'a>(
&'a self,
_proxy_url: &'a str,
_body: &'a [u8],
) -> Pin<Box<dyn Future<Output = Option<String>> + Send + 'a>> {
Box::pin(async { None })
}
}
#[tokio::test]
async fn resolver_returns_some_on_success() {
let r = AlwaysSolves;
let result = r
.resolve("socks5h://1.2.3.4:1080", b"<html>challenge</html>")
.await;
assert_eq!(result, Some("cookie123".to_string()));
}
#[tokio::test]
async fn resolver_returns_none_on_failure() {
let r = AlwaysFails;
let result = r
.resolve("socks5h://1.2.3.4:1080", b"<html>hard challenge</html>")
.await;
assert!(result.is_none());
}
#[tokio::test]
async fn resolver_can_be_used_as_trait_object() {
let r: Arc<dyn ChallengeResolver> = Arc::new(AlwaysSolves);
let result = r.resolve("socks5h://1.2.3.4:1080", b"body").await;
assert!(result.is_some());
}
}