viewpoint_core/network/response/
mod.rs1use std::collections::HashMap;
4use std::sync::Arc;
5
6use viewpoint_cdp::protocol::network::GetResponseBodyParams;
7use viewpoint_cdp::CdpConnection;
8
9use super::request::Request;
10use crate::error::NetworkError;
11
12#[derive(Debug, Clone)]
17pub struct Response {
18 url: String,
20 status: u16,
22 status_text: String,
24 headers: HashMap<String, String>,
26 mime_type: String,
28 from_cache: bool,
30 from_service_worker: bool,
32 request: Request,
34 connection: Arc<CdpConnection>,
36 session_id: String,
38 request_id: String,
40 security_details: Option<SecurityDetails>,
42 remote_address: Option<RemoteAddress>,
44}
45
46impl Response {
47 #[allow(clippy::too_many_arguments)]
49 pub(crate) fn new(
50 cdp_response: viewpoint_cdp::protocol::network::Response,
51 request: Request,
52 connection: Arc<CdpConnection>,
53 session_id: String,
54 request_id: String,
55 ) -> Self {
56 let remote_address = cdp_response.remote_ip_address.map(|ip| RemoteAddress {
57 ip_address: ip,
58 port: cdp_response.remote_port.unwrap_or(0) as u16,
59 });
60
61 let security_details = cdp_response.security_details.map(SecurityDetails::from);
63
64 Self {
65 url: cdp_response.url,
66 status: cdp_response.status as u16,
67 status_text: cdp_response.status_text,
68 headers: cdp_response.headers,
69 mime_type: cdp_response.mime_type,
70 from_cache: cdp_response.from_disk_cache.unwrap_or(false),
71 from_service_worker: cdp_response.from_service_worker.unwrap_or(false),
72 request,
73 connection,
74 session_id,
75 request_id,
76 security_details,
77 remote_address,
78 }
79 }
80
81 pub fn url(&self) -> &str {
85 &self.url
86 }
87
88 pub fn status(&self) -> u16 {
90 self.status
91 }
92
93 pub fn status_text(&self) -> &str {
95 &self.status_text
96 }
97
98 pub fn ok(&self) -> bool {
100 (200..300).contains(&self.status)
101 }
102
103 pub fn headers(&self) -> &HashMap<String, String> {
105 &self.headers
106 }
107
108 pub fn header_value(&self, name: &str) -> Option<&str> {
110 self.headers
111 .iter()
112 .find(|(k, _)| k.eq_ignore_ascii_case(name))
113 .map(|(_, v)| v.as_str())
114 }
115
116 pub async fn all_headers(&self) -> HashMap<String, String> {
120 self.headers.clone()
122 }
123
124 pub fn mime_type(&self) -> &str {
126 &self.mime_type
127 }
128
129 pub fn from_cache(&self) -> bool {
131 self.from_cache
132 }
133
134 pub fn from_service_worker(&self) -> bool {
136 self.from_service_worker
137 }
138
139 pub fn request(&self) -> &Request {
141 &self.request
142 }
143
144 pub async fn body(&self) -> Result<Vec<u8>, NetworkError> {
150 let result: viewpoint_cdp::protocol::network::GetResponseBodyResult = self
151 .connection
152 .send_command(
153 "Network.getResponseBody",
154 Some(GetResponseBodyParams {
155 request_id: self.request_id.clone(),
156 }),
157 Some(&self.session_id),
158 )
159 .await
160 .map_err(NetworkError::from)?;
161
162 if result.base64_encoded {
163 use base64::Engine;
164 base64::engine::general_purpose::STANDARD
165 .decode(&result.body)
166 .map_err(|e| NetworkError::InvalidResponse(format!("Failed to decode base64: {e}")))
167 } else {
168 Ok(result.body.into_bytes())
169 }
170 }
171
172 pub async fn text(&self) -> Result<String, NetworkError> {
178 let body = self.body().await?;
179 String::from_utf8(body)
180 .map_err(|e| NetworkError::InvalidResponse(format!("Response is not valid UTF-8: {e}")))
181 }
182
183 pub async fn json<T: serde::de::DeserializeOwned>(&self) -> Result<T, NetworkError> {
189 let text = self.text().await?;
190 serde_json::from_str(&text)
191 .map_err(|e| NetworkError::InvalidResponse(format!("Failed to parse JSON: {e}")))
192 }
193
194 pub fn security_details(&self) -> Option<&SecurityDetails> {
196 self.security_details.as_ref()
197 }
198
199 pub fn server_addr(&self) -> Option<&RemoteAddress> {
201 self.remote_address.as_ref()
202 }
203
204 pub async fn finished(&self) -> Result<(), NetworkError> {
206 Ok(())
209 }
210}
211
212#[derive(Debug, Clone)]
214pub struct SecurityDetails {
215 pub protocol: String,
217 pub subject_name: String,
219 pub issuer: String,
221 pub valid_from: f64,
223 pub valid_to: f64,
225 pub san_list: Vec<String>,
227}
228
229impl From<viewpoint_cdp::protocol::network::SecurityDetails> for SecurityDetails {
230 fn from(details: viewpoint_cdp::protocol::network::SecurityDetails) -> Self {
231 Self {
232 protocol: details.protocol,
233 subject_name: details.subject_name,
234 issuer: details.issuer,
235 valid_from: details.valid_from,
236 valid_to: details.valid_to,
237 san_list: details.san_list,
238 }
239 }
240}
241
242#[derive(Debug, Clone)]
244pub struct RemoteAddress {
245 pub ip_address: String,
247 pub port: u16,
249}
250
251#[cfg(test)]
252mod tests;