diff --git a/Cargo.toml b/Cargo.toml index 9cd3304fd..fe5dfba02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ base64 = "0.9" bitflags = "1.0" failure = "0.1.1" h2 = "0.1" +fnv = "1.0.5" http = "^0.1.5" httparse = "1.2" http-range = "0.1" diff --git a/src/extensions.rs b/src/extensions.rs new file mode 100644 index 000000000..1c64623f2 --- /dev/null +++ b/src/extensions.rs @@ -0,0 +1,90 @@ +use std::any::{Any, TypeId}; +use std::collections::HashMap; +use std::fmt; +use std::hash::BuildHasherDefault; + +use fnv::FnvHasher; + +type AnyMap = HashMap, BuildHasherDefault>; + +/// A type map of request extensions. +pub struct Extensions { + map: AnyMap, +} + +impl Extensions { + /// Create an empty `Extensions`. + #[inline] + pub(crate) fn new() -> Extensions { + Extensions { + map: HashMap::default(), + } + } + + /// Insert a type into this `Extensions`. + /// + /// If a extension of this type already existed, it will + /// be returned. + pub fn insert(&mut self, val: T) { + self.map.insert(TypeId::of::(), Box::new(val)); + } + + /// Get a reference to a type previously inserted on this `Extensions`. + pub fn get(&self) -> Option<&T> { + self.map + .get(&TypeId::of::()) + .and_then(|boxed| (&**boxed as &(Any + 'static)).downcast_ref()) + } + + /// Get a mutable reference to a type previously inserted on this `Extensions`. + pub fn get_mut(&mut self) -> Option<&mut T> { + self.map + .get_mut(&TypeId::of::()) + .and_then(|boxed| (&mut **boxed as &mut (Any + 'static)).downcast_mut()) + } + + /// Remove a type from this `Extensions`. + /// + /// If a extension of this type existed, it will be returned. + pub fn remove(&mut self) -> Option { + self.map.remove(&TypeId::of::()).and_then(|boxed| { + //TODO: we can use unsafe and remove double checking the type id + (boxed as Box) + .downcast() + .ok() + .map(|boxed| *boxed) + }) + } + + /// Clear the `Extensions` of all inserted extensions. + #[inline] + pub fn clear(&mut self) { + self.map.clear(); + } +} + +impl fmt::Debug for Extensions { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Extensions").finish() + } +} + +#[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))); +} diff --git a/src/httprequest.rs b/src/httprequest.rs index 0cbdbb25c..dded35f68 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -9,12 +9,13 @@ use cookie::Cookie; use failure; use futures::{Async, Poll, Stream}; use futures_cpupool::CpuPool; -use http::{header, Extensions, HeaderMap, Method, StatusCode, Uri, Version}; +use http::{header, HeaderMap, Method, StatusCode, Uri, Version}; use tokio_io::AsyncRead; use url::{form_urlencoded, Url}; use body::Body; use error::{CookieParseError, PayloadError, UrlGenerationError}; +use extensions::Extensions; use handler::FromRequest; use httpmessage::HttpMessage; use httpresponse::{HttpResponse, HttpResponseBuilder}; diff --git a/src/lib.rs b/src/lib.rs index c3b9fc7af..b3e143a5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,6 +97,7 @@ extern crate time; extern crate bitflags; #[macro_use] extern crate failure; +extern crate fnv; #[macro_use] extern crate lazy_static; #[macro_use] @@ -155,6 +156,7 @@ mod application; mod body; mod context; mod de; +mod extensions; mod extractor; mod handler; mod header; @@ -188,6 +190,7 @@ pub use application::App; pub use body::{Binary, Body}; pub use context::HttpContext; pub use error::{Error, ResponseError, Result}; +pub use extensions::Extensions; pub use extractor::{Form, Path, Query}; pub use handler::{ AsyncResponder, Either, FromRequest, FutureResponse, Responder, State,