Skip to content

Commit 5ed6993

Browse files
committed
Fixup: 12492 Split HeaderValueValidationUtils to GrpcService to match the updated requirements
1 parent 5654c64 commit 5ed6993

File tree

5 files changed

+161
-113
lines changed

5 files changed

+161
-113
lines changed

xds/src/main/java/io/grpc/xds/internal/XdsHeaderValidator.java

Lines changed: 0 additions & 40 deletions
This file was deleted.

xds/src/main/java/io/grpc/xds/internal/grpcservice/GrpcServiceConfigParser.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import io.grpc.alts.GoogleDefaultChannelCredentials;
3434
import io.grpc.auth.MoreCallCredentials;
3535
import io.grpc.xds.XdsChannelCredentials;
36-
import io.grpc.xds.internal.XdsHeaderValidator;
3736
import java.time.Duration;
3837
import java.util.ArrayList;
3938
import java.util.Date;
@@ -88,17 +87,16 @@ public static GrpcServiceConfig parse(GrpcService grpcServiceProto,
8887
for (io.envoyproxy.envoy.config.core.v3.HeaderValue header : grpcServiceProto
8988
.getInitialMetadataList()) {
9089
String key = header.getKey();
90+
HeaderValue headerValue;
9191
if (key.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
92-
if (!XdsHeaderValidator.isValid(key, header.getRawValue().size())) {
93-
throw new GrpcServiceParseException("Invalid initial metadata header: " + key);
94-
}
95-
initialMetadata.add(HeaderValue.create(key, header.getRawValue()));
92+
headerValue = HeaderValue.create(key, header.getRawValue());
9693
} else {
97-
if (!XdsHeaderValidator.isValid(key, header.getValue().length())) {
98-
throw new GrpcServiceParseException("Invalid initial metadata header: " + key);
99-
}
100-
initialMetadata.add(HeaderValue.create(key, header.getValue()));
94+
headerValue = HeaderValue.create(key, header.getValue());
95+
}
96+
if (HeaderValueValidationUtils.shouldIgnore(headerValue)) {
97+
throw new GrpcServiceParseException("Invalid initial metadata header: " + key);
10198
}
99+
initialMetadata.add(headerValue);
102100
}
103101
builder.initialMetadata(initialMetadata.build());
104102

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2025 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.xds.internal.grpcservice;
18+
19+
import java.util.Locale;
20+
21+
/**
22+
* Utility class for validating HTTP headers.
23+
*/
24+
public final class HeaderValueValidationUtils {
25+
public static final int MAX_HEADER_LENGTH = 16384;
26+
27+
private HeaderValueValidationUtils() {}
28+
29+
/**
30+
* Returns true if the header key should be ignored for mutations or validation.
31+
*
32+
* @param key The header key (e.g., "content-type")
33+
*/
34+
public static boolean shouldIgnore(String key) {
35+
if (key.isEmpty() || key.length() > MAX_HEADER_LENGTH) {
36+
return true;
37+
}
38+
if (!key.equals(key.toLowerCase(Locale.ROOT))) {
39+
return true;
40+
}
41+
if (key.startsWith("grpc-")) {
42+
return true;
43+
}
44+
if (key.startsWith(":") || key.equals("host")) {
45+
return true;
46+
}
47+
return false;
48+
}
49+
50+
/**
51+
* Returns true if the header value should be ignored.
52+
*
53+
* @param header The HeaderValue containing key and values
54+
*/
55+
public static boolean shouldIgnore(HeaderValue header) {
56+
if (shouldIgnore(header.key())) {
57+
return true;
58+
}
59+
if (header.value().isPresent() && header.value().get().length() > MAX_HEADER_LENGTH) {
60+
return true;
61+
}
62+
if (header.rawValue().isPresent() && header.rawValue().get().size() > MAX_HEADER_LENGTH) {
63+
return true;
64+
}
65+
return false;
66+
}
67+
}

xds/src/test/java/io/grpc/xds/internal/XdsHeaderValidatorTest.java

Lines changed: 0 additions & 64 deletions
This file was deleted.
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright 2026 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.xds.internal.grpcservice;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
21+
import com.google.protobuf.ByteString;
22+
import org.junit.Test;
23+
import org.junit.runner.RunWith;
24+
import org.junit.runners.JUnit4;
25+
26+
/**
27+
* Unit tests for {@link HeaderValueValidationUtils}.
28+
*/
29+
@RunWith(JUnit4.class)
30+
public class HeaderValueValidationUtilsTest {
31+
32+
@Test
33+
public void shouldIgnore_string_emptyKey() {
34+
assertThat(HeaderValueValidationUtils.shouldIgnore("")).isTrue();
35+
}
36+
37+
@Test
38+
public void shouldIgnore_string_tooLongKey() {
39+
String longKey = new String(new char[16385]).replace('\0', 'a');
40+
assertThat(HeaderValueValidationUtils.shouldIgnore(longKey)).isTrue();
41+
}
42+
43+
@Test
44+
public void shouldIgnore_string_notLowercase() {
45+
assertThat(HeaderValueValidationUtils.shouldIgnore("Content-Type")).isTrue();
46+
}
47+
48+
@Test
49+
public void shouldIgnore_string_grpcPrefix() {
50+
assertThat(HeaderValueValidationUtils.shouldIgnore("grpc-timeout")).isTrue();
51+
}
52+
53+
@Test
54+
public void shouldIgnore_string_systemHeader_colon() {
55+
assertThat(HeaderValueValidationUtils.shouldIgnore(":authority")).isTrue();
56+
}
57+
58+
@Test
59+
public void shouldIgnore_string_systemHeader_host() {
60+
assertThat(HeaderValueValidationUtils.shouldIgnore("host")).isTrue();
61+
}
62+
63+
@Test
64+
public void shouldIgnore_string_valid() {
65+
assertThat(HeaderValueValidationUtils.shouldIgnore("content-type")).isFalse();
66+
}
67+
68+
@Test
69+
public void shouldIgnore_headerValue_tooLongValue() {
70+
String longValue = new String(new char[16385]).replace('\0', 'v');
71+
HeaderValue header = HeaderValue.create("content-type", longValue);
72+
assertThat(HeaderValueValidationUtils.shouldIgnore(header)).isTrue();
73+
}
74+
75+
@Test
76+
public void shouldIgnore_headerValue_tooLongRawValue() {
77+
ByteString longRawValue = ByteString.copyFrom(new byte[16385]);
78+
HeaderValue header = HeaderValue.create("content-type", longRawValue);
79+
assertThat(HeaderValueValidationUtils.shouldIgnore(header)).isTrue();
80+
}
81+
82+
@Test
83+
public void shouldIgnore_headerValue_valid() {
84+
HeaderValue header = HeaderValue.create("content-type", "application/grpc");
85+
assertThat(HeaderValueValidationUtils.shouldIgnore(header)).isFalse();
86+
}
87+
}

0 commit comments

Comments
 (0)