1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-12-18 14:16:47 +00:00

use custom cloneany trait

This commit is contained in:
Rob Ede 2021-07-14 00:20:45 +01:00
parent 3b2e2acb6c
commit 6bb33ec5db
No known key found for this signature in database
GPG key ID: 97C636207D3EF933
6 changed files with 118 additions and 69 deletions

View file

@ -54,6 +54,7 @@ bitflags = "1.2"
bytes = "1" bytes = "1"
bytestring = "1" bytestring = "1"
derive_more = "0.99.5" derive_more = "0.99.5"
dyn-clone = "1"
encoding_rs = "0.8" encoding_rs = "0.8"
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] } futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] } futures-util = { version = "0.3.7", default-features = false, features = ["alloc", "sink"] }

View file

@ -3,7 +3,15 @@ use std::{error::Error as StdError, fmt, marker::PhantomData, net, rc::Rc};
use actix_codec::Framed; use actix_codec::Framed;
use actix_service::{IntoServiceFactory, Service, ServiceFactory}; use actix_service::{IntoServiceFactory, Service, ServiceFactory};
use crate::{ConnectCallback, Extensions, Request, Response, body::{AnyBody, MessageBody}, config::{KeepAlive, ServiceConfig}, h1::{self, ExpectHandler, H1Service, UpgradeHandler}, h2::H2Service, service::HttpService}; use crate::{
body::{AnyBody, MessageBody},
config::{KeepAlive, ServiceConfig},
extensions::CloneableExtensions,
h1::{self, ExpectHandler, H1Service, UpgradeHandler},
h2::H2Service,
service::HttpService,
ConnectCallback, Request, Response,
};
/// A HTTP service builder /// A HTTP service builder
/// ///
@ -160,7 +168,7 @@ where
/// and handlers. /// and handlers.
pub fn on_connect_ext<F>(mut self, f: F) -> Self pub fn on_connect_ext<F>(mut self, f: F) -> Self
where where
F: Fn(&T, &mut Extensions) + 'static, F: Fn(&T, &mut CloneableExtensions) + 'static,
{ {
self.on_connect_ext = Some(Rc::new(f)); self.on_connect_ext = Some(Rc::new(f));
self self

View file

@ -1,7 +1,6 @@
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
fmt, mem, fmt, mem,
rc::Rc,
}; };
use ahash::AHashMap; use ahash::AHashMap;
@ -13,7 +12,7 @@ use ahash::AHashMap;
pub struct Extensions { pub struct Extensions {
/// Use FxHasher with a std HashMap with for faster /// Use FxHasher with a std HashMap with for faster
/// lookups on the small `TypeId` (u64 equivalent) keys. /// lookups on the small `TypeId` (u64 equivalent) keys.
map: AHashMap<TypeId, Rc<dyn Any>>, map: AHashMap<TypeId, Box<dyn Any>>,
} }
impl Extensions { impl Extensions {
@ -39,8 +38,8 @@ impl Extensions {
/// ``` /// ```
pub fn insert<T: 'static>(&mut self, val: T) -> Option<T> { pub fn insert<T: 'static>(&mut self, val: T) -> Option<T> {
self.map self.map
.insert(TypeId::of::<T>(), Rc::new(val)) .insert(TypeId::of::<T>(), Box::new(val))
.and_then(downcast_rc) .and_then(downcast_owned)
} }
/// Check if map contains an item of a given type. /// Check if map contains an item of a given type.
@ -71,19 +70,19 @@ impl Extensions {
.and_then(|boxed| boxed.downcast_ref()) .and_then(|boxed| boxed.downcast_ref())
} }
// /// Get a mutable reference to an item of a given type. /// Get a mutable reference to an item of a given type.
// /// ///
// /// ``` /// ```
// /// # use actix_http::Extensions; /// # use actix_http::Extensions;
// /// let mut map = Extensions::new(); /// let mut map = Extensions::new();
// /// map.insert(1u32); /// map.insert(1u32);
// /// assert_eq!(map.get_mut::<u32>(), Some(&mut 1u32)); /// assert_eq!(map.get_mut::<u32>(), Some(&mut 1u32));
// /// ``` /// ```
// pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> { pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
// self.map self.map
// .get_mut(&TypeId::of::<T>()) .get_mut(&TypeId::of::<T>())
// .and_then(|boxed| boxed.downcast_mut()) .and_then(|boxed| boxed.downcast_mut())
// } }
/// Remove an item from the map of a given type. /// Remove an item from the map of a given type.
/// ///
@ -100,7 +99,7 @@ impl Extensions {
/// assert!(!map.contains::<u32>()); /// assert!(!map.contains::<u32>());
/// ``` /// ```
pub fn remove<T: 'static>(&mut self) -> Option<T> { pub fn remove<T: 'static>(&mut self) -> Option<T> {
self.map.remove(&TypeId::of::<T>()).and_then(downcast_rc) self.map.remove(&TypeId::of::<T>()).and_then(downcast_owned)
} }
/// Clear the `Extensions` of all inserted extensions. /// Clear the `Extensions` of all inserted extensions.
@ -131,9 +130,9 @@ impl Extensions {
} }
/// Sets (or overrides) items from cloneable extensions map into this map. /// Sets (or overrides) items from cloneable extensions map into this map.
pub(crate) fn clone_from(&mut self, other: &Self) { pub(crate) fn clone_from(&mut self, other: &CloneableExtensions) {
for (k, val) in &other.map { for (k, val) in &other.map {
self.map.insert(*k, Rc::clone(val)); self.map.insert(*k, (**val).clone_to_any());
} }
} }
} }
@ -148,48 +147,86 @@ fn downcast_owned<T: 'static>(boxed: Box<dyn Any>) -> Option<T> {
boxed.downcast().ok().map(|boxed| *boxed) boxed.downcast().ok().map(|boxed| *boxed)
} }
fn downcast_rc<T: 'static>(boxed: Rc<dyn Any>) -> Option<T> { // fn downcast_rc<T: 'static>(boxed: Rc<dyn Any>) -> Option<T> {
boxed // boxed
.downcast() // .downcast()
.ok() // .ok()
.and_then(|boxed| Rc::try_unwrap(boxed).ok()) // .and_then(|boxed| Rc::try_unwrap(boxed).ok())
// }
#[doc(hidden)]
pub trait CloneToAny {
/// Clone `self` into a new `Box<Any>` object.
fn clone_to_any(&self) -> Box<dyn Any>;
/// Clone `self` into a new `Box<CloneAny>` object.
fn clone_to_clone_any(&self) -> Box<dyn CloneAny>;
} }
// /// A type map for request extensions. impl<T: Clone + Any> CloneToAny for T {
// /// #[inline]
// /// All entries into this map must be owned types (or static references). fn clone_to_any(&self) -> Box<dyn Any> {
// #[derive(Default)] Box::new(self.clone())
// pub struct CloneableExtensions { }
// /// Use FxHasher with a std HashMap with for faster
// /// lookups on the small `TypeId` (u64 equivalent) keys.
// map: AHashMap<TypeId, Rc<dyn Any>>,
// }
// impl CloneableExtensions { #[inline]
// pub(crate) fn priv_clone(&self) -> CloneableExtensions { fn clone_to_clone_any(&self) -> Box<dyn CloneAny> {
// Self { Box::new(self.clone())
// map: self.map.clone(), }
// } }
// }
// /// Insert an item into the map. /// An [`Any`] trait with an additional [`Clone`] requirement.
// /// pub trait CloneAny: Any + CloneToAny {}
// /// If an item of this type was already stored, it will be replaced and returned. impl<T: Any + Clone> CloneAny for T {}
// ///
// /// ``` impl Clone for Box<dyn CloneAny> {
// /// # use actix_http::Extensions; fn clone(&self) -> Self {
// /// let mut map = Extensions::new(); (**self).clone_to_clone_any()
// /// assert_eq!(map.insert(""), None); }
// /// assert_eq!(map.insert(1u32), None); }
// /// assert_eq!(map.insert(2u32), Some(1u32));
// /// assert_eq!(*map.get::<u32>().unwrap(), 2u32); trait UncheckedAnyExt {
// /// ``` #[inline]
// pub fn insert<T: Clone + 'static>(&mut self, val: T) -> Option<T> { unsafe fn downcast_unchecked<T: 'static>(self: Box<Self>) -> Box<T> {
// self.map Box::from_raw(Box::into_raw(self) as *mut T)
// .insert(TypeId::of::<T>(), Rc::new(val)) }
// .and_then(downcast_rc) }
// }
// } impl UncheckedAnyExt for dyn CloneAny {}
fn downcast_cloneable<T: 'static>(boxed: Box<dyn CloneAny>) -> T {
*unsafe { UncheckedAnyExt::downcast_unchecked::<T>(boxed) }
}
/// A type map for request extensions.
///
/// All entries into this map must be owned types (or static references).
#[derive(Default)]
pub struct CloneableExtensions {
/// Use FxHasher with a std HashMap with for faster
/// lookups on the small `TypeId` (u64 equivalent) keys.
map: AHashMap<TypeId, Box<dyn CloneAny>>,
}
impl CloneableExtensions {
/// 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::<u32>().unwrap(), 2u32);
/// ```
pub fn insert<T: CloneAny + 'static>(&mut self, val: T) -> Option<T> {
self.map
.insert(TypeId::of::<T>(), Box::new(val))
.and_then(downcast_cloneable)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -55,7 +55,7 @@ pub mod ws;
pub use self::builder::HttpServiceBuilder; pub use self::builder::HttpServiceBuilder;
pub use self::config::{KeepAlive, ServiceConfig}; pub use self::config::{KeepAlive, ServiceConfig};
pub use self::error::Error; pub use self::error::Error;
pub use self::extensions::Extensions; pub use self::extensions::{CloneableExtensions, Extensions};
pub use self::header::ContentEncoding; pub use self::header::ContentEncoding;
pub use self::http_message::HttpMessage; pub use self::http_message::HttpMessage;
pub use self::message::ConnectionType; pub use self::message::ConnectionType;
@ -98,13 +98,13 @@ pub enum Protocol {
Http3, Http3,
} }
type ConnectCallback<IO> = dyn Fn(&IO, &mut Extensions); type ConnectCallback<IO> = dyn Fn(&IO, &mut CloneableExtensions);
/// Container for data that extract with ConnectCallback. /// Container for data that extract with ConnectCallback.
/// ///
/// # Implementation Details /// # Implementation Details
/// Uses Option to reduce necessary allocations when merging with request extensions. /// Uses Option to reduce necessary allocations when merging with request extensions.
pub(crate) struct OnConnectData(Option<Extensions>); pub(crate) struct OnConnectData(Option<CloneableExtensions>);
impl Default for OnConnectData { impl Default for OnConnectData {
fn default() -> Self { fn default() -> Self {
@ -119,7 +119,7 @@ impl OnConnectData {
on_connect_ext: Option<&ConnectCallback<T>>, on_connect_ext: Option<&ConnectCallback<T>>,
) -> Self { ) -> Self {
let ext = on_connect_ext.map(|handler| { let ext = on_connect_ext.map(|handler| {
let mut extensions = Extensions::default(); let mut extensions = CloneableExtensions::default();
handler(io, &mut extensions); handler(io, &mut extensions);
extensions extensions
}); });

View file

@ -6,7 +6,8 @@
use std::{any::Any, io, net::SocketAddr}; use std::{any::Any, io, net::SocketAddr};
use actix_web::{dev::Extensions, rt::net::TcpStream, web, App, HttpServer}; use actix_web::{rt::net::TcpStream, web, App, HttpServer};
use actix_http::CloneableExtensions;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct ConnectionInfo { struct ConnectionInfo {
@ -22,7 +23,7 @@ async fn route_whoami(conn_info: web::ReqData<ConnectionInfo>) -> String {
) )
} }
fn get_conn_info(connection: &dyn Any, data: &mut Extensions) { fn get_conn_info(connection: &dyn Any, data: &mut CloneableExtensions) {
if let Some(sock) = connection.downcast_ref::<TcpStream>() { if let Some(sock) = connection.downcast_ref::<TcpStream>() {
data.insert(ConnectionInfo { data.insert(ConnectionInfo {
bind: sock.local_addr().unwrap(), bind: sock.local_addr().unwrap(),

View file

@ -8,7 +8,9 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
use actix_http::{body::MessageBody, Extensions, HttpService, KeepAlive, Request, Response}; use actix_http::{
body::MessageBody, CloneableExtensions, HttpService, KeepAlive, Request, Response,
};
use actix_server::{Server, ServerBuilder}; use actix_server::{Server, ServerBuilder};
use actix_service::{ use actix_service::{
map_config, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _, map_config, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt as _,
@ -65,7 +67,7 @@ where
backlog: u32, backlog: u32,
sockets: Vec<Socket>, sockets: Vec<Socket>,
builder: ServerBuilder, builder: ServerBuilder,
on_connect_fn: Option<Arc<dyn Fn(&dyn Any, &mut Extensions) + Send + Sync>>, on_connect_fn: Option<Arc<dyn Fn(&dyn Any, &mut CloneableExtensions) + Send + Sync>>,
_phantom: PhantomData<(S, B)>, _phantom: PhantomData<(S, B)>,
} }
@ -115,7 +117,7 @@ where
/// See `on_connect` example for additional details. /// See `on_connect` example for additional details.
pub fn on_connect<CB>(self, f: CB) -> HttpServer<F, I, S, B> pub fn on_connect<CB>(self, f: CB) -> HttpServer<F, I, S, B>
where where
CB: Fn(&dyn Any, &mut Extensions) + Send + Sync + 'static, CB: Fn(&dyn Any, &mut CloneableExtensions) + Send + Sync + 'static,
{ {
HttpServer { HttpServer {
factory: self.factory, factory: self.factory,