Merge branch '6.0.x'

This commit is contained in:
rstoyanchev
2023-11-14 19:24:43 +00:00
2 changed files with 259 additions and 115 deletions

View File

@@ -23,7 +23,10 @@ import java.util.Collections;
import java.util.List;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@@ -42,12 +45,14 @@ import org.springframework.web.context.support.AnnotationConfigWebApplicationCon
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.RouterFunctions;
import org.springframework.web.servlet.function.ServerResponse;
import org.springframework.web.servlet.function.support.RouterFunctionMapping;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector.CachedResult;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.testfixture.servlet.MockFilterChain;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
@@ -202,68 +207,67 @@ public class HandlerMappingIntrospectorTests {
@Test
void cacheFilter() throws Exception {
testCacheFilter(new MockHttpServletRequest());
}
@Test
void cacheFilterRestoresPreviousValues() throws Exception {
TestMatchableHandlerMapping previousMapping = new TestMatchableHandlerMapping();
CorsConfiguration previousCorsConfig = new CorsConfiguration();
MockHttpServletRequest request = new MockHttpServletRequest();
request.setAttribute(HandlerMappingIntrospector.MAPPING_ATTRIBUTE, previousMapping);
request.setAttribute(HandlerMappingIntrospector.CORS_CONFIG_ATTRIBUTE, previousCorsConfig);
testCacheFilter(request);
assertThat(previousMapping.getInvocationCount()).isEqualTo(0);
assertThat(request.getAttribute(HandlerMappingIntrospector.MAPPING_ATTRIBUTE)).isSameAs(previousMapping);
assertThat(request.getAttribute(HandlerMappingIntrospector.CORS_CONFIG_ATTRIBUTE)).isSameAs(previousCorsConfig);
}
private void testCacheFilter(MockHttpServletRequest request) throws IOException, ServletException {
CorsConfiguration corsConfig = new CorsConfiguration();
TestMatchableHandlerMapping mapping = new TestMatchableHandlerMapping();
StaticWebApplicationContext context = new StaticWebApplicationContext();
context.registerBean(TestMatchableHandlerMapping.class, () -> mapping);
context.refresh();
mapping.registerHandler("/test", new TestHandler(corsConfig));
HandlerMappingIntrospector introspector = initIntrospector(context);
HandlerMappingIntrospector introspector = initIntrospector(mapping);
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/test");
MockHttpServletResponse response = new MockHttpServletResponse();
Filter filter = (req, res, chain) -> {
try {
for (int i = 0; i < 10; i++) {
introspector.getMatchableHandlerMapping((HttpServletRequest) req);
introspector.getCorsConfiguration((HttpServletRequest) req);
}
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
chain.doFilter(req, res);
};
MockFilterChain filterChain = new MockFilterChain(
new TestServlet(), new CacheResultFilter(introspector), new AuthFilter(introspector, corsConfig));
HttpServlet servlet = new HttpServlet() {
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) {
try {
res.getWriter().print("Success");
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
};
new MockFilterChain(servlet, introspector.createCacheFilter(), filter)
.doFilter(request, response);
filterChain.doFilter(request, response);
assertThat(response.getContentAsString()).isEqualTo("Success");
assertThat(mapping.getInvocationCount()).isEqualTo(1);
assertThat(mapping.getMatchCount()).isEqualTo(1);
}
private HandlerMappingIntrospector initIntrospector(WebApplicationContext context) {
@Test
void cacheFilterWithNestedDispatch() throws Exception {
CorsConfiguration corsConfig1 = new CorsConfiguration();
CorsConfiguration corsConfig2 = new CorsConfiguration();
TestMatchableHandlerMapping mapping1 = new TestMatchableHandlerMapping();
TestMatchableHandlerMapping mapping2 = new TestMatchableHandlerMapping();
mapping1.registerHandler("/1", new TestHandler(corsConfig1));
mapping2.registerHandler("/2", new TestHandler(corsConfig2));
HandlerMappingIntrospector introspector = initIntrospector(mapping1, mapping2);
MockFilterChain filterChain = new MockFilterChain(
new TestServlet(),
new CacheResultFilter(introspector),
new AuthFilter(introspector, corsConfig1),
(req, res, chain) -> chain.doFilter(new MockHttpServletRequest("GET", "/2"), res),
new CacheResultFilter(introspector),
new AuthFilter(introspector, corsConfig2));
MockHttpServletResponse response = new MockHttpServletResponse();
filterChain.doFilter(new MockHttpServletRequest("GET", "/1"), response);
assertThat(response.getContentAsString()).isEqualTo("Success");
assertThat(mapping1.getInvocationCount()).isEqualTo(2);
assertThat(mapping2.getInvocationCount()).isEqualTo(1);
assertThat(mapping1.getMatchCount()).isEqualTo(1);
assertThat(mapping2.getMatchCount()).isEqualTo(1);
}
private HandlerMappingIntrospector initIntrospector(TestMatchableHandlerMapping... mappings) {
StaticWebApplicationContext context = new StaticWebApplicationContext();
int index = 0;
for (TestMatchableHandlerMapping mapping : mappings) {
context.registerBean("mapping" + index++, TestMatchableHandlerMapping.class, () -> mapping);
}
context.refresh();
return initIntrospector(context);
}
private static HandlerMappingIntrospector initIntrospector(WebApplicationContext context) {
HandlerMappingIntrospector introspector = new HandlerMappingIntrospector();
introspector.setApplicationContext(context);
introspector.afterPropertiesSet();
@@ -327,23 +331,111 @@ public class HandlerMappingIntrospectorTests {
}
private static class TestMatchableHandlerMapping implements MatchableHandlerMapping {
private static class TestMatchableHandlerMapping extends SimpleUrlHandlerMapping {
private int invocationCount;
private int matchCount;
public int getInvocationCount() {
return this.invocationCount;
}
@Override
public HandlerExecutionChain getHandler(HttpServletRequest request) {
this.invocationCount++;
return new HandlerExecutionChain(new Object());
public int getMatchCount() {
return this.matchCount;
}
@Override
public RequestMatchResult match(HttpServletRequest request, String pattern) {
throw new UnsupportedOperationException();
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
this.invocationCount++;
Object handler = super.getHandlerInternal(request);
if (handler != null) {
this.matchCount++;
}
return handler;
}
}
private static class TestHandler implements CorsConfigurationSource {
private final CorsConfiguration corsConfig;
private TestHandler(CorsConfiguration corsConfig) {
this.corsConfig = corsConfig;
}
@Override
public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
return this.corsConfig;
}
}
private static class CacheResultFilter implements Filter {
private final HandlerMappingIntrospector introspector;
private CacheResultFilter(HandlerMappingIntrospector introspector) {
this.introspector = introspector;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws ServletException {
CachedResult previousValue = this.introspector.setCache((HttpServletRequest) req);
try {
chain.doFilter(req, res);
}
catch (Exception ex) {
throw new ServletException("HandlerMapping introspection failed", ex);
}
finally {
this.introspector.resetCache(req, previousValue);
}
}
}
private static class AuthFilter implements Filter {
private final HandlerMappingIntrospector introspector;
private final CorsConfiguration corsConfig;
private AuthFilter(HandlerMappingIntrospector introspector, CorsConfiguration corsConfig) {
this.introspector = introspector;
this.corsConfig = corsConfig;
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
try {
for (int i = 0; i < 10; i++) {
HttpServletRequest httpRequest = (HttpServletRequest) req;
assertThat(introspector.getMatchableHandlerMapping(httpRequest)).isNotNull();
assertThat(introspector.getCorsConfiguration(httpRequest)).isSameAs(corsConfig);
}
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
chain.doFilter(req, res);
}
}
private static class TestServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) {
try {
res.getWriter().print("Success");
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
}