opentelemetry-java-instrume.../dd-java-agent/instrumentation/http-url-connection/src/test/groovy/HttpUrlConnectionTest.groovy

488 lines
15 KiB
Groovy

import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.api.Config
import datadog.trace.api.DDSpanTypes
import io.opentracing.tag.Tags
import io.opentracing.util.GlobalTracer
import org.springframework.web.client.RestTemplate
import spock.lang.AutoCleanup
import spock.lang.Requires
import spock.lang.Shared
import sun.net.www.protocol.https.HttpsURLConnectionImpl
import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer
import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
import static datadog.trace.agent.test.utils.TraceUtils.withConfigOverride
import static datadog.trace.instrumentation.http_url_connection.HttpUrlConnectionInstrumentation.HttpUrlState.OPERATION_NAME
class HttpUrlConnectionTest extends AgentTestRunner {
static final RESPONSE = "<html><body><h1>Hello test.</h1>"
static final STATUS = 202
@AutoCleanup
@Shared
def server = httpServer {
handlers {
all {
handleDistributedRequest()
response.status(STATUS).send(RESPONSE)
}
}
}
def "trace request with propagation (useCaches: #useCaches)"() {
setup:
withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "$renameService") {
runUnderTrace("someTrace") {
HttpURLConnection connection = server.address.toURL().openConnection()
connection.useCaches = useCaches
assert GlobalTracer.get().scopeManager().active() != null
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 = server.getAddress().toURL().openConnection()
connection.useCaches = useCaches
assert GlobalTracer.get().scopeManager().active() != null
assert connection.getResponseCode() == STATUS // call before input stream to test alternate behavior
connection.inputStream
stream = connection.inputStream // one more to ensure state is working
lines = stream.readLines()
stream.close()
assert lines == [RESPONSE]
}
}
expect:
assertTraces(3) {
server.distributedRequestTrace(it, 0, TEST_WRITER[2][2])
server.distributedRequestTrace(it, 1, TEST_WRITER[2][1])
trace(2, 3) {
span(0) {
operationName "someTrace"
parent()
errored false
tags {
defaultTags()
}
}
span(1) {
serviceName renameService ? "localhost" : "unnamed-java-app"
operationName OPERATION_NAME
resourceName "GET /"
spanType DDSpanTypes.HTTP_CLIENT
childOf span(0)
errored false
tags {
"$Tags.COMPONENT.key" "http-url-connection"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" STATUS
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
defaultTags()
}
}
span(2) {
serviceName renameService ? "localhost" : "unnamed-java-app"
operationName OPERATION_NAME
resourceName "GET /"
spanType DDSpanTypes.HTTP_CLIENT
childOf span(0)
errored false
tags {
"$Tags.COMPONENT.key" "http-url-connection"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" STATUS
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
defaultTags()
}
}
}
}
where:
useCaches << [false, true]
renameService << [true, false]
}
def "trace request without propagation (useCaches: #useCaches)"() {
setup:
withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "$renameService") {
runUnderTrace("someTrace") {
HttpURLConnection connection = server.address.toURL().openConnection()
connection.useCaches = useCaches
connection.addRequestProperty("is-dd-server", "false")
assert GlobalTracer.get().scopeManager().active() != null
def stream = connection.inputStream
connection.inputStream // one more to ensure state is working
def lines = stream.readLines()
stream.close()
assert connection.getResponseCode() == STATUS
assert lines == [RESPONSE]
// call again to ensure the cycling is ok
connection = server.getAddress().toURL().openConnection()
connection.useCaches = useCaches
connection.addRequestProperty("is-dd-server", "false")
assert GlobalTracer.get().scopeManager().active() != null
assert connection.getResponseCode() == STATUS // call before input stream to test alternate behavior
stream = connection.inputStream
lines = stream.readLines()
stream.close()
assert lines == [RESPONSE]
}
}
expect:
assertTraces(1) {
trace(0, 3) {
span(0) {
operationName "someTrace"
parent()
errored false
tags {
defaultTags()
}
}
span(1) {
serviceName renameService ? "localhost" : "unnamed-java-app"
operationName OPERATION_NAME
resourceName "GET /"
spanType DDSpanTypes.HTTP_CLIENT
childOf span(0)
errored false
tags {
"$Tags.COMPONENT.key" "http-url-connection"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" STATUS
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
defaultTags()
}
}
span(2) {
serviceName renameService ? "localhost" : "unnamed-java-app"
operationName OPERATION_NAME
resourceName "GET /"
spanType DDSpanTypes.HTTP_CLIENT
childOf span(0)
errored false
tags {
"$Tags.COMPONENT.key" "http-url-connection"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" STATUS
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
defaultTags()
}
}
}
}
where:
useCaches << [false, true]
renameService << [false, true]
}
def "test response code"() {
setup:
withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "$renameService") {
runUnderTrace("someTrace") {
HttpURLConnection connection = server.address.toURL().openConnection()
connection.setRequestMethod("HEAD")
connection.addRequestProperty("is-dd-server", "false")
assert GlobalTracer.get().scopeManager().active() != null
assert connection.getResponseCode() == STATUS
}
}
expect:
assertTraces(1) {
trace(0, 2) {
span(0) {
operationName "someTrace"
parent()
errored false
tags {
defaultTags()
}
}
span(1) {
serviceName renameService ? "localhost" : "unnamed-java-app"
operationName OPERATION_NAME
resourceName "HEAD /"
spanType DDSpanTypes.HTTP_CLIENT
childOf span(0)
errored false
tags {
"$Tags.COMPONENT.key" "http-url-connection"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.HTTP_METHOD.key" "HEAD"
"$Tags.HTTP_STATUS.key" STATUS
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
defaultTags()
}
}
}
}
where:
renameService << [false, true]
}
def "test broken API usage"() {
setup:
HttpURLConnection conn = withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "$renameService") {
runUnderTrace("someTrace") {
HttpURLConnection connection = server.address.toURL().openConnection()
connection.setRequestProperty("Connection", "close")
connection.addRequestProperty("is-dd-server", "false")
assert GlobalTracer.get().scopeManager().active() != null
assert connection.getResponseCode() == STATUS
return connection
}
}
expect:
assertTraces(1) {
trace(0, 2) {
span(0) {
operationName "someTrace"
parent()
errored false
tags {
defaultTags()
}
}
span(1) {
serviceName renameService ? "localhost" : "unnamed-java-app"
operationName OPERATION_NAME
resourceName "GET /"
spanType DDSpanTypes.HTTP_CLIENT
childOf span(0)
errored false
tags {
"$Tags.COMPONENT.key" "http-url-connection"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" STATUS
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
defaultTags()
}
}
}
}
cleanup:
conn.disconnect()
where:
iteration << (1..10)
renameService = (iteration % 2 == 0) // alternate even/odd
}
def "test post request"() {
setup:
withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "$renameService") {
runUnderTrace("someTrace") {
HttpURLConnection connection = server.address.toURL().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(2) {
server.distributedRequestTrace(it, 0, TEST_WRITER[1][1])
trace(1, 2) {
span(0) {
operationName "someTrace"
parent()
errored false
tags {
defaultTags()
}
}
span(1) {
serviceName renameService ? "localhost" : "unnamed-java-app"
operationName OPERATION_NAME
resourceName "POST /"
spanType DDSpanTypes.HTTP_CLIENT
childOf span(0)
errored false
tags {
"$Tags.COMPONENT.key" "http-url-connection"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.HTTP_METHOD.key" "POST"
"$Tags.HTTP_STATUS.key" STATUS
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
defaultTags()
}
}
}
}
where:
renameService << [false, true]
}
def "request that looks like a trace submission is ignored"() {
setup:
runUnderTrace("someTrace") {
HttpURLConnection connection = server.address.toURL().openConnection()
connection.addRequestProperty("Datadog-Meta-Lang", "false")
connection.addRequestProperty("is-dd-server", "false")
def stream = connection.inputStream
def lines = stream.readLines()
stream.close()
assert connection.getResponseCode() == STATUS
assert lines == [RESPONSE]
}
expect:
assertTraces(1) {
trace(0, 1) {
span(0) {
operationName "someTrace"
parent()
errored false
tags {
defaultTags()
}
}
}
}
}
def "top level httpurlconnection tracing disabled"() {
setup:
withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "$renameService") {
HttpURLConnection connection = server.address.toURL().openConnection()
connection.addRequestProperty("is-dd-server", "false")
def stream = connection.inputStream
def lines = stream.readLines()
stream.close()
assert connection.getResponseCode() == STATUS
assert lines == [RESPONSE]
}
expect:
assertTraces(1) {
trace(0, 1) {
span(0) {
serviceName renameService ? "localhost" : "unnamed-java-app"
operationName OPERATION_NAME
resourceName "GET /"
spanType DDSpanTypes.HTTP_CLIENT
parent()
errored false
tags {
"$Tags.COMPONENT.key" "http-url-connection"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.HTTP_METHOD.key" "GET"
"$Tags.HTTP_STATUS.key" STATUS
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
defaultTags()
}
}
}
}
where:
renameService << [false, true]
}
def "rest template"() {
setup:
withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "$renameService") {
runUnderTrace("someTrace") {
RestTemplate restTemplate = new RestTemplate()
String res = restTemplate.postForObject(server.address.toString(), "Hello", String)
assert res == "$RESPONSE"
}
}
expect:
assertTraces(2) {
server.distributedRequestTrace(it, 0, TEST_WRITER[1][1])
trace(1, 2) {
span(0) {
operationName "someTrace"
parent()
errored false
tags {
defaultTags()
}
}
span(1) {
serviceName renameService ? "localhost" : "unnamed-java-app"
operationName OPERATION_NAME
resourceName "POST /"
spanType DDSpanTypes.HTTP_CLIENT
childOf span(0)
errored false
tags {
"$Tags.COMPONENT.key" "http-url-connection"
"$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT
"$Tags.HTTP_URL.key" "$server.address/"
"$Tags.HTTP_METHOD.key" "POST"
"$Tags.HTTP_STATUS.key" STATUS
"$Tags.PEER_HOSTNAME.key" "localhost"
"$Tags.PEER_PORT.key" server.address.port
defaultTags()
}
}
}
}
where:
renameService << [false, true]
}
// This test makes no sense on IBM JVM because there is no HttpsURLConnectionImpl class there
@Requires({ !System.getProperty("java.vm.name").contains("IBM J9 VM") })
def "Make sure we can load HttpsURLConnectionImpl"() {
when:
def instance = new HttpsURLConnectionImpl(null, null, null)
then:
instance != null
}
}