diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java index e7e7880339..3b11003aaa 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnection.java @@ -63,6 +63,7 @@ public final class MockMvcWebConnection implements WebConnection { private final MockMvc mockMvc; + @Nullable private final String contextPath; private WebClient webClient; @@ -91,7 +92,7 @@ public final class MockMvcWebConnection implements WebConnection { * @param webClient the {@link WebClient} to use (never {@code null}) * @param contextPath the contextPath to use */ - public MockMvcWebConnection(MockMvc mockMvc, WebClient webClient, String contextPath) { + public MockMvcWebConnection(MockMvc mockMvc, WebClient webClient, @Nullable String contextPath) { Assert.notNull(mockMvc, "MockMvc must not be null"); Assert.notNull(webClient, "WebClient must not be null"); validateContextPath(contextPath); diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/ForwardController.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/ForwardController.java index 0ab357e274..0a96d54bac 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/ForwardController.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/ForwardController.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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. @@ -28,7 +28,7 @@ public class ForwardController { @RequestMapping("/forward") public String forward() { - return "forward:/"; + return "forward:/a"; } } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HelloController.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HelloController.java index 98e09a810e..848aa70422 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HelloController.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/HelloController.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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. @@ -28,7 +28,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { - @RequestMapping + @RequestMapping("/a") public String header(HttpServletRequest request) { return "hello"; } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcConnectionBuilderSupportTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcConnectionBuilderSupportTests.java index 176f48d78e..b9b40cf85e 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcConnectionBuilderSupportTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcConnectionBuilderSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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. @@ -151,7 +151,7 @@ public class MockMvcConnectionBuilderSupportTests { @RestController static class ContextPathController { - @RequestMapping + @RequestMapping("/def") public String contextPath(HttpServletRequest request) { return request.getContextPath(); } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java index c008dd79ae..e12a20a189 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebClientBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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. @@ -157,7 +157,7 @@ public class MockMvcWebClientBuilderTests { @RestController static class ContextPathController { - @RequestMapping + @RequestMapping("/test") public String contextPath(HttpServletRequest request) { return "mvc"; } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java index 6a3d0c5157..50388375f2 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/MockMvcWebConnectionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2019 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. @@ -18,6 +18,7 @@ package org.springframework.test.web.servlet.htmlunit; import java.io.IOException; +import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.Page; import com.gargoylesoftware.htmlunit.WebClient; import org.junit.Test; @@ -44,7 +45,7 @@ public class MockMvcWebConnectionTests { @Test public void contextPathNull() throws IOException { - this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient)); + this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient, null)); Page page = this.webClient.getPage("http://localhost/context/a"); assertThat(page.getWebResponse().getStatusCode(), equalTo(200)); } @@ -59,8 +60,21 @@ public class MockMvcWebConnectionTests { @Test public void contextPathEmpty() throws IOException { this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient, "")); - Page page = this.webClient.getPage("http://localhost/context/a"); - assertThat(page.getWebResponse().getStatusCode(), equalTo(200)); + try { + this.webClient.getPage("http://localhost/context/a"); + fail("Empty context path (root context) should not match to a URL with a context path"); + } + catch (FailingHttpStatusCodeException ex) { + assertEquals(404, ex.getStatusCode()); + } + this.webClient.setWebConnection(new MockMvcWebConnection(this.mockMvc, this.webClient)); + try { + this.webClient.getPage("http://localhost/context/a"); + fail("No context is the same providing an empty context path"); + } + catch (FailingHttpStatusCodeException ex) { + assertEquals(404, ex.getStatusCode()); + } } @Test diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java index 6881c9b3ca..9903825222 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/htmlunit/webdriver/MockMvcHtmlUnitDriverBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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. @@ -145,7 +145,7 @@ public class MockMvcHtmlUnitDriverBuilderTests { @RestController static class ContextPathController { - @RequestMapping + @RequestMapping("/test") public String contextPath(HttpServletRequest request) { return EXPECTED_BODY; } diff --git a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/RedirectTests.java b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/RedirectTests.java index 03d8b858f8..0c43c76e6c 100644 --- a/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/RedirectTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/servlet/samples/standalone/RedirectTests.java @@ -115,7 +115,7 @@ public class RedirectTests { return "persons/index"; } - @PostMapping + @PostMapping("/persons") public String save(@Valid Person person, Errors errors, RedirectAttributes redirectAttrs) { if (errors.hasErrors()) { return "persons/add"; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java index ff87244c8b..f485c64560 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/condition/PatternsRequestCondition.java @@ -16,9 +16,9 @@ package org.springframework.web.reactive.result.condition; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Set; @@ -29,6 +29,7 @@ import org.springframework.http.server.PathContainer; import org.springframework.lang.Nullable; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.pattern.PathPattern; +import org.springframework.web.util.pattern.PathPatternParser; /** * A logical disjunction (' || ') request condition that matches a request @@ -40,6 +41,10 @@ import org.springframework.web.util.pattern.PathPattern; */ public final class PatternsRequestCondition extends AbstractRequestCondition { + private static final SortedSet EMPTY_PATTERNS = + new TreeSet<>(Collections.singleton(new PathPatternParser().parse(""))); + + private final SortedSet patterns; @@ -55,7 +60,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition patterns) { - this(new TreeSet<>(patterns)); + this(patterns.isEmpty() ? EMPTY_PATTERNS : new TreeSet<>(patterns)); } @@ -89,8 +94,9 @@ public final class PatternsRequestCondition extends AbstractRequestCondition combined = new ArrayList<>(); + SortedSet combined; if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) { + combined = new TreeSet<>(); for (PathPattern pattern1 : this.patterns) { for (PathPattern pattern2 : other.patterns) { combined.add(pattern1.combine(pattern2)); @@ -98,10 +104,13 @@ public final class PatternsRequestCondition extends AbstractRequestCondition prependLeadingSlash(Collection patterns) { + if (patterns.isEmpty()) { + return Collections.singleton(""); + } Set result = new LinkedHashSet<>(patterns.size()); for (String pattern : patterns) { if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/condition/PatternsRequestConditionTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/condition/PatternsRequestConditionTests.java index 303ff19c0d..4219de361a 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/condition/PatternsRequestConditionTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/condition/PatternsRequestConditionTests.java @@ -115,9 +115,7 @@ public class PatternsRequestConditionTests { assertEquals("/{foo}", match.getPatterns().iterator().next()); } - // SPR-8410 - - @Test + @Test // SPR-8410 public void matchSuffixPatternUsingFileExtensions() { String[] patterns = new String[] {"/jobs/{jobName}"}; List extensions = Arrays.asList("json"); @@ -183,6 +181,19 @@ public class PatternsRequestConditionTests { assertNull(match); } + @Test // gh-22543 + public void matchWithEmptyPatterns() { + PatternsRequestCondition condition = new PatternsRequestCondition(); + assertEquals(new PatternsRequestCondition(""), condition); + assertNotNull(condition.getMatchingCondition(new MockHttpServletRequest("GET", ""))); + assertNull(condition.getMatchingCondition(new MockHttpServletRequest("GET", "/anything"))); + + condition = condition.combine(new PatternsRequestCondition()); + assertEquals(new PatternsRequestCondition(""), condition); + assertNotNull(condition.getMatchingCondition(new MockHttpServletRequest("GET", ""))); + assertNull(condition.getMatchingCondition(new MockHttpServletRequest("GET", "/anything"))); + } + @Test public void compareEqualPatterns() { PatternsRequestCondition c1 = new PatternsRequestCondition("/foo*"); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java index 324ed59475..6ff416fdae 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMappingTests.java @@ -295,14 +295,12 @@ public class RequestMappingInfoHandlerMappingTests { assertEquals("/{path1}/2", request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)); } - @Test + @Test // gh-22543 public void handleMatchBestMatchingPatternAttributeNoPatternsDefined() { - RequestMappingInfo key = RequestMappingInfo.paths().build(); - MockHttpServletRequest request = new MockHttpServletRequest("GET", "/1/2"); - - this.handlerMapping.handleMatch(key, "/1/2", request); - - assertEquals("/1/2", request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)); + String path = ""; + MockHttpServletRequest request = new MockHttpServletRequest("GET", path); + this.handlerMapping.handleMatch(RequestMappingInfo.paths().build(), path, request); + assertEquals(path, request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)); } @Test diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java index d2fcb90af2..ce1c1e206c 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/RequestMappingInfoTests.java @@ -49,7 +49,7 @@ public class RequestMappingInfoTests { public void createEmpty() { RequestMappingInfo info = paths().build(); - assertEquals(0, info.getPatternsCondition().getPatterns().size()); + assertEquals(Collections.singleton(""), info.getPatternsCondition().getPatterns()); // gh-22543 assertEquals(0, info.getMethodsCondition().getMethods().size()); assertEquals(true, info.getConsumesCondition().isEmpty()); assertEquals(true, info.getProducesCondition().isEmpty()); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java index 5383b85220..791ae3fe81 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ServletAnnotationControllerHandlerMethodTests.java @@ -796,13 +796,19 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl .hasMessageContaining("Ambiguous mapping"); } - @Test + @Test // gh-22543 public void unmappedPathMapping() throws Exception { initServletWithControllers(UnmappedPathController.class); MockHttpServletRequest request = new MockHttpServletRequest("GET", "/bogus-unmapped"); MockHttpServletResponse response = new MockHttpServletResponse(); getServlet().service(request, response); + assertEquals(404, response.getStatus()); + + request = new MockHttpServletRequest("GET", ""); + response = new MockHttpServletResponse(); + getServlet().service(request, response); + assertEquals(200, response.getStatus()); assertEquals("get", response.getContentAsString()); }