1use self::Inner::*;
19use self::extension::{InlineExtension, AllocatedExtension};
20
21use std::convert::AsRef;
22use std::error::Error;
23use std::str::FromStr;
24use std::convert::TryFrom;
25use std::{fmt, str};
26
27#[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
70
71impl Method {
72 pub const GET: Method = Method(Get);
74
75 pub const POST: Method = Method(Post);
77
78 pub const PUT: Method = Method(Put);
80
81 pub const DELETE: Method = Method(Delete);
83
84 pub const HEAD: Method = Method(Head);
86
87 pub const OPTIONS: Method = Method(Options);
89
90 pub const CONNECT: Method = Method(Connect);
92
93 pub const PATCH: Method = Method(Patch);
95
96 pub const TRACE: Method = Method(Trace);
98
99 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
139 fn extension_inline(src: &[u8]) -> Result<Method, InvalidMethod> {
140 let inline = InlineExtension::new(src)?;
141
142 Ok(Method(ExtensionInline(inline)))
143 }
144
145 pub fn is_safe(&self) -> bool {
151 match self.0 {
152 Get | Head | Options | Trace => true,
153 _ => false,
154 }
155 }
156
157 pub fn is_idempotent(&self) -> bool {
163 match self.0 {
164 Put | Delete => true,
165 _ => self.is_safe(),
166 }
167 }
168
169 #[inline]
171 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
188impl AsRef<str> for Method {
189 #[inline]
190 fn as_ref(&self) -> &str {
191 self.as_str()
192 }
193}
194
195impl<'a> PartialEq<&'a Method> for Method {
196 #[inline]
197 fn eq(&self, other: &&'a Method) -> bool {
198 self == *other
199 }
200}
201
202impl<'a> PartialEq<Method> for &'a Method {
203 #[inline]
204 fn eq(&self, other: &Method) -> bool {
205 *self == other
206 }
207}
208
209impl PartialEq<str> for Method {
210 #[inline]
211 fn eq(&self, other: &str) -> bool {
212 self.as_ref() == other
213 }
214}
215
216impl PartialEq<Method> for str {
217 #[inline]
218 fn eq(&self, other: &Method) -> bool {
219 self == other.as_ref()
220 }
221}
222
223impl<'a> PartialEq<&'a str> for Method {
224 #[inline]
225 fn eq(&self, other: &&'a str) -> bool {
226 self.as_ref() == *other
227 }
228}
229
230impl<'a> PartialEq<Method> for &'a str {
231 #[inline]
232 fn eq(&self, other: &Method) -> bool {
233 *self == other.as_ref()
234 }
235}
236
237impl fmt::Debug for Method {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 f.write_str(self.as_ref())
240 }
241}
242
243impl fmt::Display for Method {
244 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
245 fmt.write_str(self.as_ref())
246 }
247}
248
249impl Default for Method {
250 #[inline]
251 fn default() -> Method {
252 Method::GET
253 }
254}
255
256impl<'a> From<&'a Method> for Method {
257 #[inline]
258 fn from(t: &'a Method) -> Self {
259 t.clone()
260 }
261}
262
263impl<'a> TryFrom<&'a [u8]> for Method {
264 type Error = InvalidMethod;
265
266 #[inline]
267 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
268 Method::from_bytes(t)
269 }
270}
271
272impl<'a> TryFrom<&'a str> for Method {
273 type Error = InvalidMethod;
274
275 #[inline]
276 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
277 TryFrom::try_from(t.as_bytes())
278 }
279}
280
281impl FromStr for Method {
282 type Err = InvalidMethod;
283
284 #[inline]
285 fn from_str(t: &str) -> Result<Self, Self::Err> {
286 TryFrom::try_from(t)
287 }
288}
289
290impl InvalidMethod {
291 fn new() -> InvalidMethod {
292 InvalidMethod { _priv: () }
293 }
294}
295
296impl fmt::Debug for InvalidMethod {
297 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
298 f.debug_struct("InvalidMethod")
299 .finish()
301 }
302}
303
304impl fmt::Display for InvalidMethod {
305 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306 f.write_str("invalid HTTP method")
307 }
308}
309
310impl Error for InvalidMethod {}
311
312mod extension {
313 use super::InvalidMethod;
314 use std::str;
315
316 #[derive(Clone, PartialEq, Eq, Hash)]
317 pub struct InlineExtension([u8; InlineExtension::MAX], u8);
319
320 #[derive(Clone, PartialEq, Eq, Hash)]
321 pub struct AllocatedExtension(Box<[u8]>);
323
324 impl InlineExtension {
325 pub const MAX: usize = 15;
327
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 Ok(InlineExtension(data, src.len() as u8))
336 }
337
338 pub fn as_str(&self) -> &str {
339 let InlineExtension(ref data, len) = self;
340 unsafe {str::from_utf8_unchecked(&data[..*len as usize])}
343 }
344 }
345
346 impl AllocatedExtension {
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 Ok(AllocatedExtension(data.into_boxed_slice()))
355 }
356
357 pub fn as_str(&self) -> &str {
358 unsafe {str::from_utf8_unchecked(&self.0)}
361 }
362 }
363
364 const METHOD_CHARS: [u8; 256] = [
380 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' ];
408
409 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)]
427mod test {
428 use super::*;
429
430 #[test]
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]
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()); assert!(Method::from_bytes(&[0x10]).is_err()); }
450
451 #[test]
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]
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}