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