diff --git a/sdk-tests/src/test/java/io/dapr/it/methodinvoke/http/GH_1314_IT.java b/sdk-tests/src/test/java/io/dapr/it/methodinvoke/http/GH_1314_IT.java new file mode 100644 index 000000000..112fad9cb --- /dev/null +++ b/sdk-tests/src/test/java/io/dapr/it/methodinvoke/http/GH_1314_IT.java @@ -0,0 +1,87 @@ +package io.dapr.it.methodinvoke.http; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.dapr.client.DaprClient; +import io.dapr.client.DaprClientBuilder; +import io.dapr.client.domain.HttpExtension; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.post; +import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class GH_1314_IT { + + private static WireMockServer wireMockServer; + + @BeforeAll + static void setup() { + wireMockServer = new WireMockServer(3500); + + wireMockServer.start(); + + WireMock.configureFor("localhost", 3500); + + stubFor(post( + urlEqualTo("/v1.0/invoke/say-hello/method/say-hello/hello") + ) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("\"Hello from say-hello app!\""))); + } + + @AfterAll + static void teardown() { + wireMockServer.stop(); + } + + @Test + void testInvokeSayHelloWithoutLeadingSlash() { + String urlWithoutLeadingSlash = "say-hello/hello"; + + sayHelloUsingURL(urlWithoutLeadingSlash); + } + + @Test + void testInvokeSayHelloWithLeadingSlash() { + String urlWithLeadingSlash = "/say-hello/hello"; + + sayHelloUsingURL(urlWithLeadingSlash); + } + + private static void sayHelloUsingURL(String url) { + DaprClient client = new DaprClientBuilder().build(); + + try { + Map requestData = Map.of("message", "Hello"); + + String response = client.invokeMethod( + "say-hello", + url, + requestData, + HttpExtension.POST, + String.class + ).block(); + + assertEquals("Hello from say-hello app!", response); + } catch (Exception e) { + fail("Exception occurred: " + e.getMessage()); + } finally { + try { + client.close(); + } catch (Exception e) { + fail(e); + } + } + } + +} diff --git a/sdk/src/main/java/io/dapr/client/DaprHttp.java b/sdk/src/main/java/io/dapr/client/DaprHttp.java index 5b23d733e..489fd4049 100644 --- a/sdk/src/main/java/io/dapr/client/DaprHttp.java +++ b/sdk/src/main/java/io/dapr/client/DaprHttp.java @@ -24,7 +24,6 @@ import reactor.util.context.ContextView; import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -324,10 +323,17 @@ public class DaprHttp implements AutoCloseable { private static URI createUri(URI uri, String[] pathSegments, Map> urlParameters) { String path = createPath(uri, pathSegments); String query = createQuery(urlParameters); + StringBuilder result = new StringBuilder(); + + result.append(uri.getScheme()).append("://").append(uri.getAuthority()).append(path); + + if (query != null) { + result.append("?").append(query); + } try { - return new URI(uri.getScheme(), uri.getAuthority(), path, query, null); - } catch (URISyntaxException exception) { + return URI.create(result.toString()); + } catch (IllegalArgumentException exception) { throw new DaprException(exception); } } @@ -346,6 +352,10 @@ public class DaprHttp implements AutoCloseable { } for (String segment : pathSegments) { + if (segment == null || segment.isEmpty()) { + continue; // Skip empty segments + } + pathBuilder.append(encodePathSegment(segment)).append("/"); // Encode each segment } @@ -363,6 +373,11 @@ public class DaprHttp implements AutoCloseable { for (Map.Entry> entry : urlParameters.entrySet()) { String key = entry.getKey(); + + if (key == null || key.isEmpty()) { + continue; // Skip empty keys + } + List values = entry.getValue(); for (String value : values) {