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