use crate::event::{ObservedHost, ObservedMethod};
use crate::response::InternalResponseMetadata;
use std::fmt;
#[derive(Clone, Eq, PartialEq)]
pub struct ProxyResponseMetadataObservation {
generation: u64,
host: ObservedHost,
method: ObservedMethod,
status_code: u16,
likely_main_frame: bool,
observation_id: Option<u64>,
metadata: InternalResponseMetadata,
}
impl ProxyResponseMetadataObservation {
pub(crate) fn new(
generation: u64,
host: ObservedHost,
method: ObservedMethod,
status_code: u16,
likely_main_frame: bool,
observation_id: Option<u64>,
metadata: InternalResponseMetadata,
) -> Self {
Self {
generation,
host,
method,
status_code,
likely_main_frame,
observation_id,
metadata,
}
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn host(&self) -> &ObservedHost {
&self.host
}
pub fn method(&self) -> ObservedMethod {
self.method
}
pub fn status_code(&self) -> u16 {
self.status_code
}
pub fn is_likely_main_frame(&self) -> bool {
self.likely_main_frame
}
pub fn observation_id(&self) -> Option<u64> {
self.observation_id
}
pub fn metadata(&self) -> &InternalResponseMetadata {
&self.metadata
}
}
impl fmt::Debug for ProxyResponseMetadataObservation {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ProxyResponseMetadataObservation")
.field("generation", &self.generation)
.field("host", &self.host)
.field("method", &self.method)
.field("status_code", &self.status_code)
.field("likely_main_frame", &self.likely_main_frame)
.field("observation_id_present", &self.observation_id.is_some())
.field("metadata", &self.metadata)
.finish()
}
}
pub trait ProxyResponseMetadataObserver: Send + Sync + 'static {
fn observe(&self, observation: &ProxyResponseMetadataObservation);
}
impl<F> ProxyResponseMetadataObserver for F
where
F: Fn(&ProxyResponseMetadataObservation) + Send + Sync + 'static,
{
fn observe(&self, observation: &ProxyResponseMetadataObservation) {
self(observation);
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct NoopProxyResponseMetadataObserver;
impl ProxyResponseMetadataObserver for NoopProxyResponseMetadataObserver {
fn observe(&self, _observation: &ProxyResponseMetadataObservation) {}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sanitize_response_headers;
#[test]
fn observation_debug_redacts_sensitive_metadata_values() {
let headers = sanitize_response_headers(&[
(
"X-HNS-Resolution-Trace".to_owned(),
"private?token=secret".to_owned(),
),
("X-HNS-Security-Path".to_owned(), "dane".to_owned()),
])
.unwrap();
let observation = ProxyResponseMetadataObservation::new(
7,
ObservedHost::new("welcome").unwrap(),
ObservedMethod::Get,
200,
true,
Some(9),
headers.metadata().clone(),
);
assert_eq!(observation.generation(), 7);
assert_eq!(observation.host().as_str(), "welcome");
assert_eq!(observation.method(), ObservedMethod::Get);
assert_eq!(observation.status_code(), 200);
assert!(observation.is_likely_main_frame());
assert_eq!(observation.observation_id(), Some(9));
assert_eq!(
observation.metadata().get("X-HNS-Security-Path"),
Some("dane")
);
let diagnostic = format!("{observation:?}");
assert!(!diagnostic.contains("private"));
assert!(!diagnostic.contains("token"));
assert!(!diagnostic.contains("secret"));
assert!(!diagnostic.contains("path"));
assert!(!diagnostic.contains("query"));
assert!(!diagnostic.contains("body"));
}
}