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:
parent
3b2e2acb6c
commit
6bb33ec5db
6 changed files with 118 additions and 69 deletions
|
@ -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"] }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
});
|
});
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue