mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77140da643 |
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("10.23.1");
|
||||
checkstyle.setToolVersion("10.22.0");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
|
||||
@@ -9,15 +9,15 @@ javaPlatform {
|
||||
dependencies {
|
||||
api(platform("com.fasterxml.jackson:jackson-bom:2.15.4"))
|
||||
api(platform("io.micrometer:micrometer-bom:1.12.12"))
|
||||
api(platform("io.netty:netty-bom:4.1.121.Final"))
|
||||
api(platform("io.netty:netty-bom:4.1.119.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2023.0.19"))
|
||||
api(platform("io.projectreactor:reactor-bom:2023.0.16"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.5"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.27"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.26"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
api(platform("org.assertj:assertj-bom:3.27.3"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.0.21"))
|
||||
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.21"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.0.18"))
|
||||
api(platform("org.eclipse.jetty.ee10:jetty-ee10-bom:12.0.18"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.6.3"))
|
||||
api(platform("org.junit:junit-bom:5.10.5"))
|
||||
@@ -101,7 +101,7 @@ dependencies {
|
||||
api("org.apache.derby:derby:10.16.1.1")
|
||||
api("org.apache.derby:derbyclient:10.16.1.1")
|
||||
api("org.apache.derby:derbytools:10.16.1.1")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.4.4")
|
||||
api("org.apache.httpcomponents.client5:httpclient5:5.4.3")
|
||||
api("org.apache.httpcomponents.core5:httpcore5-reactive:5.3.4")
|
||||
api("org.apache.poi:poi-ooxml:5.2.5")
|
||||
api("org.apache.tomcat.embed:tomcat-embed-core:10.1.28")
|
||||
@@ -121,7 +121,7 @@ dependencies {
|
||||
api("org.eclipse:yasson:2.0.4")
|
||||
api("org.ehcache:ehcache:3.10.8")
|
||||
api("org.ehcache:jcache:1.0.1")
|
||||
api("org.freemarker:freemarker:2.3.34")
|
||||
api("org.freemarker:freemarker:2.3.33")
|
||||
api("org.glassfish.external:opendmk_jmxremote_optional_jar:1.0-b01-ea")
|
||||
api("org.glassfish:jakarta.el:4.0.2")
|
||||
api("org.glassfish.tyrus:tyrus-container-servlet:2.1.3")
|
||||
@@ -140,7 +140,7 @@ dependencies {
|
||||
api("org.seleniumhq.selenium:htmlunit-driver:2.70.0")
|
||||
api("org.seleniumhq.selenium:selenium-java:3.141.59")
|
||||
api("org.skyscreamer:jsonassert:1.5.3")
|
||||
api("org.slf4j:slf4j-api:2.0.17")
|
||||
api("org.slf4j:slf4j-api:2.0.16")
|
||||
api("org.testng:testng:7.9.0")
|
||||
api("org.webjars:underscorejs:1.8.3")
|
||||
api("org.webjars:webjars-locator-core:0.55")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.1.22-SNAPSHOT
|
||||
version=6.1.19
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -58,10 +58,3 @@ dependencies {
|
||||
testRuntimeOnly("org.javamoney:moneta")
|
||||
testRuntimeOnly("org.junit.vintage:junit-vintage-engine") // for @Inject TCK
|
||||
}
|
||||
|
||||
test {
|
||||
description = "Runs JUnit Jupiter tests and the @Inject TCK via JUnit Vintage."
|
||||
useJUnitPlatform {
|
||||
includeEngines "junit-jupiter", "junit-vintage"
|
||||
}
|
||||
}
|
||||
|
||||
+2
-9
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.context.annotation;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
@@ -140,20 +139,14 @@ class ConfigurationClassEnhancer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given config class relies on package visibility, either for
|
||||
* the class and any of its constructors or for any of its {@code @Bean} methods.
|
||||
* Checks whether the given config class relies on package visibility,
|
||||
* either for the class itself or for any of its {@code @Bean} methods.
|
||||
*/
|
||||
private boolean reliesOnPackageVisibility(Class<?> configSuperClass) {
|
||||
int mod = configSuperClass.getModifiers();
|
||||
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) {
|
||||
return true;
|
||||
}
|
||||
for (Constructor<?> ctor : configSuperClass.getDeclaredConstructors()) {
|
||||
mod = ctor.getModifiers();
|
||||
if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (Method method : ReflectionUtils.getDeclaredMethods(configSuperClass)) {
|
||||
if (BeanAnnotationHelper.isBeanAnnotated(method)) {
|
||||
mod = method.getModifiers();
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
@@ -542,13 +543,15 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
* <p>Mark fields as disallowed, for example to avoid unwanted
|
||||
* modifications by malicious users when binding HTTP request parameters.
|
||||
* <p>Supports {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
|
||||
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts),
|
||||
* as well as direct equality.
|
||||
* <p>The default implementation of this method stores disallowed field
|
||||
* patterns in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String)
|
||||
* canonical} form, and subsequently pattern matching in {@link #isAllowed}
|
||||
* is case-insensitive. Subclasses that override this method must therefore
|
||||
* take this transformation into account.
|
||||
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
|
||||
* well as direct equality.
|
||||
* <p>The default implementation of this method stores disallowed field patterns
|
||||
* in {@linkplain PropertyAccessorUtils#canonicalPropertyName(String) canonical}
|
||||
* form. As of Spring Framework 5.2.21, the default implementation also transforms
|
||||
* disallowed field patterns to {@linkplain String#toLowerCase() lowercase} to
|
||||
* support case-insensitive pattern matching in {@link #isAllowed}. Subclasses
|
||||
* which override this method must therefore take both of these transformations
|
||||
* into account.
|
||||
* <p>More sophisticated matching can be implemented by overriding the
|
||||
* {@link #isAllowed} method.
|
||||
* <p>Alternatively, specify a list of <i>allowed</i> field patterns.
|
||||
@@ -566,7 +569,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
else {
|
||||
String[] fieldPatterns = new String[disallowedFields.length];
|
||||
for (int i = 0; i < fieldPatterns.length; i++) {
|
||||
fieldPatterns[i] = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]);
|
||||
String field = PropertyAccessorUtils.canonicalPropertyName(disallowedFields[i]);
|
||||
fieldPatterns[i] = field.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
this.disallowedFields = fieldPatterns;
|
||||
}
|
||||
@@ -1136,9 +1140,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
* Determine if the given field is allowed for binding.
|
||||
* <p>Invoked for each passed-in property value.
|
||||
* <p>Checks for {@code "xxx*"}, {@code "*xxx"}, {@code "*xxx*"}, and
|
||||
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts),
|
||||
* as well as direct equality, in the configured lists of allowed field
|
||||
* patterns and disallowed field patterns.
|
||||
* {@code "xxx*yyy"} matches (with an arbitrary number of pattern parts), as
|
||||
* well as direct equality, in the configured lists of allowed field patterns
|
||||
* and disallowed field patterns.
|
||||
* <p>Matching against allowed field patterns is case-sensitive; whereas,
|
||||
* matching against disallowed field patterns is case-insensitive.
|
||||
* <p>A field matching a disallowed pattern will not be accepted even if it
|
||||
@@ -1154,13 +1158,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
protected boolean isAllowed(String field) {
|
||||
String[] allowed = getAllowedFields();
|
||||
String[] disallowed = getDisallowedFields();
|
||||
if (!ObjectUtils.isEmpty(allowed) && !PatternMatchUtils.simpleMatch(allowed, field)) {
|
||||
return false;
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(disallowed)) {
|
||||
return !PatternMatchUtils.simpleMatchIgnoreCase(disallowed, field);
|
||||
}
|
||||
return true;
|
||||
return ((ObjectUtils.isEmpty(allowed) || PatternMatchUtils.simpleMatch(allowed, field)) &&
|
||||
(ObjectUtils.isEmpty(disallowed) || !PatternMatchUtils.simpleMatch(disallowed, field.toLowerCase(Locale.ROOT))));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
-38
@@ -104,31 +104,6 @@ class ConfigurationClassEnhancerTests {
|
||||
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withNonPublicConstructor() {
|
||||
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
|
||||
|
||||
ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader());
|
||||
Class<?> enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
|
||||
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
|
||||
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||
|
||||
classLoader = new OverridingClassLoader(getClass().getClassLoader());
|
||||
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
|
||||
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
|
||||
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||
|
||||
classLoader = new CustomSmartClassLoader(getClass().getClassLoader());
|
||||
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
|
||||
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
|
||||
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||
|
||||
classLoader = new BasicSmartClassLoader(getClass().getClassLoader());
|
||||
enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader);
|
||||
assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass);
|
||||
assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent());
|
||||
}
|
||||
|
||||
@Test
|
||||
void withNonPublicMethod() {
|
||||
ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer();
|
||||
@@ -185,19 +160,6 @@ class ConfigurationClassEnhancerTests {
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
public static class MyConfigWithNonPublicConstructor {
|
||||
|
||||
MyConfigWithNonPublicConstructor() {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public String myBean() {
|
||||
return "bean";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Configuration
|
||||
public static class MyConfigWithNonPublicMethod {
|
||||
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -38,8 +38,7 @@ import org.springframework.context.support.GenericApplicationContext;
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
*/
|
||||
// WARNING: This class MUST be public, since it is based on JUnit 3.
|
||||
public class SpringAtInjectTckTests {
|
||||
class SpringAtInjectTckTests {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Test suite() {
|
||||
|
||||
+1
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -29,7 +29,6 @@ import org.springframework.beans.testfixture.beans.TestBean;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
@@ -73,33 +72,6 @@ class PropertySourcesPlaceholderConfigurerTests {
|
||||
assertThat(ppc.getAppliedPropertySources()).isNotNull();
|
||||
}
|
||||
|
||||
@Test // gh-34936
|
||||
void replacementFromEnvironmentPropertiesWithConversion() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
bf.registerBeanDefinition("testBean",
|
||||
genericBeanDefinition(TestBean.class)
|
||||
.addPropertyValue("name", "${my.name}")
|
||||
.getBeanDefinition());
|
||||
|
||||
record Point(int x, int y) {
|
||||
}
|
||||
|
||||
Converter<Point, String> pointToStringConverter =
|
||||
point -> "(%d,%d)".formatted(point.x, point.y);
|
||||
|
||||
DefaultConversionService conversionService = new DefaultConversionService();
|
||||
conversionService.addConverter(Point.class, String.class, pointToStringConverter);
|
||||
|
||||
MockEnvironment env = new MockEnvironment();
|
||||
env.setConversionService(conversionService);
|
||||
env.setProperty("my.name", new Point(4,5));
|
||||
|
||||
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
|
||||
ppc.setEnvironment(env);
|
||||
ppc.postProcessBeanFactory(bf);
|
||||
assertThat(bf.getBean(TestBean.class).getName()).isEqualTo("(4,5)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void localPropertiesViaResource() {
|
||||
DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
|
||||
|
||||
+9
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -27,7 +27,7 @@ import org.springframework.core.testfixture.env.MockPropertySource;
|
||||
* @author Chris Beams
|
||||
* @author Sam Brannen
|
||||
* @since 3.2
|
||||
* @see MockPropertySource
|
||||
* @see org.springframework.core.testfixture.env.MockPropertySource
|
||||
*/
|
||||
public class MockEnvironment extends AbstractEnvironment {
|
||||
|
||||
@@ -44,21 +44,19 @@ public class MockEnvironment extends AbstractEnvironment {
|
||||
|
||||
/**
|
||||
* Set a property on the underlying {@link MockPropertySource} for this environment.
|
||||
* @see MockPropertySource#setProperty(String, Object)
|
||||
*/
|
||||
public void setProperty(String name, Object value) {
|
||||
this.propertySource.setProperty(name, value);
|
||||
public void setProperty(String key, String value) {
|
||||
this.propertySource.setProperty(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenient synonym for {@link #setProperty(String, Object)} that returns
|
||||
* the current instance.
|
||||
* <p>Useful for method chaining and fluent-style use.
|
||||
* Convenient synonym for {@link #setProperty} that returns the current instance.
|
||||
* Useful for method chaining and fluent-style use.
|
||||
* @return this {@link MockEnvironment} instance
|
||||
* @see MockPropertySource#withProperty(String, Object)
|
||||
* @see MockPropertySource#withProperty
|
||||
*/
|
||||
public MockEnvironment withProperty(String name, Object value) {
|
||||
setProperty(name, value);
|
||||
public MockEnvironment withProperty(String key, String value) {
|
||||
setProperty(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -463,21 +463,10 @@ public class ReflectUtils {
|
||||
c = lookup.defineClass(b);
|
||||
}
|
||||
catch (LinkageError | IllegalArgumentException ex) {
|
||||
if (ex instanceof LinkageError) {
|
||||
// Could be a ClassLoader mismatch with the class pre-existing in a
|
||||
// parent ClassLoader -> try loadClass before giving up completely.
|
||||
try {
|
||||
c = contextClass.getClassLoader().loadClass(className);
|
||||
}
|
||||
catch (ClassNotFoundException cnfe) {
|
||||
}
|
||||
}
|
||||
if (c == null) {
|
||||
// in case of plain LinkageError (class already defined)
|
||||
// or IllegalArgumentException (class in different package):
|
||||
// fall through to traditional ClassLoader.defineClass below
|
||||
t = ex;
|
||||
}
|
||||
// in case of plain LinkageError (class already defined)
|
||||
// or IllegalArgumentException (class in different package):
|
||||
// fall through to traditional ClassLoader.defineClass below
|
||||
t = ex;
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
throw new CodeGenerationException(ex);
|
||||
|
||||
+2
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -33,10 +33,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* <p>As of Spring 4.1.2, this class extends {@link EnumerablePropertySource} instead
|
||||
* of plain {@link PropertySource}, exposing {@link #getPropertyNames()} based on the
|
||||
* accumulated property names from all contained sources - and failing with an
|
||||
* {@code IllegalStateException} against any non-{@code EnumerablePropertySource}.
|
||||
* <b>When used through the {@code EnumerablePropertySource} contract, all contained
|
||||
* sources are expected to be of type {@code EnumerablePropertySource} as well.</b>
|
||||
* accumulated property names from all contained sources (as far as possible).
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @author Juergen Hoeller
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -37,25 +37,13 @@ public abstract class PatternMatchUtils {
|
||||
* @return whether the String matches the given pattern
|
||||
*/
|
||||
public static boolean simpleMatch(@Nullable String pattern, @Nullable String str) {
|
||||
return simpleMatch(pattern, str, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #simpleMatch(String, String)} that ignores upper/lower case.
|
||||
* @since 6.1.20
|
||||
*/
|
||||
public static boolean simpleMatchIgnoreCase(@Nullable String pattern, @Nullable String str) {
|
||||
return simpleMatch(pattern, str, true);
|
||||
}
|
||||
|
||||
private static boolean simpleMatch(@Nullable String pattern, @Nullable String str, boolean ignoreCase) {
|
||||
if (pattern == null || str == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int firstIndex = pattern.indexOf('*');
|
||||
if (firstIndex == -1) {
|
||||
return (ignoreCase ? pattern.equalsIgnoreCase(str) : pattern.equals(str));
|
||||
return pattern.equals(str);
|
||||
}
|
||||
|
||||
if (firstIndex == 0) {
|
||||
@@ -64,43 +52,25 @@ public abstract class PatternMatchUtils {
|
||||
}
|
||||
int nextIndex = pattern.indexOf('*', 1);
|
||||
if (nextIndex == -1) {
|
||||
String part = pattern.substring(1);
|
||||
return (ignoreCase ? StringUtils.endsWithIgnoreCase(str, part) : str.endsWith(part));
|
||||
return str.endsWith(pattern.substring(1));
|
||||
}
|
||||
String part = pattern.substring(1, nextIndex);
|
||||
if (part.isEmpty()) {
|
||||
return simpleMatch(pattern.substring(nextIndex), str, ignoreCase);
|
||||
return simpleMatch(pattern.substring(nextIndex), str);
|
||||
}
|
||||
int partIndex = indexOf(str, part, 0, ignoreCase);
|
||||
int partIndex = str.indexOf(part);
|
||||
while (partIndex != -1) {
|
||||
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()), ignoreCase)) {
|
||||
if (simpleMatch(pattern.substring(nextIndex), str.substring(partIndex + part.length()))) {
|
||||
return true;
|
||||
}
|
||||
partIndex = indexOf(str, part, partIndex + 1, ignoreCase);
|
||||
partIndex = str.indexOf(part, partIndex + 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return (str.length() >= firstIndex &&
|
||||
checkStartsWith(pattern, str, firstIndex, ignoreCase) &&
|
||||
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex), ignoreCase));
|
||||
}
|
||||
|
||||
private static boolean checkStartsWith(String pattern, String str, int index, boolean ignoreCase) {
|
||||
String part = str.substring(0, index);
|
||||
return (ignoreCase ? StringUtils.startsWithIgnoreCase(pattern, part) : pattern.startsWith(part));
|
||||
}
|
||||
|
||||
private static int indexOf(String str, String otherStr, int startIndex, boolean ignoreCase) {
|
||||
if (!ignoreCase) {
|
||||
return str.indexOf(otherStr, startIndex);
|
||||
}
|
||||
for (int i = startIndex; i <= (str.length() - otherStr.length()); i++) {
|
||||
if (str.regionMatches(true, i, otherStr, 0, otherStr.length())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
pattern.startsWith(str.substring(0, firstIndex)) &&
|
||||
simpleMatch(pattern.substring(firstIndex), str.substring(firstIndex)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,19 +94,4 @@ public abstract class PatternMatchUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #simpleMatch(String[], String)} that ignores upper/lower case.
|
||||
* @since 6.1.20
|
||||
*/
|
||||
public static boolean simpleMatchIgnoreCase(@Nullable String[] patterns, @Nullable String str) {
|
||||
if (patterns != null) {
|
||||
for (String pattern : patterns) {
|
||||
if (simpleMatch(pattern, str, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -85,7 +85,6 @@ public class ExponentialBackOff implements BackOff {
|
||||
*/
|
||||
public static final int DEFAULT_MAX_ATTEMPTS = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
private long initialInterval = DEFAULT_INITIAL_INTERVAL;
|
||||
|
||||
private double multiplier = DEFAULT_MULTIPLIER;
|
||||
@@ -205,7 +204,6 @@ public class ExponentialBackOff implements BackOff {
|
||||
return this.maxAttempts;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BackOffExecution start() {
|
||||
return new ExponentialBackOffExecution();
|
||||
@@ -227,7 +225,6 @@ public class ExponentialBackOff implements BackOff {
|
||||
.toString();
|
||||
}
|
||||
|
||||
|
||||
private class ExponentialBackOffExecution implements BackOffExecution {
|
||||
|
||||
private long currentInterval = -1;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -35,7 +35,6 @@ public class FixedBackOff implements BackOff {
|
||||
*/
|
||||
public static final long UNLIMITED_ATTEMPTS = Long.MAX_VALUE;
|
||||
|
||||
|
||||
private long interval = DEFAULT_INTERVAL;
|
||||
|
||||
private long maxAttempts = UNLIMITED_ATTEMPTS;
|
||||
@@ -87,7 +86,6 @@ public class FixedBackOff implements BackOff {
|
||||
return this.maxAttempts;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BackOffExecution start() {
|
||||
return new FixedBackOffExecution();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -53,22 +53,18 @@ class PatternMatchUtilsTests {
|
||||
assertMatches(new String[] { null, "" }, "");
|
||||
assertMatches(new String[] { null, "123" }, "123");
|
||||
assertMatches(new String[] { null, "*" }, "123");
|
||||
|
||||
testMixedCaseMatch("abC", "Abc");
|
||||
}
|
||||
|
||||
@Test
|
||||
void startsWith() {
|
||||
assertMatches("get*", "getMe");
|
||||
assertDoesNotMatch("get*", "setMe");
|
||||
testMixedCaseMatch("geT*", "GetMe");
|
||||
}
|
||||
|
||||
@Test
|
||||
void endsWith() {
|
||||
assertMatches("*Test", "getMeTest");
|
||||
assertDoesNotMatch("*Test", "setMe");
|
||||
testMixedCaseMatch("*TeSt", "getMeTesT");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -78,10 +74,6 @@ class PatternMatchUtilsTests {
|
||||
assertMatches("*stuff*", "stuffTest");
|
||||
assertMatches("*stuff*", "getstuff");
|
||||
assertMatches("*stuff*", "stuff");
|
||||
testMixedCaseMatch("*stuff*", "getStuffTest");
|
||||
testMixedCaseMatch("*stuff*", "StuffTest");
|
||||
testMixedCaseMatch("*stuff*", "getStuff");
|
||||
testMixedCaseMatch("*stuff*", "Stuff");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -90,8 +82,6 @@ class PatternMatchUtilsTests {
|
||||
assertMatches("on*Event", "onEvent");
|
||||
assertDoesNotMatch("3*3", "3");
|
||||
assertMatches("3*3", "33");
|
||||
testMixedCaseMatch("on*Event", "OnMyEvenT");
|
||||
testMixedCaseMatch("on*Event", "OnEvenT");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -132,27 +122,18 @@ class PatternMatchUtilsTests {
|
||||
|
||||
private void assertMatches(String pattern, String str) {
|
||||
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isTrue();
|
||||
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isTrue();
|
||||
}
|
||||
|
||||
private void assertDoesNotMatch(String pattern, String str) {
|
||||
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isFalse();
|
||||
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isFalse();
|
||||
}
|
||||
|
||||
private void testMixedCaseMatch(String pattern, String str) {
|
||||
assertThat(PatternMatchUtils.simpleMatch(pattern, str)).isFalse();
|
||||
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(pattern, str)).isTrue();
|
||||
}
|
||||
|
||||
private void assertMatches(String[] patterns, String str) {
|
||||
assertThat(PatternMatchUtils.simpleMatch(patterns, str)).isTrue();
|
||||
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(patterns, str)).isTrue();
|
||||
}
|
||||
|
||||
private void assertDoesNotMatch(String[] patterns, String str) {
|
||||
assertThat(PatternMatchUtils.simpleMatch(patterns, str)).isFalse();
|
||||
assertThat(PatternMatchUtils.simpleMatchIgnoreCase(patterns, str)).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vendored
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -26,7 +26,7 @@ import org.springframework.core.env.PropertySource;
|
||||
* a user-provided {@link Properties} object, or if omitted during construction,
|
||||
* the implementation will initialize its own.
|
||||
*
|
||||
* <p>The {@link #setProperty} and {@link #withProperty} methods are exposed for
|
||||
* The {@link #setProperty} and {@link #withProperty} methods are exposed for
|
||||
* convenience, for example:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
@@ -95,7 +95,7 @@ public class MockPropertySource extends PropertiesPropertySource {
|
||||
|
||||
/**
|
||||
* Convenient synonym for {@link #setProperty} that returns the current instance.
|
||||
* <p>Useful for method chaining and fluent-style use.
|
||||
* Useful for method chaining and fluent-style use.
|
||||
* @return this {@link MockPropertySource} instance
|
||||
*/
|
||||
public MockPropertySource withProperty(String name, Object value) {
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -26,7 +26,7 @@ import org.springframework.core.env.PropertySource;
|
||||
* a user-provided {@link Properties} object, or if omitted during construction,
|
||||
* the implementation will initialize its own.
|
||||
*
|
||||
* <p>The {@link #setProperty} and {@link #withProperty} methods are exposed for
|
||||
* The {@link #setProperty} and {@link #withProperty} methods are exposed for
|
||||
* convenience, for example:
|
||||
* <pre class="code">
|
||||
* {@code
|
||||
@@ -36,7 +36,7 @@ import org.springframework.core.env.PropertySource;
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
* @see MockEnvironment
|
||||
* @see org.springframework.mock.env.MockEnvironment
|
||||
*/
|
||||
public class MockPropertySource extends PropertiesPropertySource {
|
||||
|
||||
@@ -95,7 +95,7 @@ public class MockPropertySource extends PropertiesPropertySource {
|
||||
|
||||
/**
|
||||
* Convenient synonym for {@link #setProperty} that returns the current instance.
|
||||
* <p>Useful for method chaining and fluent-style use.
|
||||
* Useful for method chaining and fluent-style use.
|
||||
* @return this {@link MockPropertySource} instance
|
||||
*/
|
||||
public MockPropertySource withProperty(String name, Object value) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -67,7 +67,6 @@ public final class ContentDisposition {
|
||||
for (int i=33; i<= 126; i++) {
|
||||
PRINTABLE.set(i);
|
||||
}
|
||||
PRINTABLE.set(34, false); // "
|
||||
PRINTABLE.set(61, false); // =
|
||||
PRINTABLE.set(63, false); // ?
|
||||
PRINTABLE.set(95, false); // _
|
||||
|
||||
+3
-14
@@ -216,8 +216,9 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest
|
||||
context = HttpClientContext.create();
|
||||
}
|
||||
|
||||
// No custom request configuration was set
|
||||
if (!hasCustomRequestConfig(context)) {
|
||||
// Request configuration not set in the context
|
||||
if (!(context instanceof HttpClientContext clientContext && clientContext.getRequestConfig() != null) &&
|
||||
context.getAttribute(HttpClientContext.REQUEST_CONFIG) == null) {
|
||||
RequestConfig config = null;
|
||||
// Use request configuration given by the user, when available
|
||||
if (httpRequest instanceof Configurable configurable) {
|
||||
@@ -236,18 +237,6 @@ public class HttpComponentsClientHttpRequestFactory implements ClientHttpRequest
|
||||
return new HttpComponentsClientHttpRequest(client, httpRequest, context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // HttpClientContext.REQUEST_CONFIG
|
||||
private static boolean hasCustomRequestConfig(HttpContext context) {
|
||||
if (context instanceof HttpClientContext clientContext) {
|
||||
// Prior to 5.4, the default config was set to RequestConfig.DEFAULT
|
||||
// As of 5.4, it is set to null
|
||||
RequestConfig requestConfig = clientContext.getRequestConfig();
|
||||
return requestConfig != null && !requestConfig.equals(RequestConfig.DEFAULT);
|
||||
}
|
||||
// Prior to 5.4, the config was stored as an attribute
|
||||
return context.getAttribute(HttpClientContext.REQUEST_CONFIG) != null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a default {@link RequestConfig} to use with the given client.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2023 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.
|
||||
@@ -107,12 +107,12 @@ public interface ServerWebExchange {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the web session for the current request.
|
||||
* <p>Always guaranteed to return either an instance matching the session id
|
||||
* requested by the client, or a new session either because the client did not
|
||||
* specify a session id or because the underlying session expired.
|
||||
* <p>Use of this method does not automatically create a session. See
|
||||
* {@link WebSession} for more details.
|
||||
* Return the web session for the current request. Always guaranteed to
|
||||
* return an instance either matching to the session id requested by the
|
||||
* client, or with a new session id either because the client did not
|
||||
* specify one or because the underlying session had expired. Use of this
|
||||
* method does not automatically create a session. See {@link WebSession}
|
||||
* for more details.
|
||||
*/
|
||||
Mono<WebSession> getSession();
|
||||
|
||||
|
||||
+14
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -79,10 +79,10 @@ public class InMemoryWebSessionStore implements WebSessionStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the {@link Clock} to use to set the {@code lastAccessTime} on
|
||||
* every created session and to calculate if the session has expired.
|
||||
* <p>This may be useful to align to different time zones or to set the clock
|
||||
* back in a test, for example, {@code Clock.offset(clock, Duration.ofMinutes(-31))}
|
||||
* Configure the {@link Clock} to use to set lastAccessTime on every created
|
||||
* session and to calculate if it is expired.
|
||||
* <p>This may be useful to align to different timezone or to set the clock
|
||||
* back in a test, e.g. {@code Clock.offset(clock, Duration.ofMinutes(-31))}
|
||||
* in order to simulate session expiration.
|
||||
* <p>By default this is {@code Clock.system(ZoneId.of("GMT"))}.
|
||||
* @param clock the clock to use
|
||||
@@ -94,17 +94,16 @@ public class InMemoryWebSessionStore implements WebSessionStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured clock for session {@code lastAccessTime} calculations.
|
||||
* Return the configured clock for session lastAccessTime calculations.
|
||||
*/
|
||||
public Clock getClock() {
|
||||
return this.clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an {@linkplain Collections#unmodifiableMap unmodifiable} copy of the
|
||||
* map of sessions.
|
||||
* <p>This could be used for management purposes, to list active sessions,
|
||||
* to invalidate expired sessions, etc.
|
||||
* Return the map of sessions with an {@link Collections#unmodifiableMap
|
||||
* unmodifiable} wrapper. This could be used for management purposes, to
|
||||
* list active sessions, invalidate expired ones, etc.
|
||||
* @since 5.0.8
|
||||
*/
|
||||
public Map<String, WebSession> getSessions() {
|
||||
@@ -158,11 +157,10 @@ public class InMemoryWebSessionStore implements WebSessionStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for expired sessions and remove them.
|
||||
* <p>Typically such checks are kicked off lazily during calls to
|
||||
* {@link #createWebSession()} or {@link #retrieveSession}, no less than 60
|
||||
* seconds apart.
|
||||
* <p>This method can be called to force a check at a specific time.
|
||||
* Check for expired sessions and remove them. Typically such checks are
|
||||
* kicked off lazily during calls to {@link #createWebSession() create} or
|
||||
* {@link #retrieveSession retrieve}, no less than 60 seconds apart.
|
||||
* This method can be called to force a check at a specific time.
|
||||
* @since 5.0.8
|
||||
*/
|
||||
public void removeExpiredSessions() {
|
||||
@@ -280,7 +278,7 @@ public class InMemoryWebSessionStore implements WebSessionStore {
|
||||
private void checkMaxSessionsLimit() {
|
||||
if (sessions.size() >= maxSessions) {
|
||||
expiredSessionChecker.removeExpiredSessions(clock.instant());
|
||||
if (sessions.size() >= maxSessions && !sessions.containsKey(this.id.get())) {
|
||||
if (sessions.size() >= maxSessions) {
|
||||
throw new IllegalStateException("Max sessions limit reached: " + sessions.size());
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -32,10 +32,10 @@ import org.springframework.web.server.WebSession;
|
||||
public interface WebSessionManager {
|
||||
|
||||
/**
|
||||
* Return the {@link WebSession} for the given exchange.
|
||||
* <p>Always guaranteed to return either an instance matching the session id
|
||||
* requested by the client, or a new session either because the client did not
|
||||
* specify a session id or because the underlying session expired.
|
||||
* Return the {@link WebSession} for the given exchange. Always guaranteed
|
||||
* to return an instance either matching to the session id requested by the
|
||||
* client, or a new session either because the client did not specify one
|
||||
* or because the underlying session expired.
|
||||
* @param exchange the current exchange
|
||||
* @return promise for the WebSession
|
||||
*/
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -43,7 +43,7 @@ public interface WebSessionStore {
|
||||
* Return the WebSession for the given id.
|
||||
* <p><strong>Note:</strong> This method should perform an expiration check,
|
||||
* and if it has expired remove the session and return empty. This method
|
||||
* should also update the {@code lastAccessTime} of retrieved sessions.
|
||||
* should also update the lastAccessTime of retrieved sessions.
|
||||
* @param sessionId the session to load
|
||||
* @return the session, or an empty {@code Mono}
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -305,13 +305,6 @@ class ContentDispositionTests {
|
||||
tester.accept("foo.txt\\\\\\", "foo.txt\\\\\\\\\\\\");
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatWithUtf8FilenameWithQuotes() {
|
||||
String filename = "\"中文.txt";
|
||||
assertThat(ContentDisposition.formData().filename(filename, StandardCharsets.UTF_8).build().toString())
|
||||
.isEqualTo("form-data; filename=\"=?UTF-8?Q?=22=E4=B8=AD=E6=96=87.txt?=\"; filename*=UTF-8''%22%E4%B8%AD%E6%96%87.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void formatWithEncodedFilenameUsingInvalidCharset() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() ->
|
||||
|
||||
+22
-64
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -19,11 +19,11 @@ package org.springframework.web.server.session;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.scheduler.Schedulers;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.web.server.WebSession;
|
||||
@@ -35,11 +35,10 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
* Tests for {@link InMemoryWebSessionStore}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Sam Brannen
|
||||
*/
|
||||
class InMemoryWebSessionStoreTests {
|
||||
|
||||
private final InMemoryWebSessionStore store = new InMemoryWebSessionStore();
|
||||
private InMemoryWebSessionStore store = new InMemoryWebSessionStore();
|
||||
|
||||
|
||||
@Test
|
||||
@@ -54,14 +53,13 @@ class InMemoryWebSessionStoreTests {
|
||||
void startsSessionImplicitly() {
|
||||
WebSession session = this.store.createWebSession().block();
|
||||
assertThat(session).isNotNull();
|
||||
// We intentionally do not invoke start().
|
||||
// session.start();
|
||||
session.start();
|
||||
session.getAttributes().put("foo", "bar");
|
||||
assertThat(session.isStarted()).isTrue();
|
||||
}
|
||||
|
||||
@Test // gh-24027, gh-26958
|
||||
void createSessionDoesNotBlock() {
|
||||
public void createSessionDoesNotBlock() {
|
||||
this.store.createWebSession()
|
||||
.doOnNext(session -> assertThat(Schedulers.isInNonBlockingThread()).isTrue())
|
||||
.block();
|
||||
@@ -105,7 +103,7 @@ class InMemoryWebSessionStoreTests {
|
||||
}
|
||||
|
||||
@Test // SPR-17051
|
||||
void sessionInvalidatedBeforeSave() {
|
||||
public void sessionInvalidatedBeforeSave() {
|
||||
// Request 1 creates session
|
||||
WebSession session1 = this.store.createWebSession().block();
|
||||
assertThat(session1).isNotNull();
|
||||
@@ -134,69 +132,33 @@ class InMemoryWebSessionStoreTests {
|
||||
|
||||
@Test
|
||||
void expirationCheckPeriod() {
|
||||
// Create 100 sessions
|
||||
IntStream.rangeClosed(1, 100).forEach(i -> insertSession());
|
||||
assertNumSessions(100);
|
||||
|
||||
// Force a new clock (31 min later). Don't use setter which would clean expired sessions.
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(this.store);
|
||||
accessor.setPropertyValue("clock", Clock.offset(this.store.getClock(), Duration.ofMinutes(31)));
|
||||
assertNumSessions(100);
|
||||
Map<?,?> sessions = (Map<?, ?>) accessor.getPropertyValue("sessions");
|
||||
assertThat(sessions).isNotNull();
|
||||
|
||||
// Create 1 more which forces a time-based check (clock moved forward).
|
||||
// Create 100 sessions
|
||||
IntStream.range(0, 100).forEach(i -> insertSession());
|
||||
assertThat(sessions).hasSize(100);
|
||||
|
||||
// Force a new clock (31 min later), don't use setter which would clean expired sessions
|
||||
accessor.setPropertyValue("clock", Clock.offset(this.store.getClock(), Duration.ofMinutes(31)));
|
||||
assertThat(sessions).hasSize(100);
|
||||
|
||||
// Create 1 more which forces a time-based check (clock moved forward)
|
||||
insertSession();
|
||||
assertNumSessions(1);
|
||||
assertThat(sessions).hasSize(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void maxSessions() {
|
||||
this.store.setMaxSessions(10);
|
||||
|
||||
IntStream.rangeClosed(1, 10).forEach(i -> insertSession());
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(this::insertSession)
|
||||
.withMessage("Max sessions limit reached: 10");
|
||||
IntStream.range(0, 10000).forEach(i -> insertSession());
|
||||
assertThatIllegalStateException().isThrownBy(
|
||||
this::insertSession)
|
||||
.withMessage("Max sessions limit reached: 10000");
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateSession() {
|
||||
WebSession session = insertSession();
|
||||
|
||||
StepVerifier.create(session.save())
|
||||
.expectComplete()
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test // gh-35013
|
||||
void updateSessionAfterMaxSessionLimitIsExceeded() {
|
||||
this.store.setMaxSessions(10);
|
||||
|
||||
WebSession session = insertSession();
|
||||
assertNumSessions(1);
|
||||
|
||||
IntStream.rangeClosed(1, 9).forEach(i -> insertSession());
|
||||
assertNumSessions(10);
|
||||
|
||||
// Updating an existing session should succeed.
|
||||
StepVerifier.create(session.save())
|
||||
.expectComplete()
|
||||
.verify();
|
||||
assertNumSessions(10);
|
||||
|
||||
// Saving an additional new session should fail.
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(this::insertSession)
|
||||
.withMessage("Max sessions limit reached: 10");
|
||||
assertNumSessions(10);
|
||||
|
||||
// Updating an existing session again should still succeed.
|
||||
StepVerifier.create(session.save())
|
||||
.expectComplete()
|
||||
.verify();
|
||||
assertNumSessions(10);
|
||||
}
|
||||
|
||||
|
||||
private WebSession insertSession() {
|
||||
WebSession session = this.store.createWebSession().block();
|
||||
assertThat(session).isNotNull();
|
||||
@@ -205,8 +167,4 @@ class InMemoryWebSessionStoreTests {
|
||||
return session;
|
||||
}
|
||||
|
||||
private void assertNumSessions(int numSessions) {
|
||||
assertThat(store.getSessions()).hasSize(numSessions);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user