mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-27 17:22:57 +01:00
Update dependencies (Tokio 1.0) (#144)
This commit is contained in:
parent
86ff1302ad
commit
ca85f6b245
2
.github/workflows/msrv.yml
vendored
2
.github/workflows/msrv.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version:
|
||||
- 1.42.0
|
||||
- 1.46.0
|
||||
|
||||
name: ${{ matrix.version }} - x86_64-unknown-linux-gnu
|
||||
runs-on: ubuntu-latest
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2021-xx-xx
|
||||
|
||||
* Minimum supported Rust version (MSRV) is now 1.46.0.
|
||||
|
||||
|
||||
## 0.5.4 - 2020-12-31
|
||||
|
@ -19,7 +19,7 @@ name = "actix_cors"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "3.0.0", default-features = false }
|
||||
actix-web = { version = "4.0.0-beta.4", default-features = false }
|
||||
derive_more = "0.99.5"
|
||||
futures-util = { version = "0.3.7", default-features = false }
|
||||
log = "0.4"
|
||||
@ -27,7 +27,7 @@ once_cell = "1"
|
||||
tinyvec = { version = "1", features = ["alloc"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-service = "1"
|
||||
actix-rt = "1"
|
||||
actix-service = "2.0.0-beta.5"
|
||||
actix-rt = "2"
|
||||
pretty_env_logger = "0.4"
|
||||
regex = "1.4"
|
||||
|
@ -12,4 +12,4 @@
|
||||
- [API Documentation](https://docs.rs/actix-cors)
|
||||
- [Example Project](https://github.com/actix/examples/tree/master/security/web-cors)
|
||||
- [Chat on Gitter](https://gitter.im/actix/actix-web)
|
||||
- Minimum Supported Rust Version (MSRV): 1.42.0
|
||||
- Minimum Supported Rust Version (MSRV): 1.46.0
|
||||
|
@ -145,7 +145,7 @@ impl Cors {
|
||||
match TryInto::<Uri>::try_into(origin) {
|
||||
Ok(_) if origin == "*" => {
|
||||
error!("Wildcard in `allowed_origin` is not allowed. Use `send_wildcard`.");
|
||||
self.error = Some(Either::B(CorsError::WildcardOrigin));
|
||||
self.error = Some(Either::Right(CorsError::WildcardOrigin));
|
||||
}
|
||||
|
||||
Ok(_) => {
|
||||
@ -162,7 +162,7 @@ impl Cors {
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
self.error = Some(Either::A(err.into()));
|
||||
self.error = Some(Either::Left(err.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -224,7 +224,7 @@ impl Cors {
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
self.error = Some(Either::A(err.into()));
|
||||
self.error = Some(Either::Left(err.into()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -266,7 +266,7 @@ impl Cors {
|
||||
}
|
||||
}
|
||||
|
||||
Err(err) => self.error = Some(Either::A(err.into())),
|
||||
Err(err) => self.error = Some(Either::Left(err.into())),
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ impl Cors {
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
self.error = Some(Either::A(err.into()));
|
||||
self.error = Some(Either::Left(err.into()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -351,7 +351,7 @@ impl Cors {
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
self.error = Some(Either::A(err.into()));
|
||||
self.error = Some(Either::Left(err.into()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -483,13 +483,12 @@ impl Default for Cors {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B> Transform<S> for Cors
|
||||
impl<S, B> Transform<S, ServiceRequest> for Cors
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S::Future: 'static,
|
||||
B: 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type InitError = ();
|
||||
@ -499,8 +498,8 @@ where
|
||||
fn new_transform(&self, service: S) -> Self::Future {
|
||||
if let Some(ref err) = self.error {
|
||||
match err {
|
||||
Either::A(err) => error!("{}", err),
|
||||
Either::B(err) => error!("{}", err),
|
||||
Either::Left(err) => error!("{}", err),
|
||||
Either::Right(err) => error!("{}", err),
|
||||
}
|
||||
|
||||
return future::err(());
|
||||
@ -592,15 +591,16 @@ mod test {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn restrictive_defaults() {
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.new_transform(test::ok_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::default()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
|
||||
|
@ -235,8 +235,8 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::get()
|
||||
.header(header::ORIGIN, "https://www.unknown.com")
|
||||
.header(header::ACCESS_CONTROL_REQUEST_HEADERS, "DNT")
|
||||
.insert_header((header::ORIGIN, "https://www.unknown.com"))
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_HEADERS, "DNT"))
|
||||
.to_srv_request();
|
||||
|
||||
assert!(cors.inner.validate_origin(req.head()).is_err());
|
||||
@ -257,34 +257,37 @@ mod test {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::default()
|
||||
.method(Method::OPTIONS)
|
||||
.header(header::ACCESS_CONTROL_REQUEST_HEADERS, "X-Not-Allowed")
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_HEADERS, "X-Not-Allowed"))
|
||||
.to_srv_request();
|
||||
|
||||
assert!(cors.inner.validate_allowed_method(req.head()).is_err());
|
||||
assert!(cors.inner.validate_allowed_headers(req.head()).is_err());
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "put")
|
||||
let req = TestRequest::default()
|
||||
.method(Method::OPTIONS)
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "put"))
|
||||
.to_srv_request();
|
||||
|
||||
assert!(cors.inner.validate_allowed_method(req.head()).is_err());
|
||||
assert!(cors.inner.validate_allowed_headers(req.head()).is_ok());
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST")
|
||||
.header(
|
||||
let req = TestRequest::default()
|
||||
.method(Method::OPTIONS)
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "POST"))
|
||||
.insert_header((
|
||||
header::ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
"AUTHORIZATION,ACCEPT",
|
||||
)
|
||||
.method(Method::OPTIONS)
|
||||
))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(
|
||||
Some(&b"*"[..]),
|
||||
resp.headers()
|
||||
@ -319,16 +322,17 @@ mod test {
|
||||
|
||||
Rc::get_mut(&mut cors.inner).unwrap().preflight = false;
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST")
|
||||
.header(
|
||||
let req = TestRequest::default()
|
||||
.method(Method::OPTIONS)
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "POST"))
|
||||
.insert_header((
|
||||
header::ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
"AUTHORIZATION,ACCEPT",
|
||||
)
|
||||
.method(Method::OPTIONS)
|
||||
))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
}
|
||||
|
@ -42,34 +42,34 @@ impl<S> CorsMiddleware<S> {
|
||||
let mut res = HttpResponse::Ok();
|
||||
|
||||
if let Some(origin) = inner.access_control_allow_origin(req.head()) {
|
||||
res.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin);
|
||||
res.insert_header((header::ACCESS_CONTROL_ALLOW_ORIGIN, origin));
|
||||
}
|
||||
|
||||
if let Some(ref allowed_methods) = inner.allowed_methods_baked {
|
||||
res.header(
|
||||
res.insert_header((
|
||||
header::ACCESS_CONTROL_ALLOW_METHODS,
|
||||
allowed_methods.clone(),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(ref headers) = inner.allowed_headers_baked {
|
||||
res.header(header::ACCESS_CONTROL_ALLOW_HEADERS, headers.clone());
|
||||
res.insert_header((header::ACCESS_CONTROL_ALLOW_HEADERS, headers.clone()));
|
||||
} else if let Some(headers) =
|
||||
req.headers().get(header::ACCESS_CONTROL_REQUEST_HEADERS)
|
||||
{
|
||||
// all headers allowed, return
|
||||
res.header(header::ACCESS_CONTROL_ALLOW_HEADERS, headers.clone());
|
||||
res.insert_header((header::ACCESS_CONTROL_ALLOW_HEADERS, headers.clone()));
|
||||
}
|
||||
|
||||
if inner.supports_credentials {
|
||||
res.header(
|
||||
res.insert_header((
|
||||
header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
||||
HeaderValue::from_static("true"),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(max_age) = inner.max_age {
|
||||
res.header(header::ACCESS_CONTROL_MAX_AGE, max_age.to_string());
|
||||
res.insert_header((header::ACCESS_CONTROL_MAX_AGE, max_age.to_string()));
|
||||
}
|
||||
|
||||
let res = res.finish();
|
||||
@ -121,22 +121,21 @@ type CorsMiddlewareServiceFuture<B> = Either<
|
||||
LocalBoxFuture<'static, Result<ServiceResponse<B>, Error>>,
|
||||
>;
|
||||
|
||||
impl<S, B> Service for CorsMiddleware<S>
|
||||
impl<S, B> Service<ServiceRequest> for CorsMiddleware<S>
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||
S::Future: 'static,
|
||||
B: 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type Future = CorsMiddlewareServiceFuture<B>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: ServiceRequest) -> Self::Future {
|
||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||
if self.inner.preflight && req.method() == Method::OPTIONS {
|
||||
let inner = Rc::clone(&self.inner);
|
||||
let res = Self::handle_preflight(&inner, req);
|
||||
@ -187,7 +186,7 @@ mod tests {
|
||||
// Tests case where allowed_origins is All but there are validate functions to run incase.
|
||||
// In this case, origins are only allowed when the DNT header is sent.
|
||||
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allow_any_origin()
|
||||
.allowed_origin_fn(|origin, req_head| {
|
||||
assert_eq!(&origin, req_head.headers.get(header::ORIGIN).unwrap());
|
||||
@ -199,7 +198,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::get()
|
||||
.header(header::ORIGIN, "http://example.com")
|
||||
.insert_header((header::ORIGIN, "http://example.com"))
|
||||
.to_srv_request();
|
||||
let res = cors.call(req).await.unwrap();
|
||||
assert_eq!(
|
||||
@ -210,8 +209,8 @@ mod tests {
|
||||
);
|
||||
|
||||
let req = TestRequest::get()
|
||||
.header(header::ORIGIN, "http://example.com")
|
||||
.header(header::DNT, "1")
|
||||
.insert_header((header::ORIGIN, "http://example.com"))
|
||||
.insert_header((header::DNT, "1"))
|
||||
.to_srv_request();
|
||||
let res = cors.call(req).await.unwrap();
|
||||
assert_eq!(
|
||||
|
@ -26,7 +26,7 @@ async fn test_wildcard_origin() {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_not_allowed_origin_fn() {
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allowed_origin("https://www.example.com")
|
||||
.allowed_origin_fn(|origin, req| {
|
||||
assert_eq!(&origin, req.headers.get(header::ORIGIN).unwrap());
|
||||
@ -42,11 +42,11 @@ async fn test_not_allowed_origin_fn() {
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.method(Method::GET)
|
||||
let req = TestRequest::get()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
|
||||
assert_eq!(
|
||||
Some(&b"https://www.example.com"[..]),
|
||||
@ -57,11 +57,11 @@ async fn test_not_allowed_origin_fn() {
|
||||
}
|
||||
|
||||
{
|
||||
let req = TestRequest::with_header("Origin", "https://www.known.com")
|
||||
.method(Method::GET)
|
||||
let req = TestRequest::get()
|
||||
.insert_header(("Origin", "https://www.known.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
|
||||
assert_eq!(
|
||||
None,
|
||||
@ -72,7 +72,7 @@ async fn test_not_allowed_origin_fn() {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_allowed_origin_fn() {
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allowed_origin("https://www.example.com")
|
||||
.allowed_origin_fn(|origin, req| {
|
||||
assert_eq!(&origin, req.headers.get(header::ORIGIN).unwrap());
|
||||
@ -87,11 +87,11 @@ async fn test_allowed_origin_fn() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.method(Method::GET)
|
||||
let req = TestRequest::get()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
|
||||
assert_eq!(
|
||||
"https://www.example.com",
|
||||
@ -101,11 +101,11 @@ async fn test_allowed_origin_fn() {
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.unknown.com")
|
||||
.method(Method::GET)
|
||||
let req = TestRequest::get()
|
||||
.insert_header(("Origin", "https://www.unknown.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
|
||||
assert_eq!(
|
||||
Some(&b"https://www.unknown.com"[..]),
|
||||
@ -119,7 +119,7 @@ async fn test_allowed_origin_fn() {
|
||||
async fn test_allowed_origin_fn_with_environment() {
|
||||
let regex = Regex::new("https:.+\\.unknown\\.com").unwrap();
|
||||
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allowed_origin("https://www.example.com")
|
||||
.allowed_origin_fn(move |origin, req| {
|
||||
assert_eq!(&origin, req.headers.get(header::ORIGIN).unwrap());
|
||||
@ -134,11 +134,11 @@ async fn test_allowed_origin_fn_with_environment() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.method(Method::GET)
|
||||
let req = TestRequest::get()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
|
||||
assert_eq!(
|
||||
"https://www.example.com",
|
||||
@ -148,11 +148,11 @@ async fn test_allowed_origin_fn_with_environment() {
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.unknown.com")
|
||||
.method(Method::GET)
|
||||
let req = TestRequest::get()
|
||||
.insert_header(("Origin", "https://www.unknown.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
|
||||
assert_eq!(
|
||||
Some(&b"https://www.unknown.com"[..]),
|
||||
@ -164,7 +164,7 @@ async fn test_allowed_origin_fn_with_environment() {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_multiple_origins_preflight() {
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allowed_origin("https://example.com")
|
||||
.allowed_origin("https://example.org")
|
||||
.allowed_methods(vec![Method::GET])
|
||||
@ -172,12 +172,13 @@ async fn test_multiple_origins_preflight() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://example.com")
|
||||
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET")
|
||||
let req = TestRequest::default()
|
||||
.insert_header(("Origin", "https://example.com"))
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "GET"))
|
||||
.method(Method::OPTIONS)
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(
|
||||
Some(&b"https://example.com"[..]),
|
||||
resp.headers()
|
||||
@ -185,12 +186,13 @@ async fn test_multiple_origins_preflight() {
|
||||
.map(HeaderValue::as_bytes)
|
||||
);
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://example.org")
|
||||
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "GET")
|
||||
let req = TestRequest::default()
|
||||
.insert_header(("Origin", "https://example.org"))
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "GET"))
|
||||
.method(Method::OPTIONS)
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(
|
||||
Some(&b"https://example.org"[..]),
|
||||
resp.headers()
|
||||
@ -201,7 +203,7 @@ async fn test_multiple_origins_preflight() {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_multiple_origins() {
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allowed_origin("https://example.com")
|
||||
.allowed_origin("https://example.org")
|
||||
.allowed_methods(vec![Method::GET])
|
||||
@ -209,11 +211,11 @@ async fn test_multiple_origins() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://example.com")
|
||||
.method(Method::GET)
|
||||
let req = TestRequest::get()
|
||||
.insert_header(("Origin", "https://example.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(
|
||||
Some(&b"https://example.com"[..]),
|
||||
resp.headers()
|
||||
@ -221,11 +223,11 @@ async fn test_multiple_origins() {
|
||||
.map(HeaderValue::as_bytes)
|
||||
);
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://example.org")
|
||||
.method(Method::GET)
|
||||
let req = TestRequest::get()
|
||||
.insert_header(("Origin", "https://example.org"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(
|
||||
Some(&b"https://example.org"[..]),
|
||||
resp.headers()
|
||||
@ -237,7 +239,7 @@ async fn test_multiple_origins() {
|
||||
#[actix_rt::test]
|
||||
async fn test_response() {
|
||||
let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT];
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allow_any_origin()
|
||||
.send_wildcard()
|
||||
.disable_preflight()
|
||||
@ -250,10 +252,11 @@ async fn test_response() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::default()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.method(Method::OPTIONS)
|
||||
.to_srv_request();
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(
|
||||
Some(&b"*"[..]),
|
||||
resp.headers()
|
||||
@ -283,7 +286,7 @@ async fn test_response() {
|
||||
}
|
||||
|
||||
let exposed_headers = vec![header::AUTHORIZATION, header::ACCEPT];
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allow_any_origin()
|
||||
.send_wildcard()
|
||||
.disable_preflight()
|
||||
@ -294,22 +297,25 @@ async fn test_response() {
|
||||
.allowed_header(header::CONTENT_TYPE)
|
||||
.new_transform(fn_service(|req: ServiceRequest| {
|
||||
ok(req.into_response({
|
||||
HttpResponse::Ok().header(header::VARY, "Accept").finish()
|
||||
HttpResponse::Ok()
|
||||
.insert_header((header::VARY, "Accept"))
|
||||
.finish()
|
||||
}))
|
||||
}))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::default()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.method(Method::OPTIONS)
|
||||
.to_srv_request();
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(
|
||||
Some(&b"Accept, Origin"[..]),
|
||||
resp.headers().get(header::VARY).map(HeaderValue::as_bytes)
|
||||
);
|
||||
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.disable_vary_header()
|
||||
.allowed_methods(vec!["POST"])
|
||||
.allowed_origin("https://www.example.com")
|
||||
@ -318,11 +324,12 @@ async fn test_response() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::default()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.method(Method::OPTIONS)
|
||||
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST")
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "POST"))
|
||||
.to_srv_request();
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
let origins_str = resp
|
||||
.headers()
|
||||
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
@ -332,39 +339,40 @@ async fn test_response() {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_validate_origin() {
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allowed_origin("https://www.example.com")
|
||||
.new_transform(test::ok_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
.method(Method::GET)
|
||||
let req = TestRequest::get()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_no_origin_response() {
|
||||
let mut cors = Cors::permissive()
|
||||
let cors = Cors::permissive()
|
||||
.disable_preflight()
|
||||
.new_transform(test::ok_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::default().method(Method::GET).to_srv_request();
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert!(resp
|
||||
.headers()
|
||||
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.is_none());
|
||||
|
||||
let req = TestRequest::with_header("Origin", "https://www.example.com")
|
||||
let req = TestRequest::default()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.method(Method::OPTIONS)
|
||||
.to_srv_request();
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(
|
||||
Some(&b"https://www.example.com"[..]),
|
||||
resp.headers()
|
||||
@ -375,21 +383,22 @@ async fn test_no_origin_response() {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn validate_origin_allows_all_origins() {
|
||||
let mut cors = Cors::permissive()
|
||||
let cors = Cors::permissive()
|
||||
.new_transform(test::ok_service())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req =
|
||||
TestRequest::with_header("Origin", "https://www.example.com").to_srv_request();
|
||||
let req = TestRequest::default()
|
||||
.insert_header(("Origin", "https://www.example.com"))
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_allow_any_origin_any_method_any_header() {
|
||||
let mut cors = Cors::default()
|
||||
let cors = Cors::default()
|
||||
.allow_any_origin()
|
||||
.allow_any_method()
|
||||
.allow_any_header()
|
||||
@ -397,12 +406,13 @@ async fn test_allow_any_origin_any_method_any_header() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let req = TestRequest::with_header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST")
|
||||
.header(header::ACCESS_CONTROL_REQUEST_HEADERS, "content-type")
|
||||
.header(header::ORIGIN, "https://www.example.com")
|
||||
let req = TestRequest::default()
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_METHOD, "POST"))
|
||||
.insert_header((header::ACCESS_CONTROL_REQUEST_HEADERS, "content-type"))
|
||||
.insert_header((header::ORIGIN, "https://www.example.com"))
|
||||
.method(Method::OPTIONS)
|
||||
.to_srv_request();
|
||||
|
||||
let resp = test::call_service(&mut cors, req).await;
|
||||
let resp = test::call_service(&cors, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2020-xx-xx
|
||||
* Minimum supported Rust version (MSRV) is now 1.46.0.
|
||||
|
||||
|
||||
## 0.3.1 - 2020-09-20
|
||||
|
@ -16,13 +16,13 @@ name = "actix_identity"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "3.0.0", default-features = false, features = ["secure-cookies"] }
|
||||
actix-service = "1.0.6"
|
||||
futures-util = { version = "0.3.4", default-features = false }
|
||||
actix-web = { version = "4.0.0-beta.4", default-features = false, features = ["secure-cookies"] }
|
||||
actix-service = "2.0.0-beta.5"
|
||||
futures-util = { version = "0.3", default-features = false }
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
time = { version = "0.2.7", default-features = false, features = ["std"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "1.1.1"
|
||||
actix-http = "2.0.0"
|
||||
actix-rt = "2"
|
||||
actix-http = "3.0.0-beta.4"
|
||||
|
@ -223,15 +223,13 @@ impl<T> IdentityService<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T, B> Transform<S> for IdentityService<T>
|
||||
impl<S, T, B> Transform<S, ServiceRequest> for IdentityService<T>
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>
|
||||
+ 'static,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||
S::Future: 'static,
|
||||
T: IdentityPolicy,
|
||||
B: 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type InitError = ();
|
||||
@ -261,24 +259,22 @@ impl<S, T> Clone for IdentityServiceMiddleware<S, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T, B> Service for IdentityServiceMiddleware<S, T>
|
||||
impl<S, T, B> Service<ServiceRequest> for IdentityServiceMiddleware<S, T>
|
||||
where
|
||||
B: 'static,
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>
|
||||
+ 'static,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||
S::Future: 'static,
|
||||
T: IdentityPolicy,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.borrow_mut().poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
||||
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||
let srv = self.service.clone();
|
||||
let backend = self.backend.clone();
|
||||
let fut = self.backend.from_request(&mut req);
|
||||
@ -637,7 +633,7 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity() {
|
||||
let mut srv = test::init_service(
|
||||
let srv = test::init_service(
|
||||
App::new()
|
||||
.wrap(IdentityService::new(
|
||||
CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
|
||||
@ -668,18 +664,16 @@ mod tests {
|
||||
)
|
||||
.await;
|
||||
let resp =
|
||||
test::call_service(&mut srv, TestRequest::with_uri("/index").to_request())
|
||||
.await;
|
||||
test::call_service(&srv, TestRequest::with_uri("/index").to_request()).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
|
||||
let resp =
|
||||
test::call_service(&mut srv, TestRequest::with_uri("/login").to_request())
|
||||
.await;
|
||||
test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
let c = resp.response().cookies().next().unwrap().to_owned();
|
||||
|
||||
let resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/index")
|
||||
.cookie(c.clone())
|
||||
.to_request(),
|
||||
@ -688,7 +682,7 @@ mod tests {
|
||||
assert_eq!(resp.status(), StatusCode::CREATED);
|
||||
|
||||
let resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/logout")
|
||||
.cookie(c.clone())
|
||||
.to_request(),
|
||||
@ -701,7 +695,7 @@ mod tests {
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_max_age_time() {
|
||||
let duration = Duration::days(1);
|
||||
let mut srv = test::init_service(
|
||||
let srv = test::init_service(
|
||||
App::new()
|
||||
.wrap(IdentityService::new(
|
||||
CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
|
||||
@ -718,8 +712,7 @@ mod tests {
|
||||
)
|
||||
.await;
|
||||
let resp =
|
||||
test::call_service(&mut srv, TestRequest::with_uri("/login").to_request())
|
||||
.await;
|
||||
test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
assert!(resp.headers().contains_key(header::SET_COOKIE));
|
||||
let c = resp.response().cookies().next().unwrap().to_owned();
|
||||
@ -728,7 +721,7 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_http_only_same_site() {
|
||||
let mut srv = test::init_service(
|
||||
let srv = test::init_service(
|
||||
App::new()
|
||||
.wrap(IdentityService::new(
|
||||
CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
|
||||
@ -746,8 +739,7 @@ mod tests {
|
||||
.await;
|
||||
|
||||
let resp =
|
||||
test::call_service(&mut srv, TestRequest::with_uri("/login").to_request())
|
||||
.await;
|
||||
test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await;
|
||||
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
assert!(resp.headers().contains_key(header::SET_COOKIE));
|
||||
@ -760,7 +752,7 @@ mod tests {
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_max_age() {
|
||||
let seconds = 60;
|
||||
let mut srv = test::init_service(
|
||||
let srv = test::init_service(
|
||||
App::new()
|
||||
.wrap(IdentityService::new(
|
||||
CookieIdentityPolicy::new(&COOKIE_KEY_MASTER)
|
||||
@ -777,8 +769,7 @@ mod tests {
|
||||
)
|
||||
.await;
|
||||
let resp =
|
||||
test::call_service(&mut srv, TestRequest::with_uri("/login").to_request())
|
||||
.await;
|
||||
test::call_service(&srv, TestRequest::with_uri("/login").to_request()).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
assert!(resp.headers().contains_key(header::SET_COOKIE));
|
||||
let c = resp.response().cookies().next().unwrap().to_owned();
|
||||
@ -790,7 +781,7 @@ mod tests {
|
||||
>(
|
||||
f: F,
|
||||
) -> impl actix_service::Service<
|
||||
Request = actix_http::Request,
|
||||
actix_http::Request,
|
||||
Response = ServiceResponse<actix_web::body::Body>,
|
||||
Error = Error,
|
||||
> {
|
||||
@ -925,19 +916,19 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_legacy_cookie_is_set() {
|
||||
let mut srv = create_identity_server(|c| c).await;
|
||||
let srv = create_identity_server(|c| c).await;
|
||||
let mut resp =
|
||||
test::call_service(&mut srv, TestRequest::with_uri("/").to_request()).await;
|
||||
test::call_service(&srv, TestRequest::with_uri("/").to_request()).await;
|
||||
assert_legacy_login_cookie(&mut resp, COOKIE_LOGIN);
|
||||
assert_logged_in(resp, None).await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_legacy_cookie_works() {
|
||||
let mut srv = create_identity_server(|c| c).await;
|
||||
let srv = create_identity_server(|c| c).await;
|
||||
let cookie = legacy_login_cookie(COOKIE_LOGIN);
|
||||
let mut resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/")
|
||||
.cookie(cookie.clone())
|
||||
.to_request(),
|
||||
@ -949,11 +940,10 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_legacy_cookie_rejected_if_visit_timestamp_needed() {
|
||||
let mut srv =
|
||||
create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
|
||||
let srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
|
||||
let cookie = legacy_login_cookie(COOKIE_LOGIN);
|
||||
let mut resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/")
|
||||
.cookie(cookie.clone())
|
||||
.to_request(),
|
||||
@ -970,11 +960,10 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_legacy_cookie_rejected_if_login_timestamp_needed() {
|
||||
let mut srv =
|
||||
create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
|
||||
let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
|
||||
let cookie = legacy_login_cookie(COOKIE_LOGIN);
|
||||
let mut resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/")
|
||||
.cookie(cookie.clone())
|
||||
.to_request(),
|
||||
@ -991,11 +980,10 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_cookie_rejected_if_login_timestamp_needed() {
|
||||
let mut srv =
|
||||
create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
|
||||
let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
|
||||
let cookie = login_cookie(COOKIE_LOGIN, None, Some(SystemTime::now()));
|
||||
let mut resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/")
|
||||
.cookie(cookie.clone())
|
||||
.to_request(),
|
||||
@ -1012,11 +1000,10 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_cookie_rejected_if_visit_timestamp_needed() {
|
||||
let mut srv =
|
||||
create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
|
||||
let srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
|
||||
let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None);
|
||||
let mut resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/")
|
||||
.cookie(cookie.clone())
|
||||
.to_request(),
|
||||
@ -1033,15 +1020,14 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_cookie_rejected_if_login_timestamp_too_old() {
|
||||
let mut srv =
|
||||
create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
|
||||
let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
|
||||
let cookie = login_cookie(
|
||||
COOKIE_LOGIN,
|
||||
Some(SystemTime::now() - Duration::days(180)),
|
||||
None,
|
||||
);
|
||||
let mut resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/")
|
||||
.cookie(cookie.clone())
|
||||
.to_request(),
|
||||
@ -1058,15 +1044,14 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_cookie_rejected_if_visit_timestamp_too_old() {
|
||||
let mut srv =
|
||||
create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
|
||||
let srv = create_identity_server(|c| c.visit_deadline(Duration::days(90))).await;
|
||||
let cookie = login_cookie(
|
||||
COOKIE_LOGIN,
|
||||
None,
|
||||
Some(SystemTime::now() - Duration::days(180)),
|
||||
);
|
||||
let mut resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/")
|
||||
.cookie(cookie.clone())
|
||||
.to_request(),
|
||||
@ -1083,11 +1068,10 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_cookie_not_updated_on_login_deadline() {
|
||||
let mut srv =
|
||||
create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
|
||||
let srv = create_identity_server(|c| c.login_deadline(Duration::days(90))).await;
|
||||
let cookie = login_cookie(COOKIE_LOGIN, Some(SystemTime::now()), None);
|
||||
let mut resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/")
|
||||
.cookie(cookie.clone())
|
||||
.to_request(),
|
||||
@ -1100,7 +1084,7 @@ mod tests {
|
||||
// https://github.com/actix/actix-web/issues/1263
|
||||
#[actix_rt::test]
|
||||
async fn test_identity_cookie_updated_on_visit_deadline() {
|
||||
let mut srv = create_identity_server(|c| {
|
||||
let srv = create_identity_server(|c| {
|
||||
c.visit_deadline(Duration::days(90))
|
||||
.login_deadline(Duration::days(90))
|
||||
})
|
||||
@ -1108,7 +1092,7 @@ mod tests {
|
||||
let timestamp = SystemTime::now() - Duration::days(1);
|
||||
let cookie = login_cookie(COOKIE_LOGIN, Some(timestamp), Some(timestamp));
|
||||
let mut resp = test::call_service(
|
||||
&mut srv,
|
||||
&srv,
|
||||
TestRequest::with_uri("/")
|
||||
.cookie(cookie.clone())
|
||||
.to_request(),
|
||||
@ -1146,22 +1130,22 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
let mut srv = IdentityServiceMiddleware {
|
||||
let srv = IdentityServiceMiddleware {
|
||||
backend: Rc::new(Ident),
|
||||
service: Rc::new(RefCell::new(into_service(
|
||||
|_: ServiceRequest| async move {
|
||||
actix_rt::time::delay_for(std::time::Duration::from_secs(100)).await;
|
||||
actix_rt::time::sleep(std::time::Duration::from_secs(100)).await;
|
||||
Err::<ServiceResponse, _>(error::ErrorBadRequest("error"))
|
||||
},
|
||||
))),
|
||||
};
|
||||
|
||||
let mut srv2 = srv.clone();
|
||||
let srv2 = srv.clone();
|
||||
let req = TestRequest::default().to_srv_request();
|
||||
actix_rt::spawn(async move {
|
||||
let _ = srv2.call(req).await;
|
||||
});
|
||||
actix_rt::time::delay_for(std::time::Duration::from_millis(50)).await;
|
||||
actix_rt::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
|
||||
let _ = lazy(|cx| srv.poll_ready(cx)).await;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Changes
|
||||
|
||||
## Unreleased - 2020-xx-xx
|
||||
* Minimum supported Rust version (MSRV) is now 1.46.0.
|
||||
|
||||
|
||||
## 0.6.0 - 2020-09-11
|
||||
|
@ -19,11 +19,11 @@ name = "actix_protobuf"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "3.0.0", default_features = false }
|
||||
actix-rt = "1.1.1"
|
||||
actix-web = { version = "4.0.0-beta.4", default_features = false }
|
||||
actix-rt = "2"
|
||||
futures-util = { version = "0.3.5", default-features = false }
|
||||
derive_more = "0.99"
|
||||
prost = "0.6.0"
|
||||
prost = "0.7"
|
||||
|
||||
[dev-dependencies]
|
||||
prost-derive = "0.6.0"
|
||||
prost-derive = "0.7"
|
||||
|
@ -8,9 +8,9 @@ authors = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
actix-web = "3.0.0"
|
||||
actix-web = "4.0.0-beta.4"
|
||||
actix-protobuf = { path = "../../" }
|
||||
|
||||
env_logger = "0.7"
|
||||
prost = "0.6.0"
|
||||
prost-derive = "0.6.0"
|
||||
env_logger = "0.8"
|
||||
prost = "0.7"
|
||||
prost-derive = "0.7"
|
||||
|
@ -17,7 +17,7 @@ use actix_web::error::{Error, PayloadError, ResponseError};
|
||||
use actix_web::http::header::{CONTENT_LENGTH, CONTENT_TYPE};
|
||||
use actix_web::web::BytesMut;
|
||||
use actix_web::{FromRequest, HttpMessage, HttpRequest, HttpResponse, Responder};
|
||||
use futures_util::future::{ready, FutureExt, LocalBoxFuture, Ready};
|
||||
use futures_util::future::{FutureExt, LocalBoxFuture};
|
||||
use futures_util::StreamExt;
|
||||
|
||||
#[derive(Debug, Display)]
|
||||
@ -137,21 +137,16 @@ where
|
||||
}
|
||||
|
||||
impl<T: Message + Default> Responder for ProtoBuf<T> {
|
||||
type Error = Error;
|
||||
type Future = Ready<Result<HttpResponse, Error>>;
|
||||
|
||||
fn respond_to(self, _: &HttpRequest) -> Self::Future {
|
||||
fn respond_to(self, _: &HttpRequest) -> HttpResponse {
|
||||
let mut buf = Vec::new();
|
||||
ready(
|
||||
self.0
|
||||
.encode(&mut buf)
|
||||
.map_err(|e| Error::from(ProtoBufPayloadError::Serialize(e)))
|
||||
.map(|()| {
|
||||
HttpResponse::Ok()
|
||||
.content_type("application/protobuf")
|
||||
.body(buf)
|
||||
}),
|
||||
)
|
||||
match self.0.encode(&mut buf) {
|
||||
Ok(()) => HttpResponse::Ok()
|
||||
.content_type("application/protobuf")
|
||||
.body(buf),
|
||||
Err(err) => HttpResponse::from_error(Error::from(
|
||||
ProtoBufPayloadError::Serialize(err),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,7 +250,7 @@ pub trait ProtoBufResponseBuilder {
|
||||
|
||||
impl ProtoBufResponseBuilder for HttpResponseBuilder {
|
||||
fn protobuf<T: Message>(&mut self, value: T) -> Result<HttpResponse, Error> {
|
||||
self.header(CONTENT_TYPE, "application/protobuf");
|
||||
self.append_header((CONTENT_TYPE, "application/protobuf"));
|
||||
|
||||
let mut body = Vec::new();
|
||||
value
|
||||
@ -313,16 +308,16 @@ mod tests {
|
||||
let protobuf = ProtoBufMessage::<MyObject>::new(&req, &mut pl).await;
|
||||
assert_eq!(protobuf.err().unwrap(), ProtoBufPayloadError::ContentType);
|
||||
|
||||
let (req, mut pl) =
|
||||
TestRequest::with_header(header::CONTENT_TYPE, "application/text")
|
||||
.to_http_parts();
|
||||
let (req, mut pl) = TestRequest::get()
|
||||
.append_header((header::CONTENT_TYPE, "application/text"))
|
||||
.to_http_parts();
|
||||
let protobuf = ProtoBufMessage::<MyObject>::new(&req, &mut pl).await;
|
||||
assert_eq!(protobuf.err().unwrap(), ProtoBufPayloadError::ContentType);
|
||||
|
||||
let (req, mut pl) =
|
||||
TestRequest::with_header(header::CONTENT_TYPE, "application/protobuf")
|
||||
.header(header::CONTENT_LENGTH, "10000")
|
||||
.to_http_parts();
|
||||
let (req, mut pl) = TestRequest::get()
|
||||
.append_header((header::CONTENT_TYPE, "application/protobuf"))
|
||||
.append_header((header::CONTENT_LENGTH, "10000"))
|
||||
.to_http_parts();
|
||||
let protobuf = ProtoBufMessage::<MyObject>::new(&req, &mut pl)
|
||||
.limit(100)
|
||||
.await;
|
||||
|
@ -22,7 +22,6 @@ default = ["web"]
|
||||
|
||||
# actix-web integration
|
||||
web = [
|
||||
"actix-http/actors",
|
||||
"actix-service",
|
||||
"actix-web",
|
||||
"actix-session/cookie-session",
|
||||
@ -32,28 +31,27 @@ web = [
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
actix = "0.10.0"
|
||||
actix-utils = "2.0.0"
|
||||
|
||||
log = "0.4.6"
|
||||
backoff = "0.2.1"
|
||||
derive_more = "0.99.2"
|
||||
futures-util = { version = "0.3.7", default-features = false }
|
||||
redis-async = "0.6.3"
|
||||
actix-rt = "1.1.1"
|
||||
futures-channel = { version = "0.3.5", default-features = false }
|
||||
redis-async = "0.9"
|
||||
time = "0.2.23"
|
||||
tokio = "0.2.6"
|
||||
tokio-util = "0.3.0"
|
||||
actix-rt = "2"
|
||||
tokio = "1"
|
||||
tokio-util = "0.6"
|
||||
|
||||
trust-dns-resolver = { version = "0.20.0", default-features = false, features = ["tokio-runtime", "system-config"] }
|
||||
|
||||
# actix-session
|
||||
actix-web = { version = "3.0.0", default_features = false, optional = true }
|
||||
actix-http = { version = "2.0.0", optional = true }
|
||||
actix-service = { version = "1.0.6", optional = true }
|
||||
actix-web = { version = "4.0.0-beta.4", default_features = false, optional = true }
|
||||
actix-http = { version = "3.0.0-beta.4", optional = true }
|
||||
actix-service = { version = "2.0.0-beta.5", optional = true }
|
||||
actix-session = { version = "0.4.0", optional = true }
|
||||
rand = { version = "0.7.0", optional = true }
|
||||
rand = { version = "0.8", optional = true }
|
||||
serde = { version = "1.0.101", optional = true }
|
||||
serde_json = { version = "1.0.40", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = "0.7"
|
||||
env_logger = "0.8"
|
||||
serde_derive = "1.0"
|
||||
|
@ -19,7 +19,7 @@ struct User {
|
||||
}
|
||||
|
||||
impl User {
|
||||
fn authenticate(credentials: Credentials) -> Result<Self, actix_http::Response> {
|
||||
fn authenticate(credentials: Credentials) -> Result<Self, HttpResponse> {
|
||||
// TODO: figure out why I keep getting hacked
|
||||
if &credentials.password != "hunter2" {
|
||||
return Err(HttpResponse::Unauthorized().json("Unauthorized"));
|
||||
@ -33,7 +33,7 @@ impl User {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_session(session: &Session) -> Result<i64, actix_http::Response> {
|
||||
pub fn validate_session(session: &Session) -> Result<i64, HttpResponse> {
|
||||
let user_id: Option<i64> = session.get("user_id").unwrap_or(None);
|
||||
|
||||
match user_id {
|
||||
@ -49,7 +49,7 @@ pub fn validate_session(session: &Session) -> Result<i64, actix_http::Response>
|
||||
async fn login(
|
||||
credentials: web::Json<Credentials>,
|
||||
session: Session,
|
||||
) -> Result<impl Responder, actix_http::Response> {
|
||||
) -> Result<impl Responder, HttpResponse> {
|
||||
let credentials = credentials.into_inner();
|
||||
|
||||
match User::authenticate(credentials) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
#![deny(rust_2018_idioms)]
|
||||
|
||||
mod redis;
|
||||
pub use redis::{Command, RedisActor};
|
||||
pub use redis::RedisClient;
|
||||
|
||||
use derive_more::{Display, Error, From};
|
||||
|
||||
@ -25,6 +25,12 @@ pub enum Error {
|
||||
/// Cancel all waters when connection get dropped
|
||||
#[display(fmt = "Redis: Disconnected")]
|
||||
Disconnected,
|
||||
/// Invalid address
|
||||
#[display(fmt = "Redis: Invalid address")]
|
||||
InvalidAddress,
|
||||
/// DNS resolve error
|
||||
#[display(fmt = "Redis: DNS resolve error")]
|
||||
ResolveError,
|
||||
}
|
||||
|
||||
#[cfg(feature = "web")]
|
||||
|
@ -1,150 +1,98 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use actix::actors::resolver::{Connect, Resolver};
|
||||
use actix::prelude::*;
|
||||
use actix_utils::oneshot;
|
||||
use backoff::backoff::Backoff;
|
||||
use backoff::ExponentialBackoff;
|
||||
use futures_util::FutureExt;
|
||||
use log::{error, info, warn};
|
||||
use redis_async::error::Error as RespError;
|
||||
use redis_async::resp::{RespCodec, RespValue};
|
||||
use tokio::io::{split, WriteHalf};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::codec::FramedRead;
|
||||
use redis_async::client::{paired_connect, PairedConnection};
|
||||
use redis_async::resp::RespValue;
|
||||
use tokio::sync::Mutex;
|
||||
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
|
||||
use trust_dns_resolver::TokioAsyncResolver as AsyncResolver;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
/// Command for send data to Redis
|
||||
#[derive(Debug)]
|
||||
pub struct Command(pub RespValue);
|
||||
|
||||
impl Message for Command {
|
||||
type Result = Result<RespValue, Error>;
|
||||
}
|
||||
|
||||
/// Redis comminucation actor
|
||||
pub struct RedisActor {
|
||||
pub struct RedisClient {
|
||||
addr: String,
|
||||
backoff: ExponentialBackoff,
|
||||
cell: Option<actix::io::FramedWrite<RespValue, WriteHalf<TcpStream>, RespCodec>>,
|
||||
queue: VecDeque<oneshot::Sender<Result<RespValue, Error>>>,
|
||||
connection: Mutex<Option<PairedConnection>>,
|
||||
}
|
||||
|
||||
impl RedisActor {
|
||||
/// Start new `Supervisor` with `RedisActor`.
|
||||
pub fn start<S: Into<String>>(addr: S) -> Addr<RedisActor> {
|
||||
let addr = addr.into();
|
||||
impl RedisClient {
|
||||
pub fn new(addr: impl Into<String>) -> Self {
|
||||
Self {
|
||||
addr: addr.into(),
|
||||
connection: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
let backoff = ExponentialBackoff {
|
||||
max_elapsed_time: None,
|
||||
..Default::default()
|
||||
};
|
||||
async fn get_connection(&self) -> Result<PairedConnection, Error> {
|
||||
let mut connection = self.connection.lock().await;
|
||||
if let Some(ref connection) = *connection {
|
||||
return Ok(connection.clone());
|
||||
}
|
||||
|
||||
Supervisor::start(|_| RedisActor {
|
||||
addr,
|
||||
cell: None,
|
||||
backoff,
|
||||
queue: VecDeque::new(),
|
||||
let mut addrs = resolve(&self.addr).await?;
|
||||
loop {
|
||||
// try to connect
|
||||
let socket_addr = addrs.pop_front().ok_or_else(|| {
|
||||
log::warn!("Cannot connect to {}.", self.addr);
|
||||
Error::NotConnected
|
||||
})?;
|
||||
match paired_connect(socket_addr).await {
|
||||
Ok(conn) => {
|
||||
*connection = Some(conn.clone());
|
||||
return Ok(conn);
|
||||
}
|
||||
Err(err) => log::warn!(
|
||||
"Attempt to connect to {} as {} failed: {}.",
|
||||
self.addr,
|
||||
socket_addr,
|
||||
err
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send(&self, req: RespValue) -> Result<RespValue, Error> {
|
||||
let res = self.get_connection().await?.send(req).await?;
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_addr(addr: &str, default_port: u16) -> Option<(&str, u16)> {
|
||||
// split the string by ':' and convert the second part to u16
|
||||
let mut parts_iter = addr.splitn(2, ':');
|
||||
let host = parts_iter.next()?;
|
||||
let port_str = parts_iter.next().unwrap_or("");
|
||||
let port: u16 = port_str.parse().unwrap_or(default_port);
|
||||
Some((host, port))
|
||||
}
|
||||
|
||||
async fn resolve(addr: &str) -> Result<VecDeque<SocketAddr>, Error> {
|
||||
// try to parse as a regular SocketAddr first
|
||||
if let Ok(addr) = addr.parse::<SocketAddr>() {
|
||||
let mut addrs = VecDeque::new();
|
||||
addrs.push_back(addr);
|
||||
return Ok(addrs);
|
||||
}
|
||||
|
||||
let (host, port) = parse_addr(addr, 6379).ok_or(Error::InvalidAddress)?;
|
||||
|
||||
// we need to do dns resolution
|
||||
let resolver = AsyncResolver::tokio_from_system_conf()
|
||||
.or_else(|err| {
|
||||
log::warn!("Cannot create system DNS resolver: {}", err);
|
||||
AsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Actor for RedisActor {
|
||||
type Context = Context<Self>;
|
||||
|
||||
fn started(&mut self, ctx: &mut Context<Self>) {
|
||||
Resolver::from_registry()
|
||||
.send(Connect::host(self.addr.as_str()))
|
||||
.into_actor(self)
|
||||
.map(|res, act, ctx| match res {
|
||||
Ok(res) => match res {
|
||||
Ok(stream) => {
|
||||
info!("Connected to redis server: {}", act.addr);
|
||||
|
||||
let (r, w) = split(stream);
|
||||
|
||||
// configure write side of the connection
|
||||
let framed = actix::io::FramedWrite::new(w, RespCodec, ctx);
|
||||
act.cell = Some(framed);
|
||||
|
||||
// read side of the connection
|
||||
ctx.add_stream(FramedRead::new(r, RespCodec));
|
||||
|
||||
act.backoff.reset();
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Can not connect to redis server: {}", err);
|
||||
// re-connect with backoff time.
|
||||
// we stop current context, supervisor will restart it.
|
||||
if let Some(timeout) = act.backoff.next_backoff() {
|
||||
ctx.run_later(timeout, |_, ctx| ctx.stop());
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
error!("Can not connect to redis server: {}", err);
|
||||
// re-connect with backoff time.
|
||||
// we stop current context, supervisor will restart it.
|
||||
if let Some(timeout) = act.backoff.next_backoff() {
|
||||
ctx.run_later(timeout, |_, ctx| ctx.stop());
|
||||
}
|
||||
}
|
||||
})
|
||||
.wait(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
impl Supervised for RedisActor {
|
||||
fn restarting(&mut self, _: &mut Self::Context) {
|
||||
self.cell.take();
|
||||
for tx in self.queue.drain(..) {
|
||||
let _ = tx.send(Err(Error::Disconnected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl actix::io::WriteHandler<io::Error> for RedisActor {
|
||||
fn error(&mut self, err: io::Error, _: &mut Self::Context) -> Running {
|
||||
warn!("Redis connection dropped: {} error: {}", self.addr, err);
|
||||
Running::Stop
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamHandler<Result<RespValue, RespError>> for RedisActor {
|
||||
fn handle(&mut self, msg: Result<RespValue, RespError>, ctx: &mut Self::Context) {
|
||||
match msg {
|
||||
Err(e) => {
|
||||
if let Some(tx) = self.queue.pop_front() {
|
||||
let _ = tx.send(Err(e.into()));
|
||||
}
|
||||
ctx.stop();
|
||||
}
|
||||
Ok(val) => {
|
||||
if let Some(tx) = self.queue.pop_front() {
|
||||
let _ = tx.send(Ok(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler<Command> for RedisActor {
|
||||
type Result = ResponseFuture<Result<RespValue, Error>>;
|
||||
|
||||
fn handle(&mut self, msg: Command, _: &mut Self::Context) -> Self::Result {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
if let Some(ref mut cell) = self.cell {
|
||||
self.queue.push_back(tx);
|
||||
cell.write(msg.0);
|
||||
} else {
|
||||
let _ = tx.send(Err(Error::NotConnected));
|
||||
}
|
||||
|
||||
Box::pin(rx.map(|res| match res {
|
||||
Ok(res) => res,
|
||||
Err(_) => Err(Error::Disconnected),
|
||||
}))
|
||||
}
|
||||
.map_err(|err| {
|
||||
log::error!("Cannot create DNS resolver: {}", err);
|
||||
Error::ResolveError
|
||||
})?;
|
||||
|
||||
let addrs = resolver
|
||||
.lookup_ip(host)
|
||||
.await
|
||||
.map_err(|_| Error::ResolveError)?
|
||||
.into_iter()
|
||||
.map(|ip| SocketAddr::new(ip, port))
|
||||
.collect();
|
||||
|
||||
Ok(addrs)
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{collections::HashMap, iter, rc::Rc};
|
||||
|
||||
use actix::prelude::*;
|
||||
use actix_service::{Service, Transform};
|
||||
use actix_session::{Session, SessionStatus};
|
||||
use actix_web::cookie::{Cookie, CookieJar, Key, SameSite};
|
||||
@ -16,7 +15,7 @@ use redis_async::resp::RespValue;
|
||||
use redis_async::resp_array;
|
||||
use time::{self, Duration, OffsetDateTime};
|
||||
|
||||
use crate::redis::{Command, RedisActor};
|
||||
use crate::redis::RedisClient;
|
||||
|
||||
/// Use redis as session storage.
|
||||
///
|
||||
@ -36,7 +35,7 @@ impl RedisSession {
|
||||
key: Key::derive_from(key),
|
||||
cache_keygen: Box::new(|key: &str| format!("session:{}", &key)),
|
||||
ttl: "7200".to_owned(),
|
||||
addr: RedisActor::start(addr),
|
||||
redis_client: RedisClient::new(addr),
|
||||
name: "actix-session".to_owned(),
|
||||
path: "/".to_owned(),
|
||||
domain: None,
|
||||
@ -113,14 +112,12 @@ impl RedisSession {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B> Transform<S> for RedisSession
|
||||
impl<S, B> Transform<S, ServiceRequest> for RedisSession
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>
|
||||
+ 'static,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||
S::Future: 'static,
|
||||
B: 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = S::Error;
|
||||
type InitError = ();
|
||||
@ -141,25 +138,23 @@ pub struct RedisSessionMiddleware<S: 'static> {
|
||||
inner: Rc<Inner>,
|
||||
}
|
||||
|
||||
impl<S, B> Service for RedisSessionMiddleware<S>
|
||||
impl<S, B> Service<ServiceRequest> for RedisSessionMiddleware<S>
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>
|
||||
+ 'static,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||
S::Future: 'static,
|
||||
B: 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
#[allow(clippy::type_complexity)]
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>>>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.borrow_mut().poll_ready(cx)
|
||||
}
|
||||
|
||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
||||
let mut srv = self.service.clone();
|
||||
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||
let srv = self.service.clone();
|
||||
let inner = self.inner.clone();
|
||||
|
||||
Box::pin(async move {
|
||||
@ -215,7 +210,7 @@ struct Inner {
|
||||
key: Key,
|
||||
cache_keygen: Box<dyn Fn(&str) -> String>,
|
||||
ttl: String,
|
||||
addr: Addr<RedisActor>,
|
||||
redis_client: RedisClient,
|
||||
name: String,
|
||||
path: String,
|
||||
domain: Option<String>,
|
||||
@ -256,13 +251,11 @@ impl Inner {
|
||||
}
|
||||
};
|
||||
|
||||
let res = self
|
||||
.addr
|
||||
.send(Command(resp_array!["GET", cache_key]))
|
||||
let val = self
|
||||
.redis_client
|
||||
.send(resp_array!["GET", cache_key])
|
||||
.await?;
|
||||
|
||||
let val = res.map_err(error::ErrorInternalServerError)?;
|
||||
|
||||
match val {
|
||||
RespValue::Error(err) => {
|
||||
return Err(error::ErrorInternalServerError(err));
|
||||
@ -294,6 +287,7 @@ impl Inner {
|
||||
} else {
|
||||
let value: String = iter::repeat(())
|
||||
.map(|()| OsRng.sample(Alphanumeric))
|
||||
.map(char::from)
|
||||
.take(32)
|
||||
.collect();
|
||||
|
||||
@ -331,12 +325,9 @@ impl Inner {
|
||||
Ok(body) => body,
|
||||
};
|
||||
|
||||
let cmd = Command(resp_array!["SET", cache_key, body, "EX", &self.ttl]);
|
||||
|
||||
self.addr
|
||||
.send(cmd)
|
||||
.await?
|
||||
.map_err(error::ErrorInternalServerError)?;
|
||||
self.redis_client
|
||||
.send(resp_array!["SET", cache_key, body, "EX", &self.ttl])
|
||||
.await?;
|
||||
|
||||
if let Some(jar) = jar {
|
||||
for cookie in jar.delta() {
|
||||
@ -352,17 +343,16 @@ impl Inner {
|
||||
async fn clear_cache(&self, key: String) -> Result<(), Error> {
|
||||
let cache_key = (self.cache_keygen)(&key);
|
||||
|
||||
match self.addr.send(Command(resp_array!["DEL", cache_key])).await {
|
||||
Err(e) => Err(Error::from(e)),
|
||||
Ok(res) => {
|
||||
match res {
|
||||
// redis responds with number of deleted records
|
||||
Ok(RespValue::Integer(x)) if x > 0 => Ok(()),
|
||||
_ => Err(error::ErrorInternalServerError(
|
||||
"failed to remove session from cache",
|
||||
)),
|
||||
}
|
||||
}
|
||||
match self
|
||||
.redis_client
|
||||
.send(resp_array!["DEL", cache_key])
|
||||
.await?
|
||||
{
|
||||
// redis responds with number of deleted records
|
||||
RespValue::Integer(x) if x > 0 => Ok(()),
|
||||
_ => Err(error::ErrorInternalServerError(
|
||||
"failed to remove session from cache",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -406,7 +396,7 @@ mod test {
|
||||
.unwrap_or(Some(0))
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(HttpResponse::Ok().json(IndexResponse { user_id, counter }))
|
||||
Ok(HttpResponse::Ok().json(&IndexResponse { user_id, counter }))
|
||||
}
|
||||
|
||||
async fn do_something(session: Session) -> Result<HttpResponse> {
|
||||
@ -417,7 +407,7 @@ mod test {
|
||||
.map_or(1, |inner| inner + 1);
|
||||
session.set("counter", counter)?;
|
||||
|
||||
Ok(HttpResponse::Ok().json(IndexResponse { user_id, counter }))
|
||||
Ok(HttpResponse::Ok().json(&IndexResponse { user_id, counter }))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -438,7 +428,7 @@ mod test {
|
||||
.unwrap_or(Some(0))
|
||||
.unwrap_or(0);
|
||||
|
||||
Ok(HttpResponse::Ok().json(IndexResponse {
|
||||
Ok(HttpResponse::Ok().json(&IndexResponse {
|
||||
user_id: Some(id),
|
||||
counter,
|
||||
}))
|
||||
|
@ -1,42 +1,31 @@
|
||||
#[macro_use]
|
||||
extern crate redis_async;
|
||||
|
||||
use actix_redis::{Command, Error, RedisActor, RespValue};
|
||||
use actix_redis::{Error, RedisClient, RespValue};
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_error_connect() {
|
||||
let addr = RedisActor::start("localhost:54000");
|
||||
let _addr2 = addr.clone();
|
||||
let addr = RedisClient::new("localhost:54000");
|
||||
|
||||
let res = addr.send(Command(resp_array!["GET", "test"])).await;
|
||||
let res = addr.send(resp_array!["GET", "test"]).await;
|
||||
match res {
|
||||
Ok(Err(Error::NotConnected)) => (),
|
||||
Err(Error::NotConnected) => (),
|
||||
_ => panic!("Should not happen {:?}", res),
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_redis() {
|
||||
async fn test_redis() -> Result<(), Error> {
|
||||
env_logger::init();
|
||||
|
||||
let addr = RedisActor::start("127.0.0.1:6379");
|
||||
let res = addr
|
||||
.send(Command(resp_array!["SET", "test", "value"]))
|
||||
.await;
|
||||
let addr = RedisClient::new("127.0.0.1:6379");
|
||||
|
||||
match res {
|
||||
Ok(Ok(resp)) => {
|
||||
assert_eq!(resp, RespValue::SimpleString("OK".to_owned()));
|
||||
let resp = addr.send(resp_array!["SET", "test", "value"]).await?;
|
||||
|
||||
let res = addr.send(Command(resp_array!["GET", "test"])).await;
|
||||
match res {
|
||||
Ok(Ok(resp)) => {
|
||||
println!("RESP: {:?}", resp);
|
||||
assert_eq!(resp, RespValue::BulkString((&b"value"[..]).into()));
|
||||
}
|
||||
_ => panic!("Should not happen {:?}", res),
|
||||
}
|
||||
}
|
||||
_ => panic!("Should not happen {:?}", res),
|
||||
}
|
||||
assert_eq!(resp, RespValue::SimpleString("OK".to_owned()));
|
||||
|
||||
let resp = addr.send(resp_array!["GET", "test"]).await?;
|
||||
println!("RESP: {:?}", resp);
|
||||
assert_eq!(resp, RespValue::BulkString((&b"value"[..]).into()));
|
||||
Ok(())
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
## 0.4.1 - 2020-03-21
|
||||
* `Session::set_session` takes a `IntoIterator` instead of `Iterator`. [#105]
|
||||
* Fix calls to `session.purge()` from paths other than the one specified in the cookie. [#129]
|
||||
* Minimum supported Rust version (MSRV) is now 1.46.0.
|
||||
|
||||
[#105]: https://github.com/actix/actix-extras/pull/105
|
||||
[#129]: https://github.com/actix/actix-extras/pull/129
|
||||
|
@ -20,8 +20,8 @@ default = ["cookie-session"]
|
||||
cookie-session = ["actix-web/secure-cookies"]
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "3.0.0", default_features = false }
|
||||
actix-service = "1.0.6"
|
||||
actix-web = { version = "4.0.0-beta.4", default_features = false, features = ["cookies"] }
|
||||
actix-service = "2.0.0-beta.5"
|
||||
derive_more = "0.99.2"
|
||||
futures-util = { version = "0.3.7", default-features = false }
|
||||
serde = "1.0"
|
||||
@ -29,4 +29,4 @@ serde_json = "1.0"
|
||||
time = { version = "0.2.23", default-features = false, features = ["std"] }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "1"
|
||||
actix-rt = "2"
|
||||
|
@ -9,7 +9,7 @@ use actix_web::cookie::{Cookie, CookieJar, Key, SameSite};
|
||||
use actix_web::dev::{ServiceRequest, ServiceResponse};
|
||||
use actix_web::http::{header::SET_COOKIE, HeaderValue};
|
||||
use actix_web::{Error, HttpMessage, ResponseError};
|
||||
use derive_more::{Display, From};
|
||||
use derive_more::Display;
|
||||
use futures_util::future::{ok, FutureExt, LocalBoxFuture, Ready};
|
||||
use serde_json::error::Error as JsonError;
|
||||
use time::{Duration, OffsetDateTime};
|
||||
@ -17,7 +17,7 @@ use time::{Duration, OffsetDateTime};
|
||||
use crate::{Session, SessionStatus};
|
||||
|
||||
/// Errors that can occur during handling cookie session
|
||||
#[derive(Debug, From, Display)]
|
||||
#[derive(Debug, Display)]
|
||||
pub enum CookieSessionError {
|
||||
/// Size of the serialized session is greater than 4000 bytes.
|
||||
#[display(fmt = "Size of the serialized session is greater than 4000 bytes.")]
|
||||
@ -290,13 +290,12 @@ impl CookieSession {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B: 'static> Transform<S> for CookieSession
|
||||
impl<S, B: 'static> Transform<S, ServiceRequest> for CookieSession
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>>,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = S::Error;
|
||||
type InitError = ();
|
||||
@ -317,18 +316,17 @@ pub struct CookieSessionMiddleware<S> {
|
||||
inner: Rc<CookieSessionInner>,
|
||||
}
|
||||
|
||||
impl<S, B: 'static> Service for CookieSessionMiddleware<S>
|
||||
impl<S, B: 'static> Service<ServiceRequest> for CookieSessionMiddleware<S>
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>>,
|
||||
S::Future: 'static,
|
||||
S::Error: 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = S::Error;
|
||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.poll_ready(cx)
|
||||
}
|
||||
|
||||
@ -337,7 +335,7 @@ where
|
||||
/// session state changes, then set-cookie is returned in response. As
|
||||
/// a user logs out, call session.purge() to set SessionStatus accordingly
|
||||
/// and this will trigger removal of the session cookie in the response.
|
||||
fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
|
||||
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||
let inner = self.inner.clone();
|
||||
let (is_new, state) = self.inner.load(&req);
|
||||
let prolong_expiration = self.inner.expires_in.is_some();
|
||||
@ -387,7 +385,7 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn cookie_session() {
|
||||
let mut app = test::init_service(
|
||||
let app = test::init_service(
|
||||
App::new()
|
||||
.wrap(CookieSession::signed(&[0; 32]).secure(false))
|
||||
.service(web::resource("/").to(|ses: Session| async move {
|
||||
@ -407,7 +405,7 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn private_cookie() {
|
||||
let mut app = test::init_service(
|
||||
let app = test::init_service(
|
||||
App::new()
|
||||
.wrap(CookieSession::private(&[0; 32]).secure(false))
|
||||
.service(web::resource("/").to(|ses: Session| async move {
|
||||
@ -427,7 +425,7 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn lazy_cookie() {
|
||||
let mut app = test::init_service(
|
||||
let app = test::init_service(
|
||||
App::new()
|
||||
.wrap(CookieSession::signed(&[0; 32]).secure(false).lazy(true))
|
||||
.service(web::resource("/count").to(|ses: Session| async move {
|
||||
@ -453,7 +451,7 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn cookie_session_extractor() {
|
||||
let mut app = test::init_service(
|
||||
let app = test::init_service(
|
||||
App::new()
|
||||
.wrap(CookieSession::signed(&[0; 32]).secure(false))
|
||||
.service(web::resource("/").to(|ses: Session| async move {
|
||||
@ -473,7 +471,7 @@ mod tests {
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn basics() {
|
||||
let mut app = test::init_service(
|
||||
let app = test::init_service(
|
||||
App::new()
|
||||
.wrap(
|
||||
CookieSession::signed(&[0; 32])
|
||||
@ -508,13 +506,13 @@ mod tests {
|
||||
let request = test::TestRequest::with_uri("/test/")
|
||||
.cookie(cookie)
|
||||
.to_request();
|
||||
let body = test::read_response(&mut app, request).await;
|
||||
let body = test::read_response(&app, request).await;
|
||||
assert_eq!(body, Bytes::from_static(b"counter: 100"));
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn prolong_expiration() {
|
||||
let mut app = test::init_service(
|
||||
let app = test::init_service(
|
||||
App::new()
|
||||
.wrap(CookieSession::signed(&[0; 32]).secure(false).expires_in(60))
|
||||
.service(web::resource("/").to(|ses: Session| async move {
|
||||
@ -538,7 +536,7 @@ mod tests {
|
||||
.expires()
|
||||
.expect("Expiration is set");
|
||||
|
||||
actix_rt::time::delay_for(std::time::Duration::from_secs(1)).await;
|
||||
actix_rt::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
|
||||
let request = test::TestRequest::with_uri("/test/").to_request();
|
||||
let response = app.call(request).await.unwrap();
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
## 0.5.1 - 2020-03-21
|
||||
* Correct error handling when extracting auth details from request. [#128]
|
||||
* Minimum supported Rust version (MSRV) is now 1.46.0.
|
||||
|
||||
[#128]: https://github.com/actix/actix-extras/pull/128
|
||||
|
||||
|
@ -20,11 +20,11 @@ name = "actix_web_httpauth"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "3.0.0", default_features = false }
|
||||
actix-web = { version = "4.0.0-beta.4", default_features = false }
|
||||
base64 = "0.13"
|
||||
futures-util = { version = "0.3.7", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
actix-cors = "0.5"
|
||||
actix-rt = "1.1.1"
|
||||
actix-service = "1.0.6"
|
||||
actix-rt = "2"
|
||||
actix-service = "2.0.0-beta.5"
|
||||
|
@ -54,7 +54,7 @@ impl<C: 'static + Challenge> ResponseError for AuthenticationError<C> {
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
HttpResponse::build(self.status_code)
|
||||
// TODO: Get rid of the `.clone()`
|
||||
.set(WwwAuthenticate(self.challenge.clone()))
|
||||
.insert_header(WwwAuthenticate(self.challenge.clone()))
|
||||
.finish()
|
||||
}
|
||||
|
||||
|
@ -91,8 +91,8 @@ impl<S: Scheme> Header for Authorization<S> {
|
||||
impl<S: Scheme> IntoHeaderValue for Authorization<S> {
|
||||
type Error = <S as IntoHeaderValue>::Error;
|
||||
|
||||
fn try_into(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
self.0.try_into()
|
||||
fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
|
||||
self.0.try_into_value()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ impl fmt::Display for Basic {
|
||||
impl IntoHeaderValue for Basic {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
fn try_into(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
let mut credentials = BytesMut::with_capacity(
|
||||
self.user_id.len()
|
||||
+ 1 // ':'
|
||||
@ -187,7 +187,7 @@ mod tests {
|
||||
password: Some("open sesame".into()),
|
||||
};
|
||||
|
||||
let result = basic.try_into();
|
||||
let result = basic.try_into_value();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(
|
||||
result.unwrap(),
|
||||
|
@ -76,7 +76,7 @@ impl fmt::Display for Bearer {
|
||||
impl IntoHeaderValue for Bearer {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
fn try_into(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
let mut buffer = BytesMut::with_capacity(7 + self.token.len());
|
||||
buffer.put(&b"Bearer "[..]);
|
||||
buffer.extend_from_slice(self.token.as_bytes());
|
||||
@ -128,7 +128,7 @@ mod tests {
|
||||
fn test_into_header_value() {
|
||||
let bearer = Bearer::new("mF_9.B5f-4.1JqM");
|
||||
|
||||
let result = bearer.try_into();
|
||||
let result = bearer.try_into_value();
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(
|
||||
result.unwrap(),
|
||||
|
@ -25,7 +25,7 @@ use crate::utils;
|
||||
/// let challenge = Basic::with_realm("Restricted area");
|
||||
///
|
||||
/// HttpResponse::Unauthorized()
|
||||
/// .set(WwwAuthenticate(challenge))
|
||||
/// .insert_header(WwwAuthenticate(challenge))
|
||||
/// .finish()
|
||||
/// }
|
||||
/// ```
|
||||
@ -106,7 +106,7 @@ impl fmt::Display for Basic {
|
||||
impl IntoHeaderValue for Basic {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
fn try_into(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
HeaderValue::from_maybe_shared(self.to_bytes())
|
||||
}
|
||||
}
|
||||
@ -120,7 +120,7 @@ mod tests {
|
||||
fn test_plain_into_header_value() {
|
||||
let challenge = Basic { realm: None };
|
||||
|
||||
let value = challenge.try_into();
|
||||
let value = challenge.try_into_value();
|
||||
assert!(value.is_ok());
|
||||
let value = value.unwrap();
|
||||
assert_eq!(value, "Basic");
|
||||
@ -132,7 +132,7 @@ mod tests {
|
||||
realm: Some("Restricted area".into()),
|
||||
};
|
||||
|
||||
let value = challenge.try_into();
|
||||
let value = challenge.try_into_value();
|
||||
assert!(value.is_ok());
|
||||
let value = value.unwrap();
|
||||
assert_eq!(value, "Basic realm=\"Restricted area\"");
|
||||
|
@ -31,7 +31,7 @@ use crate::utils;
|
||||
/// .finish();
|
||||
///
|
||||
/// HttpResponse::Unauthorized()
|
||||
/// .set(WwwAuthenticate(challenge))
|
||||
/// .insert_header(WwwAuthenticate(challenge))
|
||||
/// .finish()
|
||||
/// }
|
||||
/// ```
|
||||
@ -133,7 +133,7 @@ impl fmt::Display for Bearer {
|
||||
impl IntoHeaderValue for Bearer {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
fn try_into(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
HeaderValue::from_maybe_shared(self.to_bytes())
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ impl<C: Challenge> Header for WwwAuthenticate<C> {
|
||||
impl<C: Challenge> IntoHeaderValue for WwwAuthenticate<C> {
|
||||
type Error = <C as IntoHeaderValue>::Error;
|
||||
|
||||
fn try_into(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
self.0.try_into()
|
||||
fn try_into_value(self) -> Result<HeaderValue, <Self as IntoHeaderValue>::Error> {
|
||||
self.0.try_into_value()
|
||||
}
|
||||
}
|
||||
|
@ -118,16 +118,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, B, T, F, O> Transform<S> for HttpAuthentication<T, F>
|
||||
impl<S, B, T, F, O> Transform<S, ServiceRequest> for HttpAuthentication<T, F>
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>
|
||||
+ 'static,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||
S::Future: 'static,
|
||||
F: Fn(ServiceRequest, T) -> O + 'static,
|
||||
O: Future<Output = Result<ServiceRequest, Error>> + 'static,
|
||||
T: AuthExtractor + 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = Error;
|
||||
type Transform = AuthenticationMiddleware<S, F, T>;
|
||||
@ -153,25 +151,23 @@ where
|
||||
_extractor: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<S, B, F, T, O> Service for AuthenticationMiddleware<S, F, T>
|
||||
impl<S, B, F, T, O> Service<ServiceRequest> for AuthenticationMiddleware<S, F, T>
|
||||
where
|
||||
S: Service<Request = ServiceRequest, Response = ServiceResponse<B>, Error = Error>
|
||||
+ 'static,
|
||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||
S::Future: 'static,
|
||||
F: Fn(ServiceRequest, T) -> O + 'static,
|
||||
O: Future<Output = Result<ServiceRequest, Error>> + 'static,
|
||||
T: AuthExtractor + 'static,
|
||||
{
|
||||
type Request = ServiceRequest;
|
||||
type Response = ServiceResponse<B>;
|
||||
type Error = S::Error;
|
||||
type Future = LocalBoxFuture<'static, Result<ServiceResponse<B>, Error>>;
|
||||
|
||||
fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.service.borrow_mut().poll_ready(ctx)
|
||||
}
|
||||
|
||||
fn call(&mut self, req: Self::Request) -> Self::Future {
|
||||
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||
let process_fn = Arc::clone(&self.process_fn);
|
||||
|
||||
let service = Rc::clone(&self.service);
|
||||
@ -251,15 +247,14 @@ mod tests {
|
||||
use actix_service::{into_service, Service};
|
||||
use actix_web::error;
|
||||
use actix_web::test::TestRequest;
|
||||
use futures_util::join;
|
||||
|
||||
/// This is a test for https://github.com/actix/actix-extras/issues/10
|
||||
#[actix_rt::test]
|
||||
async fn test_middleware_panic() {
|
||||
let mut middleware = AuthenticationMiddleware {
|
||||
let middleware = AuthenticationMiddleware {
|
||||
service: Rc::new(RefCell::new(into_service(
|
||||
|_: ServiceRequest| async move {
|
||||
actix_rt::time::delay_for(std::time::Duration::from_secs(1)).await;
|
||||
actix_rt::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
Err::<ServiceResponse, _>(error::ErrorBadRequest("error"))
|
||||
},
|
||||
))),
|
||||
@ -267,22 +262,24 @@ mod tests {
|
||||
_extractor: PhantomData,
|
||||
};
|
||||
|
||||
let req = TestRequest::with_header("Authorization", "Bearer 1").to_srv_request();
|
||||
let req = TestRequest::get()
|
||||
.append_header(("Authorization", "Bearer 1"))
|
||||
.to_srv_request();
|
||||
|
||||
let f = middleware.call(req);
|
||||
let f = middleware.call(req).await;
|
||||
|
||||
let res = futures_util::future::lazy(|cx| middleware.poll_ready(cx));
|
||||
let _res = futures_util::future::lazy(|cx| middleware.poll_ready(cx)).await;
|
||||
|
||||
assert!(join!(f, res).0.is_err());
|
||||
assert!(f.is_err());
|
||||
}
|
||||
|
||||
/// This is a test for https://github.com/actix/actix-extras/issues/10
|
||||
#[actix_rt::test]
|
||||
async fn test_middleware_panic_several_orders() {
|
||||
let mut middleware = AuthenticationMiddleware {
|
||||
let middleware = AuthenticationMiddleware {
|
||||
service: Rc::new(RefCell::new(into_service(
|
||||
|_: ServiceRequest| async move {
|
||||
actix_rt::time::delay_for(std::time::Duration::from_secs(1)).await;
|
||||
actix_rt::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
Err::<ServiceResponse, _>(error::ErrorBadRequest("error"))
|
||||
},
|
||||
))),
|
||||
@ -290,24 +287,28 @@ mod tests {
|
||||
_extractor: PhantomData,
|
||||
};
|
||||
|
||||
let req = TestRequest::with_header("Authorization", "Bearer 1").to_srv_request();
|
||||
let req = TestRequest::get()
|
||||
.append_header(("Authorization", "Bearer 1"))
|
||||
.to_srv_request();
|
||||
|
||||
let f1 = middleware.call(req);
|
||||
let f1 = middleware.call(req).await;
|
||||
|
||||
let req = TestRequest::with_header("Authorization", "Bearer 1").to_srv_request();
|
||||
let req = TestRequest::get()
|
||||
.append_header(("Authorization", "Bearer 1"))
|
||||
.to_srv_request();
|
||||
|
||||
let f2 = middleware.call(req);
|
||||
let f2 = middleware.call(req).await;
|
||||
|
||||
let req = TestRequest::with_header("Authorization", "Bearer 1").to_srv_request();
|
||||
let req = TestRequest::get()
|
||||
.append_header(("Authorization", "Bearer 1"))
|
||||
.to_srv_request();
|
||||
|
||||
let f3 = middleware.call(req);
|
||||
let f3 = middleware.call(req).await;
|
||||
|
||||
let res = futures_util::future::lazy(|cx| middleware.poll_ready(cx));
|
||||
let _res = futures_util::future::lazy(|cx| middleware.poll_ready(cx)).await;
|
||||
|
||||
let result = join!(f1, f2, f3, res);
|
||||
|
||||
assert!(result.0.is_err());
|
||||
assert!(result.1.is_err());
|
||||
assert!(result.2.is_err());
|
||||
assert!(f1.is_err());
|
||||
assert!(f2.is_err());
|
||||
assert!(f3.is_err());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user