1
0
mirror of https://github.com/facebook/proxygen.git synced 2026-10-24 01:33:54 +03:00

Add plain-ASCII fast path to CodecUtil::validateHeaderValue on HPACK decode path

Reviewed By: yfeldblum

Differential Revision: D111136730

fbshipit-source-id: 30da0bc5fea87a52cdfc6e8e77080f135688fecb
This commit is contained in:
Chen Li (Efficiency)
2026-09-18 06:47:04 -07:00
committed by meta-codesync[bot]
parent d1bad34f59
commit f5d54ce2dd
2 changed files with 56 additions and 0 deletions

View File

@@ -124,6 +124,21 @@ class CodecUtil {
static HeaderValueError validateHeaderValueDetail(folly::ByteRange value,
CtlEscapeMode mode) {
// Fast path: values made up entirely of "plain" bytes -- printable
// US-ASCII excluding '"' and '\\', plus HTAB -- cannot alter the state
// machine below and are valid in every mode. The (c - 0x20) <= 0x5e idiom
// folds the 0x20..0x7e range check into a single unsigned compare, keeping
// the scan branch-light so the compiler can auto-vectorize it.
bool plain = true;
for (uint8_t c : value) {
plain &=
(static_cast<uint8_t>(c - 0x20) <= 0x5e && c != '"' && c != '\\') ||
c == '\t';
}
if (plain) {
return HeaderValueError::None;
}
bool escape = false;
bool quote = false;
enum {

View File

@@ -186,6 +186,47 @@ TEST(CodecUtil, validateHeaderValueDetail) {
Error::DanglingEscape);
}
// Regression guard for the plain-ASCII fast path in validateHeaderValue. The
// fast path accepts a value outright only when every byte is "plain" (HTAB or
// printable US-ASCII excluding '"' and '\\'); any other byte falls through to
// the state machine. These cases pin down the transition bytes on both sides
// of that fast/slow split in all three modes, so the fast path can never drift
// from the state machine it short-circuits. Expected values are derived from
// the RFC rules the state machine implements, not copied from the fast path.
TEST(CodecUtil, validateHeaderValueFastPathBoundaries) {
auto check = [](const std::string &value,
bool compliant,
bool strictCompat,
bool strict) {
auto range = folly::ByteRange{
reinterpret_cast<const uint8_t *>(value.data()), value.size()};
EXPECT_EQ(CodecUtil::validateHeaderValue(range, CodecUtil::COMPLIANT),
compliant);
EXPECT_EQ(CodecUtil::validateHeaderValue(range, CodecUtil::STRICT_COMPAT),
strictCompat);
EXPECT_EQ(CodecUtil::validateHeaderValue(range, CodecUtil::STRICT), strict);
};
// COMPLIANT STRICT_COMPAT STRICT
// 0x1F (CTL) rejects; 0x20 (SP) is the first plain byte.
check(std::string("a\x1f"), false, false, false);
check(std::string("a\x20"), true, true, true);
// 0x7E (~) is the last plain byte; 0x7F (DEL) rejects; 0x80 (obs-text) is
// accepted except under STRICT.
check(std::string("a\x7e"), true, true, true);
check(std::string("a\x7f"), false, false, false);
check(std::string("a\x80"), true, true, false);
// 0x09 (HTAB) is plain; 0x22 (") and 0x5C (\) are the state-machine trigger
// bytes (unterminated quote/backslash are accepted).
check(std::string("a\x09"), true, true, true);
check(std::string("a\""), true, true, true);
check(std::string("a\\"), true, true, true);
// A quoted-and-escaped CTL is accepted only in COMPLIANT mode.
check(std::string("\"\\\x01\""), true, false, false);
// An LWS fold (CR LF SP) is accepted in every mode.
check(std::string("abc\r\n def"), true, true, true);
}
TEST(CodecUtil, hasGzipAndDeflate) {
bool gzip = false;
bool deflate = false;