Fix WebSocketHandlerMapping match for "/*"

Closes gh-34503
This commit is contained in:
rstoyanchev 2025-03-12 10:03:00 +00:00
parent 057742f27a
commit f8a82b46c1
3 changed files with 64 additions and 29 deletions

View File

@ -72,6 +72,14 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
private final Map<PathPattern, Object> pathPatternHandlerMap = new LinkedHashMap<>();
/**
* Handler for "/*", to be checked after all other handlers.
* Effectively similar to {@link #getDefaultHandler}, but processed at our level,
* within {@link #getHandlerInternal} where the request is available.
* @see org.springframework.web.socket.server.support.WebSocketHandlerMapping
*/
private @Nullable Object wildcardHandler;
@Override
public void setPatternParser(@Nullable PathPatternParser patternParser) {
@ -166,9 +174,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
}
else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Default mapping to " + getHandlerDescription(handler));
logger.trace("Wildcard mapping to " + getHandlerDescription(handler));
}
setDefaultHandler(resolvedHandler);
this.wildcardHandler = resolvedHandler;
}
else {
this.handlerMap.put(urlPath, resolvedHandler);
@ -197,9 +205,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
}
else if (urlPath.equals("/*")) {
if (logger.isTraceEnabled()) {
logger.trace("Removing default mapping: " + getDefaultHandler());
logger.trace("Removing wildcard mapping: " + getDefaultHandler());
}
setDefaultHandler(null);
this.wildcardHandler = null;
}
else {
Object mappedHandler = this.handlerMap.get(urlPath);
@ -248,6 +256,9 @@ public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping i
if (StringUtils.matchesCharacter(lookupPath, '/')) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = this.wildcardHandler;
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -23,6 +23,7 @@ import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
@ -45,10 +46,11 @@ class AbstractUrlHandlerMappingTests {
}
@Test
void registerDefaultHandler() {
TestController defaultHandler = new TestController();
mapping.registerHandler("/*", defaultHandler);
assertThat(mapping).satisfies(hasMappings(null, defaultHandler, Map.of()));
void registerWildcardHandler() throws Exception {
TestController handler = new TestController();
mapping.registerHandler("/*", handler);
assertThat(mapping).satisfies(hasMappings(null, null, Map.of()));
assertThat(getHandlerForPath("/abc")).isNotNull();
}
@Test
@ -70,43 +72,41 @@ class AbstractUrlHandlerMappingTests {
@Test
void unregisterRootHandler() {
TestController rootHandler = new TestController();
TestController defaultHandler = new TestController();
TestController specificHandler = new TestController();
mapping.registerHandler("/", rootHandler);
mapping.registerHandler("/*", defaultHandler);
mapping.registerHandler("/test", specificHandler);
assertThat(mapping).satisfies(hasMappings(rootHandler, defaultHandler, Map.of("/test", specificHandler)));
assertThat(mapping).satisfies(hasMappings(rootHandler, null, Map.of("/test", specificHandler)));
mapping.unregisterHandler("/");
assertThat(mapping).satisfies(hasMappings(null, defaultHandler, Map.of("/test", specificHandler)));
assertThat(mapping).satisfies(hasMappings(null, null, Map.of("/test", specificHandler)));
}
@Test
void unregisterDefaultHandler() {
TestController rootHandler = new TestController();
TestController defaultHandler = new TestController();
void unregisterDefaultHandler() throws Exception {
TestController wildcardHandler = new TestController();
TestController specificHandler = new TestController();
mapping.registerHandler("/", rootHandler);
mapping.registerHandler("/*", defaultHandler);
mapping.registerHandler("/*", wildcardHandler);
mapping.registerHandler("/test", specificHandler);
assertThat(mapping).satisfies(hasMappings(rootHandler, defaultHandler, Map.of("/test", specificHandler)));
assertThat(mapping).satisfies(hasMappings(null, null, Map.of("/test", specificHandler)));
assertThat(getHandlerForPath("/abc")).isNotNull();
mapping.unregisterHandler("/*");
assertThat(mapping).satisfies(hasMappings(rootHandler, null, Map.of("/test", specificHandler)));
assertThat(mapping).satisfies(hasMappings(null, null, Map.of("/test", specificHandler)));
assertThat(getHandlerForPath("/abc")).isNull();
}
@Test
void unregisterSpecificHandler() {
TestController rootHandler = new TestController();
TestController defaultHandler = new TestController();
TestController wildcardHandler = new TestController();
TestController specificHandler = new TestController();
mapping.registerHandler("/", rootHandler);
mapping.registerHandler("/*", defaultHandler);
mapping.registerHandler("/*", wildcardHandler);
mapping.registerHandler("/test", specificHandler);
assertThat(mapping).satisfies(hasMappings(rootHandler, defaultHandler, Map.of("/test", specificHandler)));
assertThat(mapping).satisfies(hasMappings(rootHandler, null, Map.of("/test", specificHandler)));
mapping.unregisterHandler("/test");
assertThat(mapping).satisfies(hasMappings(rootHandler, defaultHandler, Map.of()));
assertThat(mapping).satisfies(hasMappings(rootHandler, null, Map.of()));
}
@Test
@ -129,8 +129,9 @@ class AbstractUrlHandlerMappingTests {
}
private Consumer<AbstractUrlHandlerMapping> hasMappings(@Nullable Object rootHandler,
@Nullable Object defaultHandler, Map<String, Object> handlerMap) {
private Consumer<AbstractUrlHandlerMapping> hasMappings(
@Nullable Object rootHandler, @Nullable Object defaultHandler, Map<String, Object> handlerMap) {
return actual -> {
assertThat(actual.getRootHandler()).isEqualTo(rootHandler);
assertThat(actual.getDefaultHandler()).isEqualTo(defaultHandler);
@ -138,6 +139,12 @@ class AbstractUrlHandlerMappingTests {
};
}
private Object getHandlerForPath(String path) throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", path);
return mapping.getHandlerInternal(request);
}
private static class TestController {}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2024 the original author or authors.
* Copyright 2002-2025 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.
@ -16,7 +16,7 @@
package org.springframework.web.socket.server.support;
import java.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.Test;
@ -40,7 +40,7 @@ class WebSocketHandlerMappingTests {
HttpRequestHandler handler = new WebSocketHttpRequestHandler(mock());
WebSocketHandlerMapping mapping = new WebSocketHandlerMapping();
mapping.setUrlMap(Collections.singletonMap("/path", handler));
mapping.setUrlMap(Map.of("/path", handler));
mapping.setApplicationContext(new StaticWebApplicationContext());
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/path");
@ -66,4 +66,21 @@ class WebSocketHandlerMappingTests {
assertThat(chain).isNull();
}
@Test // gh-34503
void defaultHandler() throws Exception {
HttpRequestHandler handler = new WebSocketHttpRequestHandler(mock());
WebSocketHandlerMapping mapping = new WebSocketHandlerMapping();
mapping.setUrlMap(Map.of("/*", handler));
mapping.setApplicationContext(new StaticWebApplicationContext());
assertThat(mapping.getDefaultHandler()).isNull();
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/path");
HandlerExecutionChain chain = mapping.getHandler(request);
assertThat(chain).isNotNull();
assertThat(chain.getHandler()).isSameAs(handler);
}
}