convert http url connection test from groovy to java (#7676)

related to #7195
This commit is contained in:
xiangtianyu 2023-02-01 21:13:25 +08:00 committed by GitHub
parent 880c6db059
commit 6885c80fef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 573 additions and 516 deletions

View File

@ -1,57 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.instrumentation.test.AgentTestTrait
import io.opentelemetry.instrumentation.test.base.HttpClientTest
class HttpUrlConnectionResponseCodeOnlyTest extends HttpClientTest<HttpURLConnection> implements AgentTestTrait {
@Override
HttpURLConnection buildRequest(String method, URI uri, Map<String, String> headers) {
return uri.toURL().openConnection() as HttpURLConnection
}
@Override
int sendRequest(HttpURLConnection connection, String method, URI uri, Map<String, String> headers) {
try {
connection.setRequestMethod(method)
connection.connectTimeout = CONNECT_TIMEOUT_MS
if (uri.toString().contains("/read-timeout")) {
connection.readTimeout = READ_TIMEOUT_MS
}
headers.each { connection.setRequestProperty(it.key, it.value) }
connection.setRequestProperty("Connection", "close")
return connection.getResponseCode()
} finally {
connection.disconnect()
}
}
@Override
int maxRedirects() {
20
}
@Override
Integer responseCodeOnRedirectError() {
return 302
}
@Override
boolean testReusedRequest() {
// HttpURLConnection can't be reused
return false
}
@Override
boolean testCallback() {
return false
}
@Override
boolean testReadTimeout() {
true
}
}

View File

@ -1,333 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.api.trace.Span
import io.opentelemetry.instrumentation.test.AgentTestTrait
import io.opentelemetry.instrumentation.test.base.HttpClientTest
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import spock.lang.Unroll
import static io.opentelemetry.api.trace.SpanKind.CLIENT
import static io.opentelemetry.api.trace.SpanKind.SERVER
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP
class HttpUrlConnectionTest extends HttpClientTest<HttpURLConnection> implements AgentTestTrait {
static final RESPONSE = "Hello."
static final STATUS = 200
@Override
HttpURLConnection buildRequest(String method, URI uri, Map<String, String> headers) {
return uri.toURL().openConnection() as HttpURLConnection
}
@Override
int sendRequest(HttpURLConnection connection, String method, URI uri, Map<String, String> headers) {
if (uri.toString().contains("/read-timeout")) {
connection.readTimeout = READ_TIMEOUT_MS
}
try {
connection.setRequestMethod(method)
headers.each { connection.setRequestProperty(it.key, it.value) }
connection.setRequestProperty("Connection", "close")
connection.useCaches = true
connection.connectTimeout = CONNECT_TIMEOUT_MS
def parentSpan = Span.current()
def stream = connection.inputStream
assert Span.current() == parentSpan
stream.readLines()
stream.close()
return connection.getResponseCode()
} finally {
connection.disconnect()
}
}
@Override
int maxRedirects() {
20
}
@Override
Integer responseCodeOnRedirectError() {
return 302
}
@Override
boolean testReusedRequest() {
// HttpURLConnection can't be reused
return false
}
@Override
boolean testCallback() {
return false
}
@Override
boolean testReadTimeout() {
true
}
@Unroll
def "trace request (useCaches: #useCaches)"() {
setup:
def url = resolveAddress("/success").toURL()
runWithSpan("someTrace") {
HttpURLConnection connection = url.openConnection()
connection.useCaches = useCaches
assert Span.current().getSpanContext().isValid()
def stream = connection.inputStream
def lines = stream.readLines()
stream.close()
assert connection.getResponseCode() == STATUS
assert lines == [RESPONSE]
// call again to ensure the cycling is ok
connection = url.openConnection()
connection.useCaches = useCaches
assert Span.current().getSpanContext().isValid()
// call before input stream to test alternate behavior
assert connection.getResponseCode() == STATUS
connection.inputStream
stream = connection.inputStream // one more to ensure state is working
lines = stream.readLines()
stream.close()
assert lines == [RESPONSE]
}
expect:
assertTraces(1) {
trace(0, 5) {
span(0) {
name "someTrace"
hasNoParent()
attributes {
}
}
span(1) {
name "HTTP GET"
kind CLIENT
childOf span(0)
attributes {
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_PEER_NAME" "localhost"
"$SemanticAttributes.NET_PEER_PORT" server.httpPort()
"$SemanticAttributes.HTTP_URL" "$url"
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" STATUS
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long
}
}
span(2) {
name "test-http-server"
kind SERVER
childOf span(1)
attributes {
}
}
span(3) {
name "HTTP GET"
kind CLIENT
childOf span(0)
attributes {
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_PEER_NAME" "localhost"
"$SemanticAttributes.NET_PEER_PORT" server.httpPort()
"$SemanticAttributes.HTTP_URL" "$url"
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" STATUS
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long
}
}
span(4) {
name "test-http-server"
kind SERVER
childOf span(3)
attributes {
}
}
}
}
where:
useCaches << [false, true]
}
def "test broken API usage (#iteration)"() {
setup:
def url = resolveAddress("/success").toURL()
HttpURLConnection connection = runWithSpan("someTrace") {
HttpURLConnection con = url.openConnection()
con.setRequestProperty("Connection", "close")
assert Span.current().getSpanContext().isValid()
assert con.getResponseCode() == STATUS
return con
}
expect:
assertTraces(1) {
trace(0, 3) {
span(0) {
name "someTrace"
hasNoParent()
attributes {
}
}
span(1) {
name "HTTP GET"
kind CLIENT
childOf span(0)
attributes {
"$SemanticAttributes.NET_PEER_NAME" "localhost"
"$SemanticAttributes.NET_PEER_PORT" server.httpPort()
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.HTTP_URL" "$url"
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" STATUS
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long
}
}
serverSpan(it, 2, span(1))
}
}
cleanup:
connection.disconnect()
where:
iteration << (1..10)
}
def "test post request"() {
setup:
def url = resolveAddress("/success").toURL()
runWithSpan("someTrace") {
HttpURLConnection connection = url.openConnection()
connection.setRequestMethod("POST")
String urlParameters = "q=ASDF&w=&e=&r=12345&t="
// Send post request
connection.setDoOutput(true)
DataOutputStream wr = new DataOutputStream(connection.getOutputStream())
wr.writeBytes(urlParameters)
wr.flush()
wr.close()
assert connection.getResponseCode() == STATUS
def stream = connection.inputStream
def lines = stream.readLines()
stream.close()
assert lines == [RESPONSE]
}
expect:
assertTraces(1) {
trace(0, 3) {
span(0) {
name "someTrace"
hasNoParent()
attributes {
}
}
span(1) {
name "HTTP POST"
kind CLIENT
childOf span(0)
attributes {
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_PEER_NAME" "localhost"
"$SemanticAttributes.NET_PEER_PORT" server.httpPort()
"$SemanticAttributes.HTTP_URL" "$url"
"$SemanticAttributes.HTTP_METHOD" "POST"
"$SemanticAttributes.HTTP_STATUS_CODE" STATUS
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" Long
"$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long
}
}
span(2) {
name "test-http-server"
kind SERVER
childOf span(1)
attributes {
}
}
}
}
}
def "sun.net.www.protocol.http.HttpURLConnection.getOutputStream should transform GET into POST"() {
setup:
def url = resolveAddress("/success").toURL()
runWithSpan("someTrace") {
HttpURLConnection connection = url.openConnection()
def connectionClassName = connection.getClass().getName()
assert "sun.net.www.protocol.http.HttpURLConnection".equals(connectionClassName)
connection.setRequestMethod("GET")
String urlParameters = "q=ASDF&w=&e=&r=12345&t="
// Send POST request
connection.setDoOutput(true)
DataOutputStream wr = new DataOutputStream(connection.getOutputStream())
wr.writeBytes(urlParameters)
wr.flush()
wr.close()
assert connection.getResponseCode() == STATUS
def stream = connection.inputStream
def lines = stream.readLines()
stream.close()
assert lines == [RESPONSE]
}
expect:
assertTraces(1) {
trace(0, 3) {
span(0) {
name "someTrace"
hasNoParent()
attributes {
}
}
span(1) {
name "HTTP POST"
kind CLIENT
childOf span(0)
attributes {
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_PEER_NAME" "localhost"
"$SemanticAttributes.NET_PEER_PORT" server.httpPort()
"$SemanticAttributes.HTTP_URL" "$url"
"$SemanticAttributes.HTTP_METHOD" "POST"
"$SemanticAttributes.HTTP_STATUS_CODE" STATUS
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" Long
"$SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH" Long
}
}
span(2) {
name "test-http-server"
kind SERVER
childOf span(1)
attributes {
}
}
}
}
}
}

View File

@ -1,64 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.api.trace.Span
import io.opentelemetry.instrumentation.test.AgentTestTrait
import io.opentelemetry.instrumentation.test.base.HttpClientTest
class HttpUrlConnectionUseCachesFalseTest extends HttpClientTest<HttpURLConnection> implements AgentTestTrait {
@Override
HttpURLConnection buildRequest(String method, URI uri, Map<String, String> headers) {
return uri.toURL().openConnection() as HttpURLConnection
}
@Override
int sendRequest(HttpURLConnection connection, String method, URI uri, Map<String, String> headers) {
try {
connection.setRequestMethod(method)
headers.each { connection.setRequestProperty(it.key, it.value) }
connection.setRequestProperty("Connection", "close")
connection.useCaches = false
connection.connectTimeout = CONNECT_TIMEOUT_MS
if (uri.toString().contains("/read-timeout")) {
connection.readTimeout = READ_TIMEOUT_MS
}
def parentSpan = Span.current()
def stream = connection.inputStream
assert Span.current() == parentSpan
stream.readLines()
stream.close()
return connection.getResponseCode()
} finally {
connection.disconnect()
}
}
@Override
int maxRedirects() {
20
}
@Override
Integer responseCodeOnRedirectError() {
return 302
}
@Override
boolean testReusedRequest() {
// HttpURLConnection can't be reused
return false
}
@Override
boolean testCallback() {
return false
}
@Override
boolean testReadTimeout() {
true
}
}

View File

@ -1,62 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.api.trace.Span
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import static io.opentelemetry.api.trace.SpanKind.CLIENT
import static io.opentelemetry.api.trace.StatusCode.ERROR
import static io.opentelemetry.instrumentation.test.utils.PortUtils.UNUSABLE_PORT
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP
class UrlConnectionTest extends AgentInstrumentationSpecification {
def "trace request with connection failure #scheme"() {
when:
runWithSpan("someTrace") {
URLConnection connection = url.openConnection()
connection.setConnectTimeout(10000)
connection.setReadTimeout(10000)
assert Span.current() != null
connection.inputStream
}
then:
thrown ConnectException
expect:
assertTraces(1) {
trace(0, 2) {
span(0) {
name "someTrace"
hasNoParent()
status ERROR
errorEvent ConnectException, String
}
span(1) {
name "HTTP GET"
kind CLIENT
childOf span(0)
status ERROR
errorEvent ConnectException, String
attributes {
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_PEER_NAME" "localhost"
"$SemanticAttributes.NET_PEER_PORT" UNUSABLE_PORT
"$SemanticAttributes.HTTP_URL" "$url"
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
}
}
}
}
where:
scheme << ["http", "https"]
url = new URI("$scheme://localhost:$UNUSABLE_PORT").toURL()
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Map;
import org.junit.jupiter.api.extension.RegisterExtension;
class HttpUrlConnectionResponseCodeOnlyTest extends AbstractHttpClientTest<HttpURLConnection> {
@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
@Override
public HttpURLConnection buildRequest(String method, URI uri, Map<String, String> headers)
throws Exception {
return (HttpURLConnection) uri.toURL().openConnection();
}
@Override
public int sendRequest(
HttpURLConnection connection, String method, URI uri, Map<String, String> headers)
throws Exception {
try {
connection.setRequestMethod(method);
connection.setConnectTimeout((int) CONNECTION_TIMEOUT.toMillis());
if (uri.toString().contains("/read-timeout")) {
connection.setReadTimeout((int) READ_TIMEOUT.toMillis());
}
headers.forEach(connection::setRequestProperty);
connection.setRequestProperty("Connection", "close");
return connection.getResponseCode();
} finally {
connection.disconnect();
}
}
@Override
public int maxRedirects() {
return 20;
}
@Override
public Integer responseCodeOnRedirectError() {
return 302;
}
@Override
public boolean testReusedRequest() {
// HttpURLConnection can't be reused
return false;
}
@Override
public boolean testCallback() {
return false;
}
@Override
public boolean testReadTimeout() {
return true;
}
}

View File

@ -0,0 +1,316 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
import static io.opentelemetry.api.trace.SpanKind.SERVER;
import static io.opentelemetry.javaagent.instrumentation.httpurlconnection.StreamUtils.readLines;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.assertj.core.api.AbstractLongAssert;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class HttpUrlConnectionTest extends AbstractHttpClientTest<HttpURLConnection> {
@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
static final List<String> RESPONSE = Collections.singletonList("Hello.");
static final int STATUS = 200;
@Override
public HttpURLConnection buildRequest(String method, URI uri, Map<String, String> headers)
throws Exception {
return (HttpURLConnection) uri.toURL().openConnection();
}
@Override
public int sendRequest(
HttpURLConnection connection, String method, URI uri, Map<String, String> headers)
throws Exception {
if (uri.toString().contains("/read-timeout")) {
connection.setReadTimeout((int) READ_TIMEOUT.toMillis());
}
try {
connection.setRequestMethod(method);
headers.forEach(connection::setRequestProperty);
connection.setRequestProperty("Connection", "close");
connection.setUseCaches(true);
connection.setConnectTimeout((int) CONNECTION_TIMEOUT.toMillis());
Span parentSpan = Span.current();
InputStream stream = connection.getInputStream();
assertThat(Span.current()).isEqualTo(parentSpan);
stream.close();
return connection.getResponseCode();
} finally {
connection.disconnect();
}
}
@Override
public int maxRedirects() {
return 20;
}
@Override
public Integer responseCodeOnRedirectError() {
return 302;
}
@Override
public boolean testReusedRequest() {
// HttpURLConnection can't be reused
return false;
}
@Override
public boolean testCallback() {
return false;
}
@Override
public boolean testReadTimeout() {
return true;
}
@ParameterizedTest
@ValueSource(booleans = {false, true})
public void traceRequest(boolean useCache) throws IOException {
URL url = resolveAddress("/success").toURL();
testing.runWithSpan(
"someTrace",
() -> {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setUseCaches(useCache);
assertThat(Span.current().getSpanContext().isValid()).isTrue();
InputStream stream = connection.getInputStream();
List<String> lines = readLines(stream);
stream.close();
assertThat(connection.getResponseCode()).isEqualTo(STATUS);
assertThat(lines).isEqualTo(RESPONSE);
// call again to ensure the cycling is ok
connection = (HttpURLConnection) url.openConnection();
connection.setUseCaches(useCache);
assertThat(Span.current().getSpanContext().isValid()).isTrue();
// call before input stream to test alternate behavior
assertThat(connection.getResponseCode()).isEqualTo(STATUS);
connection.getInputStream();
stream = connection.getInputStream(); // one more to ensure state is working
lines = readLines(stream);
stream.close();
assertThat(lines).isEqualTo(RESPONSE);
});
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasNoParent(),
span ->
span.hasName("HTTP GET")
.hasKind(CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP),
equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"),
equalTo(SemanticAttributes.NET_PEER_PORT, url.getPort()),
equalTo(SemanticAttributes.HTTP_URL, url.toString()),
equalTo(SemanticAttributes.HTTP_METHOD, "GET"),
equalTo(SemanticAttributes.HTTP_STATUS_CODE, STATUS),
equalTo(SemanticAttributes.HTTP_FLAVOR, "1.1"),
satisfies(
SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
AbstractLongAssert::isNotNegative)),
span ->
span.hasName("test-http-server").hasKind(SERVER).hasParent(trace.getSpan(1)),
span ->
span.hasName("HTTP GET")
.hasKind(CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP),
equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"),
equalTo(SemanticAttributes.NET_PEER_PORT, url.getPort()),
equalTo(SemanticAttributes.HTTP_URL, url.toString()),
equalTo(SemanticAttributes.HTTP_METHOD, "GET"),
equalTo(SemanticAttributes.HTTP_STATUS_CODE, STATUS),
equalTo(SemanticAttributes.HTTP_FLAVOR, "1.1"),
satisfies(
SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
AbstractLongAssert::isNotNegative)),
span ->
span.hasName("test-http-server").hasKind(SERVER).hasParent(trace.getSpan(3))));
}
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10})
public void testBrokenApiUsage() throws IOException {
URL url = resolveAddress("/success").toURL();
HttpURLConnection connection =
testing.runWithSpan(
"someTrace",
() -> {
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Connection", "close");
assertThat(Span.current().getSpanContext().isValid()).isTrue();
assertThat(con.getResponseCode()).isEqualTo(STATUS);
return con;
});
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasNoParent(),
span ->
span.hasName("HTTP GET")
.hasKind(CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP),
equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"),
equalTo(SemanticAttributes.NET_PEER_PORT, url.getPort()),
equalTo(SemanticAttributes.HTTP_URL, url.toString()),
equalTo(SemanticAttributes.HTTP_METHOD, "GET"),
equalTo(SemanticAttributes.HTTP_STATUS_CODE, STATUS),
equalTo(SemanticAttributes.HTTP_FLAVOR, "1.1"),
satisfies(
SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
AbstractLongAssert::isNotNegative)),
span ->
span.hasName("test-http-server").hasKind(SERVER).hasParent(trace.getSpan(1))));
connection.disconnect();
}
@Test
public void testPostRequest() throws IOException {
URL url = resolveAddress("/success").toURL();
testing.runWithSpan(
"someTrace",
() -> {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
String urlParameters = "q=ASDF&w=&e=&r=12345&t=";
// Send post request
connection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
assertThat(connection.getResponseCode()).isEqualTo(STATUS);
InputStream stream = connection.getInputStream();
List<String> lines = readLines(stream);
stream.close();
assertThat(lines).isEqualTo(RESPONSE);
});
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasNoParent(),
span ->
span.hasName("HTTP POST")
.hasKind(CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP),
equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"),
equalTo(SemanticAttributes.NET_PEER_PORT, url.getPort()),
equalTo(SemanticAttributes.HTTP_URL, url.toString()),
equalTo(SemanticAttributes.HTTP_METHOD, "POST"),
equalTo(SemanticAttributes.HTTP_STATUS_CODE, STATUS),
equalTo(SemanticAttributes.HTTP_FLAVOR, "1.1"),
satisfies(
SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH,
AbstractLongAssert::isNotNegative),
satisfies(
SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
AbstractLongAssert::isNotNegative)),
span ->
span.hasName("test-http-server").hasKind(SERVER).hasParent(trace.getSpan(1))));
}
@Test
public void getOutputStreamShouldTransformGetIntoPost() throws IOException {
URL url = resolveAddress("/success").toURL();
testing.runWithSpan(
"someTrace",
() -> {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
assertThat(connection.getClass().getName())
.isEqualTo("sun.net.www.protocol.http.HttpURLConnection");
connection.setRequestMethod("GET");
String urlParameters = "q=ASDF&w=&e=&r=12345&t=";
// Send POST request
connection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
assertThat(connection.getResponseCode()).isEqualTo(STATUS);
InputStream stream = connection.getInputStream();
List<String> lines = readLines(stream);
stream.close();
assertThat(lines).isEqualTo(RESPONSE);
});
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("someTrace").hasNoParent(),
span ->
span.hasName("HTTP POST")
.hasKind(CLIENT)
.hasParent(trace.getSpan(0))
.hasAttributesSatisfyingExactly(
equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP),
equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"),
equalTo(SemanticAttributes.NET_PEER_PORT, url.getPort()),
equalTo(SemanticAttributes.HTTP_URL, url.toString()),
equalTo(SemanticAttributes.HTTP_METHOD, "POST"),
equalTo(SemanticAttributes.HTTP_STATUS_CODE, STATUS),
equalTo(SemanticAttributes.HTTP_FLAVOR, "1.1"),
satisfies(
SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH,
AbstractLongAssert::isNotNegative),
satisfies(
SemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH,
AbstractLongAssert::isNotNegative)),
span ->
span.hasName("test-http-server").hasKind(SERVER).hasParent(trace.getSpan(1))));
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import static io.opentelemetry.javaagent.instrumentation.httpurlconnection.StreamUtils.readLines;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Map;
import org.junit.jupiter.api.extension.RegisterExtension;
class HttpUrlConnectionUseCachesFalseTest extends AbstractHttpClientTest<HttpURLConnection> {
@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
@Override
public HttpURLConnection buildRequest(String method, URI uri, Map<String, String> headers)
throws Exception {
return (HttpURLConnection) uri.toURL().openConnection();
}
@Override
public int sendRequest(
HttpURLConnection connection, String method, URI uri, Map<String, String> headers)
throws Exception {
try {
connection.setRequestMethod(method);
headers.forEach(connection::setRequestProperty);
connection.setRequestProperty("Connection", "close");
connection.setUseCaches(false);
connection.setConnectTimeout((int) CONNECTION_TIMEOUT.toMillis());
if (uri.toString().contains("/read-timeout")) {
connection.setReadTimeout((int) READ_TIMEOUT.toMillis());
}
Span parentSpan = Span.current();
InputStream stream = connection.getInputStream();
assertThat(Span.current()).isEqualTo(parentSpan);
readLines(stream);
stream.close();
return connection.getResponseCode();
} finally {
connection.disconnect();
}
}
@Override
public int maxRedirects() {
return 20;
}
@Override
public Integer responseCodeOnRedirectError() {
return 302;
}
@Override
public boolean testReusedRequest() {
// HttpURLConnection can't be reused
return false;
}
@Override
public boolean testCallback() {
return false;
}
@Override
public boolean testReadTimeout() {
return true;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Strings;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
final class StreamUtils {
static List<String> readLines(InputStream stream) throws IOException {
List<String> lines = new ArrayList<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, UTF_8));
while (reader.ready()) {
String line = reader.readLine();
if (!Strings.isNullOrEmpty(line)) {
lines.add(line);
}
}
return lines;
}
private StreamUtils() {}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.httpurlconnection;
import static io.opentelemetry.api.trace.SpanKind.CLIENT;
import static io.opentelemetry.api.trace.SpanKind.INTERNAL;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowable;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.instrumentation.test.utils.PortUtils;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class UrlConnectionTest {
@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
@ParameterizedTest
@ValueSource(strings = {"http", "https"})
public void traceRequestWithConnectionFailure(String scheme) {
String uri = scheme + "://localhost:" + PortUtils.UNUSABLE_PORT;
Throwable thrown =
catchThrowable(
() ->
testing.runWithSpan(
"someTrace",
() -> {
URL url = new URI(uri).toURL();
URLConnection connection = url.openConnection();
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
assertThat(Span.current().getSpanContext().isValid()).isTrue();
connection.getInputStream();
}));
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span ->
span.hasName("someTrace")
.hasKind(INTERNAL)
.hasNoParent()
.hasStatus(StatusData.error())
.hasException(thrown),
span ->
span.hasName("HTTP GET")
.hasKind(CLIENT)
.hasParent(trace.getSpan(0))
.hasStatus(StatusData.error())
.hasException(thrown)
.hasAttributesSatisfyingExactly(
equalTo(SemanticAttributes.NET_TRANSPORT, IP_TCP),
equalTo(SemanticAttributes.NET_PEER_NAME, "localhost"),
equalTo(SemanticAttributes.NET_PEER_PORT, PortUtils.UNUSABLE_PORT),
equalTo(SemanticAttributes.HTTP_URL, uri),
equalTo(SemanticAttributes.HTTP_METHOD, "GET"),
equalTo(SemanticAttributes.HTTP_FLAVOR, "1.1"))));
}
}