Skip to content

Commit 724bde7

Browse files
authored
perf(http1): avoid copy-allocation in request path parsing (#3575)
This tweaks the request path parsing logic (in server role) in order to perform zero-copy URI parsing. Closes #3574
1 parent 198c1b9 commit 724bde7

File tree

1 file changed

+23
-4
lines changed

1 file changed

+23
-4
lines changed

src/proto/h1/role.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ impl Http1Transaction for Server {
135135
let version;
136136
let len;
137137
let headers_len;
138+
let method;
139+
let path_range;
138140

139141
// Both headers_indices and headers are using uninitialized memory,
140142
// but we *never* read any of it until after httparse has assigned
@@ -162,10 +164,8 @@ impl Http1Transaction for Server {
162164
if uri.len() > MAX_URI_LEN {
163165
return Err(Parse::UriTooLong);
164166
}
165-
subject = RequestLine(
166-
Method::from_bytes(req.method.unwrap().as_bytes())?,
167-
uri.parse()?,
168-
);
167+
method = Method::from_bytes(req.method.unwrap().as_bytes())?;
168+
path_range = Server::record_path_range(bytes, uri);
169169
version = if req.version.unwrap() == 1 {
170170
keep_alive = true;
171171
is_http_11 = true;
@@ -198,6 +198,12 @@ impl Http1Transaction for Server {
198198
};
199199

200200
let slice = buf.split_to(len).freeze();
201+
let uri = {
202+
let uri_bytes = slice.slice_ref(&slice[path_range]);
203+
// TODO(lucab): switch to `Uri::from_shared()` once public.
204+
http::Uri::from_maybe_shared(uri_bytes)?
205+
};
206+
subject = RequestLine(method, uri);
201207

202208
// According to https://tools.ietf.org/html/rfc7230#section-3.3.3
203209
// 1. (irrelevant to Request)
@@ -945,6 +951,15 @@ impl Server {
945951

946952
Ok(encoder.set_last(is_last))
947953
}
954+
955+
/// Helper for zero-copy parsing of request path URI.
956+
#[inline]
957+
fn record_path_range(bytes: &[u8], req_path: &str) -> std::ops::Range<usize> {
958+
let bytes_ptr = bytes.as_ptr() as usize;
959+
let start = req_path.as_ptr() as usize - bytes_ptr;
960+
let end = start + req_path.len();
961+
std::ops::Range { start, end }
962+
}
948963
}
949964

950965
#[cfg(feature = "server")]
@@ -2936,8 +2951,12 @@ mod tests {
29362951
.unwrap()
29372952
.unwrap();
29382953
::test::black_box(&msg);
2954+
2955+
// Remove all references pointing into BytesMut.
29392956
msg.head.headers.clear();
29402957
headers = Some(msg.head.headers);
2958+
std::mem::take(&mut msg.head.subject);
2959+
29412960
restart(&mut raw, len);
29422961
});
29432962

0 commit comments

Comments
 (0)