Skip to content

Commit 493b38f

Browse files
sbosnickseanmonstar
authored andcommitted
Refactor Method internals
Extract the inner types for ExtensionAllocated and ExtensionInline into a separate extension module that has the supporting functions as non-public elements. This refactoring moves the use of "unsafe" into this new "extension" module and provides a safe wrappers around the two uses of "unsafe".
1 parent f769e1e commit 493b38f

File tree

1 file changed

+109
-72
lines changed

1 file changed

+109
-72
lines changed

src/method.rs

Lines changed: 109 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//! ```
1717
1818
use self::Inner::*;
19+
use self::extension::{InlineExtension, AllocatedExtension};
1920

2021
use std::convert::AsRef;
2122
use std::error::Error;
@@ -61,54 +62,11 @@ enum Inner {
6162
Connect,
6263
Patch,
6364
// If the extension is short enough, store it inline
64-
ExtensionInline([u8; MAX_INLINE], u8),
65+
ExtensionInline(InlineExtension),
6566
// Otherwise, allocate it
66-
ExtensionAllocated(Box<[u8]>),
67+
ExtensionAllocated(AllocatedExtension),
6768
}
6869

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-
];
11270

11371
impl Method {
11472
/// GET
@@ -167,25 +125,21 @@ impl Method {
167125
_ => Method::extension_inline(src),
168126
},
169127
_ => {
170-
if src.len() < MAX_INLINE {
128+
if src.len() < InlineExtension::MAX {
171129
Method::extension_inline(src)
172130
} else {
173-
let mut data: Vec<u8> = vec![0; src.len()];
131+
let allocated = AllocatedExtension::new(src)?;
174132

175-
write_checked(src, &mut data)?;
176-
177-
Ok(Method(ExtensionAllocated(data.into_boxed_slice())))
133+
Ok(Method(ExtensionAllocated(allocated)))
178134
}
179135
}
180136
}
181137
}
182138

183139
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)?;
187141

188-
Ok(Method(ExtensionInline(data, src.len() as u8)))
142+
Ok(Method(ExtensionInline(inline)))
189143
}
190144

191145
/// Whether a method is considered "safe", meaning the request is
@@ -225,28 +179,12 @@ impl Method {
225179
Trace => "TRACE",
226180
Connect => "CONNECT",
227181
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(),
232184
}
233185
}
234186
}
235187

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-
250188
impl AsRef<str> for Method {
251189
#[inline]
252190
fn as_ref(&self) -> &str {
@@ -371,6 +309,105 @@ impl fmt::Display for InvalidMethod {
371309

372310
impl Error for InvalidMethod {}
373311

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+
374411
#[cfg(test)]
375412
mod test {
376413
use super::*;

0 commit comments

Comments
 (0)