Skip to main content

playwright_rs/protocol/
response.rs

1// Response protocol object
2//
3// Represents an HTTP response from navigation operations.
4// Response objects are created by the server when Frame.goto() or similar navigation
5// methods complete successfully.
6
7use crate::error::Result;
8use crate::server::channel_owner::{ChannelOwner, ChannelOwnerImpl, ParentOrConnection};
9use serde_json::Value;
10use std::any::Any;
11use std::sync::Arc;
12
13/// A single HTTP header entry with a name and value.
14///
15/// Used by `Response::headers_array()` to return all headers preserving duplicates.
16///
17/// See: <https://playwright.dev/docs/api/class-response#response-headers-array>
18#[derive(Debug, Clone)]
19pub struct HeaderEntry {
20    /// Header name (lowercase)
21    pub name: String,
22    /// Header value
23    pub value: String,
24}
25
26/// Response represents an HTTP response from a navigation operation.
27///
28/// Response objects are not created directly - they are returned from
29/// navigation methods like page.goto() or page.reload().
30///
31/// See: <https://playwright.dev/docs/api/class-response>
32#[derive(Clone)]
33pub struct ResponseObject {
34    base: ChannelOwnerImpl,
35}
36
37impl ResponseObject {
38    /// Creates a new Response from protocol initialization
39    ///
40    /// This is called by the object factory when the server sends a `__create__` message
41    /// for a Response object.
42    pub fn new(
43        parent: Arc<dyn ChannelOwner>,
44        type_name: String,
45        guid: Arc<str>,
46        initializer: Value,
47    ) -> Result<Self> {
48        let base = ChannelOwnerImpl::new(
49            ParentOrConnection::Parent(parent),
50            type_name,
51            guid,
52            initializer,
53        );
54
55        Ok(Self { base })
56    }
57
58    /// Returns the status code of the response (e.g., 200 for a success).
59    ///
60    /// See: <https://playwright.dev/docs/api/class-response#response-status>
61    pub fn status(&self) -> u16 {
62        self.initializer()
63            .get("status")
64            .and_then(|v| v.as_u64())
65            .unwrap_or(0) as u16
66    }
67
68    /// Returns the status text of the response (e.g. usually an "OK" for a success).
69    ///
70    /// See: <https://playwright.dev/docs/api/class-response#response-status-text>
71    pub fn status_text(&self) -> &str {
72        self.initializer()
73            .get("statusText")
74            .and_then(|v| v.as_str())
75            .unwrap_or("")
76    }
77
78    /// Returns the URL of the response.
79    ///
80    /// See: <https://playwright.dev/docs/api/class-response#response-url>
81    pub fn url(&self) -> &str {
82        self.initializer()
83            .get("url")
84            .and_then(|v| v.as_str())
85            .unwrap_or("")
86    }
87
88    /// Returns the response body as bytes.
89    ///
90    /// Sends a `"body"` RPC call to the Playwright server, which returns the body
91    /// as a base64-encoded binary string.
92    ///
93    /// See: <https://playwright.dev/docs/api/class-response#response-body>
94    pub async fn body(&self) -> Result<Vec<u8>> {
95        use serde::Deserialize;
96
97        #[derive(Deserialize)]
98        struct BodyResponse {
99            binary: String,
100        }
101
102        let result: BodyResponse = self.channel().send("body", serde_json::json!({})).await?;
103
104        use base64::Engine;
105        let bytes = base64::engine::general_purpose::STANDARD
106            .decode(&result.binary)
107            .map_err(|e| {
108                crate::error::Error::ProtocolError(format!(
109                    "Failed to decode response body from base64: {}",
110                    e
111                ))
112            })?;
113        Ok(bytes)
114    }
115
116    /// Returns the raw response headers as name-value pairs (preserving duplicates).
117    ///
118    /// Sends a `"rawResponseHeaders"` RPC call to the Playwright server.
119    ///
120    /// See: <https://playwright.dev/docs/api/class-response#response-headers-array>
121    pub async fn raw_headers(&self) -> Result<Vec<HeaderEntry>> {
122        use serde::Deserialize;
123
124        #[derive(Deserialize)]
125        struct RawHeadersResponse {
126            headers: Vec<HeaderEntryRaw>,
127        }
128
129        #[derive(Deserialize)]
130        struct HeaderEntryRaw {
131            name: String,
132            value: String,
133        }
134
135        let result: RawHeadersResponse = self
136            .channel()
137            .send("rawResponseHeaders", serde_json::json!({}))
138            .await?;
139
140        Ok(result
141            .headers
142            .into_iter()
143            .map(|h| HeaderEntry {
144                name: h.name,
145                value: h.value,
146            })
147            .collect())
148    }
149}
150
151impl ChannelOwner for ResponseObject {
152    fn guid(&self) -> &str {
153        self.base.guid()
154    }
155
156    fn type_name(&self) -> &str {
157        self.base.type_name()
158    }
159
160    fn parent(&self) -> Option<Arc<dyn ChannelOwner>> {
161        self.base.parent()
162    }
163
164    fn connection(&self) -> Arc<dyn crate::server::connection::ConnectionLike> {
165        self.base.connection()
166    }
167
168    fn initializer(&self) -> &Value {
169        self.base.initializer()
170    }
171
172    fn channel(&self) -> &crate::server::channel::Channel {
173        self.base.channel()
174    }
175
176    fn dispose(&self, reason: crate::server::channel_owner::DisposeReason) {
177        self.base.dispose(reason)
178    }
179
180    fn adopt(&self, child: Arc<dyn ChannelOwner>) {
181        self.base.adopt(child)
182    }
183
184    fn add_child(&self, guid: Arc<str>, child: Arc<dyn ChannelOwner>) {
185        self.base.add_child(guid, child)
186    }
187
188    fn remove_child(&self, guid: &str) {
189        self.base.remove_child(guid)
190    }
191
192    fn on_event(&self, _method: &str, _params: Value) {
193        // Response objects don't have events
194    }
195
196    fn was_collected(&self) -> bool {
197        self.base.was_collected()
198    }
199
200    fn as_any(&self) -> &dyn Any {
201        self
202    }
203}
204
205impl std::fmt::Debug for ResponseObject {
206    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207        f.debug_struct("ResponseObject")
208            .field("guid", &self.guid())
209            .finish()
210    }
211}