Improve support for caching encoded resources

The key in CachingResourceResolver now includes the "Accept-Encoding"
request header cleaned to exclude "*", "identity", and parameters, and
also sorted alphabetically.

For encoded resources the response now includes a response header with
"Vary: Accept-Encoding".

Issue: SPR-16381
This commit is contained in:
Rossen Stoyanchev
2018-05-23 09:10:07 -04:00
parent 9274de390a
commit b472d192f4
10 changed files with 91 additions and 28 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -16,14 +16,18 @@
package org.springframework.web.servlet.resource;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* A {@link org.springframework.web.servlet.resource.ResourceResolver} that
@@ -95,14 +99,31 @@ public class CachingResourceResolver extends AbstractResourceResolver {
StringBuilder key = new StringBuilder(RESOLVED_RESOURCE_CACHE_KEY_PREFIX);
key.append(requestPath);
if (request != null) {
String encoding = request.getHeader("Accept-Encoding");
if (encoding != null && encoding.contains("gzip")) {
key.append("+encoding=gzip");
String codingKey = getContentCodingKey(request);
if (codingKey != null) {
key.append("+encoding=").append(codingKey);
}
}
return key.toString();
}
@Nullable
private static String getContentCodingKey(HttpServletRequest request) {
String header = request.getHeader(HttpHeaders.ACCEPT_ENCODING);
if (!StringUtils.hasText(header)) {
return null;
}
return Arrays.stream(StringUtils.tokenizeToStringArray(header, ","))
.map(token -> {
int index = token.indexOf(';');
return (index >= 0 ? token.substring(0, index) : token).trim().toLowerCase();
})
.filter(coding -> !coding.equals("*"))
.filter(coding -> !coding.equals("identity"))
.sorted()
.collect(Collectors.joining(","));
}
@Override
protected String resolveUrlPathInternal(String resourceUrlPath,
List<? extends Resource> locations, ResourceResolverChain chain) {

View File

@@ -263,6 +263,7 @@ public class EncodedResourceResolver extends AbstractResourceResolver {
headers = new HttpHeaders();
}
headers.add(HttpHeaders.CONTENT_ENCODING, this.coding);
headers.add(HttpHeaders.VARY, HttpHeaders.ACCEPT_ENCODING);
return headers;
}
}

View File

@@ -167,6 +167,7 @@ public class GzipResourceResolver extends AbstractResourceResolver {
headers = new HttpHeaders();
}
headers.add(HttpHeaders.CONTENT_ENCODING, "gzip");
headers.add(HttpHeaders.VARY, HttpHeaders.ACCEPT_ENCODING);
return headers;
}
}