1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 16:02:59 +01:00

add unsafe checks #331

This commit is contained in:
Nikolay Kim 2018-06-21 11:20:21 +06:00
parent c2c4a5ba3f
commit ebc59cf7b9
2 changed files with 78 additions and 24 deletions

View File

@ -111,8 +111,18 @@ where
T: FromRequest<S>, T: FromRequest<S>,
S: 'static, S: 'static,
{ {
hnd: Rc<UnsafeCell<F>>, hnd: Rc<WithHnd<T, S, F, R>>,
cfg: ExtractorConfig<S, T>, cfg: ExtractorConfig<S, T>,
}
pub struct WithHnd<T, S, F, R>
where
F: Fn(T) -> R,
T: FromRequest<S>,
S: 'static,
{
hnd: Rc<UnsafeCell<F>>,
_t: PhantomData<T>,
_s: PhantomData<S>, _s: PhantomData<S>,
} }
@ -125,8 +135,11 @@ where
pub fn new(f: F, cfg: ExtractorConfig<S, T>) -> Self { pub fn new(f: F, cfg: ExtractorConfig<S, T>) -> Self {
With { With {
cfg, cfg,
hnd: Rc::new(UnsafeCell::new(f)), hnd: Rc::new(WithHnd {
_s: PhantomData, hnd: Rc::new(UnsafeCell::new(f)),
_t: PhantomData,
_s: PhantomData,
}),
} }
} }
} }
@ -166,7 +179,7 @@ where
S: 'static, S: 'static,
{ {
started: bool, started: bool,
hnd: Rc<UnsafeCell<F>>, hnd: Rc<WithHnd<T, S, F, R>>,
cfg: ExtractorConfig<S, T>, cfg: ExtractorConfig<S, T>,
req: HttpRequest<S>, req: HttpRequest<S>,
fut1: Option<Box<Future<Item = T, Error = Error>>>, fut1: Option<Box<Future<Item = T, Error = Error>>>,
@ -206,20 +219,28 @@ where
} }
}; };
let hnd: &mut F = unsafe { &mut *self.hnd.get() }; let fut = {
let item = match (*hnd)(item).respond_to(&self.req) { // clone handler, inicrease ref counter
Ok(item) => item.into(), let h = self.hnd.as_ref().hnd.clone();
Err(e) => return Err(e.into()), // Enforce invariants before entering unsafe code.
}; // Only two references could exists With struct owns one, and line above
if Rc::weak_count(&h) != 0 && Rc::strong_count(&h) != 2 {
match item.into() { panic!("Multiple copies of handler are in use")
AsyncResultItem::Err(err) => Err(err),
AsyncResultItem::Ok(resp) => Ok(Async::Ready(resp)),
AsyncResultItem::Future(fut) => {
self.fut2 = Some(fut);
self.poll()
} }
} let hnd: &mut F = unsafe { &mut *h.as_ref().get() };
let item = match (*hnd)(item).respond_to(&self.req) {
Ok(item) => item.into(),
Err(e) => return Err(e.into()),
};
match item.into() {
AsyncResultItem::Err(err) => return Err(err),
AsyncResultItem::Ok(resp) => return Ok(Async::Ready(resp)),
AsyncResultItem::Future(fut) => fut,
}
};
self.fut2 = Some(fut);
self.poll()
} }
} }
@ -232,9 +253,8 @@ where
T: FromRequest<S>, T: FromRequest<S>,
S: 'static, S: 'static,
{ {
hnd: Rc<UnsafeCell<F>>, hnd: Rc<WithHnd<T, S, F, R>>,
cfg: ExtractorConfig<S, T>, cfg: ExtractorConfig<S, T>,
_s: PhantomData<S>,
} }
impl<T, S, F, R, I, E> WithAsync<T, S, F, R, I, E> impl<T, S, F, R, I, E> WithAsync<T, S, F, R, I, E>
@ -249,8 +269,11 @@ where
pub fn new(f: F, cfg: ExtractorConfig<S, T>) -> Self { pub fn new(f: F, cfg: ExtractorConfig<S, T>) -> Self {
WithAsync { WithAsync {
cfg, cfg,
hnd: Rc::new(UnsafeCell::new(f)), hnd: Rc::new(WithHnd {
_s: PhantomData, hnd: Rc::new(UnsafeCell::new(f)),
_s: PhantomData,
_t: PhantomData,
}),
} }
} }
} }
@ -295,7 +318,7 @@ where
S: 'static, S: 'static,
{ {
started: bool, started: bool,
hnd: Rc<UnsafeCell<F>>, hnd: Rc<WithHnd<T, S, F, R>>,
cfg: ExtractorConfig<S, T>, cfg: ExtractorConfig<S, T>,
req: HttpRequest<S>, req: HttpRequest<S>,
fut1: Option<Box<Future<Item = T, Error = Error>>>, fut1: Option<Box<Future<Item = T, Error = Error>>>,
@ -356,8 +379,17 @@ where
} }
}; };
let hnd: &mut F = unsafe { &mut *self.hnd.get() }; self.fut2 = {
self.fut2 = Some((*hnd)(item)); // clone handler, inicrease ref counter
let h = self.hnd.as_ref().hnd.clone();
// Enforce invariants before entering unsafe code.
// Only two references could exists With struct owns one, and line above
if Rc::weak_count(&h) != 0 && Rc::strong_count(&h) != 2 {
panic!("Multiple copies of handler are in use")
}
let hnd: &mut F = unsafe { &mut *h.as_ref().get() };
Some((*hnd)(item))
};
self.poll() self.poll()
} }
} }

View File

@ -42,6 +42,28 @@ fn test_path_extractor() {
assert_eq!(bytes, Bytes::from_static(b"Welcome test!")); assert_eq!(bytes, Bytes::from_static(b"Welcome test!"));
} }
#[test]
fn test_async_handler() {
let mut srv = test::TestServer::new(|app| {
app.resource("/{username}/index.html", |r| {
r.route().with(|p: Path<PParam>| {
Delay::new(Instant::now() + Duration::from_millis(10))
.and_then(move |_| Ok(format!("Welcome {}!", p.username)))
.responder()
})
});
});
// client request
let request = srv.get().uri(srv.url("/test/index.html")).finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success());
// read response
let bytes = srv.execute(response.body()).unwrap();
assert_eq!(bytes, Bytes::from_static(b"Welcome test!"));
}
#[test] #[test]
fn test_query_extractor() { fn test_query_extractor() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {