1 //! The HTTP request method
2 //!
3 //! This module contains HTTP-method related structs and errors and such. The
4 //! main type of this module, `Method`, is also reexported at the root of the
5 //! crate as `http::Method` and is intended for import through that location
6 //! primarily.
7 //!
8 //! # Examples
9 //!
10 //! ```
11 //! use http::Method;
12 //!
13 //! assert_eq!(Method::GET, Method::from_bytes(b"GET").unwrap());
14 //! assert!(Method::GET.is_idempotent());
15 //! assert_eq!(Method::POST.as_str(), "POST");
16 //! ```
17 
18 use self::Inner::*;
19 use self::extension::{InlineExtension, AllocatedExtension};
20 
21 use std::convert::AsRef;
22 use std::error::Error;
23 use std::str::FromStr;
24 use std::convert::TryFrom;
25 use std::{fmt, str};
26 
27 /// The Request Method (VERB)
28 ///
29 /// This type also contains constants for a number of common HTTP methods such
30 /// as GET, POST, etc.
31 ///
32 /// Currently includes 8 variants representing the 8 methods defined in
33 /// [RFC 7230](https://tools.ietf.org/html/rfc7231#section-4.1), plus PATCH,
34 /// and an Extension variant for all extensions.
35 ///
36 /// # Examples
37 ///
38 /// ```
39 /// use http::Method;
40 ///
41 /// assert_eq!(Method::GET, Method::from_bytes(b"GET").unwrap());
42 /// assert!(Method::GET.is_idempotent());
43 /// assert_eq!(Method::POST.as_str(), "POST");
44 /// ```
45 #[derive(Clone, PartialEq, Eq, Hash)]
46 pub struct Method(Inner);
47 
48 /// A possible error value when converting `Method` from bytes.
49 pub struct InvalidMethod {
50     _priv: (),
51 }
52 
53 #[derive(Clone, PartialEq, Eq, Hash)]
54 enum Inner {
55     Options,
56     Get,
57     Post,
58     Put,
59     Delete,
60     Head,
61     Trace,
62     Connect,
63     Patch,
64     // If the extension is short enough, store it inline
65     ExtensionInline(InlineExtension),
66     // Otherwise, allocate it
67     ExtensionAllocated(AllocatedExtension),
68 }
69 
70 
71 impl Method {
72     /// GET
73     pub const GET: Method = Method(Get);
74 
75     /// POST
76     pub const POST: Method = Method(Post);
77 
78     /// PUT
79     pub const PUT: Method = Method(Put);
80 
81     /// DELETE
82     pub const DELETE: Method = Method(Delete);
83 
84     /// HEAD
85     pub const HEAD: Method = Method(Head);
86 
87     /// OPTIONS
88     pub const OPTIONS: Method = Method(Options);
89 
90     /// CONNECT
91     pub const CONNECT: Method = Method(Connect);
92 
93     /// PATCH
94     pub const PATCH: Method = Method(Patch);
95 
96     /// TRACE
97     pub const TRACE: Method = Method(Trace);
98 
99     /// Converts a slice of bytes to an HTTP method.
from_bytes(src: &[u8]) -> Result<Method, InvalidMethod>100     pub fn from_bytes(src: &[u8]) -> Result<Method, InvalidMethod> {
101         match src.len() {
102             0 => Err(InvalidMethod::new()),
103             3 => match src {
104                 b"GET" => Ok(Method(Get)),
105                 b"PUT" => Ok(Method(Put)),
106                 _ => Method::extension_inline(src),
107             },
108             4 => match src {
109                 b"POST" => Ok(Method(Post)),
110                 b"HEAD" => Ok(Method(Head)),
111                 _ => Method::extension_inline(src),
112             },
113             5 => match src {
114                 b"PATCH" => Ok(Method(Patch)),
115                 b"TRACE" => Ok(Method(Trace)),
116                 _ => Method::extension_inline(src),
117             },
118             6 => match src {
119                 b"DELETE" => Ok(Method(Delete)),
120                 _ => Method::extension_inline(src),
121             },
122             7 => match src {
123                 b"OPTIONS" => Ok(Method(Options)),
124                 b"CONNECT" => Ok(Method(Connect)),
125                 _ => Method::extension_inline(src),
126             },
127             _ => {
128                 if src.len() < InlineExtension::MAX {
129                     Method::extension_inline(src)
130                 } else {
131                     let allocated = AllocatedExtension::new(src)?;
132 
133                     Ok(Method(ExtensionAllocated(allocated)))
134                 }
135             }
136         }
137     }
138 
extension_inline(src: &[u8]) -> Result<Method, InvalidMethod>139     fn extension_inline(src: &[u8]) -> Result<Method, InvalidMethod> {
140         let inline = InlineExtension::new(src)?;
141 
142         Ok(Method(ExtensionInline(inline)))
143     }
144 
145     /// Whether a method is considered "safe", meaning the request is
146     /// essentially read-only.
147     ///
148     /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1)
149     /// for more words.
is_safe(&self) -> bool150     pub fn is_safe(&self) -> bool {
151         match self.0 {
152             Get | Head | Options | Trace => true,
153             _ => false,
154         }
155     }
156 
157     /// Whether a method is considered "idempotent", meaning the request has
158     /// the same result if executed multiple times.
159     ///
160     /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.2) for
161     /// more words.
is_idempotent(&self) -> bool162     pub fn is_idempotent(&self) -> bool {
163         match self.0 {
164             Put | Delete => true,
165             _ => self.is_safe(),
166         }
167     }
168 
169     /// Return a &str representation of the HTTP method
170     #[inline]
as_str(&self) -> &str171     pub fn as_str(&self) -> &str {
172         match self.0 {
173             Options => "OPTIONS",
174             Get => "GET",
175             Post => "POST",
176             Put => "PUT",
177             Delete => "DELETE",
178             Head => "HEAD",
179             Trace => "TRACE",
180             Connect => "CONNECT",
181             Patch => "PATCH",
182             ExtensionInline(ref inline) => inline.as_str(),
183             ExtensionAllocated(ref allocated) => allocated.as_str(),
184         }
185     }
186 }
187 
188 impl AsRef<str> for Method {
189     #[inline]
as_ref(&self) -> &str190     fn as_ref(&self) -> &str {
191         self.as_str()
192     }
193 }
194 
195 impl<'a> PartialEq<&'a Method> for Method {
196     #[inline]
eq(&self, other: &&'a Method) -> bool197     fn eq(&self, other: &&'a Method) -> bool {
198         self == *other
199     }
200 }
201 
202 impl<'a> PartialEq<Method> for &'a Method {
203     #[inline]
eq(&self, other: &Method) -> bool204     fn eq(&self, other: &Method) -> bool {
205         *self == other
206     }
207 }
208 
209 impl PartialEq<str> for Method {
210     #[inline]
eq(&self, other: &str) -> bool211     fn eq(&self, other: &str) -> bool {
212         self.as_ref() == other
213     }
214 }
215 
216 impl PartialEq<Method> for str {
217     #[inline]
eq(&self, other: &Method) -> bool218     fn eq(&self, other: &Method) -> bool {
219         self == other.as_ref()
220     }
221 }
222 
223 impl<'a> PartialEq<&'a str> for Method {
224     #[inline]
eq(&self, other: &&'a str) -> bool225     fn eq(&self, other: &&'a str) -> bool {
226         self.as_ref() == *other
227     }
228 }
229 
230 impl<'a> PartialEq<Method> for &'a str {
231     #[inline]
eq(&self, other: &Method) -> bool232     fn eq(&self, other: &Method) -> bool {
233         *self == other.as_ref()
234     }
235 }
236 
237 impl fmt::Debug for Method {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result238     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239         f.write_str(self.as_ref())
240     }
241 }
242 
243 impl fmt::Display for Method {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result244     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
245         fmt.write_str(self.as_ref())
246     }
247 }
248 
249 impl Default for Method {
250     #[inline]
default() -> Method251     fn default() -> Method {
252         Method::GET
253     }
254 }
255 
256 impl<'a> From<&'a Method> for Method {
257     #[inline]
from(t: &'a Method) -> Self258     fn from(t: &'a Method) -> Self {
259         t.clone()
260     }
261 }
262 
263 impl<'a> TryFrom<&'a [u8]> for Method {
264     type Error = InvalidMethod;
265 
266     #[inline]
try_from(t: &'a [u8]) -> Result<Self, Self::Error>267     fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
268         Method::from_bytes(t)
269     }
270 }
271 
272 impl<'a> TryFrom<&'a str> for Method {
273     type Error = InvalidMethod;
274 
275     #[inline]
try_from(t: &'a str) -> Result<Self, Self::Error>276     fn try_from(t: &'a str) -> Result<Self, Self::Error> {
277         TryFrom::try_from(t.as_bytes())
278     }
279 }
280 
281 impl FromStr for Method {
282     type Err = InvalidMethod;
283 
284     #[inline]
from_str(t: &str) -> Result<Self, Self::Err>285     fn from_str(t: &str) -> Result<Self, Self::Err> {
286         TryFrom::try_from(t)
287     }
288 }
289 
290 impl InvalidMethod {
new() -> InvalidMethod291     fn new() -> InvalidMethod {
292         InvalidMethod { _priv: () }
293     }
294 }
295 
296 impl fmt::Debug for InvalidMethod {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result297     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
298         f.debug_struct("InvalidMethod")
299             // skip _priv noise
300             .finish()
301     }
302 }
303 
304 impl fmt::Display for InvalidMethod {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result305     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306         f.write_str("invalid HTTP method")
307     }
308 }
309 
310 impl Error for InvalidMethod {}
311 
312 mod extension {
313     use super::InvalidMethod;
314     use std::str;
315 
316     #[derive(Clone, PartialEq, Eq, Hash)]
317     // Invariant: the first self.1 bytes of self.0 are valid UTF-8.
318     pub struct InlineExtension([u8; InlineExtension::MAX], u8);
319 
320     #[derive(Clone, PartialEq, Eq, Hash)]
321     // Invariant: self.0 contains valid UTF-8.
322     pub struct AllocatedExtension(Box<[u8]>);
323 
324     impl InlineExtension {
325         // Method::from_bytes() assumes this is at least 7
326         pub const MAX: usize = 15;
327 
new(src: &[u8]) -> Result<InlineExtension, InvalidMethod>328         pub fn new(src: &[u8]) -> Result<InlineExtension, InvalidMethod> {
329             let mut data: [u8; InlineExtension::MAX] = Default::default();
330 
331             write_checked(src, &mut data)?;
332 
333             // Invariant: write_checked ensures that the first src.len() bytes
334             // of data are valid UTF-8.
335             Ok(InlineExtension(data, src.len() as u8))
336         }
337 
as_str(&self) -> &str338         pub fn as_str(&self) -> &str {
339             let InlineExtension(ref data, len) = self;
340             // Safety: the invariant of InlineExtension ensures that the first
341             // len bytes of data contain valid UTF-8.
342             unsafe {str::from_utf8_unchecked(&data[..*len as usize])}
343         }
344     }
345 
346     impl AllocatedExtension {
new(src: &[u8]) -> Result<AllocatedExtension, InvalidMethod>347         pub fn new(src: &[u8]) -> Result<AllocatedExtension, InvalidMethod> {
348             let mut data: Vec<u8> = vec![0; src.len()];
349 
350             write_checked(src, &mut data)?;
351 
352             // Invariant: data is exactly src.len() long and write_checked
353             // ensures that the first src.len() bytes of data are valid UTF-8.
354             Ok(AllocatedExtension(data.into_boxed_slice()))
355         }
356 
as_str(&self) -> &str357         pub fn as_str(&self) -> &str {
358             // Safety: the invariant of AllocatedExtension ensures that self.0
359             // contains valid UTF-8.
360             unsafe {str::from_utf8_unchecked(&self.0)}
361         }
362     }
363 
364     // From the HTTP spec section 5.1.1, the HTTP method is case-sensitive and can
365     // contain the following characters:
366     //
367     // ```
368     // method = token
369     // token = 1*tchar
370     // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
371     //     "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
372     // ```
373     //
374     // https://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01#Method
375     //
376     // Note that this definition means that any &[u8] that consists solely of valid
377     // characters is also valid UTF-8 because the valid method characters are a
378     // subset of the valid 1 byte UTF-8 encoding.
379     const METHOD_CHARS: [u8; 256] = [
380         //  0      1      2      3      4      5      6      7      8      9
381         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', //   x
382         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', //  1x
383         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', //  2x
384         b'\0', b'\0', b'\0',  b'!', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', //  3x
385         b'\0', b'\0',  b'*',  b'+', b'\0',  b'-',  b'.', b'\0',  b'0',  b'1', //  4x
386          b'2',  b'3',  b'4',  b'5',  b'6',  b'7',  b'8',  b'9', b'\0', b'\0', //  5x
387         b'\0', b'\0', b'\0', b'\0', b'\0',  b'A',  b'B',  b'C',  b'D',  b'E', //  6x
388          b'F',  b'G',  b'H',  b'I',  b'J',  b'K',  b'L',  b'M',  b'N',  b'O', //  7x
389          b'P',  b'Q',  b'R',  b'S',  b'T',  b'U',  b'V',  b'W',  b'X',  b'Y', //  8x
390          b'Z', b'\0', b'\0', b'\0',  b'^',  b'_',  b'`',  b'a',  b'b',  b'c', //  9x
391          b'd',  b'e',  b'f',  b'g',  b'h',  b'i',  b'j',  b'k',  b'l',  b'm', // 10x
392          b'n',  b'o',  b'p',  b'q',  b'r',  b's',  b't',  b'u',  b'v',  b'w', // 11x
393          b'x',  b'y',  b'z', b'\0',  b'|', b'\0',  b'~', b'\0', b'\0', b'\0', // 12x
394         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 13x
395         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 14x
396         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 15x
397         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 16x
398         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 17x
399         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 18x
400         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 19x
401         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 20x
402         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 21x
403         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 22x
404         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 23x
405         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 24x
406         b'\0', b'\0', b'\0', b'\0', b'\0', b'\0'                              // 25x
407     ];
408 
409     // write_checked ensures (among other things) that the first src.len() bytes
410     // of dst are valid UTF-8
write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod>411     fn write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod> {
412         for (i, &b) in src.iter().enumerate() {
413             let b = METHOD_CHARS[b as usize];
414 
415             if b == 0 {
416                 return Err(InvalidMethod::new());
417             }
418 
419             dst[i] = b;
420         }
421 
422         Ok(())
423     }
424 }
425 
426 #[cfg(test)]
427 mod test {
428     use super::*;
429 
430     #[test]
test_method_eq()431     fn test_method_eq() {
432         assert_eq!(Method::GET, Method::GET);
433         assert_eq!(Method::GET, "GET");
434         assert_eq!(&Method::GET, "GET");
435 
436         assert_eq!("GET", Method::GET);
437         assert_eq!("GET", &Method::GET);
438 
439         assert_eq!(&Method::GET, Method::GET);
440         assert_eq!(Method::GET, &Method::GET);
441     }
442 
443     #[test]
test_invalid_method()444     fn test_invalid_method() {
445         assert!(Method::from_str("").is_err());
446         assert!(Method::from_bytes(b"").is_err());
447         assert!(Method::from_bytes(&[0xC0]).is_err()); // invalid utf-8
448         assert!(Method::from_bytes(&[0x10]).is_err()); // invalid method characters
449     }
450 
451     #[test]
test_is_idempotent()452     fn test_is_idempotent() {
453         assert!(Method::OPTIONS.is_idempotent());
454         assert!(Method::GET.is_idempotent());
455         assert!(Method::PUT.is_idempotent());
456         assert!(Method::DELETE.is_idempotent());
457         assert!(Method::HEAD.is_idempotent());
458         assert!(Method::TRACE.is_idempotent());
459 
460         assert!(!Method::POST.is_idempotent());
461         assert!(!Method::CONNECT.is_idempotent());
462         assert!(!Method::PATCH.is_idempotent());
463     }
464 
465     #[test]
test_extention_method()466     fn test_extention_method() {
467         assert_eq!(Method::from_str("WOW").unwrap(), "WOW");
468         assert_eq!(Method::from_str("wOw!!").unwrap(), "wOw!!");
469 
470         let long_method = "This_is_a_very_long_method.It_is_valid_but_unlikely.";
471         assert_eq!(Method::from_str(&long_method).unwrap(), long_method);
472     }
473 }
474