mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-28 09:42:40 +01:00
match resource path before executing middlewares
This commit is contained in:
parent
74d0656d27
commit
6c906b08e1
@ -1,6 +1,6 @@
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::UnsafeCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use handler::Reply;
|
use handler::Reply;
|
||||||
@ -9,7 +9,7 @@ use resource::{ResourceHandler};
|
|||||||
use header::ContentEncoding;
|
use header::ContentEncoding;
|
||||||
use handler::{Handler, RouteHandler, WrapHandler};
|
use handler::{Handler, RouteHandler, WrapHandler};
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use pipeline::{Pipeline, PipelineHandler};
|
use pipeline::{Pipeline, PipelineHandler, HandlerType};
|
||||||
use middleware::Middleware;
|
use middleware::Middleware;
|
||||||
use server::{HttpHandler, IntoHttpHandler, HttpHandlerTask, ServerSettings};
|
use server::{HttpHandler, IntoHttpHandler, HttpHandlerTask, ServerSettings};
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ pub struct HttpApplication<S=()> {
|
|||||||
state: Rc<S>,
|
state: Rc<S>,
|
||||||
prefix: String,
|
prefix: String,
|
||||||
router: Router,
|
router: Router,
|
||||||
inner: Rc<RefCell<Inner<S>>>,
|
inner: Rc<UnsafeCell<Inner<S>>>,
|
||||||
middlewares: Rc<Vec<Box<Middleware<S>>>>,
|
middlewares: Rc<Vec<Box<Middleware<S>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +29,6 @@ pub(crate) struct Inner<S> {
|
|||||||
prefix: usize,
|
prefix: usize,
|
||||||
default: ResourceHandler<S>,
|
default: ResourceHandler<S>,
|
||||||
encoding: ContentEncoding,
|
encoding: ContentEncoding,
|
||||||
router: Router,
|
|
||||||
resources: Vec<ResourceHandler<S>>,
|
resources: Vec<ResourceHandler<S>>,
|
||||||
handlers: Vec<(String, Box<RouteHandler<S>>)>,
|
handlers: Vec<(String, Box<RouteHandler<S>>)>,
|
||||||
}
|
}
|
||||||
@ -40,39 +39,60 @@ impl<S: 'static> PipelineHandler<S> for Inner<S> {
|
|||||||
self.encoding
|
self.encoding
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(&mut self, mut req: HttpRequest<S>) -> Reply {
|
fn handle(&mut self, req: HttpRequest<S>, htype: HandlerType) -> Reply {
|
||||||
if let Some(idx) = self.router.recognize(&mut req) {
|
match htype {
|
||||||
self.resources[idx].handle(req.clone(), Some(&mut self.default))
|
HandlerType::Normal(idx) =>
|
||||||
|
self.resources[idx].handle(req, Some(&mut self.default)),
|
||||||
|
HandlerType::Handler(idx) =>
|
||||||
|
self.handlers[idx].1.handle(req),
|
||||||
|
HandlerType::Default =>
|
||||||
|
self.default.handle(req, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: 'static> HttpApplication<S> {
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &Inner<S> {
|
||||||
|
unsafe{&*self.inner.get()}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn get_handler(&self, req: &mut HttpRequest<S>) -> HandlerType {
|
||||||
|
if let Some(idx) = self.router.recognize(req) {
|
||||||
|
HandlerType::Normal(idx)
|
||||||
} else {
|
} else {
|
||||||
for &mut (ref prefix, ref mut handler) in &mut self.handlers {
|
let inner = self.as_ref();
|
||||||
|
for idx in 0..inner.handlers.len() {
|
||||||
|
let &(ref prefix, _) = &inner.handlers[idx];
|
||||||
let m = {
|
let m = {
|
||||||
let path = &req.path()[self.prefix..];
|
let path = &req.path()[inner.prefix..];
|
||||||
path.starts_with(prefix) && (
|
path.starts_with(prefix) && (
|
||||||
path.len() == prefix.len() ||
|
path.len() == prefix.len() ||
|
||||||
path.split_at(prefix.len()).1.starts_with('/'))
|
path.split_at(prefix.len()).1.starts_with('/'))
|
||||||
};
|
};
|
||||||
if m {
|
if m {
|
||||||
let path: &'static str = unsafe {
|
let path: &'static str = unsafe {
|
||||||
mem::transmute(&req.path()[self.prefix+prefix.len()..]) };
|
mem::transmute(&req.path()[inner.prefix+prefix.len()..]) };
|
||||||
if path.is_empty() {
|
if path.is_empty() {
|
||||||
req.match_info_mut().add("tail", "");
|
req.match_info_mut().add("tail", "");
|
||||||
} else {
|
} else {
|
||||||
req.match_info_mut().add("tail", path.split_at(1).1);
|
req.match_info_mut().add("tail", path.split_at(1).1);
|
||||||
}
|
}
|
||||||
return handler.handle(req)
|
return HandlerType::Handler(idx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.default.handle(req, None)
|
HandlerType::Default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
impl<S: 'static> HttpApplication<S> {
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn run(&mut self, req: HttpRequest<S>) -> Reply {
|
pub(crate) fn run(&mut self, mut req: HttpRequest<S>) -> Reply {
|
||||||
self.inner.borrow_mut().handle(req)
|
let tp = self.get_handler(&mut req);
|
||||||
|
unsafe{&mut *self.inner.get()}.handle(req, tp)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest<S> {
|
pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest<S> {
|
||||||
req.with_state(Rc::clone(&self.state), self.router.clone())
|
req.with_state(Rc::clone(&self.state), self.router.clone())
|
||||||
@ -89,10 +109,10 @@ impl<S: 'static> HttpHandler for HttpApplication<S> {
|
|||||||
path.split_at(self.prefix.len()).1.starts_with('/'))
|
path.split_at(self.prefix.len()).1.starts_with('/'))
|
||||||
};
|
};
|
||||||
if m {
|
if m {
|
||||||
|
let mut req = req.with_state(Rc::clone(&self.state), self.router.clone());
|
||||||
|
let tp = self.get_handler(&mut req);
|
||||||
let inner = Rc::clone(&self.inner);
|
let inner = Rc::clone(&self.inner);
|
||||||
let req = req.with_state(Rc::clone(&self.state), self.router.clone());
|
Ok(Box::new(Pipeline::new(req, Rc::clone(&self.middlewares), inner, tp)))
|
||||||
|
|
||||||
Ok(Box::new(Pipeline::new(req, Rc::clone(&self.middlewares), inner)))
|
|
||||||
} else {
|
} else {
|
||||||
Err(req)
|
Err(req)
|
||||||
}
|
}
|
||||||
@ -392,12 +412,11 @@ impl<S> App<S> where S: 'static {
|
|||||||
|
|
||||||
let (router, resources) = Router::new(prefix, parts.settings, resources);
|
let (router, resources) = Router::new(prefix, parts.settings, resources);
|
||||||
|
|
||||||
let inner = Rc::new(RefCell::new(
|
let inner = Rc::new(UnsafeCell::new(
|
||||||
Inner {
|
Inner {
|
||||||
prefix: prefix.len(),
|
prefix: prefix.len(),
|
||||||
default: parts.default,
|
default: parts.default,
|
||||||
encoding: parts.encoding,
|
encoding: parts.encoding,
|
||||||
router: router.clone(),
|
|
||||||
handlers: parts.handlers,
|
handlers: parts.handlers,
|
||||||
resources,
|
resources,
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,13 @@ pub struct HttpInnerMessage {
|
|||||||
pub addr: Option<SocketAddr>,
|
pub addr: Option<SocketAddr>,
|
||||||
pub payload: Option<Payload>,
|
pub payload: Option<Payload>,
|
||||||
pub info: Option<ConnectionInfo<'static>>,
|
pub info: Option<ConnectionInfo<'static>>,
|
||||||
pub resource: i16,
|
resource: RouterResource,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone,PartialEq)]
|
||||||
|
enum RouterResource {
|
||||||
|
Notset,
|
||||||
|
Normal(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for HttpInnerMessage {
|
impl Default for HttpInnerMessage {
|
||||||
@ -58,7 +64,7 @@ impl Default for HttpInnerMessage {
|
|||||||
payload: None,
|
payload: None,
|
||||||
extensions: Extensions::new(),
|
extensions: Extensions::new(),
|
||||||
info: None,
|
info: None,
|
||||||
resource: -1,
|
resource: RouterResource::Notset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,12 +101,12 @@ impl HttpInnerMessage {
|
|||||||
self.addr = None;
|
self.addr = None;
|
||||||
self.info = None;
|
self.info = None;
|
||||||
self.payload = None;
|
self.payload = None;
|
||||||
self.resource = -1;
|
self.resource = RouterResource::Notset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static!{
|
lazy_static!{
|
||||||
static ref RESOURCE: Resource = Resource::default();
|
static ref RESOURCE: Resource = Resource::unset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -128,7 +134,7 @@ impl HttpRequest<()> {
|
|||||||
addr: None,
|
addr: None,
|
||||||
extensions: Extensions::new(),
|
extensions: Extensions::new(),
|
||||||
info: None,
|
info: None,
|
||||||
resource: -1,
|
resource: RouterResource::Notset,
|
||||||
}),
|
}),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
@ -330,17 +336,16 @@ impl<S> HttpRequest<S> {
|
|||||||
/// This method returns reference to matched `Resource` object.
|
/// This method returns reference to matched `Resource` object.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn resource(&self) -> &Resource {
|
pub fn resource(&self) -> &Resource {
|
||||||
let idx = self.as_ref().resource;
|
if let Some(ref router) = self.2 {
|
||||||
if idx >= 0 {
|
if let RouterResource::Normal(idx) = self.as_ref().resource {
|
||||||
if let Some(ref router) = self.2 {
|
|
||||||
return router.get_resource(idx as usize)
|
return router.get_resource(idx as usize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&*RESOURCE
|
&*RESOURCE
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_resource(&mut self, idx: usize) {
|
pub(crate) fn set_resource(&mut self, res: usize) {
|
||||||
self.as_mut().resource = idx as i16;
|
self.as_mut().resource = RouterResource::Normal(res as u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Peer socket address
|
/// Peer socket address
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::{io, mem};
|
use std::{io, mem};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::cell::RefCell;
|
use std::cell::UnsafeCell;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use log::Level::Debug;
|
use log::Level::Debug;
|
||||||
@ -18,11 +18,18 @@ use middleware::{Middleware, Finished, Started, Response};
|
|||||||
use application::Inner;
|
use application::Inner;
|
||||||
use server::{Writer, WriterState, HttpHandlerTask};
|
use server::{Writer, WriterState, HttpHandlerTask};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub(crate) enum HandlerType {
|
||||||
|
Normal(usize),
|
||||||
|
Handler(usize),
|
||||||
|
Default,
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) trait PipelineHandler<S> {
|
pub(crate) trait PipelineHandler<S> {
|
||||||
|
|
||||||
fn encoding(&self) -> ContentEncoding;
|
fn encoding(&self) -> ContentEncoding;
|
||||||
|
|
||||||
fn handle(&mut self, req: HttpRequest<S>) -> Reply;
|
fn handle(&mut self, req: HttpRequest<S>, htype: HandlerType) -> Reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Pipeline<S, H>(PipelineInfo<S>, PipelineState<S, H>);
|
pub(crate) struct Pipeline<S, H>(PipelineInfo<S>, PipelineState<S, H>);
|
||||||
@ -105,7 +112,7 @@ impl<S: 'static, H: PipelineHandler<S>> Pipeline<S, H> {
|
|||||||
|
|
||||||
pub fn new(req: HttpRequest<S>,
|
pub fn new(req: HttpRequest<S>,
|
||||||
mws: Rc<Vec<Box<Middleware<S>>>>,
|
mws: Rc<Vec<Box<Middleware<S>>>>,
|
||||||
handler: Rc<RefCell<H>>) -> Pipeline<S, H>
|
handler: Rc<UnsafeCell<H>>, htype: HandlerType) -> Pipeline<S, H>
|
||||||
{
|
{
|
||||||
let mut info = PipelineInfo {
|
let mut info = PipelineInfo {
|
||||||
req, mws,
|
req, mws,
|
||||||
@ -113,9 +120,9 @@ impl<S: 'static, H: PipelineHandler<S>> Pipeline<S, H> {
|
|||||||
error: None,
|
error: None,
|
||||||
context: None,
|
context: None,
|
||||||
disconnected: None,
|
disconnected: None,
|
||||||
encoding: handler.borrow().encoding(),
|
encoding: unsafe{&*handler.get()}.encoding(),
|
||||||
};
|
};
|
||||||
let state = StartMiddlewares::init(&mut info, handler);
|
let state = StartMiddlewares::init(&mut info, handler, htype);
|
||||||
|
|
||||||
Pipeline(info, state)
|
Pipeline(info, state)
|
||||||
}
|
}
|
||||||
@ -209,20 +216,23 @@ type Fut = Box<Future<Item=Option<HttpResponse>, Error=Error>>;
|
|||||||
|
|
||||||
/// Middlewares start executor
|
/// Middlewares start executor
|
||||||
struct StartMiddlewares<S, H> {
|
struct StartMiddlewares<S, H> {
|
||||||
hnd: Rc<RefCell<H>>,
|
hnd: Rc<UnsafeCell<H>>,
|
||||||
|
htype: HandlerType,
|
||||||
fut: Option<Fut>,
|
fut: Option<Fut>,
|
||||||
_s: PhantomData<S>,
|
_s: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
|
impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
|
||||||
|
|
||||||
fn init(info: &mut PipelineInfo<S>, handler: Rc<RefCell<H>>) -> PipelineState<S, H> {
|
fn init(info: &mut PipelineInfo<S>, hnd: Rc<UnsafeCell<H>>, htype: HandlerType)
|
||||||
|
-> PipelineState<S, H>
|
||||||
|
{
|
||||||
// execute middlewares, we need this stage because middlewares could be non-async
|
// execute middlewares, we need this stage because middlewares could be non-async
|
||||||
// and we can move to next state immediately
|
// and we can move to next state immediately
|
||||||
let len = info.mws.len() as u16;
|
let len = info.mws.len() as u16;
|
||||||
loop {
|
loop {
|
||||||
if info.count == len {
|
if info.count == len {
|
||||||
let reply = handler.borrow_mut().handle(info.req.clone());
|
let reply = unsafe{&mut *hnd.get()}.handle(info.req.clone(), htype);
|
||||||
return WaitingResponse::init(info, reply)
|
return WaitingResponse::init(info, reply)
|
||||||
} else {
|
} else {
|
||||||
match info.mws[info.count as usize].start(&mut info.req) {
|
match info.mws[info.count as usize].start(&mut info.req) {
|
||||||
@ -234,7 +244,7 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
|
|||||||
match fut.poll() {
|
match fut.poll() {
|
||||||
Ok(Async::NotReady) =>
|
Ok(Async::NotReady) =>
|
||||||
return PipelineState::Starting(StartMiddlewares {
|
return PipelineState::Starting(StartMiddlewares {
|
||||||
hnd: handler,
|
hnd, htype,
|
||||||
fut: Some(fut),
|
fut: Some(fut),
|
||||||
_s: PhantomData}),
|
_s: PhantomData}),
|
||||||
Ok(Async::Ready(resp)) => {
|
Ok(Async::Ready(resp)) => {
|
||||||
@ -264,7 +274,8 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
|
|||||||
return Some(RunMiddlewares::init(info, resp));
|
return Some(RunMiddlewares::init(info, resp));
|
||||||
}
|
}
|
||||||
if info.count == len {
|
if info.count == len {
|
||||||
let reply = (*self.hnd.borrow_mut()).handle(info.req.clone());
|
let reply = unsafe{
|
||||||
|
&mut *self.hnd.get()}.handle(info.req.clone(), self.htype);
|
||||||
return Some(WaitingResponse::init(info, reply));
|
return Some(WaitingResponse::init(info, reply));
|
||||||
} else {
|
} else {
|
||||||
loop {
|
loop {
|
||||||
|
@ -12,7 +12,6 @@ use resource::ResourceHandler;
|
|||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use server::ServerSettings;
|
use server::ServerSettings;
|
||||||
|
|
||||||
|
|
||||||
/// Interface for application router.
|
/// Interface for application router.
|
||||||
pub struct Router(Rc<Inner>);
|
pub struct Router(Rc<Inner>);
|
||||||
|
|
||||||
@ -127,7 +126,6 @@ impl Clone for Router {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
enum PatternElement {
|
enum PatternElement {
|
||||||
Str(String),
|
Str(String),
|
||||||
@ -140,26 +138,27 @@ enum PatternType {
|
|||||||
Dynamic(Regex, Vec<String>),
|
Dynamic(Regex, Vec<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
/// Resource type
|
||||||
|
pub enum ResourceType {
|
||||||
|
/// Normal resource
|
||||||
|
Normal,
|
||||||
|
/// Resource for applicaiton default handler
|
||||||
|
Default,
|
||||||
|
/// External resource
|
||||||
|
External,
|
||||||
|
/// Unknown resource type
|
||||||
|
Unset,
|
||||||
|
}
|
||||||
|
|
||||||
/// Reslource type describes an entry in resources table
|
/// Reslource type describes an entry in resources table
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Resource {
|
pub struct Resource {
|
||||||
tp: PatternType,
|
tp: PatternType,
|
||||||
|
rtp: ResourceType,
|
||||||
name: String,
|
name: String,
|
||||||
pattern: String,
|
pattern: String,
|
||||||
elements: Vec<PatternElement>,
|
elements: Vec<PatternElement>,
|
||||||
external: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Resource {
|
|
||||||
fn default() -> Resource {
|
|
||||||
Resource {
|
|
||||||
tp: PatternType::Static("".to_owned()),
|
|
||||||
name: "".to_owned(),
|
|
||||||
pattern: "".to_owned(),
|
|
||||||
elements: Vec::new(),
|
|
||||||
external: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resource {
|
impl Resource {
|
||||||
@ -175,10 +174,21 @@ impl Resource {
|
|||||||
/// Panics if path pattern is wrong.
|
/// Panics if path pattern is wrong.
|
||||||
pub fn external(name: &str, path: &str) -> Self {
|
pub fn external(name: &str, path: &str) -> Self {
|
||||||
let mut resource = Resource::with_prefix(name, path, "/");
|
let mut resource = Resource::with_prefix(name, path, "/");
|
||||||
resource.external = true;
|
resource.rtp = ResourceType::External;
|
||||||
resource
|
resource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Unset resource type
|
||||||
|
pub(crate) fn unset() -> Resource {
|
||||||
|
Resource {
|
||||||
|
tp: PatternType::Static("".to_owned()),
|
||||||
|
rtp: ResourceType::Unset,
|
||||||
|
name: "".to_owned(),
|
||||||
|
pattern: "".to_owned(),
|
||||||
|
elements: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse path pattern and create new `Resource` instance with custom prefix
|
/// Parse path pattern and create new `Resource` instance with custom prefix
|
||||||
pub fn with_prefix(name: &str, path: &str, prefix: &str) -> Self {
|
pub fn with_prefix(name: &str, path: &str, prefix: &str) -> Self {
|
||||||
let (pattern, elements, is_dynamic) = Resource::parse(path, prefix);
|
let (pattern, elements, is_dynamic) = Resource::parse(path, prefix);
|
||||||
@ -200,8 +210,8 @@ impl Resource {
|
|||||||
tp,
|
tp,
|
||||||
elements,
|
elements,
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
|
rtp: ResourceType::Normal,
|
||||||
pattern: path.to_owned(),
|
pattern: path.to_owned(),
|
||||||
external: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,6 +220,11 @@ impl Resource {
|
|||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resource type
|
||||||
|
pub fn rtype(&self) -> ResourceType {
|
||||||
|
self.rtp
|
||||||
|
}
|
||||||
|
|
||||||
/// Path pattern of the resource
|
/// Path pattern of the resource
|
||||||
pub fn pattern(&self) -> &str {
|
pub fn pattern(&self) -> &str {
|
||||||
&self.pattern
|
&self.pattern
|
||||||
@ -253,7 +268,7 @@ impl Resource {
|
|||||||
I: AsRef<str>,
|
I: AsRef<str>,
|
||||||
{
|
{
|
||||||
let mut iter = elements.into_iter();
|
let mut iter = elements.into_iter();
|
||||||
let mut path = if !self.external {
|
let mut path = if self.rtp != ResourceType::External {
|
||||||
format!("{}/", router.prefix())
|
format!("{}/", router.prefix())
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
|
Loading…
Reference in New Issue
Block a user