use std::collections::HashMap;
use std::sync::Arc;
use viewpoint_cdp::CdpConnection;
use super::types::ResourceType;
#[derive(Debug, Clone)]
pub struct Request {
pub(crate) url: String,
pub(crate) method: String,
pub(crate) headers: HashMap<String, String>,
pub(crate) post_data: Option<String>,
pub(crate) resource_type: ResourceType,
pub(crate) frame_id: String,
pub(crate) is_navigation: bool,
pub(crate) connection: Option<Arc<CdpConnection>>,
pub(crate) session_id: Option<String>,
pub(crate) request_id: Option<String>,
pub(crate) redirected_from: Option<Box<Request>>,
pub(crate) redirected_to: Option<Box<Request>>,
pub(crate) timing: Option<RequestTiming>,
pub(crate) failure_text: Option<String>,
}
impl Request {
pub(crate) fn from_cdp(
cdp_request: viewpoint_cdp::protocol::network::Request,
resource_type: viewpoint_cdp::protocol::network::ResourceType,
frame_id: String,
connection: Option<Arc<CdpConnection>>,
session_id: Option<String>,
request_id: Option<String>,
) -> Self {
Self {
url: cdp_request.url,
method: cdp_request.method,
headers: cdp_request.headers,
post_data: cdp_request.post_data,
resource_type: resource_type.into(),
frame_id,
is_navigation: false, connection,
session_id,
request_id,
redirected_from: None,
redirected_to: None,
timing: None,
failure_text: None,
}
}
pub fn url(&self) -> &str {
&self.url
}
pub fn method(&self) -> &str {
&self.method
}
pub fn headers(&self) -> &HashMap<String, String> {
&self.headers
}
pub fn header_value(&self, name: &str) -> Option<&str> {
self.headers
.iter()
.find(|(k, _)| k.eq_ignore_ascii_case(name))
.map(|(_, v)| v.as_str())
}
pub async fn all_headers(&self) -> HashMap<String, String> {
self.headers.clone()
}
pub fn post_data(&self) -> Option<&str> {
self.post_data.as_deref()
}
pub fn post_data_buffer(&self) -> Option<Vec<u8>> {
self.post_data.as_ref().map(|s| s.as_bytes().to_vec())
}
pub fn post_data_json<T: serde::de::DeserializeOwned>(
&self,
) -> Result<Option<T>, serde_json::Error> {
match &self.post_data {
Some(data) => serde_json::from_str(data).map(Some),
None => Ok(None),
}
}
pub fn resource_type(&self) -> ResourceType {
self.resource_type
}
pub fn frame_id(&self) -> &str {
&self.frame_id
}
pub fn is_navigation_request(&self) -> bool {
self.is_navigation
}
pub fn redirected_from(&self) -> Option<&Request> {
self.redirected_from.as_deref()
}
pub fn redirected_to(&self) -> Option<&Request> {
self.redirected_to.as_deref()
}
pub fn timing(&self) -> Option<&RequestTiming> {
self.timing.as_ref()
}
pub async fn sizes(&self) -> RequestSizes {
let body_size = self.post_data.as_ref().map_or(0, std::string::String::len);
let headers_size = self
.headers
.iter()
.map(|(k, v)| k.len() + v.len() + 4) .sum();
RequestSizes {
request_body_size: body_size,
request_headers_size: headers_size,
}
}
pub fn failure(&self) -> Option<&str> {
self.failure_text.as_deref()
}
pub(crate) fn set_is_navigation(&mut self, is_navigation: bool) {
self.is_navigation = is_navigation;
}
pub(crate) fn set_redirected_from(&mut self, from: Request) {
self.redirected_from = Some(Box::new(from));
}
pub(crate) fn set_redirected_to(&mut self, to: Request) {
self.redirected_to = Some(Box::new(to));
}
pub(crate) fn set_timing(&mut self, timing: RequestTiming) {
self.timing = Some(timing);
}
pub(crate) fn set_failure_text(&mut self, text: String) {
self.failure_text = Some(text);
}
}
#[derive(Debug, Clone)]
pub struct RequestTiming {
pub start_time: f64,
pub proxy_start: f64,
pub proxy_end: f64,
pub dns_start: f64,
pub dns_end: f64,
pub connect_start: f64,
pub connect_end: f64,
pub ssl_start: f64,
pub ssl_end: f64,
pub send_start: f64,
pub send_end: f64,
pub receive_headers_start: f64,
pub receive_headers_end: f64,
}
impl From<viewpoint_cdp::protocol::network::ResourceTiming> for RequestTiming {
fn from(timing: viewpoint_cdp::protocol::network::ResourceTiming) -> Self {
Self {
start_time: timing.request_time * 1000.0,
proxy_start: timing.proxy_start,
proxy_end: timing.proxy_end,
dns_start: timing.dns_start,
dns_end: timing.dns_end,
connect_start: timing.connect_start,
connect_end: timing.connect_end,
ssl_start: timing.ssl_start,
ssl_end: timing.ssl_end,
send_start: timing.send_start,
send_end: timing.send_end,
receive_headers_start: timing.receive_headers_start.unwrap_or(0.0),
receive_headers_end: timing.receive_headers_end.unwrap_or(0.0),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct RequestSizes {
pub request_body_size: usize,
pub request_headers_size: usize,
}
#[cfg(test)]
mod tests;