1use crate::cdp::session::CdpSession;
10use crate::cdp::CdpEvent;
11use crate::request::Request;
12use crate::response::Response;
13use crate::types::Headers;
14use parking_lot::Mutex;
15use serde::{Deserialize, Serialize};
16use serde_json::Value;
17use std::collections::HashMap;
18use std::future::Future;
19use std::pin::Pin;
20use std::sync::atomic::{AtomicU64, Ordering};
21use std::sync::{Arc, Weak};
22use tokio::sync::broadcast;
23
24pub(crate) type RequestHandler =
25 Arc<dyn Fn(Request) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
26pub(crate) type ResponseHandler =
27 Arc<dyn Fn(Response) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>;
28
29#[derive(Debug, Clone, Default, Serialize, Deserialize)]
38pub struct Timing {
39 pub request_time: Option<f64>,
41 pub proxy_start: Option<f64>,
43 pub proxy_end: Option<f64>,
45 pub dns_start: Option<f64>,
47 pub dns_end: Option<f64>,
48 pub connect_start: Option<f64>,
50 pub connect_end: Option<f64>,
51 pub ssl_start: Option<f64>,
53 pub ssl_end: Option<f64>,
54 pub worker_start: Option<f64>,
56 pub worker_ready: Option<f64>,
57 pub worker_fetch_start: Option<f64>,
58 pub send_start: Option<f64>,
60 pub send_end: Option<f64>,
61 pub receive_headers_end: Option<f64>,
63}
64
65impl Timing {
66 fn from_value(v: Option<&Value>) -> Option<Self> {
67 let t = v?;
68 Some(Timing {
69 request_time: t.get("requestTime").and_then(|v| v.as_f64()),
70 proxy_start: t.get("proxyStart").and_then(|v| v.as_f64()),
71 proxy_end: t.get("proxyEnd").and_then(|v| v.as_f64()),
72 dns_start: t.get("dnsStart").and_then(|v| v.as_f64()),
73 dns_end: t.get("dnsEnd").and_then(|v| v.as_f64()),
74 connect_start: t.get("connectStart").and_then(|v| v.as_f64()),
75 connect_end: t.get("connectEnd").and_then(|v| v.as_f64()),
76 ssl_start: t.get("sslStart").and_then(|v| v.as_f64()),
77 ssl_end: t.get("sslEnd").and_then(|v| v.as_f64()),
78 worker_start: t.get("workerStart").and_then(|v| v.as_f64()),
79 worker_ready: t.get("workerReady").and_then(|v| v.as_f64()),
80 worker_fetch_start: t.get("workerFetchStart").and_then(|v| v.as_f64()),
81 send_start: t.get("sendStart").and_then(|v| v.as_f64()),
82 send_end: t.get("sendEnd").and_then(|v| v.as_f64()),
83 receive_headers_end: t
84 .get("receiveHeadersEnd")
85 .and_then(|v| v.as_f64()),
86 })
87 }
88}
89
90#[derive(Debug, Clone, Default, Serialize, Deserialize)]
93pub struct Sizes {
94 pub encoded_data_length: Option<u64>,
97}
98
99#[derive(Debug, Clone, Default, Serialize, Deserialize)]
102pub struct SecurityDetails {
103 pub protocol: Option<String>,
105 pub subject_name: Option<String>,
107 pub issuer: Option<String>,
109 pub valid_from: Option<f64>,
111 pub valid_to: Option<f64>,
113}
114
115impl SecurityDetails {
116 fn from_value(v: Option<&Value>) -> Option<Self> {
117 let s = v?;
118 Some(SecurityDetails {
119 protocol: s.get("protocol").and_then(|v| v.as_str()).map(String::from),
120 subject_name: s
121 .get("subjectName")
122 .and_then(|v| v.as_str())
123 .map(String::from),
124 issuer: s.get("issuer").and_then(|v| v.as_str()).map(String::from),
125 valid_from: s.get("validFrom").and_then(|v| v.as_f64()),
126 valid_to: s.get("validTo").and_then(|v| v.as_f64()),
127 })
128 }
129}
130
131#[derive(Debug, Clone, Default, Serialize, Deserialize)]
133pub struct ServerAddr {
134 pub ip_address: Option<String>,
136 pub port: Option<u16>,
138}
139
140impl ServerAddr {
141 fn from_value(v: Option<&Value>) -> Option<Self> {
142 let r = v?;
143 let ip_address = r.get("remoteIPAddress").and_then(|v| v.as_str()).map(String::from);
144 let port = r
145 .get("remotePort")
146 .and_then(|v| v.as_i64())
147 .map(|n| n as u16);
148 if ip_address.is_none() && port.is_none() {
150 None
151 } else {
152 Some(ServerAddr { ip_address, port })
153 }
154 }
155}
156
157#[derive(Debug, Clone, Default)]
159struct RequestDetail {
160 post_data: Option<String>,
163 redirected_from: Option<String>,
166 redirected_to: Option<String>,
169 failure: Option<String>,
171 timing: Option<Timing>,
174 sizes: Option<Sizes>,
176 http_version: Option<String>,
178 security_details: Option<SecurityDetails>,
180 server_addr: Option<ServerAddr>,
182 raw_response_headers: Option<Headers>,
184 raw_request_headers: Option<Headers>,
186 finished: bool,
188}
189
190pub(crate) struct NetworkStore {
192 self_ref: Mutex<Weak<NetworkStore>>,
193 requests: Mutex<HashMap<String, Request>>,
194 responses: Mutex<HashMap<String, Response>>,
195 details: Mutex<HashMap<String, RequestDetail>>,
197 loaders: Mutex<HashMap<String, String>>,
199 inflight: AtomicU64,
201}
202
203impl NetworkStore {
204 pub(crate) fn new() -> Arc<Self> {
205 let s = Arc::new(Self {
206 self_ref: Mutex::new(Weak::new()),
207 requests: Mutex::new(HashMap::new()),
208 responses: Mutex::new(HashMap::new()),
209 details: Mutex::new(HashMap::new()),
210 loaders: Mutex::new(HashMap::new()),
211 inflight: AtomicU64::new(0),
212 });
213 *s.self_ref.lock() = Arc::downgrade(&s);
214 s
215 }
216
217 pub(crate) fn inflight(&self) -> u64 {
219 self.inflight.load(Ordering::Relaxed)
220 }
221
222 fn weak(&self) -> Weak<NetworkStore> {
223 self.self_ref.lock().clone()
224 }
225
226 pub(crate) fn get_request(&self, request_id: &str) -> Option<Request> {
227 self.requests.lock().get(request_id).cloned()
228 }
229
230 pub(crate) fn get_response(&self, request_id: &str) -> Option<Response> {
231 self.responses.lock().get(request_id).cloned()
232 }
233
234 pub(crate) fn post_data(&self, request_id: &str) -> Option<String> {
237 self.details
238 .lock()
239 .get(request_id)
240 .and_then(|d| d.post_data.clone())
241 }
242
243 pub(crate) fn redirect_source(&self, request_id: &str) -> Option<String> {
244 self.details
245 .lock()
246 .get(request_id)
247 .and_then(|d| d.redirected_from.clone())
248 }
249
250 pub(crate) fn redirect_target(&self, request_id: &str) -> Option<String> {
251 self.details
252 .lock()
253 .get(request_id)
254 .and_then(|d| d.redirected_to.clone())
255 }
256
257 pub(crate) fn failure(&self, request_id: &str) -> Option<String> {
258 self.details
259 .lock()
260 .get(request_id)
261 .and_then(|d| d.failure.clone())
262 }
263
264 pub(crate) fn timing(&self, request_id: &str) -> Option<Timing> {
265 self.details
266 .lock()
267 .get(request_id)
268 .and_then(|d| d.timing.clone())
269 }
270
271 pub(crate) fn sizes(&self, request_id: &str) -> Option<Sizes> {
272 self.details
273 .lock()
274 .get(request_id)
275 .and_then(|d| d.sizes.clone())
276 }
277
278 pub(crate) fn http_version(&self, request_id: &str) -> Option<String> {
279 self.details
280 .lock()
281 .get(request_id)
282 .and_then(|d| d.http_version.clone())
283 }
284
285 pub(crate) fn security_details(&self, request_id: &str) -> Option<SecurityDetails> {
286 self.details
287 .lock()
288 .get(request_id)
289 .and_then(|d| d.security_details.clone())
290 }
291
292 pub(crate) fn server_addr(&self, request_id: &str) -> Option<ServerAddr> {
293 self.details
294 .lock()
295 .get(request_id)
296 .and_then(|d| d.server_addr.clone())
297 }
298
299 pub(crate) fn raw_response_headers(&self, request_id: &str) -> Option<Headers> {
300 self.details
301 .lock()
302 .get(request_id)
303 .and_then(|d| d.raw_response_headers.clone())
304 }
305
306 pub(crate) fn raw_request_headers(&self, request_id: &str) -> Option<Headers> {
307 self.details
308 .lock()
309 .get(request_id)
310 .and_then(|d| d.raw_request_headers.clone())
311 }
312
313 pub(crate) fn finished(&self, request_id: &str) -> bool {
314 self.details
315 .lock()
316 .get(request_id)
317 .map(|d| d.finished)
318 .unwrap_or(false)
319 }
320
321 pub(crate) fn response_for_loader(&self, loader_id: &str) -> Option<Response> {
323 let req_id = self.loaders.lock().get(loader_id).cloned()?;
324 self.get_response(&req_id)
325 }
326
327 pub(crate) fn on_request_will_be_sent(&self, params: &Value) -> Option<Request> {
329 let req = params.get("request")?;
330 let request_id = params.get("requestId").and_then(|v| v.as_str())?.to_string();
331 let url = req.get("url").and_then(|v| v.as_str())?.to_string();
332 let method = req
333 .get("method")
334 .and_then(|v| v.as_str())
335 .unwrap_or("GET")
336 .to_string();
337 let headers = headers_from_value(req.get("headers"));
338 let resource_type = params
339 .get("type")
340 .and_then(|v| v.as_str())
341 .unwrap_or("Other")
342 .to_string();
343 let is_navigation = resource_type == "Document";
344 let loader_id = params.get("loaderId").and_then(|v| v.as_str()).map(String::from);
345
346 let request = Request::new(
347 url,
348 method,
349 headers,
350 resource_type,
351 request_id.clone(),
352 is_navigation,
353 self.weak(),
354 );
355 if is_navigation {
356 if let Some(lid) = &loader_id {
357 self.loaders.lock().insert(lid.clone(), request_id.clone());
358 }
359 }
360 self.requests.lock().insert(request_id.clone(), request.clone());
361 self.inflight.fetch_add(1, Ordering::Relaxed);
362
363 let post_data = req
365 .get("postData")
366 .and_then(|v| v.as_str())
367 .map(String::from);
368 let raw_request_headers = headers_raw_from_value(req.get("headers"));
369 let redirected_from = params
372 .get("redirectSource")
373 .and_then(|v| v.as_str())
374 .map(String::from);
375
376 let mut details = self.details.lock();
377 let entry = details.entry(request_id.clone()).or_default();
378 entry.post_data = post_data;
379 entry.raw_request_headers = raw_request_headers;
380 if let Some(src) = &redirected_from {
381 entry.redirected_from = Some(src.clone());
382 let src_entry = details.entry(src.clone()).or_default();
384 src_entry.redirected_to = Some(request_id.clone());
385 }
386
387 Some(request)
388 }
389
390 pub(crate) fn on_loading_finished(&self, params: &Value) {
393 let request_id = match params.get("requestId").and_then(|v| v.as_str()) {
394 Some(id) => id.to_string(),
395 None => return,
396 };
397 let encoded = params
398 .get("encodedDataLength")
399 .and_then(|v| v.as_u64())
400 .or_else(|| params.get("encodedDataLength").and_then(|v| v.as_f64()).map(|n| n as u64));
401 let mut details = self.details.lock();
402 let entry = details.entry(request_id).or_default();
403 entry.finished = true;
404 if let Some(enc) = encoded {
405 entry.sizes = Some(Sizes {
407 encoded_data_length: Some(enc),
408 });
409 }
410 drop(details);
411 self.decrement_inflight();
413 }
414
415 pub(crate) fn on_loading_failed(&self, params: &Value) {
417 let request_id = match params.get("requestId").and_then(|v| v.as_str()) {
418 Some(id) => id.to_string(),
419 None => return,
420 };
421 let error_text = params
422 .get("errorText")
423 .and_then(|v| v.as_str())
424 .map(String::from);
425 let mut details = self.details.lock();
426 let entry = details.entry(request_id).or_default();
427 if let Some(t) = error_text {
428 entry.failure = Some(t);
429 }
430 drop(details);
431 self.decrement_inflight();
432 }
433
434 fn decrement_inflight(&self) {
436 let prev = self.inflight.fetch_sub(1, Ordering::Relaxed);
437 if prev == 0 {
438 self.inflight.store(0, Ordering::Relaxed);
440 }
441 }
442
443 pub(crate) fn on_response_received(
445 &self,
446 params: &Value,
447 session: Arc<CdpSession>,
448 ) -> Option<Response> {
449 let resp = params.get("response")?;
450 let request_id = params.get("requestId").and_then(|v| v.as_str())?.to_string();
451 let request = self.get_request(&request_id)?;
452 let url = resp.get("url").and_then(|v| v.as_str()).unwrap_or("").to_string();
453 let status = resp.get("status").and_then(|v| v.as_u64()).unwrap_or(0) as u16;
454 let status_text = resp
455 .get("statusText")
456 .and_then(|v| v.as_str())
457 .unwrap_or("")
458 .to_string();
459 let headers = headers_from_value(resp.get("headers"));
460 let from_disk = resp.get("fromDiskCache").and_then(|v| v.as_bool()).unwrap_or(false);
461 let from_prefetch = resp
462 .get("fromPrefetchCache")
463 .and_then(|v| v.as_bool())
464 .unwrap_or(false);
465 let from_service_worker = resp
466 .get("fromServiceWorker")
467 .and_then(|v| v.as_bool())
468 .unwrap_or(false);
469
470 let response = Response::new(
471 url,
472 status,
473 status_text,
474 headers,
475 request_id.clone(),
476 from_disk || from_prefetch,
477 from_service_worker,
478 session,
479 request,
480 );
481 self.responses.lock().insert(request_id.clone(), response.clone());
482
483 let timing = Timing::from_value(resp.get("timing"));
485 let security_details = SecurityDetails::from_value(resp.get("securityDetails"));
486 let server_addr = ServerAddr::from_value(Some(resp));
487 let http_version = resp
488 .get("protocol")
489 .and_then(|v| v.as_str())
490 .map(String::from);
491 let raw_response_headers = headers_raw_from_value(resp.get("headers"));
492
493 let mut details = self.details.lock();
494 let entry = details.entry(request_id).or_default();
495 if let Some(t) = timing {
496 entry.timing = Some(t);
497 }
498 if let Some(s) = security_details {
499 entry.security_details = Some(s);
500 }
501 if let Some(a) = server_addr {
502 entry.server_addr = Some(a);
503 }
504 if let Some(v) = http_version {
505 entry.http_version = Some(v);
506 }
507 if let Some(h) = raw_response_headers {
508 entry.raw_response_headers = Some(h);
509 }
510
511 Some(response)
512 }
513}
514
515pub(crate) async fn network_tracker(
517 mut rx: broadcast::Receiver<CdpEvent>,
518 session: Arc<CdpSession>,
519 store: Arc<NetworkStore>,
520 request_handlers: Arc<Mutex<Vec<RequestHandler>>>,
521 response_handlers: Arc<Mutex<Vec<ResponseHandler>>>,
522 failed_handlers: Arc<Mutex<Vec<RequestHandler>>>,
523) {
524 loop {
525 match rx.recv().await {
526 Ok(ev) => match ev.method.as_str() {
527 "Network.requestWillBeSent" => {
528 if let Some(req) = store.on_request_will_be_sent(&ev.params) {
529 fire(&request_handlers, req);
530 }
531 }
532 "Network.responseReceived" => {
533 if let Some(resp) =
534 store.on_response_received(&ev.params, Arc::clone(&session))
535 {
536 fire(&response_handlers, resp);
537 }
538 }
539 "Network.loadingFinished" => {
540 store.on_loading_finished(&ev.params);
541 }
542 "Network.loadingFailed" => {
543 store.on_loading_failed(&ev.params);
544 if let Some(rid) = ev.params.get("requestId").and_then(|v| v.as_str()) {
545 if let Some(req) = store.get_request(rid) {
546 fire(&failed_handlers, req);
547 }
548 }
549 }
550 _ => {}
551 },
552 Err(broadcast::error::RecvError::Closed) => break,
553 Err(broadcast::error::RecvError::Lagged(_)) => continue,
554 }
555 }
556}
557
558fn fire<T: Clone + Send + 'static>(
559 handlers: &Arc<Mutex<Vec<Arc<dyn Fn(T) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync>>>>,
560 value: T,
561) {
562 let list = handlers.lock().clone();
563 for h in list {
564 let v = value.clone();
565 tokio::spawn(async move {
566 (h)(v).await;
567 });
568 }
569}
570
571fn headers_from_value(v: Option<&Value>) -> Headers {
573 let mut out = Headers::new();
574 if let Some(obj) = v.and_then(|v| v.as_object()) {
575 for (k, val) in obj {
576 if let Some(s) = val.as_str() {
577 out.insert(k.to_lowercase(), s.to_string());
578 }
579 }
580 }
581 out
582}
583
584fn headers_raw_from_value(v: Option<&Value>) -> Option<Headers> {
586 let obj = v.and_then(|v| v.as_object())?;
587 let mut out = Headers::new();
588 for (k, val) in obj {
589 if let Some(s) = val.as_str() {
590 out.insert(k.clone(), s.to_string());
591 }
592 }
593 Some(out)
594}