-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
param.rs
384 lines (355 loc) · 12.4 KB
/
param.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
use std::str::FromStr;
use std::path::PathBuf;
use std::fmt::Debug;
use std::borrow::Cow;
use http::uri::{Uri, Segments, SegmentError};
use http::RawStr;
/// Trait to convert a dynamic path segment string to a concrete value.
///
/// This trait is used by Rocket's code generation facilities to parse dynamic
/// path segment string values into a given type. That is, when a path contains
/// a dynamic segment `<param>` where `param` has some type `T` that implements
/// `FromParam`, `T::from_param` will be called.
///
/// # Forwarding
///
/// If the conversion fails, the incoming request will be forwarded to the next
/// matching route, if any. For instance, consider the following route and
/// handler for the dynamic `"/<id>"` path:
///
/// ```rust
/// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)]
/// # extern crate rocket;
/// #[get("/<id>")]
/// fn hello(id: usize) -> String {
/// # let _id = id;
/// # /*
/// ...
/// # */
/// # "".to_string()
/// }
/// # fn main() { }
/// ```
///
/// If `usize::from_param` returns an `Ok(usize)` variant, the encapsulated
/// value is used as the `id` function parameter. If not, the request is
/// forwarded to the next matching route. Since there are no additional matching
/// routes, this example will result in a 404 error for requests with invalid
/// `id` values.
///
/// # Catching Errors
///
/// Sometimes, a forward is not desired, and instead, we simply want to know
/// that the dynamic path segment could not be parsed into some desired type
/// `T`. In these cases, types of `Option<T>` or `Result<T, T::Error>` can be
/// used. These types implement `FromParam` themselves. Their implementations
/// always return successfully, so they never forward. They can be used to
/// determine if the `FromParam` call failed and to retrieve the error value
/// from the failed `from_param` call.
///
/// For instance, imagine you've asked for an `<id>` as a `usize`. To determine
/// when the `<id>` was not a valid `usize` and retrieve the string that failed
/// to parse, you can use a `Result<usize, &RawStr>` type for the `<id>`
/// parameter as follows:
///
/// ```rust
/// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)]
/// # extern crate rocket;
/// # use rocket::http::RawStr;
/// #[get("/<id>")]
/// fn hello(id: Result<usize, &RawStr>) -> String {
/// match id {
/// Ok(id_num) => format!("usize: {}", id_num),
/// Err(string) => format!("Not a usize: {}", string)
/// }
/// }
/// # fn main() { }
/// ```
///
/// # Provided Implementations
///
/// Rocket implements `FromParam` for several standard library types. Their
/// behavior is documented here.
///
/// * **f32, f64, isize, i8, i16, i32, i64, usize, u8, u16, u32, u64, bool,
/// IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr**
///
/// A value is parsed successfully if the `from_str` method from the given
/// type returns successfully. Otherwise, the raw path segment is returned
/// in the `Err` value.
///
/// * **&[`RawStr`](/rocket/http/struct.RawStr.html)**
///
/// _This implementation always returns successfully._
///
/// The path segment is passed directly with no modification.
///
/// * **String**
///
/// Percent decodes the path segment. If the decode is successful, the
/// decoded string is returned. Otherwise, an `Err` with the original path
/// segment is returned.
///
/// * **Cow<str>**
///
/// Percent decodes the path segment, allocating only when necessary. If the
/// decode is successful, the decoded string is returned. Otherwise, an
/// `Err` with the original path segment is returned.
///
/// * **Option<T>** _where_ **T: FromParam**
///
/// _This implementation always returns successfully._
///
/// The path segment is parsed by `T`'s `FromParam` implementation. If the
/// parse succeeds, a `Some(parsed_value)` is returned. Otherwise, a `None`
/// is returned.
///
/// * **Result<T, T::Error>** _where_ **T: FromParam**
///
/// _This implementation always returns successfully._
///
/// The path segment is parsed by `T`'s `FromParam` implementation. The
/// returned `Result` value is returned.
///
/// # Example
///
/// Say you want to parse a segment of the form:
///
/// ```ignore
/// [a-zA-Z]+:[0-9]+
/// ```
///
/// into the following structure, where the string before the `:` is stored in
/// `key` and the number after the colon is stored in `value`:
///
/// ```rust
/// # #[allow(dead_code)]
/// struct MyParam<'r> {
/// key: &'r str,
/// value: usize
/// }
/// ```
///
/// The following implementation accomplishes this:
///
/// ```rust
/// use rocket::request::FromParam;
/// use rocket::http::RawStr;
/// # #[allow(dead_code)]
/// # struct MyParam<'r> { key: &'r str, value: usize }
///
/// impl<'r> FromParam<'r> for MyParam<'r> {
/// type Error = &'r RawStr;
///
/// fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
/// let (key, val_str) = match param.find(':') {
/// Some(i) if i > 0 => (¶m[..i], ¶m[(i + 1)..]),
/// _ => return Err(param)
/// };
///
/// if !key.chars().all(|c| (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
/// return Err(param);
/// }
///
/// val_str.parse().map(|value| {
/// MyParam {
/// key: key,
/// value: value
/// }
/// }).map_err(|_| param)
/// }
/// }
/// ```
///
/// With the implementation, the `MyParam` type can be used as the target of a
/// dynamic path segment:
///
/// ```rust
/// # #![feature(plugin, decl_macro)]
/// # #![plugin(rocket_codegen)]
/// # extern crate rocket;
/// # use rocket::request::FromParam;
/// # use rocket::http::RawStr;
/// # #[allow(dead_code)]
/// # struct MyParam<'r> { key: &'r str, value: usize }
/// # impl<'r> FromParam<'r> for MyParam<'r> {
/// # type Error = &'r RawStr;
/// # fn from_param(param: &'r RawStr) -> Result<Self, Self::Error> {
/// # Err(param)
/// # }
/// # }
/// #
/// #[get("/<key_val>")]
/// fn hello(key_val: MyParam) -> String {
/// # let _kv = key_val;
/// # /*
/// ...
/// # */
/// # "".to_string()
/// }
/// # fn main() { }
/// ```
pub trait FromParam<'a>: Sized {
/// The associated error to be returned when parsing fails.
type Error: Debug;
/// Parses an instance of `Self` from a dynamic path parameter string or
/// returns an `Error` if one cannot be parsed.
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error>;
}
impl<'a> FromParam<'a> for &'a RawStr {
type Error = ();
#[inline(always)]
fn from_param(param: &'a RawStr) -> Result<&'a RawStr, Self::Error> {
Ok(param)
}
}
impl<'a> FromParam<'a> for String {
type Error = &'a RawStr;
#[inline(always)]
fn from_param(param: &'a RawStr) -> Result<String, Self::Error> {
param.percent_decode().map(|cow| cow.into_owned()).map_err(|_| param)
}
}
impl<'a> FromParam<'a> for Cow<'a, str> {
type Error = &'a RawStr;
#[inline(always)]
fn from_param(param: &'a RawStr) -> Result<Cow<'a, str>, Self::Error> {
param.percent_decode().map_err(|_| param)
}
}
macro_rules! impl_with_fromstr {
($($T:ty),+) => ($(
impl<'a> FromParam<'a> for $T {
type Error = &'a RawStr;
#[inline(always)]
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> {
<$T as FromStr>::from_str(param.as_str()).map_err(|_| param)
}
}
)+)
}
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
impl_with_fromstr! {
i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64, bool,
IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr
}
impl<'a, T: FromParam<'a>> FromParam<'a> for Result<T, T::Error> {
type Error = !;
#[inline]
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> {
match T::from_param(param) {
Ok(val) => Ok(Ok(val)),
Err(e) => Ok(Err(e)),
}
}
}
impl<'a, T: FromParam<'a>> FromParam<'a> for Option<T> {
type Error = !;
#[inline]
fn from_param(param: &'a RawStr) -> Result<Self, Self::Error> {
match T::from_param(param) {
Ok(val) => Ok(Some(val)),
Err(_) => Ok(None)
}
}
}
/// Trait to convert _many_ dynamic path segment strings to a concrete value.
///
/// This is the `..` analog to [FromParam](trait.FromParam.html), and its
/// functionality is identical to it with one exception: this trait applies to
/// segment parameters of the form `<param..>`, where `param` is of some type
/// `T` that implements `FromSegments`. `T::from_segments` is called to convert
/// the matched segments (via the
/// [Segments](/rocket/http/uri/struct.Segments.html) iterator) into the
/// implementing type.
///
/// # Provided Implementations
///
/// Rocket implements `FromParam` for `PathBuf`. The `PathBuf` implementation
/// constructs a path from the segments iterator. Each segment is
/// percent-decoded. If a segment equals ".." before or after decoding, the
/// previous segment (if any) is omitted. For security purposes, any other
/// segments that begin with "*" or "." are ignored. If a percent-decoded
/// segment results in invalid UTF8, an `Err` is returned with the `Utf8Error`.
pub trait FromSegments<'a>: Sized {
/// The associated error to be returned when parsing fails.
type Error: Debug;
/// Parses an instance of `Self` from many dynamic path parameter strings or
/// returns an `Error` if one cannot be parsed.
fn from_segments(segments: Segments<'a>) -> Result<Self, Self::Error>;
}
impl<'a> FromSegments<'a> for Segments<'a> {
type Error = !;
#[inline(always)]
fn from_segments(segments: Segments<'a>) -> Result<Segments<'a>, Self::Error> {
Ok(segments)
}
}
/// Creates a `PathBuf` from a `Segments` iterator. The returned `PathBuf` is
/// percent-decoded. If a segment is equal to "..", the previous segment (if
/// any) is skipped.
///
/// For security purposes, if a segment meets any of the following conditions,
/// an `Err` is returned indicating the condition met:
///
/// * Decoded segment starts with any of: `.` (except `..`), `*`
/// * Decoded segment ends with any of: `:`, `>`, `<`
/// * Decoded segment contains any of: `/`
/// * On Windows, decoded segment contains any of: '\'
/// * Percent-encoding results in invalid UTF8.
///
/// As a result of these conditions, a `PathBuf` derived via `FromSegments` is
/// safe to interpolate within, or use as a suffix of, a path without additional
/// checks.
impl<'a> FromSegments<'a> for PathBuf {
type Error = SegmentError;
fn from_segments(segments: Segments<'a>) -> Result<PathBuf, SegmentError> {
let mut buf = PathBuf::new();
for segment in segments {
let decoded = Uri::percent_decode(segment.as_bytes())
.map_err(|e| SegmentError::Utf8(e))?;
if decoded == ".." {
buf.pop();
} else if decoded.starts_with('.') {
return Err(SegmentError::BadStart('.'))
} else if decoded.starts_with('*') {
return Err(SegmentError::BadStart('*'))
} else if decoded.ends_with(':') {
return Err(SegmentError::BadEnd(':'))
} else if decoded.ends_with('>') {
return Err(SegmentError::BadEnd('>'))
} else if decoded.ends_with('<') {
return Err(SegmentError::BadEnd('<'))
} else if decoded.contains('/') {
return Err(SegmentError::BadChar('/'))
} else if cfg!(windows) && decoded.contains('\\') {
return Err(SegmentError::BadChar('\\'))
} else {
buf.push(&*decoded)
}
}
Ok(buf)
}
}
impl<'a, T: FromSegments<'a>> FromSegments<'a> for Result<T, T::Error> {
type Error = !;
#[inline]
fn from_segments(segments: Segments<'a>) -> Result<Result<T, T::Error>, !> {
match T::from_segments(segments) {
Ok(val) => Ok(Ok(val)),
Err(e) => Ok(Err(e)),
}
}
}
impl<'a, T: FromSegments<'a>> FromSegments<'a> for Option<T> {
type Error = !;
#[inline]
fn from_segments(segments: Segments<'a>) -> Result<Option<T>, !> {
match T::from_segments(segments) {
Ok(val) => Ok(Some(val)),
Err(_) => Ok(None)
}
}
}