Skip to content

Commit 58beb3f

Browse files
feat: attach SDK User-Agent in DaprInvokeHttpClient and clarify DaprBodyPublishers.json scope (#1758) (#1760)
newRequestBuilder now sets the User-Agent header to the SDK version, matching what the deprecated invokeMethod path emitted via DaprHttp. This is the only default the SDK can supply that callers cannot, and it is useful for runtime side triage. Other implicit invokeMethod behaviors (Content-Type default, request-id, error mapping, trace propagation) are intentionally left to the caller, since exposing the native HttpClient is the point of the migration. Also document on DaprBodyPublishers.json that the helper uses the SDK's default object serializer and is not a wrapper around a configured custom DaprObjectSerializer. Its only contribution over a plain ofByteArray call is guaranteeing a length-known BodyPublisher, which keeps the JDK on Content-Length framing and sidesteps the Transfer-Encoding: chunked race reported under load. (cherry picked from commit 43cf7c6) Signed-off-by: Javier Aliaga <javier@diagrid.io> Co-authored-by: Javier Aliaga <javier@diagrid.io>
1 parent 42c3736 commit 58beb3f

3 files changed

Lines changed: 31 additions & 3 deletions

File tree

sdk/src/main/java/io/dapr/client/DaprBodyPublishers.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ private DaprBodyPublishers() {
5959
* <p>Callers are still responsible for setting an appropriate
6060
* {@code Content-Type} header (typically {@code application/json}).
6161
*
62+
* <p>This helper is a convenience for the default-serializer case. It does
63+
* <em>not</em> honor a custom {@link io.dapr.serializer.DaprObjectSerializer}
64+
* configured on the {@link DaprClientBuilder}. Callers with a custom serializer
65+
* should serialize the value themselves and wrap the resulting bytes:
66+
* <pre>{@code
67+
* byte[] bytes = mySerializer.serialize(value);
68+
* BodyPublisher body = HttpRequest.BodyPublishers.ofByteArray(bytes);
69+
* }</pre>
70+
* The only behavior this helper adds over a direct {@code ofByteArray} call is
71+
* choosing a length-known {@link BodyPublisher} so the JDK emits
72+
* {@code Content-Length} rather than {@code Transfer-Encoding: chunked}.
73+
*
6274
* @param value object to serialize; {@code null} yields an empty body.
6375
* @return a body publisher carrying the JSON-encoded bytes.
6476
* @throws UncheckedIOException if serialization fails.

sdk/src/main/java/io/dapr/client/DaprInvokeHttpClient.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
package io.dapr.client;
1515

16+
import io.dapr.utils.Version;
17+
1618
import java.io.IOException;
1719
import java.net.URI;
1820
import java.net.http.HttpClient;
@@ -100,8 +102,9 @@ public URI baseUri() {
100102

101103
/**
102104
* Creates an {@link HttpRequest.Builder} pre-bound to the Dapr invoke URL for the
103-
* configured app id, with the {@code dapr-api-token} header attached (when one is
104-
* configured) and the SDK's HTTP read timeout applied.
105+
* configured app id, with the SDK {@code User-Agent} header attached, the
106+
* {@code dapr-api-token} header attached (when one is configured) and the SDK's
107+
* HTTP read timeout applied.
105108
*
106109
* <p>The {@code relativePath} is resolved against {@link #baseUri()} via
107110
* {@link URI#resolve(String)}. Per {@link URI#resolve(String)} semantics, a leading
@@ -113,7 +116,9 @@ public URI baseUri() {
113116
*/
114117
public HttpRequest.Builder newRequestBuilder(String relativePath) {
115118
Objects.requireNonNull(relativePath, "relativePath");
116-
HttpRequest.Builder builder = HttpRequest.newBuilder().uri(baseUri.resolve(relativePath));
119+
HttpRequest.Builder builder = HttpRequest.newBuilder()
120+
.uri(baseUri.resolve(relativePath))
121+
.header(Headers.DAPR_USER_AGENT, Version.getSdkVersion());
117122
if (daprApiToken != null && !daprApiToken.isEmpty()) {
118123
builder.header(Headers.DAPR_API_TOKEN, daprApiToken);
119124
}

sdk/src/test/java/io/dapr/client/DaprInvokeHttpClientTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
package io.dapr.client;
1515

16+
import io.dapr.utils.Version;
1617
import org.junit.jupiter.api.BeforeEach;
1718
import org.junit.jupiter.api.Test;
1819

@@ -80,6 +81,16 @@ public void newRequestBuilder_resolvesRelativePathAgainstBaseUri() {
8081
request.uri().toString());
8182
}
8283

84+
@Test
85+
public void newRequestBuilder_attachesSdkUserAgentHeader() {
86+
DaprInvokeHttpClient invoker = new DaprInvokeHttpClient(httpClient, BASE_URI, null, null);
87+
88+
HttpRequest request = invoker.newRequestBuilder("orders").GET().build();
89+
90+
assertEquals(Version.getSdkVersion(),
91+
request.headers().firstValue(Headers.DAPR_USER_AGENT).orElse(null));
92+
}
93+
8394
@Test
8495
public void newRequestBuilder_attachesApiTokenHeaderWhenConfigured() {
8596
DaprInvokeHttpClient invoker = new DaprInvokeHttpClient(httpClient, BASE_URI, "xyz", null);

0 commit comments

Comments
 (0)