1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-25 00:12:59 +01:00
actix-extras/src/client/parser.rs

179 lines
6.3 KiB
Rust
Raw Normal View History

2018-01-28 07:03:03 +01:00
use std::mem;
use httparse;
use http::{Version, HttpTryFrom, HeaderMap, StatusCode};
use http::header::{self, HeaderName, HeaderValue};
2018-02-19 12:11:11 +01:00
use bytes::{Bytes, BytesMut};
2018-01-28 07:03:03 +01:00
use futures::{Poll, Async};
use error::{ParseError, PayloadError};
use server::{utils, IoStream};
use server::h1::{Decoder, chunked};
use super::ClientResponse;
use super::response::ClientMessage;
2018-01-28 07:03:03 +01:00
const MAX_BUFFER_SIZE: usize = 131_072;
const MAX_HEADERS: usize = 96;
#[derive(Default)]
2018-01-28 07:03:03 +01:00
pub struct HttpResponseParser {
2018-02-19 12:11:11 +01:00
decoder: Option<Decoder>,
2018-01-28 07:03:03 +01:00
}
2018-02-19 22:18:18 +01:00
#[derive(Debug, Fail)]
2018-01-28 07:03:03 +01:00
pub enum HttpResponseParserError {
2018-02-19 22:18:18 +01:00
/// Server disconnected
#[fail(display="Server disconnected")]
2018-01-28 07:03:03 +01:00
Disconnect,
2018-02-19 22:18:18 +01:00
#[fail(display="{}", _0)]
Error(#[cause] ParseError),
2018-01-28 07:03:03 +01:00
}
impl HttpResponseParser {
pub fn parse<T>(&mut self, io: &mut T, buf: &mut BytesMut)
-> Poll<ClientResponse, HttpResponseParserError>
where T: IoStream
{
// if buf is empty parse_message will always return NotReady, let's avoid that
let read = if buf.is_empty() {
match utils::read_from_io(io, buf) {
Ok(Async::Ready(0)) => {
// debug!("Ignored premature client disconnection");
return Err(HttpResponseParserError::Disconnect);
},
Ok(Async::Ready(_)) => (),
Ok(Async::NotReady) =>
return Ok(Async::NotReady),
Err(err) =>
return Err(HttpResponseParserError::Error(err.into()))
}
false
} else {
true
};
loop {
match HttpResponseParser::parse_message(buf).map_err(HttpResponseParserError::Error)? {
Async::Ready((msg, decoder)) => {
2018-02-19 12:11:11 +01:00
self.decoder = decoder;
2018-01-28 07:03:03 +01:00
return Ok(Async::Ready(msg));
},
Async::NotReady => {
if buf.capacity() >= MAX_BUFFER_SIZE {
return Err(HttpResponseParserError::Error(ParseError::TooLarge));
}
if read {
match utils::read_from_io(io, buf) {
2018-02-19 12:11:11 +01:00
Ok(Async::Ready(0)) => return Err(HttpResponseParserError::Disconnect),
2018-01-28 07:03:03 +01:00
Ok(Async::Ready(_)) => (),
2018-02-19 12:11:11 +01:00
Ok(Async::NotReady) => return Ok(Async::NotReady),
Err(err) => return Err(HttpResponseParserError::Error(err.into())),
2018-01-28 07:03:03 +01:00
}
} else {
return Ok(Async::NotReady)
}
},
}
}
}
2018-02-19 12:11:11 +01:00
pub fn parse_payload<T>(&mut self, io: &mut T, buf: &mut BytesMut)
-> Poll<Option<Bytes>, PayloadError>
where T: IoStream
{
if let Some(ref mut decoder) = self.decoder {
// read payload
match utils::read_from_io(io, buf) {
Ok(Async::Ready(0)) => return Err(PayloadError::Incomplete),
Err(err) => return Err(err.into()),
_ => (),
2018-01-30 00:45:37 +01:00
}
2018-02-19 12:11:11 +01:00
decoder.decode(buf).map_err(|e| e.into())
2018-01-30 00:45:37 +01:00
} else {
2018-02-19 12:11:11 +01:00
Ok(Async::Ready(None))
2018-01-30 00:45:37 +01:00
}
}
2018-02-19 12:11:11 +01:00
fn parse_message(buf: &mut BytesMut) -> Poll<(ClientResponse, Option<Decoder>), ParseError> {
2018-01-28 07:03:03 +01:00
// Parse http message
let bytes_ptr = buf.as_ref().as_ptr() as usize;
let mut headers: [httparse::Header; MAX_HEADERS] =
unsafe{mem::uninitialized()};
let (len, version, status, headers_len) = {
let b = unsafe{ let b: &[u8] = buf; mem::transmute(b) };
let mut resp = httparse::Response::new(&mut headers);
match resp.parse(b)? {
httparse::Status::Complete(len) => {
let version = if resp.version.unwrap() == 1 {
Version::HTTP_11
} else {
Version::HTTP_10
};
let status = StatusCode::from_u16(resp.code.unwrap())
.map_err(|_| ParseError::Status)?;
(len, version, status, resp.headers.len())
}
httparse::Status::Partial => return Ok(Async::NotReady),
}
};
let slice = buf.split_to(len).freeze();
// convert headers
let mut hdrs = HeaderMap::new();
for header in headers[..headers_len].iter() {
if let Ok(name) = HeaderName::try_from(header.name) {
let v_start = header.value.as_ptr() as usize - bytes_ptr;
let v_end = v_start + header.value.len();
let value = unsafe {
HeaderValue::from_shared_unchecked(slice.slice(v_start, v_end)) };
hdrs.append(name, value);
} else {
return Err(ParseError::Header)
}
}
let decoder = if let Some(len) = hdrs.get(header::CONTENT_LENGTH) {
// Content-Length
if let Ok(s) = len.to_str() {
if let Ok(len) = s.parse::<u64>() {
Some(Decoder::length(len))
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header)
}
} else {
debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header)
}
} else if chunked(&hdrs)? {
// Chunked encoding
Some(Decoder::chunked())
} else if hdrs.contains_key(header::UPGRADE) {
Some(Decoder::eof())
} else {
None
};
if let Some(decoder) = decoder {
2018-02-19 12:11:11 +01:00
//let info = PayloadInfo {
//tx: PayloadType::new(&hdrs, psender),
// decoder: decoder,
//};
2018-01-28 07:03:03 +01:00
Ok(Async::Ready(
(ClientResponse::new(
2018-02-19 12:11:11 +01:00
ClientMessage{status: status, version: version,
headers: hdrs, cookies: None}), Some(decoder))))
2018-01-28 07:03:03 +01:00
} else {
Ok(Async::Ready(
(ClientResponse::new(
2018-02-19 12:11:11 +01:00
ClientMessage{status: status, version: version,
headers: hdrs, cookies: None}), None)))
2018-01-28 07:03:03 +01:00
}
}
}