1use self::extension::{AllocatedExtension, InlineExtension};
20use self::Inner::*;
21
22use std::convert::AsRef;
23use std::convert::TryFrom;
24use std::error::Error;
25use std::str::FromStr;
26use std::{fmt, str};
27
28#[derive(Clone, PartialEq, Eq, Hash)]
46pub struct Method(Inner);
47
48pub struct InvalidMethod {
50 _priv: (),
51}
52
53#[derive(Clone, PartialEq, Eq, Hash)]
54enum Inner {
55 Options,
56 Get,
57 Post,
58 Put,
59 Delete,
60 Head,
61 Trace,
62 Connect,
63 Patch,
64 ExtensionInline(InlineExtension),
66 ExtensionAllocated(AllocatedExtension),
68}
69
70impl Method {
71 pub const GET: Method = Method(Get);
73
74 pub const POST: Method = Method(Post);
76
77 pub const PUT: Method = Method(Put);
79
80 pub const DELETE: Method = Method(Delete);
82
83 pub const HEAD: Method = Method(Head);
85
86 pub const OPTIONS: Method = Method(Options);
88
89 pub const CONNECT: Method = Method(Connect);
91
92 pub const PATCH: Method = Method(Patch);
94
95 pub const TRACE: Method = Method(Trace);
97
98 pub fn from_bytes(src: &[u8]) -> Result<Method, InvalidMethod> {
100 match src.len() {
101 0 => Err(InvalidMethod::new()),
102 3 => match src {
103 b"GET" => Ok(Method(Get)),
104 b"PUT" => Ok(Method(Put)),
105 _ => Method::extension_inline(src),
106 },
107 4 => match src {
108 b"POST" => Ok(Method(Post)),
109 b"HEAD" => Ok(Method(Head)),
110 _ => Method::extension_inline(src),
111 },
112 5 => match src {
113 b"PATCH" => Ok(Method(Patch)),
114 b"TRACE" => Ok(Method(Trace)),
115 _ => Method::extension_inline(src),
116 },
117 6 => match src {
118 b"DELETE" => Ok(Method(Delete)),
119 _ => Method::extension_inline(src),
120 },
121 7 => match src {
122 b"OPTIONS" => Ok(Method(Options)),
123 b"CONNECT" => Ok(Method(Connect)),
124 _ => Method::extension_inline(src),
125 },
126 _ => {
127 if src.len() < InlineExtension::MAX {
128 Method::extension_inline(src)
129 } else {
130 let allocated = AllocatedExtension::new(src)?;
131
132 Ok(Method(ExtensionAllocated(allocated)))
133 }
134 }
135 }
136 }
137
138 fn extension_inline(src: &[u8]) -> Result<Method, InvalidMethod> {
139 let inline = InlineExtension::new(src)?;
140
141 Ok(Method(ExtensionInline(inline)))
142 }
143
144 pub(crate) fn is_safe(&self) -> bool {
150 match self.0 {
151 Get | Head | Options | Trace => true,
152 _ => false,
153 }
154 }
155
156 pub(crate) fn is_idempotent(&self) -> bool {
162 match self.0 {
163 Put | Delete => true,
164 _ => self.is_safe(),
165 }
166 }
167
168 #[inline]
169 pub fn as_str(&self) -> &str {
170 match self.0 {
171 Options => "OPTIONS",
172 Get => "GET",
173 Post => "POST",
174 Put => "PUT",
175 Delete => "DELETE",
176 Head => "HEAD",
177 Trace => "TRACE",
178 Connect => "CONNECT",
179 Patch => "PATCH",
180 ExtensionInline(ref inline) => inline.as_str(),
181 ExtensionAllocated(ref allocated) => allocated.as_str(),
182 }
183 }
184}
185
186impl AsRef<str> for Method {
187 #[inline]
188 fn as_ref(&self) -> &str {
189 self.as_str()
190 }
191}
192
193impl<'a> PartialEq<&'a Method> for Method {
194 #[inline]
195 fn eq(&self, other: &&'a Method) -> bool {
196 self == *other
197 }
198}
199
200impl<'a> PartialEq<Method> for &'a Method {
201 #[inline]
202 fn eq(&self, other: &Method) -> bool {
203 *self == other
204 }
205}
206
207impl PartialEq<str> for Method {
208 #[inline]
209 fn eq(&self, other: &str) -> bool {
210 self.as_ref() == other
211 }
212}
213
214impl PartialEq<Method> for str {
215 #[inline]
216 fn eq(&self, other: &Method) -> bool {
217 self == other.as_ref()
218 }
219}
220
221impl<'a> PartialEq<&'a str> for Method {
222 #[inline]
223 fn eq(&self, other: &&'a str) -> bool {
224 self.as_ref() == *other
225 }
226}
227
228impl<'a> PartialEq<Method> for &'a str {
229 #[inline]
230 fn eq(&self, other: &Method) -> bool {
231 *self == other.as_ref()
232 }
233}
234
235impl fmt::Debug for Method {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 f.write_str(self.as_ref())
238 }
239}
240
241impl fmt::Display for Method {
242 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
243 fmt.write_str(self.as_ref())
244 }
245}
246
247impl Default for Method {
248 #[inline]
249 fn default() -> Method {
250 Method::GET
251 }
252}
253
254impl<'a> From<&'a Method> for Method {
255 #[inline]
256 fn from(t: &'a Method) -> Self {
257 t.clone()
258 }
259}
260
261impl<'a> TryFrom<&'a [u8]> for Method {
262 type Error = InvalidMethod;
263
264 #[inline]
265 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
266 Method::from_bytes(t)
267 }
268}
269
270impl<'a> TryFrom<&'a str> for Method {
271 type Error = InvalidMethod;
272
273 #[inline]
274 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
275 TryFrom::try_from(t.as_bytes())
276 }
277}
278
279impl FromStr for Method {
280 type Err = InvalidMethod;
281
282 #[inline]
283 fn from_str(t: &str) -> Result<Self, Self::Err> {
284 TryFrom::try_from(t)
285 }
286}
287
288impl InvalidMethod {
289 fn new() -> InvalidMethod {
290 InvalidMethod { _priv: () }
291 }
292}
293
294impl fmt::Debug for InvalidMethod {
295 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
296 f.debug_struct("InvalidMethod")
297 .finish()
299 }
300}
301
302impl fmt::Display for InvalidMethod {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 f.write_str("invalid HTTP method")
305 }
306}
307
308impl Error for InvalidMethod {}
309
310mod extension {
311 use super::InvalidMethod;
312 use std::str;
313
314 #[derive(Clone, PartialEq, Eq, Hash)]
315 pub(crate) struct InlineExtension([u8; InlineExtension::MAX], u8);
317
318 #[derive(Clone, PartialEq, Eq, Hash)]
319 pub(crate) struct AllocatedExtension(Box<[u8]>);
321
322 impl InlineExtension {
323 pub(crate) const MAX: usize = 15;
325
326 pub(crate) fn new(src: &[u8]) -> Result<InlineExtension, InvalidMethod> {
327 let mut data: [u8; InlineExtension::MAX] = Default::default();
328
329 write_checked(src, &mut data)?;
330
331 Ok(InlineExtension(data, src.len() as u8))
334 }
335
336 pub(crate) fn as_str(&self) -> &str {
337 let InlineExtension(ref data, len) = self;
338 unsafe { str::from_utf8_unchecked(&data[..*len as usize]) }
341 }
342 }
343
344 impl AllocatedExtension {
345 pub(crate) fn new(src: &[u8]) -> Result<AllocatedExtension, InvalidMethod> {
346 let mut data: Vec<u8> = vec![0; src.len()];
347
348 write_checked(src, &mut data)?;
349
350 Ok(AllocatedExtension(data.into_boxed_slice()))
353 }
354
355 pub(crate) fn as_str(&self) -> &str {
356 unsafe { str::from_utf8_unchecked(&self.0) }
359 }
360 }
361
362 const METHOD_CHARS: [u8; 256] = [
378 b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'!', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'*', b'+', b'\0', b'-', b'.', b'\0', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'\0', b'\0', b'\0', b'^', b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'\0', b'|', b'\0', b'~', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', ];
406
407 fn write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod> {
410 for (i, &b) in src.iter().enumerate() {
411 let b = METHOD_CHARS[b as usize];
412
413 if b == 0 {
414 return Err(InvalidMethod::new());
415 }
416
417 dst[i] = b;
418 }
419
420 Ok(())
421 }
422}
423
424#[cfg(test)]
425mod test {
426 use super::*;
427
428 #[test]
429 fn test_method_eq() {
430 assert_eq!(Method::GET, Method::GET);
431 assert_eq!(Method::GET, "GET");
432 assert_eq!(&Method::GET, "GET");
433
434 assert_eq!("GET", Method::GET);
435 assert_eq!("GET", &Method::GET);
436
437 assert_eq!(&Method::GET, Method::GET);
438 assert_eq!(Method::GET, &Method::GET);
439 }
440
441 #[test]
442 fn test_invalid_method() {
443 assert!(Method::from_str("").is_err());
444 assert!(Method::from_bytes(b"").is_err());
445 assert!(Method::from_bytes(&[0xC0]).is_err()); assert!(Method::from_bytes(&[0x10]).is_err()); }
448
449 #[test]
450 fn test_is_idempotent() {
451 assert!(Method::OPTIONS.is_idempotent());
452 assert!(Method::PUT.is_idempotent());
453 assert!(Method::DELETE.is_idempotent());
454 assert!(Method::HEAD.is_idempotent());
455 assert!(Method::TRACE.is_idempotent());
456
457 assert!(!Method::POST.is_idempotent());
458 assert!(!Method::CONNECT.is_idempotent());
459 assert!(!Method::PATCH.is_idempotent());
460 }
461
462 #[test]
463 fn test_extention_method() {
464 assert_eq!(Method::from_str("WOW").unwrap(), "WOW");
465 assert_eq!(Method::from_str("wOw!!").unwrap(), "wOw!!");
466
467 let long_method = "This_is_a_very_long_method.It_is_valid_but_unlikely.";
468 assert_eq!(Method::from_str(&long_method).unwrap(), long_method);
469 }
470}