diff --git a/Cargo.toml b/Cargo.toml index 6c0f0bc8cd8..6c9d0348734 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ actix-service = "1.0.0-alpha.1" actix-utils = "0.5.0-alpha.1" actix-router = "0.1.5" actix-rt = "1.0.0-alpha.1" -actix-web-codegen = "0.1.2" +actix-web-codegen = "0.2.0-alpha.1" actix-http = "0.3.0-alpha.1" actix-server = "0.8.0-alpha.1" actix-server-config = "0.3.0-alpha.1" diff --git a/MIGRATION.md b/MIGRATION.md index 2f0f369ad03..9709b4f046a 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,3 +1,10 @@ +## 2.0.0 + +* Sync handlers has been removed. `.to_async()` methtod has been renamed to `.to()` + + replace `fn` with `async fn` to convert sync handler to async + + ## 1.0.1 * Cors middleware has been moved to `actix-cors` crate diff --git a/README.md b/README.md index 99b7b176020..cee8b73c07c 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Actix web is a simple, pragmatic and extremely fast web framework for Rust. ```rust use actix_web::{web, App, HttpServer, Responder}; -fn index(info: web::Path<(u32, String)>) -> impl Responder { +async fn index(info: web::Path<(u32, String)>) -> impl Responder { format!("Hello {}! id:{}", info.1, info.0) } diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index d05505d8083..31876813b2f 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -1,11 +1,14 @@ //! Http response use std::cell::{Ref, RefMut}; +use std::future::Future; use std::io::Write; +use std::pin::Pin; +use std::task::{Context, Poll}; use std::{fmt, str}; use bytes::{BufMut, Bytes, BytesMut}; use futures::future::{ok, Ready}; -use futures::Stream; +use futures::stream::Stream; use serde::Serialize; use serde_json; @@ -280,15 +283,20 @@ impl fmt::Debug for Response { } } -// impl IntoFuture for Response { -// type Item = Response; -// type Error = Error; -// type Future = FutureResult; +impl Future for Response { + type Output = Result; -// fn into_future(self) -> Self::Future { -// ok(self) -// } -// } + fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(Ok(Response { + head: std::mem::replace( + &mut self.head, + BoxedResponseHead::new(StatusCode::OK), + ), + body: self.body.take_body(), + error: self.error.take(), + })) + } +} pub struct CookieIter<'a> { iter: header::GetAll<'a>, @@ -757,15 +765,13 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder { } } -// impl IntoFuture for ResponseBuilder { -// type Item = Response; -// type Error = Error; -// type Future = FutureResult; +impl Future for ResponseBuilder { + type Output = Result; -// fn into_future(mut self) -> Self::Future { -// ok(self.finish()) -// } -// } + fn poll(mut self: Pin<&mut Self>, _: &mut Context) -> Poll { + Poll::Ready(Ok(self.finish())) + } +} impl fmt::Debug for ResponseBuilder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/actix-web-codegen/src/route.rs b/actix-web-codegen/src/route.rs index e792a7f0a32..f8e2496c48a 100644 --- a/actix-web-codegen/src/route.rs +++ b/actix-web-codegen/src/route.rs @@ -13,7 +13,7 @@ enum ResourceType { impl ToTokens for ResourceType { fn to_tokens(&self, stream: &mut TokenStream2) { let ident = match self { - ResourceType::Async => "to_async", + ResourceType::Async => "to", ResourceType::Sync => "to", }; let ident = Ident::new(ident, Span::call_site()); diff --git a/examples/basic.rs b/examples/basic.rs index d25db789553..6d9a4dcd866 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,9 +1,7 @@ -use actix_web::{ - get, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, -}; +use actix_web::{get, middleware, web, App, HttpRequest, HttpResponse, HttpServer}; #[get("/resource1/{name}/index.html")] -fn index(req: HttpRequest, name: web::Path) -> String { +async fn index(req: HttpRequest, name: web::Path) -> String { println!("REQ: {:?}", req); format!("Hello: {}!\r\n", name) } @@ -14,7 +12,7 @@ async fn index_async(req: HttpRequest) -> &'static str { } #[get("/")] -fn no_params() -> &'static str { +async fn no_params() -> &'static str { "Hello world!\r\n" } @@ -37,9 +35,9 @@ fn main() -> std::io::Result<()> { .default_service( web::route().to(|| HttpResponse::MethodNotAllowed()), ) - .route(web::get().to_async(index_async)), + .route(web::get().to(index_async)), ) - .service(web::resource("/test1.html").to(|| "Test\r\n")) + .service(web::resource("/test1.html").to(|| async { "Test\r\n" })) }) .bind("127.0.0.1:8080")? .workers(1) diff --git a/examples/uds.rs b/examples/uds.rs index 7da41a2c5bb..fc6a58de1c3 100644 --- a/examples/uds.rs +++ b/examples/uds.rs @@ -3,7 +3,7 @@ use actix_web::{ }; #[get("/resource1/{name}/index.html")] -fn index(req: HttpRequest, name: web::Path) -> String { +async fn index(req: HttpRequest, name: web::Path) -> String { println!("REQ: {:?}", req); format!("Hello: {}!\r\n", name) } @@ -14,11 +14,11 @@ async fn index_async(req: HttpRequest) -> Result<&'static str, Error> { } #[get("/")] -fn no_params() -> &'static str { +async fn no_params() -> &'static str { "Hello world!\r\n" } -#[cfg(feature = "uds")] +#[cfg(unix)] fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); env_logger::init(); @@ -27,7 +27,7 @@ fn main() -> std::io::Result<()> { App::new() .wrap(middleware::DefaultHeaders::new().header("X-Version", "0.2")) .wrap(middleware::Compress::default()) - // .wrap(middleware::Logger::default()) + .wrap(middleware::Logger::default()) .service(index) .service(no_params) .service( @@ -36,16 +36,16 @@ fn main() -> std::io::Result<()> { middleware::DefaultHeaders::new().header("X-Version-R2", "0.3"), ) .default_service( - web::route().to(|| ok(HttpResponse::MethodNotAllowed())), + web::route().to(|| HttpResponse::MethodNotAllowed()), ) - .route(web::get().to_async(index_async)), + .route(web::get().to(index_async)), ) - .service(web::resource("/test1.html").to(|| "Test\r\n")) + .service(web::resource("/test1.html").to(|| async { "Test\r\n" })) }) .bind_uds("/Users/fafhrd91/uds-test")? .workers(1) .run() } -#[cfg(not(feature = "uds"))] +#[cfg(not(unix))] fn main() {} diff --git a/src/app.rs b/src/app.rs index 4c2b3462855..d9ac8c09d8c 100644 --- a/src/app.rs +++ b/src/app.rs @@ -90,7 +90,7 @@ where /// counter: Cell, /// } /// - /// fn index(data: web::Data) { + /// async fn index(data: web::Data) { /// data.counter.set(data.counter.get() + 1); /// } /// @@ -192,7 +192,7 @@ where /// ```rust /// use actix_web::{web, App, HttpResponse}; /// - /// fn index(data: web::Path<(String, String)>) -> &'static str { + /// async fn index(data: web::Path<(String, String)>) -> &'static str { /// "Welcome!" /// } /// @@ -247,7 +247,7 @@ where /// ```rust /// use actix_web::{web, App, HttpResponse}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// @@ -302,7 +302,7 @@ where /// ```rust /// use actix_web::{web, App, HttpRequest, HttpResponse, Result}; /// - /// fn index(req: HttpRequest) -> Result { + /// async fn index(req: HttpRequest) -> Result { /// let url = req.url_for("youtube", &["asdlkjqme"])?; /// assert_eq!(url.as_str(), "https://youtube.com/watch/asdlkjqme"); /// Ok(HttpResponse::Ok().into()) @@ -346,7 +346,7 @@ where /// use actix_web::{middleware, web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// @@ -404,7 +404,7 @@ where /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// diff --git a/src/data.rs b/src/data.rs index a11175c1236..a026946aa0a 100644 --- a/src/data.rs +++ b/src/data.rs @@ -45,7 +45,7 @@ pub(crate) trait DataFactory { /// } /// /// /// Use `Data` extractor to access data in handler. -/// fn index(data: web::Data>) { +/// async fn index(data: web::Data>) { /// let mut data = data.lock().unwrap(); /// data.counter += 1; /// } diff --git a/src/extract.rs b/src/extract.rs index 20a1180ec25..9c8633368bb 100644 --- a/src/extract.rs +++ b/src/extract.rs @@ -75,7 +75,7 @@ pub trait FromRequest: Sized { /// } /// /// /// extract `Thing` from request -/// fn index(supplied_thing: Option) -> String { +/// async fn index(supplied_thing: Option) -> String { /// match supplied_thing { /// // Puns not intended /// Some(thing) => format!("Got something: {:?}", thing), @@ -146,7 +146,7 @@ where /// } /// /// /// extract `Thing` from request -/// fn index(supplied_thing: Result) -> String { +/// async fn index(supplied_thing: Result) -> String { /// match supplied_thing { /// Ok(thing) => format!("Got thing: {:?}", thing), /// Err(e) => format!("Error extracting thing: {}", e) diff --git a/src/handler.rs b/src/handler.rs index 767f630dab5..a7023422bf6 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -15,111 +15,8 @@ use crate::request::HttpRequest; use crate::responder::Responder; use crate::service::{ServiceRequest, ServiceResponse}; -/// Handler converter factory -pub trait Factory: Clone -where - R: Responder, -{ - fn call(&self, param: T) -> R; -} - -impl Factory<(), R> for F -where - F: Fn() -> R + Clone, - R: Responder, -{ - fn call(&self, _: ()) -> R { - (self)() - } -} - -#[doc(hidden)] -pub struct Handler -where - F: Factory, - R: Responder, -{ - hnd: F, - _t: PhantomData<(T, R)>, -} - -impl Handler -where - F: Factory, - R: Responder, -{ - pub fn new(hnd: F) -> Self { - Handler { - hnd, - _t: PhantomData, - } - } -} - -impl Clone for Handler -where - F: Factory, - R: Responder, -{ - fn clone(&self) -> Self { - Self { - hnd: self.hnd.clone(), - _t: PhantomData, - } - } -} - -impl Service for Handler -where - F: Factory, - R: Responder, -{ - type Request = (T, HttpRequest); - type Response = ServiceResponse; - type Error = Infallible; - type Future = HandlerServiceResponse; - - fn poll_ready(&mut self, _: &mut Context) -> Poll> { - Poll::Ready(Ok(())) - } - - fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future { - let fut = self.hnd.call(param).respond_to(&req); - HandlerServiceResponse { - fut, - req: Some(req), - } - } -} - -#[pin_project] -pub struct HandlerServiceResponse { - #[pin] - fut: T::Future, - req: Option, -} - -impl Future for HandlerServiceResponse { - type Output = Result; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { - let this = self.project(); - - match this.fut.poll(cx) { - Poll::Ready(Ok(res)) => { - Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) - } - Poll::Pending => Poll::Pending, - Poll::Ready(Err(e)) => { - let res: Response = e.into().into(); - Poll::Ready(Ok(ServiceResponse::new(this.req.take().unwrap(), res))) - } - } - } -} - /// Async handler converter factory -pub trait AsyncFactory: Clone + 'static +pub trait Factory: Clone + 'static where R: Future, O: Responder, @@ -127,7 +24,7 @@ where fn call(&self, param: T) -> R; } -impl AsyncFactory<(), R, O> for F +impl Factory<(), R, O> for F where F: Fn() -> R + Clone + 'static, R: Future, @@ -139,9 +36,9 @@ where } #[doc(hidden)] -pub struct AsyncHandler +pub struct Handler where - F: AsyncFactory, + F: Factory, R: Future, O: Responder, { @@ -149,51 +46,51 @@ where _t: PhantomData<(T, R, O)>, } -impl AsyncHandler +impl Handler where - F: AsyncFactory, + F: Factory, R: Future, O: Responder, { pub fn new(hnd: F) -> Self { - AsyncHandler { + Handler { hnd, _t: PhantomData, } } } -impl Clone for AsyncHandler +impl Clone for Handler where - F: AsyncFactory, + F: Factory, R: Future, O: Responder, { fn clone(&self) -> Self { - AsyncHandler { + Handler { hnd: self.hnd.clone(), _t: PhantomData, } } } -impl Service for AsyncHandler +impl Service for Handler where - F: AsyncFactory, + F: Factory, R: Future, O: Responder, { type Request = (T, HttpRequest); type Response = ServiceResponse; type Error = Infallible; - type Future = AsyncHandlerServiceResponse; + type Future = HandlerServiceResponse; fn poll_ready(&mut self, _: &mut Context) -> Poll> { Poll::Ready(Ok(())) } fn call(&mut self, (param, req): (T, HttpRequest)) -> Self::Future { - AsyncHandlerServiceResponse { + HandlerServiceResponse { fut: self.hnd.call(param), fut2: None, req: Some(req), @@ -203,7 +100,7 @@ where #[doc(hidden)] #[pin_project] -pub struct AsyncHandlerServiceResponse +pub struct HandlerServiceResponse where T: Future, R: Responder, @@ -215,7 +112,7 @@ where req: Option, } -impl Future for AsyncHandlerServiceResponse +impl Future for HandlerServiceResponse where T: Future, R: Responder, @@ -366,16 +263,7 @@ where /// FromRequest trait impl for tuples macro_rules! factory_tuple ({ $(($n:tt, $T:ident)),+} => { - impl Factory<($($T,)+), Res> for Func - where Func: Fn($($T,)+) -> Res + Clone, - Res: Responder, - { - fn call(&self, param: ($($T,)+)) -> Res { - (self)($(param.$n,)+) - } - } - - impl AsyncFactory<($($T,)+), Res, O> for Func + impl Factory<($($T,)+), Res, O> for Func where Func: Fn($($T,)+) -> Res + Clone + 'static, Res: Future, O: Responder, diff --git a/src/lib.rs b/src/lib.rs index 3cd1f78d60b..8063d0d35a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ //! use actix_web::{web, App, Responder, HttpServer}; //! # use std::thread; //! -//! fn index(info: web::Path<(String, u32)>) -> impl Responder { +//! async fn index(info: web::Path<(String, u32)>) -> impl Responder { //! format!("Hello {}! id:{}", info.0, info.1) //! } //! @@ -136,7 +136,7 @@ pub mod dev { pub use crate::config::{AppConfig, AppService}; #[doc(hidden)] - pub use crate::handler::{AsyncFactory, Factory}; + pub use crate::handler::Factory; pub use crate::info::ConnectionInfo; pub use crate::rmap::ResourceMap; pub use crate::service::{ diff --git a/src/request.rs b/src/request.rs index 84744af28a9..19072fcb137 100644 --- a/src/request.rs +++ b/src/request.rs @@ -276,7 +276,7 @@ impl Drop for HttpRequest { /// use serde_derive::Deserialize; /// /// /// extract `Thing` from request -/// fn index(req: HttpRequest) -> String { +/// async fn index(req: HttpRequest) -> String { /// format!("Got thing: {:?}", req) /// } /// diff --git a/src/resource.rs b/src/resource.rs index a1c0d396bd9..a06530d48f6 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -17,7 +17,7 @@ use crate::data::Data; use crate::dev::{insert_slash, AppService, HttpServiceFactory, ResourceDef}; use crate::extract::FromRequest; use crate::guard::Guard; -use crate::handler::{AsyncFactory, Factory}; +use crate::handler::Factory; use crate::responder::Responder; use crate::route::{CreateRouteService, Route, RouteService}; use crate::service::{ServiceRequest, ServiceResponse}; @@ -98,7 +98,7 @@ where /// ```rust /// use actix_web::{web, guard, App, HttpResponse}; /// - /// fn index(data: web::Path<(String, String)>) -> &'static str { + /// async fn index(data: web::Path<(String, String)>) -> &'static str { /// "Welcome!" /// } /// @@ -156,9 +156,9 @@ where /// .route(web::delete().to(delete_handler)) /// ); /// } - /// # fn get_handler() {} - /// # fn post_handler() {} - /// # fn delete_handler() {} + /// # async fn get_handler() -> impl actix_web::Responder { HttpResponse::Ok() } + /// # async fn post_handler() -> impl actix_web::Responder { HttpResponse::Ok() } + /// # async fn delete_handler() -> impl actix_web::Responder { HttpResponse::Ok() } /// ``` pub fn route(mut self, route: Route) -> Self { self.routes.push(route); @@ -174,7 +174,7 @@ where /// use actix_web::{web, App, FromRequest}; /// /// /// extract text data from request - /// fn index(body: String) -> String { + /// async fn index(body: String) -> String { /// format!("Body {}!", body) /// } /// @@ -230,46 +230,14 @@ where /// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } /// App::new().service(web::resource("/").route(web::route().to(index))); /// ``` - pub fn to(mut self, handler: F) -> Self + pub fn to(mut self, handler: F) -> Self where - F: Factory + 'static, - I: FromRequest + 'static, - R: Responder + 'static, - { - self.routes.push(Route::new().to(handler)); - self - } - - /// Register a new route and add async handler. - /// - /// ```rust - /// use actix_web::*; - /// - /// async fn index(req: HttpRequest) -> Result { - /// Ok(HttpResponse::Ok().finish()) - /// } - /// - /// App::new().service(web::resource("/").to_async(index)); - /// ``` - /// - /// This is shortcut for: - /// - /// ```rust - /// # use actix_web::*; - /// # async fn index(req: HttpRequest) -> Result { - /// # unimplemented!() - /// # } - /// App::new().service(web::resource("/").route(web::route().to_async(index))); - /// ``` - #[allow(clippy::wrong_self_convention)] - pub fn to_async(mut self, handler: F) -> Self - where - F: AsyncFactory, + F: Factory, I: FromRequest + 'static, R: Future + 'static, U: Responder + 'static, { - self.routes.push(Route::new().to_async(handler)); + self.routes.push(Route::new().to(handler)); self } @@ -327,7 +295,7 @@ where /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// @@ -705,17 +673,16 @@ mod tests { } #[test] - fn test_to_async() { + fn test_to() { block_on(async { - let mut srv = init_service(App::new().service( - web::resource("/test").to_async(|| { + let mut srv = + init_service(App::new().service(web::resource("/test").to(|| { async { delay_for(Duration::from_millis(100)).await; Ok::<_, Error>(HttpResponse::Ok()) } - }), - )) - .await; + }))) + .await; let req = TestRequest::with_uri("/test").to_request(); let resp = call_service(&mut srv, req).await; assert_eq!(resp.status(), StatusCode::OK); diff --git a/src/responder.rs b/src/responder.rs index 3f1471721b2..b254567de1f 100644 --- a/src/responder.rs +++ b/src/responder.rs @@ -475,9 +475,10 @@ pub(crate) mod tests { let mut srv = init_service( App::new() .service( - web::resource("/none").to(|| -> Option<&'static str> { None }), + web::resource("/none") + .to(|| async { Option::<&'static str>::None }), ) - .service(web::resource("/some").to(|| Some("some"))), + .service(web::resource("/some").to(|| async { Some("some") })), ) .await; diff --git a/src/route.rs b/src/route.rs index 51305d840ff..3ebfc3f52c7 100644 --- a/src/route.rs +++ b/src/route.rs @@ -5,11 +5,11 @@ use std::task::{Context, Poll}; use actix_http::{http::Method, Error}; use actix_service::{Service, ServiceFactory}; -use futures::future::{ok, Either, FutureExt, LocalBoxFuture, Ready}; +use futures::future::{ok, ready, Either, FutureExt, LocalBoxFuture, Ready}; use crate::extract::FromRequest; use crate::guard::{self, Guard}; -use crate::handler::{AsyncFactory, AsyncHandler, Extract, Factory, Handler}; +use crate::handler::{Extract, Factory, Handler}; use crate::responder::Responder; use crate::service::{ServiceRequest, ServiceResponse}; use crate::HttpResponse; @@ -49,7 +49,7 @@ impl Route { pub fn new() -> Route { Route { service: Box::new(RouteNewService::new(Extract::new(Handler::new(|| { - HttpResponse::NotFound() + ready(HttpResponse::NotFound()) })))), guards: Rc::new(Vec::new()), } @@ -187,7 +187,7 @@ impl Route { /// } /// /// /// extract path info using serde - /// fn index(info: web::Path) -> String { + /// async fn index(info: web::Path) -> String { /// format!("Welcome {}!", info.username) /// } /// @@ -212,7 +212,7 @@ impl Route { /// } /// /// /// extract path info using serde - /// fn index(path: web::Path, query: web::Query>, body: web::Json) -> String { + /// async fn index(path: web::Path, query: web::Query>, body: web::Json) -> String { /// format!("Welcome {}!", path.username) /// } /// @@ -223,52 +223,15 @@ impl Route { /// ); /// } /// ``` - pub fn to(mut self, handler: F) -> Route + pub fn to(mut self, handler: F) -> Self where - F: Factory + 'static, - T: FromRequest + 'static, - R: Responder + 'static, - { - self.service = - Box::new(RouteNewService::new(Extract::new(Handler::new(handler)))); - self - } - - /// Set async handler function, use request extractors for parameters. - /// This method has to be used if your handler function returns `impl Future<>` - /// - /// ```rust - /// use actix_web::{web, App, Error}; - /// use serde_derive::Deserialize; - /// - /// #[derive(Deserialize)] - /// struct Info { - /// username: String, - /// } - /// - /// /// extract path info using serde - /// async fn index(info: web::Path) -> Result<&'static str, Error> { - /// Ok("Hello World!") - /// } - /// - /// fn main() { - /// let app = App::new().service( - /// web::resource("/{username}/index.html") // <- define path parameters - /// .route(web::get().to_async(index)) // <- register async handler - /// ); - /// } - /// ``` - #[allow(clippy::wrong_self_convention)] - pub fn to_async(mut self, handler: F) -> Self - where - F: AsyncFactory, + F: Factory, T: FromRequest + 'static, R: Future + 'static, U: Responder + 'static, { - self.service = Box::new(RouteNewService::new(Extract::new(AsyncHandler::new( - handler, - )))); + self.service = + Box::new(RouteNewService::new(Extract::new(Handler::new(handler)))); self } } @@ -402,22 +365,24 @@ mod tests { web::resource("/test") .route(web::get().to(|| HttpResponse::Ok())) .route(web::put().to(|| { - Err::(error::ErrorBadRequest("err")) + async { + Err::(error::ErrorBadRequest("err")) + } })) - .route(web::post().to_async(|| { + .route(web::post().to(|| { async { delay_for(Duration::from_millis(100)).await; HttpResponse::Created() } })) - .route(web::delete().to_async(|| { + .route(web::delete().to(|| { async { delay_for(Duration::from_millis(100)).await; Err::(error::ErrorBadRequest("err")) } })), ) - .service(web::resource("/json").route(web::get().to_async(|| { + .service(web::resource("/json").route(web::get().to(|| { async { delay_for(Duration::from_millis(25)).await; web::Json(MyObject { diff --git a/src/scope.rs b/src/scope.rs index f5ffe05faad..e5c04d71eca 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -46,7 +46,7 @@ type BoxedResponse = LocalBoxFuture<'static, Result>; /// fn main() { /// let app = App::new().service( /// web::scope("/{project_id}/") -/// .service(web::resource("/path1").to(|| HttpResponse::Ok())) +/// .service(web::resource("/path1").to(|| async { HttpResponse::Ok() })) /// .service(web::resource("/path2").route(web::get().to(|| HttpResponse::Ok()))) /// .service(web::resource("/path3").route(web::head().to(|| HttpResponse::MethodNotAllowed()))) /// ); @@ -101,7 +101,7 @@ where /// ```rust /// use actix_web::{web, guard, App, HttpRequest, HttpResponse}; /// - /// fn index(data: web::Path<(String, String)>) -> &'static str { + /// async fn index(data: web::Path<(String, String)>) -> &'static str { /// "Welcome!" /// } /// @@ -132,7 +132,7 @@ where /// counter: Cell, /// } /// - /// fn index(data: web::Data) { + /// async fn index(data: web::Data) { /// data.counter.set(data.counter.get() + 1); /// } /// @@ -228,7 +228,7 @@ where /// /// struct AppState; /// - /// fn index(req: HttpRequest) -> &'static str { + /// async fn index(req: HttpRequest) -> &'static str { /// "Welcome!" /// } /// @@ -258,7 +258,7 @@ where /// ```rust /// use actix_web::{web, App, HttpResponse}; /// - /// fn index(data: web::Path<(String, String)>) -> &'static str { + /// async fn index(data: web::Path<(String, String)>) -> &'static str { /// "Welcome!" /// } /// @@ -356,7 +356,7 @@ where /// use actix_web::{web, App}; /// use actix_web::http::{header::CONTENT_TYPE, HeaderValue}; /// - /// fn index() -> &'static str { + /// async fn index() -> &'static str { /// "Welcome!" /// } /// @@ -846,8 +846,10 @@ mod tests { let mut srv = init_service(App::new().service(web::scope("/ab-{project}").service( web::resource("/path1").to(|r: HttpRequest| { - HttpResponse::Ok() - .body(format!("project: {}", &r.match_info()["project"])) + async move { + HttpResponse::Ok() + .body(format!("project: {}", &r.match_info()["project"])) + } }), ))) .await; @@ -962,8 +964,12 @@ mod tests { let mut srv = init_service(App::new().service(web::scope("/app").service( web::scope("/{project_id}").service(web::resource("/path1").to( |r: HttpRequest| { - HttpResponse::Created() - .body(format!("project: {}", &r.match_info()["project_id"])) + async move { + HttpResponse::Created().body(format!( + "project: {}", + &r.match_info()["project_id"] + )) + } }, )), ))) @@ -989,11 +995,13 @@ mod tests { let mut srv = init_service(App::new().service(web::scope("/app").service( web::scope("/{project}").service(web::scope("/{id}").service( web::resource("/path1").to(|r: HttpRequest| { - HttpResponse::Created().body(format!( - "project: {} - {}", - &r.match_info()["project"], - &r.match_info()["id"], - )) + async move { + HttpResponse::Created().body(format!( + "project: {} - {}", + &r.match_info()["project"], + &r.match_info()["id"], + )) + } }), )), ))) @@ -1241,12 +1249,14 @@ mod tests { s.route( "/", web::get().to(|req: HttpRequest| { - HttpResponse::Ok().body(format!( - "{}", - req.url_for("youtube", &["xxxxxx"]) - .unwrap() - .as_str() - )) + async move { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("youtube", &["xxxxxx"]) + .unwrap() + .as_str() + )) + } }), ); })); @@ -1267,8 +1277,12 @@ mod tests { let mut srv = init_service(App::new().service(web::scope("/a").service( web::scope("/b").service(web::resource("/c/{stuff}").name("c").route( web::get().to(|req: HttpRequest| { - HttpResponse::Ok() - .body(format!("{}", req.url_for("c", &["12345"]).unwrap())) + async move { + HttpResponse::Ok().body(format!( + "{}", + req.url_for("c", &["12345"]).unwrap() + )) + } }), )), ))) diff --git a/src/test.rs b/src/test.rs index 8cee3bc6aaf..0776b0f154c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -55,7 +55,7 @@ pub fn default_service( /// fn test_init_service() { /// let mut app = test::init_service( /// App::new() -/// .service(web::resource("/test").to(|| HttpResponse::Ok())) +/// .service(web::resource("/test").to(|| async { HttpResponse::Ok() })) /// ); /// /// // Create request object @@ -94,14 +94,16 @@ where /// fn test_response() { /// let mut app = test::init_service( /// App::new() -/// .service(web::resource("/test").to(|| HttpResponse::Ok())) -/// ); +/// .service(web::resource("/test").to(|| async { +/// HttpResponse::Ok() +/// })) +/// ).await; /// /// // Create request object /// let req = test::TestRequest::with_uri("/test").to_request(); /// /// // Call application -/// let resp = test::call_service(&mut app, req); +/// let resp = test::call_service(&mut app, req).await; /// assert_eq!(resp.status(), StatusCode::OK); /// } /// ``` @@ -125,15 +127,17 @@ where /// let mut app = test::init_service( /// App::new().service( /// web::resource("/index.html") -/// .route(web::post().to( -/// || HttpResponse::Ok().body("welcome!"))))); +/// .route(web::post().to(|| async { +/// HttpResponse::Ok().body("welcome!") +/// }))) +/// ).await; /// /// let req = test::TestRequest::post() /// .uri("/index.html") /// .header(header::CONTENT_TYPE, "application/json") /// .to_request(); /// -/// let result = test::read_response(&mut app, req); +/// let result = test::read_response(&mut app, req).await; /// assert_eq!(result, Bytes::from_static(b"welcome!")); /// } /// ``` @@ -167,15 +171,17 @@ where /// let mut app = test::init_service( /// App::new().service( /// web::resource("/index.html") -/// .route(web::post().to( -/// || HttpResponse::Ok().body("welcome!"))))); +/// .route(web::post().to(|| async { +/// HttpResponse::Ok().body("welcome!") +/// }))) +/// ).await; /// /// let req = test::TestRequest::post() /// .uri("/index.html") /// .header(header::CONTENT_TYPE, "application/json") /// .to_request(); /// -/// let resp = test::call_service(&mut app, req); +/// let resp = test::call_service(&mut app, req).await; /// let result = test::read_body(resp); /// assert_eq!(result, Bytes::from_static(b"welcome!")); /// } @@ -221,10 +227,11 @@ where /// let mut app = test::init_service( /// App::new().service( /// web::resource("/people") -/// .route(web::post().to(|person: web::Json| { +/// .route(web::post().to(|person: web::Json| async { /// HttpResponse::Ok() /// .json(person.into_inner())}) -/// ))); +/// )) +/// ).await; /// /// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes(); /// @@ -234,7 +241,7 @@ where /// .set_payload(payload) /// .to_request(); /// -/// let result: Person = test::read_response_json(&mut app, req); +/// let result: Person = test::read_response_json(&mut app, req).await; /// } /// ``` pub async fn read_response_json(app: &mut S, req: Request) -> T @@ -262,7 +269,7 @@ where /// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage}; /// use actix_web::http::{header, StatusCode}; /// -/// fn index(req: HttpRequest) -> HttpResponse { +/// async fn index(req: HttpRequest) -> HttpResponse { /// if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) { /// HttpResponse::Ok().into() /// } else { @@ -275,11 +282,11 @@ where /// let req = test::TestRequest::with_header("content-type", "text/plain") /// .to_http_request(); /// -/// let resp = test::block_on(index(req)).unwrap(); +/// let resp = index(req).await.unwrap(); /// assert_eq!(resp.status(), StatusCode::OK); /// /// let req = test::TestRequest::default().to_http_request(); -/// let resp = test::block_on(index(req)).unwrap(); +/// let resp = index(req).await.unwrap(); /// assert_eq!(resp.status(), StatusCode::BAD_REQUEST); /// } /// ``` @@ -535,9 +542,17 @@ mod tests { let mut app = init_service( App::new().service( web::resource("/index.html") - .route(web::put().to(|| HttpResponse::Ok().body("put!"))) - .route(web::patch().to(|| HttpResponse::Ok().body("patch!"))) - .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))), + .route( + web::put().to(|| async { HttpResponse::Ok().body("put!") }), + ) + .route( + web::patch() + .to(|| async { HttpResponse::Ok().body("patch!") }), + ) + .route( + web::delete() + .to(|| async { HttpResponse::Ok().body("delete!") }), + ), ), ) .await; @@ -567,13 +582,11 @@ mod tests { #[test] fn test_response() { block_on(async { - let mut app = init_service( - App::new().service( - web::resource("/index.html") - .route(web::post().to(|| HttpResponse::Ok().body("welcome!"))), - ), - ) - .await; + let mut app = + init_service(App::new().service(web::resource("/index.html").route( + web::post().to(|| async { HttpResponse::Ok().body("welcome!") }), + ))) + .await; let req = TestRequest::post() .uri("/index.html") @@ -597,7 +610,7 @@ mod tests { let mut app = init_service(App::new().service(web::resource("/people").route( web::post().to(|person: web::Json| { - HttpResponse::Ok().json(person.into_inner()) + async { HttpResponse::Ok().json(person.into_inner()) } }), ))) .await; @@ -621,7 +634,7 @@ mod tests { let mut app = init_service(App::new().service(web::resource("/people").route( web::post().to(|person: web::Form| { - HttpResponse::Ok().json(person.into_inner()) + async { HttpResponse::Ok().json(person.into_inner()) } }), ))) .await; @@ -650,7 +663,7 @@ mod tests { let mut app = init_service(App::new().service(web::resource("/people").route( web::post().to(|person: web::Json| { - HttpResponse::Ok().json(person.into_inner()) + async { HttpResponse::Ok().json(person.into_inner()) } }), ))) .await; @@ -688,8 +701,7 @@ mod tests { } let mut app = init_service( - App::new() - .service(web::resource("/index.html").to_async(async_with_block)), + App::new().service(web::resource("/index.html").to(async_with_block)), ) .await; @@ -721,7 +733,7 @@ mod tests { // let addr = run_on(|| MyActor.start()); // let mut app = init_service(App::new().service( - // web::resource("/index.html").to_async(move || { + // web::resource("/index.html").to(move || { // addr.send(Num(1)).from_err().and_then(|res| { // if res == 1 { // HttpResponse::Ok() diff --git a/src/types/form.rs b/src/types/form.rs index 694fe6dbd72..c20dc7a05e8 100644 --- a/src/types/form.rs +++ b/src/types/form.rs @@ -181,7 +181,7 @@ impl Responder for Form { /// /// /// Extract form data using serde. /// /// Custom configuration is used for this handler, max payload size is 4k -/// fn index(form: web::Form) -> Result { +/// async fn index(form: web::Form) -> Result { /// Ok(format!("Welcome {}!", form.username)) /// } /// diff --git a/src/types/json.rs b/src/types/json.rs index 19f8532bd36..206a4e42598 100644 --- a/src/types/json.rs +++ b/src/types/json.rs @@ -46,7 +46,7 @@ use crate::responder::Responder; /// } /// /// /// deserialize `Info` from request's body -/// fn index(info: web::Json) -> String { +/// async fn index(info: web::Json) -> String { /// format!("Welcome {}!", info.username) /// } /// @@ -157,7 +157,7 @@ impl Responder for Json { /// } /// /// /// deserialize `Info` from request's body -/// fn index(info: web::Json) -> String { +/// async fn index(info: web::Json) -> String { /// format!("Welcome {}!", info.username) /// } /// @@ -217,7 +217,7 @@ where /// } /// /// /// deserialize `Info` from request's body, max payload size is 4kb -/// fn index(info: web::Json) -> String { +/// async fn index(info: web::Json) -> String { /// format!("Welcome {}!", info.username) /// } /// diff --git a/src/types/path.rs b/src/types/path.rs index 89b9392be86..29a574feb9d 100644 --- a/src/types/path.rs +++ b/src/types/path.rs @@ -24,7 +24,7 @@ use crate::FromRequest; /// /// extract path info from "/{username}/{count}/index.html" url /// /// {username} - deserializes to a String /// /// {count} - - deserializes to a u32 -/// fn index(info: web::Path<(String, u32)>) -> String { +/// async fn index(info: web::Path<(String, u32)>) -> String { /// format!("Welcome {}! {}", info.0, info.1) /// } /// @@ -49,7 +49,7 @@ use crate::FromRequest; /// } /// /// /// extract `Info` from a path using serde -/// fn index(info: web::Path) -> Result { +/// async fn index(info: web::Path) -> Result { /// Ok(format!("Welcome {}!", info.username)) /// } /// @@ -119,7 +119,7 @@ impl fmt::Display for Path { /// /// extract path info from "/{username}/{count}/index.html" url /// /// {username} - deserializes to a String /// /// {count} - - deserializes to a u32 -/// fn index(info: web::Path<(String, u32)>) -> String { +/// async fn index(info: web::Path<(String, u32)>) -> String { /// format!("Welcome {}! {}", info.0, info.1) /// } /// @@ -144,7 +144,7 @@ impl fmt::Display for Path { /// } /// /// /// extract `Info` from a path using serde -/// fn index(info: web::Path) -> Result { +/// async fn index(info: web::Path) -> Result { /// Ok(format!("Welcome {}!", info.username)) /// } /// @@ -206,7 +206,7 @@ where /// } /// /// // deserialize `Info` from request's path -/// fn index(folder: web::Path) -> String { +/// async fn index(folder: web::Path) -> String { /// format!("Selected folder: {:?}!", folder) /// } /// diff --git a/src/types/payload.rs b/src/types/payload.rs index 61f7328b41f..ee7e1166790 100644 --- a/src/types/payload.rs +++ b/src/types/payload.rs @@ -40,7 +40,7 @@ use crate::request::HttpRequest; /// fn main() { /// let app = App::new().service( /// web::resource("/index.html").route( -/// web::get().to_async(index)) +/// web::get().to(index)) /// ); /// } /// ``` @@ -88,7 +88,7 @@ impl Stream for Payload { /// fn main() { /// let app = App::new().service( /// web::resource("/index.html").route( -/// web::get().to_async(index)) +/// web::get().to(index)) /// ); /// } /// ``` @@ -117,7 +117,7 @@ impl FromRequest for Payload { /// use actix_web::{web, App}; /// /// /// extract binary data from request -/// fn index(body: Bytes) -> String { +/// async fn index(body: Bytes) -> String { /// format!("Body {:?}!", body) /// } /// @@ -169,7 +169,7 @@ impl FromRequest for Bytes { /// use actix_web::{web, App, FromRequest}; /// /// /// extract text data from request -/// fn index(text: String) -> String { +/// async fn index(text: String) -> String { /// format!("Body {}!", text) /// } /// diff --git a/src/types/query.rs b/src/types/query.rs index 8061d723399..e442f1c3130 100644 --- a/src/types/query.rs +++ b/src/types/query.rs @@ -40,7 +40,7 @@ use crate::request::HttpRequest; /// // Use `Query` extractor for query information (and destructure it within the signature). /// // This handler gets called only if the request's query string contains a `username` field. /// // The correct request for this handler would be `/index.html?id=64&response_type=Code"`. -/// fn index(web::Query(info): web::Query) -> String { +/// async fn index(web::Query(info): web::Query) -> String { /// format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type) /// } /// @@ -118,7 +118,7 @@ impl fmt::Display for Query { /// // Use `Query` extractor for query information. /// // This handler get called only if request's query contains `username` field /// // The correct request for this handler would be `/index.html?id=64&response_type=Code"` -/// fn index(info: web::Query) -> String { +/// async fn index(info: web::Query) -> String { /// format!("Authorization request for client with id={} and type={:?}!", info.id, info.response_type) /// } /// @@ -179,7 +179,7 @@ where /// } /// /// /// deserialize `Info` from request's querystring -/// fn index(info: web::Query) -> String { +/// async fn index(info: web::Query) -> String { /// format!("Welcome {}!", info.username) /// } /// diff --git a/src/web.rs b/src/web.rs index 099e2662736..22630ae81b3 100644 --- a/src/web.rs +++ b/src/web.rs @@ -8,7 +8,7 @@ pub use futures::channel::oneshot::Canceled; use crate::error::Error; use crate::extract::FromRequest; -use crate::handler::{AsyncFactory, Factory}; +use crate::handler::Factory; use crate::resource::Resource; use crate::responder::Responder; use crate::route::Route; @@ -231,10 +231,10 @@ pub fn method(method: Method) -> Route { /// Create a new route and add handler. /// /// ```rust -/// use actix_web::{web, App, HttpResponse}; +/// use actix_web::{web, App, HttpResponse, Responder}; /// -/// fn index() -> HttpResponse { -/// unimplemented!() +/// async fn index() -> impl Responder { +/// HttpResponse::Ok() /// } /// /// App::new().service( @@ -242,36 +242,14 @@ pub fn method(method: Method) -> Route { /// web::to(index)) /// ); /// ``` -pub fn to(handler: F) -> Route +pub fn to(handler: F) -> Route where - F: Factory + 'static, - I: FromRequest + 'static, - R: Responder + 'static, -{ - Route::new().to(handler) -} - -/// Create a new route and add async handler. -/// -/// ```rust -/// use actix_web::{web, App, HttpResponse, Error}; -/// -/// async fn index() -> Result { -/// Ok(HttpResponse::Ok().finish()) -/// } -/// -/// App::new().service(web::resource("/").route( -/// web::to_async(index)) -/// ); -/// ``` -pub fn to_async(handler: F) -> Route -where - F: AsyncFactory, + F: Factory, I: FromRequest + 'static, R: Future + 'static, U: Responder + 'static, { - Route::new().to_async(handler) + Route::new().to(handler) } /// Create raw service for a specific path. diff --git a/tests/test_server.rs b/tests/test_server.rs index eeaedec0592..0114b21ffd7 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -11,13 +11,11 @@ use bytes::Bytes; use flate2::read::GzDecoder; use flate2::write::{GzEncoder, ZlibDecoder, ZlibEncoder}; use flate2::Compression; -use futures::future::ok; -use futures::stream::once; +use futures::{future::ok, stream::once}; use rand::{distributions::Alphanumeric, Rng}; -use actix_connect::start_default_resolver; use actix_web::middleware::{BodyEncoding, Compress}; -use actix_web::{dev, http, test, web, App, HttpResponse, HttpServer}; +use actix_web::{dev, http, web, App, HttpResponse, HttpServer}; const STR: &str = "Hello World Hello World Hello World Hello World Hello World \ Hello World Hello World Hello World Hello World Hello World \ @@ -817,18 +815,19 @@ fn test_brotli_encoding_large() { // } #[cfg(all( - feature = "rust-tls", - feature = "ssl", + feature = "rustls", + feature = "openssl", any(feature = "flate2-zlib", feature = "flate2-rust") ))] #[test] fn test_reading_deflate_encoding_large_random_ssl() { block_on(async { - use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; - use rustls::internal::pemfile::{certs, pkcs8_private_keys}; - use rustls::{NoClientAuth, ServerConfig}; + use open_ssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; + use rust_tls::internal::pemfile::{certs, pkcs8_private_keys}; + use rust_tls::{NoClientAuth, ServerConfig}; use std::fs::File; use std::io::BufReader; + use std::sync::mpsc; let addr = TestServer::unused_addr(); let (tx, rx) = mpsc::channel(); @@ -838,7 +837,7 @@ fn test_reading_deflate_encoding_large_random_ssl() { .take(160_000) .collect::(); - thread::spawn(move || { + std::thread::spawn(move || { let sys = actix_rt::System::new("test"); // load ssl keys @@ -851,11 +850,13 @@ fn test_reading_deflate_encoding_large_random_ssl() { let srv = HttpServer::new(|| { App::new().service(web::resource("/").route(web::to(|bytes: Bytes| { - Ok::<_, Error>( - HttpResponse::Ok() - .encoding(http::ContentEncoding::Identity) - .body(bytes), - ) + async move { + Ok::<_, Error>( + HttpResponse::Ok() + .encoding(http::ContentEncoding::Identity) + .body(bytes), + ) + } }))) }) .bind_rustls(addr, config) @@ -866,8 +867,7 @@ fn test_reading_deflate_encoding_large_random_ssl() { let _ = sys.run(); }); let (srv, _sys) = rx.recv().unwrap(); - test::block_on(futures::lazy(|| Ok::<_, ()>(start_default_resolver()))).unwrap(); - let client = test::run_on(|| { + let client = { let mut builder = SslConnector::builder(SslMethod::tls()).unwrap(); builder.set_verify(SslVerifyMode::NONE); let _ = builder.set_alpn_protos(b"\x02h2\x08http/1.1").unwrap(); @@ -880,7 +880,7 @@ fn test_reading_deflate_encoding_large_random_ssl() { .finish(), ) .finish() - }); + }; // encode data let mut e = ZlibEncoder::new(Vec::new(), Compression::default()); @@ -893,11 +893,11 @@ fn test_reading_deflate_encoding_large_random_ssl() { .header(http::header::CONTENT_ENCODING, "deflate") .send_body(enc); - let mut response = test::block_on(req).unwrap(); + let mut response = req.await.unwrap(); assert!(response.status().is_success()); // read response - let bytes = test::block_on(response.body()).unwrap(); + let bytes = response.body().await.unwrap(); assert_eq!(bytes.len(), data.len()); assert_eq!(bytes, Bytes::from(data));