#![cfg_attr(test, deny(warnings))]
#![deny(missing_docs)]
use std::{str, slice};
use iter::Bytes;
mod iter;
macro_rules! next {
($bytes:ident) => ({
match $bytes.next() {
Some(b) => b,
None => return Ok(Status::Partial)
}
})
}
macro_rules! expect {
($bytes:ident.next() == $pat:pat => $ret:expr) => {
match next!($bytes) {
v@$pat => v,
_ => return $ret
}
}
}
macro_rules! complete {
($e:expr) => {
match try!($e) {
Status::Complete(v) => v,
Status::Partial => return Ok(Status::Partial)
}
}
}
#[inline]
fn shrink<T>(slice: &mut &mut [T], len: usize) {
debug_assert!(slice.len() >= len);
let ptr = slice.as_mut_ptr();
*slice = unsafe { slice::from_raw_parts_mut(ptr, len) };
}
#[inline]
fn is_token(b: u8) -> bool {
b > 0x1F && b < 0x7F
}
macro_rules! space {
($bytes:ident or $err:expr) => ({
expect!($bytes.next() == b' ' => Err($err));
$bytes.slice();
})
}
macro_rules! newline {
($bytes:ident) => ({
match next!($bytes) {
b'\r' => {
expect!($bytes.next() == b'\n' => Err(Error::NewLine));
$bytes.slice();
},
b'\n' => {
$bytes.slice();
},
_ => return Err(Error::NewLine)
}
})
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Error {
HeaderName,
HeaderValue,
NewLine,
Status,
Token,
TooManyHeaders,
Version
}
pub type Result<T> = ::std::result::Result<Status<T>, Error>;
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Status<T> {
Complete(T),
Partial
}
impl<T> Status<T> {
#[inline]
pub fn is_complete(&self) -> bool {
match *self {
Status::Complete(..) => true,
Status::Partial => false
}
}
#[inline]
pub fn is_partial(&self) -> bool {
match *self {
Status::Complete(..) => false,
Status::Partial => true
}
}
#[inline]
pub fn unwrap(self) -> T {
match self {
Status::Complete(t) => t,
Status::Partial => panic!("Tried to unwrap Status::Partial")
}
}
}
pub struct Request<'headers, 'buf: 'headers> {
pub method: Option<&'buf str>,
pub path: Option<&'buf str>,
pub version: Option<u8>,
pub headers: &'headers mut [Header<'buf>]
}
impl<'h, 'b> Request<'h, 'b> {
#[inline]
pub fn new(headers: &'h mut [Header<'b>]) -> Request<'h, 'b> {
Request {
method: None,
path: None,
version: None,
headers: headers,
}
}
pub fn parse(&mut self, buf: &'b [u8]) -> Result<usize> {
let orig_len = buf.len();
let mut bytes = Bytes::new(buf);
complete!(skip_empty_lines(&mut bytes));
self.method = Some(complete!(parse_token(&mut bytes)));
self.path = Some(complete!(parse_token(&mut bytes)));
self.version = Some(complete!(parse_version(&mut bytes)));
newline!(bytes);
let len = orig_len - bytes.len();
let headers_len = complete!(parse_headers_iter(&mut self.headers, &mut bytes));
Ok(Status::Complete(len + headers_len))
}
}
#[inline]
fn skip_empty_lines(bytes: &mut Bytes) -> Result<()> {
loop {
let b = bytes.peek();
match b {
Some(b'\r') => {
bytes.bump();
expect!(bytes.next() == b'\n' => Err(Error::NewLine));
},
Some(b'\n') => {
bytes.bump();
},
Some(..) => {
bytes.slice();
return Ok(Status::Complete(()));
},
None => return Ok(Status::Partial)
}
}
}
pub struct Response<'headers, 'buf: 'headers> {
pub version: Option<u8>,
pub code: Option<u16>,
pub reason: Option<&'buf str>,
pub headers: &'headers mut [Header<'buf>]
}
impl<'h, 'b> Response<'h, 'b> {
#[inline]
pub fn new(headers: &'h mut [Header<'b>]) -> Response<'h, 'b> {
Response {
version: None,
code: None,
reason: None,
headers: headers,
}
}
pub fn parse(&mut self, buf: &'b [u8]) -> Result<usize> {
let orig_len = buf.len();
let mut bytes = Bytes::new(buf);
self.version = Some(complete!(parse_version(&mut bytes)));
space!(bytes or Error::Version);
self.code = Some(complete!(parse_code(&mut bytes)));
space!(bytes or Error::Status);
self.reason = Some(complete!(parse_reason(&mut bytes)));
let len = orig_len - bytes.len();
let headers_len = complete!(parse_headers_iter(&mut self.headers, &mut bytes));
Ok(Status::Complete(len + headers_len))
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Header<'a> {
pub name: &'a str,
pub value: &'a [u8],
}
pub const EMPTY_HEADER: Header<'static> = Header { name: "", value: b"" };
#[inline]
fn parse_version(bytes: &mut Bytes) -> Result<u8> {
expect!(bytes.next() == b'H' => Err(Error::Version));
expect!(bytes.next() == b'T' => Err(Error::Version));
expect!(bytes.next() == b'T' => Err(Error::Version));
expect!(bytes.next() == b'P' => Err(Error::Version));
expect!(bytes.next() == b'/' => Err(Error::Version));
expect!(bytes.next() == b'1' => Err(Error::Version));
expect!(bytes.next() == b'.' => Err(Error::Version));
let v = match next!(bytes) {
b'0' => 0,
b'1' => 1,
_ => return Err(Error::Version)
};
Ok(Status::Complete(v))
}
#[inline]
fn parse_reason<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> {
loop {
let b = next!(bytes);
if b == b'\r' {
expect!(bytes.next() == b'\n' => Err(Error::Status));
return Ok(Status::Complete(unsafe {
str::from_utf8_unchecked(bytes.slice_skip(2))
}));
} else if b == b'\n' {
return Ok(Status::Complete(unsafe {
str::from_utf8_unchecked(bytes.slice_skip(1))
}));
} else if !((b >= 0x20 && b <= 0x7E) || b == b'\t') {
return Err(Error::Status);
}
}
}
#[inline]
fn parse_token<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> {
loop {
let b = next!(bytes);
if b == b' ' || b == b'\r' || b == b'\n' {
return Ok(Status::Complete(unsafe {
str::from_utf8_unchecked(bytes.slice_skip(1))
}));
} else if !is_token(b) {
return Err(Error::Token);
}
}
}
#[inline]
fn parse_code(bytes: &mut Bytes) -> Result<u16> {
let hundreds = expect!(bytes.next() == b'0'...b'9' => Err(Error::Status));
let tens = expect!(bytes.next() == b'0'...b'9' => Err(Error::Status));
let ones = expect!(bytes.next() == b'0'...b'9' => Err(Error::Status));
Ok(Status::Complete((hundreds - b'0') as u16 * 100 +
(tens - b'0') as u16 * 10 +
(ones - b'0') as u16))
}
pub fn parse_headers<'b: 'h, 'h>(src: &'b [u8], mut dst: &'h mut [Header<'b>])
-> Result<(usize, &'h [Header<'b>])> {
let mut iter = Bytes::new(src);
let pos = complete!(parse_headers_iter(&mut dst, &mut iter));
Ok(Status::Complete((pos, dst)))
}
#[inline]
fn parse_headers_iter<'a, 'b>(headers: &mut &mut [Header<'a>], bytes: &'b mut Bytes<'a>)
-> Result<usize> {
let mut num_headers: usize = 0;
let mut count: usize = 0;
let mut result = Err(Error::TooManyHeaders);
{
let mut iter = headers.iter_mut();
'headers: loop {
let b = next!(bytes);
if b == b'\r' {
expect!(bytes.next() == b'\n' => Err(Error::NewLine));
result = Ok(Status::Complete(count + bytes.pos()));
break;
} else if b == b'\n' {
result = Ok(Status::Complete(count + bytes.pos()));
break;
} else if b == b':' || !is_token(b) {
return Err(Error::HeaderName);
}
let header = match iter.next() {
Some(header) => header,
None => break 'headers
};
num_headers += 1;
loop {
let b = next!(bytes);
if b == b':' {
count += bytes.pos();
header.name = unsafe {
str::from_utf8_unchecked(bytes.slice_skip(1))
};
break;
} else if !is_token(b) {
return Err(Error::HeaderName);
}
}
let mut b;
'value: loop {
loop {
b = next!(bytes);
if b == b' ' || b == b'\t' {
count += bytes.pos();
bytes.slice();
continue;
} else {
if !is_token(b) {
if (b < 0o40 && b != 0o11) || b == 0o177 {
break 'value;
}
}
break;
}
}
macro_rules! check {
($bytes:ident, $i:ident) => ({
b = $bytes.$i();
if !is_token(b) {
if (b < 0o40 && b != 0o11) || b == 0o177 {
break 'value;
}
}
});
($bytes:ident) => ({
check!($bytes, _0);
check!($bytes, _1);
check!($bytes, _2);
check!($bytes, _3);
check!($bytes, _4);
check!($bytes, _5);
check!($bytes, _6);
check!($bytes, _7);
})
}
while let Some(mut bytes8) = bytes.next_8() {
check!(bytes8);
}
loop {
b = next!(bytes);
if !is_token(b) {
if (b < 0o40 && b != 0o11) || b == 0o177 {
break 'value;
}
}
}
}
if b == b'\r' {
expect!(bytes.next() == b'\n' => Err(Error::HeaderValue));
count += bytes.pos();
header.value = bytes.slice_skip(2);
} else if b == b'\n' {
count += bytes.pos();
header.value = bytes.slice_skip(1);
} else {
return Err(Error::HeaderValue);
}
}
}
shrink(headers, num_headers);
result
}
#[cfg(test)]
mod tests {
use super::{Request, Response, Status, EMPTY_HEADER, shrink};
const NUM_OF_HEADERS: usize = 4;
#[test]
fn test_shrink() {
let mut arr = [EMPTY_HEADER; 16];
{
let slice = &mut &mut arr[..];
assert_eq!(slice.len(), 16);
shrink(slice, 4);
assert_eq!(slice.len(), 4);
}
assert_eq!(arr.len(), 16);
}
macro_rules! req {
($name:ident, $buf:expr, $closure:expr) => (
req! {$name, $buf, Ok(Status::Complete($buf.len())), $closure }
);
($name:ident, $buf:expr, $len:expr, $closure:expr) => (
#[test]
fn $name() {
let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
let mut req = Request::new(&mut headers[..]);
let closure: Box<Fn(Request)> = Box::new($closure);
let status = req.parse($buf.as_ref());
assert_eq!(status, $len);
closure(req);
}
)
}
req! {
test_request_simple,
b"GET / HTTP/1.1\r\n\r\n",
|req| {
assert_eq!(req.method.unwrap(), "GET");
assert_eq!(req.path.unwrap(), "/");
assert_eq!(req.version.unwrap(), 1);
assert_eq!(req.headers.len(), 0);
}
}
req! {
test_request_headers,
b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n\r\n",
|req| {
assert_eq!(req.method.unwrap(), "GET");
assert_eq!(req.path.unwrap(), "/");
assert_eq!(req.version.unwrap(), 1);
assert_eq!(req.headers.len(), 2);
assert_eq!(req.headers[0].name, "Host");
assert_eq!(req.headers[0].value, b"foo.com");
assert_eq!(req.headers[1].name, "Cookie");
assert_eq!(req.headers[1].value, b"");
}
}
req! {
test_request_headers_max,
b"GET / HTTP/1.1\r\nA: A\r\nB: B\r\nC: C\r\nD: D\r\n\r\n",
|req| {
assert_eq!(req.headers.len(), NUM_OF_HEADERS);
}
}
req! {
test_request_multibyte,
b"GET / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: \xe3\x81\xb2\xe3/1.0\r\n\r\n",
|req| {
assert_eq!(req.method.unwrap(), "GET");
assert_eq!(req.path.unwrap(), "/");
assert_eq!(req.version.unwrap(), 1);
assert_eq!(req.headers[0].name, "Host");
assert_eq!(req.headers[0].value, b"foo.com");
assert_eq!(req.headers[1].name, "User-Agent");
assert_eq!(req.headers[1].value, b"\xe3\x81\xb2\xe3/1.0");
}
}
req! {
test_request_partial,
b"GET / HTTP/1.1\r\n\r", Ok(Status::Partial),
|_req| {}
}
req! {
test_request_newlines,
b"GET / HTTP/1.1\nHost: foo.bar\n\n",
|_| {}
}
req! {
test_request_empty_lines_prefix,
b"\r\n\r\nGET / HTTP/1.1\r\n\r\n",
|req| {
assert_eq!(req.method.unwrap(), "GET");
assert_eq!(req.path.unwrap(), "/");
assert_eq!(req.version.unwrap(), 1);
assert_eq!(req.headers.len(), 0);
}
}
req! {
test_request_empty_lines_prefix_lf_only,
b"\n\nGET / HTTP/1.1\n\n",
|req| {
assert_eq!(req.method.unwrap(), "GET");
assert_eq!(req.path.unwrap(), "/");
assert_eq!(req.version.unwrap(), 1);
assert_eq!(req.headers.len(), 0);
}
}
macro_rules! res {
($name:ident, $buf:expr, $closure:expr) => (
res! {$name, $buf, Ok(Status::Complete($buf.len())), $closure }
);
($name:ident, $buf:expr, $len:expr, $closure:expr) => (
#[test]
fn $name() {
let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
let mut res = Response::new(&mut headers[..]);
let closure: Box<Fn(Response)> = Box::new($closure);
let status = res.parse($buf.as_ref());
assert_eq!(status, $len);
closure(res);
}
)
}
res! {
test_response_simple,
b"HTTP/1.1 200 OK\r\n\r\n",
|res| {
assert_eq!(res.version.unwrap(), 1);
assert_eq!(res.code.unwrap(), 200);
assert_eq!(res.reason.unwrap(), "OK");
}
}
res! {
test_response_newlines,
b"HTTP/1.0 403 Forbidden\nServer: foo.bar\n\n",
|_| {}
}
res! {
test_response_reason_missing,
b"HTTP/1.1 200 \r\n\r\n",
|res| {
assert_eq!(res.version.unwrap(), 1);
assert_eq!(res.code.unwrap(), 200);
assert_eq!(res.reason.unwrap(), "");
}
}
res! {
test_response_reason_with_space_and_tab,
b"HTTP/1.1 101 Switching Protocols\t\r\n\r\n",
|res| {
assert_eq!(res.version.unwrap(), 1);
assert_eq!(res.code.unwrap(), 101);
assert_eq!(res.reason.unwrap(), "Switching Protocols\t");
}
}
static RESPONSE_REASON_WITH_OBS_TEXT_BYTE: &'static [u8] = b"HTTP/1.1 200 X\xFFZ\r\n\r\n";
res! {
test_response_reason_with_obsolete_text_byte,
RESPONSE_REASON_WITH_OBS_TEXT_BYTE,
Err(::Error::Status),
|_res| {}
}
res! {
test_response_reason_with_nul_byte,
b"HTTP/1.1 200 \x00\r\n\r\n",
Err(::Error::Status),
|_res| {}
}
res! {
test_response_version_missing_space,
b"HTTP/1.1",
Ok(Status::Partial),
|_res| {}
}
res! {
test_response_code_missing_space,
b"HTTP/1.1 200",
Ok(Status::Partial),
|_res| {}
}
}