1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-12-01 02:44:37 +01:00

refactor h1decoder

This commit is contained in:
Nikolay Kim 2018-06-23 12:28:55 +06:00
parent ff0ab733e4
commit e3dc6f0ca8
2 changed files with 57 additions and 32 deletions

View File

@ -1,13 +1,14 @@
use std::mem;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::{Async, Poll}; use futures::{Async, Poll};
use http::header::{self, HeaderName, HeaderValue}; use http::header::{self, HeaderName, HeaderValue};
use http::{HeaderMap, HttpTryFrom, StatusCode, Version}; use http::{HeaderMap, StatusCode, Version};
use httparse; use httparse;
use std::mem;
use error::{ParseError, PayloadError}; use error::{ParseError, PayloadError};
use server::h1decoder::EncodingDecoder; use server::h1decoder::{EncodingDecoder, HeaderIndex};
use server::IoStream; use server::IoStream;
use super::response::ClientMessage; use super::response::ClientMessage;
@ -117,24 +118,23 @@ impl HttpResponseParser {
fn parse_message( fn parse_message(
buf: &mut BytesMut, buf: &mut BytesMut,
) -> Poll<(ClientResponse, Option<EncodingDecoder>), ParseError> { ) -> Poll<(ClientResponse, Option<EncodingDecoder>), ParseError> {
// Parse http message // Unsafe: we read only this data only after httparse parses headers into.
let bytes_ptr = buf.as_ref().as_ptr() as usize; // performance bump for pipeline benchmarks.
let mut headers: [httparse::Header; MAX_HEADERS] = let mut headers: [HeaderIndex; MAX_HEADERS] = unsafe { mem::uninitialized() };
unsafe { mem::uninitialized() };
let (len, version, status, headers_len) = { let (len, version, status, headers_len) = {
let b = unsafe { let mut parsed: [httparse::Header; MAX_HEADERS] =
let b: &[u8] = buf; unsafe { mem::uninitialized() };
&*(b as *const _)
}; let mut resp = httparse::Response::new(&mut parsed);
let mut resp = httparse::Response::new(&mut headers); match resp.parse(buf)? {
match resp.parse(b)? {
httparse::Status::Complete(len) => { httparse::Status::Complete(len) => {
let version = if resp.version.unwrap_or(1) == 1 { let version = if resp.version.unwrap_or(1) == 1 {
Version::HTTP_11 Version::HTTP_11
} else { } else {
Version::HTTP_10 Version::HTTP_10
}; };
HeaderIndex::record(buf, resp.headers, &mut headers);
let status = StatusCode::from_u16(resp.code.unwrap()) let status = StatusCode::from_u16(resp.code.unwrap())
.map_err(|_| ParseError::Status)?; .map_err(|_| ParseError::Status)?;
@ -148,12 +148,13 @@ impl HttpResponseParser {
// convert headers // convert headers
let mut hdrs = HeaderMap::new(); let mut hdrs = HeaderMap::new();
for header in headers[..headers_len].iter() { for idx in headers[..headers_len].iter() {
if let Ok(name) = HeaderName::try_from(header.name) { if let Ok(name) = HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]) {
let v_start = header.value.as_ptr() as usize - bytes_ptr; // Unsafe: httparse check header value for valid utf-8
let v_end = v_start + header.value.len();
let value = unsafe { let value = unsafe {
HeaderValue::from_shared_unchecked(slice.slice(v_start, v_end)) HeaderValue::from_shared_unchecked(
slice.slice(idx.value.0, idx.value.1),
)
}; };
hdrs.append(name, value); hdrs.append(name, value);
} else { } else {

View File

@ -91,17 +91,17 @@ impl H1Decoder {
let mut content_length = None; let mut content_length = None;
let msg = { let msg = {
let bytes_ptr = buf.as_ref().as_ptr() as usize; // Unsafe: we read only this data only after httparse parses headers into.
let mut headers: [httparse::Header; MAX_HEADERS] = // performance bump for pipeline benchmarks.
let mut headers: [HeaderIndex; MAX_HEADERS] =
unsafe { mem::uninitialized() }; unsafe { mem::uninitialized() };
let (len, method, path, version, headers_len) = { let (len, method, path, version, headers_len) = {
let b = unsafe { let mut parsed: [httparse::Header; MAX_HEADERS] =
let b: &[u8] = buf; unsafe { mem::uninitialized() };
&*(b as *const [u8])
}; let mut req = httparse::Request::new(&mut parsed);
let mut req = httparse::Request::new(&mut headers); match req.parse(buf)? {
match req.parse(b)? {
httparse::Status::Complete(len) => { httparse::Status::Complete(len) => {
let method = Method::from_bytes(req.method.unwrap().as_bytes()) let method = Method::from_bytes(req.method.unwrap().as_bytes())
.map_err(|_| ParseError::Method)?; .map_err(|_| ParseError::Method)?;
@ -111,6 +111,8 @@ impl H1Decoder {
} else { } else {
Version::HTTP_10 Version::HTTP_10
}; };
HeaderIndex::record(buf, req.headers, &mut headers);
(len, method, path, version, req.headers.len()) (len, method, path, version, req.headers.len())
} }
httparse::Status::Partial => return Ok(Async::NotReady), httparse::Status::Partial => return Ok(Async::NotReady),
@ -127,15 +129,15 @@ impl H1Decoder {
.flags .flags
.set(MessageFlags::KEEPALIVE, version != Version::HTTP_10); .set(MessageFlags::KEEPALIVE, version != Version::HTTP_10);
for header in headers[..headers_len].iter() { for idx in headers[..headers_len].iter() {
if let Ok(name) = HeaderName::from_bytes(header.name.as_bytes()) { if let Ok(name) =
HeaderName::from_bytes(&slice[idx.name.0..idx.name.1])
{
has_upgrade = has_upgrade || name == header::UPGRADE; has_upgrade = has_upgrade || name == header::UPGRADE;
// Unsafe: httparse check header value for valid utf-8
let v_start = header.value.as_ptr() as usize - bytes_ptr;
let v_end = v_start + header.value.len();
let value = unsafe { let value = unsafe {
HeaderValue::from_shared_unchecked( HeaderValue::from_shared_unchecked(
slice.slice(v_start, v_end), slice.slice(idx.value.0, idx.value.1),
) )
}; };
match name { match name {
@ -211,6 +213,28 @@ impl H1Decoder {
} }
} }
#[derive(Clone, Copy)]
pub(crate) struct HeaderIndex {
pub(crate) name: (usize, usize),
pub(crate) value: (usize, usize),
}
impl HeaderIndex {
pub(crate) fn record(
bytes: &[u8], headers: &[httparse::Header], indices: &mut [HeaderIndex],
) {
let bytes_ptr = bytes.as_ptr() as usize;
for (header, indices) in headers.iter().zip(indices.iter_mut()) {
let name_start = header.name.as_ptr() as usize - bytes_ptr;
let name_end = name_start + header.name.len();
indices.name = (name_start, name_end);
let value_start = header.value.as_ptr() as usize - bytes_ptr;
let value_end = value_start + header.value.len();
indices.value = (value_start, value_end);
}
}
}
/// Decoders to handle different Transfer-Encodings. /// Decoders to handle different Transfer-Encodings.
/// ///
/// If a message body does not include a Transfer-Encoding, it *should* /// If a message body does not include a Transfer-Encoding, it *should*