mirror of https://github.com/rwf2/Rocket.git
Compile-time enforce paths as absolute, non-empty, valid segments.
This commit is contained in:
parent
20a548b11b
commit
8eef42a256
|
@ -3,6 +3,7 @@ mod route;
|
||||||
mod error;
|
mod error;
|
||||||
mod param;
|
mod param;
|
||||||
mod function;
|
mod function;
|
||||||
|
mod uri;
|
||||||
|
|
||||||
pub use self::keyvalue::KVSpanned;
|
pub use self::keyvalue::KVSpanned;
|
||||||
pub use self::route::RouteParams;
|
pub use self::route::RouteParams;
|
||||||
|
|
|
@ -2,24 +2,24 @@ use syntax::ast::Ident;
|
||||||
use syntax::ext::base::ExtCtxt;
|
use syntax::ext::base::ExtCtxt;
|
||||||
use syntax::codemap::{Span, Spanned, BytePos};
|
use syntax::codemap::{Span, Spanned, BytePos};
|
||||||
|
|
||||||
use utils::{span, SpanExt, is_valid_ident};
|
use utils::span;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Param {
|
pub enum Param {
|
||||||
Single(Spanned<Ident>),
|
Single(Spanned<Ident>),
|
||||||
Many(Spanned<Ident>)
|
Many(Spanned<Ident>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Param {
|
impl Param {
|
||||||
pub fn inner(&self) -> &Spanned<Ident> {
|
pub fn inner(&self) -> &Spanned<Ident> {
|
||||||
match *self {
|
match *self {
|
||||||
Param::Single(ref ident) | Param::Many(ref ident) => ident
|
Param::Single(ref ident) | Param::Many(ref ident) => ident,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ident(&self) -> &Ident {
|
pub fn ident(&self) -> &Ident {
|
||||||
match *self {
|
match *self {
|
||||||
Param::Single(ref ident) | Param::Many(ref ident) => &ident.node
|
Param::Single(ref ident) | Param::Many(ref ident) => &ident.node,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,7 @@ pub struct ParamIter<'s, 'a, 'c: 'a> {
|
||||||
|
|
||||||
impl<'s, 'a, 'c: 'a> ParamIter<'s, 'a, 'c> {
|
impl<'s, 'a, 'c: 'a> ParamIter<'s, 'a, 'c> {
|
||||||
pub fn new(c: &'a ExtCtxt<'c>, s: &'s str, p: Span) -> ParamIter<'s, 'a, 'c> {
|
pub fn new(c: &'a ExtCtxt<'c>, s: &'s str, p: Span) -> ParamIter<'s, 'a, 'c> {
|
||||||
ParamIter {
|
ParamIter { ctxt: c, span: p, string: s }
|
||||||
ctxt: c,
|
|
||||||
span: p,
|
|
||||||
string: s,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +41,7 @@ impl<'s, 'a, 'c> Iterator for ParamIter<'s, 'a, 'c> {
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Param> {
|
fn next(&mut self) -> Option<Param> {
|
||||||
let err = |ecx: &ExtCtxt, sp: Span, msg: &str| {
|
let err = |ecx: &ExtCtxt, sp: Span, msg: &str| {
|
||||||
ecx.span_err(sp, msg);
|
ecx.span_err(sp, msg);
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,14 +49,14 @@ impl<'s, 'a, 'c> Iterator for ParamIter<'s, 'a, 'c> {
|
||||||
let (start, end) = match self.string.find('<') {
|
let (start, end) = match self.string.find('<') {
|
||||||
Some(i) => match self.string.find('>') {
|
Some(i) => match self.string.find('>') {
|
||||||
Some(j) => (i, j),
|
Some(j) => (i, j),
|
||||||
None => return err(self.ctxt, self.span, "malformed parameter list")
|
None => return err(self.ctxt, self.span, "malformed parameters")
|
||||||
},
|
},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure we found a valid parameter.
|
// Ensure we found a valid parameter.
|
||||||
if end <= start {
|
if end <= start {
|
||||||
return err(self.ctxt, self.span, "malformed parameter list");
|
return err(self.ctxt, self.span, "malformed parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the parameter's ident.
|
// Calculate the parameter's ident.
|
||||||
|
@ -79,27 +75,11 @@ impl<'s, 'a, 'c> Iterator for ParamIter<'s, 'a, 'c> {
|
||||||
self.string = &self.string[(end + 1)..];
|
self.string = &self.string[(end + 1)..];
|
||||||
self.span.lo = self.span.lo + BytePos((end + 1) as u32);
|
self.span.lo = self.span.lo + BytePos((end + 1) as u32);
|
||||||
|
|
||||||
// Check for nonemptiness, that the characters are correct, and return.
|
let spanned_ident = span(Ident::from_str(param), param_span);
|
||||||
if param.is_empty() {
|
if is_many {
|
||||||
err(self.ctxt, param_span, "parameter names cannot be empty")
|
Some(Param::Many(spanned_ident))
|
||||||
} else if !is_valid_ident(param) {
|
|
||||||
err(self.ctxt, param_span, "parameter names must be valid identifiers")
|
|
||||||
} else if param.starts_with('_') {
|
|
||||||
err(self.ctxt, param_span, "parameters cannot be ignored")
|
|
||||||
} else if is_many && !self.string.is_empty() {
|
|
||||||
let sp = self.span.shorten_to(self.string.len());
|
|
||||||
self.ctxt.struct_span_err(sp, "text after a trailing '..' param")
|
|
||||||
.span_note(param_span, "trailing param is here")
|
|
||||||
.emit();
|
|
||||||
None
|
|
||||||
} else {
|
} else {
|
||||||
let spanned_ident = span(Ident::from_str(param), param_span);
|
Some(Param::Single(spanned_ident))
|
||||||
if is_many {
|
|
||||||
Some(Param::Many(spanned_ident))
|
|
||||||
} else {
|
|
||||||
Some(Param::Single(spanned_ident))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use syntax::codemap::{Span, Spanned, dummy_spanned};
|
||||||
use utils::{span, MetaItemExt, SpanExt, is_valid_ident};
|
use utils::{span, MetaItemExt, SpanExt, is_valid_ident};
|
||||||
use super::{Function, ParamIter};
|
use super::{Function, ParamIter};
|
||||||
use super::keyvalue::KVSpanned;
|
use super::keyvalue::KVSpanned;
|
||||||
|
use super::uri::validate_uri;
|
||||||
use rocket::http::{Method, ContentType};
|
use rocket::http::{Method, ContentType};
|
||||||
use rocket::http::uri::URI;
|
use rocket::http::uri::URI;
|
||||||
|
|
||||||
|
@ -151,7 +152,7 @@ pub fn kv_from_nested(item: &NestedMetaItem) -> Option<KVSpanned<LitKind>> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn param_string_to_ident(ecx: &ExtCtxt, s: Spanned<&str>) -> Option<Spanned<Ident>> {
|
pub fn param_to_ident(ecx: &ExtCtxt, s: Spanned<&str>) -> Option<Spanned<Ident>> {
|
||||||
let string = s.node;
|
let string = s.node;
|
||||||
if string.starts_with('<') && string.ends_with('>') {
|
if string.starts_with('<') && string.ends_with('>') {
|
||||||
let param = &string[1..(string.len() - 1)];
|
let param = &string[1..(string.len() - 1)];
|
||||||
|
@ -187,27 +188,20 @@ fn parse_method(ecx: &ExtCtxt, meta_item: &NestedMetaItem) -> Spanned<Method> {
|
||||||
dummy_spanned(Method::Get)
|
dummy_spanned(Method::Get)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_path(ecx: &ExtCtxt, meta_item: &NestedMetaItem)
|
fn parse_path(ecx: &ExtCtxt,
|
||||||
-> (Spanned<URI<'static>>, Option<Spanned<Ident>>) {
|
meta_item: &NestedMetaItem)
|
||||||
let from_string = |string: &str, sp: Span| {
|
-> (Spanned<URI<'static>>, Option<Spanned<Ident>>) {
|
||||||
let query_param = string.find('?')
|
|
||||||
.map(|i| span(&string[(i + 1)..], sp.trim_left(i + 1)))
|
|
||||||
.and_then(|spanned_q_param| param_string_to_ident(ecx, spanned_q_param));
|
|
||||||
|
|
||||||
(span(URI::from(string.to_string()), sp), query_param)
|
|
||||||
};
|
|
||||||
|
|
||||||
let sp = meta_item.span();
|
let sp = meta_item.span();
|
||||||
if let Some((name, lit)) = meta_item.name_value() {
|
if let Some((name, lit)) = meta_item.name_value() {
|
||||||
if name != &"path" {
|
if name != &"path" {
|
||||||
ecx.span_err(sp, "the first key, if any, must be 'path'");
|
ecx.span_err(sp, "the first key, if any, must be 'path'");
|
||||||
} else if let LitKind::Str(ref s, _) = lit.node {
|
} else if let LitKind::Str(ref s, _) = lit.node {
|
||||||
return from_string(&s.as_str(), lit.span);
|
return validate_uri(ecx, &s.as_str(), lit.span);
|
||||||
} else {
|
} else {
|
||||||
ecx.span_err(lit.span, "`path` value must be a string")
|
ecx.span_err(lit.span, "`path` value must be a string")
|
||||||
}
|
}
|
||||||
} else if let Some(s) = meta_item.str_lit() {
|
} else if let Some(s) = meta_item.str_lit() {
|
||||||
return from_string(&s.as_str(), sp);
|
return validate_uri(ecx, &s.as_str(), sp);
|
||||||
} else {
|
} else {
|
||||||
ecx.struct_span_err(sp, r#"expected `path = string` or a path string"#)
|
ecx.struct_span_err(sp, r#"expected `path = string` or a path string"#)
|
||||||
.help(r#"you can specify the path directly as a string, \
|
.help(r#"you can specify the path directly as a string, \
|
||||||
|
@ -229,7 +223,7 @@ fn parse_data(ecx: &ExtCtxt, kv: &KVSpanned<LitKind>) -> Ident {
|
||||||
let mut ident = Ident::from_str("unknown");
|
let mut ident = Ident::from_str("unknown");
|
||||||
if let LitKind::Str(ref s, _) = *kv.value() {
|
if let LitKind::Str(ref s, _) = *kv.value() {
|
||||||
ident = Ident::from_str(&s.as_str());
|
ident = Ident::from_str(&s.as_str());
|
||||||
if let Some(id) = param_string_to_ident(ecx, span(&s.as_str(), kv.value.span)) {
|
if let Some(id) = param_to_ident(ecx, span(&s.as_str(), kv.value.span)) {
|
||||||
return id.node;
|
return id.node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,9 @@ pub trait SpanExt {
|
||||||
|
|
||||||
/// Trim the span on the left by `length`.
|
/// Trim the span on the left by `length`.
|
||||||
fn trim_left(self, length: usize) -> Span;
|
fn trim_left(self, length: usize) -> Span;
|
||||||
|
|
||||||
|
/// Trim the span on the right by `length`.
|
||||||
|
fn trim_right(self, length: usize) -> Span;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpanExt for Span {
|
impl SpanExt for Span {
|
||||||
|
@ -16,6 +19,11 @@ impl SpanExt for Span {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn trim_right(mut self, length: usize) -> Span {
|
||||||
|
self.hi = self.hi - BytePos(length as u32);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn shorten_to(mut self, to_length: usize) -> Span {
|
fn shorten_to(mut self, to_length: usize) -> Span {
|
||||||
self.hi = self.lo + BytePos(to_length as u32);
|
self.hi = self.lo + BytePos(to_length as u32);
|
||||||
self
|
self
|
||||||
|
|
|
@ -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() { }
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
#[get(path = "hello", 123)] //~ ERROR expected
|
#[get(path = "/hello", 123)] //~ ERROR expected
|
||||||
fn get() -> &'static str { "hi" }
|
fn get() -> &'static str { "hi" }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -9,10 +9,10 @@ fn get0() -> &'static str { "hi" }
|
||||||
#[get(path = 1)] //~ ERROR must be a string
|
#[get(path = 1)] //~ ERROR must be a string
|
||||||
fn get1() -> &'static str { "hi" }
|
fn get1() -> &'static str { "hi" }
|
||||||
|
|
||||||
#[get(path = "h", rank = "2")] //~ ERROR must be an int
|
#[get(path = "/", rank = "2")] //~ ERROR must be an int
|
||||||
fn get2() -> &'static str { "hi" }
|
fn get2() -> &'static str { "hi" }
|
||||||
|
|
||||||
#[get(path = "h", format = 100)] //~ ERROR must be a "content/type"
|
#[get(path = "/", format = 100)] //~ ERROR must be a "content/type"
|
||||||
fn get3() -> &'static str { "hi" }
|
fn get3() -> &'static str { "hi" }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -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() { }
|
|
@ -7,7 +7,7 @@ fn get() -> &'static str { "hi" }
|
||||||
#[get("/<name><")] //~ ERROR malformed
|
#[get("/<name><")] //~ ERROR malformed
|
||||||
fn get1(name: &str) -> &'static str { "hi" }
|
fn get1(name: &str) -> &'static str { "hi" }
|
||||||
|
|
||||||
#[get("/<<<<name><")] //~ ERROR identifiers
|
#[get("/<<<<name><")] //~ ERROR malformed
|
||||||
fn get2(name: &str) -> &'static str { "hi" }
|
fn get2(name: &str) -> &'static str { "hi" }
|
||||||
|
|
||||||
#[get("/<!>")] //~ ERROR identifiers
|
#[get("/<!>")] //~ ERROR identifiers
|
||||||
|
@ -19,7 +19,13 @@ fn get4() -> &'static str { "hi" }
|
||||||
#[get("/<1>")] //~ ERROR identifiers
|
#[get("/<1>")] //~ ERROR identifiers
|
||||||
fn get5() -> &'static str { "hi" }
|
fn get5() -> &'static str { "hi" }
|
||||||
|
|
||||||
#[get("/<>name><")] //~ ERROR cannot be empty
|
#[get("/<>name><")] //~ ERROR malformed
|
||||||
fn get6() -> &'static str { "hi" }
|
fn get6() -> &'static str { "hi" }
|
||||||
|
|
||||||
|
#[get("/<name>:<id>")] //~ ERROR identifiers
|
||||||
|
fn get7() -> &'static str { "hi" }
|
||||||
|
|
||||||
|
#[get("/<>")] //~ ERROR empty
|
||||||
|
fn get8() -> &'static str { "hi" }
|
||||||
|
|
||||||
fn main() { }
|
fn main() { }
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
#[route(FIX, "hello")] //~ ERROR is not a valid HTTP method
|
#[route(FIX, "/hello")] //~ ERROR is not a valid HTTP method
|
||||||
//~^ ERROR valid HTTP method
|
//~^ ERROR valid HTTP method
|
||||||
fn get() -> &'static str { "hi" }
|
fn get() -> &'static str { "hi" }
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
#[route(CONNECT, "hello")] //~ ERROR valid HTTP method
|
#[route(CONNECT, "/hello")] //~ ERROR valid HTTP method
|
||||||
fn get() -> &'static str { "hi" }
|
fn get() -> &'static str { "hi" }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
#[get(path = "hello", unknown = 123)] //~ ERROR 'unknown' is not a known param
|
#[get(path = "/hello", unknown = 123)] //~ ERROR 'unknown' is not a known param
|
||||||
fn get() -> &'static str { "hi" }
|
fn get() -> &'static str { "hi" }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
#[get("")]
|
#[get("/")]
|
||||||
fn get() -> &'static str { "hi" }
|
fn get() -> &'static str { "hi" }
|
||||||
|
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
|
|
Loading…
Reference in New Issue