diff --git a/sdk/src/main/java/io/dapr/client/DaprBodyPublishers.java b/sdk/src/main/java/io/dapr/client/DaprBodyPublishers.java index 37651e30b..4c48bef91 100644 --- a/sdk/src/main/java/io/dapr/client/DaprBodyPublishers.java +++ b/sdk/src/main/java/io/dapr/client/DaprBodyPublishers.java @@ -59,6 +59,18 @@ private DaprBodyPublishers() { *

Callers are still responsible for setting an appropriate * {@code Content-Type} header (typically {@code application/json}). * + *

This helper is a convenience for the default-serializer case. It does + * not honor a custom {@link io.dapr.serializer.DaprObjectSerializer} + * configured on the {@link DaprClientBuilder}. Callers with a custom serializer + * should serialize the value themselves and wrap the resulting bytes: + *

{@code
+   * byte[] bytes = mySerializer.serialize(value);
+   * BodyPublisher body = HttpRequest.BodyPublishers.ofByteArray(bytes);
+   * }
+ * The only behavior this helper adds over a direct {@code ofByteArray} call is + * choosing a length-known {@link BodyPublisher} so the JDK emits + * {@code Content-Length} rather than {@code Transfer-Encoding: chunked}. + * * @param value object to serialize; {@code null} yields an empty body. * @return a body publisher carrying the JSON-encoded bytes. * @throws UncheckedIOException if serialization fails. diff --git a/sdk/src/main/java/io/dapr/client/DaprInvokeHttpClient.java b/sdk/src/main/java/io/dapr/client/DaprInvokeHttpClient.java index 34564dcbb..f13edb6cf 100644 --- a/sdk/src/main/java/io/dapr/client/DaprInvokeHttpClient.java +++ b/sdk/src/main/java/io/dapr/client/DaprInvokeHttpClient.java @@ -13,6 +13,8 @@ package io.dapr.client; +import io.dapr.utils.Version; + import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; @@ -100,8 +102,9 @@ public URI baseUri() { /** * Creates an {@link HttpRequest.Builder} pre-bound to the Dapr invoke URL for the - * configured app id, with the {@code dapr-api-token} header attached (when one is - * configured) and the SDK's HTTP read timeout applied. + * configured app id, with the SDK {@code User-Agent} header attached, the + * {@code dapr-api-token} header attached (when one is configured) and the SDK's + * HTTP read timeout applied. * *

The {@code relativePath} is resolved against {@link #baseUri()} via * {@link URI#resolve(String)}. Per {@link URI#resolve(String)} semantics, a leading @@ -113,7 +116,9 @@ public URI baseUri() { */ public HttpRequest.Builder newRequestBuilder(String relativePath) { Objects.requireNonNull(relativePath, "relativePath"); - HttpRequest.Builder builder = HttpRequest.newBuilder().uri(baseUri.resolve(relativePath)); + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(baseUri.resolve(relativePath)) + .header(Headers.DAPR_USER_AGENT, Version.getSdkVersion()); if (daprApiToken != null && !daprApiToken.isEmpty()) { builder.header(Headers.DAPR_API_TOKEN, daprApiToken); } diff --git a/sdk/src/test/java/io/dapr/client/DaprInvokeHttpClientTest.java b/sdk/src/test/java/io/dapr/client/DaprInvokeHttpClientTest.java index 826f9583f..c3c0514a0 100644 --- a/sdk/src/test/java/io/dapr/client/DaprInvokeHttpClientTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprInvokeHttpClientTest.java @@ -13,6 +13,7 @@ package io.dapr.client; +import io.dapr.utils.Version; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -80,6 +81,16 @@ public void newRequestBuilder_resolvesRelativePathAgainstBaseUri() { request.uri().toString()); } + @Test + public void newRequestBuilder_attachesSdkUserAgentHeader() { + DaprInvokeHttpClient invoker = new DaprInvokeHttpClient(httpClient, BASE_URI, null, null); + + HttpRequest request = invoker.newRequestBuilder("orders").GET().build(); + + assertEquals(Version.getSdkVersion(), + request.headers().firstValue(Headers.DAPR_USER_AGENT).orElse(null)); + } + @Test public void newRequestBuilder_attachesApiTokenHeaderWhenConfigured() { DaprInvokeHttpClient invoker = new DaprInvokeHttpClient(httpClient, BASE_URI, "xyz", null);