From d83cbc8542e068ce3908e070b56895bfe46a2d27 Mon Sep 17 00:00:00 2001 From: Masaki Hara Date: Tue, 7 Jan 2020 01:00:43 +0900 Subject: [PATCH] Support actix-web 2.x (#14) --- Cargo.toml | 10 ++- examples/middleware-closure.rs | 11 +-- examples/middleware.rs | 14 +-- src/extractors/basic.rs | 57 +++++++------ src/extractors/bearer.rs | 49 ++++++----- src/extractors/mod.rs | 4 +- src/headers/authorization/scheme/basic.rs | 6 +- src/headers/authorization/scheme/bearer.rs | 6 +- .../www_authenticate/challenge/basic.rs | 6 +- .../challenge/bearer/challenge.rs | 6 +- src/middleware.rs | 85 +++++++++---------- 11 files changed, 133 insertions(+), 121 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 39ba959ef..de6c12ae7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,13 +14,15 @@ exclude = [".travis.yml", ".gitignore"] edition = "2018" [dependencies] -actix-web = { version = "^1.0", default_features = false } -actix-service = "0.4.0" -futures = "0.1" -futures-locks = "0.3.3" +actix-web = { version = "^2.0", default_features = false } +actix-service = "1.0" +futures = "0.3" bytes = "0.4" base64 = "0.10" +[dev-dependencies] +actix-rt = "1.0" + [features] default = [] nightly = [] diff --git a/examples/middleware-closure.rs b/examples/middleware-closure.rs index 0135bac9a..0f623bfe0 100644 --- a/examples/middleware-closure.rs +++ b/examples/middleware-closure.rs @@ -1,18 +1,19 @@ use actix_web::{middleware, web, App, HttpServer}; -use futures::future; - use actix_web_httpauth::middleware::HttpAuthentication; -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { HttpServer::new(|| { - let auth = HttpAuthentication::basic(|req, _credentials| future::ok(req)); + let auth = + HttpAuthentication::basic(|req, _credentials| async { Ok(req) }); App::new() .wrap(middleware::Logger::default()) .wrap(auth) - .service(web::resource("/").to(|| "Test\r\n")) + .service(web::resource("/").to(|| async { "Test\r\n" })) }) .bind("127.0.0.1:8080")? .workers(1) .run() + .await } diff --git a/examples/middleware.rs b/examples/middleware.rs index 468d69de9..672e08d90 100644 --- a/examples/middleware.rs +++ b/examples/middleware.rs @@ -1,27 +1,27 @@ use actix_web::dev::ServiceRequest; use actix_web::{middleware, web, App, Error, HttpServer}; -use futures::future; - use actix_web_httpauth::extractors::basic::BasicAuth; use actix_web_httpauth::middleware::HttpAuthentication; -fn validator( +async fn validator( req: ServiceRequest, _credentials: BasicAuth, -) -> future::FutureResult { - future::ok(req) +) -> Result { + Ok(req) } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { HttpServer::new(|| { let auth = HttpAuthentication::basic(validator); App::new() .wrap(middleware::Logger::default()) .wrap(auth) - .service(web::resource("/").to(|| "Test\r\n")) + .service(web::resource("/").to(|| async { "Test\r\n" })) }) .bind("127.0.0.1:8080")? .workers(1) .run() + .await } diff --git a/src/extractors/basic.rs b/src/extractors/basic.rs index 3ad5deb1b..700568267 100644 --- a/src/extractors/basic.rs +++ b/src/extractors/basic.rs @@ -5,6 +5,7 @@ use std::borrow::Cow; use actix_web::dev::{Payload, ServiceRequest}; use actix_web::http::header::Header; use actix_web::{FromRequest, HttpRequest}; +use futures::future; use super::config::AuthExtractorConfig; use super::errors::AuthenticationError; @@ -57,7 +58,7 @@ impl AuthExtractorConfig for Config { /// use actix_web::Result; /// use actix_web_httpauth::extractors::basic::BasicAuth; /// -/// fn index(auth: BasicAuth) -> String { +/// async fn index(auth: BasicAuth) -> String { /// format!("Hello, {}!", auth.user_id()) /// } /// ``` @@ -72,7 +73,7 @@ impl AuthExtractorConfig for Config { /// use actix_web::{web, App}; /// use actix_web_httpauth::extractors::basic::{BasicAuth, Config}; /// -/// fn index(auth: BasicAuth) -> String { +/// async fn index(auth: BasicAuth) -> String { /// format!("Hello, {}!", auth.user_id()) /// } /// @@ -101,7 +102,7 @@ impl BasicAuth { } impl FromRequest for BasicAuth { - type Future = Result; + type Future = future::Ready>; type Config = Config; type Error = AuthenticationError; @@ -109,37 +110,41 @@ impl FromRequest for BasicAuth { req: &HttpRequest, _: &mut Payload, ) -> ::Future { - Authorization::::parse(req) - .map(|auth| BasicAuth(auth.into_scheme())) - .map_err(|_| { - // TODO: debug! the original error - let challenge = req - .app_data::() - .map(|config| config.0.clone()) - // TODO: Add trace! about `Default::default` call - .unwrap_or_else(Default::default); + future::ready( + Authorization::::parse(req) + .map(|auth| BasicAuth(auth.into_scheme())) + .map_err(|_| { + // TODO: debug! the original error + let challenge = req + .app_data::() + .map(|config| config.0.clone()) + // TODO: Add trace! about `Default::default` call + .unwrap_or_else(Default::default); - AuthenticationError::new(challenge) - }) + AuthenticationError::new(challenge) + }), + ) } } impl AuthExtractor for BasicAuth { type Error = AuthenticationError; - type Future = Result; + type Future = future::Ready>; fn from_service_request(req: &ServiceRequest) -> Self::Future { - Authorization::::parse(req) - .map(|auth| BasicAuth(auth.into_scheme())) - .map_err(|_| { - // TODO: debug! the original error - let challenge = req - .app_data::() - .map(|config| config.0.clone()) - // TODO: Add trace! about `Default::default` call - .unwrap_or_else(Default::default); + future::ready( + Authorization::::parse(req) + .map(|auth| BasicAuth(auth.into_scheme())) + .map_err(|_| { + // TODO: debug! the original error + let challenge = req + .app_data::() + .map(|config| config.0.clone()) + // TODO: Add trace! about `Default::default` call + .unwrap_or_else(Default::default); - AuthenticationError::new(challenge) - }) + AuthenticationError::new(challenge) + }), + ) } } diff --git a/src/extractors/bearer.rs b/src/extractors/bearer.rs index a735954c8..37788d70f 100644 --- a/src/extractors/bearer.rs +++ b/src/extractors/bearer.rs @@ -6,6 +6,7 @@ use std::default::Default; use actix_web::dev::{Payload, ServiceRequest}; use actix_web::http::header::Header; use actix_web::{FromRequest, HttpRequest}; +use futures::future; use super::config::AuthExtractorConfig; use super::errors::AuthenticationError; @@ -60,7 +61,7 @@ impl AuthExtractorConfig for Config { /// ```rust /// use actix_web_httpauth::extractors::bearer::BearerAuth; /// -/// fn index(auth: BearerAuth) -> String { +/// async fn index(auth: BearerAuth) -> String { /// format!("Hello, user with token {}!", auth.token()) /// } /// ``` @@ -75,7 +76,7 @@ impl AuthExtractorConfig for Config { /// use actix_web::{web, App}; /// use actix_web_httpauth::extractors::bearer::{BearerAuth, Config}; /// -/// fn index(auth: BearerAuth) -> String { +/// async fn index(auth: BearerAuth) -> String { /// format!("Hello, {}!", auth.token()) /// } /// @@ -101,41 +102,45 @@ impl BearerAuth { impl FromRequest for BearerAuth { type Config = Config; - type Future = Result; + type Future = future::Ready>; type Error = AuthenticationError; fn from_request( req: &HttpRequest, _payload: &mut Payload, ) -> ::Future { - authorization::Authorization::::parse(req) - .map(|auth| BearerAuth(auth.into_scheme())) - .map_err(|_| { - let bearer = req - .app_data::() - .map(|config| config.0.clone()) - .unwrap_or_else(Default::default); + future::ready( + authorization::Authorization::::parse(req) + .map(|auth| BearerAuth(auth.into_scheme())) + .map_err(|_| { + let bearer = req + .app_data::() + .map(|config| config.0.clone()) + .unwrap_or_else(Default::default); - AuthenticationError::new(bearer) - }) + AuthenticationError::new(bearer) + }), + ) } } impl AuthExtractor for BearerAuth { - type Future = Result; + type Future = future::Ready>; type Error = AuthenticationError; fn from_service_request(req: &ServiceRequest) -> Self::Future { - authorization::Authorization::::parse(req) - .map(|auth| BearerAuth(auth.into_scheme())) - .map_err(|_| { - let bearer = req - .app_data::() - .map(|config| config.0.clone()) - .unwrap_or_else(Default::default); + future::ready( + authorization::Authorization::::parse(req) + .map(|auth| BearerAuth(auth.into_scheme())) + .map_err(|_| { + let bearer = req + .app_data::() + .map(|config| config.0.clone()) + .unwrap_or_else(Default::default); - AuthenticationError::new(bearer) - }) + AuthenticationError::new(bearer) + }), + ) } } diff --git a/src/extractors/mod.rs b/src/extractors/mod.rs index 062efba2d..27a3e4e33 100644 --- a/src/extractors/mod.rs +++ b/src/extractors/mod.rs @@ -2,7 +2,7 @@ use actix_web::dev::ServiceRequest; use actix_web::Error; -use futures::IntoFuture; +use futures::future::Future; pub mod basic; pub mod bearer; @@ -26,7 +26,7 @@ pub trait AuthExtractor: Sized { type Error: Into; /// Future that resolves into extracted credentials type. - type Future: IntoFuture; + type Future: Future>; /// Parse the authentication credentials from the actix' `ServiceRequest`. fn from_service_request(req: &ServiceRequest) -> Self::Future; diff --git a/src/headers/authorization/scheme/basic.rs b/src/headers/authorization/scheme/basic.rs index 0889044b3..751c08861 100644 --- a/src/headers/authorization/scheme/basic.rs +++ b/src/headers/authorization/scheme/basic.rs @@ -3,7 +3,7 @@ use std::fmt; use std::str; use actix_web::http::header::{ - HeaderValue, IntoHeaderValue, InvalidHeaderValueBytes, + HeaderValue, IntoHeaderValue, InvalidHeaderValue, }; use base64; use bytes::{BufMut, BytesMut}; @@ -101,7 +101,7 @@ impl fmt::Display for Basic { } impl IntoHeaderValue for Basic { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result::Error> { let mut credentials = BytesMut::with_capacity( @@ -123,7 +123,7 @@ impl IntoHeaderValue for Basic { value.put("Basic "); value.put(&encoded); - HeaderValue::from_shared(value.freeze()) + HeaderValue::from_maybe_shared(value.freeze()) } } diff --git a/src/headers/authorization/scheme/bearer.rs b/src/headers/authorization/scheme/bearer.rs index d9eac636d..c163fbdda 100644 --- a/src/headers/authorization/scheme/bearer.rs +++ b/src/headers/authorization/scheme/bearer.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::fmt; use actix_web::http::header::{ - HeaderValue, IntoHeaderValue, InvalidHeaderValueBytes, + HeaderValue, IntoHeaderValue, InvalidHeaderValue, }; use bytes::{BufMut, BytesMut}; @@ -76,14 +76,14 @@ impl fmt::Display for Bearer { } impl IntoHeaderValue for Bearer { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result::Error> { let mut buffer = BytesMut::with_capacity(7 + self.token.len()); buffer.put("Bearer "); buffer.extend_from_slice(self.token.as_bytes()); - HeaderValue::from_shared(buffer.freeze()) + HeaderValue::from_maybe_shared(buffer.freeze()) } } diff --git a/src/headers/www_authenticate/challenge/basic.rs b/src/headers/www_authenticate/challenge/basic.rs index d5699e01a..b4cfa3d7a 100644 --- a/src/headers/www_authenticate/challenge/basic.rs +++ b/src/headers/www_authenticate/challenge/basic.rs @@ -6,7 +6,7 @@ use std::fmt; use std::str; use actix_web::http::header::{ - HeaderValue, IntoHeaderValue, InvalidHeaderValueBytes, + HeaderValue, IntoHeaderValue, InvalidHeaderValue, }; use bytes::{BufMut, Bytes, BytesMut}; @@ -106,10 +106,10 @@ impl fmt::Display for Basic { } impl IntoHeaderValue for Basic { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result::Error> { - HeaderValue::from_shared(self.to_bytes()) + HeaderValue::from_maybe_shared(self.to_bytes()) } } diff --git a/src/headers/www_authenticate/challenge/bearer/challenge.rs b/src/headers/www_authenticate/challenge/bearer/challenge.rs index 92f977dda..b6f85c1de 100644 --- a/src/headers/www_authenticate/challenge/bearer/challenge.rs +++ b/src/headers/www_authenticate/challenge/bearer/challenge.rs @@ -3,7 +3,7 @@ use std::fmt; use std::str; use actix_web::http::header::{ - HeaderValue, IntoHeaderValue, InvalidHeaderValueBytes, + HeaderValue, IntoHeaderValue, InvalidHeaderValue, }; use bytes::{BufMut, Bytes, BytesMut}; @@ -133,9 +133,9 @@ impl fmt::Display for Bearer { } impl IntoHeaderValue for Bearer { - type Error = InvalidHeaderValueBytes; + type Error = InvalidHeaderValue; fn try_into(self) -> Result::Error> { - HeaderValue::from_shared(self.to_bytes()) + HeaderValue::from_maybe_shared(self.to_bytes()) } } diff --git a/src/middleware.rs b/src/middleware.rs index d9ce8c563..3ef8a9018 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -1,14 +1,15 @@ //! HTTP Authentication middleware. use std::marker::PhantomData; +use std::pin::Pin; use std::sync::Arc; use actix_service::{Service, Transform}; use actix_web::dev::{ServiceRequest, ServiceResponse}; use actix_web::Error; -use futures::future::{self, FutureResult}; -use futures::{Async, Future, IntoFuture, Poll}; -use futures_locks::Mutex; +use futures::future::{self, Future, FutureExt, LocalBoxFuture, TryFutureExt}; +use futures::lock::Mutex; +use futures::task::{Context, Poll}; use crate::extractors::{basic, bearer, AuthExtractor}; @@ -35,7 +36,7 @@ impl HttpAuthentication where T: AuthExtractor, F: Fn(ServiceRequest, T) -> O, - O: IntoFuture, + O: Future>, { /// Construct `HttpAuthentication` middleware /// with the provided auth extractor `T` and @@ -51,7 +52,7 @@ where impl HttpAuthentication where F: Fn(ServiceRequest, basic::BasicAuth) -> O, - O: IntoFuture, + O: Future>, { /// Construct `HttpAuthentication` middleware for the HTTP "Basic" /// authentication scheme. @@ -61,7 +62,6 @@ where /// ```rust /// # use actix_web::Error; /// # use actix_web::dev::ServiceRequest; - /// # use futures::future::{self, FutureResult}; /// # use actix_web_httpauth::middleware::HttpAuthentication; /// # use actix_web_httpauth::extractors::basic::BasicAuth; /// // In this example validator returns immediately, @@ -69,12 +69,12 @@ where /// // that implements `IntoFuture` trait, /// // it can be extended to query database /// // or to do something else in a async manner. - /// fn validator( + /// async fn validator( /// req: ServiceRequest, /// credentials: BasicAuth, - /// ) -> FutureResult { + /// ) -> Result { /// // All users are great and more than welcome! - /// future::ok(req) + /// Ok(req) /// } /// /// let middleware = HttpAuthentication::basic(validator); @@ -87,7 +87,7 @@ where impl HttpAuthentication where F: Fn(ServiceRequest, bearer::BearerAuth) -> O, - O: IntoFuture, + O: Future>, { /// Construct `HttpAuthentication` middleware for the HTTP "Bearer" /// authentication scheme. @@ -97,20 +97,19 @@ where /// ```rust /// # use actix_web::Error; /// # use actix_web::dev::ServiceRequest; - /// # use futures::future::{self, FutureResult}; /// # use actix_web_httpauth::middleware::HttpAuthentication; /// # use actix_web_httpauth::extractors::bearer::{Config, BearerAuth}; /// # use actix_web_httpauth::extractors::{AuthenticationError, AuthExtractorConfig}; - /// fn validator(req: ServiceRequest, credentials: BearerAuth) -> FutureResult { + /// async fn validator(req: ServiceRequest, credentials: BearerAuth) -> Result { /// if credentials.token() == "mF_9.B5f-4.1JqM" { - /// future::ok(req) + /// Ok(req) /// } else { /// let config = req.app_data::() /// .map(|data| data.get_ref().clone()) /// .unwrap_or_else(Default::default) /// .scope("urn:example:channel=HBO&urn:example:rating=G,PG-13"); /// - /// future::err(AuthenticationError::from(config).into()) + /// Err(AuthenticationError::from(config).into()) /// } /// } /// @@ -130,7 +129,7 @@ where > + 'static, S::Future: 'static, F: Fn(ServiceRequest, T) -> O + 'static, - O: IntoFuture + 'static, + O: Future> + 'static, T: AuthExtractor + 'static, { type Request = ServiceRequest; @@ -138,11 +137,11 @@ where type Error = Error; type Transform = AuthenticationMiddleware; type InitError = (); - type Future = FutureResult; + type Future = future::Ready>; fn new_transform(&self, service: S) -> Self::Future { future::ok(AuthenticationMiddleware { - service: Mutex::new(service), + service: Arc::new(Mutex::new(service)), process_fn: self.process_fn.clone(), _extractor: PhantomData, }) @@ -154,7 +153,7 @@ pub struct AuthenticationMiddleware where T: AuthExtractor, { - service: Mutex, + service: Arc>, process_fn: Arc, _extractor: PhantomData, } @@ -168,19 +167,22 @@ where > + 'static, S::Future: 'static, F: Fn(ServiceRequest, T) -> O + 'static, - O: IntoFuture + 'static, + O: Future> + 'static, T: AuthExtractor + 'static, { type Request = ServiceRequest; type Response = ServiceResponse; type Error = S::Error; - type Future = Box, Error = Error>>; + type Future = LocalBoxFuture<'static, Result, Error>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { + fn poll_ready( + &mut self, + ctx: &mut Context<'_>, + ) -> Poll> { self.service .try_lock() .expect("AuthenticationMiddleware was called already") - .poll_ready() + .poll_ready(ctx) } fn call(&mut self, req: Self::Request) -> Self::Future { @@ -188,23 +190,20 @@ where // Note: cloning the mutex, not the service itself let inner = self.service.clone(); - let f = Extract::new(req) - .and_then(move |(req, credentials)| (process_fn)(req, credentials)) - .and_then(move |req| { - inner - .lock() - .map_err(Into::into) - .and_then(|mut service| service.call(req)) - }); - - Box::new(f) + async move { + let (req, credentials) = Extract::::new(req).await?; + let req = process_fn(req, credentials).await?; + let mut service = inner.lock().await; + service.call(req).await + } + .boxed_local() } } struct Extract { req: Option, - f: Option>>, - _extractor: PhantomData, + f: Option>>, + _extractor: PhantomData T>, } impl Extract { @@ -223,26 +222,26 @@ where T::Future: 'static, T::Error: 'static, { - type Item = (ServiceRequest, T); - type Error = Error; + type Output = Result<(ServiceRequest, T), Error>; - fn poll(&mut self) -> Poll { + fn poll( + mut self: Pin<&mut Self>, + ctx: &mut Context<'_>, + ) -> Poll { if self.f.is_none() { let req = self.req.as_ref().expect("Extract future was polled twice!"); - let f = T::from_service_request(req) - .into_future() - .map_err(Into::into); - self.f = Some(Box::new(f)); + let f = T::from_service_request(req).map_err(Into::into); + self.f = Some(f.boxed_local()); } let f = self .f .as_mut() .expect("Extraction future should be initialized at this point"); - let credentials = futures::try_ready!(f.poll()); + let credentials = futures::ready!(Future::poll(f.as_mut(), ctx))?; let req = self.req.take().expect("Extract future was polled twice!"); - Ok(Async::Ready((req, credentials))) + Poll::Ready(Ok((req, credentials))) } }