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.servlet.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 javax.servlet.http.HttpServletRequest;
027
028import org.springframework.core.io.AbstractResource;
029import org.springframework.core.io.Resource;
030import org.springframework.http.HttpHeaders;
031import org.springframework.lang.Nullable;
032
033/**
034 * A {@code ResourceResolver} that delegates to the chain to locate a resource
035 * and then attempts to find a variation with the ".gz" extension.
036 *
037 * <p>The resolver gets involved only if the "Accept-Encoding" request header
038 * contains the value "gzip" indicating the client accepts gzipped responses.
039 *
040 * @author Jeremy Grelle
041 * @author Rossen Stoyanchev
042 * @author Sam Brannen
043 * @since 4.1
044 * @deprecated as of 5.1, in favor of using {@link EncodedResourceResolver}
045 */
046@Deprecated
047public class GzipResourceResolver extends AbstractResourceResolver {
048
049        @Override
050        protected Resource resolveResourceInternal(@Nullable HttpServletRequest request, String requestPath,
051                        List<? extends Resource> locations, ResourceResolverChain chain) {
052
053                Resource resource = chain.resolveResource(request, requestPath, locations);
054                if (resource == null || (request != null && !isGzipAccepted(request))) {
055                        return resource;
056                }
057
058                try {
059                        Resource gzipped = new GzippedResource(resource);
060                        if (gzipped.exists()) {
061                                return gzipped;
062                        }
063                }
064                catch (IOException ex) {
065                        logger.trace("No gzip resource for [" + resource.getFilename() + "]", ex);
066                }
067
068                return resource;
069        }
070
071        private boolean isGzipAccepted(HttpServletRequest request) {
072                String value = request.getHeader("Accept-Encoding");
073                return (value != null && value.toLowerCase().contains("gzip"));
074        }
075
076        @Override
077        protected String resolveUrlPathInternal(String resourceUrlPath,
078                        List<? extends Resource> locations, ResourceResolverChain chain) {
079
080                return chain.resolveUrlPath(resourceUrlPath, locations);
081        }
082
083
084        /**
085         * A gzipped {@link HttpResource}.
086         */
087        static final class GzippedResource extends AbstractResource implements HttpResource {
088
089                private final Resource original;
090
091                private final Resource gzipped;
092
093                public GzippedResource(Resource original) throws IOException {
094                        this.original = original;
095                        this.gzipped = original.createRelative(original.getFilename() + ".gz");
096                }
097
098                @Override
099                public InputStream getInputStream() throws IOException {
100                        return this.gzipped.getInputStream();
101                }
102
103                @Override
104                public boolean exists() {
105                        return this.gzipped.exists();
106                }
107
108                @Override
109                public boolean isReadable() {
110                        return this.gzipped.isReadable();
111                }
112
113                @Override
114                public boolean isOpen() {
115                        return this.gzipped.isOpen();
116                }
117
118                @Override
119                public boolean isFile() {
120                        return this.gzipped.isFile();
121                }
122
123                @Override
124                public URL getURL() throws IOException {
125                        return this.gzipped.getURL();
126                }
127
128                @Override
129                public URI getURI() throws IOException {
130                        return this.gzipped.getURI();
131                }
132
133                @Override
134                public File getFile() throws IOException {
135                        return this.gzipped.getFile();
136                }
137
138                @Override
139                public long contentLength() throws IOException {
140                        return this.gzipped.contentLength();
141                }
142
143                @Override
144                public long lastModified() throws IOException {
145                        return this.gzipped.lastModified();
146                }
147
148                @Override
149                public Resource createRelative(String relativePath) throws IOException {
150                        return this.gzipped.createRelative(relativePath);
151                }
152
153                @Override
154                @Nullable
155                public String getFilename() {
156                        return this.original.getFilename();
157                }
158
159                @Override
160                public String getDescription() {
161                        return this.gzipped.getDescription();
162                }
163
164                @Override
165                public HttpHeaders getResponseHeaders() {
166                        HttpHeaders headers = (this.original instanceof HttpResource ?
167                                        ((HttpResource) this.original).getResponseHeaders() : new HttpHeaders());
168                        headers.add(HttpHeaders.CONTENT_ENCODING, "gzip");
169                        headers.add(HttpHeaders.VARY, HttpHeaders.ACCEPT_ENCODING);
170                        return headers;
171                }
172        }
173
174}