mirror of https://github.com/fafhrd91/actix-web
fix url_for parser
This commit is contained in:
parent
ec6d284a8e
commit
d397900e1a
|
@ -33,8 +33,8 @@ pub enum UrlGenerationError {
|
||||||
#[display(fmt = "Resource not found")]
|
#[display(fmt = "Resource not found")]
|
||||||
ResourceNotFound,
|
ResourceNotFound,
|
||||||
|
|
||||||
/// Not all path pattern covered
|
/// Not all url paramters covered
|
||||||
#[display(fmt = "Not all path pattern covered")]
|
#[display(fmt = "Not all url parameters covered")]
|
||||||
NotEnoughElements,
|
NotEnoughElements,
|
||||||
|
|
||||||
/// URL parse error
|
/// URL parse error
|
||||||
|
|
|
@ -114,12 +114,20 @@ impl HttpRequest {
|
||||||
self.uri().query().unwrap_or_default()
|
self.uri().query().unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a reference to the Path parameters.
|
/// Get a reference to url parameters container
|
||||||
///
|
///
|
||||||
/// Params is a container for url parameters.
|
/// A url parameter is specified in the form `{identifier}`,
|
||||||
/// A variable segment is specified in the form `{identifier}`,
|
|
||||||
/// where the identifier can be used later in a request handler to
|
/// where the identifier can be used later in a request handler to
|
||||||
/// access the matched value for that segment.
|
/// access the matched value for that parameter.
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// # Percent Enconding and Url Paramters
|
||||||
|
///
|
||||||
|
/// Because each url paramter is able to capture multiple path segments,
|
||||||
|
/// both `["%2F", "%25"]` found in the request uri are not decoded into `["/", "%"]`
|
||||||
|
/// in order to preserve path segment boundaries.
|
||||||
|
/// If a url paramter is expected to contain these characters, then it is on the user to decode them.
|
||||||
|
///
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn match_info(&self) -> &Path<Url> {
|
pub fn match_info(&self) -> &Path<Url> {
|
||||||
&self.inner.path
|
&self.inner.path
|
||||||
|
@ -163,6 +171,14 @@ impl HttpRequest {
|
||||||
|
|
||||||
/// Generate url for named resource
|
/// Generate url for named resource
|
||||||
///
|
///
|
||||||
|
/// This substitutes in sequence all url parameters that appear in the resource itself
|
||||||
|
/// and in parent [scopes](crate::web::scope), if any.
|
||||||
|
///
|
||||||
|
/// It is worth noting that the characters `['/', '%']` are not escaped and therefore a single
|
||||||
|
/// url parameter may expand into multiple path segments and `elements`
|
||||||
|
/// can be percent-encoded beforehand without worrying about double encoding.
|
||||||
|
/// Any other character that is not valid in url ath context is escaped using percent-encoding.
|
||||||
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// # use actix_web::{web, App, HttpRequest, HttpResponse};
|
/// # use actix_web::{web, App, HttpRequest, HttpResponse};
|
||||||
/// #
|
/// #
|
||||||
|
|
61
src/rmap.rs
61
src/rmap.rs
|
@ -1,3 +1,4 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::{Rc, Weak};
|
use std::rc::{Rc, Weak};
|
||||||
|
|
||||||
|
@ -102,17 +103,23 @@ impl ResourceMap {
|
||||||
})
|
})
|
||||||
.ok_or(UrlGenerationError::NotEnoughElements)?;
|
.ok_or(UrlGenerationError::NotEnoughElements)?;
|
||||||
|
|
||||||
if path.starts_with('/') {
|
let (base, path): (Cow<'_, _>, _) = if path.starts_with('/') {
|
||||||
let conn = req.connection_info();
|
let conn = req.connection_info();
|
||||||
Ok(Url::parse(&format!(
|
let base = format!("{}://{}", conn.scheme(), conn.host(),).into();
|
||||||
"{}://{}{}",
|
(base, path.as_str())
|
||||||
conn.scheme(),
|
|
||||||
conn.host(),
|
|
||||||
path
|
|
||||||
))?)
|
|
||||||
} else {
|
} else {
|
||||||
Ok(Url::parse(&path)?)
|
// external resource
|
||||||
}
|
let third_slash_index = path
|
||||||
|
.char_indices()
|
||||||
|
.filter_map(|(i, c)| (c == '/').then(|| i))
|
||||||
|
.nth(2)
|
||||||
|
.unwrap_or(path.len());
|
||||||
|
(path[..third_slash_index].into(), &path[third_slash_index..])
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut url = Url::parse(&base)?;
|
||||||
|
url.set_path(path);
|
||||||
|
Ok(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_resource(&self, path: &str) -> bool {
|
pub fn has_resource(&self, path: &str) -> bool {
|
||||||
|
@ -406,6 +413,42 @@ mod tests {
|
||||||
assert!(rmap.url_for(&req, "missing", &["u123"]).is_err());
|
assert!(rmap.url_for(&req, "missing", &["u123"]).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn url_for_parser() {
|
||||||
|
let mut root = ResourceMap::new(ResourceDef::prefix(""));
|
||||||
|
|
||||||
|
let mut rdef_1 = ResourceDef::new("/{var}");
|
||||||
|
rdef_1.set_name("internal");
|
||||||
|
|
||||||
|
let mut rdef_2 = ResourceDef::new("http://host.dom/{var}");
|
||||||
|
rdef_2.set_name("external.1");
|
||||||
|
|
||||||
|
let mut rdef_3 = ResourceDef::new("{var}");
|
||||||
|
rdef_3.set_name("external.2");
|
||||||
|
|
||||||
|
root.add(&mut rdef_1, None);
|
||||||
|
root.add(&mut rdef_2, None);
|
||||||
|
root.add(&mut rdef_3, None);
|
||||||
|
let rmap = Rc::new(root);
|
||||||
|
ResourceMap::finish(&rmap);
|
||||||
|
|
||||||
|
let mut req = crate::test::TestRequest::default();
|
||||||
|
req.set_server_hostname("localhost:8888");
|
||||||
|
let req = req.to_http_request();
|
||||||
|
|
||||||
|
const INPUT: &[&str] = &["a/../quick brown%20fox/%nan?query#frag"];
|
||||||
|
const OUTPUT: &str = "/quick%20brown%20fox/%nan%3Fquery%23frag";
|
||||||
|
|
||||||
|
let url = rmap.url_for(&req, "internal", INPUT).unwrap();
|
||||||
|
assert_eq!(url.path(), OUTPUT);
|
||||||
|
|
||||||
|
let url = rmap.url_for(&req, "external.1", INPUT).unwrap();
|
||||||
|
assert_eq!(url.path(), OUTPUT);
|
||||||
|
|
||||||
|
assert!(rmap.url_for(&req, "external.2", INPUT).is_err());
|
||||||
|
assert!(rmap.url_for(&req, "external.2", &[""]).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn external_resource_with_no_name() {
|
fn external_resource_with_no_name() {
|
||||||
let mut root = ResourceMap::new(ResourceDef::prefix(""));
|
let mut root = ResourceMap::new(ResourceDef::prefix(""));
|
||||||
|
|
Loading…
Reference in New Issue