From 8a9b082d8a0f84e7b72416e57e9b6a7a9321567e Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Mon, 27 Jun 2022 16:51:03 +0100 Subject: [PATCH] Improve MappingMatch determination in mock request MockHttpServletRequest now checks the requestURI and servletPath to check whether they imply a Servlet path mapping, which is the case when the requestURI is longer than the contextPath + servletPath. This is essential when parsed patterns are in use in which case the request path is parsed taking into account only the requestURI and the contextPath. However, if the MappingMatch indicates a match by Servlet path, then the servletPath is also taken into account. See gh-28607 --- .../mock/web/MockHttpServletRequest.java | 35 ++++++++++++++++++- .../servlet/MockHttpServletRequest.java | 29 ++++++++++++--- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java index 106c87c49258..052bee59876e 100644 --- a/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java +++ b/spring-test/src/main/java/org/springframework/mock/web/MockHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,10 +52,12 @@ import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletMapping; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpUpgradeHandler; +import jakarta.servlet.http.MappingMatch; import jakarta.servlet.http.Part; import org.springframework.http.HttpHeaders; @@ -69,6 +71,7 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; +import org.springframework.web.util.UrlPathHelper; /** * Mock implementation of the {@link jakarta.servlet.http.HttpServletRequest} interface. @@ -274,6 +277,9 @@ public class MockHttpServletRequest implements HttpServletRequest { private final MultiValueMap parts = new LinkedMultiValueMap<>(); + @Nullable + private HttpServletMapping httpServletMapping; + // --------------------------------------------------------------------- // Constructors @@ -1389,6 +1395,33 @@ public Collection getParts() throws IOException, ServletException { return result; } + public void setHttpServletMapping(@Nullable HttpServletMapping httpServletMapping) { + this.httpServletMapping = httpServletMapping; + } + + @Override + public HttpServletMapping getHttpServletMapping() { + return (this.httpServletMapping == null ? + new MockHttpServletMapping("", "", "", determineMappingMatch()) : + this.httpServletMapping); + } + + /** + * Best effort to detect a Servlet path mapping, e.g. {@code "/foo/*"}, by + * checking whether the length of requestURI > contextPath + servletPath. + * This helps {@link org.springframework.web.util.ServletRequestPathUtils} + * to take into account the Servlet path when parsing the requestURI. + */ + @Nullable + private MappingMatch determineMappingMatch() { + if (StringUtils.hasText(this.requestURI) && StringUtils.hasText(this.servletPath)) { + String path = UrlPathHelper.defaultInstance.getRequestUri(this); + String prefix = this.contextPath + this.servletPath; + return (path.startsWith(prefix) && (path.length() > prefix.length()) ? MappingMatch.PATH : null); + } + return null; + } + @Override public T upgrade(Class handlerClass) throws IOException, ServletException { throw new UnsupportedOperationException(); diff --git a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletRequest.java b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletRequest.java index 1eb76b1f5aed..1218e2757cb7 100644 --- a/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletRequest.java +++ b/spring-web/src/testFixtures/java/org/springframework/web/testfixture/servlet/MockHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,6 +57,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpUpgradeHandler; +import jakarta.servlet.http.MappingMatch; import jakarta.servlet.http.Part; import org.springframework.http.HttpHeaders; @@ -70,6 +71,7 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.StreamUtils; import org.springframework.util.StringUtils; +import org.springframework.web.util.UrlPathHelper; /** * Mock implementation of the {@link jakarta.servlet.http.HttpServletRequest} interface. @@ -275,7 +277,8 @@ public class MockHttpServletRequest implements HttpServletRequest { private final MultiValueMap parts = new LinkedMultiValueMap<>(); - private HttpServletMapping httpServletMapping = new MockHttpServletMapping("", "", "", null); + @Nullable + private HttpServletMapping httpServletMapping; // --------------------------------------------------------------------- @@ -1392,13 +1395,31 @@ public Collection getParts() throws IOException, ServletException { return result; } - public void setHttpServletMapping(HttpServletMapping httpServletMapping) { + public void setHttpServletMapping(@Nullable HttpServletMapping httpServletMapping) { this.httpServletMapping = httpServletMapping; } @Override public HttpServletMapping getHttpServletMapping() { - return this.httpServletMapping; + return (this.httpServletMapping == null ? + new MockHttpServletMapping("", "", "", determineMappingMatch()) : + this.httpServletMapping); + } + + /** + * Best effort to detect a Servlet path mapping, e.g. {@code "/foo/*"}, by + * checking whether the length of requestURI > contextPath + servletPath. + * This helps {@link org.springframework.web.util.ServletRequestPathUtils} + * to take into account the Servlet path when parsing the requestURI. + */ + @Nullable + private MappingMatch determineMappingMatch() { + if (StringUtils.hasText(this.requestURI) && StringUtils.hasText(this.servletPath)) { + String path = UrlPathHelper.defaultInstance.getRequestUri(this); + String prefix = this.contextPath + this.servletPath; + return (path.startsWith(prefix) && (path.length() > prefix.length()) ? MappingMatch.PATH : null); + } + return null; } @Override