use prelude::*;
pub struct HttpParser;
impl Parsable<PathIp> for HttpParser {
fn parse<'a>(&mut self,
input: &'a [u8],
result: Option<&ParserResultVec>,
_: Option<&mut PathIp>)
-> IResult<&'a [u8], ParserResult> {
do_parse!(input,
result: alt!(
cond_reduce!(match result {
Some(vector) => match vector.last() {
Some(ref any) => if let Some(_) = any.downcast_ref::<TcpPacket>() {
true
} else {
false
},
_ => false, },
None => true, }, HttpPacket::parse_plain) |
apply!(HttpPacket::parse_encrypted, result)
) >>
(result)
)
}
}
impl fmt::Display for HttpParser {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "HTTP")
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum HttpPacket {
Request(HttpRequest),
Response(HttpResponse),
Any,
}
impl HttpPacket {
named!(parse_plain<&[u8], ParserResult>,
alt!(call!(HttpRequest::parse) | call!(HttpResponse::parse))
);
fn parse_encrypted<'a>(input: &'a [u8], result: Option<&ParserResultVec>) -> IResult<&'a [u8], ParserResult> {
expr_opt!(input,
match result {
Some(vector) => match (vector.last(), vector.iter().rev().nth(1)) {
(Some(ref any_tls), Some(ref any_tcp)) => {
match (any_tls.downcast_ref::<TlsPacket>(),
any_tcp.downcast_ref::<TcpPacket>()) {
(Some(_), Some(tcp)) if (tcp.header.source_port == 443 || tcp.header.dest_port == 443) => {
Some(Box::new(HttpPacket::Any))
}
_ => None,
}
},
_ => None, },
_ => None,
}
)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct HttpRequest {
pub request_method: HttpRequestMethod,
pub version: HttpVersion,
pub path: String,
pub headers: Vec<HttpHeader>,
}
impl HttpRequest {
named!(parse<&[u8], ParserResult>,
ws!(do_parse!(
method: alt!(
map!(tag!("GET"), |_| HttpRequestMethod::Get) |
map!(tag!("POST"), |_| HttpRequestMethod::Post) |
map!(tag!("HEAD"), |_| HttpRequestMethod::Head) |
map!(tag!("PUT"), |_| HttpRequestMethod::Put) |
map!(tag!("DELETE"), |_| HttpRequestMethod::Delete) |
map!(tag!("TRACE"), |_| HttpRequestMethod::Trace) |
map!(tag!("OPTIONS"), |_| HttpRequestMethod::Options) |
map!(tag!("CONNECT"), |_| HttpRequestMethod::Connect) |
map!(tag!("PATCH"), |_| HttpRequestMethod::Patch)
) >>
path: map_res!(take_until!(" "), str::from_utf8) >>
tag!("HTTP/") >>
version: call!(HttpVersion::parse) >>
headers: call!(HttpHeader::parse) >>
(Box::new(HttpPacket::Request(HttpRequest {
request_method: method,
path: path.to_owned(),
version: version,
headers: headers,
})))
))
);
}
#[derive(Debug, Eq, PartialEq)]
pub enum HttpRequestMethod {
Get,
Post,
Head,
Put,
Delete,
Trace,
Options,
Connect,
Patch,
}
#[derive(Debug, Eq, PartialEq)]
pub struct HttpVersion {
pub major: u8,
pub minor: u8,
}
impl HttpVersion {
named!(parse<&[u8], HttpVersion>,
do_parse!(
tuple: separated_pair!(map_res!(map_res!(digit, str::from_utf8), FromStr::from_str),
tag!("."),
map_res!(map_res!(digit, str::from_utf8), FromStr::from_str)) >>
(HttpVersion {
major: tuple.0,
minor: tuple.1,
})
)
);
}
#[derive(Debug, Eq, PartialEq)]
pub struct HttpHeader {
pub key: String,
pub value: String,
}
impl HttpHeader {
fn from_tuple(tuple: (&str, &str)) -> Self {
HttpHeader {
key: tuple.0.to_owned(),
value: tuple.1.to_owned(),
}
}
named!(parse<&[u8], Vec<HttpHeader> >,
do_parse!(
result: many_till!(map!(separated_pair!(
map_res!(ws!(take_until!(":")), str::from_utf8),
tag!(": "),
map_res!(take_until!("\r"), str::from_utf8)), HttpHeader::from_tuple),
tag!("\r\n\r\n")) >>
(result.0)
)
);
}
#[derive(Debug, Eq, PartialEq)]
pub struct HttpResponse {
pub version: HttpVersion,
pub code: u16,
pub reason: String,
pub headers: Vec<HttpHeader>,
}
impl HttpResponse {
named!(parse<&[u8], ParserResult>,
ws!(do_parse!(
tag!("HTTP/") >>
version: call!(HttpVersion::parse) >>
code: map_res!(map_res!(take_until!(" "), str::from_utf8), FromStr::from_str) >>
reason: map_res!(take_until!("\r"), str::from_utf8) >>
headers: call!(HttpHeader::parse) >>
(Box::new(HttpPacket::Response(HttpResponse {
version: version,
code: code,
reason: reason.to_owned(),
headers: headers,
})))
))
);
}