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
20 use std::convert::AsRef;
21 use std::error::Error;
22 use std::str::FromStr;
23 use std::convert::TryFrom;
24 use std::{fmt, str};
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 impl Method {
114 /// GET
115 pub const GET: Method = Method(Get);
116
117 /// POST
118 pub const POST: Method = Method(Post);
119
120 /// PUT
121 pub const PUT: Method = Method(Put);
122
123 /// DELETE
124 pub const DELETE: Method = Method(Delete);
125
126 /// HEAD
127 pub const HEAD: Method = Method(Head);
128
129 /// OPTIONS
130 pub const OPTIONS: Method = Method(Options);
131
132 /// CONNECT
133 pub const CONNECT: Method = Method(Connect);
134
135 /// PATCH
136 pub const PATCH: Method = Method(Patch);
137
138 /// TRACE
139 pub const TRACE: Method = Method(Trace);
140
141 /// Converts a slice of bytes to an HTTP method.
from_bytes(src: &[u8]) -> Result<Method, InvalidMethod>142 pub fn from_bytes(src: &[u8]) -> Result<Method, InvalidMethod> {
143 match src.len() {
144 0 => Err(InvalidMethod::new()),
145 3 => match src {
146 b"GET" => Ok(Method(Get)),
147 b"PUT" => Ok(Method(Put)),
148 _ => Method::extension_inline(src),
149 },
150 4 => match src {
151 b"POST" => Ok(Method(Post)),
152 b"HEAD" => Ok(Method(Head)),
153 _ => Method::extension_inline(src),
154 },
155 5 => match src {
156 b"PATCH" => Ok(Method(Patch)),
157 b"TRACE" => Ok(Method(Trace)),
158 _ => Method::extension_inline(src),
159 },
160 6 => match src {
161 b"DELETE" => Ok(Method(Delete)),
162 _ => Method::extension_inline(src),
163 },
164 7 => match src {
165 b"OPTIONS" => Ok(Method(Options)),
166 b"CONNECT" => Ok(Method(Connect)),
167 _ => Method::extension_inline(src),
168 },
169 _ => {
170 if src.len() < MAX_INLINE {
171 Method::extension_inline(src)
172 } else {
173 let mut data: Vec<u8> = vec![0; src.len()];
174
175 write_checked(src, &mut data)?;
176
177 Ok(Method(ExtensionAllocated(data.into_boxed_slice())))
178 }
179 }
180 }
181 }
182
extension_inline(src: &[u8]) -> Result<Method, InvalidMethod>183 fn extension_inline(src: &[u8]) -> Result<Method, InvalidMethod> {
184 let mut data: [u8; MAX_INLINE] = Default::default();
185
186 write_checked(src, &mut data)?;
187
188 Ok(Method(ExtensionInline(data, src.len() as u8)))
189 }
190
191 /// Whether a method is considered "safe", meaning the request is
192 /// essentially read-only.
193 ///
194 /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1)
195 /// for more words.
is_safe(&self) -> bool196 pub fn is_safe(&self) -> bool {
197 match self.0 {
198 Get | Head | Options | Trace => true,
199 _ => false,
200 }
201 }
202
203 /// Whether a method is considered "idempotent", meaning the request has
204 /// the same result if executed multiple times.
205 ///
206 /// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.2) for
207 /// more words.
is_idempotent(&self) -> bool208 pub fn is_idempotent(&self) -> bool {
209 match self.0 {
210 Put | Delete => true,
211 _ => self.is_safe(),
212 }
213 }
214
215 /// Return a &str representation of the HTTP method
216 #[inline]
as_str(&self) -> &str217 pub fn as_str(&self) -> &str {
218 match self.0 {
219 Options => "OPTIONS",
220 Get => "GET",
221 Post => "POST",
222 Put => "PUT",
223 Delete => "DELETE",
224 Head => "HEAD",
225 Trace => "TRACE",
226 Connect => "CONNECT",
227 Patch => "PATCH",
228 ExtensionInline(ref data, len) => unsafe {
229 str::from_utf8_unchecked(&data[..len as usize])
230 },
231 ExtensionAllocated(ref data) => unsafe { str::from_utf8_unchecked(data) },
232 }
233 }
234 }
235
write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod>236 fn write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod> {
237 for (i, &b) in src.iter().enumerate() {
238 let b = METHOD_CHARS[b as usize];
239
240 if b == 0 {
241 return Err(InvalidMethod::new());
242 }
243
244 dst[i] = b;
245 }
246
247 Ok(())
248 }
249
250 impl AsRef<str> for Method {
251 #[inline]
as_ref(&self) -> &str252 fn as_ref(&self) -> &str {
253 self.as_str()
254 }
255 }
256
257 impl<'a> PartialEq<&'a Method> for Method {
258 #[inline]
eq(&self, other: &&'a Method) -> bool259 fn eq(&self, other: &&'a Method) -> bool {
260 self == *other
261 }
262 }
263
264 impl<'a> PartialEq<Method> for &'a Method {
265 #[inline]
eq(&self, other: &Method) -> bool266 fn eq(&self, other: &Method) -> bool {
267 *self == other
268 }
269 }
270
271 impl PartialEq<str> for Method {
272 #[inline]
eq(&self, other: &str) -> bool273 fn eq(&self, other: &str) -> bool {
274 self.as_ref() == other
275 }
276 }
277
278 impl PartialEq<Method> for str {
279 #[inline]
eq(&self, other: &Method) -> bool280 fn eq(&self, other: &Method) -> bool {
281 self == other.as_ref()
282 }
283 }
284
285 impl<'a> PartialEq<&'a str> for Method {
286 #[inline]
eq(&self, other: &&'a str) -> bool287 fn eq(&self, other: &&'a str) -> bool {
288 self.as_ref() == *other
289 }
290 }
291
292 impl<'a> PartialEq<Method> for &'a str {
293 #[inline]
eq(&self, other: &Method) -> bool294 fn eq(&self, other: &Method) -> bool {
295 *self == other.as_ref()
296 }
297 }
298
299 impl fmt::Debug for Method {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 f.write_str(self.as_ref())
302 }
303 }
304
305 impl fmt::Display for Method {
fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result306 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
307 fmt.write_str(self.as_ref())
308 }
309 }
310
311 impl Default for Method {
312 #[inline]
default() -> Method313 fn default() -> Method {
314 Method::GET
315 }
316 }
317
318 impl<'a> From<&'a Method> for Method {
319 #[inline]
from(t: &'a Method) -> Self320 fn from(t: &'a Method) -> Self {
321 t.clone()
322 }
323 }
324
325 impl<'a> TryFrom<&'a [u8]> for Method {
326 type Error = InvalidMethod;
327
328 #[inline]
try_from(t: &'a [u8]) -> Result<Self, Self::Error>329 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
330 Method::from_bytes(t)
331 }
332 }
333
334 impl<'a> TryFrom<&'a str> for Method {
335 type Error = InvalidMethod;
336
337 #[inline]
try_from(t: &'a str) -> Result<Self, Self::Error>338 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
339 TryFrom::try_from(t.as_bytes())
340 }
341 }
342
343 impl FromStr for Method {
344 type Err = InvalidMethod;
345
346 #[inline]
from_str(t: &str) -> Result<Self, Self::Err>347 fn from_str(t: &str) -> Result<Self, Self::Err> {
348 TryFrom::try_from(t)
349 }
350 }
351
352 impl InvalidMethod {
new() -> InvalidMethod353 fn new() -> InvalidMethod {
354 InvalidMethod { _priv: () }
355 }
356 }
357
358 impl fmt::Debug for InvalidMethod {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result359 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
360 f.debug_struct("InvalidMethod")
361 // skip _priv noise
362 .finish()
363 }
364 }
365
366 impl fmt::Display for InvalidMethod {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result367 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
368 f.write_str("invalid HTTP method")
369 }
370 }
371
372 impl Error for InvalidMethod {}
373
374 #[test]
test_method_eq()375 fn test_method_eq() {
376 assert_eq!(Method::GET, Method::GET);
377 assert_eq!(Method::GET, "GET");
378 assert_eq!(&Method::GET, "GET");
379
380 assert_eq!("GET", Method::GET);
381 assert_eq!("GET", &Method::GET);
382
383 assert_eq!(&Method::GET, Method::GET);
384 assert_eq!(Method::GET, &Method::GET);
385 }
386
387 #[test]
test_invalid_method()388 fn test_invalid_method() {
389 assert!(Method::from_str("").is_err());
390 assert!(Method::from_bytes(b"").is_err());
391 }
392
393 #[test]
test_is_idempotent()394 fn test_is_idempotent() {
395 assert!(Method::OPTIONS.is_idempotent());
396 assert!(Method::GET.is_idempotent());
397 assert!(Method::PUT.is_idempotent());
398 assert!(Method::DELETE.is_idempotent());
399 assert!(Method::HEAD.is_idempotent());
400 assert!(Method::TRACE.is_idempotent());
401
402 assert!(!Method::POST.is_idempotent());
403 assert!(!Method::CONNECT.is_idempotent());
404 assert!(!Method::PATCH.is_idempotent());
405 }
406