|
16 | 16 | //! ```
|
17 | 17 |
|
18 | 18 | use self::Inner::*;
|
| 19 | +use self::extension::{InlineExtension, AllocatedExtension}; |
19 | 20 |
|
20 | 21 | use std::convert::AsRef;
|
21 | 22 | use std::error::Error;
|
@@ -61,54 +62,11 @@ enum Inner {
|
61 | 62 | Connect,
|
62 | 63 | Patch,
|
63 | 64 | // If the extension is short enough, store it inline
|
64 |
| - ExtensionInline([u8; MAX_INLINE], u8), |
| 65 | + ExtensionInline(InlineExtension), |
65 | 66 | // Otherwise, allocate it
|
66 |
| - ExtensionAllocated(Box<[u8]>), |
| 67 | + ExtensionAllocated(AllocatedExtension), |
67 | 68 | }
|
68 | 69 |
|
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 | 70 |
|
113 | 71 | impl Method {
|
114 | 72 | /// GET
|
@@ -167,25 +125,21 @@ impl Method {
|
167 | 125 | _ => Method::extension_inline(src),
|
168 | 126 | },
|
169 | 127 | _ => {
|
170 |
| - if src.len() < MAX_INLINE { |
| 128 | + if src.len() < InlineExtension::MAX { |
171 | 129 | Method::extension_inline(src)
|
172 | 130 | } else {
|
173 |
| - let mut data: Vec<u8> = vec![0; src.len()]; |
| 131 | + let allocated = AllocatedExtension::new(src)?; |
174 | 132 |
|
175 |
| - write_checked(src, &mut data)?; |
176 |
| - |
177 |
| - Ok(Method(ExtensionAllocated(data.into_boxed_slice()))) |
| 133 | + Ok(Method(ExtensionAllocated(allocated))) |
178 | 134 | }
|
179 | 135 | }
|
180 | 136 | }
|
181 | 137 | }
|
182 | 138 |
|
183 | 139 | fn extension_inline(src: &[u8]) -> Result<Method, InvalidMethod> {
|
184 |
| - let mut data: [u8; MAX_INLINE] = Default::default(); |
185 |
| - |
186 |
| - write_checked(src, &mut data)?; |
| 140 | + let inline = InlineExtension::new(src)?; |
187 | 141 |
|
188 |
| - Ok(Method(ExtensionInline(data, src.len() as u8))) |
| 142 | + Ok(Method(ExtensionInline(inline))) |
189 | 143 | }
|
190 | 144 |
|
191 | 145 | /// Whether a method is considered "safe", meaning the request is
|
@@ -225,28 +179,12 @@ impl Method {
|
225 | 179 | Trace => "TRACE",
|
226 | 180 | Connect => "CONNECT",
|
227 | 181 | Patch => "PATCH",
|
228 |
| - ExtensionInline(ref data, len) => unsafe { |
229 |
| - str::from_utf8_unchecked(&data[..len as usize]) |
230 |
| - }, |
231 |
| - ExtensionAllocated(ref data) => unsafe { str::from_utf8_unchecked(data) }, |
| 182 | + ExtensionInline(ref inline) => inline.as_str(), |
| 183 | + ExtensionAllocated(ref allocated) => allocated.as_str(), |
232 | 184 | }
|
233 | 185 | }
|
234 | 186 | }
|
235 | 187 |
|
236 |
| -fn write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod> { |
237 |
| - for (i, &b) in src.iter().enumerate() { |
238 |
| - let b = METHOD_CHARS[b as usize]; |
239 |
| - |
240 |
| - if b == 0 { |
241 |
| - return Err(InvalidMethod::new()); |
242 |
| - } |
243 |
| - |
244 |
| - dst[i] = b; |
245 |
| - } |
246 |
| - |
247 |
| - Ok(()) |
248 |
| -} |
249 |
| - |
250 | 188 | impl AsRef<str> for Method {
|
251 | 189 | #[inline]
|
252 | 190 | fn as_ref(&self) -> &str {
|
@@ -371,6 +309,105 @@ impl fmt::Display for InvalidMethod {
|
371 | 309 |
|
372 | 310 | impl Error for InvalidMethod {}
|
373 | 311 |
|
| 312 | +mod extension { |
| 313 | + use super::InvalidMethod; |
| 314 | + use std::str; |
| 315 | + |
| 316 | + #[derive(Clone, PartialEq, Eq, Hash)] |
| 317 | + pub struct InlineExtension([u8; InlineExtension::MAX], u8); |
| 318 | + |
| 319 | + #[derive(Clone, PartialEq, Eq, Hash)] |
| 320 | + pub struct AllocatedExtension(Box<[u8]>); |
| 321 | + |
| 322 | + impl InlineExtension { |
| 323 | + // Method::from_bytes() assumes this is at least 7 |
| 324 | + pub const MAX: usize = 15; |
| 325 | + |
| 326 | + pub 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)) |
| 332 | + } |
| 333 | + |
| 334 | + pub fn as_str(&self) -> &str { |
| 335 | + let InlineExtension(ref data, len) = self; |
| 336 | + unsafe {str::from_utf8_unchecked(&data[..*len as usize])} |
| 337 | + } |
| 338 | + } |
| 339 | + |
| 340 | + impl AllocatedExtension { |
| 341 | + pub fn new(src: &[u8]) -> Result<AllocatedExtension, InvalidMethod> { |
| 342 | + let mut data: Vec<u8> = vec![0; src.len()]; |
| 343 | + |
| 344 | + write_checked(src, &mut data)?; |
| 345 | + |
| 346 | + Ok(AllocatedExtension(data.into_boxed_slice())) |
| 347 | + } |
| 348 | + |
| 349 | + pub fn as_str(&self) -> &str { |
| 350 | + unsafe {str::from_utf8_unchecked(&self.0)} |
| 351 | + } |
| 352 | + } |
| 353 | + |
| 354 | + // From the HTTP spec section 5.1.1, the HTTP method is case-sensitive and can |
| 355 | + // contain the following characters: |
| 356 | + // |
| 357 | + // ``` |
| 358 | + // method = token |
| 359 | + // token = 1*tchar |
| 360 | + // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / |
| 361 | + // "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA |
| 362 | + // ``` |
| 363 | + // |
| 364 | + // https://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01#Method |
| 365 | + // |
| 366 | + const METHOD_CHARS: [u8; 256] = [ |
| 367 | + // 0 1 2 3 4 5 6 7 8 9 |
| 368 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // x |
| 369 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 1x |
| 370 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 2x |
| 371 | + b'\0', b'\0', b'\0', b'!', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 3x |
| 372 | + b'\0', b'\0', b'*', b'+', b'\0', b'-', b'.', b'\0', b'0', b'1', // 4x |
| 373 | + b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'\0', b'\0', // 5x |
| 374 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'A', b'B', b'C', b'D', b'E', // 6x |
| 375 | + b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', // 7x |
| 376 | + b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', // 8x |
| 377 | + b'Z', b'\0', b'\0', b'\0', b'^', b'_', b'`', b'a', b'b', b'c', // 9x |
| 378 | + b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x |
| 379 | + b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x |
| 380 | + b'x', b'y', b'z', b'\0', b'|', b'\0', b'~', b'\0', b'\0', b'\0', // 12x |
| 381 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 13x |
| 382 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 14x |
| 383 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 15x |
| 384 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 16x |
| 385 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 17x |
| 386 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 18x |
| 387 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 19x |
| 388 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 20x |
| 389 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 21x |
| 390 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 22x |
| 391 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 23x |
| 392 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 24x |
| 393 | + b'\0', b'\0', b'\0', b'\0', b'\0', b'\0' // 25x |
| 394 | + ]; |
| 395 | + |
| 396 | + fn write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod> { |
| 397 | + for (i, &b) in src.iter().enumerate() { |
| 398 | + let b = METHOD_CHARS[b as usize]; |
| 399 | + |
| 400 | + if b == 0 { |
| 401 | + return Err(InvalidMethod::new()); |
| 402 | + } |
| 403 | + |
| 404 | + dst[i] = b; |
| 405 | + } |
| 406 | + |
| 407 | + Ok(()) |
| 408 | + } |
| 409 | +} |
| 410 | + |
374 | 411 | #[cfg(test)]
|
375 | 412 | mod test {
|
376 | 413 | use super::*;
|
|
0 commit comments