forked from rwf2/Rocket
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Compile-time enforce paths as absolute, non-empty, valid segments.
- Loading branch information
1 parent
20a548b
commit 8eef42a
Showing
14 changed files
with
199 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
use syntax::ast::*; | ||
use syntax::codemap::{Span, Spanned, dummy_spanned}; | ||
use syntax::ext::base::ExtCtxt; | ||
|
||
use rocket::http::uri::URI; | ||
use super::route::param_to_ident; | ||
use utils::{span, SpanExt, is_valid_ident}; | ||
|
||
// We somewhat arbitrarily enforce absolute paths. This is mostly because we | ||
// want the initial "/" to represent the mount point. Empty segments are | ||
// stripped out at runtime. So, to avoid any confusion, we issue an error at | ||
// compile-time for empty segments. At the moment, this disallows trailing | ||
// slashes as well, since then the last segment is empty. | ||
fn valid_path(ecx: &ExtCtxt, uri: &URI, sp: Span) -> bool { | ||
let cleaned = uri.to_string(); | ||
if !uri.as_str().starts_with('/') { | ||
ecx.struct_span_err(sp, "route paths must be absolute") | ||
.note(&format!("expected {:?}, found {:?}", cleaned, uri.as_str())) | ||
.emit() | ||
} else if cleaned != uri.as_str() { | ||
ecx.struct_span_err(sp, "paths cannot contain empty segments") | ||
.note(&format!("expected {:?}, found {:?}", cleaned, uri.as_str())) | ||
.emit() | ||
} else { | ||
return true; | ||
} | ||
|
||
false | ||
} | ||
|
||
fn valid_segments(ecx: &ExtCtxt, uri: &URI, sp: Span) -> bool { | ||
let mut validated = true; | ||
let mut segments_span = None; | ||
for segment in uri.segments() { | ||
// We add one to the index to account for the '/'. | ||
let index = segment.as_ptr() as usize - uri.path().as_ptr() as usize; | ||
let span = sp.trim_left(index + 1).shorten_to(segment.len()); | ||
|
||
// If we're iterating after a '..' param, that's a hard error. | ||
if let Some(span) = segments_span { | ||
let rem_sp = sp.trim_left(index).trim_right(1); | ||
ecx.struct_span_err(rem_sp, "text after a trailing '..' param") | ||
.help("a segments param must be the final text in a path") | ||
.span_note(span, "trailing param is here") | ||
.emit(); | ||
return false; | ||
} | ||
|
||
// Check if this is a dynamic param. If so, check it's well-formedness. | ||
if segment.starts_with("<") && segment.ends_with(">") { | ||
let mut param = &segment[1..(segment.len() - 1)]; | ||
if segment.ends_with("..>") { | ||
segments_span = Some(span); | ||
param = ¶m[..(param.len() - 2)]; | ||
} | ||
|
||
if param.is_empty() { | ||
ecx.span_err(span, "parameters cannot be empty"); | ||
} else if !is_valid_ident(param) { | ||
ecx.struct_span_err(span, "parameter names must be valid identifiers") | ||
.note(&format!("{:?} is not a valid identifier", param)) | ||
.emit(); | ||
} else if param.starts_with('_') { | ||
ecx.struct_span_err(span, "parameters cannot be ignored") | ||
.note(&format!("{:?} is being ignored", param)) | ||
.emit(); | ||
} else { | ||
continue | ||
} | ||
|
||
validated = false; | ||
} else if segment.starts_with("<") { | ||
if segment[1..].contains("<") || segment.contains(">") { | ||
ecx.struct_span_err(span, "malformed parameter") | ||
.help("parameters must be of the form '<param>'") | ||
.emit(); | ||
} else { | ||
ecx.struct_span_err(span, "parameter is missing a closing bracket") | ||
.help(&format!("perhaps you meant '{}>'?", segment)) | ||
.emit(); | ||
} | ||
|
||
validated = false; | ||
} else if URI::percent_encode(segment) != segment { | ||
if segment.contains("<") || segment.contains(">") { | ||
ecx.struct_span_err(span, "malformed parameter") | ||
.help("parameters must be of the form '<param>'") | ||
.emit(); | ||
} else { | ||
ecx.span_err(span, "segment contains invalid characters"); | ||
} | ||
|
||
validated = false; | ||
} | ||
} | ||
|
||
validated | ||
} | ||
|
||
pub fn validate_uri(ecx: &ExtCtxt, | ||
string: &str, | ||
sp: Span) | ||
-> (Spanned<URI<'static>>, Option<Spanned<Ident>>) { | ||
let uri = URI::from(string.to_string()); | ||
let query_param = string.find('?') | ||
.map(|i| span(&string[(i + 1)..], sp.trim_left(i + 1))) | ||
.and_then(|spanned_q_param| param_to_ident(ecx, spanned_q_param)); | ||
|
||
if valid_segments(ecx, &uri, sp) && valid_path(ecx, &uri, sp) { | ||
(span(uri, sp), query_param) | ||
} else { | ||
(dummy_spanned(URI::new("")), query_param) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#![feature(plugin)] | ||
#![plugin(rocket_codegen)] | ||
|
||
#[get("a")] //~ ERROR absolute | ||
fn get() -> &'static str { "hi" } | ||
|
||
#[get("")] //~ ERROR absolute | ||
fn get1(name: &str) -> &'static str { "hi" } | ||
|
||
#[get("a/b/c")] //~ ERROR absolute | ||
fn get2(name: &str) -> &'static str { "hi" } | ||
|
||
fn main() { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
#![feature(plugin)] | ||
#![plugin(rocket_codegen)] | ||
|
||
#[get("/a/b/c//d")] //~ ERROR paths cannot contain empty segments | ||
fn get() -> &'static str { "hi" } | ||
|
||
#[get("//")] //~ ERROR paths cannot contain empty segments | ||
fn get1(name: &str) -> &'static str { "hi" } | ||
|
||
#[get("/a/")] //~ ERROR paths cannot contain empty segments | ||
fn get2(name: &str) -> &'static str { "hi" } | ||
|
||
#[get("////")] //~ ERROR paths cannot contain empty segments | ||
fn get3() -> &'static str { "hi" } | ||
|
||
#[get("/a///")] //~ ERROR paths cannot contain empty segments | ||
fn get4() -> &'static str { "hi" } | ||
|
||
#[get("/a/b//")] //~ ERROR paths cannot contain empty segments | ||
fn get5() -> &'static str { "hi" } | ||
|
||
#[get("/a/b/c/")] //~ ERROR paths cannot contain empty segments | ||
fn get6() -> &'static str { "hi" } | ||
|
||
#[get("/a/b/c/d//e/")] //~ ERROR paths cannot contain empty segments | ||
fn get7() -> &'static str { "hi" } | ||
|
||
fn main() { } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.