refactor(http1): use MaybeUninit for parsing with uninitialized headers (#2545)
Closes #2532
This commit is contained in:
@@ -1,9 +1,5 @@
|
|||||||
// `mem::uninitialized` replaced with `mem::MaybeUninit`,
|
|
||||||
// can't upgrade yet
|
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use std::mem;
|
use std::mem::{self, MaybeUninit};
|
||||||
|
|
||||||
#[cfg(any(test, feature = "server", feature = "ffi"))]
|
#[cfg(any(test, feature = "server", feature = "ffi"))]
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
@@ -115,17 +111,23 @@ impl Http1Transaction for Server {
|
|||||||
// but we *never* read any of it until after httparse has assigned
|
// but we *never* read any of it until after httparse has assigned
|
||||||
// values into it. By not zeroing out the stack memory, this saves
|
// values into it. By not zeroing out the stack memory, this saves
|
||||||
// a good ~5% on pipeline benchmarks.
|
// a good ~5% on pipeline benchmarks.
|
||||||
let mut headers_indices: [HeaderIndices; MAX_HEADERS] = unsafe { mem::uninitialized() };
|
let mut headers_indices: [MaybeUninit<HeaderIndices>; MAX_HEADERS] = unsafe {
|
||||||
|
// SAFETY: We can go safely from MaybeUninit array to array of MaybeUninit
|
||||||
|
MaybeUninit::uninit().assume_init()
|
||||||
|
};
|
||||||
{
|
{
|
||||||
let mut headers: [httparse::Header<'_>; MAX_HEADERS] = unsafe { mem::uninitialized() };
|
/* SAFETY: it is safe to go from MaybeUninit array to array of MaybeUninit */
|
||||||
|
let mut headers: [MaybeUninit<httparse::Header<'_>>; MAX_HEADERS] = unsafe {
|
||||||
|
MaybeUninit::uninit().assume_init()
|
||||||
|
};
|
||||||
trace!(
|
trace!(
|
||||||
"Request.parse([Header; {}], [u8; {}])",
|
"Request.parse([Header; {}], [u8; {}])",
|
||||||
headers.len(),
|
headers.len(),
|
||||||
buf.len()
|
buf.len()
|
||||||
);
|
);
|
||||||
let mut req = httparse::Request::new(&mut headers);
|
let mut req = httparse::Request::new(&mut []);
|
||||||
let bytes = buf.as_ref();
|
let bytes = buf.as_ref();
|
||||||
match req.parse(bytes) {
|
match req.parse_with_uninit_headers(bytes, &mut headers) {
|
||||||
Ok(httparse::Status::Complete(parsed_len)) => {
|
Ok(httparse::Status::Complete(parsed_len)) => {
|
||||||
trace!("Request.parse Complete({})", parsed_len);
|
trace!("Request.parse Complete({})", parsed_len);
|
||||||
len = parsed_len;
|
len = parsed_len;
|
||||||
@@ -194,6 +196,8 @@ impl Http1Transaction for Server {
|
|||||||
headers.reserve(headers_len);
|
headers.reserve(headers_len);
|
||||||
|
|
||||||
for header in &headers_indices[..headers_len] {
|
for header in &headers_indices[..headers_len] {
|
||||||
|
// SAFETY: array is valid up to `headers_len`
|
||||||
|
let header = unsafe { &*header.as_ptr() };
|
||||||
let name = header_name!(&slice[header.name.0..header.name.1]);
|
let name = header_name!(&slice[header.name.0..header.name.1]);
|
||||||
let value = header_value!(slice.slice(header.value.0..header.value.1));
|
let value = header_value!(slice.slice(header.value.0..header.value.1));
|
||||||
|
|
||||||
@@ -867,18 +871,24 @@ impl Http1Transaction for Client {
|
|||||||
// Loop to skip information status code headers (100 Continue, etc).
|
// Loop to skip information status code headers (100 Continue, etc).
|
||||||
loop {
|
loop {
|
||||||
// Unsafe: see comment in Server Http1Transaction, above.
|
// Unsafe: see comment in Server Http1Transaction, above.
|
||||||
let mut headers_indices: [HeaderIndices; MAX_HEADERS] = unsafe { mem::uninitialized() };
|
let mut headers_indices: [MaybeUninit<HeaderIndices>; MAX_HEADERS] = unsafe {
|
||||||
|
// SAFETY: We can go safely from MaybeUninit array to array of MaybeUninit
|
||||||
|
MaybeUninit::uninit().assume_init()
|
||||||
|
};
|
||||||
let (len, status, reason, version, headers_len) = {
|
let (len, status, reason, version, headers_len) = {
|
||||||
let mut headers: [httparse::Header<'_>; MAX_HEADERS] =
|
// SAFETY: We can go safely from MaybeUninit array to array of MaybeUninit
|
||||||
unsafe { mem::uninitialized() };
|
let mut headers: [MaybeUninit<httparse::Header<'_>>; MAX_HEADERS] =
|
||||||
|
unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
trace!(
|
trace!(
|
||||||
"Response.parse([Header; {}], [u8; {}])",
|
"Response.parse([Header; {}], [u8; {}])",
|
||||||
headers.len(),
|
headers.len(),
|
||||||
buf.len()
|
buf.len()
|
||||||
);
|
);
|
||||||
let mut res = httparse::Response::new(&mut headers);
|
let mut res = httparse::Response::new(&mut []);
|
||||||
let bytes = buf.as_ref();
|
let bytes = buf.as_ref();
|
||||||
match ctx.h1_parser_config.parse_response(&mut res, bytes) {
|
match ctx.h1_parser_config
|
||||||
|
.parse_response_with_uninit_headers(&mut res, bytes, &mut headers)
|
||||||
|
{
|
||||||
Ok(httparse::Status::Complete(len)) => {
|
Ok(httparse::Status::Complete(len)) => {
|
||||||
trace!("Response.parse Complete({})", len);
|
trace!("Response.parse Complete({})", len);
|
||||||
let status = StatusCode::from_u16(res.code.unwrap())?;
|
let status = StatusCode::from_u16(res.code.unwrap())?;
|
||||||
@@ -934,6 +944,8 @@ impl Http1Transaction for Client {
|
|||||||
|
|
||||||
headers.reserve(headers_len);
|
headers.reserve(headers_len);
|
||||||
for header in &headers_indices[..headers_len] {
|
for header in &headers_indices[..headers_len] {
|
||||||
|
// SAFETY: array is valid up to `headers_len`
|
||||||
|
let header = unsafe { &*header.as_ptr() };
|
||||||
let name = header_name!(&slice[header.name.0..header.name.1]);
|
let name = header_name!(&slice[header.name.0..header.name.1]);
|
||||||
let value = header_value!(slice.slice(header.value.0..header.value.1));
|
let value = header_value!(slice.slice(header.value.0..header.value.1));
|
||||||
|
|
||||||
@@ -1288,7 +1300,7 @@ struct HeaderIndices {
|
|||||||
fn record_header_indices(
|
fn record_header_indices(
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
headers: &[httparse::Header<'_>],
|
headers: &[httparse::Header<'_>],
|
||||||
indices: &mut [HeaderIndices],
|
indices: &mut [MaybeUninit<HeaderIndices>],
|
||||||
) -> Result<(), crate::error::Parse> {
|
) -> Result<(), crate::error::Parse> {
|
||||||
let bytes_ptr = bytes.as_ptr() as usize;
|
let bytes_ptr = bytes.as_ptr() as usize;
|
||||||
|
|
||||||
@@ -1299,10 +1311,19 @@ fn record_header_indices(
|
|||||||
}
|
}
|
||||||
let name_start = header.name.as_ptr() as usize - bytes_ptr;
|
let name_start = header.name.as_ptr() as usize - bytes_ptr;
|
||||||
let name_end = name_start + header.name.len();
|
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_start = header.value.as_ptr() as usize - bytes_ptr;
|
||||||
let value_end = value_start + header.value.len();
|
let value_end = value_start + header.value.len();
|
||||||
indices.value = (value_start, value_end);
|
|
||||||
|
// FIXME(maybe_uninit_extra)
|
||||||
|
// FIXME(addr_of)
|
||||||
|
// Currently we don't have `ptr::addr_of_mut` in stable rust or
|
||||||
|
// MaybeUninit::write, so this is some way of assigning into a MaybeUninit
|
||||||
|
// safely
|
||||||
|
let new_header_indices = HeaderIndices {
|
||||||
|
name: (name_start, name_end),
|
||||||
|
value: (value_start, value_end),
|
||||||
|
};
|
||||||
|
*indices = MaybeUninit::new(new_header_indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user