mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-27 17:22:57 +01:00
migrate to actix-web beta 14 (#209)
This commit is contained in:
parent
700d90b68b
commit
74ec115161
@ -1,6 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Update `actix-web` dependency to `4.0.0.beta-14`. [#209]
|
||||||
|
|
||||||
|
[#209]: https://github.com/actix/actix-extras/pull/209
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.4 - 2021-11-22
|
## 0.6.0-beta.4 - 2021-11-22
|
||||||
|
@ -19,7 +19,7 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
actix-web = { version = "4.0.0-beta.10", default-features = false }
|
actix-web = { version = "4.0.0-beta.14", default-features = false }
|
||||||
|
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
@ -4,11 +4,14 @@ use std::{
|
|||||||
|
|
||||||
use actix_utils::future::{self, Ready};
|
use actix_utils::future::{self, Ready};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::MessageBody,
|
body::{EitherBody, MessageBody},
|
||||||
dev::{RequestHead, Service, ServiceRequest, ServiceResponse, Transform},
|
dev::{RequestHead, Service, ServiceRequest, ServiceResponse, Transform},
|
||||||
error::{Error, Result},
|
error::HttpError,
|
||||||
http::{self, header::HeaderName, Error as HttpError, HeaderValue, Method, Uri},
|
http::{
|
||||||
Either,
|
header::{HeaderName, HeaderValue},
|
||||||
|
Method, Uri,
|
||||||
|
},
|
||||||
|
Either, Error, Result,
|
||||||
};
|
};
|
||||||
use log::error;
|
use log::error;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
@ -20,7 +23,7 @@ use crate::{AllOrSome, CorsError, CorsMiddleware, Inner, OriginFn};
|
|||||||
/// Additionally, always causes first error (if any) to be reported during initialization.
|
/// Additionally, always causes first error (if any) to be reported during initialization.
|
||||||
fn cors<'a>(
|
fn cors<'a>(
|
||||||
inner: &'a mut Rc<Inner>,
|
inner: &'a mut Rc<Inner>,
|
||||||
err: &Option<Either<http::Error, CorsError>>,
|
err: &Option<Either<HttpError, CorsError>>,
|
||||||
) -> Option<&'a mut Inner> {
|
) -> Option<&'a mut Inner> {
|
||||||
if err.is_some() {
|
if err.is_some() {
|
||||||
return None;
|
return None;
|
||||||
@ -74,7 +77,7 @@ static ALL_METHODS_SET: Lazy<HashSet<Method>> = Lazy::new(|| {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Cors {
|
pub struct Cors {
|
||||||
inner: Rc<Inner>,
|
inner: Rc<Inner>,
|
||||||
error: Option<Either<http::Error, CorsError>>,
|
error: Option<Either<HttpError, CorsError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cors {
|
impl Cors {
|
||||||
@ -490,7 +493,7 @@ where
|
|||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
B::Error: StdError,
|
B::Error: StdError,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Transform = CorsMiddleware<S>;
|
type Transform = CorsMiddleware<S>;
|
||||||
@ -571,15 +574,13 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::convert::{Infallible, TryInto};
|
use std::convert::{Infallible, TryInto};
|
||||||
use std::pin::Pin;
|
|
||||||
use std::task::{Context, Poll};
|
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::{BodySize, MessageBody},
|
body,
|
||||||
dev::{fn_service, Transform},
|
dev::{fn_service, Transform},
|
||||||
http::{HeaderName, StatusCode},
|
http::{header::HeaderName, StatusCode},
|
||||||
test::{self, TestRequest},
|
test::{self, TestRequest},
|
||||||
web::{Bytes, HttpResponse},
|
web::HttpResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -634,23 +635,8 @@ mod test {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn middleware_generic_over_body_type() {
|
async fn middleware_generic_over_body_type() {
|
||||||
struct Foo;
|
|
||||||
|
|
||||||
impl MessageBody for Foo {
|
|
||||||
type Error = std::io::Error;
|
|
||||||
fn size(&self) -> BodySize {
|
|
||||||
BodySize::None
|
|
||||||
}
|
|
||||||
fn poll_next(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
_: &mut Context<'_>,
|
|
||||||
) -> Poll<Option<Result<Bytes, Self::Error>>> {
|
|
||||||
Poll::Ready(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let srv = fn_service(|req: ServiceRequest| async move {
|
let srv = fn_service(|req: ServiceRequest| async move {
|
||||||
Ok(req.into_response(HttpResponse::Ok().message_body(Foo)?))
|
Ok(req.into_response(HttpResponse::Ok().message_body(body::None::new())?))
|
||||||
});
|
});
|
||||||
|
|
||||||
Cors::default().new_transform(srv).await.unwrap();
|
Cors::default().new_transform(srv).await.unwrap();
|
||||||
|
@ -7,35 +7,35 @@ use derive_more::{Display, Error};
|
|||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum CorsError {
|
pub enum CorsError {
|
||||||
/// Allowed origin argument must not be wildcard (`*`).
|
/// Allowed origin argument must not be wildcard (`*`).
|
||||||
#[display(fmt = "`allowed_origin` argument must not be wildcard (`*`).")]
|
#[display(fmt = "`allowed_origin` argument must not be wildcard (`*`)")]
|
||||||
WildcardOrigin,
|
WildcardOrigin,
|
||||||
|
|
||||||
/// Request header `Origin` is required but was not provided.
|
/// Request header `Origin` is required but was not provided.
|
||||||
#[display(fmt = "Request header `Origin` is required but was not provided.")]
|
#[display(fmt = "Request header `Origin` is required but was not provided")]
|
||||||
MissingOrigin,
|
MissingOrigin,
|
||||||
|
|
||||||
/// Request header `Access-Control-Request-Method` is required but is missing.
|
/// Request header `Access-Control-Request-Method` is required but is missing.
|
||||||
#[display(fmt = "Request header `Access-Control-Request-Method` is required but is missing.")]
|
#[display(fmt = "Request header `Access-Control-Request-Method` is required but is missing")]
|
||||||
MissingRequestMethod,
|
MissingRequestMethod,
|
||||||
|
|
||||||
/// Request header `Access-Control-Request-Method` has an invalid value.
|
/// Request header `Access-Control-Request-Method` has an invalid value.
|
||||||
#[display(fmt = "Request header `Access-Control-Request-Method` has an invalid value.")]
|
#[display(fmt = "Request header `Access-Control-Request-Method` has an invalid value")]
|
||||||
BadRequestMethod,
|
BadRequestMethod,
|
||||||
|
|
||||||
/// Request header `Access-Control-Request-Headers` has an invalid value.
|
/// Request header `Access-Control-Request-Headers` has an invalid value.
|
||||||
#[display(fmt = "Request header `Access-Control-Request-Headers` has an invalid value.")]
|
#[display(fmt = "Request header `Access-Control-Request-Headers` has an invalid value")]
|
||||||
BadRequestHeaders,
|
BadRequestHeaders,
|
||||||
|
|
||||||
/// Origin is not allowed to make this request.
|
/// Origin is not allowed to make this request.
|
||||||
#[display(fmt = "Origin is not allowed to make this request.")]
|
#[display(fmt = "Origin is not allowed to make this request")]
|
||||||
OriginNotAllowed,
|
OriginNotAllowed,
|
||||||
|
|
||||||
/// Request method is not allowed.
|
/// Request method is not allowed.
|
||||||
#[display(fmt = "Requested method is not allowed.")]
|
#[display(fmt = "Requested method is not allowed")]
|
||||||
MethodNotAllowed,
|
MethodNotAllowed,
|
||||||
|
|
||||||
/// One or more request headers are not allowed.
|
/// One or more request headers are not allowed.
|
||||||
#[display(fmt = "One or more request headers are not allowed.")]
|
#[display(fmt = "One or more request headers are not allowed")]
|
||||||
HeadersNotAllowed,
|
HeadersNotAllowed,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +45,6 @@ impl ResponseError for CorsError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn error_response(&self) -> HttpResponse {
|
fn error_response(&self) -> HttpResponse {
|
||||||
HttpResponse::with_body(StatusCode::BAD_REQUEST, self.to_string().into())
|
HttpResponse::with_body(StatusCode::BAD_REQUEST, self.to_string()).map_into_boxed_body()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,10 @@ mod test {
|
|||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::Transform,
|
dev::Transform,
|
||||||
http::{header, HeaderValue, Method, StatusCode},
|
http::{
|
||||||
|
header::{self, HeaderValue},
|
||||||
|
Method, StatusCode,
|
||||||
|
},
|
||||||
test::{self, TestRequest},
|
test::{self, TestRequest},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use std::{collections::HashSet, convert::TryInto, error::Error as StdError, rc::Rc};
|
use std::{collections::HashSet, convert::TryInto, error::Error as StdError, rc::Rc};
|
||||||
|
|
||||||
use actix_utils::future::{ok, Either, Ready};
|
use actix_utils::future::ok;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::{AnyBody, MessageBody},
|
body::{EitherBody, MessageBody},
|
||||||
dev::{Service, ServiceRequest, ServiceResponse},
|
dev::{Service, ServiceRequest, ServiceResponse},
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
http::{
|
http::{
|
||||||
@ -11,7 +11,7 @@ use actix_web::{
|
|||||||
},
|
},
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
};
|
};
|
||||||
use futures_util::future::{FutureExt as _, LocalBoxFuture, TryFutureExt as _};
|
use futures_util::future::{FutureExt as _, LocalBoxFuture};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::{builder::intersperse_header_values, AllOrSome, Inner};
|
use crate::{builder::intersperse_header_values, AllOrSome, Inner};
|
||||||
@ -134,11 +134,6 @@ impl<S> CorsMiddleware<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CorsMiddlewareServiceFuture = Either<
|
|
||||||
Ready<Result<ServiceResponse, Error>>,
|
|
||||||
LocalBoxFuture<'static, Result<ServiceResponse, Error>>,
|
|
||||||
>;
|
|
||||||
|
|
||||||
impl<S, B> Service<ServiceRequest> for CorsMiddleware<S>
|
impl<S, B> Service<ServiceRequest> for CorsMiddleware<S>
|
||||||
where
|
where
|
||||||
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
||||||
@ -146,9 +141,9 @@ where
|
|||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
B::Error: StdError,
|
B::Error: StdError,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = CorsMiddlewareServiceFuture;
|
type Future = LocalBoxFuture<'static, Result<ServiceResponse<EitherBody<B>>, Error>>;
|
||||||
|
|
||||||
actix_service::forward_ready!(service);
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
@ -156,7 +151,7 @@ where
|
|||||||
if self.inner.preflight && req.method() == Method::OPTIONS {
|
if self.inner.preflight && req.method() == Method::OPTIONS {
|
||||||
let inner = Rc::clone(&self.inner);
|
let inner = Rc::clone(&self.inner);
|
||||||
let res = Self::handle_preflight(&inner, req);
|
let res = Self::handle_preflight(&inner, req);
|
||||||
Either::left(ok(res))
|
ok(res.map_into_right_body()).boxed_local()
|
||||||
} else {
|
} else {
|
||||||
let origin = req.headers().get(header::ORIGIN).cloned();
|
let origin = req.headers().get(header::ORIGIN).cloned();
|
||||||
|
|
||||||
@ -164,27 +159,37 @@ where
|
|||||||
// Only check requests with a origin header.
|
// Only check requests with a origin header.
|
||||||
if let Err(err) = self.inner.validate_origin(req.head()) {
|
if let Err(err) = self.inner.validate_origin(req.head()) {
|
||||||
debug!("origin validation failed; inner service is not called");
|
debug!("origin validation failed; inner service is not called");
|
||||||
return Either::left(ok(req.error_response(err)));
|
return ok(req.error_response(err).map_into_right_body()).boxed_local();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let (req, pl) = req.into_parts();
|
||||||
|
let req2 = req.clone();
|
||||||
|
let req = ServiceRequest::from_parts(req, pl);
|
||||||
|
|
||||||
let inner = Rc::clone(&self.inner);
|
let inner = Rc::clone(&self.inner);
|
||||||
let fut = self.service.call(req);
|
let fut = self.service.call(req);
|
||||||
|
|
||||||
let res = async move {
|
async move {
|
||||||
let res = fut.await;
|
let res = fut.await;
|
||||||
|
|
||||||
if origin.is_some() {
|
if origin.is_some() {
|
||||||
let res = res?;
|
let res = match res {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => {
|
||||||
|
let res = HttpResponse::from_error(err);
|
||||||
|
let res = ServiceResponse::new(req2, res);
|
||||||
|
return Ok(res.map_into_right_body());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self::augment_response(&inner, res))
|
Ok(Self::augment_response(&inner, res))
|
||||||
} else {
|
} else {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
.map(|res| res.map_into_left_body())
|
||||||
}
|
}
|
||||||
.map_ok(|res| res.map_body(|_, body| AnyBody::new_boxed(body)))
|
.boxed_local()
|
||||||
.boxed_local();
|
|
||||||
|
|
||||||
Either::right(res)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@ use actix_service::fn_service;
|
|||||||
use actix_utils::future::ok;
|
use actix_utils::future::ok;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::{ServiceRequest, Transform},
|
dev::{ServiceRequest, Transform},
|
||||||
http::{header, HeaderValue, Method, StatusCode},
|
http::{
|
||||||
|
header::{self, HeaderValue},
|
||||||
|
Method, StatusCode,
|
||||||
|
},
|
||||||
test::{self, TestRequest},
|
test::{self, TestRequest},
|
||||||
HttpResponse,
|
HttpResponse,
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Update `actix-web` dependency to `4.0.0.beta-14`. [#209]
|
||||||
|
|
||||||
|
[#209]: https://github.com/actix/actix-extras/pull/209
|
||||||
|
|
||||||
|
|
||||||
## 0.4.0-beta.4 - 2021-11-22
|
## 0.4.0-beta.4 - 2021-11-22
|
||||||
|
@ -16,12 +16,13 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
actix-web = { version = "4.0.0-beta.10", default-features = false, features = ["cookies", "secure-cookies"] }
|
actix-web = { version = "4.0.0-beta.14", default-features = false, features = ["cookies", "secure-cookies"] }
|
||||||
|
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
time = "0.2.23"
|
time = "0.2.23"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-http = "3.0.0-beta.11"
|
actix-http = "3.0.0-beta.15"
|
||||||
actix-rt = "2"
|
actix-rt = "2"
|
||||||
|
@ -371,6 +371,7 @@ mod tests {
|
|||||||
use std::{borrow::Borrow, time::SystemTime};
|
use std::{borrow::Borrow, time::SystemTime};
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
|
body::{BoxBody, EitherBody},
|
||||||
cookie::{Cookie, CookieJar, Key, SameSite},
|
cookie::{Cookie, CookieJar, Key, SameSite},
|
||||||
dev::ServiceResponse,
|
dev::ServiceResponse,
|
||||||
http::{header, StatusCode},
|
http::{header, StatusCode},
|
||||||
@ -408,7 +409,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn assert_login_cookie(
|
fn assert_login_cookie(
|
||||||
response: &mut ServiceResponse,
|
response: &mut ServiceResponse<EitherBody<BoxBody>>,
|
||||||
identity: &str,
|
identity: &str,
|
||||||
login_timestamp: LoginTimestampCheck,
|
login_timestamp: LoginTimestampCheck,
|
||||||
visit_timestamp: VisitTimeStampCheck,
|
visit_timestamp: VisitTimeStampCheck,
|
||||||
@ -577,13 +578,19 @@ mod tests {
|
|||||||
jar.get(COOKIE_NAME).unwrap().clone()
|
jar.get(COOKIE_NAME).unwrap().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn assert_logged_in(response: ServiceResponse, identity: Option<&str>) {
|
async fn assert_logged_in(
|
||||||
|
response: ServiceResponse<EitherBody<BoxBody>>,
|
||||||
|
identity: Option<&str>,
|
||||||
|
) {
|
||||||
let bytes = test::read_body(response).await;
|
let bytes = test::read_body(response).await;
|
||||||
let resp: Option<String> = serde_json::from_slice(&bytes[..]).unwrap();
|
let resp: Option<String> = serde_json::from_slice(&bytes[..]).unwrap();
|
||||||
assert_eq!(resp.as_ref().map(|s| s.borrow()), identity);
|
assert_eq!(resp.as_ref().map(|s| s.borrow()), identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_legacy_login_cookie(response: &mut ServiceResponse, identity: &str) {
|
fn assert_legacy_login_cookie(
|
||||||
|
response: &mut ServiceResponse<EitherBody<BoxBody>>,
|
||||||
|
identity: &str,
|
||||||
|
) {
|
||||||
let mut cookies = CookieJar::new();
|
let mut cookies = CookieJar::new();
|
||||||
for cookie in response.headers().get_all(header::SET_COOKIE) {
|
for cookie in response.headers().get_all(header::SET_COOKIE) {
|
||||||
cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
|
cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
|
||||||
@ -595,7 +602,7 @@ mod tests {
|
|||||||
assert_eq!(cookie.value(), identity);
|
assert_eq!(cookie.value(), identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_no_login_cookie(response: &mut ServiceResponse) {
|
fn assert_no_login_cookie(response: &mut ServiceResponse<EitherBody<BoxBody>>) {
|
||||||
let mut cookies = CookieJar::new();
|
let mut cookies = CookieJar::new();
|
||||||
for cookie in response.headers().get_all(header::SET_COOKIE) {
|
for cookie in response.headers().get_all(header::SET_COOKIE) {
|
||||||
cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
|
cookies.add(Cookie::parse(cookie.to_str().unwrap().to_string()).unwrap());
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
use actix_utils::future::{ready, Ready};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::{Extensions, Payload},
|
dev::{Extensions, Payload},
|
||||||
Error, FromRequest, HttpRequest,
|
Error, FromRequest, HttpRequest,
|
||||||
};
|
};
|
||||||
use actix_utils::future::{ready, Ready};
|
|
||||||
|
|
||||||
pub(crate) struct IdentityItem {
|
pub(crate) struct IdentityItem {
|
||||||
pub(crate) id: Option<String>,
|
pub(crate) id: Option<String>,
|
||||||
@ -48,12 +48,12 @@ impl Identity {
|
|||||||
/// Return the claimed identity of the user associated request or `None` if no identity can be
|
/// Return the claimed identity of the user associated request or `None` if no identity can be
|
||||||
/// found associated with the request.
|
/// found associated with the request.
|
||||||
pub fn identity(&self) -> Option<String> {
|
pub fn identity(&self) -> Option<String> {
|
||||||
Identity::get_identity(&self.0.extensions())
|
Identity::get_identity(&self.0.req_data())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remember identity.
|
/// Remember identity.
|
||||||
pub fn remember(&self, identity: String) {
|
pub fn remember(&self, identity: String) {
|
||||||
if let Some(id) = self.0.extensions_mut().get_mut::<IdentityItem>() {
|
if let Some(id) = self.0.req_data_mut().get_mut::<IdentityItem>() {
|
||||||
id.id = Some(identity);
|
id.id = Some(identity);
|
||||||
id.changed = true;
|
id.changed = true;
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ impl Identity {
|
|||||||
|
|
||||||
/// This method is used to 'forget' the current identity on subsequent requests.
|
/// This method is used to 'forget' the current identity on subsequent requests.
|
||||||
pub fn forget(&self) {
|
pub fn forget(&self) {
|
||||||
if let Some(id) = self.0.extensions_mut().get_mut::<IdentityItem>() {
|
if let Some(id) = self.0.req_data_mut().get_mut::<IdentityItem>() {
|
||||||
id.id = None;
|
id.id = None;
|
||||||
id.changed = true;
|
id.changed = true;
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,11 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use actix_web::{dev::ServiceResponse, test, web, App, Error};
|
use actix_web::{
|
||||||
|
body::{BoxBody, EitherBody},
|
||||||
|
dev::ServiceResponse,
|
||||||
|
test, web, App, Error,
|
||||||
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@ -130,7 +134,7 @@ mod tests {
|
|||||||
f: F,
|
f: F,
|
||||||
) -> impl actix_service::Service<
|
) -> impl actix_service::Service<
|
||||||
actix_http::Request,
|
actix_http::Request,
|
||||||
Response = ServiceResponse<actix_web::body::AnyBody>,
|
Response = ServiceResponse<EitherBody<BoxBody>>,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
> {
|
> {
|
||||||
test::init_service(
|
test::init_service(
|
||||||
|
@ -2,11 +2,11 @@ use std::{error::Error as StdError, rc::Rc};
|
|||||||
|
|
||||||
use actix_utils::future::{ready, Ready};
|
use actix_utils::future::{ready, Ready};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::{AnyBody, MessageBody},
|
body::{EitherBody, MessageBody},
|
||||||
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
||||||
Error, HttpMessage, Result,
|
Error, HttpMessage, Result,
|
||||||
};
|
};
|
||||||
use futures_util::future::{FutureExt as _, LocalBoxFuture, TryFutureExt as _};
|
use futures_util::future::{FutureExt as _, LocalBoxFuture};
|
||||||
|
|
||||||
use crate::{identity::IdentityItem, IdentityPolicy};
|
use crate::{identity::IdentityItem, IdentityPolicy};
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ where
|
|||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
B::Error: StdError,
|
B::Error: StdError,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Transform = IdentityServiceMiddleware<S, T>;
|
type Transform = IdentityServiceMiddleware<S, T>;
|
||||||
@ -82,7 +82,7 @@ where
|
|||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
B::Error: StdError,
|
B::Error: StdError,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
@ -104,17 +104,16 @@ where
|
|||||||
|
|
||||||
if let Some(id) = id {
|
if let Some(id) = id {
|
||||||
match backend.to_response(id.id, id.changed, &mut res).await {
|
match backend.to_response(id.id, id.changed, &mut res).await {
|
||||||
Ok(_) => Ok(res.map_body(|_, body| AnyBody::new_boxed(body))),
|
Ok(_) => Ok(res.map_into_left_body()),
|
||||||
Err(e) => Ok(res.error_response(e)),
|
Err(err) => Ok(res.error_response(err).map_into_right_body()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(res.map_body(|_, body| AnyBody::new_boxed(body)))
|
Ok(res.map_into_left_body())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => Ok(req.error_response(err)),
|
Err(err) => Ok(req.error_response(err).map_into_right_body()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.map_ok(|res| res.map_body(|_, body| AnyBody::new_boxed(body)))
|
|
||||||
.boxed_local()
|
.boxed_local()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Update `actix-web` dependency to `4.0.0.beta-14`. [#209]
|
||||||
|
|
||||||
|
[#209]: https://github.com/actix/actix-extras/pull/209
|
||||||
|
|
||||||
|
|
||||||
## 0.7.0-beta.2 - 2021-10-21
|
## 0.7.0-beta.2 - 2021-10-21
|
||||||
|
@ -19,7 +19,7 @@ path = "src/lib.rs"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-rt = "2"
|
actix-rt = "2"
|
||||||
actix-web = { version = "4.0.0-beta.10", default_features = false }
|
actix-web = { version = "4.0.0-beta.14", default_features = false }
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
prost = { version = "0.8", default_features = false }
|
prost = { version = "0.8", default_features = false }
|
||||||
|
@ -8,7 +8,7 @@ authors = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = "4.0.0-beta.10"
|
actix-web = "4.0.0-beta.14"
|
||||||
actix-protobuf = { path = "../../" }
|
actix-protobuf = { path = "../../" }
|
||||||
|
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
|
@ -11,6 +11,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
|
body::BoxBody,
|
||||||
dev::Payload,
|
dev::Payload,
|
||||||
error::PayloadError,
|
error::PayloadError,
|
||||||
http::header::{CONTENT_LENGTH, CONTENT_TYPE},
|
http::header::{CONTENT_LENGTH, CONTENT_TYPE},
|
||||||
@ -145,6 +146,8 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Message + Default> Responder for ProtoBuf<T> {
|
impl<T: Message + Default> Responder for ProtoBuf<T> {
|
||||||
|
type Body = BoxBody;
|
||||||
|
|
||||||
fn respond_to(self, _: &HttpRequest) -> HttpResponse {
|
fn respond_to(self, _: &HttpRequest) -> HttpResponse {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
match self.0.encode(&mut buf) {
|
match self.0.encode(&mut buf) {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Update `actix-web` dependency to `4.0.0.beta-14`. [#209]
|
||||||
|
|
||||||
|
[#209]: https://github.com/actix/actix-extras/pull/209
|
||||||
|
|
||||||
|
|
||||||
## 0.10.0-beta.3 - 2021-10-21
|
## 0.10.0-beta.3 - 2021-10-21
|
||||||
|
@ -32,7 +32,7 @@ web = [
|
|||||||
actix = { version = "0.12.0", default-features = false }
|
actix = { version = "0.12.0", default-features = false }
|
||||||
actix-rt = { version = "2.1", default-features = false }
|
actix-rt = { version = "2.1", default-features = false }
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-tls = { version = "3.0.0-rc.1", default-features = false, features = ["connect"] }
|
actix-tls = { version = "3.0.0-rc.2", default-features = false, features = ["connect"] }
|
||||||
|
|
||||||
log = "0.4.6"
|
log = "0.4.6"
|
||||||
backoff = "0.2.1"
|
backoff = "0.2.1"
|
||||||
@ -45,15 +45,14 @@ tokio = { version = "1", features = ["sync"] }
|
|||||||
tokio-util = "0.6.1"
|
tokio-util = "0.6.1"
|
||||||
|
|
||||||
# actix-session
|
# actix-session
|
||||||
actix-web = { version = "4.0.0-beta.10", default_features = false, optional = true }
|
actix-web = { version = "4.0.0-beta.14", default_features = false, optional = true }
|
||||||
actix-session = { version = "0.5.0-beta.4", optional = true }
|
actix-session = { version = "0.5.0-beta.4", optional = true }
|
||||||
rand = { version = "0.8.0", optional = true }
|
rand = { version = "0.8.0", optional = true }
|
||||||
serde = { version = "1.0.101", optional = true }
|
serde = { version = "1.0.101", optional = true }
|
||||||
serde_json = { version = "1.0.40", optional = true }
|
serde_json = { version = "1.0.40", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-test = "0.1.0-beta.5"
|
actix-test = "0.1.0-beta.8"
|
||||||
actix-http = "3.0.0-beta.11"
|
|
||||||
actix-rt = "2.1"
|
actix-rt = "2.1"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
serde = { version = "1.0.101", features = ["derive"] }
|
serde = { version = "1.0.101", features = ["derive"] }
|
||||||
|
@ -3,8 +3,8 @@ use std::io;
|
|||||||
|
|
||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_rt::net::TcpStream;
|
use actix_rt::net::TcpStream;
|
||||||
use actix_service::boxed::{service, BoxService};
|
use actix_service::boxed::{self, BoxService};
|
||||||
use actix_tls::connect::{ConnectError, ConnectInfo as Connect, Connection, Connector};
|
use actix_tls::connect::{ConnectError, ConnectInfo, Connection, ConnectorService};
|
||||||
use backoff::backoff::Backoff;
|
use backoff::backoff::Backoff;
|
||||||
use backoff::ExponentialBackoff;
|
use backoff::ExponentialBackoff;
|
||||||
use log::{error, info, warn};
|
use log::{error, info, warn};
|
||||||
@ -27,7 +27,7 @@ impl Message for Command {
|
|||||||
/// Redis communication actor
|
/// Redis communication actor
|
||||||
pub struct RedisActor {
|
pub struct RedisActor {
|
||||||
addr: String,
|
addr: String,
|
||||||
connector: BoxService<Connect<String>, Connection<String, TcpStream>, ConnectError>,
|
connector: BoxService<ConnectInfo<String>, Connection<String, TcpStream>, ConnectError>,
|
||||||
backoff: ExponentialBackoff,
|
backoff: ExponentialBackoff,
|
||||||
cell: Option<actix::io::FramedWrite<RespValue, WriteHalf<TcpStream>, RespCodec>>,
|
cell: Option<actix::io::FramedWrite<RespValue, WriteHalf<TcpStream>, RespCodec>>,
|
||||||
queue: VecDeque<oneshot::Sender<Result<RespValue, Error>>>,
|
queue: VecDeque<oneshot::Sender<Result<RespValue, Error>>>,
|
||||||
@ -45,7 +45,7 @@ impl RedisActor {
|
|||||||
|
|
||||||
Supervisor::start(|_| RedisActor {
|
Supervisor::start(|_| RedisActor {
|
||||||
addr,
|
addr,
|
||||||
connector: service(Connector::default().service()),
|
connector: boxed::service(ConnectorService::default()),
|
||||||
cell: None,
|
cell: None,
|
||||||
backoff,
|
backoff,
|
||||||
queue: VecDeque::new(),
|
queue: VecDeque::new(),
|
||||||
@ -57,7 +57,7 @@ impl Actor for RedisActor {
|
|||||||
type Context = Context<Self>;
|
type Context = Context<Self>;
|
||||||
|
|
||||||
fn started(&mut self, ctx: &mut Context<Self>) {
|
fn started(&mut self, ctx: &mut Context<Self>) {
|
||||||
let req = Connect::new(self.addr.to_owned());
|
let req = ConnectInfo::new(self.addr.to_owned());
|
||||||
self.connector
|
self.connector
|
||||||
.call(req)
|
.call(req)
|
||||||
.into_actor(self)
|
.into_actor(self)
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Update `actix-web` dependency to `4.0.0.beta-14`. [#209]
|
||||||
|
* Remove `UserSession` implementation for `RequestHead`. [#209]
|
||||||
|
|
||||||
|
[#209]: https://github.com/actix/actix-extras/pull/209
|
||||||
|
|
||||||
|
|
||||||
## 0.5.0-beta.4 - 2021-11-22
|
## 0.5.0-beta.4 - 2021-11-22
|
||||||
|
@ -20,7 +20,7 @@ cookie-session = ["actix-web/secure-cookies"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
actix-web = { version = "4.0.0-beta.10", default_features = false, features = ["cookies"] }
|
actix-web = { version = "4.0.0-beta.14", default_features = false, features = ["cookies"] }
|
||||||
|
|
||||||
derive_more = "0.99.5"
|
derive_more = "0.99.5"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
@ -4,10 +4,10 @@ use std::{collections::HashMap, error::Error as StdError, rc::Rc};
|
|||||||
|
|
||||||
use actix_utils::future::{ok, Ready};
|
use actix_utils::future::{ok, Ready};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::{AnyBody, MessageBody},
|
body::{EitherBody, MessageBody},
|
||||||
cookie::{Cookie, CookieJar, Key, SameSite},
|
cookie::{Cookie, CookieJar, Key, SameSite},
|
||||||
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
||||||
http::{header::SET_COOKIE, HeaderValue},
|
http::header::{HeaderValue, SET_COOKIE},
|
||||||
Error, ResponseError,
|
Error, ResponseError,
|
||||||
};
|
};
|
||||||
use derive_more::Display;
|
use derive_more::Display;
|
||||||
@ -303,7 +303,7 @@ where
|
|||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
B::Error: StdError,
|
B::Error: StdError,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
type Error = S::Error;
|
type Error = S::Error;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
type Transform = CookieSessionMiddleware<S>;
|
type Transform = CookieSessionMiddleware<S>;
|
||||||
@ -331,7 +331,7 @@ where
|
|||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
B::Error: StdError,
|
B::Error: StdError,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
type Error = S::Error;
|
type Error = S::Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
@ -379,8 +379,8 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(()) => Ok(res.map_body(|_, body| AnyBody::new_boxed(body))),
|
Ok(()) => Ok(res.map_into_left_body()),
|
||||||
Err(error) => Ok(res.error_response(error)),
|
Err(error) => Ok(res.error_response(error).map_into_right_body()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.boxed_local()
|
.boxed_local()
|
||||||
|
@ -51,7 +51,7 @@ use std::{
|
|||||||
|
|
||||||
use actix_utils::future::{ok, Ready};
|
use actix_utils::future::{ok, Ready};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
dev::{Extensions, Payload, RequestHead, ServiceRequest, ServiceResponse},
|
dev::{Extensions, Payload, ServiceRequest, ServiceResponse},
|
||||||
Error, FromRequest, HttpMessage, HttpRequest,
|
Error, FromRequest, HttpMessage, HttpRequest,
|
||||||
};
|
};
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
@ -101,12 +101,6 @@ impl UserSession for ServiceRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserSession for RequestHead {
|
|
||||||
fn get_session(&self) -> Session {
|
|
||||||
Session::get_session(&mut *self.extensions_mut())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Status of a [`Session`].
|
/// Status of a [`Session`].
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
pub enum SessionStatus {
|
pub enum SessionStatus {
|
||||||
@ -355,20 +349,6 @@ mod tests {
|
|||||||
assert_eq!(res, Some(true));
|
assert_eq!(res, Some(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_web::test]
|
|
||||||
async fn get_session_from_request_head() {
|
|
||||||
let mut req = test::TestRequest::default().to_srv_request();
|
|
||||||
|
|
||||||
Session::set_session(
|
|
||||||
&mut req,
|
|
||||||
vec![("key".to_string(), serde_json::to_string(&10).unwrap())],
|
|
||||||
);
|
|
||||||
|
|
||||||
let session = req.head_mut().get_session();
|
|
||||||
let res = session.get::<u32>("key").unwrap();
|
|
||||||
assert_eq!(res, Some(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_web::test]
|
#[actix_web::test]
|
||||||
async fn purge_session() {
|
async fn purge_session() {
|
||||||
let req = test::TestRequest::default().to_srv_request();
|
let req = test::TestRequest::default().to_srv_request();
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
# Changes
|
# Changes
|
||||||
|
|
||||||
## Unreleased - 2021-xx-xx
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Update `actix-web` dependency to `4.0.0.beta-14`. [#209]
|
||||||
|
|
||||||
|
[#209]: https://github.com/actix/actix-extras/pull/209
|
||||||
|
|
||||||
|
|
||||||
## 0.6.0-beta.4 - 2021-11-22
|
## 0.6.0-beta.4 - 2021-11-22
|
||||||
|
@ -20,7 +20,7 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
actix-service = "2.0.0"
|
actix-service = "2.0.0"
|
||||||
actix-utils = "3"
|
actix-utils = "3"
|
||||||
actix-web = { version = "4.0.0-beta.10", default_features = false }
|
actix-web = { version = "4.0.0-beta.14", default_features = false }
|
||||||
|
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
@ -11,7 +11,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
body::{AnyBody, MessageBody},
|
body::{EitherBody, MessageBody},
|
||||||
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
dev::{Service, ServiceRequest, ServiceResponse, Transform},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
@ -127,7 +127,7 @@ where
|
|||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
B::Error: StdError,
|
B::Error: StdError,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Transform = AuthenticationMiddleware<S, F, T>;
|
type Transform = AuthenticationMiddleware<S, F, T>;
|
||||||
type InitError = ();
|
type InitError = ();
|
||||||
@ -162,9 +162,9 @@ where
|
|||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
B::Error: StdError,
|
B::Error: StdError,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse;
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
type Error = S::Error;
|
type Error = S::Error;
|
||||||
type Future = LocalBoxFuture<'static, Result<ServiceResponse, Error>>;
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
actix_service::forward_ready!(service);
|
actix_service::forward_ready!(service);
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ where
|
|||||||
let (req, credentials) = match Extract::<T>::new(req).await {
|
let (req, credentials) = match Extract::<T>::new(req).await {
|
||||||
Ok(req) => req,
|
Ok(req) => req,
|
||||||
Err((err, req)) => {
|
Err((err, req)) => {
|
||||||
return Ok(req.error_response(err));
|
return Ok(req.error_response(err).map_into_right_body());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -185,10 +185,7 @@ where
|
|||||||
// middleware to do their thing (eg. cors adding headers)
|
// middleware to do their thing (eg. cors adding headers)
|
||||||
let req = process_fn(req, credentials).await?;
|
let req = process_fn(req, credentials).await?;
|
||||||
|
|
||||||
service
|
service.call(req).await.map(|res| res.map_into_left_body())
|
||||||
.call(req)
|
|
||||||
.await
|
|
||||||
.map(|res| res.map_body(|_, body| AnyBody::new_boxed(body)))
|
|
||||||
}
|
}
|
||||||
.boxed_local()
|
.boxed_local()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user