use std::fmt;
use http::{header::HeaderName, HeaderMap, HeaderValue, Request, Response};
pub mod request;
pub mod response;
#[doc(inline)]
pub use self::{
request::{SetRequestHeader, SetRequestHeaderLayer},
response::{SetResponseHeader, SetResponseHeaderLayer},
};
pub trait MakeHeaderValue<T> {
fn make_header_value(&mut self, message: &T) -> Option<HeaderValue>;
}
impl<F, T> MakeHeaderValue<T> for F
where
F: FnMut(&T) -> Option<HeaderValue>,
{
fn make_header_value(&mut self, message: &T) -> Option<HeaderValue> {
self(message)
}
}
impl<T> MakeHeaderValue<T> for HeaderValue {
fn make_header_value(&mut self, _message: &T) -> Option<HeaderValue> {
Some(self.clone())
}
}
impl<T> MakeHeaderValue<T> for Option<HeaderValue> {
fn make_header_value(&mut self, _message: &T) -> Option<HeaderValue> {
self.clone()
}
}
#[derive(Debug, Clone, Copy)]
enum InsertHeaderMode {
Override,
Append,
IfNotPresent,
}
impl InsertHeaderMode {
fn apply<T, M>(self, header_name: &HeaderName, target: &mut T, make: &mut M)
where
T: Headers,
M: MakeHeaderValue<T>,
{
match self {
InsertHeaderMode::Override => {
if let Some(value) = make.make_header_value(target) {
target.headers_mut().insert(header_name.clone(), value);
}
}
InsertHeaderMode::IfNotPresent => {
if !target.headers().contains_key(header_name) {
if let Some(value) = make.make_header_value(target) {
target.headers_mut().insert(header_name.clone(), value);
}
}
}
InsertHeaderMode::Append => {
if let Some(value) = make.make_header_value(target) {
target.headers_mut().append(header_name.clone(), value);
}
}
}
}
}
trait Headers {
fn headers(&self) -> &HeaderMap;
fn headers_mut(&mut self) -> &mut HeaderMap;
}
impl<B> Headers for Request<B> {
fn headers(&self) -> &HeaderMap {
Request::headers(self)
}
fn headers_mut(&mut self) -> &mut HeaderMap {
Request::headers_mut(self)
}
}
impl<B> Headers for Response<B> {
fn headers(&self) -> &HeaderMap {
Response::headers(self)
}
fn headers_mut(&mut self) -> &mut HeaderMap {
Response::headers_mut(self)
}
}
trait CloneableMakeHeaderValue<T>: MakeHeaderValue<T> + Send + Sync {
fn clone_box(&self) -> Box<dyn CloneableMakeHeaderValue<T>>;
}
impl<T, M> CloneableMakeHeaderValue<T> for M
where
M: MakeHeaderValue<T> + Clone + Send + Sync + 'static,
{
fn clone_box(&self) -> Box<dyn CloneableMakeHeaderValue<T>> {
Box::new(self.clone())
}
}
struct BoxedMakeHeaderValue<T>(Box<dyn CloneableMakeHeaderValue<T>>);
impl<T> BoxedMakeHeaderValue<T> {
fn new<M>(maker: M) -> Self
where
M: MakeHeaderValue<T> + Clone + Send + Sync + 'static,
{
Self(Box::new(maker))
}
}
impl<T> Clone for BoxedMakeHeaderValue<T> {
fn clone(&self) -> Self {
Self(self.0.clone_box())
}
}
impl<T> MakeHeaderValue<T> for BoxedMakeHeaderValue<T> {
fn make_header_value(&mut self, message: &T) -> Option<HeaderValue> {
self.0.make_header_value(message)
}
}
impl<T> fmt::Debug for BoxedMakeHeaderValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BoxedMakeHeaderValue").finish()
}
}
#[derive(Clone, Debug)]
pub struct HeaderMetadata<T> {
header_name: HeaderName,
make: BoxedMakeHeaderValue<T>,
}
impl<T> HeaderMetadata<T> {
fn new<M: MakeHeaderValue<T> + Clone + 'static + Send + Sync>(
header_name: HeaderName,
make: M,
) -> Self {
Self {
header_name,
make: BoxedMakeHeaderValue::new(make),
}
}
fn build_config(self, mode: InsertHeaderMode) -> HeaderInsertionConfig<T> {
HeaderInsertionConfig {
header_name: self.header_name,
make: self.make,
mode,
}
}
}
impl<T, M> From<(HeaderName, M)> for HeaderMetadata<T>
where
M: MakeHeaderValue<T> + Clone + 'static + Send + Sync,
{
fn from((header_name, make): (HeaderName, M)) -> Self {
HeaderMetadata::new(header_name, make)
}
}
struct HeaderInsertionConfig<T> {
header_name: HeaderName,
make: BoxedMakeHeaderValue<T>,
mode: InsertHeaderMode,
}
impl<T> Clone for HeaderInsertionConfig<T>
where
BoxedMakeHeaderValue<T>: Clone,
{
fn clone(&self) -> Self {
Self {
header_name: self.header_name.clone(),
make: self.make.clone(),
mode: self.mode,
}
}
}
impl<T> fmt::Debug for HeaderInsertionConfig<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("HeaderInsertionConfig")
.field("header_name", &self.header_name)
.field("mode", &self.mode)
.field("make", &"BoxedMakeHeaderValue")
.finish()
}
}