Early removal of 5.x-deprecated code
Closes gh-27686
This commit is contained in:
@@ -43,11 +43,8 @@ public abstract class ExchangeFilterFunctions {
|
||||
|
||||
/**
|
||||
* Name of the request attribute with {@link Credentials} for {@link #basicAuthentication()}.
|
||||
* @deprecated as of Spring 5.1 in favor of using
|
||||
* {@link HttpHeaders#setBasicAuth(String, String)} while building the request.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE =
|
||||
private static final String BASIC_AUTHENTICATION_CREDENTIALS_ATTRIBUTE =
|
||||
ExchangeFilterFunctions.class.getName() + ".basicAuthenticationCredentials";
|
||||
|
||||
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2020 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.reactive.resource;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Scanner;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import reactor.core.Exceptions;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.core.publisher.SynchronousSink;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* A {@link ResourceTransformer} HTML5 AppCache manifests.
|
||||
*
|
||||
* <p>This transformer:
|
||||
* <ul>
|
||||
* <li>modifies links to match the public URL paths that should be exposed to
|
||||
* clients, using configured {@code ResourceResolver} strategies
|
||||
* <li>appends a comment in the manifest, containing a Hash
|
||||
* (e.g. "# Hash: 9de0f09ed7caf84e885f1f0f11c7e326"), thus changing the content
|
||||
* of the manifest in order to trigger an appcache reload in the browser.
|
||||
* </ul>
|
||||
*
|
||||
* <p>All files with an ".appcache" file extension (or the extension given
|
||||
* to the constructor) will be transformed by this class. The hash is computed
|
||||
* using the content of the appcache manifest so that changes in the manifest
|
||||
* should invalidate the browser cache. This should also work with changes in
|
||||
* referenced resources whose links are also versioned.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
* @since 5.0
|
||||
* @see <a href="https://html.spec.whatwg.org/multipage/browsers.html#offline">HTML5 offline applications spec</a>
|
||||
* @deprecated as of 5.3 since browser support is going away
|
||||
*/
|
||||
@Deprecated
|
||||
public class AppCacheManifestTransformer extends ResourceTransformerSupport {
|
||||
|
||||
private static final String MANIFEST_HEADER = "CACHE MANIFEST";
|
||||
|
||||
private static final String CACHE_HEADER = "CACHE:";
|
||||
|
||||
private static final Collection<String> MANIFEST_SECTION_HEADERS =
|
||||
Arrays.asList(MANIFEST_HEADER, "NETWORK:", "FALLBACK:", CACHE_HEADER);
|
||||
|
||||
private static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(AppCacheManifestTransformer.class);
|
||||
|
||||
|
||||
private final String fileExtension;
|
||||
|
||||
|
||||
/**
|
||||
* Create an AppCacheResourceTransformer that transforms files with extension ".appcache".
|
||||
*/
|
||||
public AppCacheManifestTransformer() {
|
||||
this("appcache");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AppCacheResourceTransformer that transforms files with the extension
|
||||
* given as a parameter.
|
||||
*/
|
||||
public AppCacheManifestTransformer(String fileExtension) {
|
||||
this.fileExtension = fileExtension;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Mono<Resource> transform(ServerWebExchange exchange, Resource inputResource,
|
||||
ResourceTransformerChain chain) {
|
||||
|
||||
return chain.transform(exchange, inputResource)
|
||||
.flatMap(outputResource -> {
|
||||
String name = outputResource.getFilename();
|
||||
if (!this.fileExtension.equals(StringUtils.getFilenameExtension(name))) {
|
||||
return Mono.just(outputResource);
|
||||
}
|
||||
DataBufferFactory bufferFactory = exchange.getResponse().bufferFactory();
|
||||
Flux<DataBuffer> flux = DataBufferUtils
|
||||
.read(outputResource, bufferFactory, StreamUtils.BUFFER_SIZE);
|
||||
return DataBufferUtils.join(flux)
|
||||
.flatMap(dataBuffer -> {
|
||||
CharBuffer charBuffer = DEFAULT_CHARSET.decode(dataBuffer.asByteBuffer());
|
||||
DataBufferUtils.release(dataBuffer);
|
||||
String content = charBuffer.toString();
|
||||
return transform(content, outputResource, chain, exchange);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private Mono<? extends Resource> transform(String content, Resource resource,
|
||||
ResourceTransformerChain chain, ServerWebExchange exchange) {
|
||||
|
||||
if (!content.startsWith(MANIFEST_HEADER)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(exchange.getLogPrefix() +
|
||||
"Skipping " + resource + ": Manifest does not start with 'CACHE MANIFEST'");
|
||||
}
|
||||
return Mono.just(resource);
|
||||
}
|
||||
return Flux.generate(new LineInfoGenerator(content))
|
||||
.concatMap(info -> processLine(info, exchange, resource, chain))
|
||||
.reduce(new ByteArrayOutputStream(), (out, line) -> {
|
||||
writeToByteArrayOutputStream(out, line + "\n");
|
||||
return out;
|
||||
})
|
||||
.map(out -> {
|
||||
String hash = DigestUtils.md5DigestAsHex(out.toByteArray());
|
||||
writeToByteArrayOutputStream(out, "\n" + "# Hash: " + hash);
|
||||
return new TransformedResource(resource, out.toByteArray());
|
||||
});
|
||||
}
|
||||
|
||||
private static void writeToByteArrayOutputStream(ByteArrayOutputStream out, String toWrite) {
|
||||
try {
|
||||
byte[] bytes = toWrite.getBytes(DEFAULT_CHARSET);
|
||||
out.write(bytes);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw Exceptions.propagate(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Mono<String> processLine(LineInfo info, ServerWebExchange exchange,
|
||||
Resource resource, ResourceTransformerChain chain) {
|
||||
|
||||
if (!info.isLink()) {
|
||||
return Mono.just(info.getLine());
|
||||
}
|
||||
|
||||
String link = toAbsolutePath(info.getLine(), exchange);
|
||||
return resolveUrlPath(link, exchange, resource, chain);
|
||||
}
|
||||
|
||||
|
||||
private static class LineInfoGenerator implements Consumer<SynchronousSink<LineInfo>> {
|
||||
|
||||
private final Scanner scanner;
|
||||
|
||||
@Nullable
|
||||
private LineInfo previous;
|
||||
|
||||
|
||||
LineInfoGenerator(String content) {
|
||||
this.scanner = new Scanner(content);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void accept(SynchronousSink<LineInfo> sink) {
|
||||
if (this.scanner.hasNext()) {
|
||||
String line = this.scanner.nextLine();
|
||||
LineInfo current = new LineInfo(line, this.previous);
|
||||
sink.next(current);
|
||||
this.previous = current;
|
||||
}
|
||||
else {
|
||||
sink.complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LineInfo {
|
||||
|
||||
private final String line;
|
||||
|
||||
private final boolean cacheSection;
|
||||
|
||||
private final boolean link;
|
||||
|
||||
|
||||
LineInfo(String line, @Nullable LineInfo previousLine) {
|
||||
this.line = line;
|
||||
this.cacheSection = initCacheSectionFlag(line, previousLine);
|
||||
this.link = iniLinkFlag(line, this.cacheSection);
|
||||
}
|
||||
|
||||
|
||||
private static boolean initCacheSectionFlag(String line, @Nullable LineInfo previousLine) {
|
||||
String trimmedLine = line.trim();
|
||||
if (MANIFEST_SECTION_HEADERS.contains(trimmedLine)) {
|
||||
return trimmedLine.equals(CACHE_HEADER);
|
||||
}
|
||||
else if (previousLine != null) {
|
||||
return previousLine.isCacheSection();
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Manifest does not start with " + MANIFEST_HEADER + ": " + line);
|
||||
}
|
||||
|
||||
private static boolean iniLinkFlag(String line, boolean isCacheSection) {
|
||||
return (isCacheSection && StringUtils.hasText(line) && !line.startsWith("#")
|
||||
&& !line.startsWith("//") && !hasScheme(line));
|
||||
}
|
||||
|
||||
private static boolean hasScheme(String line) {
|
||||
int index = line.indexOf(':');
|
||||
return (line.startsWith("//") || (index > 0 && !line.substring(0, index).contains("/")));
|
||||
}
|
||||
|
||||
|
||||
public String getLine() {
|
||||
return this.line;
|
||||
}
|
||||
|
||||
public boolean isCacheSection() {
|
||||
return this.cacheSection;
|
||||
}
|
||||
|
||||
public boolean isLink() {
|
||||
return this.link;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,8 +79,7 @@ public class CssLinkResourceTransformer extends ResourceTransformerSupport {
|
||||
.flatMap(outputResource -> {
|
||||
String filename = outputResource.getFilename();
|
||||
if (!"css".equals(StringUtils.getFilenameExtension(filename)) ||
|
||||
inputResource instanceof EncodedResourceResolver.EncodedResource ||
|
||||
inputResource instanceof GzipResourceResolver.GzippedResource) {
|
||||
inputResource instanceof EncodedResourceResolver.EncodedResource) {
|
||||
return Mono.just(outputResource);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.reactive.resource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
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.lang.Nullable;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* A {@code ResourceResolver} that delegates to the chain to locate a resource
|
||||
* and then attempts to find a variation with the ".gz" extension.
|
||||
*
|
||||
* <p>The resolver gets involved only if the "Accept-Encoding" request header
|
||||
* contains the value "gzip" indicating the client accepts gzipped responses.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
* @deprecated as of 5.1, in favor of using {@link EncodedResourceResolver}
|
||||
*/
|
||||
@Deprecated
|
||||
public class GzipResourceResolver extends AbstractResourceResolver {
|
||||
|
||||
@Override
|
||||
protected Mono<Resource> resolveResourceInternal(@Nullable ServerWebExchange exchange,
|
||||
String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
|
||||
return chain.resolveResource(exchange, requestPath, locations)
|
||||
.map(resource -> {
|
||||
if (exchange == null || isGzipAccepted(exchange)) {
|
||||
try {
|
||||
Resource gzipped = new GzippedResource(resource);
|
||||
if (gzipped.exists()) {
|
||||
resource = gzipped;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
String logPrefix = exchange != null ? exchange.getLogPrefix() : "";
|
||||
logger.trace(logPrefix + "No gzip resource for [" + resource.getFilename() + "]", ex);
|
||||
}
|
||||
}
|
||||
return resource;
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isGzipAccepted(ServerWebExchange exchange) {
|
||||
String value = exchange.getRequest().getHeaders().getFirst("Accept-Encoding");
|
||||
return (value != null && value.toLowerCase().contains("gzip"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Mono<String> resolveUrlPathInternal(String resourceUrlPath,
|
||||
List<? extends Resource> locations, ResourceResolverChain chain) {
|
||||
|
||||
return chain.resolveUrlPath(resourceUrlPath, locations);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A gzipped {@link HttpResource}.
|
||||
*/
|
||||
static final class GzippedResource extends AbstractResource implements HttpResource {
|
||||
|
||||
private final Resource original;
|
||||
|
||||
private final Resource gzipped;
|
||||
|
||||
public GzippedResource(Resource original) throws IOException {
|
||||
this.original = original;
|
||||
this.gzipped = original.createRelative(original.getFilename() + ".gz");
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return this.gzipped.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return this.gzipped.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadable() {
|
||||
return this.gzipped.isReadable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen() {
|
||||
return this.gzipped.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return this.gzipped.isFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() throws IOException {
|
||||
return this.gzipped.getURL();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getURI() throws IOException {
|
||||
return this.gzipped.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFile() throws IOException {
|
||||
return this.gzipped.getFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() throws IOException {
|
||||
return this.gzipped.contentLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long lastModified() throws IOException {
|
||||
return this.gzipped.lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Resource createRelative(String relativePath) throws IOException {
|
||||
return this.gzipped.createRelative(relativePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFilename() {
|
||||
return this.original.getFilename();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.gzipped.getDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders getResponseHeaders() {
|
||||
HttpHeaders headers = (this.original instanceof HttpResource ?
|
||||
((HttpResource) this.original).getResponseHeaders() : new HttpHeaders());
|
||||
headers.add(HttpHeaders.CONTENT_ENCODING, "gzip");
|
||||
headers.add(HttpHeaders.VARY, HttpHeaders.ACCEPT_ENCODING);
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2019 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -230,9 +230,7 @@ public abstract class AbstractView implements View, BeanNameAware, ApplicationCo
|
||||
attributes = new ConcurrentHashMap<>(0);
|
||||
}
|
||||
|
||||
//noinspection deprecation
|
||||
return resolveAsyncAttributes(attributes)
|
||||
.then(resolveAsyncAttributes(attributes, exchange))
|
||||
return resolveAsyncAttributes(attributes, exchange)
|
||||
.doOnTerminate(() -> exchange.getAttributes().remove(BINDING_CONTEXT_ATTRIBUTE))
|
||||
.thenReturn(attributes);
|
||||
}
|
||||
@@ -293,22 +291,6 @@ public abstract class AbstractView implements View, BeanNameAware, ApplicationCo
|
||||
model.put(BindingResult.MODEL_KEY_PREFIX + name, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the configured {@link ReactiveAdapterRegistry} to adapt asynchronous
|
||||
* attributes to {@code Mono<T>} or {@code Mono<List<T>>} and then wait to
|
||||
* resolve them into actual values. When the returned {@code Mono<Void>}
|
||||
* completes, the asynchronous attributes in the model would have been
|
||||
* replaced with their corresponding resolved values.
|
||||
* @return result {@code Mono} that completes when the model is ready
|
||||
* @deprecated as of 5.1.8 this method is still invoked but it is a no-op.
|
||||
* Please use {@link #resolveAsyncAttributes(Map, ServerWebExchange)}
|
||||
* instead. It is invoked after this one and does the actual work.
|
||||
*/
|
||||
@Deprecated
|
||||
protected Mono<Void> resolveAsyncAttributes(Map<String, Object> model) {
|
||||
return Mono.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@link RequestContext} to expose under the
|
||||
* {@linkplain #setRequestContextAttribute specified attribute name}.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -41,23 +41,6 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
*/
|
||||
public interface RequestUpgradeStrategy {
|
||||
|
||||
/**
|
||||
* Upgrade to a WebSocket session and handle it with the given handler.
|
||||
* @param exchange the current exchange
|
||||
* @param webSocketHandler handler for the WebSocket session
|
||||
* @param subProtocol the selected sub-protocol got the handler
|
||||
* @return completion {@code Mono<Void>} to indicate the outcome of the
|
||||
* WebSocket session handling.
|
||||
* @deprecated as of 5.1 in favor of
|
||||
* {@link #upgrade(ServerWebExchange, WebSocketHandler, String, Supplier)}
|
||||
*/
|
||||
@Deprecated
|
||||
default Mono<Void> upgrade(ServerWebExchange exchange, WebSocketHandler webSocketHandler,
|
||||
@Nullable String subProtocol) {
|
||||
|
||||
return Mono.error(new UnsupportedOperationException());
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade to a WebSocket session and handle it with the given handler.
|
||||
* @param exchange the current exchange
|
||||
@@ -68,10 +51,7 @@ public interface RequestUpgradeStrategy {
|
||||
* WebSocket session handling.
|
||||
* @since 5.1
|
||||
*/
|
||||
default Mono<Void> upgrade(ServerWebExchange exchange, WebSocketHandler webSocketHandler,
|
||||
@Nullable String subProtocol, Supplier<HandshakeInfo> handshakeInfoFactory) {
|
||||
|
||||
return upgrade(exchange, webSocketHandler, subProtocol);
|
||||
}
|
||||
Mono<Void> upgrade(ServerWebExchange exchange, WebSocketHandler webSocketHandler,
|
||||
@Nullable String subProtocol, Supplier<HandshakeInfo> handshakeInfoFactory);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -165,14 +165,12 @@ class ResourceHandlerRegistryTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation")
|
||||
void resourceChainWithVersionResolver() {
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver()
|
||||
.addFixedVersionStrategy("fixed", "/**/*.js")
|
||||
.addContentVersionStrategy("/**");
|
||||
|
||||
this.registration.resourceChain(true).addResolver(versionResolver)
|
||||
.addTransformer(new org.springframework.web.reactive.resource.AppCacheManifestTransformer());
|
||||
this.registration.resourceChain(true).addResolver(versionResolver);
|
||||
|
||||
ResourceWebHandler handler = getHandler("/resources/**");
|
||||
List<ResourceResolver> resolvers = handler.getResourceResolvers();
|
||||
@@ -183,10 +181,9 @@ class ResourceHandlerRegistryTests {
|
||||
assertThat(resolvers.get(3)).isInstanceOf(PathResourceResolver.class);
|
||||
|
||||
List<ResourceTransformer> transformers = handler.getResourceTransformers();
|
||||
assertThat(transformers).hasSize(3);
|
||||
assertThat(transformers).hasSize(2);
|
||||
assertThat(transformers.get(0)).isInstanceOf(CachingResourceTransformer.class);
|
||||
assertThat(transformers.get(1)).isInstanceOf(CssLinkResourceTransformer.class);
|
||||
assertThat(transformers.get(2)).isInstanceOf(org.springframework.web.reactive.resource.AppCacheManifestTransformer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -197,8 +194,6 @@ class ResourceHandlerRegistryTests {
|
||||
WebJarsResourceResolver webjarsResolver = Mockito.mock(WebJarsResourceResolver.class);
|
||||
PathResourceResolver pathResourceResolver = new PathResourceResolver();
|
||||
CachingResourceTransformer cachingTransformer = Mockito.mock(CachingResourceTransformer.class);
|
||||
org.springframework.web.reactive.resource.AppCacheManifestTransformer appCacheTransformer =
|
||||
Mockito.mock(org.springframework.web.reactive.resource.AppCacheManifestTransformer.class);
|
||||
CssLinkResourceTransformer cssLinkTransformer = new CssLinkResourceTransformer();
|
||||
|
||||
this.registration.setCacheControl(CacheControl.maxAge(3600, TimeUnit.MILLISECONDS))
|
||||
@@ -208,7 +203,6 @@ class ResourceHandlerRegistryTests {
|
||||
.addResolver(webjarsResolver)
|
||||
.addResolver(pathResourceResolver)
|
||||
.addTransformer(cachingTransformer)
|
||||
.addTransformer(appCacheTransformer)
|
||||
.addTransformer(cssLinkTransformer);
|
||||
|
||||
ResourceWebHandler handler = getHandler("/resources/**");
|
||||
@@ -220,10 +214,9 @@ class ResourceHandlerRegistryTests {
|
||||
assertThat(resolvers.get(3)).isSameAs(pathResourceResolver);
|
||||
|
||||
List<ResourceTransformer> transformers = handler.getResourceTransformers();
|
||||
assertThat(transformers).hasSize(3);
|
||||
assertThat(transformers).hasSize(2);
|
||||
assertThat(transformers.get(0)).isSameAs(cachingTransformer);
|
||||
assertThat(transformers.get(1)).isSameAs(appCacheTransformer);
|
||||
assertThat(transformers.get(2)).isSameAs(cssLinkTransformer);
|
||||
assertThat(transformers.get(1)).isSameAs(cssLinkTransformer);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2021 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.reactive.resource;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
import org.springframework.web.testfixture.server.MockServerWebExchange;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest.get;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link AppCacheManifestTransformer}.
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class AppCacheManifestTransformerTests {
|
||||
|
||||
private static final Duration TIMEOUT = Duration.ofSeconds(5);
|
||||
|
||||
|
||||
private final AppCacheManifestTransformer transformer = new AppCacheManifestTransformer();
|
||||
|
||||
private ResourceTransformerChain chain;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
VersionResourceResolver versionResolver = new VersionResourceResolver();
|
||||
versionResolver.setStrategyMap(Collections.singletonMap("/**", new ContentVersionStrategy()));
|
||||
List<ResourceResolver> resolvers = new ArrayList<>();
|
||||
resolvers.add(versionResolver);
|
||||
resolvers.add(new PathResourceResolver());
|
||||
ResourceResolverChain resolverChain = new DefaultResourceResolverChain(resolvers);
|
||||
|
||||
this.chain = new DefaultResourceTransformerChain(resolverChain, Collections.emptyList());
|
||||
this.transformer.setResourceUrlProvider(createUrlProvider(resolvers));
|
||||
}
|
||||
|
||||
private ResourceUrlProvider createUrlProvider(List<ResourceResolver> resolvers) {
|
||||
ResourceWebHandler handler = new ResourceWebHandler();
|
||||
handler.setLocations(Collections.singletonList(new ClassPathResource("test/", getClass())));
|
||||
handler.setResourceResolvers(resolvers);
|
||||
|
||||
ResourceUrlProvider urlProvider = new ResourceUrlProvider();
|
||||
urlProvider.registerHandlers(Collections.singletonMap("/static/**", handler));
|
||||
return urlProvider;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void noTransformIfExtensionDoesNotMatch() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(get("/static/foo.css"));
|
||||
Resource expected = getResource("foo.css");
|
||||
Resource actual = this.transformer.transform(exchange, expected, this.chain).block(TIMEOUT);
|
||||
|
||||
assertThat(actual).isSameAs(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void syntaxErrorInManifest() {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(get("/static/error.appcache"));
|
||||
Resource expected = getResource("error.appcache");
|
||||
Resource actual = this.transformer.transform(exchange, expected, this.chain).block(TIMEOUT);
|
||||
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transformManifest() throws Exception {
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(get("/static/test.appcache"));
|
||||
Resource resource = getResource("test.appcache");
|
||||
Resource actual = this.transformer.transform(exchange, resource, this.chain).block(TIMEOUT);
|
||||
|
||||
assertThat(actual).isNotNull();
|
||||
byte[] bytes = FileCopyUtils.copyToByteArray(actual.getInputStream());
|
||||
String content = new String(bytes, "UTF-8");
|
||||
|
||||
assertThat(content).as("rewrite resource links")
|
||||
.contains("/static/foo-e36d2e05253c6c7085a91522ce43a0b4.css")
|
||||
.contains("/static/bar-11e16cf79faee7ac698c805cf28248d2.css")
|
||||
.contains("/static/js/bar-bd508c62235b832d960298ca6c0b7645.js");
|
||||
|
||||
assertThat(content).as("not rewrite external resources")
|
||||
.contains("//example.org/style.css")
|
||||
.contains("https://example.org/image.png");
|
||||
|
||||
// Not the same hash as Spring MVC
|
||||
// Hash is computed from links, and not from the linked content
|
||||
|
||||
assertThat(content).as("generate fingerprint")
|
||||
.contains("# Hash: d4437f1d7ae9530ab3ae71d5375b46ff");
|
||||
}
|
||||
|
||||
private Resource getResource(String filePath) {
|
||||
return new ClassPathResource("test/" + filePath, getClass());
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user