Skip to content

Commit f20f172

Browse files
authored
Fixes problems converting HTTP/2 headers to Metadata and vice versa. Adds new test. (#10300)
1 parent 91c3cae commit f20f172

File tree

3 files changed

+83
-12
lines changed

3 files changed

+83
-12
lines changed

webserver/grpc/pom.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,9 @@
8787
<scope>test</scope>
8888
</dependency>
8989
<dependency>
90-
<groupId>io.helidon.codegen</groupId>
91-
<artifactId>helidon-codegen</artifactId>
90+
<groupId>org.mockito</groupId>
91+
<artifactId>mockito-core</artifactId>
92+
<scope>test</scope>
9293
</dependency>
9394
</dependencies>
9495

webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcHeadersUtil.java

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2024, 2025 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,15 +31,19 @@ class GrpcHeadersUtil {
3131
private GrpcHeadersUtil() {
3232
}
3333

34-
static void updateHeaders(WritableHeaders<?> writable, Metadata headers) {
34+
static void updateHeaders(WritableHeaders<?> headers, Metadata metadata) {
3535
Base64.Encoder encoder = Base64.getEncoder();
36-
headers.keys().forEach(name -> {
36+
metadata.keys().forEach(name -> {
3737
if (name.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
38-
byte[] binary = headers.get(Metadata.Key.of(name, Metadata.BINARY_BYTE_MARSHALLER));
39-
writable.add(HeaderNames.create(name, new String(encoder.encode(binary), US_ASCII)));
38+
Metadata.Key<byte[]> key = Metadata.Key.of(name, Metadata.BINARY_BYTE_MARSHALLER);
39+
byte[] binary = metadata.get(key);
40+
headers.add(HeaderNames.create(name, new String(encoder.encode(binary), US_ASCII)));
4041
} else {
41-
String ascii = headers.get(Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER));
42-
writable.add(HeaderNames.create(name, ascii));
42+
Metadata.Key<String> key = Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER);
43+
Iterable<String> ascii = metadata.getAll(key);
44+
if (ascii != null) {
45+
ascii.forEach(v -> headers.add(HeaderNames.create(name), v));
46+
}
4347
}
4448
});
4549
}
@@ -50,10 +54,11 @@ static Metadata toMetadata(Http2Headers headers) {
5054
headers.httpHeaders().forEach(header -> {
5155
String name = header.name();
5256
if (name.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
53-
metadata.put(Metadata.Key.of(name, Metadata.BINARY_BYTE_MARSHALLER),
54-
decoder.decode(name.getBytes(US_ASCII)));
57+
Metadata.Key<byte[]> key = Metadata.Key.of(name, Metadata.BINARY_BYTE_MARSHALLER);
58+
metadata.put(key, decoder.decode(header.valueBytes()));
5559
} else {
56-
metadata.put(Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER), name);
60+
Metadata.Key<String> key = Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER);
61+
header.allValues().forEach(value -> metadata.put(key, value));
5762
}
5863
});
5964
return metadata;
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) 2025 Oracle and/or its affiliates.
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+
package io.helidon.webserver.grpc;
17+
18+
import java.util.HashSet;
19+
import java.util.List;
20+
import java.util.Set;
21+
22+
import io.helidon.http.HeaderNames;
23+
import io.helidon.http.WritableHeaders;
24+
import io.helidon.http.http2.Http2Headers;
25+
26+
import io.grpc.Metadata;
27+
import org.junit.jupiter.api.Test;
28+
29+
import static org.hamcrest.CoreMatchers.hasItem;
30+
import static org.hamcrest.CoreMatchers.is;
31+
import static org.hamcrest.MatcherAssert.assertThat;
32+
import static org.mockito.Mockito.mock;
33+
import static org.mockito.Mockito.when;
34+
35+
class GrpcHeadersUtilTest {
36+
37+
@Test
38+
void testUpdateHeaders() {
39+
Metadata metadata = new Metadata();
40+
Metadata.Key<String> key = Metadata.Key.of("cookie", Metadata.ASCII_STRING_MARSHALLER);
41+
metadata.put(key, "sugar");
42+
metadata.put(key, "almond");
43+
WritableHeaders<?> headers = WritableHeaders.create();
44+
GrpcHeadersUtil.updateHeaders(headers, metadata);
45+
assertThat(headers.size(), is(2));
46+
List<String> values = headers.get(HeaderNames.COOKIE).allValues();
47+
assertThat(values, hasItem("sugar"));
48+
assertThat(values, hasItem("almond"));
49+
}
50+
51+
@Test
52+
void testToMetadata() {
53+
WritableHeaders<?> headers = WritableHeaders.create();
54+
headers.add(HeaderNames.COOKIE, "sugar", "almond");
55+
Http2Headers http2Headers = mock(Http2Headers.class);
56+
when(http2Headers.httpHeaders()).thenReturn(headers);
57+
Metadata metadata = GrpcHeadersUtil.toMetadata(http2Headers);
58+
Metadata.Key<String> key = Metadata.Key.of("cookie", Metadata.ASCII_STRING_MARSHALLER);
59+
assertThat(metadata.containsKey(key), is(true));
60+
Set<String> values = new HashSet<>();
61+
metadata.getAll(key).forEach(values::add);
62+
assertThat(values, hasItem("sugar"));
63+
assertThat(values, hasItem("almond"));
64+
}
65+
}

0 commit comments

Comments
 (0)