1use HttpTryFrom;
19use self::Inner::*;
20
21use std::{fmt, str};
22use std::convert::AsRef;
23use std::error::Error;
24use std::str::FromStr;
25
26#[derive(Clone, PartialEq, Eq, Hash)]
45pub struct Method(Inner);
46
47#[derive(Debug)]
49pub 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([u8; MAX_INLINE], u8),
66 ExtensionAllocated(Box<[u8]>),
68}
69
70const MAX_INLINE: usize = 15;
71
72const METHOD_CHARS: [u8; 256] = [
85 b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\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' ];
113
114
115impl Method {
116 pub const GET: Method = Method(Get);
118
119 pub const POST: Method = Method(Post);
121
122 pub const PUT: Method = Method(Put);
124
125 pub const DELETE: Method = Method(Delete);
127
128 pub const HEAD: Method = Method(Head);
130
131 pub const OPTIONS: Method = Method(Options);
133
134 pub const CONNECT: Method = Method(Connect);
136
137 pub const PATCH: Method = Method(Patch);
139
140 pub const TRACE: Method = Method(Trace);
142
143 pub fn from_bytes(src: &[u8]) -> Result<Method, InvalidMethod> {
145 match src.len() {
146 3 => {
147 match src {
148 b"GET" => Ok(Method(Get)),
149 b"PUT" => Ok(Method(Put)),
150 _ => Method::extension_inline(src),
151 }
152 }
153 4 => {
154 match src {
155 b"POST" => Ok(Method(Post)),
156 b"HEAD" => Ok(Method(Head)),
157 _ => Method::extension_inline(src),
158 }
159 }
160 5 => {
161 match src {
162 b"PATCH" => Ok(Method(Patch)),
163 b"TRACE" => Ok(Method(Trace)),
164 _ => Method::extension_inline(src),
165 }
166 }
167 6 => {
168 match src {
169 b"DELETE" => Ok(Method(Delete)),
170 _ => Method::extension_inline(src),
171 }
172 }
173 7 => {
174 match src {
175 b"OPTIONS" => Ok(Method(Options)),
176 b"CONNECT" => Ok(Method(Connect)),
177 _ => Method::extension_inline(src),
178 }
179 }
180 _ => {
181 if src.len() < MAX_INLINE {
182 Method::extension_inline(src)
183 } else {
184 let mut data: Vec<u8> = vec![0; src.len()];
185
186 write_checked(src, &mut data)?;
187
188 Ok(Method(ExtensionAllocated(data.into_boxed_slice())))
189 }
190 }
191 }
192 }
193
194 fn extension_inline(src: &[u8]) -> Result<Method, InvalidMethod> {
195 let mut data: [u8; MAX_INLINE] = Default::default();
196
197 write_checked(src, &mut data)?;
198
199 Ok(Method(ExtensionInline(data, src.len() as u8)))
200 }
201
202 pub fn is_safe(&self) -> bool {
208 match self.0 {
209 Get | Head | Options | Trace => true,
210 _ => false
211 }
212 }
213
214 pub fn is_idempotent(&self) -> bool {
220 if self.is_safe() {
221 true
222 } else {
223 match self.0 {
224 Put | Delete => true,
225 _ => false
226 }
227 }
228 }
229
230 #[inline]
232 pub fn as_str(&self) -> &str {
233 match self.0 {
234 Options => "OPTIONS",
235 Get => "GET",
236 Post => "POST",
237 Put => "PUT",
238 Delete => "DELETE",
239 Head => "HEAD",
240 Trace => "TRACE",
241 Connect => "CONNECT",
242 Patch => "PATCH",
243 ExtensionInline(ref data, len) => {
244 unsafe {
245 str::from_utf8_unchecked(&data[..len as usize])
246 }
247 }
248 ExtensionAllocated(ref data) => {
249 unsafe {
250 str::from_utf8_unchecked(data)
251 }
252 }
253 }
254 }
255}
256
257fn write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod> {
258 for (i, &b) in src.iter().enumerate() {
259 let b = METHOD_CHARS[b as usize];
260
261 if b == 0 {
262 return Err(InvalidMethod::new());
263 }
264
265 dst[i] = b;
266 }
267
268 Ok(())
269}
270
271impl AsRef<str> for Method {
272 #[inline]
273 fn as_ref(&self) -> &str {
274 self.as_str()
275 }
276}
277
278impl PartialEq<str> for Method {
279 #[inline]
280 fn eq(&self, other: &str) -> bool {
281 self.as_ref() == other
282 }
283}
284
285impl<'a> PartialEq<&'a str> for Method {
286 #[inline]
287 fn eq(&self, other: &&'a str) -> bool {
288 self.as_ref() == *other
289 }
290}
291
292impl fmt::Debug for Method {
293 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
294 f.write_str(self.as_ref())
295 }
296}
297
298impl fmt::Display for Method {
299 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
300 fmt.write_str(self.as_ref())
301 }
302}
303
304impl Default for Method {
305 #[inline]
306 fn default() -> Method {
307 Method::GET
308 }
309}
310
311impl<'a> HttpTryFrom<&'a [u8]> for Method {
312 type Error = InvalidMethod;
313
314 #[inline]
315 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
316 Method::from_bytes(t)
317 }
318}
319
320impl<'a> HttpTryFrom<&'a str> for Method {
321 type Error = InvalidMethod;
322
323 #[inline]
324 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
325 HttpTryFrom::try_from(t.as_bytes())
326 }
327}
328
329impl FromStr for Method {
330 type Err = InvalidMethod;
331
332 #[inline]
333 fn from_str(t: &str) -> Result<Self, Self::Err> {
334 HttpTryFrom::try_from(t)
335 }
336}
337
338impl InvalidMethod {
339 fn new() -> InvalidMethod {
340 InvalidMethod {
341 _priv: (),
342 }
343 }
344}
345
346impl fmt::Display for InvalidMethod {
347 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
348 write!(f, "{}", self.description())
349 }
350}
351
352impl Error for InvalidMethod {
353 fn description(&self) -> &str {
354 "invalid HTTP method"
355 }
356}