Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
Expand Down Expand Up @@ -72,14 +71,11 @@ public abstract class MvcNamespaceUtils {

private static final String CORS_CONFIGURATION_BEAN_NAME = "mvcCorsConfigurations";

private static final String HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME = "mvcHandlerMappingIntrospector";


public static void registerDefaultComponents(ParserContext context, @Nullable Object source) {
registerBeanNameUrlHandlerMapping(context, source);
registerHttpRequestHandlerAdapter(context, source);
registerSimpleControllerHandlerAdapter(context, source);
registerHandlerMappingIntrospector(context, source);
registerLocaleResolver(context, source);
registerViewNameTranslator(context, source);
registerFlashMapManager(context, source);
Expand Down Expand Up @@ -275,22 +271,6 @@ else if (corsConfigurations != null) {
return new RuntimeBeanReference(CORS_CONFIGURATION_BEAN_NAME);
}

/**
* Registers an {@link HandlerMappingIntrospector} under a well-known name
* unless already registered.
*/
@SuppressWarnings("removal")
private static void registerHandlerMappingIntrospector(ParserContext context, @Nullable Object source) {
if (!context.getRegistry().containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
RootBeanDefinition beanDef = new RootBeanDefinition(HandlerMappingIntrospector.class);
beanDef.setSource(source);
beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
beanDef.setLazyInit(true);
context.getRegistry().registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME, beanDef);
context.registerComponent(new BeanComponentDefinition(beanDef, HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME));
}
}

/**
* Registers an {@link AcceptHeaderLocaleResolver} under a well-known name
* unless already registered.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.KotlinDetector;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.Formatter;
Expand All @@ -61,6 +60,7 @@
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.PreFlightRequestHandler;
import org.springframework.web.method.support.CompositeUriComponentsContributor;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
Expand All @@ -76,8 +76,8 @@
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
import org.springframework.web.servlet.handler.ConversionServiceExposingInterceptor;
import org.springframework.web.servlet.handler.DefaultPreFlightRequestHandler;
import org.springframework.web.servlet.handler.HandlerExceptionResolverComposite;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
Expand Down Expand Up @@ -1161,11 +1161,9 @@ protected final Map<String, CorsConfiguration> getCorsConfigurations() {
protected void addCorsMappings(CorsRegistry registry) {
}

@SuppressWarnings("removal")
@Bean
@Lazy
public HandlerMappingIntrospector mvcHandlerMappingIntrospector() {
return new HandlerMappingIntrospector();
public PreFlightRequestHandler preFlightRequestHandler() {
return new DefaultPreFlightRequestHandler();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ private class CorsInterceptor implements HandlerInterceptor, CorsConfigurationSo

private final @Nullable CorsConfiguration config;

public CorsInterceptor(@Nullable CorsConfiguration config) {
CorsInterceptor(@Nullable CorsConfiguration config) {
this.config = config;
}

Expand Down Expand Up @@ -795,7 +795,7 @@ protected boolean invokeCorsProcessor(
private final class PreFlightHttpRequestHandler
extends CorsInterceptor implements HttpRequestHandler, PreFlightRequestHandler {

public PreFlightHttpRequestHandler(@Nullable CorsConfiguration config) {
PreFlightHttpRequestHandler(@Nullable CorsConfiguration config) {
super(config);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* Copyright 2002-present 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.web.servlet.handler;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jspecify.annotations.Nullable;

import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.PreFlightRequestHandler;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.util.ServletRequestPathUtils;

/**
* Default implementation of {@link PreFlightRequestHandler} that discovers all
* {@link HandlerMapping} beans in the {@link ApplicationContext} and uses them to
* find a handler for a pre-flight CORS request, then delegates to the
* {@link PreFlightRequestHandler} returned by that mapping.
*
* <p>Handler mappings are detected and sorted in the same way as in
* {@link DispatcherServlet}, with fallback to the default mappings configured in
* {@code DispatcherServlet.properties} if no mappings are found.
*
* @author Rossen Stoyanchev
* @since 7.1
* @see PreFlightRequestHandler
* @see HandlerMapping
* @see DispatcherServlet
*/
public class DefaultPreFlightRequestHandler
implements PreFlightRequestHandler, ApplicationContextAware, InitializingBean {

private @Nullable ApplicationContext applicationContext;

private @Nullable List<HandlerMapping> handlerMappings;

@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}

@Override
public void afterPropertiesSet() {
if (this.handlerMappings == null) {
Assert.notNull(this.applicationContext, "No ApplicationContext");
this.handlerMappings = initHandlerMappings(this.applicationContext);
}
}

private static List<HandlerMapping> initHandlerMappings(ApplicationContext context) {
Map<String, HandlerMapping> beans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);

if (!beans.isEmpty()) {
List<HandlerMapping> mappings = new ArrayList<>(beans.values());
AnnotationAwareOrderComparator.sort(mappings);
return Collections.unmodifiableList(mappings);
}

return Collections.unmodifiableList(initFallback(context));
}

private static List<HandlerMapping> initFallback(ApplicationContext applicationContext) {
Properties properties;
try {
Resource resource = new ClassPathResource("DispatcherServlet.properties", DispatcherServlet.class);
properties = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load DispatcherServlet.properties: " + ex.getMessage());
}

String value = properties.getProperty(HandlerMapping.class.getName());
String[] names = StringUtils.commaDelimitedListToStringArray(value);
List<HandlerMapping> result = new ArrayList<>(names.length);
for (String name : names) {
try {
Class<?> clazz = ClassUtils.forName(name, DispatcherServlet.class.getClassLoader());
Object mapping = applicationContext.getAutowireCapableBeanFactory().createBean(clazz);
result.add((HandlerMapping) mapping);
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException("Could not find default HandlerMapping [" + name + "]");
}
}
return result;
}

/**
* Find the matching {@link HandlerMapping} for the request, and invoke the
* handler it returns as a {@link PreFlightRequestHandler}.
* @throws NoHandlerFoundException if no handler matches the request
* @since 7.1
*/
@Override
public void handlePreFlight(HttpServletRequest request, HttpServletResponse response) throws Exception {
Assert.state(this.handlerMappings != null, "Not yet initialized via afterPropertiesSet.");
Assert.state(CorsUtils.isPreFlightRequest(request), "Not a pre-flight request.");
RequestPath previousPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);
try {
ServletRequestPathUtils.parseAndCache(request);
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain chain = mapping.getHandler(request);
if (chain != null) {
Object handler = chain.getHandler();
if (handler instanceof PreFlightRequestHandler preFlightHandler) {
preFlightHandler.handlePreFlight(request, response);
return;
}
throw new IllegalStateException("Expected PreFlightRequestHandler: " + handler.getClass());
}
}
throw new NoHandlerFoundException(
request.getMethod(), request.getRequestURI(), new ServletServerHttpRequest(request).getHeaders());
}
finally {
ServletRequestPathUtils.setParsedRequestPath(previousPath, request);
}
}
}
Loading