use std::fmt;
use std::future::Future;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use http::{HeaderName, HeaderValue};
use super::{Middleware, Next};
use crate::context::Context;
use crate::worker::Outcome;
#[derive(Clone)]
pub struct SetHeader {
name: HeaderName,
values: Arc<[HeaderValue]>,
next: Arc<AtomicUsize>,
}
impl SetHeader {
#[must_use]
pub fn new(name: HeaderName, value: HeaderValue) -> Self {
Self::rotate(name, [value])
}
#[must_use]
pub fn rotate(name: HeaderName, values: impl IntoIterator<Item = HeaderValue>) -> Self {
let values: Arc<[HeaderValue]> = values.into_iter().collect();
assert!(
!values.is_empty(),
"`SetHeader::rotate` needs a value for `{name}`"
);
Self {
name,
values,
next: Arc::default(),
}
}
fn value(&self) -> HeaderValue {
let index = self.next.fetch_add(1, Ordering::Relaxed) % self.values.len();
self.values[index].clone()
}
}
impl<C: Send + 'static> Middleware<C> for SetHeader {
fn handle(&self, mut cx: Context<C>, next: Next<C>) -> impl Future<Output = Outcome> + Send {
let headers = cx.request_mut().headers_mut();
if !headers.contains_key(&self.name) {
headers.insert(self.name.clone(), self.value());
}
next.run(cx)
}
}
impl fmt::Debug for SetHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SetHeader")
.field("name", &self.name)
.field("values", &self.values.len())
.finish_non_exhaustive()
}
}