mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Share codings resolution in resource resolvers
Prior to this commit, different resource resolvers would resolve accepted codings from the HTTP request sent by the client. This would be done with different implementations, which could lead to resolution errors and desynchronizations. This commit now introduced a new shared method in `EncodedResourceResolver` (Servlet and Reactive) to perform a consisten resolution. Fixes gh-36507
This commit is contained in:
+4
-9
@@ -17,10 +17,8 @@
|
||||
package org.springframework.web.reactive.resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
@@ -133,15 +131,12 @@ public class CachingResourceResolver extends AbstractResourceResolver {
|
||||
}
|
||||
|
||||
private @Nullable String getContentCodingKey(ServerWebExchange exchange) {
|
||||
String header = exchange.getRequest().getHeaders().getFirst("Accept-Encoding");
|
||||
if (!StringUtils.hasText(header)) {
|
||||
List<String> acceptedCodings = EncodedResourceResolver.parseAcceptEncoding(exchange);
|
||||
if (acceptedCodings.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.stream(StringUtils.tokenizeToStringArray(header, ","))
|
||||
.map(token -> {
|
||||
int index = token.indexOf(';');
|
||||
return (index >= 0 ? token.substring(0, index) : token).trim().toLowerCase(Locale.ROOT);
|
||||
})
|
||||
return acceptedCodings
|
||||
.stream()
|
||||
.filter(this.contentCodings::contains)
|
||||
.sorted()
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
+25
-14
@@ -38,8 +38,8 @@ import reactor.core.publisher.Mono;
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
@@ -142,23 +142,23 @@ public class EncodedResourceResolver extends AbstractResourceResolver {
|
||||
return resource;
|
||||
}
|
||||
|
||||
String acceptEncoding = getAcceptEncoding(exchange);
|
||||
if (acceptEncoding == null) {
|
||||
List<String> acceptedCodings = parseAcceptEncoding(exchange);
|
||||
if (acceptedCodings.isEmpty()) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
for (String coding : this.contentCodings) {
|
||||
if (acceptEncoding.contains(coding)) {
|
||||
for (String acceptedCoding : acceptedCodings) {
|
||||
if (this.contentCodings.contains(acceptedCoding)) {
|
||||
try {
|
||||
String extension = getExtension(coding);
|
||||
Resource encoded = new EncodedResource(resource, coding, extension);
|
||||
String extension = getExtension(acceptedCoding);
|
||||
Resource encoded = new EncodedResource(resource, acceptedCoding, extension);
|
||||
if (encoded.exists()) {
|
||||
return encoded;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
logger.trace(exchange.getLogPrefix() +
|
||||
"No " + coding + " resource for [" + resource.getFilename() + "]", ex);
|
||||
"No " + acceptedCoding + " resource for [" + resource.getFilename() + "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,12 +167,6 @@ public class EncodedResourceResolver extends AbstractResourceResolver {
|
||||
});
|
||||
}
|
||||
|
||||
private @Nullable String getAcceptEncoding(ServerWebExchange exchange) {
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
String header = request.getHeaders().getFirst(HttpHeaders.ACCEPT_ENCODING);
|
||||
return (header != null ? header.toLowerCase(Locale.ROOT) : null);
|
||||
}
|
||||
|
||||
private String getExtension(String coding) {
|
||||
String extension = this.extensions.get(coding);
|
||||
if (extension == null) {
|
||||
@@ -188,6 +182,23 @@ public class EncodedResourceResolver extends AbstractResourceResolver {
|
||||
return chain.resolveUrlPath(resourceUrlPath, locations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the accepted encodings from the given HTTP exchange request.
|
||||
*/
|
||||
static List<String> parseAcceptEncoding(ServerWebExchange exchange) {
|
||||
String header = exchange.getRequest().getHeaders().getFirst("Accept-Encoding");
|
||||
if (!StringUtils.hasText(header)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
header = header.toLowerCase(Locale.ROOT);
|
||||
return Arrays.stream(StringUtils.tokenizeToStringArray(header, ","))
|
||||
.map(token -> {
|
||||
int index = token.indexOf(';');
|
||||
return (index >= 0 ? token.substring(0, index) : token).trim();
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An encoded {@link HttpResource}.
|
||||
|
||||
+4
-10
@@ -17,10 +17,8 @@
|
||||
package org.springframework.web.servlet.resource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -29,7 +27,6 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.CacheManager;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -139,15 +136,12 @@ public class CachingResourceResolver extends AbstractResourceResolver {
|
||||
}
|
||||
|
||||
private @Nullable String getContentCodingKey(HttpServletRequest request) {
|
||||
String header = request.getHeader(HttpHeaders.ACCEPT_ENCODING);
|
||||
if (!StringUtils.hasText(header)) {
|
||||
List<String> acceptedCodings = EncodedResourceResolver.parseAcceptEncoding(request);
|
||||
if (acceptedCodings.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return Arrays.stream(StringUtils.tokenizeToStringArray(header, ","))
|
||||
.map(token -> {
|
||||
int index = token.indexOf(';');
|
||||
return (index >= 0 ? token.substring(0, index) : token).trim().toLowerCase(Locale.ROOT);
|
||||
})
|
||||
return acceptedCodings
|
||||
.stream()
|
||||
.filter(this.contentCodings::contains)
|
||||
.sorted()
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
+25
-12
@@ -39,6 +39,7 @@ import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Resolver that delegates to the chain, and if a resource is found, it then
|
||||
@@ -139,23 +140,23 @@ public class EncodedResourceResolver extends AbstractResourceResolver {
|
||||
return resource;
|
||||
}
|
||||
|
||||
String acceptEncoding = getAcceptEncoding(request);
|
||||
if (acceptEncoding == null) {
|
||||
List<String> acceptedCodings = parseAcceptEncoding(request);
|
||||
if (acceptedCodings.isEmpty()) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
for (String coding : this.contentCodings) {
|
||||
if (acceptEncoding.contains(coding)) {
|
||||
for (String acceptedCoding : acceptedCodings) {
|
||||
if (this.contentCodings.contains(acceptedCoding)) {
|
||||
try {
|
||||
String extension = getExtension(coding);
|
||||
Resource encoded = new EncodedResource(resource, coding, extension);
|
||||
String extension = getExtension(acceptedCoding);
|
||||
Resource encoded = new EncodedResource(resource, acceptedCoding, extension);
|
||||
if (encoded.exists()) {
|
||||
return encoded;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No " + coding + " resource for [" + resource.getFilename() + "]", ex);
|
||||
logger.trace("No " + acceptedCoding + " resource for [" + resource.getFilename() + "]", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,11 +165,6 @@ public class EncodedResourceResolver extends AbstractResourceResolver {
|
||||
return resource;
|
||||
}
|
||||
|
||||
private @Nullable String getAcceptEncoding(HttpServletRequest request) {
|
||||
String header = request.getHeader(HttpHeaders.ACCEPT_ENCODING);
|
||||
return (header != null ? header.toLowerCase(Locale.ROOT) : null);
|
||||
}
|
||||
|
||||
private String getExtension(String coding) {
|
||||
String extension = this.extensions.get(coding);
|
||||
if (extension == null) {
|
||||
@@ -184,6 +180,23 @@ public class EncodedResourceResolver extends AbstractResourceResolver {
|
||||
return chain.resolveUrlPath(resourceUrlPath, locations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the accepted encodings from the given HTTP request.
|
||||
*/
|
||||
static List<String> parseAcceptEncoding(HttpServletRequest request) {
|
||||
String header = request.getHeader("Accept-Encoding");
|
||||
if (!StringUtils.hasText(header)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
header = header.toLowerCase(Locale.ROOT);
|
||||
return Arrays.stream(StringUtils.tokenizeToStringArray(header, ","))
|
||||
.map(token -> {
|
||||
int index = token.indexOf(';');
|
||||
return (index >= 0 ? token.substring(0, index) : token).trim();
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An encoded {@link HttpResource}.
|
||||
|
||||
Reference in New Issue
Block a user