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}