While migrating my spring server from servlets to reactive I had to change all the filters in the code to WebFilter. One of the filters was decompressing gzipped content, but I couldn't do the same with the new WebFilter.
With servlets I wrapped the inputstream with a GzipInputStream. What is the best practice to do it with spring reactive?
Solution:
@Component
public class GzipFilter implements WebFilter {
private static final Logger LOG = LoggerFactory.getLogger(GzipFilter.class);
public static final String CONTENT_ENCODING = "content-encoding";
public static final String GZIP = "gzip";
public static final String UTF_8 = "UTF-8";
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
if (!isGzip(request)) {
return chain.filter(exchange);
}
else {
ServerHttpRequest mutatedRequest = new ServerHttpRequestWrapper(request);
ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
return chain.filter(mutatedExchange);
}
}
private boolean isGzip(ServerHttpRequest serverHttpRequest) {
String encoding = serverHttpRequest.getHeaders().getFirst(CONTENT_ENCODING);
return encoding != null && encoding.contains(GZIP);
}
private static class ServerHttpRequestWrapper implements ServerHttpRequest {
private ServerHttpRequest request;
public ServerHttpRequestWrapper(ServerHttpRequest request) {
this.request = request;
}
private static byte[] getDeflatedBytes(GZIPInputStream gzipInputStream) throws IOException {
StringWriter writer = new StringWriter();
IOUtils.copy(gzipInputStream, writer, UTF_8);
return writer.toString().getBytes();
}
@Override
public String getId() {
return request.getId();
}
@Override
public RequestPath getPath() {
return request.getPath();
}
@Override
public MultiValueMap<String, String> getQueryParams() {
return request.getQueryParams();
}
@Override
public MultiValueMap<String, HttpCookie> getCookies() {
return request.getCookies();
}
@Override
public String getMethodValue() {
return request.getMethodValue();
}
@Override
public URI getURI() {
return request.getURI();
}
@Override
public Flux<DataBuffer> getBody() {
Mono<DataBuffer> mono = request.getBody()
.map(dataBuffer -> dataBuffer.asInputStream(true))
.reduce(SequenceInputStream::new)
.map(inputStream -> {
try (GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream)) {
byte[] targetArray = getDeflatedBytes(gzipInputStream);
return new DefaultDataBufferFactory().wrap(targetArray);
}
catch (IOException e) {
throw new IllegalGzipRequest(String.format("failed to decompress gzip content. Path: %s", request.getPath()));
}
});
return mono.flux();
}
@Override
public HttpHeaders getHeaders() {
return request.getHeaders();
}
}
}