Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactored header name matching for WASM target #324

Merged
merged 2 commits into from Jul 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Expand Up @@ -39,6 +39,10 @@ doc-comment = "0.3"
name = "header_map"
path = "benches/header_map/mod.rs"

[[bench]]
name = "header_name"
path = "benches/header_name.rs"

[[bench]]
name = "header_value"
path = "benches/header_value.rs"
Expand Down
157 changes: 157 additions & 0 deletions benches/header_name.rs
@@ -0,0 +1,157 @@
#![feature(test)]

extern crate bytes;
extern crate http;
extern crate test;

use http::header::HeaderName;
use test::Bencher;

fn make_all_known_headers() -> Vec<Vec<u8>> {
// Standard request headers
vec![b"A-IM".to_vec(),
b"Accept".to_vec(),
b"Accept-Charset".to_vec(),
b"Accept-Datetime".to_vec(),
b"Accept-Encoding".to_vec(),
b"Accept-Language".to_vec(),
b"Access-Control-Request-Method".to_vec(),
b"Authorization".to_vec(),
b"Cache-Control".to_vec(),
b"Connection".to_vec(),
b"Permanent".to_vec(),
b"Content-Length".to_vec(),
b"Content-MD5".to_vec(),
b"Content-Type".to_vec(),
b"Cookie".to_vec(),
b"Date".to_vec(),
b"Expect".to_vec(),
b"Forwarded".to_vec(),
b"From".to_vec(),
b"Host".to_vec(),
b"Permanent".to_vec(),
b"HTTP2-Settings".to_vec(),
b"If-Match".to_vec(),
b"If-Modified-Since".to_vec(),
b"If-None-Match".to_vec(),
b"If-Range".to_vec(),
b"If-Unmodified-Since".to_vec(),
b"Max-Forwards".to_vec(),
b"Origin".to_vec(),
b"Pragma".to_vec(),
b"Proxy-Authorization".to_vec(),
b"Range".to_vec(),
b"Referer".to_vec(),
b"TE".to_vec(),
b"User-Agent".to_vec(),
b"Upgrade".to_vec(),
b"Via".to_vec(),
b"Warning".to_vec(),
// common_non_standard
b"Upgrade-Insecure-Requests".to_vec(),
b"Upgrade-Insecure-Requests".to_vec(),
b"X-Requested-With".to_vec(),
b"DNT".to_vec(),
b"X-Forwarded-For".to_vec(),
b"X-Forwarded-Host".to_vec(),
b"X-Forwarded-Proto".to_vec(),
b"Front-End-Https".to_vec(),
b"X-Http-Method-Override".to_vec(),
b"X-ATT-DeviceId".to_vec(),
b"X-Wap-Profile".to_vec(),
b"Proxy-Connection".to_vec(),
b"X-UIDH".to_vec(),
b"X-Csrf-Token".to_vec(),
b"X-Request-ID".to_vec(),
b"X-Correlation-ID".to_vec(),
b"Save-Data".to_vec(),
// standard_response_headers
b"Accept-Patch".to_vec(),
b"Accept-Ranges".to_vec(),
b"Access-Control-Allow-Credentials".to_vec(),
b"Access-Control-Allow-Headers".to_vec(),
b"Access-Control-Allow-Methods".to_vec(),
b"Access-Control-Allow-Origin".to_vec(),
b"Access-Control-Expose-Headers".to_vec(),
b"Access-Control-Max-Age".to_vec(),
b"Age".to_vec(),
b"Allow".to_vec(),
b"Alt-Svc".to_vec(),
b"Cache-Control".to_vec(),
b"Connection".to_vec(),
b"Content-Disposition".to_vec(),
b"Content-Encoding".to_vec(),
b"Content-Language".to_vec(),
b"Content-Length".to_vec(),
b"Content-Location".to_vec(),
b"Content-MD5".to_vec(),
b"Content-Range".to_vec(),
b"Content-Type".to_vec(),
b"Date".to_vec(),
b"Delta-Base".to_vec(),
b"ETag".to_vec(),
b"Expires".to_vec(),
b"IM".to_vec(),
b"Last-Modified".to_vec(),
b"Link".to_vec(),
b"Location".to_vec(),
b"P3P".to_vec(),
b"Permanent".to_vec(),
b"Pragma".to_vec(),
b"Proxy-Authenticate".to_vec(),
b"Public-Key-Pins".to_vec(),
b"Retry-After".to_vec(),
b"Server".to_vec(),
b"Set-Cookie".to_vec(),
b"Strict-Transport-Security".to_vec(),
b"Tk".to_vec(),
b"Trailer".to_vec(),
b"Transfer-Encoding".to_vec(),
b"Upgrade".to_vec(),
b"Vary".to_vec(),
b"Via".to_vec(),
b"Warning".to_vec(),
b"WWW-Authenticate".to_vec(),
b"X-Frame-Options".to_vec(),
// common_non_standard_response
b"Content-Security-Policy".to_vec(),
b"Refresh".to_vec(),
b"Status".to_vec(),
b"Timing-Allow-Origin".to_vec(),
b"X-Content-Duration".to_vec(),
b"X-Content-Security-Policy".to_vec(),
b"X-Content-Type-Options".to_vec(),
b"X-Correlation-ID".to_vec(),
b"X-Powered-By".to_vec(),
b"X-Request-ID".to_vec(),
b"X-UA-Compatible".to_vec(),
b"X-WebKit-CSP".to_vec(),
b"X-XSS-Protection".to_vec(),
]
}

