From f5d54ce2dd95e5a54c63a187f6cfaeefcea5f02b Mon Sep 17 00:00:00 2001 From: "Chen Li (Efficiency)" Date: Fri, 18 Sep 2026 06:47:04 -0700 Subject: [PATCH] Add plain-ASCII fast path to CodecUtil::validateHeaderValue on HPACK decode path Reviewed By: yfeldblum Differential Revision: D111136730 fbshipit-source-id: 30da0bc5fea87a52cdfc6e8e77080f135688fecb --- proxygen/lib/http/codec/CodecUtil.h | 15 +++++++ .../lib/http/codec/test/CodecUtilTests.cpp | 41 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/proxygen/lib/http/codec/CodecUtil.h b/proxygen/lib/http/codec/CodecUtil.h index df3830595..9e3022181 100644 --- a/proxygen/lib/http/codec/CodecUtil.h +++ b/proxygen/lib/http/codec/CodecUtil.h @@ -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(c - 0x20) <= 0x5e && c != '"' && c != '\\') || + c == '\t'; + } + if (plain) { + return HeaderValueError::None; + } + bool escape = false; bool quote = false; enum { diff --git a/proxygen/lib/http/codec/test/CodecUtilTests.cpp b/proxygen/lib/http/codec/test/CodecUtilTests.cpp index f44fd2618..22dc32112 100644 --- a/proxygen/lib/http/codec/test/CodecUtilTests.cpp +++ b/proxygen/lib/http/codec/test/CodecUtilTests.cpp @@ -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(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;