use sozu_command::buffer::fixed::Buffer;
use buffer_queue::BufferQueue;
use nom::{HexDisplay,IResult,Offset,Err};
use url::Url;
use std::str;
use super::{BufferMove, LengthInformation, RRequestLine, Connection, Chunk, Host, HeaderValue, TransferEncodingValue,
Method, Version, Continue, Header, message_header, request_line, crlf,};
use protocol::http::AddedRequestHeader;
#[derive(Debug,Clone,PartialEq)]
pub enum RequestState {
Initial,
Error(Option<RRequestLine>, Option<Connection>, Option<Host>, Option<LengthInformation>, Option<Chunk>),
HasRequestLine(RRequestLine, Connection),
HasHost(RRequestLine, Connection, Host),
HasLength(RRequestLine, Connection, LengthInformation),
HasHostAndLength(RRequestLine, Connection, Host, LengthInformation),
Request(RRequestLine, Connection, Host),
RequestWithBody(RRequestLine, Connection, Host, usize),
RequestWithBodyChunks(RRequestLine, Connection, Host, Chunk),
}
impl RequestState {
pub fn into_error(self) -> RequestState {
match self {
RequestState::Initial => RequestState::Error(None, None, None, None, None),
RequestState::HasRequestLine(rl, conn) => RequestState::Error(Some(rl), Some(conn), None, None, None),
RequestState::HasHost(rl, conn, host) => RequestState::Error(Some(rl), Some(conn), Some(host), None, None),
RequestState::HasHostAndLength(rl, conn, host, len) => RequestState::Error(Some(rl), Some(conn), Some(host), Some(len), None),
RequestState::Request(rl, conn, host) => RequestState::Error(Some(rl), Some(conn), Some(host), None, None),
RequestState::RequestWithBody(rl, conn, host, len) => RequestState::Error(Some(rl), Some(conn), Some(host), Some(LengthInformation::Length(len)), None),
RequestState::RequestWithBodyChunks(rl, conn, host, chunk) => RequestState::Error(Some(rl), Some(conn), Some(host), None, Some(chunk)),
err => err,
}
}
pub fn is_front_error(&self) -> bool {
if let RequestState::Error(_,_,_,_,_) = self {
true
} else {
false
}
}
pub fn get_sticky_session(&self) -> Option<&str> {
self.get_keep_alive().and_then(|con| con.sticky_session.as_ref()).map(|s| s.as_str())
}
pub fn has_host(&self) -> bool {
match *self {
RequestState::HasHost(_, _, _) |
RequestState::HasHostAndLength(_, _, _, _)|
RequestState::Request(_, _, _) |
RequestState::RequestWithBody(_, _, _, _) |
RequestState::RequestWithBodyChunks(_, _, _, _) => true,
_ => false
}
}
pub fn is_proxying(&self) -> bool {
match *self {
RequestState::Request(_, _, _) |
RequestState::RequestWithBody(_, _, _, _) |
RequestState::RequestWithBodyChunks(_, _, _, _) => true,
_ => false
}
}
pub fn is_head(&self) -> bool {
match *self {
RequestState::Request(ref rl, _, _) |
RequestState::RequestWithBody(ref rl, _, _, _) |
RequestState::RequestWithBodyChunks(ref rl, _, _, _) => {
rl.method == Method::Head
},
_ => false
}
}
pub fn get_host(&self) -> Option<&str> {
match *self {
RequestState::HasHost(_, _, ref host) |
RequestState::HasHostAndLength(_, _, ref host, _) |
RequestState::Request(_, _, ref host) |
RequestState::RequestWithBody(_, _, ref host, _) |
RequestState::RequestWithBodyChunks(_, _, ref host, _) => Some(host.as_str()),
RequestState::Error(_, _, ref host, _, _) => host.as_ref().map(|s| s.as_str()),
_ => None
}
}
pub fn get_uri(&self) -> Option<String> {
match *self {
RequestState::HasRequestLine(ref rl, _) |
RequestState::HasHost(ref rl, _, _) |
RequestState::HasHostAndLength(ref rl, _, _, _) |
RequestState::Request(ref rl , _, _) |
RequestState::RequestWithBody(ref rl, _, _, _) |
RequestState::RequestWithBodyChunks(ref rl, _, _, _) => Some(rl.uri.clone()),
RequestState::Error(ref rl, _, _, _, _) => rl.as_ref().map(|r| r.uri.clone()),
_ => None
}
}
pub fn get_request_line(&self) -> Option<&RRequestLine> {
match *self {
RequestState::HasRequestLine(ref rl, _) |
RequestState::HasHost(ref rl, _, _) |
RequestState::HasHostAndLength(ref rl, _, _, _) |
RequestState::Request(ref rl, _, _) |
RequestState::RequestWithBody(ref rl, _, _, _) |
RequestState::RequestWithBodyChunks(ref rl, _, _, _) => Some(rl),
RequestState::Error(ref rl, _, _, _, _) => rl.as_ref(),
_ => None
}
}
pub fn get_keep_alive(&self) -> Option<&Connection> {
match *self {
RequestState::HasRequestLine(_, ref conn) |
RequestState::HasHost(_, ref conn, _) |
RequestState::HasLength(_, ref conn, _) |
RequestState::HasHostAndLength(_, ref conn, _, _) |
RequestState::Request(_, ref conn, _) |
RequestState::RequestWithBody(_, ref conn, _, _) |
RequestState::RequestWithBodyChunks(_, ref conn, _, _) => Some(conn),
RequestState::Error(_, ref conn, _, _, _) => conn.as_ref(),
_ => None
}
}
pub fn get_mut_connection(&mut self) -> Option<&mut Connection> {
match *self {
RequestState::HasRequestLine(_, ref mut conn) |
RequestState::HasHost(_, ref mut conn, _) |
RequestState::HasLength(_, ref mut conn, _) |
RequestState::HasHostAndLength(_, ref mut conn, _, _) |
RequestState::Request(_, ref mut conn, _) |
RequestState::RequestWithBody(_, ref mut conn, _, _) |
RequestState::RequestWithBodyChunks(_, ref mut conn, _, _) => Some(conn),
_ => None
}
}
pub fn should_copy(&self, position: usize) -> Option<usize> {
match *self {
RequestState::RequestWithBody(_, _, _, l) => Some(position + l),
RequestState::Request(_, _, _) => Some(position),
_ => None
}
}
pub fn should_keep_alive(&self) -> bool {
let rl = self.get_request_line();
let version = rl.as_ref().map(|rl| rl.version);
let conn = self.get_keep_alive();
match (version, conn.map(|c| c.keep_alive)) {
(_, Some(Some(true))) => true,
(_, Some(Some(false))) => false,
(Some(Version::V10), _) => false,
(Some(Version::V11), _) => true,
(_, _) => false,
}
}
pub fn should_chunk(&self) -> bool {
if let RequestState::RequestWithBodyChunks(_, _, _, _) = *self {
true
} else {
false
}
}
}
pub fn default_request_result<O>(state: RequestState, res: IResult<&[u8], O>) -> (BufferMove, RequestState) {
match res {
Err(Err::Error(_)) | Err(Err::Failure(_)) => (BufferMove::None, state.into_error()),
Err(Err::Incomplete(_)) => (BufferMove::None, state),
_ => unreachable!()
}
}
pub fn validate_request_header(mut state: RequestState, header: &Header, sticky_name: &str) -> RequestState {
match header.value() {
HeaderValue::Host(host) => {
match state {
RequestState::HasRequestLine(rl, conn) => RequestState::HasHost(rl, conn, host),
RequestState::HasLength(rl, conn, l) => RequestState::HasHostAndLength(rl, conn, host, l),
s => s.into_error()
}
},
HeaderValue::ContentLength(sz) => {
match state {
RequestState::HasRequestLine(rl, conn) => RequestState::HasLength(rl, conn, LengthInformation::Length(sz)),
RequestState::HasHost(rl, conn, host) => RequestState::HasHostAndLength(rl, conn, host, LengthInformation::Length(sz)),
s => s.into_error()
}
},
HeaderValue::Encoding(TransferEncodingValue::Chunked) => {
match state {
RequestState::HasRequestLine(rl, conn) => RequestState::HasLength(rl, conn, LengthInformation::Chunked),
RequestState::HasHost(rl, conn, host) => RequestState::HasHostAndLength(rl, conn, host, LengthInformation::Chunked),
RequestState::HasHostAndLength(rl, conn, host,
LengthInformation::Length(_)) => RequestState::HasHostAndLength(rl, conn, host, LengthInformation::Chunked),
s => s.into_error()
}
},
HeaderValue::Connection(c) => {
if state.get_mut_connection().map(|conn| {
if c.has_close {
conn.keep_alive = Some(false);
}
if c.has_keep_alive {
conn.keep_alive = Some(true);
}
if c.has_upgrade {
conn.has_upgrade = true;
}
}).is_some() {
state
} else {
state.into_error()
}
},
HeaderValue::ExpectContinue => {
if state.get_mut_connection().map(|conn| {
conn.continues = Continue::Expects(0);
}).is_some() {
state
} else {
state.into_error()
}
}
HeaderValue::Encoding(_) => state,
HeaderValue::Forwarded(value) => {
state.get_mut_connection().map(|conn| {
conn.forwarded.forwarded = String::from_utf8(value.to_vec()).ok();
});
state
},
HeaderValue::XForwardedFor(value) => {
state.get_mut_connection().map(|conn| {
conn.forwarded.x_for = String::from_utf8(value.to_vec()).ok();
});
state
},
HeaderValue::XForwardedPort => {
state.get_mut_connection().map(|conn| {
conn.forwarded.x_port = true;
});
state
}
HeaderValue::XForwardedProto => {
state.get_mut_connection().map(|conn| {
conn.forwarded.x_proto = true;
});
state
},
HeaderValue::Other(_,_) => state,
HeaderValue::Upgrade(s) => {
let mut st = state;
st.get_mut_connection().map(|conn| conn.upgrade = Some(str::from_utf8(s).expect("should be ascii").to_string()));
st
},
HeaderValue::Cookie(cookies) => {
let sticky_session_header = cookies.into_iter().find(|ref cookie| &cookie.name[..] == sticky_name.as_bytes());
if let Some(sticky_session) = sticky_session_header {
let mut st = state;
st.get_mut_connection().map(|conn| conn.sticky_session = str::from_utf8(sticky_session.value).map(|s| s.to_string()).ok());
return st;
}
state
},
HeaderValue::Error => state.into_error()
}
}
pub fn parse_header<'a>(buf: &'a mut Buffer, state: RequestState, sticky_name: &str) -> IResult<&'a [u8], RequestState> {
match message_header(buf.data()) {
Ok((i, header)) => Ok((i, validate_request_header(state, &header, sticky_name))),
Err(e) => Err(e),
}
}
pub fn parse_request(state: RequestState, buf: &[u8], sticky_name: &str) -> (BufferMove, RequestState) {
match state {
RequestState::Initial => {
match request_line(buf) {
Ok((i, r)) => {
if let Some(rl) = RRequestLine::from_request_line(r) {
let conn = Connection::new();
if rl.uri.len() > 0 && rl.uri.as_bytes()[0] != b'/' {
if let Some(host) = Url::parse(&rl.uri).ok().and_then(|u| u.host_str().map(|s| s.to_string())) {
(BufferMove::Advance(buf.offset(i)), RequestState::HasHost(rl, conn, host))
} else {
(BufferMove::None, (RequestState::Initial).into_error())
}
} else {
(BufferMove::Advance(buf.offset(i)), RequestState::HasRequestLine(rl, conn))
}
} else {
(BufferMove::None, (RequestState::Initial).into_error())
}
},
res => default_request_result(state, res)
}
},
RequestState::HasRequestLine(rl, conn) => {
match message_header(buf) {
Ok((i, header)) => {
let mv = if header.should_delete(&conn, sticky_name) {
BufferMove::Delete(buf.offset(i))
} else if header.must_mutate() {
BufferMove::Multiple(header.mutate_header(buf, buf.offset(i), sticky_name))
} else {
BufferMove::Advance(buf.offset(i))
};
(mv, validate_request_header(RequestState::HasRequestLine(rl, conn), &header, sticky_name))
},
res => default_request_result(RequestState::HasRequestLine(rl, conn), res)
}
},
RequestState::HasHost(rl, conn, h) => {
match message_header(buf) {
Ok((i, header)) => {
let mv = if header.should_delete(&conn, sticky_name) {
BufferMove::Delete(buf.offset(i))
} else if header.must_mutate() {
BufferMove::Multiple(header.mutate_header(buf, buf.offset(i), sticky_name))
} else {
BufferMove::Advance(buf.offset(i))
};
(mv, validate_request_header(RequestState::HasHost(rl, conn, h), &header, sticky_name))
},
Err(Err::Incomplete(_)) => (BufferMove::None, RequestState::HasHost(rl, conn, h)),
Err(_) => {
match crlf(buf) {
Ok((i, _)) => {
(BufferMove::Advance(buf.offset(i)), RequestState::Request(rl, conn, h))
},
res => {
default_request_result(RequestState::HasHost(rl, conn, h), res)
}
}
}
}
},
RequestState::HasLength(rl, conn, l) => {
match message_header(buf) {
Ok((i, header)) => {
let mv = if header.should_delete(&conn, sticky_name) {
BufferMove::Delete(buf.offset(i))
} else if header.must_mutate() {
BufferMove::Multiple(header.mutate_header(buf, buf.offset(i), sticky_name))
} else {
BufferMove::Advance(buf.offset(i))
};
(mv, validate_request_header(RequestState::HasLength(rl, conn, l), &header, sticky_name))
},
res => default_request_result(RequestState::HasLength(rl, conn, l), res)
}
},
RequestState::HasHostAndLength(rl, conn, h, l) => {
match message_header(buf) {
Ok((i, header)) => {
let mv = if header.should_delete(&conn, sticky_name) {
BufferMove::Delete(buf.offset(i))
} else if header.must_mutate() {
BufferMove::Multiple(header.mutate_header(buf, buf.offset(i), sticky_name))
} else {
BufferMove::Advance(buf.offset(i))
};
(mv, validate_request_header(RequestState::HasHostAndLength(rl, conn, h, l), &header, sticky_name))
},
Err(Err::Incomplete(_)) => (BufferMove::None, RequestState::HasHostAndLength(rl, conn, h, l)),
Err(_) => {
match crlf(buf) {
Ok((i, _)) => {
debug!("PARSER\theaders parsed, stopping");
match l {
LengthInformation::Chunked => (BufferMove::Advance(buf.offset(i)), RequestState::RequestWithBodyChunks(rl, conn, h, Chunk::Initial)),
LengthInformation::Length(sz) => (BufferMove::Advance(buf.offset(i)), RequestState::RequestWithBody(rl, conn, h, sz)),
}
},
res => {
error!("PARSER\tHasHostAndLength could not parse header for input:\n{}\n", buf.to_hex(16));
default_request_result(RequestState::HasHostAndLength(rl, conn, h, l), res)
}
}
}
}
},
RequestState::RequestWithBodyChunks(rl, conn, h, ch) => {
let (advance, chunk_state) = ch.parse(buf);
(advance, RequestState::RequestWithBodyChunks(rl, conn, h, chunk_state))
},
_ => {
error!("PARSER\tunimplemented state: {:?}", state);
(BufferMove::None, state.into_error())
}
}
}
pub fn parse_request_until_stop(mut current_state: RequestState, mut header_end: Option<usize>,
buf: &mut BufferQueue, added_req_header: Option<&AddedRequestHeader>, sticky_name: &str)
-> (RequestState, Option<usize>) {
loop {
let (mv, new_state) = parse_request(current_state, buf.unparsed_data(), sticky_name);
current_state = new_state;
match mv {
BufferMove::Advance(sz) => {
assert!(sz != 0, "buffer move should not be 0");
buf.consume_parsed_data(sz);
if header_end.is_none() {
match current_state {
RequestState::Request(_,ref conn,_) |
RequestState::RequestWithBodyChunks(_,ref conn,_,Chunk::Initial) => {
header_end = Some(buf.start_parsing_position);
if let Some(added) = added_req_header {
let s = added.added_request_header(
&conn.forwarded);
buf.insert_output(s.into_bytes());
}
buf.slice_output(sz);
},
RequestState::RequestWithBody(_,ref mut conn,_,content_length) => {
header_end = Some(buf.start_parsing_position);
if let Some(added) = added_req_header {
let s = added.added_request_header(
&conn.forwarded);
buf.insert_output(s.into_bytes());
}
if conn.continues == Continue::None {
buf.slice_output(sz+content_length);
buf.consume_parsed_data(content_length);
} else {
buf.slice_output(sz);
conn.continues = Continue::Expects(content_length);
}
},
_ => {
buf.slice_output(sz);
}
}
} else {
buf.slice_output(sz);
}
},
BufferMove::Delete(length) => {
buf.consume_parsed_data(length);
if header_end.is_none() {
match current_state {
RequestState::Request(_,_,_) |
RequestState::RequestWithBodyChunks(_,_,_,_) => {
header_end = Some(buf.start_parsing_position);
if let Some(added) = added_req_header {
let s = added.added_request_header(
¤t_state.get_mut_connection().as_ref().unwrap().forwarded);
buf.insert_output(s.into_bytes());
}
buf.delete_output(length);
},
RequestState::RequestWithBody(_,_,_,content_length) => {
header_end = Some(buf.start_parsing_position);
if let Some(added) = added_req_header {
let s = added.added_request_header(
¤t_state.get_mut_connection().as_ref().unwrap().forwarded);
buf.insert_output(s.into_bytes());
}
buf.delete_output(length);
buf.slice_output(content_length);
buf.consume_parsed_data(content_length);
},
_ => {
buf.delete_output(length);
}
}
} else {
buf.delete_output(length);
}
},
BufferMove::Multiple(buffer_moves) => {
for buffer_move in buffer_moves {
match buffer_move {
BufferMove::Advance(length) => {
buf.consume_parsed_data(length);
buf.slice_output(length);
},
BufferMove::Delete(length) => {
buf.consume_parsed_data(length);
buf.delete_output(length);
},
e => {
error!("BufferMove {:?} isn't implemented", e);
unimplemented!();
}
}
}
}
_ => break
}
match current_state {
RequestState::Error(_,_,_,_,_) => {
incr!("http1.parser.request.error");
break;
},
RequestState::Request(_,_,_) | RequestState::RequestWithBody(_,_,_,_) |
RequestState::RequestWithBodyChunks(_,_,_,Chunk::Ended) => break,
_ => ()
}
}
(current_state, header_end)
}