mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-24 07:53:00 +01:00
update middleware impl
This commit is contained in:
parent
2e79562c9d
commit
b6fe1dacf2
@ -56,8 +56,8 @@ ssl = ["openssl", "actix-server/ssl"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.1.0"
|
actix-codec = "0.1.0"
|
||||||
actix-service = "0.3.0"
|
actix-service = "0.3.2"
|
||||||
actix-utils = "0.3.0"
|
actix-utils = "0.3.1"
|
||||||
actix-rt = "0.1.0"
|
actix-rt = "0.1.0"
|
||||||
|
|
||||||
actix-http = { git = "https://github.com/actix/actix-http.git" }
|
actix-http = { git = "https://github.com/actix/actix-http.git" }
|
||||||
@ -99,3 +99,6 @@ serde_derive = "1.0"
|
|||||||
lto = true
|
lto = true
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
actix-service = { git = "https://github.com/actix/actix-net.git" }
|
||||||
|
16
src/app.rs
16
src/app.rs
@ -7,8 +7,8 @@ use actix_http::{Extensions, PayloadStream, Request, Response};
|
|||||||
use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
|
use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
|
||||||
use actix_service::boxed::{self, BoxedNewService, BoxedService};
|
use actix_service::boxed::{self, BoxedNewService, BoxedService};
|
||||||
use actix_service::{
|
use actix_service::{
|
||||||
AndThenNewService, ApplyNewService, IntoNewService, IntoNewTransform, NewService,
|
AndThenNewService, ApplyTransform, IntoNewService, IntoTransform, NewService,
|
||||||
NewTransform, Service,
|
Service, Transform,
|
||||||
};
|
};
|
||||||
use futures::future::{ok, Either, FutureResult};
|
use futures::future::{ok, Either, FutureResult};
|
||||||
use futures::{Async, Future, IntoFuture, Poll};
|
use futures::{Async, Future, IntoFuture, Poll};
|
||||||
@ -237,17 +237,17 @@ where
|
|||||||
>,
|
>,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
M: NewTransform<
|
M: Transform<
|
||||||
AppRouting<P>,
|
AppRouting<P>,
|
||||||
Request = ServiceRequest<P>,
|
Request = ServiceRequest<P>,
|
||||||
Response = ServiceResponse<B>,
|
Response = ServiceResponse<B>,
|
||||||
Error = (),
|
Error = (),
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
F: IntoNewTransform<M, AppRouting<P>>,
|
F: IntoTransform<M, AppRouting<P>>,
|
||||||
{
|
{
|
||||||
let fref = Rc::new(RefCell::new(None));
|
let fref = Rc::new(RefCell::new(None));
|
||||||
let endpoint = ApplyNewService::new(mw, AppEntry::new(fref.clone()));
|
let endpoint = ApplyTransform::new(mw, AppEntry::new(fref.clone()));
|
||||||
AppRouter {
|
AppRouter {
|
||||||
endpoint,
|
endpoint,
|
||||||
chain: self.chain,
|
chain: self.chain,
|
||||||
@ -480,7 +480,7 @@ where
|
|||||||
>,
|
>,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
M: NewTransform<
|
M: Transform<
|
||||||
T::Service,
|
T::Service,
|
||||||
Request = ServiceRequest<P>,
|
Request = ServiceRequest<P>,
|
||||||
Response = ServiceResponse<B1>,
|
Response = ServiceResponse<B1>,
|
||||||
@ -488,9 +488,9 @@ where
|
|||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
B1: MessageBody,
|
B1: MessageBody,
|
||||||
F: IntoNewTransform<M, T::Service>,
|
F: IntoTransform<M, T::Service>,
|
||||||
{
|
{
|
||||||
let endpoint = ApplyNewService::new(mw, self.endpoint);
|
let endpoint = ApplyTransform::new(mw, self.endpoint);
|
||||||
AppRouter {
|
AppRouter {
|
||||||
endpoint,
|
endpoint,
|
||||||
chain: self.chain,
|
chain: self.chain,
|
||||||
|
@ -8,8 +8,9 @@ use actix_http::http::header::{
|
|||||||
};
|
};
|
||||||
use actix_http::http::{HttpTryFrom, StatusCode};
|
use actix_http::http::{HttpTryFrom, StatusCode};
|
||||||
use actix_http::{Error, Head, ResponseHead};
|
use actix_http::{Error, Head, ResponseHead};
|
||||||
use actix_service::{IntoNewTransform, Service, Transform};
|
use actix_service::{Service, Transform};
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
|
use futures::future::{ok, FutureResult};
|
||||||
use futures::{Async, Future, Poll};
|
use futures::{Async, Future, Poll};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
@ -18,7 +19,6 @@ use brotli2::write::BrotliEncoder;
|
|||||||
#[cfg(feature = "flate2")]
|
#[cfg(feature = "flate2")]
|
||||||
use flate2::write::{GzEncoder, ZlibEncoder};
|
use flate2::write::{GzEncoder, ZlibEncoder};
|
||||||
|
|
||||||
use crate::middleware::MiddlewareFactory;
|
|
||||||
use crate::service::{ServiceRequest, ServiceResponse};
|
use crate::service::{ServiceRequest, ServiceResponse};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -37,6 +37,33 @@ impl Default for Compress {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<S, P, B> Transform<S> for Compress
|
impl<S, P, B> Transform<S> for Compress
|
||||||
|
where
|
||||||
|
P: 'static,
|
||||||
|
B: MessageBody,
|
||||||
|
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
|
||||||
|
S::Future: 'static,
|
||||||
|
{
|
||||||
|
type Request = ServiceRequest<P>;
|
||||||
|
type Response = ServiceResponse<Encoder<B>>;
|
||||||
|
type Error = S::Error;
|
||||||
|
type InitError = ();
|
||||||
|
type Transform = CompressMiddleware<S>;
|
||||||
|
type Future = FutureResult<Self::Transform, Self::InitError>;
|
||||||
|
|
||||||
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
|
ok(CompressMiddleware {
|
||||||
|
service,
|
||||||
|
encoding: self.0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct CompressMiddleware<S> {
|
||||||
|
service: S,
|
||||||
|
encoding: ContentEncoding,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, P, B> Service for CompressMiddleware<S>
|
||||||
where
|
where
|
||||||
P: 'static,
|
P: 'static,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
@ -49,14 +76,14 @@ where
|
|||||||
type Future = CompressResponse<S, P, B>;
|
type Future = CompressResponse<S, P, B>;
|
||||||
|
|
||||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||||
Ok(Async::Ready(()))
|
self.service.poll_ready()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest<P>, srv: &mut S) -> Self::Future {
|
fn call(&mut self, req: ServiceRequest<P>) -> Self::Future {
|
||||||
// negotiate content-encoding
|
// negotiate content-encoding
|
||||||
let encoding = if let Some(val) = req.headers.get(ACCEPT_ENCODING) {
|
let encoding = if let Some(val) = req.headers.get(ACCEPT_ENCODING) {
|
||||||
if let Ok(enc) = val.to_str() {
|
if let Ok(enc) = val.to_str() {
|
||||||
AcceptEncoding::parse(enc, self.0)
|
AcceptEncoding::parse(enc, self.encoding)
|
||||||
} else {
|
} else {
|
||||||
ContentEncoding::Identity
|
ContentEncoding::Identity
|
||||||
}
|
}
|
||||||
@ -66,7 +93,7 @@ where
|
|||||||
|
|
||||||
CompressResponse {
|
CompressResponse {
|
||||||
encoding,
|
encoding,
|
||||||
fut: srv.call(req),
|
fut: self.service.call(req),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,18 +129,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, P, B> IntoNewTransform<MiddlewareFactory<Compress, S>, S> for Compress
|
|
||||||
where
|
|
||||||
P: 'static,
|
|
||||||
B: MessageBody,
|
|
||||||
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
|
|
||||||
S::Future: 'static,
|
|
||||||
{
|
|
||||||
fn into_new_transform(self) -> MiddlewareFactory<Compress, S> {
|
|
||||||
MiddlewareFactory::new(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum EncoderBody<B> {
|
enum EncoderBody<B> {
|
||||||
Body(B),
|
Body(B),
|
||||||
Other(Box<dyn MessageBody>),
|
Other(Box<dyn MessageBody>),
|
||||||
|
@ -3,10 +3,10 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use actix_http::http::header::{HeaderName, HeaderValue, CONTENT_TYPE};
|
use actix_http::http::header::{HeaderName, HeaderValue, CONTENT_TYPE};
|
||||||
use actix_http::http::{HeaderMap, HttpTryFrom};
|
use actix_http::http::{HeaderMap, HttpTryFrom};
|
||||||
use actix_service::{IntoNewTransform, Service, Transform};
|
use actix_service::{Service, Transform};
|
||||||
use futures::{Async, Future, Poll};
|
use futures::future::{ok, FutureResult};
|
||||||
|
use futures::{Future, Poll};
|
||||||
|
|
||||||
use crate::middleware::MiddlewareFactory;
|
|
||||||
use crate::service::{ServiceRequest, ServiceResponse};
|
use crate::service::{ServiceRequest, ServiceResponse};
|
||||||
|
|
||||||
/// `Middleware` for setting default response headers.
|
/// `Middleware` for setting default response headers.
|
||||||
@ -84,35 +84,49 @@ impl DefaultHeaders {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, State, B> IntoNewTransform<MiddlewareFactory<DefaultHeaders, S>, S>
|
impl<S, P, B> Transform<S> for DefaultHeaders
|
||||||
for DefaultHeaders
|
|
||||||
where
|
where
|
||||||
S: Service<Request = ServiceRequest<State>, Response = ServiceResponse<B>>,
|
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
{
|
{
|
||||||
fn into_new_transform(self) -> MiddlewareFactory<DefaultHeaders, S> {
|
type Request = ServiceRequest<P>;
|
||||||
MiddlewareFactory::new(self)
|
type Response = ServiceResponse<B>;
|
||||||
|
type Error = S::Error;
|
||||||
|
type InitError = ();
|
||||||
|
type Transform = DefaultHeadersMiddleware<S>;
|
||||||
|
type Future = FutureResult<Self::Transform, Self::InitError>;
|
||||||
|
|
||||||
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
|
ok(DefaultHeadersMiddleware {
|
||||||
|
service,
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, State, B> Transform<S> for DefaultHeaders
|
pub struct DefaultHeadersMiddleware<S> {
|
||||||
|
service: S,
|
||||||
|
inner: Rc<Inner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, P, B> Service for DefaultHeadersMiddleware<S>
|
||||||
where
|
where
|
||||||
S: Service<Request = ServiceRequest<State>, Response = ServiceResponse<B>>,
|
S: Service<Request = ServiceRequest<P>, Response = ServiceResponse<B>>,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
{
|
{
|
||||||
type Request = ServiceRequest<State>;
|
type Request = ServiceRequest<P>;
|
||||||
type Response = ServiceResponse<B>;
|
type Response = ServiceResponse<B>;
|
||||||
type Error = S::Error;
|
type Error = S::Error;
|
||||||
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
|
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;
|
||||||
|
|
||||||
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
|
||||||
Ok(Async::Ready(()))
|
self.service.poll_ready()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(&mut self, req: ServiceRequest<State>, srv: &mut S) -> Self::Future {
|
fn call(&mut self, req: ServiceRequest<P>) -> Self::Future {
|
||||||
let inner = self.inner.clone();
|
let inner = self.inner.clone();
|
||||||
|
|
||||||
Box::new(srv.call(req).map(move |mut res| {
|
Box::new(self.service.call(req).map(move |mut res| {
|
||||||
// set response headers
|
// set response headers
|
||||||
for (key, value) in inner.headers.iter() {
|
for (key, value) in inner.headers.iter() {
|
||||||
if !res.headers().contains_key(key) {
|
if !res.headers().contains_key(key) {
|
||||||
@ -143,32 +157,44 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default_headers() {
|
fn test_default_headers() {
|
||||||
let mut mw = DefaultHeaders::new().header(CONTENT_TYPE, "0001");
|
let srv = FnService::new(|req: ServiceRequest<_>| {
|
||||||
let mut srv = FnService::new(|req: ServiceRequest<_>| {
|
|
||||||
req.into_response(HttpResponse::Ok().finish())
|
req.into_response(HttpResponse::Ok().finish())
|
||||||
});
|
});
|
||||||
|
let mut mw = block_on(
|
||||||
|
DefaultHeaders::new()
|
||||||
|
.header(CONTENT_TYPE, "0001")
|
||||||
|
.new_transform(srv),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let req = TestRequest::default().to_service();
|
let req = TestRequest::default().to_service();
|
||||||
let resp = block_on(mw.call(req, &mut srv)).unwrap();
|
let resp = block_on(mw.call(req)).unwrap();
|
||||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
|
||||||
|
|
||||||
let req = TestRequest::default().to_service();
|
let req = TestRequest::default().to_service();
|
||||||
let mut srv = FnService::new(|req: ServiceRequest<_>| {
|
let srv = FnService::new(|req: ServiceRequest<_>| {
|
||||||
req.into_response(HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish())
|
req.into_response(HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish())
|
||||||
});
|
});
|
||||||
let resp = block_on(mw.call(req, &mut srv)).unwrap();
|
let mut mw = block_on(
|
||||||
|
DefaultHeaders::new()
|
||||||
|
.header(CONTENT_TYPE, "0001")
|
||||||
|
.new_transform(srv),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let resp = block_on(mw.call(req)).unwrap();
|
||||||
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002");
|
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0002");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_content_type() {
|
fn test_content_type() {
|
||||||
let mut mw = DefaultHeaders::new().content_type();
|
let srv = FnService::new(|req: ServiceRequest<_>| {
|
||||||
let mut srv = FnService::new(|req: ServiceRequest<_>| {
|
|
||||||
req.into_response(HttpResponse::Ok().finish())
|
req.into_response(HttpResponse::Ok().finish())
|
||||||
});
|
});
|
||||||
|
let mut mw =
|
||||||
|
block_on(DefaultHeaders::new().content_type().new_transform(srv)).unwrap();
|
||||||
|
|
||||||
let req = TestRequest::default().to_service();
|
let req = TestRequest::default().to_service();
|
||||||
let resp = block_on(mw.call(req, &mut srv)).unwrap();
|
let resp = block_on(mw.call(req)).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resp.headers().get(CONTENT_TYPE).unwrap(),
|
resp.headers().get(CONTENT_TYPE).unwrap(),
|
||||||
"application/octet-stream"
|
"application/octet-stream"
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use actix_service::{NewTransform, Service, Transform};
|
|
||||||
use futures::future::{ok, FutureResult};
|
|
||||||
|
|
||||||
#[cfg(any(feature = "brotli", feature = "flate2"))]
|
#[cfg(any(feature = "brotli", feature = "flate2"))]
|
||||||
mod compress;
|
mod compress;
|
||||||
#[cfg(any(feature = "brotli", feature = "flate2"))]
|
#[cfg(any(feature = "brotli", feature = "flate2"))]
|
||||||
@ -10,43 +5,3 @@ pub use self::compress::Compress;
|
|||||||
|
|
||||||
mod defaultheaders;
|
mod defaultheaders;
|
||||||
pub use self::defaultheaders::DefaultHeaders;
|
pub use self::defaultheaders::DefaultHeaders;
|
||||||
|
|
||||||
/// Helper for middleware service factory
|
|
||||||
pub struct MiddlewareFactory<T, S>
|
|
||||||
where
|
|
||||||
T: Transform<S> + Clone,
|
|
||||||
S: Service,
|
|
||||||
{
|
|
||||||
tr: T,
|
|
||||||
_t: PhantomData<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, S> MiddlewareFactory<T, S>
|
|
||||||
where
|
|
||||||
T: Transform<S> + Clone,
|
|
||||||
S: Service,
|
|
||||||
{
|
|
||||||
pub fn new(tr: T) -> Self {
|
|
||||||
MiddlewareFactory {
|
|
||||||
tr,
|
|
||||||
_t: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T, S, C> NewTransform<S, C> for MiddlewareFactory<T, S>
|
|
||||||
where
|
|
||||||
T: Transform<S> + Clone,
|
|
||||||
S: Service,
|
|
||||||
{
|
|
||||||
type Request = T::Request;
|
|
||||||
type Response = T::Response;
|
|
||||||
type Error = T::Error;
|
|
||||||
type Transform = T;
|
|
||||||
type InitError = ();
|
|
||||||
type Future = FutureResult<Self::Transform, Self::InitError>;
|
|
||||||
|
|
||||||
fn new_transform(&self, _: &C) -> Self::Future {
|
|
||||||
ok(self.tr.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,7 @@ use std::rc::Rc;
|
|||||||
use actix_http::{Error, Response};
|
use actix_http::{Error, Response};
|
||||||
use actix_service::boxed::{self, BoxedNewService, BoxedService};
|
use actix_service::boxed::{self, BoxedNewService, BoxedService};
|
||||||
use actix_service::{
|
use actix_service::{
|
||||||
ApplyNewService, IntoNewService, IntoNewTransform, NewService, NewTransform, Service,
|
ApplyTransform, IntoNewService, IntoTransform, NewService, Service, Transform,
|
||||||
};
|
};
|
||||||
use futures::future::{ok, Either, FutureResult};
|
use futures::future::{ok, Either, FutureResult};
|
||||||
use futures::{Async, Future, IntoFuture, Poll};
|
use futures::{Async, Future, IntoFuture, Poll};
|
||||||
@ -194,16 +194,16 @@ where
|
|||||||
>,
|
>,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
M: NewTransform<
|
M: Transform<
|
||||||
T::Service,
|
T::Service,
|
||||||
Request = ServiceRequest<P>,
|
Request = ServiceRequest<P>,
|
||||||
Response = ServiceResponse,
|
Response = ServiceResponse,
|
||||||
Error = (),
|
Error = (),
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
F: IntoNewTransform<M, T::Service>,
|
F: IntoTransform<M, T::Service>,
|
||||||
{
|
{
|
||||||
let endpoint = ApplyNewService::new(mw, self.endpoint);
|
let endpoint = ApplyTransform::new(mw, self.endpoint);
|
||||||
Resource {
|
Resource {
|
||||||
endpoint,
|
endpoint,
|
||||||
routes: self.routes,
|
routes: self.routes,
|
||||||
|
@ -5,7 +5,7 @@ use actix_http::Response;
|
|||||||
use actix_router::{ResourceDef, ResourceInfo, Router};
|
use actix_router::{ResourceDef, ResourceInfo, Router};
|
||||||
use actix_service::boxed::{self, BoxedNewService, BoxedService};
|
use actix_service::boxed::{self, BoxedNewService, BoxedService};
|
||||||
use actix_service::{
|
use actix_service::{
|
||||||
ApplyNewService, IntoNewService, IntoNewTransform, NewService, NewTransform, Service,
|
ApplyTransform, IntoNewService, IntoTransform, NewService, Service, Transform,
|
||||||
};
|
};
|
||||||
use futures::future::{ok, Either, Future, FutureResult};
|
use futures::future::{ok, Either, Future, FutureResult};
|
||||||
use futures::{Async, Poll};
|
use futures::{Async, Poll};
|
||||||
@ -251,16 +251,16 @@ where
|
|||||||
>,
|
>,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
M: NewTransform<
|
M: Transform<
|
||||||
T::Service,
|
T::Service,
|
||||||
Request = ServiceRequest<P>,
|
Request = ServiceRequest<P>,
|
||||||
Response = ServiceResponse,
|
Response = ServiceResponse,
|
||||||
Error = (),
|
Error = (),
|
||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
F: IntoNewTransform<M, T::Service>,
|
F: IntoTransform<M, T::Service>,
|
||||||
{
|
{
|
||||||
let endpoint = ApplyNewService::new(mw, self.endpoint);
|
let endpoint = ApplyTransform::new(mw, self.endpoint);
|
||||||
Scope {
|
Scope {
|
||||||
endpoint,
|
endpoint,
|
||||||
rdef: self.rdef,
|
rdef: self.rdef,
|
||||||
|
Loading…
Reference in New Issue
Block a user