@@ -237,6 +237,96 @@ name: match-header
237237 EXPECT_TRUE (HeaderUtility::matchHeaders (headers, header_data));
238238}
239239
240+ // Tests for matchesHeadersIndividually - validates each header value individually
241+ TEST_F (MatchHeadersTest, MatchesHeadersIndividuallyExactMatch) {
242+ // With old behavior: headers with values "true" and "false" would concatenate to "true,false"
243+ // and not match "true". With new behavior, each value is checked individually.
244+ TestRequestHeaderMapImpl headers{{" match-header" , " true" }, {" match-header" , " false" }};
245+
246+ const std::string yaml = R"EOF(
247+ name: match-header
248+ string_match:
249+ exact: "true"
250+ )EOF" ;
251+
252+ auto matcher = HeaderUtility::createHeaderData (parseHeaderMatcherFromYaml (yaml), context_);
253+
254+ EXPECT_FALSE (matcher->matchesHeaders (headers));
255+ EXPECT_TRUE (matcher->matchesHeadersIndividually (headers));
256+ }
257+
258+ // Test the invert_match case - if ANY value matches, the inverted result is false
259+ TEST_F (MatchHeadersTest, MatchesHeadersIndividuallyExactMatchInvert) {
260+ TestRequestHeaderMapImpl headers{{" match-header" , " true" }, {" match-header" , " other" }};
261+
262+ const std::string yaml = R"EOF(
263+ name: match-header
264+ string_match:
265+ exact: "true"
266+ invert_match: true
267+ )EOF" ;
268+
269+ auto matcher = HeaderUtility::createHeaderData (parseHeaderMatcherFromYaml (yaml), context_);
270+
271+ EXPECT_FALSE (matcher->matchesHeadersIndividually (headers));
272+ }
273+
274+ // Test no values match
275+ TEST_F (MatchHeadersTest, MatchesHeadersIndividuallyNoMatch) {
276+ TestRequestHeaderMapImpl headers{{" match-header" , " foo" }, {" match-header" , " bar" }};
277+
278+ const std::string yaml = R"EOF(
279+ name: match-header
280+ string_match:
281+ exact: "true"
282+ )EOF" ;
283+
284+ auto matcher = HeaderUtility::createHeaderData (parseHeaderMatcherFromYaml (yaml), context_);
285+
286+ EXPECT_FALSE (matcher->matchesHeadersIndividually (headers));
287+ }
288+
289+ // Test single value matches
290+ TEST_F (MatchHeadersTest, MatchesHeadersIndividuallySingleValue) {
291+ TestRequestHeaderMapImpl headers{{" match-header" , " true" }};
292+
293+ const std::string yaml = R"EOF(
294+ name: match-header
295+ string_match:
296+ exact: "true"
297+ )EOF" ;
298+
299+ auto matcher = HeaderUtility::createHeaderData (parseHeaderMatcherFromYaml (yaml), context_);
300+
301+ EXPECT_TRUE (matcher->matchesHeaders (headers));
302+ EXPECT_TRUE (matcher->matchesHeadersIndividually (headers));
303+ }
304+
305+ // matchesHeadersIndividually on HeaderDataPresentMatch delegates to matchesHeaders.
306+ TEST_F (MatchHeadersTest, MatchesHeadersIndividuallyPresentMatch) {
307+ TestRequestHeaderMapImpl present{{" match-header" , " val" }};
308+ TestRequestHeaderMapImpl absent{{" other-header" , " val" }};
309+
310+ auto make = [&](const std::string& yaml) {
311+ return HeaderUtility::createHeaderData (parseHeaderMatcherFromYaml (yaml), context_);
312+ };
313+
314+ // present_match: true
315+ auto matcher = make (" name: match-header\n present_match: true" );
316+ EXPECT_TRUE (matcher->matchesHeadersIndividually (present));
317+ EXPECT_FALSE (matcher->matchesHeadersIndividually (absent));
318+
319+ // present_match: true, invert_match: true
320+ matcher = make (" name: match-header\n present_match: true\n invert_match: true" );
321+ EXPECT_FALSE (matcher->matchesHeadersIndividually (present));
322+ EXPECT_TRUE (matcher->matchesHeadersIndividually (absent));
323+
324+ // present_match: true, treat_missing_header_as_empty: true — always matches.
325+ matcher = make (" name: match-header\n present_match: true\n treat_missing_header_as_empty: true" );
326+ EXPECT_TRUE (matcher->matchesHeadersIndividually (present));
327+ EXPECT_TRUE (matcher->matchesHeadersIndividually (absent));
328+ }
329+
240330TEST_F (MatchHeadersTest, MustMatchAllHeaderData) {
241331 TestRequestHeaderMapImpl matching_headers_1{{" match-header-A" , " 1" }, {" match-header-B" , " 2" }};
242332 TestRequestHeaderMapImpl matching_headers_2{
0 commit comments