use std::{ any::{Any, TypeId}, fmt, }; use ahash::AHashMap; /// A type map for request extensions. /// /// All entries into this map must be owned types (or static references). #[derive(Default)] pub struct Extensions { /// Use AHasher with a std HashMap with for faster lookups on the small `TypeId` keys. map: AHashMap>, } impl Extensions { /// Creates an empty `Extensions`. #[inline] pub fn new() -> Extensions { Extensions { map: AHashMap::default(), } } /// Insert an item into the map. /// /// If an item of this type was already stored, it will be replaced and returned. /// /// ``` /// # use actix_http::Extensions; /// let mut map = Extensions::new(); /// assert_eq!(map.insert(""), None); /// assert_eq!(map.insert(1u32), None); /// assert_eq!(map.insert(2u32), Some(1u32)); /// assert_eq!(*map.get::().unwrap(), 2u32); /// ``` pub fn insert(&mut self, val: T) -> Option { self.map .insert(TypeId::of::(), Box::new(val)) .and_then(downcast_owned) } /// Check if map contains an item of a given type. /// /// ``` /// # use actix_http::Extensions; /// let mut map = Extensions::new(); /// assert!(!map.contains::()); /// /// assert_eq!(map.insert(1u32), None); /// assert!(map.contains::()); /// ``` pub fn contains(&self) -> bool { self.map.contains_key(&TypeId::of::()) } /// Get a reference to an item of a given type. /// /// ``` /// # use actix_http::Extensions; /// let mut map = Extensions::new(); /// map.insert(1u32); /// assert_eq!(map.get::(), Some(&1u32)); /// ``` pub fn get(&self) -> Option<&T> { self.map .get(&TypeId::of::()) .and_then(|boxed| boxed.downcast_ref()) } /// Get a mutable reference to an item of a given type. /// /// ``` /// # use actix_http::Extensions; /// let mut map = Extensions::new(); /// map.insert(1u32); /// assert_eq!(map.get_mut::(), Some(&mut 1u32)); /// ``` pub fn get_mut(&mut self) -> Option<&mut T> { self.map .get_mut(&TypeId::of::()) .and_then(|boxed| boxed.downcast_mut()) } /// Remove an item from the map of a given type. /// /// If an item of this type was already stored, it will be returned. /// /// ``` /// # use actix_http::Extensions; /// let mut map = Extensions::new(); /// /// map.insert(1u32); /// assert_eq!(map.get::(), Some(&1u32)); /// /// assert_eq!(map.remove::(), Some(1u32)); /// assert!(!map.contains::()); /// ``` pub fn remove(&mut self) -> Option { self.map.remove(&TypeId::of::()).and_then(downcast_owned) } /// Clear the `Extensions` of all inserted extensions. /// /// ``` /// # use actix_http::Extensions; /// let mut map = Extensions::new(); /// /// map.insert(1u32); /// assert!(map.contains::()); /// /// map.clear(); /// assert!(!map.contains::()); /// ``` #[inline] pub fn clear(&mut self) { self.map.clear(); } /// Extends self with the items from another `Extensions`. pub fn extend(&mut self, other: Extensions) { self.map.extend(other.map); } /// Sets (or overrides) items from cloneable extensions map into this map. pub(crate) fn clone_from(&mut self, other: &CloneableExtensions) { for (k, val) in &other.map { self.map.insert(*k, (**val).clone_to_any()); } } } impl fmt::Debug for Extensions { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Extensions").finish() } } fn downcast_owned(boxed: Box) -> Option { boxed.downcast().ok().map(|boxed| *boxed) } #[doc(hidden)] pub trait CloneToAny { /// Cast `self` into an `Any` reference. #[cfg(test)] fn any_ref(&self) -> &dyn Any; /// Clone `self` to a new `Box` object. fn clone_to_any(&self) -> Box; /// Clone `self` to a new `Box` object. fn clone_to_clone_any(&self) -> Box; } impl CloneToAny for T { #[cfg(test)] fn any_ref(&self) -> &dyn Any { &*self } #[inline] fn clone_to_any(&self) -> Box { Box::new(self.clone()) } #[inline] fn clone_to_clone_any(&self) -> Box { Box::new(self.clone()) } } /// An [`Any`] trait with an additional [`Clone`] requirement. pub trait CloneAny: CloneToAny + Any {} impl CloneAny for T {} impl Clone for Box { #[inline] fn clone(&self) -> Self { (**self).clone_to_clone_any() } } trait UncheckedAnyExt { /// # Safety /// Caller must ensure type `T` is true type. #[inline] unsafe fn downcast_unchecked(self: Box) -> Box { Box::from_raw(Box::into_raw(self) as *mut T) } } impl UncheckedAnyExt for dyn CloneAny {} /// A type map for `on_connect` extensions. /// /// All entries into this map must be owned types and implement `Clone` trait. /// /// Many requests can be processed for each connection but the `on_connect` will only be run once /// when the connection is opened. Therefore, items added to this special map type need to be cloned /// into the regular extensions map for each request. Most useful connection information types are /// cloneable already but you can use reference counted wrappers if not. #[derive(Default)] pub struct CloneableExtensions { /// Use AHasher with a std HashMap with for faster lookups on the small `TypeId` keys. map: AHashMap>, } impl CloneableExtensions { /// Insert an item into the map. /// /// If an item of this type was already stored, it will be replaced and returned. /// /// # Examples /// ``` /// # use actix_http::Extensions; /// let mut map = Extensions::new(); /// assert_eq!(map.insert(""), None); /// assert_eq!(map.insert(1u32), None); /// assert_eq!(map.insert(2u32), Some(1u32)); /// assert_eq!(*map.get::().unwrap(), 2u32); /// ``` pub fn insert(&mut self, val: T) -> Option { self.map .insert(TypeId::of::(), Box::new(val)) .map(|boxed| { // Safety: // Box is owned and `T` is known to be true type from map. *unsafe { UncheckedAnyExt::downcast_unchecked::(boxed) } }) } #[cfg(test)] fn get(&self) -> Option<&T> { self.map .get(&TypeId::of::()) .and_then(|boxed| boxed.as_ref().any_ref().downcast_ref()) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_remove() { let mut map = Extensions::new(); map.insert::(123); assert!(map.get::().is_some()); map.remove::(); assert!(map.get::().is_none()); } #[test] fn test_clear() { let mut map = Extensions::new(); map.insert::(8); map.insert::(16); map.insert::(32); assert!(map.contains::()); assert!(map.contains::()); assert!(map.contains::()); map.clear(); assert!(!map.contains::()); assert!(!map.contains::()); assert!(!map.contains::()); map.insert::(10); assert_eq!(*map.get::().unwrap(), 10); } #[test] fn test_integers() { static A: u32 = 8; let mut map = Extensions::new(); map.insert::(8); map.insert::(16); map.insert::(32); map.insert::(64); map.insert::(128); map.insert::(8); map.insert::(16); map.insert::(32); map.insert::(64); map.insert::(128); map.insert::<&'static u32>(&A); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::().is_some()); assert!(map.get::<&'static u32>().is_some()); } #[test] fn test_composition() { struct Magi(pub T); struct Madoka { pub god: bool, } struct Homura { pub attempts: usize, } struct Mami { pub guns: usize, } let mut map = Extensions::new(); map.insert(Magi(Madoka { god: false })); map.insert(Magi(Homura { attempts: 0 })); map.insert(Magi(Mami { guns: 999 })); assert!(!map.get::>().unwrap().0.god); assert_eq!(0, map.get::>().unwrap().0.attempts); assert_eq!(999, map.get::>().unwrap().0.guns); } #[test] fn test_extensions() { #[derive(Debug, PartialEq)] struct MyType(i32); let mut extensions = Extensions::new(); extensions.insert(5i32); extensions.insert(MyType(10)); assert_eq!(extensions.get(), Some(&5i32)); assert_eq!(extensions.get_mut(), Some(&mut 5i32)); assert_eq!(extensions.remove::(), Some(5i32)); assert!(extensions.get::().is_none()); assert_eq!(extensions.get::(), None); assert_eq!(extensions.get(), Some(&MyType(10))); } #[test] fn test_extend() { #[derive(Debug, PartialEq)] struct MyType(i32); let mut extensions = Extensions::new(); extensions.insert(5i32); extensions.insert(MyType(10)); let mut other = Extensions::new(); other.insert(15i32); other.insert(20u8); extensions.extend(other); assert_eq!(extensions.get(), Some(&15i32)); assert_eq!(extensions.get_mut(), Some(&mut 15i32)); assert_eq!(extensions.remove::(), Some(15i32)); assert!(extensions.get::().is_none()); assert_eq!(extensions.get::(), None); assert_eq!(extensions.get(), Some(&MyType(10))); assert_eq!(extensions.get(), Some(&20u8)); assert_eq!(extensions.get_mut(), Some(&mut 20u8)); } #[test] fn test_clone_from() { #[derive(Clone)] struct NonCopy { num: u8, } let mut ext = Extensions::new(); ext.insert(2isize); assert_eq!(ext.get::(), Some(&2isize)); let mut more_ext = CloneableExtensions::default(); more_ext.insert(3isize); more_ext.insert(3usize); more_ext.insert(NonCopy { num: 8 }); ext.clone_from(&more_ext); assert_eq!(ext.get::(), Some(&3isize)); assert_eq!(ext.get::(), Some(&3usize)); assert_eq!(more_ext.get::(), Some(&3isize)); assert_eq!(more_ext.get::(), Some(&3usize)); assert!(ext.get::().is_some()); assert!(more_ext.get::().is_some()); } #[test] fn boxes_not_aliased() { let a: Box = Box::new(42); let b = a.clone_to_clone_any(); assert_ne!(Box::into_raw(a) as *const (), Box::into_raw(b) as *const ()); let a: Box = Box::new(42); let b = a.clone_to_any(); assert_ne!(Box::into_raw(a) as *const (), Box::into_raw(b) as *const ()); } }