opentelemetry-java-instrume.../instrumentation/jsp-2.3/javaagent/src/test/groovy/JspInstrumentationBasicTest...

540 lines
18 KiB
Groovy

/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
import io.opentelemetry.instrumentation.test.utils.PortUtils
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import io.opentelemetry.testing.internal.armeria.client.WebClient
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse
import io.opentelemetry.testing.internal.armeria.common.HttpMethod
import io.opentelemetry.testing.internal.armeria.common.MediaType
import io.opentelemetry.testing.internal.armeria.common.RequestHeaders
import org.apache.catalina.Context
import org.apache.catalina.startup.Tomcat
import org.apache.jasper.JasperException
import spock.lang.Shared
import spock.lang.Unroll
import java.nio.file.Files
import static io.opentelemetry.api.trace.SpanKind.SERVER
import static io.opentelemetry.api.trace.StatusCode.ERROR
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP
//TODO should this be HttpServerTest?
class JspInstrumentationBasicTests extends AgentInstrumentationSpecification {
@Shared
int port
@Shared
Tomcat tomcatServer
@Shared
Context appContext
@Shared
String jspWebappContext = "jsptest-context"
@Shared
File baseDir
@Shared
String baseUrl
@Shared
WebClient client
def setupSpec() {
baseDir = Files.createTempDirectory("jsp").toFile()
baseDir.deleteOnExit()
port = PortUtils.findOpenPort()
tomcatServer = new Tomcat()
tomcatServer.setBaseDir(baseDir.getAbsolutePath())
tomcatServer.setPort(port)
tomcatServer.getConnector()
// comment to debug
tomcatServer.setSilent(true)
// this is needed in tomcat 9, this triggers the creation of a connector, will not
// affect tomcat 7 and 8
// https://stackoverflow.com/questions/48998387/code-works-with-embedded-apache-tomcat-8-but-not-with-9-whats-changed
tomcatServer.getConnector()
baseUrl = "http://localhost:$port/$jspWebappContext"
client = WebClient.of(baseUrl)
appContext = tomcatServer.addWebapp("/$jspWebappContext",
JspInstrumentationBasicTests.getResource("/webapps/jsptest").getPath())
tomcatServer.start()
System.out.println(
"Tomcat server: http://" + tomcatServer.getHost().getName() + ":" + port + "/")
}
def cleanupSpec() {
tomcatServer.stop()
tomcatServer.destroy()
}
@Unroll
def "non-erroneous GET #test test"() {
when:
AggregatedHttpResponse res = client.get("/${jspFileName}").aggregate().join()
then:
assertTraces(1) {
trace(0, 3) {
span(0) {
def route = "/$jspWebappContext/$jspFileName"
hasNoParent()
name route
kind SERVER
attributes {
"$SemanticAttributes.HTTP_SCHEME" "http"
"$SemanticAttributes.HTTP_TARGET" route
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" 200
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_USER_AGENT" String
"$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" { it == null || it instanceof Long }
"$SemanticAttributes.HTTP_ROUTE" route
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_HOST_NAME" "localhost"
"$SemanticAttributes.NET_HOST_PORT" port
"net.sock.peer.addr" "127.0.0.1"
"net.sock.peer.port" Long
"net.sock.host.addr" "127.0.0.1"
}
}
span(1) {
childOf span(0)
name "Compile /$jspFileName"
attributes {
"jsp.classFQCN" "org.apache.jsp.$jspClassNamePrefix$jspClassName"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(2) {
childOf span(0)
name "Render /$jspFileName"
attributes {
"jsp.requestURL" "${baseUrl}/${jspFileName}"
}
}
}
}
res.status().code() == 200
where:
test | jspFileName | jspClassName | jspClassNamePrefix
"no java jsp" | "nojava.jsp" | "nojava_jsp" | ""
"basic loop jsp" | "common/loop.jsp" | "loop_jsp" | "common."
"invalid HTML markup" | "invalidMarkup.jsp" | "invalidMarkup_jsp" | ""
}
def "non-erroneous GET with query string"() {
setup:
String queryString = "HELLO"
when:
AggregatedHttpResponse res = client.get("/getQuery.jsp?${queryString}").aggregate().join()
then:
assertTraces(1) {
trace(0, 3) {
span(0) {
def route = "/$jspWebappContext/getQuery.jsp"
hasNoParent()
name route
kind SERVER
attributes {
"$SemanticAttributes.HTTP_SCHEME" "http"
"$SemanticAttributes.HTTP_TARGET" "$route?$queryString"
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" 200
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_USER_AGENT" String
"$SemanticAttributes.HTTP_ROUTE" route
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_HOST_NAME" "localhost"
"$SemanticAttributes.NET_HOST_PORT" port
"net.sock.peer.addr" "127.0.0.1"
"net.sock.peer.port" Long
"net.sock.host.addr" "127.0.0.1"
}
}
span(1) {
childOf span(0)
name "Compile /getQuery.jsp"
attributes {
"jsp.classFQCN" "org.apache.jsp.getQuery_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(2) {
childOf span(0)
name "Render /getQuery.jsp"
attributes {
"jsp.requestURL" "${baseUrl}/getQuery.jsp"
}
}
}
}
res.status().code() == 200
}
def "non-erroneous POST"() {
setup:
RequestHeaders headers = RequestHeaders.builder(HttpMethod.POST, "/post.jsp")
.contentType(MediaType.FORM_DATA)
.build()
when:
AggregatedHttpResponse res = client.execute(headers, "name=world").aggregate().join()
then:
assertTraces(1) {
trace(0, 3) {
span(0) {
def route = "/$jspWebappContext/post.jsp"
hasNoParent()
name route
kind SERVER
attributes {
"$SemanticAttributes.HTTP_SCHEME" "http"
"$SemanticAttributes.HTTP_TARGET" route
"$SemanticAttributes.HTTP_METHOD" "POST"
"$SemanticAttributes.HTTP_STATUS_CODE" 200
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_USER_AGENT" String
"$SemanticAttributes.HTTP_REQUEST_CONTENT_LENGTH" Long
"$SemanticAttributes.HTTP_ROUTE" route
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_HOST_NAME" "localhost"
"$SemanticAttributes.NET_HOST_PORT" port
"net.sock.peer.addr" "127.0.0.1"
"net.sock.peer.port" Long
"net.sock.host.addr" "127.0.0.1"
}
}
span(1) {
childOf span(0)
name "Compile /post.jsp"
attributes {
"jsp.classFQCN" "org.apache.jsp.post_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(2) {
childOf span(0)
name "Render /post.jsp"
attributes {
"jsp.requestURL" "${baseUrl}/post.jsp"
}
}
}
}
res.status().code() == 200
}
@Unroll
def "erroneous runtime errors GET jsp with #test test"() {
when:
AggregatedHttpResponse res = client.get("/${jspFileName}").aggregate().join()
then:
assertTraces(1) {
trace(0, 3) {
span(0) {
def route = "/$jspWebappContext/$jspFileName"
hasNoParent()
name route
kind SERVER
status ERROR
event(0) {
eventName(SemanticAttributes.EXCEPTION_EVENT_NAME)
attributes {
"$SemanticAttributes.EXCEPTION_TYPE" { String tagExceptionType ->
return tagExceptionType == exceptionClass.getName() || tagExceptionType.contains(exceptionClass.getSimpleName())
}
"$SemanticAttributes.EXCEPTION_MESSAGE" { String tagErrorMsg ->
return errorMessageOptional || tagErrorMsg instanceof String
}
"$SemanticAttributes.EXCEPTION_STACKTRACE" String
}
}
attributes {
"$SemanticAttributes.HTTP_SCHEME" "http"
"$SemanticAttributes.HTTP_TARGET" route
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" 500
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_USER_AGENT" String
"$SemanticAttributes.HTTP_ROUTE" route
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_HOST_NAME" "localhost"
"$SemanticAttributes.NET_HOST_PORT" port
"net.sock.peer.addr" "127.0.0.1"
"net.sock.peer.port" Long
"net.sock.host.addr" "127.0.0.1"
}
}
span(1) {
childOf span(0)
name "Compile /$jspFileName"
attributes {
"jsp.classFQCN" "org.apache.jsp.$jspClassName"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(2) {
childOf span(0)
name "Render /$jspFileName"
status ERROR
event(0) {
eventName(SemanticAttributes.EXCEPTION_EVENT_NAME)
attributes {
"$SemanticAttributes.EXCEPTION_TYPE" { String tagExceptionType ->
return tagExceptionType == exceptionClass.getName() || tagExceptionType.contains(exceptionClass.getSimpleName())
}
"$SemanticAttributes.EXCEPTION_MESSAGE" { String tagErrorMsg ->
return errorMessageOptional || tagErrorMsg instanceof String
}
"$SemanticAttributes.EXCEPTION_STACKTRACE" String
}
}
attributes {
"jsp.requestURL" "${baseUrl}/${jspFileName}"
}
}
}
}
res.status().code() == 500
where:
test | jspFileName | jspClassName | exceptionClass | errorMessageOptional
"java runtime error" | "runtimeError.jsp" | "runtimeError_jsp" | ArithmeticException | false
"invalid write" | "invalidWrite.jsp" | "invalidWrite_jsp" | IndexOutOfBoundsException | true
"missing query gives null" | "getQuery.jsp" | "getQuery_jsp" | NullPointerException | true
}
def "non-erroneous include plain HTML GET"() {
when:
AggregatedHttpResponse res = client.get("/includes/includeHtml.jsp").aggregate().join()
then:
assertTraces(1) {
trace(0, 3) {
span(0) {
def route = "/$jspWebappContext/includes/includeHtml.jsp"
hasNoParent()
name route
kind SERVER
attributes {
"$SemanticAttributes.HTTP_SCHEME" "http"
"$SemanticAttributes.HTTP_TARGET" route
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" 200
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_USER_AGENT" String
"$SemanticAttributes.HTTP_ROUTE" route
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_HOST_NAME" "localhost"
"$SemanticAttributes.NET_HOST_PORT" port
"net.sock.peer.addr" "127.0.0.1"
"net.sock.peer.port" Long
"net.sock.host.addr" "127.0.0.1"
}
}
span(1) {
childOf span(0)
name "Compile /includes/includeHtml.jsp"
attributes {
"jsp.classFQCN" "org.apache.jsp.includes.includeHtml_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(2) {
childOf span(0)
name "Render /includes/includeHtml.jsp"
attributes {
"jsp.requestURL" "${baseUrl}/includes/includeHtml.jsp"
}
}
}
}
res.status().code() == 200
}
def "non-erroneous multi GET"() {
when:
AggregatedHttpResponse res = client.get("/includes/includeMulti.jsp").aggregate().join()
then:
assertTraces(1) {
trace(0, 7) {
span(0) {
def route = "/$jspWebappContext/includes/includeMulti.jsp"
hasNoParent()
name route
kind SERVER
attributes {
"$SemanticAttributes.HTTP_SCHEME" "http"
"$SemanticAttributes.HTTP_TARGET" route
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" 200
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_USER_AGENT" String
"$SemanticAttributes.HTTP_ROUTE" route
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_HOST_NAME" "localhost"
"$SemanticAttributes.NET_HOST_PORT" port
"net.sock.peer.addr" "127.0.0.1"
"net.sock.peer.port" Long
"net.sock.host.addr" "127.0.0.1"
}
}
span(1) {
childOf span(0)
name "Compile /includes/includeMulti.jsp"
attributes {
"jsp.classFQCN" "org.apache.jsp.includes.includeMulti_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(2) {
childOf span(0)
name "Render /includes/includeMulti.jsp"
attributes {
"jsp.requestURL" "${baseUrl}/includes/includeMulti.jsp"
}
}
span(3) {
childOf span(2)
name "Compile /common/javaLoopH2.jsp"
attributes {
"jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(4) {
childOf span(2)
name "Render /common/javaLoopH2.jsp"
attributes {
"jsp.requestURL" "${baseUrl}/includes/includeMulti.jsp"
}
}
span(5) {
childOf span(2)
name "Compile /common/javaLoopH2.jsp"
attributes {
"jsp.classFQCN" "org.apache.jsp.common.javaLoopH2_jsp"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
span(6) {
childOf span(2)
name "Render /common/javaLoopH2.jsp"
attributes {
"jsp.requestURL" "${baseUrl}/includes/includeMulti.jsp"
}
}
}
}
res.status().code() == 200
}
def "#test compile error should not produce render traces and spans"() {
when:
AggregatedHttpResponse res = client.get("/${jspFileName}").aggregate().join()
then:
assertTraces(1) {
trace(0, 2) {
span(0) {
def route = "/$jspWebappContext/$jspFileName"
hasNoParent()
name route
kind SERVER
status ERROR
errorEvent(JasperException, String)
attributes {
"$SemanticAttributes.HTTP_SCHEME" "http"
"$SemanticAttributes.HTTP_TARGET" route
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" 500
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_USER_AGENT" String
"$SemanticAttributes.HTTP_ROUTE" route
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_HOST_NAME" "localhost"
"$SemanticAttributes.NET_HOST_PORT" port
"net.sock.peer.addr" "127.0.0.1"
"net.sock.peer.port" Long
"net.sock.host.addr" "127.0.0.1"
}
}
span(1) {
childOf span(0)
name "Compile /$jspFileName"
status ERROR
errorEvent(JasperException, String)
attributes {
"jsp.classFQCN" "org.apache.jsp.$jspClassNamePrefix$jspClassName"
"jsp.compiler" "org.apache.jasper.compiler.JDTCompiler"
}
}
}
}
res.status().code() == 500
where:
test | jspFileName | jspClassName | jspClassNamePrefix
"normal" | "compileError.jsp" | "compileError_jsp" | ""
"forward" | "forwards/forwardWithCompileError.jsp" | "forwardWithCompileError_jsp" | "forwards."
}
def "direct static file reference"() {
when:
AggregatedHttpResponse res = client.get("/${staticFile}").aggregate().join()
then:
res.status().code() == 200
assertTraces(1) {
trace(0, 1) {
span(0) {
def route = "/$jspWebappContext/*"
hasNoParent()
name route
kind SERVER
attributes {
"$SemanticAttributes.HTTP_SCHEME" "http"
"$SemanticAttributes.HTTP_TARGET" "/$jspWebappContext/$staticFile"
"$SemanticAttributes.HTTP_METHOD" "GET"
"$SemanticAttributes.HTTP_STATUS_CODE" 200
"$SemanticAttributes.HTTP_FLAVOR" "1.1"
"$SemanticAttributes.HTTP_USER_AGENT" String
"$SemanticAttributes.HTTP_ROUTE" route
"$SemanticAttributes.NET_TRANSPORT" IP_TCP
"$SemanticAttributes.NET_HOST_NAME" "localhost"
"$SemanticAttributes.NET_HOST_PORT" port
"net.sock.peer.addr" "127.0.0.1"
"net.sock.peer.port" Long
"net.sock.host.addr" "127.0.0.1"
}
}
}
}
where:
staticFile = "common/hello.html"
}
}