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