#[bench]
fn header_name_easy(b: &mut Bencher) {
let name = b"Content-type";
b.iter(|| {
HeaderName::from_bytes(&name[..]).unwrap();
});
}

#[bench]
fn header_name_bad(b: &mut Bencher) {
let name = b"bad header name";
b.iter(|| {
HeaderName::from_bytes(&name[..]).expect_err("Bad header name");
});
}

#[bench]
fn header_name_various(b: &mut Bencher) {
let all_known_headers = make_all_known_headers();
b.iter(|| {
for name in &all_known_headers{
HeaderName::from_bytes(name.as_slice()).unwrap();
}
});
}
127 changes: 127 additions & 0 deletions src/header/name.rs
Expand Up @@ -1043,6 +1043,7 @@ const HEADER_CHARS_H2: [u8; 256] = [
0, 0, 0, 0, 0, 0 // 25x
];

#[cfg(any(not(debug_assertions), not(target_arch = "wasm32")))]
macro_rules! eq {
(($($cmp:expr,)*) $v:ident[$n:expr] ==) => {
$($cmp) && *
Expand All @@ -1058,6 +1059,10 @@ macro_rules! eq {
};
}

#[cfg(any(not(debug_assertions), not(target_arch = "wasm32")))]
/// This version is best under optimized mode, however in a wasm debug compile,
/// the `eq` macro expands to 1 + 1 + 1 + 1... and wasm explodes when this chain gets too long
/// See https://github.com/DenisKolodin/yew/issues/478
fn parse_hdr<'a>(data: &'a [u8], b: &'a mut [u8; 64], table: &[u8; 256])
-> Result<HdrName<'a>, InvalidHeaderName>
{
Expand Down Expand Up @@ -1520,6 +1525,128 @@ fn parse_hdr<'a>(data: &'a [u8], b: &'a mut [u8; 64], table: &[u8; 256])
}
}

#[cfg(all(debug_assertions, target_arch = "wasm32"))]
/// This version works best in debug mode in wasm
fn parse_hdr<'a>(
data: &'a [u8],
b: &'a mut [u8; 64],
table: &[u8; 256],
) -> Result<HdrName<'a>, InvalidHeaderName> {
use self::StandardHeader::*;

let len = data.len();

let validate = |buf: &'a [u8], len: usize| {
let buf = &buf[..len];
if buf.iter().any(|&b| b == 0) {
Err(InvalidHeaderName::new())
} else {
Ok(HdrName::custom(buf, true))
}
};

assert!(
len < super::MAX_HEADER_NAME_LEN,
"header name too long -- max length is {}",
super::MAX_HEADER_NAME_LEN
);

