1use self::extension::{AllocatedExtension, InlineExtension};
19use self::Inner::*;
20
21use std::convert::TryFrom;
22use std::error::Error;
23use std::str::FromStr;
24use std::{fmt, str};
25
26#[derive(Clone, PartialEq, Eq, Hash)]
45pub struct Method(Inner);
46
47pub struct InvalidMethod {
49 _priv: (),
50}
51
52#[derive(Clone, PartialEq, Eq, Hash)]
53enum Inner {
54 Options,
55 Get,
56 Post,
57 Put,
58 Delete,
59 Head,
60 Trace,
61 Connect,
62 Patch,
63 Query,
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 const QUERY: Method = Method(Query);
100
101 pub fn from_bytes(src: &[u8]) -> Result<Method, InvalidMethod> {
103 match src.len() {
104 0 => Err(InvalidMethod::new()),
105 3 => match src {
106 b"GET" => Ok(Method(Get)),
107 b"PUT" => Ok(Method(Put)),
108 _ => Method::extension_inline(src),
109 },
110 4 => match src {
111 b"POST" => Ok(Method(Post)),
112 b"HEAD" => Ok(Method(Head)),
113 _ => Method::extension_inline(src),
114 },
115 5 => match src {
116 b"PATCH" => Ok(Method(Patch)),
117 b"TRACE" => Ok(Method(Trace)),
118 b"QUERY" => Ok(Method(Query)),
119 _ => Method::extension_inline(src),
120 },
121 6 => match src {
122 b"DELETE" => Ok(Method(Delete)),
123 _ => Method::extension_inline(src),
124 },
125 7 => match src {
126 b"OPTIONS" => Ok(Method(Options)),
127 b"CONNECT" => Ok(Method(Connect)),
128 _ => Method::extension_inline(src),
129 },
130 _ => {
131 if src.len() <= InlineExtension::MAX {
132 Method::extension_inline(src)
133 } else {
134 let allocated = AllocatedExtension::new(src)?;
135
136 Ok(Method(ExtensionAllocated(allocated)))
137 }
138 }
139 }
140 }
141
142 fn extension_inline(src: &[u8]) -> Result<Method, InvalidMethod> {
143 let inline = InlineExtension::new(src)?;
144
145 Ok(Method(ExtensionInline(inline)))
146 }
147
148 pub fn is_safe(&self) -> bool {
154 matches!(self.0, Get | Head | Options | Trace | Query)
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 Query => "QUERY",
183 ExtensionInline(ref inline) => inline.as_str(),
184 ExtensionAllocated(ref allocated) => allocated.as_str(),
185 }
186 }
187}
188
189impl AsRef<str> for Method {
190 #[inline]
191 fn as_ref(&self) -> &str {
192 self.as_str()
193 }
194}
195
196impl Ord for Method {
197 #[inline]
198 fn cmp(&self, other: &Method) -> std::cmp::Ordering {
199 self.as_ref().cmp(other.as_ref())
200 }
201}
202
203impl PartialOrd for Method {
204 #[inline]
205 fn partial_cmp(&self, other: &Method) -> Option<std::cmp::Ordering> {
206 Some(self.cmp(other))
207 }
208}
209
210impl PartialEq<&Method> for Method {
211 #[inline]
212 fn eq(&self, other: &&Method) -> bool {
213 self == *other
214 }
215}
216
217impl PartialEq<Method> for &Method {
218 #[inline]
219 fn eq(&self, other: &Method) -> bool {
220 *self == other
221 }
222}
223
224impl PartialEq<str> for Method {
225 #[inline]
226 fn eq(&self, other: &str) -> bool {
227 self.as_ref() == other
228 }
229}
230
231impl PartialEq<Method> for str {
232 #[inline]
233 fn eq(&self, other: &Method) -> bool {
234 self == other.as_ref()
235 }
236}
237
238impl PartialEq<&str> for Method {
239 #[inline]
240 fn eq(&self, other: &&str) -> bool {
241 self.as_ref() == *other
242 }
243}
244
245impl PartialEq<Method> for &str {
246 #[inline]
247 fn eq(&self, other: &Method) -> bool {
248 *self == other.as_ref()
249 }
250}
251
252impl fmt::Debug for Method {
253 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254 f.write_str(self.as_ref())
255 }
256}
257
258impl fmt::Display for Method {
259 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
260 fmt.write_str(self.as_ref())
261 }
262}
263
264impl Default for Method {
265 #[inline]
266 fn default() -> Method {
267 Method::GET
268 }
269}
270
271impl From<&Method> for Method {
272 #[inline]
273 fn from(t: &Method) -> Self {
274 t.clone()
275 }
276}
277
278impl TryFrom<&[u8]> for Method {
279 type Error = InvalidMethod;
280
281 #[inline]
282 fn try_from(t: &[u8]) -> Result<Self, Self::Error> {
283 Method::from_bytes(t)
284 }
285}
286
287impl TryFrom<&str> for Method {
288 type Error = InvalidMethod;
289
290 #[inline]
291 fn try_from(t: &str) -> Result<Self, Self::Error> {
292 TryFrom::try_from(t.as_bytes())
293 }
294}
295
296impl FromStr for Method {
297 type Err = InvalidMethod;
298
299 #[inline]
300 fn from_str(t: &str) -> Result<Self, Self::Err> {
301 TryFrom::try_from(t)
302 }
303}
304
305impl InvalidMethod {
306 fn new() -> InvalidMethod {
307 InvalidMethod { _priv: () }
308 }
309}
310
311impl fmt::Debug for InvalidMethod {
312 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
313 f.debug_struct("InvalidMethod")
314 .finish()
316 }
317}
318
319impl fmt::Display for InvalidMethod {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 f.write_str("invalid HTTP method")
322 }
323}
324
325impl Error for InvalidMethod {}
326
327mod extension {
328 use super::InvalidMethod;
329 use std::str;
330
331 #[derive(Clone, PartialEq, Eq, Hash)]
332 pub struct InlineExtension([u8; InlineExtension::MAX], u8);
334
335 #[derive(Clone, PartialEq, Eq, Hash)]
336 pub struct AllocatedExtension(Box<[u8]>);
338
339 impl InlineExtension {
340 pub const MAX: usize = 15;
342
343 pub fn new(src: &[u8]) -> Result<InlineExtension, InvalidMethod> {
344 let mut data: [u8; InlineExtension::MAX] = Default::default();
345
346 write_checked(src, &mut data)?;
347
348 Ok(InlineExtension(data, src.len() as u8))
351 }
352
353 pub fn as_str(&self) -> &str {
354 let InlineExtension(ref data, len) = self;
355 unsafe { str::from_utf8_unchecked(&data[..*len as usize]) }
358 }
359 }
360
361 impl AllocatedExtension {
362 pub fn new(src: &[u8]) -> Result<AllocatedExtension, InvalidMethod> {
363 let mut data: Vec<u8> = vec![0; src.len()];
364
365 write_checked(src, &mut data)?;
366
367 Ok(AllocatedExtension(data.into_boxed_slice()))
370 }
371
372 pub fn as_str(&self) -> &str {
373 unsafe { str::from_utf8_unchecked(&self.0) }
376 }
377 }
378
379 #[rustfmt::skip]
395 const METHOD_CHARS: [u8; 256] = [
396 b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\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'#', b'$', b'%', b'&', b'\'', 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' ];
424
425 fn write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod> {
428 for (i, &b) in src.iter().enumerate() {
429 let b = METHOD_CHARS[b as usize];
430
431 if b == 0 {
432 return Err(InvalidMethod::new());
433 }
434
435 dst[i] = b;
436 }
437
438 Ok(())
439 }
440}
441
442#[cfg(test)]
443mod test {
444 use super::*;
445
446 #[test]
447 fn test_method_eq() {
448 assert_eq!(Method::GET, Method::GET);
449 assert_eq!(Method::GET, "GET");
450 assert_eq!(&Method::GET, "GET");
451
452 assert_eq!("GET", Method::GET);
453 assert_eq!("GET", &Method::GET);
454
455 assert_eq!(&Method::GET, Method::GET);
456 assert_eq!(Method::GET, &Method::GET);
457 }
458
459 #[test]
460 fn test_invalid_method() {
461 assert!(Method::from_str("").is_err());
462 assert!(Method::from_bytes(b"").is_err());
463 assert!(Method::from_bytes(&[0xC0]).is_err()); assert!(Method::from_bytes(&[0x10]).is_err()); }
466
467 #[test]
468 fn test_is_idempotent() {
469 assert!(Method::OPTIONS.is_idempotent());
470 assert!(Method::GET.is_idempotent());
471 assert!(Method::PUT.is_idempotent());
472 assert!(Method::DELETE.is_idempotent());
473 assert!(Method::HEAD.is_idempotent());
474 assert!(Method::TRACE.is_idempotent());
475 assert!(Method::QUERY.is_idempotent());
476
477 assert!(!Method::POST.is_idempotent());
478 assert!(!Method::CONNECT.is_idempotent());
479 assert!(!Method::PATCH.is_idempotent());
480 }
481
482 #[test]
483 fn test_extension_method() {
484 assert_eq!(Method::from_str("WOW").unwrap(), "WOW");
485 assert_eq!(Method::from_str("wOw!!").unwrap(), "wOw!!");
486
487 let long_method = "This_is_a_very_long_method.It_is_valid_but_unlikely.";
488 assert_eq!(Method::from_str(long_method).unwrap(), long_method);
489
490 let longest_inline_method = [b'A'; InlineExtension::MAX];
491 assert_eq!(
492 Method::from_bytes(&longest_inline_method).unwrap(),
493 Method(ExtensionInline(
494 InlineExtension::new(&longest_inline_method).unwrap()
495 ))
496 );
497 let shortest_allocated_method = [b'A'; InlineExtension::MAX + 1];
498 assert_eq!(
499 Method::from_bytes(&shortest_allocated_method).unwrap(),
500 Method(ExtensionAllocated(
501 AllocatedExtension::new(&shortest_allocated_method).unwrap()
502 ))
503 );
504 }
505
506 #[test]
507 fn test_extension_method_chars() {
508 const VALID_METHOD_CHARS: &str =
509 "!#$%&'*+-.^_`|~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
510
511 for c in VALID_METHOD_CHARS.chars() {
512 let c = c.to_string();
513
514 assert_eq!(
515 Method::from_str(&c).unwrap(),
516 c.as_str(),
517 "testing {c} is a valid method character"
518 );
519 }
520 }
521}