Skip to main content

playwright_cdp/
request.rs

1//! `Request` — an HTTP request observed via the CDP `Network` domain.
2
3use crate::error::{Error, Result};
4use crate::network::{NetworkStore, Sizes, Timing};
5use crate::response::Response;
6use crate::types::Headers;
7use serde::de::DeserializeOwned;
8use std::sync::{Arc, Weak};
9
10/// An HTTP request captured for a page.
11#[derive(Clone)]
12pub struct Request {
13    inner: Arc<RequestInner>,
14}
15
16struct RequestInner {
17    url: String,
18    method: String,
19    headers: Headers,
20    resource_type: String,
21    request_id: String,
22    is_navigation: bool,
23    store: Weak<NetworkStore>,
24}
25
26impl Request {
27    pub(crate) fn new(
28        url: String,
29        method: String,
30        headers: Headers,
31        resource_type: String,
32        request_id: String,
33        is_navigation: bool,
34        store: Weak<NetworkStore>,
35    ) -> Self {
36        Self {
37            inner: Arc::new(RequestInner {
38                url,
39                method,
40                headers,
41                resource_type,
42                request_id,
43                is_navigation,
44                store,
45            }),
46        }
47    }
48
49    pub fn url(&self) -> &str {
50        &self.inner.url
51    }
52
53    pub fn method(&self) -> &str {
54        &self.inner.method
55    }
56
57    pub fn headers(&self) -> &Headers {
58        &self.inner.headers
59    }
60
61    pub fn resource_type(&self) -> &str {
62        &self.inner.resource_type
63    }
64
65    pub fn is_navigation_request(&self) -> bool {
66        self.inner.is_navigation
67    }
68
69    /// The CDP `requestId` backing this request.
70    pub fn request_id(&self) -> &str {
71        &self.inner.request_id
72    }
73
74    fn store(&self) -> Option<Arc<NetworkStore>> {
75        self.inner.store.upgrade()
76    }
77
78    /// Crate-internal access to the owning network store (for `Response`).
79    pub(crate) fn store_weak(&self) -> Option<Arc<NetworkStore>> {
80        self.inner.store.upgrade()
81    }
82
83    /// The response to this request, if it has arrived yet.
84    pub async fn response(&self) -> Result<Option<Response>> {
85        Ok(self
86            .store()
87            .and_then(|s| s.get_response(&self.inner.request_id)))
88    }
89
90    /// Raw request body (`Network.requestWillBeSent.postData`), if any.
91    pub fn post_data(&self) -> Option<String> {
92        self.store()
93            .and_then(|s| s.post_data(&self.inner.request_id))
94    }
95
96    /// Raw request body as bytes.
97    pub fn post_data_buffer(&self) -> Option<Vec<u8>> {
98        self.post_data().map(|s| s.into_bytes())
99    }
100
101    /// Parsed request body, deserialized as `T`. Returns `None` if there is no
102    /// post data; returns an error if the body is not valid JSON for `T`.
103    pub fn post_data_json<T: DeserializeOwned>(&self) -> Option<Result<T>> {
104        let data = self.post_data()?;
105        Some(serde_json::from_str(&data).map_err(Error::from))
106    }
107
108    /// The request this one redirected from (the previous hop in the redirect
109    /// chain), if any.
110    pub fn redirected_from(&self) -> Option<Request> {
111        let store = self.store()?;
112        let prev_id = store.redirect_source(&self.inner.request_id)?;
113        store.get_request(&prev_id)
114    }
115
116    /// The request this one redirected to (the next hop in the redirect chain),
117    /// if any.
118    pub fn redirected_to(&self) -> Option<Request> {
119        let store = self.store()?;
120        let next_id = store.redirect_target(&self.inner.request_id)?;
121        store.get_request(&next_id)
122    }
123
124    /// Failure text captured from `Network.loadingFailed.errorText`, if the
125    /// request failed to load.
126    pub fn failure(&self) -> Option<String> {
127        self.store()
128            .and_then(|s| s.failure(&self.inner.request_id))
129    }
130
131    /// Timing information for this request, if it has been captured.
132    pub fn timing(&self) -> Option<Timing> {
133        self.store()
134            .and_then(|s| s.timing(&self.inner.request_id))
135    }
136
137    /// Transferred size information, if `Network.loadingFinished` was received.
138    pub fn sizes(&self) -> Option<Sizes> {
139        self.store()
140            .and_then(|s| s.sizes(&self.inner.request_id))
141    }
142
143    /// All raw request headers (original case). Falls back to the lowercased
144    /// `headers()` map when raw headers were not captured.
145    pub fn all_headers(&self) -> Headers {
146        self.store()
147            .and_then(|s| s.raw_request_headers(&self.inner.request_id))
148            .unwrap_or_else(|| self.inner.headers.clone())
149    }
150
151    /// The frame that issued this request.
152    ///
153    /// Frame correlation is not tracked by the network store, so this always
154    /// returns `None`.
155    pub fn frame(&self) -> Option<crate::frame::Frame> {
156        None
157    }
158}