use std::time::Instant;
use reqwest::{Url, Version};
use tokio::sync::watch;
#[derive(Clone, Debug, Default)]
#[non_exhaustive]
pub struct RequestTiming {
pub headers_ms: f64,
pub body_ms: Option<f64>,
pub reused: bool,
pub next_hop_protocol: String,
pub content_encoding: Option<String>,
pub from_cache: bool,
}
#[derive(Debug)]
pub struct TimingSlot {
tx: watch::Sender<RequestTiming>,
started: Instant,
}
impl TimingSlot {
pub fn new(started: Instant, timing: RequestTiming) -> Self {
Self {
tx: watch::channel(timing).0,
started,
}
}
pub fn ended(&self) {
let elapsed = self.started.elapsed().as_secs_f64() * 1000.0;
self.tx.send_if_modified(|timing| {
if timing.body_ms.is_none() {
timing.body_ms = Some(elapsed);
true
} else {
false
}
});
}
pub async fn settled(&self) -> RequestTiming {
let mut rx = self.tx.subscribe();
let settled = match rx.wait_for(|timing| timing.body_ms.is_some()).await {
Ok(timing) => Some(timing.clone()),
Err(_) => None,
};
settled.unwrap_or_else(|| rx.borrow().clone())
}
}
pub(crate) fn alpn_protocol_id(version: Version, url: &Url) -> String {
let secure = url.scheme() == "https";
match version {
Version::HTTP_3 => "h3",
Version::HTTP_2 => {
if secure {
"h2"
} else {
"h2c"
}
}
Version::HTTP_11 => "http/1.1",
Version::HTTP_10 => "http/1.0",
Version::HTTP_09 => "http/0.9",
_ => "",
}
.to_owned()
}