Adds missing content-type to webclient zipkin sender; fixes gh-2139
This commit is contained in:
@@ -191,6 +191,16 @@
|
||||
<artifactId>aspectjweaver</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor.netty</groupId>
|
||||
<artifactId>reactor-netty</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Void> 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()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Void> 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 + "}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 + "}";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user