001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.reactive.resource;
018
019import java.io.File;
020import java.io.IOException;
021import java.io.InputStream;
022import java.net.URI;
023import java.net.URL;
024import java.util.List;
025
026import reactor.core.publisher.Mono;
027
028import org.springframework.core.io.AbstractResource;
029import org.springframework.core.io.Resource;
030import org.springframework.http.HttpHeaders;
031import org.springframework.lang.Nullable;
032import org.springframework.web.server.ServerWebExchange;
033
034/**
035 * A {@code ResourceResolver} that delegates to the chain to locate a resource
036 * and then attempts to find a variation with the ".gz" extension.
037 *
038 * <p>The resolver gets involved only if the "Accept-Encoding" request header
039 * contains the value "gzip" indicating the client accepts gzipped responses.
040 *
041 * @author Rossen Stoyanchev
042 * @since 5.0
043 * @deprecated as of 5.1, in favor of using {@link EncodedResourceResolver}
044 */
045@Deprecated
046public class GzipResourceResolver extends AbstractResourceResolver {
047
048        @Override
049        protected Mono<Resource> resolveResourceInternal(@Nullable ServerWebExchange exchange,
050                        String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
051
052                return chain.resolveResource(exchange, requestPath, locations)
053                                .map(resource -> {
054                                        if (exchange == null || isGzipAccepted(exchange)) {
055                                                try {
056                                                        Resource gzipped = new GzippedResource(resource);
057                                                        if (gzipped.exists()) {
058                                                                resource = gzipped;
059                                                        }
060                                                }
061                                                catch (IOException ex) {
062                                                        String logPrefix = exchange != null ? exchange.getLogPrefix() : "";
063                                                        logger.trace(logPrefix + "No gzip resource for [" + resource.getFilename() + "]", ex);
064                                                }
065                                        }
066                                        return resource;
067                                });
068        }
069
070        private boolean isGzipAccepted(ServerWebExchange exchange) {
071                String value = exchange.getRequest().getHeaders().getFirst("Accept-Encoding");
072                return (value != null && value.toLowerCase().contains("gzip"));
073        }
074
075        @Override
076        protected Mono<String> resolveUrlPathInternal(String resourceUrlPath,
077                        List<? extends Resource> locations, ResourceResolverChain chain) {
078
079                return chain.resolveUrlPath(resourceUrlPath, locations);
080        }
081
082
083        /**
084         * A gzipped {@link HttpResource}.
085         */
086        static final class GzippedResource extends AbstractResource implements HttpResource {
087
088                private final Resource original;
089
090                private final Resource gzipped;
091
092                public GzippedResource(Resource original) throws IOException {
093                        this.original = original;
094                        this.gzipped = original.createRelative(original.getFilename() + ".gz");
095                }
096
097                @Override
098                public InputStream getInputStream() throws IOException {
099                        return this.gzipped.getInputStream();
100                }
101
102                @Override
103                public boolean exists() {
104                        return this.gzipped.exists();
105                }
106
107                @Override
108                public boolean isReadable() {
109                        return this.gzipped.isReadable();
110                }
111
112                @Override
113                public boolean isOpen() {
114                        return this.gzipped.isOpen();
115                }
116
117                @Override
118                public boolean isFile() {
119                        return this.gzipped.isFile();
120                }
121
122                @Override
123                public URL getURL() throws IOException {
124                        return this.gzipped.getURL();
125                }
126
127                @Override
128                public URI getURI() throws IOException {
129                        return this.gzipped.getURI();
130                }
131
132                @Override
133                public File getFile() throws IOException {
134                        return this.gzipped.getFile();
135                }
136
137                @Override
138                public long contentLength() throws IOException {
139                        return this.gzipped.contentLength();
140                }
141
142                @Override
143                public long lastModified() throws IOException {
144                        return this.gzipped.lastModified();
145                }
146
147                @Override
148                public Resource createRelative(String relativePath) throws IOException {
149                        return this.gzipped.createRelative(relativePath);
150                }
151
152                @Override
153                @Nullable
154                public String getFilename() {
155                        return this.original.getFilename();
156                }
157
158                @Override
159                public String getDescription() {
160                        return this.gzipped.getDescription();
161                }
162
163                @Override
164                public HttpHeaders getResponseHeaders() {
165                        HttpHeaders headers = (this.original instanceof HttpResource ?
166                                        ((HttpResource) this.original).getResponseHeaders() : new HttpHeaders());
167                        headers.add(HttpHeaders.CONTENT_ENCODING, "gzip");
168                        headers.add(HttpHeaders.VARY, HttpHeaders.ACCEPT_ENCODING);
169                        return headers;
170                }
171        }
172
173}