Skip to content

Commit c2ab7e2

Browse files
authored
Ensures gRPC client calls pass metadata as headers (#10301)
Ensures gRPC client calls pass metadata as headers. Moves utility class from webserver-grpc to grpc-core. Adds test that shows headers and metadata properly merged in a client call.
1 parent f20f172 commit c2ab7e2

File tree

8 files changed

+90
-18
lines changed

8 files changed

+90
-18
lines changed

grpc/core/pom.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
<groupId>io.helidon.http</groupId>
4242
<artifactId>helidon-http</artifactId>
4343
</dependency>
44+
<dependency>
45+
<groupId>io.helidon.http</groupId>
46+
<artifactId>helidon-http-http2</artifactId>
47+
</dependency>
4448
<dependency>
4549
<groupId>io.helidon.tracing</groupId>
4650
<artifactId>helidon-tracing</artifactId>
@@ -77,5 +81,10 @@
7781
<artifactId>hamcrest-all</artifactId>
7882
<scope>test</scope>
7983
</dependency>
84+
<dependency>
85+
<groupId>org.mockito</groupId>
86+
<artifactId>mockito-core</artifactId>
87+
<scope>test</scope>
88+
</dependency>
8089
</dependencies>
8190
</project>

webserver/grpc/src/main/java/io/helidon/webserver/grpc/GrpcHeadersUtil.java renamed to grpc/core/src/main/java/io/helidon/grpc/core/GrpcHeadersUtil.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package io.helidon.webserver.grpc;
17+
package io.helidon.grpc.core;
1818

1919
import java.util.Base64;
2020

@@ -26,12 +26,21 @@
2626

2727
import static java.nio.charset.StandardCharsets.US_ASCII;
2828

29-
class GrpcHeadersUtil {
29+
/**
30+
* Utility class to map HTTP/2 headers to Metadata.
31+
*/
32+
public class GrpcHeadersUtil {
3033

3134
private GrpcHeadersUtil() {
3235
}
3336

34-
static void updateHeaders(WritableHeaders<?> headers, Metadata metadata) {
37+
/**
38+
* Updates headers with metadata.
39+
*
40+
* @param headers the headers to update
41+
* @param metadata the metadata
42+
*/
43+
public static void updateHeaders(WritableHeaders<?> headers, Metadata metadata) {
3544
Base64.Encoder encoder = Base64.getEncoder();
3645
metadata.keys().forEach(name -> {
3746
if (name.endsWith(Metadata.BINARY_HEADER_SUFFIX)) {
@@ -48,7 +57,13 @@ static void updateHeaders(WritableHeaders<?> headers, Metadata metadata) {
4857
});
4958
}
5059

51-
static Metadata toMetadata(Http2Headers headers) {
60+
/**
61+
* Converts a set of HTTP/2 headers into a Metadata instance.
62+
*
63+
* @param headers the headers to convert
64+
* @return the new metadata
65+
*/
66+
public static Metadata toMetadata(Http2Headers headers) {
5267
Base64.Decoder decoder = Base64.getDecoder();
5368
Metadata metadata = new Metadata();
5469
headers.httpHeaders().forEach(header -> {

grpc/core/src/main/java/module-info.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, 2024 Oracle and/or its affiliates.
2+
* Copyright (c) 2022, 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.
@@ -21,6 +21,7 @@
2121

2222
requires io.helidon.common;
2323
requires io.helidon.http;
24+
requires io.helidon.http.http2;
2425
requires io.helidon.common.context;
2526
requires io.helidon.tracing;
2627

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
package io.helidon.webserver.grpc;
16+
package io.helidon.grpc.core;
1717

1818
import java.util.HashSet;
1919
import java.util.List;

webclient/grpc/src/main/java/io/helidon/webclient/grpc/GrpcBaseClientCall.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import io.helidon.common.LazyValue;
3232
import io.helidon.common.buffers.BufferData;
3333
import io.helidon.common.socket.HelidonSocket;
34+
import io.helidon.grpc.core.GrpcHeadersUtil;
3435
import io.helidon.http.Header;
3536
import io.helidon.http.HeaderNames;
3637
import io.helidon.http.HeaderValues;
@@ -175,16 +176,22 @@ public Duration readTimeout() {
175176
startStreamingThreads();
176177

177178
// send HEADERS frame
178-
WritableHeaders<?> headers = WritableHeaders.create();
179-
headers.add(Http2Headers.AUTHORITY_NAME, clientUri.authority());
180-
headers.add(Http2Headers.METHOD_NAME, "POST");
181-
headers.add(Http2Headers.PATH_NAME, "/" + methodDescriptor.getFullMethodName());
182-
headers.add(Http2Headers.SCHEME_NAME, "http");
183-
headers.add(GRPC_CONTENT_TYPE);
184-
headers.add(GRPC_ACCEPT_ENCODING);
179+
WritableHeaders<?> headers = setupHeaders(metadata, clientUri.authority(), methodDescriptor.getFullMethodName());
185180
clientStream.writeHeaders(Http2Headers.create(headers), false);
186181
}
187182

183+
static WritableHeaders<?> setupHeaders(Metadata metadata, String authority, String methodName) {
184+
WritableHeaders<?> headers = WritableHeaders.create();
185+
GrpcHeadersUtil.updateHeaders(headers, metadata);
186+
headers.set(Http2Headers.AUTHORITY_NAME, authority);
187+
headers.set(Http2Headers.METHOD_NAME, "POST");
188+
headers.set(Http2Headers.PATH_NAME, "/" + methodName);
189+
headers.set(Http2Headers.SCHEME_NAME, "http");
190+
headers.set(GRPC_CONTENT_TYPE);
191+
headers.set(GRPC_ACCEPT_ENCODING);
192+
return headers;
193+
}
194+
188195
abstract void startStreamingThreads();
189196

190197
/**
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.webclient.grpc;
17+
18+
import io.helidon.http.HeaderNames;
19+
import io.helidon.http.WritableHeaders;
20+
import io.helidon.http.http2.Http2Headers;
21+
22+
import io.grpc.Metadata;
23+
import org.junit.jupiter.api.Test;
24+
25+
import static org.hamcrest.CoreMatchers.is;
26+
import static org.hamcrest.MatcherAssert.assertThat;
27+
import static org.hamcrest.Matchers.greaterThan;
28+
29+
class GrpcBaseClientCallTest {
30+
31+
@Test
32+
void testHeadersAndMetadata() {
33+
Metadata metadata = new Metadata();
34+
Metadata.Key<String> key = Metadata.Key.of("cookie", Metadata.ASCII_STRING_MARSHALLER);
35+
metadata.put(key, "sugar");
36+
WritableHeaders<?> headers = GrpcBaseClientCall.setupHeaders(metadata, "localhost", "foo");
37+
assertThat(headers.size(), greaterThan(2));
38+
assertThat(headers.get(Http2Headers.AUTHORITY_NAME).get(), is("localhost"));
39+
assertThat(headers.get(Http2Headers.METHOD_NAME).get(), is("POST"));
40+
assertThat(headers.get(Http2Headers.PATH_NAME).get(), is("/foo"));
41+
assertThat(headers.get(Http2Headers.SCHEME_NAME).get(), is("http"));
42+
assertThat(headers.get(HeaderNames.COOKIE).get(), is("sugar"));
43+
}
44+
}

webserver/grpc/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@
8686
<artifactId>hamcrest-all</artifactId>
8787
<scope>test</scope>
8888
</dependency>
89-
<dependency>
90-
<groupId>org.mockito</groupId>
91-
<artifactId>mockito-core</artifactId>
92-
<scope>test</scope>
93-
</dependency>
9489
</dependencies>
9590

9691
<build>

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131

3232
import io.helidon.common.LazyValue;
3333
import io.helidon.common.buffers.BufferData;
34+
import io.helidon.grpc.core.GrpcHeadersUtil;
3435
import io.helidon.http.Header;
3536
import io.helidon.http.HeaderName;
3637
import io.helidon.http.HeaderNames;

0 commit comments

Comments
 (0)