diff --git a/src/http.rs b/src/http.rs index a57d9526..515483ac 100644 --- a/src/http.rs +++ b/src/http.rs @@ -262,190 +262,69 @@ pub fn is_token(b: u8) -> bool { } } -// omg -enum MethodState { - MsStart, - MsG, - MsGE, - MsGET, - MsP, - MsPO, - MsPOS, - MsPOST, - MsPU, - MsPUT, - MsPA, - MsPAT, - MsPATC, - MsPATCH, - MsH, - MsHE, - MsHEA, - MsHEAD, - MsD, - MsDE, - MsDEL, - MsDELE, - MsDELET, - MsDELETE, - MsT, - MsTR, - MsTRA, - MsTRAC, - MsTRACE, - MsO, - MsOP, - MsOPT, - MsOPTI, - MsOPTIO, - MsOPTION, - MsOPTIONS, - MsC, - MsCO, - MsCON, - MsCONN, - MsCONNE, - MsCONNEC, - MsCONNECT, - MsExt -} +/// Read bytes from `stream` into `buf` until a space is encountered. +/// Returns `Ok(true)` if we read until a space, +/// `Ok(false)` if we got to the end of `buf` without encountering a space, +/// otherwise returns any error encountered reading the stream. +/// +/// The remaining contents of `buf` are left untouched. +fn read_until_space(stream: &mut R, buf: &mut [u8]) -> HttpResult { + use std::io::BufWriter; + let mut bufwrt = BufWriter::new(buf); -// omg -impl MethodState { - fn as_slice(&self) -> &str { - match *self { - MsG => "G", - MsGE => "GE", - MsGET => "GET", - MsP => "P", - MsPO => "PO", - MsPOS => "POS", - MsPOST => "POST", - MsPU => "PU", - MsPUT => "PUT", - MsPA => "PA", - MsPAT => "PAT", - MsPATC => "PATC", - MsPATCH => "PATCH", - MsH => "H", - MsHE => "HE", - MsHEA => "HEA", - MsHEAD => "HEAD", - MsD => "D", - MsDE => "DE", - MsDEL => "DEL", - MsDELE => "DELE", - MsDELET => "DELET", - MsDELETE => "DELETE", - MsT => "T", - MsTR => "TR", - MsTRA => "TRA", - MsTRAC => "TRAC", - MsTRACE => "TRACE", - MsO => "O", - MsOP => "OP", - MsOPT => "OPT", - MsOPTI => "OPTI", - MsOPTIO => "OPTIO", - MsOPTION => "OPTION", - MsOPTIONS => "OPTIONS", - MsC => "C", - MsCO => "CO", - MsCON => "CON", - MsCONN => "CONN", - MsCONNE => "CONNE", - MsCONNEC => "CONNEC", - MsCONNECT => "CONNECT", - MsStart | MsExt => unreachable!() - } + loop { + let byte = try_io!(stream.read_byte()); + + if byte == SP { + break + // Read to end but there's still more + } else if bufwrt.write_u8(byte).is_err() { + return Ok(false) + } } + + Ok(true) } /// Read a `Method` from a raw stream, such as `GET`. +/// ### Note: +/// Extension methods are only parsed to 16 characters. pub fn read_method(stream: &mut R) -> HttpResult { - let mut s = String::new(); - let mut state = MsStart; + let mut buf = [SP, ..16]; - // omg - loop { - match (state, try_io!(stream.read_byte())) { - (MsStart, b'G') => state = MsG, - (MsStart, b'P') => state = MsP, - (MsStart, b'H') => state = MsH, - (MsStart, b'O') => state = MsO, - (MsStart, b'T') => state = MsT, - (MsStart, b'C') => state = MsC, - (MsStart, b'D') => state = MsD, - (MsStart, b@b'A'...b'Z') => { - state = MsExt; - s.push(b as char) - }, - - (MsG, b'E') => state = MsGE, - (MsGE, b'T') => state = MsGET, - - (MsP, b'O') => state = MsPO, - (MsPO, b'S') => state = MsPOS, - (MsPOS, b'T') => state = MsPOST, - - (MsP, b'U') => state = MsPU, - (MsPU, b'T') => state = MsPUT, - - (MsP, b'A') => state = MsPA, - (MsPA, b'T') => state = MsPAT, - (MsPAT, b'C') => state = MsPATC, - (MsPATC, b'H') => state = MsPATCH, - - (MsH, b'E') => state = MsHE, - (MsHE, b'A') => state = MsHEA, - (MsHEA, b'D') => state = MsHEAD, - - (MsO, b'P') => state = MsOP, - (MsOP, b'T') => state = MsOPT, - (MsOPT, b'I') => state = MsOPTI, - (MsOPTI, b'O') => state = MsOPTIO, - (MsOPTIO, b'N') => state = MsOPTION, - (MsOPTION, b'S') => state = MsOPTIONS, - - (MsT, b'R') => state = MsTR, - (MsTR, b'A') => state = MsTRA, - (MsTRA, b'C') => state = MsTRAC, - (MsTRAC, b'E') => state = MsTRACE, - - (MsC, b'O') => state = MsCO, - (MsCO, b'N') => state = MsCON, - (MsCON, b'N') => state = MsCONN, - (MsCONN, b'E') => state = MsCONNE, - (MsCONNE, b'C') => state = MsCONNEC, - (MsCONNEC, b'T') => state = MsCONNECT, - - (MsD, b'E') => state = MsDE, - (MsDE, b'L') => state = MsDEL, - (MsDEL, b'E') => state = MsDELE, - (MsDELE, b'T') => state = MsDELET, - (MsDELET, b'E') => state = MsDELETE, - - (MsExt, b@b'A'...b'Z') => s.push(b as char), - - (_, b@b'A'...b'Z') => { - s = state.as_slice().to_string(); - s.push(b as char); - }, - - (MsGET, SP) => return Ok(method::Get), - (MsPOST, SP) => return Ok(method::Post), - (MsPUT, SP) => return Ok(method::Put), - (MsPATCH, SP) => return Ok(method::Patch), - (MsHEAD, SP) => return Ok(method::Head), - (MsDELETE, SP) => return Ok(method::Delete), - (MsTRACE, SP) => return Ok(method::Trace), - (MsOPTIONS, SP) => return Ok(method::Options), - (MsCONNECT, SP) => return Ok(method::Connect), - (MsExt, SP) => return Ok(method::Extension(s)), - - (_, _) => return Err(HttpMethodError) - } + if !try!(read_until_space(stream, &mut buf)){ + return Err(HttpMethodError); } + + debug!("method: {}", buf[].to_ascii()); + + let maybe_method = match buf[0..7] { + b"GET " => Some(method::Get), + b"PUT " => Some(method::Put), + b"POST " => Some(method::Post), + b"HEAD " => Some(method::Head), + b"PATCH " => Some(method::Patch), + b"TRACE " => Some(method::Trace), + b"DELETE " => Some(method::Delete), + b"CONNECT" => Some(method::Connect), + b"OPTIONS" => Some(method::Options), + _ => None, + }; + + match (maybe_method, buf[]) { + (Some(method), _) => Ok(method), + (None, ext) if is_valid_method(buf) => { + use std::str::raw; + // We already checked that the buffer is ASCII + Ok(method::Extension(unsafe { raw::from_utf8(ext) }.trim().into_string())) + }, + _ => Err(HttpMethodError) + } +} + +fn is_valid_method(buf: &[u8]) -> bool { + use std::char; + buf.iter().all(|&ch| char::is_uppercase(ch as char) || ch == SP) } /// Read a `RequestUri` from a raw stream.