match len {
0 => Err(InvalidHeaderName::new()),
len if len > 64 => Ok(HdrName::custom(data, false)),
len => {
// Read from data into the buffer - transforming using `table` as we go
data.iter().zip(b.iter_mut()).for_each(|(index, out)| *out = table[*index as usize]);
match &b[0..len] {
b"te" => Ok(Te.into()),
b"age" => Ok(Age.into()),
b"via" => Ok(Via.into()),
b"dnt" => Ok(Dnt.into()),
b"date" => Ok(Date.into()),
b"etag" => Ok(Etag.into()),
b"from" => Ok(From.into()),
b"host" => Ok(Host.into()),
b"link" => Ok(Link.into()),
b"vary" => Ok(Vary.into()),
b"allow" => Ok(Allow.into()),
b"range" => Ok(Range.into()),
b"accept" => Ok(Accept.into()),
b"cookie" => Ok(Cookie.into()),
b"expect" => Ok(Expect.into()),
b"origin" => Ok(Origin.into()),
b"pragma" => Ok(Pragma.into()),
b"server" => Ok(Server.into()),
b"alt-svc" => Ok(AltSvc.into()),
b"expires" => Ok(Expires.into()),
b"referer" => Ok(Referer.into()),
b"refresh" => Ok(Refresh.into()),
b"trailer" => Ok(Trailer.into()),
b"upgrade" => Ok(Upgrade.into()),
b"warning" => Ok(Warning.into()),
b"if-match" => Ok(IfMatch.into()),
b"if-range" => Ok(IfRange.into()),
b"location" => Ok(Location.into()),
b"forwarded" => Ok(Forwarded.into()),
b"connection" => Ok(Connection.into()),
b"set-cookie" => Ok(SetCookie.into()),
b"user-agent" => Ok(UserAgent.into()),
b"retry-after" => Ok(RetryAfter.into()),
b"content-type" => Ok(ContentType.into()),
b"max-forwards" => Ok(MaxForwards.into()),
b"accept-ranges" => Ok(AcceptRanges.into()),
b"authorization" => Ok(Authorization.into()),
b"cache-control" => Ok(CacheControl.into()),
b"content-range" => Ok(ContentRange.into()),
b"if-none-match" => Ok(IfNoneMatch.into()),
b"last-modified" => Ok(LastModified.into()),
b"accept-charset" => Ok(AcceptCharset.into()),
b"content-length" => Ok(ContentLength.into()),
b"accept-encoding" => Ok(AcceptEncoding.into()),
b"accept-language" => Ok(AcceptLanguage.into()),
b"public-key-pins" => Ok(PublicKeyPins.into()),
b"x-frame-options" => Ok(XFrameOptions.into()),
b"referrer-policy" => Ok(ReferrerPolicy.into()),
b"content-language" => Ok(ContentLanguage.into()),
b"content-location" => Ok(ContentLocation.into()),
b"content-encoding" => Ok(ContentEncoding.into()),
b"www-authenticate" => Ok(WwwAuthenticate.into()),
b"x-xss-protection" => Ok(XXssProtection.into()),
b"transfer-encoding" => Ok(TransferEncoding.into()),
b"if-modified-since" => Ok(IfModifiedSince.into()),
b"sec-websocket-key" => Ok(SecWebSocketKey.into()),
b"proxy-authenticate" => Ok(ProxyAuthenticate.into()),
b"content-disposition" => Ok(ContentDisposition.into()),
b"if-unmodified-since" => Ok(IfUnmodifiedSince.into()),
b"proxy-authorization" => Ok(ProxyAuthorization.into()),
b"sec-websocket-accept" => Ok(SecWebSocketAccept.into()),
b"sec-websocket-version" => Ok(SecWebSocketVersion.into()),
b"access-control-max-age" => Ok(AccessControlMaxAge.into()),
b"x-content-type-options" => Ok(XContentTypeOptions.into()),
b"x-dns-prefetch-control" => Ok(XDnsPrefetchControl.into()),
b"sec-websocket-protocol" => Ok(SecWebSocketProtocol.into()),
b"content-security-policy" => Ok(ContentSecurityPolicy.into()),
b"sec-websocket-extensions" => Ok(SecWebSocketExtensions.into()),
b"strict-transport-security" => Ok(StrictTransportSecurity.into()),
b"upgrade-insecure-requests" => Ok(UpgradeInsecureRequests.into()),
b"access-control-allow-origin" => Ok(AccessControlAllowOrigin.into()),
b"public-key-pins-report-only" => Ok(PublicKeyPinsReportOnly.into()),
b"access-control-allow-headers" => Ok(AccessControlAllowHeaders.into()),
b"access-control-allow-methods" => Ok(AccessControlAllowMethods.into()),
b"access-control-expose-headers" => Ok(AccessControlExposeHeaders.into()),
b"access-control-request-method" => Ok(AccessControlRequestMethod.into()),
b"access-control-request-headers" => Ok(AccessControlRequestHeaders.into()),
b"access-control-allow-credentials" => Ok(AccessControlAllowCredentials.into()),
b"content-security-policy-report-only" => {
Ok(ContentSecurityPolicyReportOnly.into())
}
other => validate(other, len),
}
}
}
}



impl<'a> From<StandardHeader> for HdrName<'a> {
fn from(hdr: StandardHeader) -> HdrName<'a> {
HdrName { inner: Repr::Standard(hdr) }
Expand Down