px_native/infrastructure/
handler.rs1use std::sync::Arc;
7use std::time::Instant;
8
9use async_trait::async_trait;
10use px_core::{CookieJarDelta, Fingerprint, PxAppId};
11use px_errors::AppError;
12use px_pipeline::{ChallengeHandler, HandlerMetrics, HandlerName, HandlerOutcome, PageHtml};
13
14use crate::domain::native_solver::{NativeSolver, SolveContext};
15
16pub struct NativePxHandler {
17 solver: Arc<dyn NativeSolver>,
18 app_id: PxAppId,
19 name: HandlerName,
20}
21
22impl NativePxHandler {
23 pub fn new(solver: Arc<dyn NativeSolver>, app_id: PxAppId) -> Self {
24 Self {
25 solver,
26 app_id,
27 name: "perimeterx-native",
28 }
29 }
30}
31
32#[async_trait]
33impl ChallengeHandler for NativePxHandler {
34 fn name(&self) -> HandlerName {
35 self.name
36 }
37
38 async fn detects(&self, _page: &PageHtml) -> Result<bool, AppError> {
39 Ok(true)
40 }
41
42 async fn solve(&self, page: &PageHtml) -> Result<HandlerOutcome, AppError> {
43 let started = Instant::now();
44 let ctx = SolveContext::new(page.url.clone(), self.app_id.clone(), default_fingerprint());
45 let bundle = self.solver.solve(&ctx).await?;
46 let metrics = HandlerMetrics {
47 solve_ms: started.elapsed().as_millis() as u64,
48 ..Default::default()
49 };
50 Ok(HandlerOutcome::solved_with_ua(
51 self.name,
52 CookieJarDelta {
53 set: bundle.cookies,
54 removed: Vec::new(),
55 },
56 Vec::new(),
57 metrics,
58 bundle.user_agent,
59 ))
60 }
61}
62
63fn default_fingerprint() -> Fingerprint {
64 Fingerprint {
65 user_agent: "Mozilla/5.0 (X11; Linux x86_64; rv:135.0) Gecko/20100101 Firefox/135.0".into(),
66 accept_language: vec!["es-AR".into(), "es".into(), "en-US".into()],
67 screen_width: 1366,
68 screen_height: 768,
69 device_pixel_ratio: 1,
70 timezone: "America/Argentina/Buenos_Aires".into(),
71 platform: "Linux x86_64".into(),
72 webgl_vendor: "Mozilla".into(),
73 webgl_renderer: "Mozilla".into(),
74 }
75}
76
77#[cfg(test)]
78#[allow(clippy::expect_used, clippy::unwrap_used)]
79mod tests {
80 use super::*;
81 use px_core::{NamedCookie, PxCookieBundle};
82 use px_pipeline::HandlerStatus;
83 use std::time::{Duration, SystemTime};
84
85 struct AlwaysOkSolver;
86
87 #[async_trait]
88 impl NativeSolver for AlwaysOkSolver {
89 async fn solve(&self, _ctx: &SolveContext) -> Result<PxCookieBundle, AppError> {
90 Ok(PxCookieBundle::new(
91 vec![NamedCookie {
92 name: "_px3".into(),
93 value: "native".into(),
94 domain: "example.com".into(),
95 path: "/".into(),
96 }],
97 "ua",
98 SystemTime::now(),
99 Duration::from_secs(60),
100 ))
101 }
102 }
103
104 fn app_id() -> PxAppId {
105 PxAppId::new("PXeT15wiaE").expect("valid app id")
106 }
107
108 #[tokio::test]
109 async fn handler_reports_solved_status() {
110 let handler =
111 NativePxHandler::new(Arc::new(AlwaysOkSolver) as Arc<dyn NativeSolver>, app_id());
112 let page = PageHtml::new("https://www.pedidosya.com.ar/", "");
113 let out = handler.solve(&page).await.expect("solve");
114 assert_eq!(out.status, HandlerStatus::Solved);
115 assert_eq!(out.cookies.set.len(), 1);
116 assert_eq!(out.user_agent.as_deref(), Some("ua"));
117 }
118}