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:
parent
ff0ab733e4
commit
e3dc6f0ca8
@ -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 {
|
||||||
|
@ -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*
|
||||||
|
Loading…
Reference in New Issue
Block a user