ic_bn_lib/http/client/
mod.rs

1#[cfg(feature = "clients-hyper")]
2pub mod clients_hyper;
3pub mod clients_reqwest;
4
5use std::{fmt, sync::Arc};
6
7use http::HeaderValue;
8use ic_bn_lib_common::traits::http::Client;
9use prometheus::{
10    HistogramVec, IntCounterVec, IntGaugeVec, Registry, register_histogram_vec_with_registry,
11    register_int_counter_vec_with_registry, register_int_gauge_vec_with_registry,
12};
13
14/// HTTP Client stats
15#[derive(Debug, Clone)]
16pub struct ClientStats {
17    pub pool_size: usize,
18    pub outstanding: usize,
19}
20
21/// Trait to get `ClientStats`
22pub trait Stats {
23    fn stats(&self) -> ClientStats;
24}
25
26/// `Client` that also emits `Stats`
27pub trait ClientWithStats: Client + Stats {
28    fn to_client(self: Arc<Self>) -> Arc<dyn Client>;
29}
30
31/// HTTP Client metrics
32#[derive(Clone, Debug)]
33struct Metrics {
34    requests: IntCounterVec,
35    requests_inflight: IntGaugeVec,
36    request_duration: HistogramVec,
37}
38
39impl Metrics {
40    pub fn new(registry: &Registry) -> Self {
41        const LABELS: &[&str] = &["host"];
42
43        Self {
44            requests: register_int_counter_vec_with_registry!(
45                format!("http_client_requests_total"),
46                format!("Counts the number of requests"),
47                LABELS,
48                registry
49            )
50            .unwrap(),
51
52            requests_inflight: register_int_gauge_vec_with_registry!(
53                format!("http_client_requests_inflight"),
54                format!("Counts the number of requests that are currently executed"),
55                LABELS,
56                registry
57            )
58            .unwrap(),
59
60            request_duration: register_histogram_vec_with_registry!(
61                format!("http_client_request_duration_sec"),
62                format!("Records the duration of requests in seconds"),
63                LABELS,
64                [0.01, 0.05, 0.1, 0.2, 0.4, 0.8, 1.6, 3.2].to_vec(),
65                registry
66            )
67            .unwrap(),
68        }
69    }
70}
71
72/// Create an HTTP header for Basic auth
73pub fn basic_auth<U, P>(username: U, password: Option<P>) -> HeaderValue
74where
75    U: fmt::Display,
76    P: fmt::Display,
77{
78    use base64::prelude::BASE64_STANDARD;
79    use base64::write::EncoderWriter;
80    use std::io::Write;
81
82    let mut buf = b"Basic ".to_vec();
83    {
84        let mut encoder = EncoderWriter::new(&mut buf, &BASE64_STANDARD);
85        let _ = write!(encoder, "{username}:");
86        if let Some(password) = password {
87            let _ = write!(encoder, "{password}");
88        }
89    }
90
91    let mut header = HeaderValue::from_bytes(&buf).expect("base64 is always valid HeaderValue");
92    header.set_sensitive(true);
93    header
94}