diff --git a/spring-cloud-sleuth-zipkin/pom.xml b/spring-cloud-sleuth-zipkin/pom.xml
index c1fc3f8a5..9a2b3ba3d 100644
--- a/spring-cloud-sleuth-zipkin/pom.xml
+++ b/spring-cloud-sleuth-zipkin/pom.xml
@@ -191,6 +191,16 @@
aspectjweaver
test
+
+ org.testcontainers
+ junit-jupiter
+ test
+
+
+ io.projectreactor.netty
+ reactor-netty
+ test
+
diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/WebClientSender.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/WebClientSender.java
index bad08821c..50ec00888 100644
--- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/WebClientSender.java
+++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin2/WebClientSender.java
@@ -37,8 +37,8 @@ public class WebClientSender extends HttpSender {
}
private static void post(String url, MediaType mediaType, byte[] json, WebClient webClient) {
- webClient.post().uri(URI.create(url)).accept(mediaType).bodyValue(json).retrieve().toBodilessEntity()
- .subscribe();
+ webClient.post().uri(URI.create(url)).accept(mediaType).contentType(mediaType).bodyValue(json).retrieve()
+ .toBodilessEntity().subscribe();
}
@Override
diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/AbstractSenderTest.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/AbstractSenderTest.java
new file mode 100644
index 000000000..0ddabe835
--- /dev/null
+++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/AbstractSenderTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.zipkin2;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+import okhttp3.mockwebserver.RecordedRequest;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import zipkin2.Call;
+import zipkin2.Endpoint;
+import zipkin2.Span;
+import zipkin2.codec.Encoding;
+import zipkin2.codec.SpanBytesEncoder;
+import zipkin2.reporter.Sender;
+import zipkin2.reporter.brave.AsyncZipkinSpanHandler;
+
+import static java.util.stream.Collectors.toList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static zipkin2.codec.SpanBytesEncoder.JSON_V2;
+
+abstract class AbstractSenderTest {
+
+ static final Span SPAN = Span.newBuilder().traceId("7180c278b62e8f6a216a2aea45d08fc9").parentId("6b221d5bc9e6496c")
+ .id("5b4185666d50f68b").name("get /backend").kind(Span.Kind.SERVER).shared(true)
+ .localEndpoint(Endpoint.newBuilder().serviceName("backend").ip("192.168.99.101").port(9000).build())
+ .timestamp(1472470996250000L).duration(100000L).putTag("http.method", "GET").putTag("http.path", "/backend")
+ .build();
+
+ public MockWebServer server = new MockWebServer();
+
+ String endpoint = this.server.url("/api/v2/spans").toString();
+
+ Sender sender = jsonSender();
+
+ abstract Sender jsonSender();
+
+ abstract Sender jsonSender(String mockedApiPath);
+
+ abstract Sender protoSender();
+
+ abstract String expectedToString();
+
+ abstract String expectedToStringWithNonEmptyApiPath(String mockedApiPath);
+
+ @AfterEach
+ void clean() throws IOException {
+ server.close();
+ }
+
+ /**
+ * Tests that json is not manipulated as a side-effect of using rest template.
+ * @throws Exception when span sending or receiving fails
+ */
+ @Test
+ public void jsonIsNormal() throws Exception {
+ this.server.enqueue(new MockResponse());
+
+ send(SPAN).execute();
+
+ RecordedRequest request = this.server.takeRequest();
+ assertThat(request.getBody().readUtf8()).isEqualTo("[" + new String(JSON_V2.encode(SPAN), "UTF-8") + "]");
+ assertThat(request.getHeader("Content-Type")).isEqualTo("application/json");
+ }
+
+ @Test
+ public void proto3() throws Exception {
+ this.server.enqueue(new MockResponse());
+ this.sender = protoSender();
+
+ send(SPAN).execute();
+
+ RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS);
+ assertThat(request.getHeader("Content-Type")).isEqualTo("application/x-protobuf");
+
+ // proto3 encoding of ListOfSpan is simply a repeated span entry
+ assertThat(request.getBody().readByteArray()).containsExactly(SpanBytesEncoder.PROTO3.encode(SPAN));
+ }
+
+ @Test
+ public void testWhereApiIsSetNonEmpty() {
+ final String mockedApiPath = "/test/v2";
+ final Sender senderWithMockedApiPath = jsonSender(mockedApiPath);
+
+ assertThat(senderWithMockedApiPath).hasToString(expectedToStringWithNonEmptyApiPath(mockedApiPath));
+ }
+
+ @Test
+ public void testWhereApiIsSetToEmpty() {
+ final String mockedApiPath = "";
+ final Sender senderWithMockedApiPath = jsonSender(mockedApiPath);
+
+ assertThat(senderWithMockedApiPath).hasToString(expectedToStringWithNonEmptyApiPath(mockedApiPath));
+ }
+
+ /**
+ * The output of toString() on {@link Sender} implementations appears in thread names
+ * created by {@link AsyncZipkinSpanHandler}. Since thread names are likely to be
+ * exposed in logs and other monitoring tools, care should be taken to ensure the
+ * toString() output is a reasonable length and does not contain sensitive
+ * information.
+ */
+ @Test
+ public void toStringContainsOnlySenderTypeAndEndpoint() {
+ assertThat(this.sender).hasToString(expectedToString());
+ }
+
+ Call send(Span... spans) {
+ SpanBytesEncoder bytesEncoder = this.sender.encoding() == Encoding.JSON ? SpanBytesEncoder.JSON_V2
+ : SpanBytesEncoder.PROTO3;
+ return this.sender.sendSpans(Stream.of(spans).map(bytesEncoder::encode).collect(toList()));
+ }
+
+}
diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/RestTemplateSenderTest.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/RestTemplateSenderTest.java
index bc201c16a..e6e972c53 100644
--- a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/RestTemplateSenderTest.java
+++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/RestTemplateSenderTest.java
@@ -16,110 +16,41 @@
package org.springframework.cloud.sleuth.zipkin2;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Stream;
-
-import okhttp3.mockwebserver.MockResponse;
-import okhttp3.mockwebserver.MockWebServer;
-import okhttp3.mockwebserver.RecordedRequest;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-import zipkin2.Call;
-import zipkin2.Endpoint;
-import zipkin2.Span;
-import zipkin2.codec.Encoding;
-import zipkin2.codec.SpanBytesEncoder;
+import zipkin2.reporter.Sender;
import org.springframework.web.client.RestTemplate;
-import static java.util.stream.Collectors.toList;
-import static org.assertj.core.api.Assertions.assertThat;
import static zipkin2.codec.SpanBytesEncoder.JSON_V2;
import static zipkin2.codec.SpanBytesEncoder.PROTO3;
-public class RestTemplateSenderTest {
+class RestTemplateSenderTest extends AbstractSenderTest {
- static final Span SPAN = Span.newBuilder().traceId("7180c278b62e8f6a216a2aea45d08fc9").parentId("6b221d5bc9e6496c")
- .id("5b4185666d50f68b").name("get /backend").kind(Span.Kind.SERVER).shared(true)
- .localEndpoint(Endpoint.newBuilder().serviceName("backend").ip("192.168.99.101").port(9000).build())
- .timestamp(1472470996250000L).duration(100000L).putTag("http.method", "GET").putTag("http.path", "/backend")
- .build();
-
- public MockWebServer server = new MockWebServer();
-
- String endpoint = this.server.url("/api/v2/spans").toString();
-
- RestTemplateSender sender = new RestTemplateSender(new RestTemplate(), this.endpoint, null, JSON_V2);
-
- @AfterEach
- void clean() throws IOException {
- server.close();
+ @Override
+ Sender jsonSender() {
+ return new RestTemplateSender(new RestTemplate(), this.endpoint, null, JSON_V2);
}
- /**
- * Tests that json is not manipulated as a side-effect of using rest template.
- * @throws Exception when span sending or receiving fails
- */
- @Test
- public void jsonIsNormal() throws Exception {
- this.server.enqueue(new MockResponse());
-
- send(SPAN).execute();
-
- assertThat(this.server.takeRequest().getBody().readUtf8())
- .isEqualTo("[" + new String(JSON_V2.encode(SPAN), "UTF-8") + "]");
+ @Override
+ Sender jsonSender(String mockedApiPath) {
+ return new RestTemplateSender(new RestTemplate(), this.endpoint, mockedApiPath, JSON_V2);
}
- @Test
- public void proto3() throws Exception {
- this.server.enqueue(new MockResponse());
- this.sender = new RestTemplateSender(new RestTemplate(), this.endpoint, "", PROTO3);
-
- send(SPAN).execute();
-
- RecordedRequest request = this.server.takeRequest(1, TimeUnit.SECONDS);
- assertThat(request.getHeader("Content-Type")).isEqualTo("application/x-protobuf");
-
- // proto3 encoding of ListOfSpan is simply a repeated span entry
- assertThat(request.getBody().readByteArray()).containsExactly(SpanBytesEncoder.PROTO3.encode(SPAN));
+ @Override
+ Sender protoSender() {
+ return new RestTemplateSender(new RestTemplate(), this.endpoint, "", PROTO3);
}
- @Test
- public void testWhereApiIsSetNonEmpty() {
- final String mockedApiPath = "/test/v2";
- final RestTemplateSender senderWithMockedApiPath = new RestTemplateSender(new RestTemplate(), this.endpoint,
- mockedApiPath, JSON_V2);
-
- assertThat(senderWithMockedApiPath.toString())
- .isEqualTo("RestTemplateSender{" + this.endpoint + mockedApiPath + "}");
+ @Override
+ String expectedToString() {
+ return "RestTemplateSender{" + this.endpoint + "/api/v2/spans}";
}
- @Test
- public void testWhereApiIsSetToEmpty() {
- final String mockedApiPath = "";
- final RestTemplateSender senderWithMockedApiPath = new RestTemplateSender(new RestTemplate(), this.endpoint,
- mockedApiPath, JSON_V2);
-
- assertThat(senderWithMockedApiPath.toString()).isEqualTo("RestTemplateSender{" + this.endpoint + "}");
- }
-
- /**
- * The output of toString() on {@link Sender} implementations appears in thread names
- * created by {@link AsyncZipkinSpanHandler}. Since thread names are likely to be
- * exposed in logs and other monitoring tools, care should be taken to ensure the
- * toString() output is a reasonable length and does not contain sensitive
- * information.
- */
- @Test
- public void toStringContainsOnlySenderTypeAndEndpoint() {
- assertThat(sender.toString()).isEqualTo("RestTemplateSender{" + this.endpoint + "/api/v2/spans}");
- }
-
- Call send(Span... spans) {
- SpanBytesEncoder bytesEncoder = this.sender.encoding() == Encoding.JSON ? SpanBytesEncoder.JSON_V2
- : SpanBytesEncoder.PROTO3;
- return this.sender.sendSpans(Stream.of(spans).map(bytesEncoder::encode).collect(toList()));
+ @Override
+ String expectedToStringWithNonEmptyApiPath(String mockedApiPath) {
+ if ("".equals(mockedApiPath)) {
+ return "RestTemplateSender{" + this.endpoint + "}";
+ }
+ return "RestTemplateSender{" + this.endpoint + mockedApiPath + "}";
}
}
diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/WebClientSenderTests.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/WebClientSenderTests.java
new file mode 100644
index 000000000..98461e837
--- /dev/null
+++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/WebClientSenderTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.sleuth.zipkin2;
+
+import zipkin2.reporter.Sender;
+
+import org.springframework.http.client.reactive.ReactorClientHttpConnector;
+import org.springframework.web.reactive.function.client.WebClient;
+
+import static zipkin2.codec.SpanBytesEncoder.JSON_V2;
+import static zipkin2.codec.SpanBytesEncoder.PROTO3;
+
+class WebClientSenderTests extends AbstractSenderTest {
+
+ @Override
+ Sender jsonSender() {
+ return new WebClientSender(WebClient.builder().clientConnector(new ReactorClientHttpConnector()).build(),
+ this.endpoint, null, JSON_V2);
+ }
+
+ @Override
+ Sender jsonSender(String mockedApiPath) {
+ return new WebClientSender(WebClient.builder().clientConnector(new ReactorClientHttpConnector()).build(),
+ this.endpoint, mockedApiPath, JSON_V2);
+ }
+
+ @Override
+ Sender protoSender() {
+ return new WebClientSender(WebClient.builder().clientConnector(new ReactorClientHttpConnector()).build(),
+ this.endpoint, "", PROTO3);
+ }
+
+ @Override
+ String expectedToString() {
+ return "WebClientSender{" + this.endpoint + "/api/v2/spans}";
+ }
+
+ @Override
+ String expectedToStringWithNonEmptyApiPath(String mockedApiPath) {
+ if ("".equals(mockedApiPath)) {
+ return "WebClientSender{" + this.endpoint + "}";
+ }
+ return "WebClientSender{" + this.endpoint + mockedApiPath + "}";
+ }
+
+}