spring mvc tests to java (#11114)
Co-authored-by: Lauri Tulmin <ltulmin@splunk.com>
This commit is contained in:
parent
32df5ae710
commit
deac3971d9
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package test.boot
|
||||
|
||||
import boot.SavingAuthenticationProvider
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
SavingAuthenticationProvider savingAuthenticationProvider() {
|
||||
return new SavingAuthenticationProvider()
|
||||
}
|
||||
|
||||
/**
|
||||
* Following configuration is required for unauthorised call tests (form would redirect, we need 401)
|
||||
*/
|
||||
@Configuration
|
||||
@Order(1)
|
||||
static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf().disable()
|
||||
.antMatcher("/basicsecured/**")
|
||||
.authorizeRequests()
|
||||
.antMatchers("/basicsecured/**").authenticated()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and().authenticationProvider(applicationContext.getBean(SavingAuthenticationProvider))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Following configuration is required in order to get form login, needed by password tests
|
||||
*/
|
||||
@Configuration
|
||||
static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http
|
||||
.csrf().disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/formsecured/**").authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.and().authenticationProvider(applicationContext.getBean(SavingAuthenticationProvider))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package test.boot
|
||||
|
||||
import boot.AbstractSpringBootBasedTest
|
||||
|
||||
class SpringBootBasedTest extends AbstractSpringBootBasedTest {
|
||||
|
||||
Class<?> securityConfigClass() {
|
||||
SecurityConfig
|
||||
}
|
||||
|
||||
@Override
|
||||
int getResponseCodeOnNonStandardHttpMethod() {
|
||||
Boolean.getBoolean("testLatestDeps") ? 500 : 200
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package test.filter
|
||||
|
||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
import javax.servlet.Filter
|
||||
import javax.servlet.FilterChain
|
||||
import javax.servlet.FilterConfig
|
||||
import javax.servlet.ServletException
|
||||
import javax.servlet.ServletRequest
|
||||
import javax.servlet.ServletResponse
|
||||
import javax.servlet.http.HttpServletRequest
|
||||
import javax.servlet.http.HttpServletResponse
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
|
||||
|
||||
@Configuration
|
||||
class ServletFilterConfig {
|
||||
|
||||
@Bean
|
||||
Filter servletFilter() {
|
||||
return new Filter() {
|
||||
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) request
|
||||
HttpServletResponse resp = (HttpServletResponse) response
|
||||
ServerEndpoint endpoint = ServerEndpoint.forPath(req.servletPath)
|
||||
HttpServerTest.controller(endpoint) {
|
||||
resp.contentType = "text/plain"
|
||||
switch (endpoint) {
|
||||
case SUCCESS:
|
||||
resp.status = endpoint.status
|
||||
resp.writer.print(endpoint.body)
|
||||
break
|
||||
case QUERY_PARAM:
|
||||
resp.status = endpoint.status
|
||||
resp.writer.print(req.queryString)
|
||||
break
|
||||
case PATH_PARAM:
|
||||
resp.status = endpoint.status
|
||||
resp.writer.print(endpoint.body)
|
||||
break
|
||||
case REDIRECT:
|
||||
resp.sendRedirect(endpoint.body)
|
||||
break
|
||||
case CAPTURE_HEADERS:
|
||||
resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request"))
|
||||
resp.status = endpoint.status
|
||||
resp.writer.print(endpoint.body)
|
||||
break
|
||||
case ERROR:
|
||||
resp.sendError(endpoint.status, endpoint.body)
|
||||
break
|
||||
case EXCEPTION:
|
||||
throw new Exception(endpoint.body)
|
||||
case INDEXED_CHILD:
|
||||
INDEXED_CHILD.collectSpanAttributes { name -> req.getParameter(name) }
|
||||
resp.writer.print(endpoint.body)
|
||||
break
|
||||
default:
|
||||
chain.doFilter(request, response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package test.filter
|
||||
|
||||
import filter.AbstractServletFilterTest
|
||||
import test.boot.SecurityConfig
|
||||
|
||||
class ServletFilterTest extends AbstractServletFilterTest {
|
||||
|
||||
Class<?> securityConfigClass() {
|
||||
SecurityConfig
|
||||
}
|
||||
|
||||
Class<?> filterConfigClass() {
|
||||
ServletFilterConfig
|
||||
}
|
||||
|
||||
@Override
|
||||
int getResponseCodeOnNonStandardHttpMethod() {
|
||||
Boolean.getBoolean("testLatestDeps") ? 500 : 200
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.boot;
|
||||
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.boot.SavingAuthenticationProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
SavingAuthenticationProvider savingAuthenticationProvider() {
|
||||
return new SavingAuthenticationProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Following configuration is required for unauthorised call tests (form would redirect, we need
|
||||
* 401)
|
||||
*/
|
||||
@Configuration
|
||||
@Order(1)
|
||||
static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf()
|
||||
.disable()
|
||||
.antMatcher("/basicsecured/**")
|
||||
.authorizeRequests()
|
||||
.antMatchers("/basicsecured/**")
|
||||
.authenticated()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authenticationProvider(
|
||||
getApplicationContext().getBean(SavingAuthenticationProvider.class));
|
||||
}
|
||||
}
|
||||
|
||||
/** Following configuration is required in order to get form login, needed by password tests */
|
||||
@Configuration
|
||||
static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
|
||||
|
||||
@Override
|
||||
protected void configure(HttpSecurity http) throws Exception {
|
||||
http.csrf()
|
||||
.disable()
|
||||
.authorizeRequests()
|
||||
.antMatchers("/formsecured/**")
|
||||
.authenticated()
|
||||
.and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.authenticationProvider(
|
||||
getApplicationContext().getBean(SavingAuthenticationProvider.class));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.boot;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.boot.AbstractSpringBootBasedTest;
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.boot.AppConfig;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
class SpringBootBasedTest extends AbstractSpringBootBasedTest {
|
||||
|
||||
@RegisterExtension
|
||||
private static final InstrumentationExtension testing =
|
||||
HttpServerInstrumentationExtension.forAgent();
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Override
|
||||
protected ConfigurableApplicationContext context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurableApplicationContext setupServer() {
|
||||
SpringApplication app = new SpringApplication(AppConfig.class, securityConfigClass());
|
||||
app.setDefaultProperties(
|
||||
ImmutableMap.of(
|
||||
"server.port",
|
||||
port,
|
||||
"server.context-path",
|
||||
getContextPath(),
|
||||
"server.servlet.contextPath",
|
||||
getContextPath(),
|
||||
"server.error.include-message",
|
||||
"always"));
|
||||
context = app.run();
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> securityConfigClass() {
|
||||
return SecurityConfig.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpServerTestOptions options) {
|
||||
super.configure(options);
|
||||
options.setResponseCodeOnNonStandardHttpMethod(
|
||||
Boolean.getBoolean("testLatestDeps") ? 500 : 200);
|
||||
options.setExpectedException(new RuntimeException(EXCEPTION.getBody()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.filter;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
|
||||
|
||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
|
||||
import java.io.IOException;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
class ServletFilterConfig {
|
||||
|
||||
@Bean
|
||||
Filter servletFilter() {
|
||||
return new Filter() {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
HttpServletResponse resp = (HttpServletResponse) response;
|
||||
ServerEndpoint endpoint = ServerEndpoint.forPath(req.getServletPath());
|
||||
HttpServerTest.controller(
|
||||
endpoint,
|
||||
() -> {
|
||||
resp.setContentType("text/plain");
|
||||
switch (endpoint.name()) {
|
||||
case "SUCCESS":
|
||||
resp.setStatus(endpoint.getStatus());
|
||||
resp.getWriter().print(endpoint.getBody());
|
||||
break;
|
||||
case "QUERY_PARAM":
|
||||
resp.setStatus(endpoint.getStatus());
|
||||
resp.getWriter().print(req.getQueryString());
|
||||
break;
|
||||
case "PATH_PARAM":
|
||||
resp.setStatus(endpoint.getStatus());
|
||||
resp.getWriter().print(endpoint.getBody());
|
||||
break;
|
||||
case "REDIRECT":
|
||||
resp.sendRedirect(endpoint.getBody());
|
||||
break;
|
||||
case "CAPTURE_HEADERS":
|
||||
resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request"));
|
||||
resp.setStatus(endpoint.getStatus());
|
||||
resp.getWriter().print(endpoint.getBody());
|
||||
break;
|
||||
case "ERROR":
|
||||
resp.sendError(endpoint.getStatus(), endpoint.getBody());
|
||||
break;
|
||||
case "EXCEPTION":
|
||||
throw new Exception(endpoint.getBody());
|
||||
case "INDEXED_CHILD":
|
||||
INDEXED_CHILD.collectSpanAttributes(req::getParameter);
|
||||
resp.getWriter().print(endpoint.getBody());
|
||||
break;
|
||||
default:
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.filter;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.filter.AbstractServletFilterTest;
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.filter.FilteredAppConfig;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
|
||||
import io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.boot.SecurityConfig;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
class ServletFilterTest extends AbstractServletFilterTest {
|
||||
|
||||
@RegisterExtension
|
||||
private static final InstrumentationExtension testing =
|
||||
HttpServerInstrumentationExtension.forAgent();
|
||||
|
||||
@Override
|
||||
protected Class<?> securityConfigClass() {
|
||||
return SecurityConfig.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> filterConfigClass() {
|
||||
return ServletFilterConfig.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurableApplicationContext setupServer() {
|
||||
SpringApplication app =
|
||||
new SpringApplication(FilteredAppConfig.class, securityConfigClass(), filterConfigClass());
|
||||
app.setDefaultProperties(
|
||||
ImmutableMap.of("server.port", port, "server.error.include-message", "always"));
|
||||
return app.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpServerTestOptions options) {
|
||||
super.configure(options);
|
||||
options.setResponseCodeOnNonStandardHttpMethod(
|
||||
Boolean.getBoolean("testLatestDeps") ? 500 : 200);
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import boot.SavingAuthenticationProvider
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.core.annotation.Order
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
|
||||
import org.springframework.security.web.SecurityFilterChain
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
SavingAuthenticationProvider savingAuthenticationProvider() {
|
||||
return new SavingAuthenticationProvider()
|
||||
}
|
||||
|
||||
/**
|
||||
* Following configuration is required for unauthorised call tests (form would redirect, we need 401)
|
||||
*/
|
||||
@Bean
|
||||
@Order(1)
|
||||
SecurityFilterChain apiWebSecurity(HttpSecurity http, SavingAuthenticationProvider savingAuthenticationProvider) {
|
||||
return http
|
||||
.csrf().disable()
|
||||
.securityMatcher("/basicsecured/**")
|
||||
.authorizeHttpRequests()
|
||||
.requestMatchers("/basicsecured/**").authenticated()
|
||||
.and()
|
||||
.httpBasic()
|
||||
.and()
|
||||
.authenticationProvider(savingAuthenticationProvider)
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Following configuration is required in order to get form login, needed by password tests
|
||||
*/
|
||||
@Bean
|
||||
SecurityFilterChain formLoginWebSecurity(HttpSecurity http, SavingAuthenticationProvider savingAuthenticationProvider) {
|
||||
return http
|
||||
.csrf().disable()
|
||||
.authorizeHttpRequests()
|
||||
.requestMatchers("/formsecured/**").authenticated()
|
||||
.anyRequest().permitAll()
|
||||
.and()
|
||||
.formLogin()
|
||||
.and()
|
||||
.authenticationProvider(savingAuthenticationProvider)
|
||||
.build()
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
|
||||
import jakarta.servlet.Filter
|
||||
import jakarta.servlet.FilterChain
|
||||
import jakarta.servlet.FilterConfig
|
||||
import jakarta.servlet.ServletException
|
||||
import jakarta.servlet.ServletRequest
|
||||
import jakarta.servlet.ServletResponse
|
||||
import jakarta.servlet.http.HttpServletRequest
|
||||
import jakarta.servlet.http.HttpServletResponse
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
|
||||
|
||||
@Configuration
|
||||
class ServletFilterConfig {
|
||||
|
||||
@Bean
|
||||
Filter servletFilter() {
|
||||
return new Filter() {
|
||||
|
||||
@Override
|
||||
void init(FilterConfig filterConfig) throws ServletException {
|
||||
}
|
||||
|
||||
@Override
|
||||
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) request
|
||||
HttpServletResponse resp = (HttpServletResponse) response
|
||||
ServerEndpoint endpoint = ServerEndpoint.forPath(req.servletPath)
|
||||
HttpServerTest.controller(endpoint) {
|
||||
resp.contentType = "text/plain"
|
||||
switch (endpoint) {
|
||||
case SUCCESS:
|
||||
resp.status = endpoint.status
|
||||
resp.writer.print(endpoint.body)
|
||||
break
|
||||
case QUERY_PARAM:
|
||||
resp.status = endpoint.status
|
||||
resp.writer.print(req.queryString)
|
||||
break
|
||||
case PATH_PARAM:
|
||||
resp.status = endpoint.status
|
||||
resp.writer.print(endpoint.body)
|
||||
break
|
||||
case REDIRECT:
|
||||
resp.sendRedirect(endpoint.body)
|
||||
break
|
||||
case CAPTURE_HEADERS:
|
||||
resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request"))
|
||||
resp.status = endpoint.status
|
||||
resp.writer.print(endpoint.body)
|
||||
break
|
||||
case ERROR:
|
||||
resp.sendError(endpoint.status, endpoint.body)
|
||||
break
|
||||
case EXCEPTION:
|
||||
throw new Exception(endpoint.body)
|
||||
case INDEXED_CHILD:
|
||||
INDEXED_CHILD.collectSpanAttributes { name -> req.getParameter(name) }
|
||||
resp.writer.print(endpoint.body)
|
||||
break
|
||||
default:
|
||||
chain.doFilter(request, response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void destroy() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import filter.AbstractServletFilterTest
|
||||
import io.opentelemetry.api.trace.SpanKind
|
||||
import io.opentelemetry.api.trace.StatusCode
|
||||
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
||||
import io.opentelemetry.sdk.trace.data.SpanData
|
||||
|
||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||
|
||||
class ServletFilterTest extends AbstractServletFilterTest {
|
||||
|
||||
Class<?> securityConfigClass() {
|
||||
SecurityConfig
|
||||
}
|
||||
|
||||
Class<?> filterConfigClass() {
|
||||
ServletFilterConfig
|
||||
}
|
||||
|
||||
@Override
|
||||
void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint) {
|
||||
if (Boolean.getBoolean("testLatestDeps") && endpoint == ServerEndpoint.NOT_FOUND) {
|
||||
trace.span(index) {
|
||||
name "ResourceHttpRequestHandler.handleRequest"
|
||||
kind INTERNAL
|
||||
childOf((SpanData) parent)
|
||||
status StatusCode.ERROR
|
||||
errorEventWithAnyMessage Class.forName("org.springframework.web.servlet.resource.NoResourceFoundException")
|
||||
}
|
||||
} else {
|
||||
super.handlerSpan(trace, index, parent, method, endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
|
||||
if (Boolean.getBoolean("testLatestDeps") && endpoint == ServerEndpoint.NOT_FOUND) {
|
||||
trace.span(index) {
|
||||
name ~/\.sendError$/
|
||||
kind SpanKind.INTERNAL
|
||||
// not verifying the parent span, in the latest version the responseSpan is the child of the SERVER span, not the handler span
|
||||
}
|
||||
} else {
|
||||
super.responseSpan(trace, index, parent, method, endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int getResponseCodeOnNonStandardHttpMethod() {
|
||||
400
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import boot.AbstractSpringBootBasedTest
|
||||
import io.opentelemetry.api.trace.StatusCode
|
||||
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
||||
import io.opentelemetry.sdk.trace.data.SpanData
|
||||
|
||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||
|
||||
class SpringBootBasedTest extends AbstractSpringBootBasedTest {
|
||||
|
||||
Class<?> securityConfigClass() {
|
||||
SecurityConfig
|
||||
}
|
||||
|
||||
@Override
|
||||
void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint) {
|
||||
if (Boolean.getBoolean("testLatestDeps") && endpoint == ServerEndpoint.NOT_FOUND) {
|
||||
trace.span(index) {
|
||||
name "ResourceHttpRequestHandler.handleRequest"
|
||||
kind INTERNAL
|
||||
childOf((SpanData) parent)
|
||||
status StatusCode.ERROR
|
||||
errorEventWithAnyMessage Class.forName("org.springframework.web.servlet.resource.NoResourceFoundException")
|
||||
}
|
||||
} else {
|
||||
super.handlerSpan(trace, index, parent, method, endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
int getResponseCodeOnNonStandardHttpMethod() {
|
||||
400
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.boot;
|
||||
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.boot.SavingAuthenticationProvider;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfig {
|
||||
|
||||
@Bean
|
||||
SavingAuthenticationProvider savingAuthenticationProvider() {
|
||||
return new SavingAuthenticationProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Following configuration is required for unauthorised call tests (form would redirect, we need
|
||||
* 401)
|
||||
*/
|
||||
@Bean
|
||||
@Order(1)
|
||||
SecurityFilterChain apiWebSecurity(
|
||||
HttpSecurity http, SavingAuthenticationProvider savingAuthenticationProvider)
|
||||
throws Exception {
|
||||
return http.csrf(AbstractHttpConfigurer::disable)
|
||||
.securityMatcher("/basicsecured/**")
|
||||
.authorizeHttpRequests(auth -> auth.requestMatchers("/basicsecured/**").authenticated())
|
||||
.authenticationProvider(savingAuthenticationProvider)
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Following configuration is required in order to get form login, needed by password tests */
|
||||
@Bean
|
||||
SecurityFilterChain formLoginWebSecurity(
|
||||
HttpSecurity http, SavingAuthenticationProvider savingAuthenticationProvider)
|
||||
throws Exception {
|
||||
return http.csrf(AbstractHttpConfigurer::disable)
|
||||
.authorizeHttpRequests(
|
||||
auth ->
|
||||
auth.requestMatchers("/formsecured/**").authenticated().anyRequest().permitAll())
|
||||
.authenticationProvider(savingAuthenticationProvider)
|
||||
.formLogin(Customizer.withDefaults())
|
||||
.build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.boot;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
|
||||
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
|
||||
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
|
||||
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.boot.AbstractSpringBootBasedTest;
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.boot.AppConfig;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
|
||||
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import io.opentelemetry.sdk.trace.data.StatusData;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
class SpringBootBasedTest extends AbstractSpringBootBasedTest {
|
||||
|
||||
@RegisterExtension
|
||||
private static final InstrumentationExtension testing =
|
||||
HttpServerInstrumentationExtension.forAgent();
|
||||
|
||||
private static final boolean testLatestDeps = Boolean.getBoolean("testLatestDeps");
|
||||
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@Override
|
||||
protected ConfigurableApplicationContext context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> securityConfigClass() {
|
||||
return SecurityConfig.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurableApplicationContext setupServer() {
|
||||
SpringApplication app = new SpringApplication(AppConfig.class, securityConfigClass());
|
||||
app.setDefaultProperties(
|
||||
ImmutableMap.of(
|
||||
"server.port",
|
||||
port,
|
||||
"server.context-path",
|
||||
getContextPath(),
|
||||
"server.servlet.contextPath",
|
||||
getContextPath(),
|
||||
"server.error.include-message",
|
||||
"always"));
|
||||
context = app.run();
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertHandlerSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
if (testLatestDeps && endpoint == ServerEndpoint.NOT_FOUND) {
|
||||
String handlerSpanName = "ResourceHttpRequestHandler.handleRequest";
|
||||
span.hasName(handlerSpanName)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasEventsSatisfyingExactly(
|
||||
event ->
|
||||
event
|
||||
.hasName("exception")
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
EXCEPTION_TYPE,
|
||||
"org.springframework.web.servlet.resource.NoResourceFoundException"),
|
||||
satisfies(EXCEPTION_MESSAGE, val -> assertThat(val).isNotNull()),
|
||||
satisfies(EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class))));
|
||||
return span;
|
||||
} else {
|
||||
return super.assertHandlerSpan(span, method, endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertResponseSpan(
|
||||
SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) {
|
||||
if (testLatestDeps && endpoint == ServerEndpoint.NOT_FOUND) {
|
||||
// not verifying the parent span, in the latest version the responseSpan is the child of the
|
||||
// SERVER span, not the handler span
|
||||
return super.assertResponseSpan(span, method, endpoint);
|
||||
} else {
|
||||
return super.assertResponseSpan(span, parentSpan, method, endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpServerTestOptions options) {
|
||||
super.configure(options);
|
||||
options.setResponseCodeOnNonStandardHttpMethod(400);
|
||||
options.setExpectedException(new RuntimeException(EXCEPTION.getBody()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.filter;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
|
||||
|
||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
class ServletFilterConfig {
|
||||
|
||||
@Bean
|
||||
Filter servletFilter() {
|
||||
return new Filter() {
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
HttpServletRequest req = (HttpServletRequest) request;
|
||||
HttpServletResponse resp = (HttpServletResponse) response;
|
||||
ServerEndpoint endpoint = ServerEndpoint.forPath(req.getServletPath());
|
||||
HttpServerTest.controller(
|
||||
endpoint,
|
||||
() -> {
|
||||
resp.setContentType("text/plain");
|
||||
switch (endpoint.name()) {
|
||||
case "SUCCESS":
|
||||
resp.setStatus(endpoint.getStatus());
|
||||
resp.getWriter().print(endpoint.getBody());
|
||||
break;
|
||||
case "QUERY_PARAM":
|
||||
resp.setStatus(endpoint.getStatus());
|
||||
resp.getWriter().print(req.getQueryString());
|
||||
break;
|
||||
case "PATH_PARAM":
|
||||
resp.setStatus(endpoint.getStatus());
|
||||
resp.getWriter().print(endpoint.getBody());
|
||||
break;
|
||||
case "REDIRECT":
|
||||
resp.sendRedirect(endpoint.getBody());
|
||||
break;
|
||||
case "CAPTURE_HEADERS":
|
||||
resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request"));
|
||||
resp.setStatus(endpoint.getStatus());
|
||||
resp.getWriter().print(endpoint.getBody());
|
||||
break;
|
||||
case "ERROR":
|
||||
resp.sendError(endpoint.getStatus(), endpoint.getBody());
|
||||
break;
|
||||
case "EXCEPTION":
|
||||
throw new Exception(endpoint.getBody());
|
||||
case "INDEXED_CHILD":
|
||||
INDEXED_CHILD.collectSpanAttributes(req::getParameter);
|
||||
resp.getWriter().print(endpoint.getBody());
|
||||
break;
|
||||
default:
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.filter;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
|
||||
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
|
||||
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
|
||||
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.filter.AbstractServletFilterTest;
|
||||
import io.opentelemetry.instrumentation.spring.webmvc.filter.FilteredAppConfig;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
|
||||
import io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.boot.SecurityConfig;
|
||||
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
|
||||
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||
import io.opentelemetry.sdk.trace.data.StatusData;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
class ServletFilterTest extends AbstractServletFilterTest {
|
||||
|
||||
private static final boolean testLatestDeps = Boolean.getBoolean("testLatestDeps");
|
||||
|
||||
@RegisterExtension
|
||||
private static final InstrumentationExtension testing =
|
||||
HttpServerInstrumentationExtension.forAgent();
|
||||
|
||||
@Override
|
||||
protected Class<?> securityConfigClass() {
|
||||
return SecurityConfig.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> filterConfigClass() {
|
||||
return ServletFilterConfig.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConfigurableApplicationContext setupServer() {
|
||||
SpringApplication app =
|
||||
new SpringApplication(FilteredAppConfig.class, securityConfigClass(), filterConfigClass());
|
||||
app.setDefaultProperties(
|
||||
ImmutableMap.of("server.port", port, "server.error.include-message", "always"));
|
||||
return app.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertHandlerSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
if (testLatestDeps && endpoint == ServerEndpoint.NOT_FOUND) {
|
||||
String handlerSpanName = "ResourceHttpRequestHandler.handleRequest";
|
||||
span.hasName(handlerSpanName)
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasStatus(StatusData.error())
|
||||
.hasEventsSatisfyingExactly(
|
||||
event ->
|
||||
event
|
||||
.hasName("exception")
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
EXCEPTION_TYPE,
|
||||
"org.springframework.web.servlet.resource.NoResourceFoundException"),
|
||||
satisfies(EXCEPTION_MESSAGE, val -> assertThat(val).isNotNull()),
|
||||
satisfies(EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class))));
|
||||
return span;
|
||||
} else {
|
||||
return super.assertHandlerSpan(span, method, endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertResponseSpan(
|
||||
SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) {
|
||||
if (testLatestDeps && endpoint == ServerEndpoint.NOT_FOUND) {
|
||||
span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError"));
|
||||
span.hasKind(SpanKind.INTERNAL);
|
||||
// not verifying the parent span, in the latest version the responseSpan is the child of the
|
||||
// SERVER span, not the handler span
|
||||
return span;
|
||||
} else {
|
||||
return super.assertResponseSpan(span, parentSpan, method, endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpServerTestOptions options) {
|
||||
super.configure(options);
|
||||
options.setResponseCodeOnNonStandardHttpMethod(400);
|
||||
}
|
||||
}
|
|
@ -5,8 +5,6 @@ plugins {
|
|||
dependencies {
|
||||
implementation(project(":testing-common"))
|
||||
|
||||
implementation("org.spockframework:spock-spring")
|
||||
|
||||
compileOnly("org.springframework.boot:spring-boot-starter-test:1.5.17.RELEASE")
|
||||
compileOnly("org.springframework.boot:spring-boot-starter-web:1.5.17.RELEASE")
|
||||
compileOnly("org.springframework.boot:spring-boot-starter-security:1.5.17.RELEASE")
|
||||
|
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package boot
|
||||
|
||||
import io.opentelemetry.api.trace.StatusCode
|
||||
import io.opentelemetry.instrumentation.api.internal.HttpConstants
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
|
||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
||||
import io.opentelemetry.sdk.trace.data.SpanData
|
||||
import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes
|
||||
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest
|
||||
import io.opentelemetry.testing.internal.armeria.common.HttpData
|
||||
import io.opentelemetry.testing.internal.armeria.common.MediaType
|
||||
import io.opentelemetry.testing.internal.armeria.common.QueryParams
|
||||
import org.springframework.boot.SpringApplication
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
import org.springframework.security.web.util.OnCommittedResponseWrapper
|
||||
import org.springframework.web.servlet.view.RedirectView
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_ERROR
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.LOGIN
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
|
||||
|
||||
abstract class AbstractSpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext> implements AgentTestTrait {
|
||||
|
||||
abstract Class<?> securityConfigClass()
|
||||
|
||||
@Override
|
||||
ConfigurableApplicationContext startServer(int port) {
|
||||
def app = new SpringApplication(AppConfig, securityConfigClass())
|
||||
app.setDefaultProperties([
|
||||
"server.port" : port,
|
||||
"server.context-path" : getContextPath(),
|
||||
"server.servlet.contextPath" : getContextPath(),
|
||||
"server.error.include-message": "always",
|
||||
])
|
||||
def context = app.run()
|
||||
return context
|
||||
}
|
||||
|
||||
@Override
|
||||
void stopServer(ConfigurableApplicationContext ctx) {
|
||||
ctx.close()
|
||||
}
|
||||
|
||||
@Override
|
||||
String getContextPath() {
|
||||
return "/xyz"
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasHandlerSpan(ServerEndpoint endpoint) {
|
||||
true
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasRenderSpan(ServerEndpoint endpoint) {
|
||||
endpoint == REDIRECT
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasResponseSpan(ServerEndpoint endpoint) {
|
||||
endpoint == REDIRECT || endpoint == NOT_FOUND
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean testPathParam() {
|
||||
true
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasErrorPageSpans(ServerEndpoint endpoint) {
|
||||
endpoint == NOT_FOUND
|
||||
}
|
||||
|
||||
@Override
|
||||
String expectedHttpRoute(ServerEndpoint endpoint, String method) {
|
||||
if (method == HttpConstants._OTHER) {
|
||||
return getContextPath() + endpoint.path
|
||||
}
|
||||
switch (endpoint) {
|
||||
case PATH_PARAM:
|
||||
return getContextPath() + "/path/{id}/param"
|
||||
case NOT_FOUND:
|
||||
return getContextPath() + "/**"
|
||||
case LOGIN:
|
||||
return getContextPath() + "/*"
|
||||
default:
|
||||
return super.expectedHttpRoute(endpoint, method)
|
||||
}
|
||||
}
|
||||
|
||||
def "test spans with auth error"() {
|
||||
setup:
|
||||
def authProvider = server.getBean(SavingAuthenticationProvider)
|
||||
def request = request(AUTH_ERROR, "GET")
|
||||
|
||||
when:
|
||||
authProvider.latestAuthentications.clear()
|
||||
def response = client.execute(request).aggregate().join()
|
||||
|
||||
then:
|
||||
response.status().code() == 401 // not secured
|
||||
|
||||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 3) {
|
||||
serverSpan(it, 0, null, null, "GET", AUTH_ERROR)
|
||||
sendErrorSpan(it, 1, span(0))
|
||||
errorPageSpans(it, 2, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def "test character encoding of #testPassword"() {
|
||||
setup:
|
||||
def authProvider = server.getBean(SavingAuthenticationProvider)
|
||||
|
||||
QueryParams form = QueryParams.of("username", "test", "password", testPassword)
|
||||
def request = AggregatedHttpRequest.of(
|
||||
request(LOGIN, "POST").headers().toBuilder().contentType(MediaType.FORM_DATA).build(),
|
||||
HttpData.ofUtf8(form.toQueryString()))
|
||||
|
||||
when:
|
||||
authProvider.latestAuthentications.clear()
|
||||
def response = client.execute(request).aggregate().join()
|
||||
|
||||
then:
|
||||
response.status().code() == 302 // redirect after success
|
||||
authProvider.latestAuthentications.get(0).password == testPassword
|
||||
|
||||
and:
|
||||
assertTraces(1) {
|
||||
trace(0, 2) {
|
||||
serverSpan(it, 0, null, null, "POST", LOGIN)
|
||||
redirectSpan(it, 1, span(0))
|
||||
}
|
||||
}
|
||||
|
||||
where:
|
||||
testPassword << ["password", "dfsdföääöüüä", "🤓"]
|
||||
}
|
||||
|
||||
@Override
|
||||
void errorPageSpans(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
||||
trace.span(index) {
|
||||
name "BasicErrorController.error"
|
||||
kind INTERNAL
|
||||
attributes {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void responseSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
||||
def methodName = endpoint == NOT_FOUND ? "sendError" : "sendRedirect"
|
||||
// the response wrapper class names vary depending on spring version & scenario
|
||||
def namePattern = Pattern.compile("(OnCommittedResponseWrapper|HttpServletResponseWrapper|FirewalledResponse).$methodName")
|
||||
trace.span(index) {
|
||||
name namePattern
|
||||
kind INTERNAL
|
||||
attributes {
|
||||
"$CodeIncubatingAttributes.CODE_NAMESPACE" {
|
||||
it == OnCommittedResponseWrapper.name
|
||||
|| it == "org.springframework.security.web.firewall.FirewalledResponse"
|
||||
|| it == "jakarta.servlet.http.HttpServletResponseWrapper"
|
||||
}
|
||||
"$CodeIncubatingAttributes.CODE_FUNCTION" methodName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void renderSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
||||
trace.span(index) {
|
||||
name "Render RedirectView"
|
||||
kind INTERNAL
|
||||
attributes {
|
||||
"spring-webmvc.view.type" RedirectView.name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
||||
def handlerSpanName = "TestController.${endpoint.name().toLowerCase()}"
|
||||
if (endpoint == NOT_FOUND) {
|
||||
handlerSpanName = "ResourceHttpRequestHandler.handleRequest"
|
||||
}
|
||||
trace.span(index) {
|
||||
name handlerSpanName
|
||||
kind INTERNAL
|
||||
if (endpoint == EXCEPTION) {
|
||||
status StatusCode.ERROR
|
||||
errorEvent(Exception, EXCEPTION.body)
|
||||
}
|
||||
childOf((SpanData) parent)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package boot
|
||||
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider
|
||||
import org.springframework.security.core.AuthenticationException
|
||||
import org.springframework.security.core.GrantedAuthority
|
||||
import org.springframework.security.core.userdetails.UserDetails
|
||||
|
||||
class SavingAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
List<TestUserDetails> latestAuthentications = new ArrayList<>()
|
||||
|
||||
@Override
|
||||
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
// none
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
|
||||
def details = new TestUserDetails(username, authentication.credentials.toString())
|
||||
|
||||
latestAuthentications.add(details)
|
||||
|
||||
return details
|
||||
}
|
||||
}
|
||||
|
||||
class TestUserDetails implements UserDetails {
|
||||
private final String username
|
||||
private final String password
|
||||
|
||||
TestUserDetails(String username, String password) {
|
||||
this.username = username
|
||||
this.password = password
|
||||
}
|
||||
|
||||
@Override
|
||||
Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return Collections.emptySet()
|
||||
}
|
||||
|
||||
@Override
|
||||
String getPassword() {
|
||||
return password
|
||||
}
|
||||
|
||||
@Override
|
||||
String getUsername() {
|
||||
return username
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isAccountNonExpired() {
|
||||
return true
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isAccountNonLocked() {
|
||||
return true
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isCredentialsNonExpired() {
|
||||
return true
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isEnabled() {
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package boot
|
||||
|
||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.RequestHeader
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.ResponseBody
|
||||
import org.springframework.web.servlet.view.RedirectView
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
|
||||
|
||||
@Controller
|
||||
class TestController {
|
||||
|
||||
@RequestMapping("/basicsecured/endpoint")
|
||||
@ResponseBody
|
||||
String secureEndpoint() {
|
||||
HttpServerTest.controller(SUCCESS) {
|
||||
SUCCESS.body
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/success")
|
||||
@ResponseBody
|
||||
String success() {
|
||||
HttpServerTest.controller(SUCCESS) {
|
||||
SUCCESS.body
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/query")
|
||||
@ResponseBody
|
||||
String query_param(@RequestParam("some") String param) {
|
||||
HttpServerTest.controller(QUERY_PARAM) {
|
||||
"some=$param"
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/redirect")
|
||||
@ResponseBody
|
||||
RedirectView redirect() {
|
||||
HttpServerTest.controller(REDIRECT) {
|
||||
new RedirectView(REDIRECT.body)
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/error-status")
|
||||
ResponseEntity error() {
|
||||
HttpServerTest.controller(ERROR) {
|
||||
new ResponseEntity(ERROR.body, HttpStatus.valueOf(ERROR.status))
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/exception")
|
||||
ResponseEntity exception() {
|
||||
HttpServerTest.controller(EXCEPTION) {
|
||||
throw new Exception(EXCEPTION.body)
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/captureHeaders")
|
||||
ResponseEntity capture_headers(@RequestHeader("X-Test-Request") String testRequestHeader) {
|
||||
HttpServerTest.controller(CAPTURE_HEADERS) {
|
||||
ResponseEntity.ok()
|
||||
.header("X-Test-Response", testRequestHeader)
|
||||
.body(CAPTURE_HEADERS.body)
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/path/{id}/param")
|
||||
@ResponseBody
|
||||
String path_param(@PathVariable("id") int id) {
|
||||
HttpServerTest.controller(PATH_PARAM) {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping("/child")
|
||||
@ResponseBody
|
||||
String indexed_child(@RequestParam("id") String id) {
|
||||
HttpServerTest.controller(INDEXED_CHILD) {
|
||||
INDEXED_CHILD.collectSpanAttributes { it == "id" ? id : null }
|
||||
INDEXED_CHILD.body
|
||||
}
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
ResponseEntity handleException(Throwable throwable) {
|
||||
new ResponseEntity(throwable.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
|
@ -1,111 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package filter
|
||||
|
||||
import io.opentelemetry.instrumentation.api.internal.HttpConstants
|
||||
import io.opentelemetry.instrumentation.test.AgentTestTrait
|
||||
import io.opentelemetry.instrumentation.test.asserts.TraceAssert
|
||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
||||
import io.opentelemetry.sdk.trace.data.SpanData
|
||||
import org.springframework.boot.SpringApplication
|
||||
import org.springframework.context.ConfigurableApplicationContext
|
||||
|
||||
import static io.opentelemetry.api.trace.SpanKind.INTERNAL
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
|
||||
|
||||
abstract class AbstractServletFilterTest extends HttpServerTest<ConfigurableApplicationContext> implements AgentTestTrait {
|
||||
|
||||
abstract Class<?> securityConfigClass()
|
||||
|
||||
abstract Class<?> filterConfigClass()
|
||||
|
||||
@Override
|
||||
ConfigurableApplicationContext startServer(int port) {
|
||||
def app = new SpringApplication(FilteredAppConfig, securityConfigClass(), filterConfigClass())
|
||||
app.setDefaultProperties(["server.port": port, "server.error.include-message": "always"])
|
||||
def context = app.run()
|
||||
return context
|
||||
}
|
||||
|
||||
@Override
|
||||
void stopServer(ConfigurableApplicationContext ctx) {
|
||||
ctx.close()
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasHandlerSpan(ServerEndpoint endpoint) {
|
||||
endpoint == NOT_FOUND
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasErrorPageSpans(ServerEndpoint endpoint) {
|
||||
endpoint == ERROR || endpoint == EXCEPTION || endpoint == NOT_FOUND
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasResponseSpan(ServerEndpoint endpoint) {
|
||||
endpoint == REDIRECT || endpoint == ERROR || endpoint == NOT_FOUND
|
||||
}
|
||||
|
||||
@Override
|
||||
void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
|
||||
switch (endpoint) {
|
||||
case REDIRECT:
|
||||
redirectSpan(trace, index, parent)
|
||||
break
|
||||
case ERROR:
|
||||
case NOT_FOUND:
|
||||
sendErrorSpan(trace, index, parent)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean testPathParam() {
|
||||
true
|
||||
}
|
||||
|
||||
@Override
|
||||
void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint) {
|
||||
trace.span(index) {
|
||||
name "ResourceHttpRequestHandler.handleRequest"
|
||||
kind INTERNAL
|
||||
childOf((SpanData) parent)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
String expectedHttpRoute(ServerEndpoint endpoint, String method) {
|
||||
if (method == HttpConstants._OTHER) {
|
||||
return getContextPath() + endpoint.path
|
||||
}
|
||||
switch (endpoint) {
|
||||
case PATH_PARAM:
|
||||
return getContextPath() + "/path/{id}/param"
|
||||
case NOT_FOUND:
|
||||
return getContextPath() + "/**"
|
||||
default:
|
||||
return super.expectedHttpRoute(endpoint, method)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void errorPageSpans(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
|
||||
trace.span(index) {
|
||||
name "BasicErrorController.error"
|
||||
kind INTERNAL
|
||||
childOf((SpanData) parent)
|
||||
attributes {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package filter
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.format.FormatterRegistry
|
||||
import org.springframework.http.HttpInputMessage
|
||||
import org.springframework.http.HttpOutputMessage
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.converter.AbstractHttpMessageConverter
|
||||
import org.springframework.http.converter.HttpMessageConverter
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException
|
||||
import org.springframework.http.converter.HttpMessageNotWritableException
|
||||
import org.springframework.util.StreamUtils
|
||||
import org.springframework.validation.MessageCodesResolver
|
||||
import org.springframework.validation.Validator
|
||||
import org.springframework.web.HttpMediaTypeNotAcceptableException
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy
|
||||
import org.springframework.web.context.request.NativeWebRequest
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver
|
||||
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer
|
||||
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry
|
||||
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
|
||||
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry
|
||||
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
@SpringBootApplication
|
||||
class FilteredAppConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
void configurePathMatch(PathMatchConfigurer configurer) {}
|
||||
|
||||
@Override
|
||||
void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
|
||||
configurer.favorPathExtension(false)
|
||||
.favorParameter(true)
|
||||
.ignoreAcceptHeader(true)
|
||||
.useJaf(false)
|
||||
.defaultContentTypeStrategy(new ContentNegotiationStrategy() {
|
||||
@Override
|
||||
List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
|
||||
return [MediaType.TEXT_PLAIN]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Override
|
||||
void configureAsyncSupport(AsyncSupportConfigurer configurer) {}
|
||||
|
||||
@Override
|
||||
void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}
|
||||
|
||||
@Override
|
||||
void addFormatters(FormatterRegistry registry) {}
|
||||
|
||||
@Override
|
||||
void addInterceptors(InterceptorRegistry registry) {}
|
||||
|
||||
@Override
|
||||
void addResourceHandlers(ResourceHandlerRegistry registry) {}
|
||||
|
||||
@Override
|
||||
void addCorsMappings(CorsRegistry registry) {}
|
||||
|
||||
@Override
|
||||
void addViewControllers(ViewControllerRegistry registry) {}
|
||||
|
||||
@Override
|
||||
void configureViewResolvers(ViewResolverRegistry registry) {}
|
||||
|
||||
@Override
|
||||
void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {}
|
||||
|
||||
@Override
|
||||
void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {}
|
||||
|
||||
@Override
|
||||
void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}
|
||||
|
||||
@Override
|
||||
void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}
|
||||
|
||||
@Override
|
||||
void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {}
|
||||
|
||||
@Override
|
||||
void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {}
|
||||
|
||||
@Override
|
||||
Validator getValidator() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
MessageCodesResolver getMessageCodesResolver() {
|
||||
return null
|
||||
}
|
||||
|
||||
@Bean
|
||||
HttpMessageConverter<Map<String, Object>> createPlainMapMessageConverter() {
|
||||
return new AbstractHttpMessageConverter<Map<String, Object>>(MediaType.TEXT_PLAIN) {
|
||||
|
||||
@Override
|
||||
protected boolean supports(Class<?> clazz) {
|
||||
return Map.isAssignableFrom(clazz)
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, Object> readInternal(Class<? extends Map<String, Object>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
|
||||
return null
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(Map<String, Object> stringObjectMap, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
|
||||
StreamUtils.copy(stringObjectMap.get("message") as String, StandardCharsets.UTF_8, outputMessage.getBody())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package filter
|
||||
|
||||
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.stereotype.Controller
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.ResponseBody
|
||||
import org.springframework.web.servlet.view.RedirectView
|
||||
|
||||
/**
|
||||
* None of the methods in this controller should be called because they are intercepted
|
||||
* by the filter
|
||||
*/
|
||||
@Controller
|
||||
class TestController {
|
||||
|
||||
@RequestMapping("/success")
|
||||
@ResponseBody
|
||||
String success() {
|
||||
throw new Exception("This should not be called")
|
||||
}
|
||||
|
||||
@RequestMapping("/query")
|
||||
@ResponseBody
|
||||
String query_param(@RequestParam("some") String param) {
|
||||
throw new Exception("This should not be called")
|
||||
}
|
||||
|
||||
@RequestMapping("/path/{id}/param")
|
||||
@ResponseBody
|
||||
String path_param(@PathVariable Integer id) {
|
||||
throw new Exception("This should not be called")
|
||||
}
|
||||
|
||||
@RequestMapping("/redirect")
|
||||
@ResponseBody
|
||||
RedirectView redirect() {
|
||||
throw new Exception("This should not be called")
|
||||
}
|
||||
|
||||
@RequestMapping("/error-status")
|
||||
ResponseEntity error() {
|
||||
throw new Exception("This should not be called")
|
||||
}
|
||||
|
||||
@RequestMapping("/exception")
|
||||
ResponseEntity exception() {
|
||||
throw new Exception("This should not be called")
|
||||
}
|
||||
|
||||
@RequestMapping("/captureHeaders")
|
||||
ResponseEntity capture_headers() {
|
||||
throw new Exception("This should not be called")
|
||||
}
|
||||
|
||||
@RequestMapping("/child")
|
||||
@ResponseBody
|
||||
ResponseEntity indexed_child(@RequestParam("id") String id) {
|
||||
throw new Exception("This should not be called")
|
||||
}
|
||||
|
||||
|
||||
@ExceptionHandler
|
||||
ResponseEntity handleException(Throwable throwable) {
|
||||
new ResponseEntity(throwable.message, HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.webmvc.boot;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_ERROR;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.LOGIN;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
|
||||
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
|
||||
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
|
||||
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
|
||||
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
|
||||
import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.opentelemetry.api.common.AttributeKey;
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.api.internal.HttpConstants;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
|
||||
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
|
||||
import io.opentelemetry.sdk.trace.data.StatusData;
|
||||
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest;
|
||||
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse;
|
||||
import io.opentelemetry.testing.internal.armeria.common.HttpData;
|
||||
import io.opentelemetry.testing.internal.armeria.common.MediaType;
|
||||
import io.opentelemetry.testing.internal.armeria.common.QueryParams;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.security.web.util.OnCommittedResponseWrapper;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
public abstract class AbstractSpringBootBasedTest
|
||||
extends AbstractHttpServerTest<ConfigurableApplicationContext> {
|
||||
|
||||
protected abstract ConfigurableApplicationContext context();
|
||||
|
||||
protected abstract Class<?> securityConfigClass();
|
||||
|
||||
@Override
|
||||
protected void stopServer(ConfigurableApplicationContext ctx) {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpServerTestOptions options) {
|
||||
super.configure(options);
|
||||
options.setContextPath("/xyz");
|
||||
options.setHasHandlerSpan(unused -> true);
|
||||
options.setHasResponseSpan(endpoint -> endpoint == REDIRECT || endpoint == NOT_FOUND);
|
||||
options.setTestPathParam(true);
|
||||
options.setHasErrorPageSpans(endpoint -> endpoint == NOT_FOUND);
|
||||
options.setHasRenderSpan(endpoint -> endpoint == REDIRECT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedHttpRoute(ServerEndpoint endpoint, String method) {
|
||||
if (HttpConstants._OTHER.equals(method)) {
|
||||
return getContextPath() + endpoint.getPath();
|
||||
}
|
||||
switch (endpoint.name()) {
|
||||
case "PATH_PARAM":
|
||||
return getContextPath() + "/path/{id}/param";
|
||||
case "NOT_FOUND":
|
||||
return getContextPath() + "/**";
|
||||
case "LOGIN":
|
||||
return getContextPath() + "/*";
|
||||
default:
|
||||
return super.expectedHttpRoute(endpoint, method);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSpansWithAuthError() {
|
||||
SavingAuthenticationProvider authProvider =
|
||||
context().getBean(SavingAuthenticationProvider.class);
|
||||
AggregatedHttpRequest request = request(AUTH_ERROR, "GET");
|
||||
|
||||
authProvider.latestAuthentications.clear();
|
||||
AggregatedHttpResponse response = client.execute(request).aggregate().join();
|
||||
|
||||
assertThat(response.status().code()).isEqualTo(401); // not secured
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> assertServerSpan(span, "GET", AUTH_ERROR, AUTH_ERROR.getStatus()),
|
||||
span ->
|
||||
span.satisfies(
|
||||
spanData -> assertThat(spanData.getName()).endsWith(".sendError"))
|
||||
.hasKind(SpanKind.INTERNAL),
|
||||
span -> errorPageSpanAssertions(null, null)));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"password", "dfsdföääöüüä", "🤓"})
|
||||
void testCharacterEncodingOfTestPassword(String testPassword) {
|
||||
SavingAuthenticationProvider authProvider =
|
||||
context().getBean(SavingAuthenticationProvider.class);
|
||||
|
||||
QueryParams form = QueryParams.of("username", "test", "password", testPassword);
|
||||
AggregatedHttpRequest request =
|
||||
AggregatedHttpRequest.of(
|
||||
request(LOGIN, "POST").headers().toBuilder().contentType(MediaType.FORM_DATA).build(),
|
||||
HttpData.ofUtf8(form.toQueryString()));
|
||||
|
||||
authProvider.latestAuthentications.clear();
|
||||
AggregatedHttpResponse response = client.execute(request).aggregate().join();
|
||||
|
||||
assertThat(response.status().code()).isEqualTo(302); // redirect after success
|
||||
assertThat(authProvider.latestAuthentications.get(0).getPassword()).isEqualTo(testPassword);
|
||||
|
||||
testing()
|
||||
.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> assertServerSpan(span, "POST", LOGIN, LOGIN.getStatus()),
|
||||
span ->
|
||||
span.satisfies(
|
||||
spanData ->
|
||||
assertThat(spanData.getName()).endsWith(".sendRedirect"))
|
||||
.hasKind(SpanKind.INTERNAL)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Consumer<SpanDataAssert>> errorPageSpanAssertions(
|
||||
String method, ServerEndpoint endpoint) {
|
||||
List<Consumer<SpanDataAssert>> spanAssertions = new ArrayList<>();
|
||||
spanAssertions.add(
|
||||
span ->
|
||||
span.hasName("BasicErrorController.error")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasAttributesSatisfying(Attributes::isEmpty));
|
||||
return spanAssertions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertResponseSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
String methodName = endpoint == NOT_FOUND ? "sendError" : "sendRedirect";
|
||||
if (endpoint == NOT_FOUND) {
|
||||
span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError"));
|
||||
} else {
|
||||
span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendRedirect"));
|
||||
}
|
||||
|
||||
span.hasKind(SpanKind.INTERNAL)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
satisfies(
|
||||
CODE_NAMESPACE,
|
||||
val ->
|
||||
val.satisfiesAnyOf(
|
||||
v -> assertThat(v).isEqualTo(OnCommittedResponseWrapper.class.getName()),
|
||||
v ->
|
||||
assertThat(v)
|
||||
.isEqualTo(
|
||||
"org.springframework.security.web.firewall.FirewalledResponse"),
|
||||
v ->
|
||||
assertThat(v)
|
||||
.isEqualTo("jakarta.servlet.http.HttpServletResponseWrapper"))),
|
||||
equalTo(CODE_FUNCTION, methodName));
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertRenderSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
span.hasName("Render RedirectView")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(
|
||||
AttributeKey.stringKey("spring-webmvc.view.type"), RedirectView.class.getName()));
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertHandlerSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
String handlerSpanName = getHandlerSpanName(endpoint);
|
||||
if (endpoint == NOT_FOUND) {
|
||||
handlerSpanName = "ResourceHttpRequestHandler.handleRequest";
|
||||
}
|
||||
span.hasName(handlerSpanName).hasKind(SpanKind.INTERNAL);
|
||||
if (endpoint == EXCEPTION) {
|
||||
span.hasStatus(StatusData.error());
|
||||
span.hasEventsSatisfyingExactly(
|
||||
event ->
|
||||
event
|
||||
.hasName("exception")
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(EXCEPTION_TYPE, "java.lang.RuntimeException"),
|
||||
equalTo(EXCEPTION_MESSAGE, EXCEPTION.getBody()),
|
||||
satisfies(EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class))));
|
||||
}
|
||||
return span;
|
||||
}
|
||||
|
||||
private static String getHandlerSpanName(ServerEndpoint endpoint) {
|
||||
if (QUERY_PARAM.equals(endpoint)) {
|
||||
return "TestController.queryParam";
|
||||
} else if (PATH_PARAM.equals(endpoint)) {
|
||||
return "TestController.pathParam";
|
||||
} else if (CAPTURE_HEADERS.equals(endpoint)) {
|
||||
return "TestController.captureHeaders";
|
||||
} else if (INDEXED_CHILD.equals(endpoint)) {
|
||||
return "TestController.indexedChild";
|
||||
}
|
||||
return "TestController." + endpoint.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
|
@ -3,11 +3,9 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package boot
|
||||
package io.opentelemetry.instrumentation.spring.webmvc.boot;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
class AppConfig {
|
||||
|
||||
}
|
||||
public class AppConfig {}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.webmvc.boot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
public class SavingAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
|
||||
|
||||
List<TestUserDetails> latestAuthentications = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void additionalAuthenticationChecks(
|
||||
UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) {
|
||||
// none
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserDetails retrieveUser(
|
||||
String username, UsernamePasswordAuthenticationToken authentication) {
|
||||
TestUserDetails details =
|
||||
new TestUserDetails(username, authentication.getCredentials().toString());
|
||||
|
||||
latestAuthentications.add(details);
|
||||
|
||||
return details;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.webmvc.boot;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest.controller;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS;
|
||||
|
||||
import java.util.Objects;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
@Controller
|
||||
public class TestController {
|
||||
|
||||
@RequestMapping("/basicsecured/endpoint")
|
||||
@ResponseBody
|
||||
String secureEndpoint() {
|
||||
return controller(SUCCESS, SUCCESS::getBody);
|
||||
}
|
||||
|
||||
@RequestMapping("/success")
|
||||
@ResponseBody
|
||||
String success() {
|
||||
return controller(SUCCESS, SUCCESS::getBody);
|
||||
}
|
||||
|
||||
@RequestMapping("/query")
|
||||
@ResponseBody
|
||||
String queryParam(@RequestParam("some") String param) {
|
||||
return controller(QUERY_PARAM, () -> "some=" + param);
|
||||
}
|
||||
|
||||
@RequestMapping("/redirect")
|
||||
@ResponseBody
|
||||
RedirectView redirect() {
|
||||
return controller(REDIRECT, () -> new RedirectView(REDIRECT.getBody()));
|
||||
}
|
||||
|
||||
@RequestMapping("/error-status")
|
||||
ResponseEntity<String> error() {
|
||||
return controller(
|
||||
ERROR,
|
||||
() ->
|
||||
ResponseEntity.status(HttpStatus.valueOf(ERROR.getStatus()).value())
|
||||
.body(ERROR.getBody()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("ThrowSpecificExceptions")
|
||||
@RequestMapping("/exception")
|
||||
ResponseEntity<String> exception() {
|
||||
return controller(
|
||||
EXCEPTION,
|
||||
() -> {
|
||||
throw new RuntimeException(EXCEPTION.getBody());
|
||||
});
|
||||
}
|
||||
|
||||
@RequestMapping("/captureHeaders")
|
||||
ResponseEntity<String> captureHeaders(@RequestHeader("X-Test-Request") String testRequestHeader) {
|
||||
return controller(
|
||||
CAPTURE_HEADERS,
|
||||
() ->
|
||||
ResponseEntity.ok()
|
||||
.header("X-Test-Response", testRequestHeader)
|
||||
.body(CAPTURE_HEADERS.getBody()));
|
||||
}
|
||||
|
||||
@RequestMapping("/path/{id}/param")
|
||||
@ResponseBody
|
||||
String pathParam(@PathVariable("id") int id) {
|
||||
return controller(PATH_PARAM, () -> String.valueOf(id));
|
||||
}
|
||||
|
||||
@RequestMapping("/child")
|
||||
@ResponseBody
|
||||
String indexedChild(@RequestParam("id") String id) {
|
||||
return controller(
|
||||
INDEXED_CHILD,
|
||||
() -> {
|
||||
INDEXED_CHILD.collectSpanAttributes(it -> Objects.equals(it, "id") ? id : null);
|
||||
return INDEXED_CHILD.getBody();
|
||||
});
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
ResponseEntity<String> handleException(Throwable throwable) {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
|
||||
.body(throwable.getMessage());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.webmvc.boot;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
public class TestUserDetails implements UserDetails {
|
||||
|
||||
private static final long serialVersionUID = 6470776949615799570L;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
TestUserDetails(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.webmvc.filter;
|
||||
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM;
|
||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.api.internal.HttpConstants;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
|
||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
|
||||
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
public abstract class AbstractServletFilterTest
|
||||
extends AbstractHttpServerTest<ConfigurableApplicationContext> {
|
||||
|
||||
protected abstract Class<?> securityConfigClass();
|
||||
|
||||
protected abstract Class<?> filterConfigClass();
|
||||
|
||||
@Override
|
||||
protected void stopServer(ConfigurableApplicationContext ctx) {
|
||||
ctx.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(HttpServerTestOptions options) {
|
||||
super.configure(options);
|
||||
options.setHasHandlerSpan(endpoint -> endpoint == NOT_FOUND);
|
||||
options.setHasErrorPageSpans(
|
||||
endpoint -> endpoint == ERROR || endpoint == EXCEPTION || endpoint == NOT_FOUND);
|
||||
options.setHasResponseSpan(
|
||||
endpoint -> endpoint == REDIRECT || endpoint == ERROR || endpoint == NOT_FOUND);
|
||||
options.setTestPathParam(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertResponseSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
if (endpoint == REDIRECT) {
|
||||
span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendRedirect"));
|
||||
} else if (endpoint == ERROR || endpoint == NOT_FOUND) {
|
||||
span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError"));
|
||||
}
|
||||
span.hasKind(SpanKind.INTERNAL);
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SpanDataAssert assertHandlerSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
String handlerSpanName = getHandlerSpanName(endpoint);
|
||||
if (endpoint == NOT_FOUND) {
|
||||
handlerSpanName = "ResourceHttpRequestHandler.handleRequest";
|
||||
}
|
||||
span.hasName(handlerSpanName).hasKind(SpanKind.INTERNAL);
|
||||
return span;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String expectedHttpRoute(ServerEndpoint endpoint, String method) {
|
||||
if (HttpConstants._OTHER.equals(method)) {
|
||||
return getContextPath() + endpoint.getPath();
|
||||
}
|
||||
switch (endpoint.name()) {
|
||||
case "PATH_PARAM":
|
||||
return getContextPath() + "/path/{id}/param";
|
||||
case "NOT_FOUND":
|
||||
return getContextPath() + "/**";
|
||||
default:
|
||||
return super.expectedHttpRoute(endpoint, method);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Consumer<SpanDataAssert>> errorPageSpanAssertions(
|
||||
String method, ServerEndpoint endpoint) {
|
||||
List<Consumer<SpanDataAssert>> spanAssertions = new ArrayList<>();
|
||||
spanAssertions.add(
|
||||
span ->
|
||||
span.hasName("BasicErrorController.error")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasAttributesSatisfying(Attributes::isEmpty));
|
||||
return spanAssertions;
|
||||
}
|
||||
|
||||
private static String getHandlerSpanName(ServerEndpoint endpoint) {
|
||||
if (QUERY_PARAM.equals(endpoint)) {
|
||||
return "TestController.queryParam";
|
||||
} else if (PATH_PARAM.equals(endpoint)) {
|
||||
return "TestController.pathParam";
|
||||
} else if (CAPTURE_HEADERS.equals(endpoint)) {
|
||||
return "TestController.captureHeaders";
|
||||
} else if (INDEXED_CHILD.equals(endpoint)) {
|
||||
return "TestController.indexedChild";
|
||||
}
|
||||
return "TestController." + endpoint.name().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.webmvc.filter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.format.FormatterRegistry;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.AbstractHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.validation.MessageCodesResolver;
|
||||
import org.springframework.validation.Validator;
|
||||
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
||||
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
|
||||
import org.springframework.web.servlet.HandlerExceptionResolver;
|
||||
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@SpringBootApplication
|
||||
public class FilteredAppConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void configurePathMatch(PathMatchConfigurer configurer) {}
|
||||
|
||||
@Override
|
||||
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
|
||||
configurer
|
||||
.favorPathExtension(false)
|
||||
.favorParameter(true)
|
||||
.ignoreAcceptHeader(true)
|
||||
.useJaf(false)
|
||||
.defaultContentTypeStrategy(webRequest -> Collections.singletonList(MediaType.TEXT_PLAIN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {}
|
||||
|
||||
@Override
|
||||
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}
|
||||
|
||||
@Override
|
||||
public void addFormatters(FormatterRegistry registry) {}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {}
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(CorsRegistry registry) {}
|
||||
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {}
|
||||
|
||||
@Override
|
||||
public void configureViewResolvers(ViewResolverRegistry registry) {}
|
||||
|
||||
@Override
|
||||
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {}
|
||||
|
||||
@Override
|
||||
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {}
|
||||
|
||||
@Override
|
||||
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}
|
||||
|
||||
@Override
|
||||
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}
|
||||
|
||||
@Override
|
||||
public void configureHandlerExceptionResolvers(
|
||||
List<HandlerExceptionResolver> exceptionResolvers) {}
|
||||
|
||||
@Override
|
||||
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {}
|
||||
|
||||
@Override
|
||||
public Validator getValidator() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageCodesResolver getMessageCodesResolver() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Bean
|
||||
HttpMessageConverter<Map<String, Object>> createPlainMapMessageConverter() {
|
||||
|
||||
return new AbstractHttpMessageConverter<Map<String, Object>>(MediaType.TEXT_PLAIN) {
|
||||
|
||||
@Override
|
||||
protected boolean supports(Class<?> clazz) {
|
||||
return Map.class.isAssignableFrom(clazz);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Map<String, Object> readInternal(
|
||||
Class<? extends Map<String, Object>> clazz, HttpInputMessage inputMessage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeInternal(
|
||||
Map<String, Object> stringObjectMap, HttpOutputMessage outputMessage) throws IOException {
|
||||
StreamUtils.copy(
|
||||
(String) stringObjectMap.get("message"),
|
||||
StandardCharsets.UTF_8,
|
||||
outputMessage.getBody());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.spring.webmvc.filter;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.servlet.view.RedirectView;
|
||||
|
||||
/**
|
||||
* None of the methods in this controller should be called because they are intercepted by the
|
||||
* filter
|
||||
*/
|
||||
@Controller
|
||||
public class TestController {
|
||||
|
||||
@RequestMapping("/success")
|
||||
@ResponseBody
|
||||
String success() throws Exception {
|
||||
throw new Exception("This should not be called");
|
||||
}
|
||||
|
||||
@RequestMapping("/query")
|
||||
@ResponseBody
|
||||
String queryParam(@RequestParam("some") String param) throws Exception {
|
||||
throw new Exception("This should not be called");
|
||||
}
|
||||
|
||||
@RequestMapping("/path/{id}/param")
|
||||
@ResponseBody
|
||||
String pathParam(@PathVariable Integer id) throws Exception {
|
||||
throw new Exception("This should not be called");
|
||||
}
|
||||
|
||||
@RequestMapping("/redirect")
|
||||
@ResponseBody
|
||||
RedirectView redirect() throws Exception {
|
||||
throw new Exception("This should not be called");
|
||||
}
|
||||
|
||||
@RequestMapping("/error-status")
|
||||
ResponseEntity<Object> error() throws Exception {
|
||||
throw new Exception("This should not be called");
|
||||
}
|
||||
|
||||
@RequestMapping("/exception")
|
||||
ResponseEntity<Object> exception() throws Exception {
|
||||
throw new Exception("This should not be called");
|
||||
}
|
||||
|
||||
@RequestMapping("/captureHeaders")
|
||||
ResponseEntity<Object> captureHeaders() throws Exception {
|
||||
throw new Exception("This should not be called");
|
||||
}
|
||||
|
||||
@RequestMapping("/child")
|
||||
@ResponseBody
|
||||
ResponseEntity<Object> indexedChild(@RequestParam("id") String id) throws Exception {
|
||||
throw new Exception("This should not be called");
|
||||
}
|
||||
|
||||
@ExceptionHandler
|
||||
ResponseEntity<String> handleException(Throwable throwable) {
|
||||
return new ResponseEntity<>(throwable.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
|
@ -650,15 +650,15 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
span, endpoint == EXCEPTION ? options.expectedException : null);
|
||||
span.hasParent(trace.getSpan(finalParentIndex));
|
||||
});
|
||||
if (options.hasRenderSpan.test(endpoint)) {
|
||||
spanAssertions.add(span -> assertRenderSpan(span, method, endpoint));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.hasResponseSpan.test(endpoint)) {
|
||||
int parentIndex = spanAssertions.size() - 1;
|
||||
spanAssertions.add(
|
||||
span -> {
|
||||
assertResponseSpan(span, method, endpoint);
|
||||
span.hasParent(trace.getSpan(parentIndex));
|
||||
});
|
||||
span -> assertResponseSpan(span, trace.getSpan(parentIndex), method, endpoint));
|
||||
}
|
||||
|
||||
if (options.hasErrorPageSpans.test(endpoint)) {
|
||||
|
@ -699,12 +699,25 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
|
|||
"assertHandlerSpan not implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
protected SpanDataAssert assertResponseSpan(
|
||||
SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) {
|
||||
span.hasParent(parentSpan);
|
||||
return assertResponseSpan(span, method, endpoint);
|
||||
}
|
||||
|
||||
protected SpanDataAssert assertResponseSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
throw new UnsupportedOperationException(
|
||||
"assertResponseSpan not implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
protected SpanDataAssert assertRenderSpan(
|
||||
SpanDataAssert span, String method, ServerEndpoint endpoint) {
|
||||
throw new UnsupportedOperationException(
|
||||
"assertRenderSpan not implemented in " + getClass().getName());
|
||||
}
|
||||
|
||||
protected List<Consumer<SpanDataAssert>> errorPageSpanAssertions(
|
||||
String method, ServerEndpoint endpoint) {
|
||||
throw new UnsupportedOperationException(
|
||||
|
|
|
@ -48,6 +48,7 @@ public final class HttpServerTestOptions {
|
|||
|
||||
Predicate<ServerEndpoint> hasHandlerSpan = unused -> false;
|
||||
Predicate<ServerEndpoint> hasResponseSpan = unused -> false;
|
||||
Predicate<ServerEndpoint> hasRenderSpan = unused -> false;
|
||||
Predicate<ServerEndpoint> hasErrorPageSpans = unused -> false;
|
||||
Predicate<ServerEndpoint> hasResponseCustomizer = unused -> false;
|
||||
|
||||
|
@ -132,6 +133,12 @@ public final class HttpServerTestOptions {
|
|||
return this;
|
||||
}
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
public HttpServerTestOptions setHasRenderSpan(Predicate<ServerEndpoint> hasRenderSpan) {
|
||||
this.hasRenderSpan = hasRenderSpan;
|
||||
return this;
|
||||
}
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
public HttpServerTestOptions setHasErrorPageSpans(Predicate<ServerEndpoint> hasErrorPageSpans) {
|
||||
this.hasErrorPageSpans = hasErrorPageSpans;
|
||||
|
|
Loading…
Reference in New Issue