|
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