Search code examples
spring-webfluxistio

is there any built in filter in istio or in spring webflux handling header "Content-Encoding: deflate" to decompress the request body


i have a microservice running on spring cloud function webflux and need to handle compressed data sent as http request body

is there any spring web filter or config built in to handle the decompression of data

$ echo '{ "key":"hello" }' > body
$ curl -X POST -H "Content-Type: application/json" --data-binary @body http://localhost:8080 # prints 'hello'
$ echo '{ "key":"hello" }' | deflate > body.dat
$ curl -X POST -H "Content-Type: application/json" -H "Content-Encoding: deflate" --data-binary @body.dat http://localhost:8080 # fails

can this be handled in istio envoy filter ?


Solution

  • import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.core.io.buffer.DataBufferFactory;
    import org.springframework.core.io.buffer.DataBufferUtils;
    import org.springframework.core.io.buffer.DefaultDataBufferFactory;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
    import org.springframework.web.server.ServerWebExchange;
    import org.springframework.web.server.ServerWebExchangeDecorator;
    import org.springframework.web.server.WebFilter;
    import org.springframework.web.server.WebFilterChain;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import java.io.InputStream;
    import java.util.List;
    import java.util.zip.InflaterInputStream;
    
    public class GzipFilter implements WebFilter {
    
        private static final String ENCODING_VALUE = "deflate";
    
        /**
         * Process the Web request and (optionally) delegate to the next
         * {@code WebFilter} through the given {@link WebFilterChain}.
         *
         * @param exchange the current server exchange
         * @param chain    provides a way to delegate to the next filter
         * @return {@code Mono<Void>} to indicate when request processing is complete
         */
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
            List<String> strings = exchange.getRequest().getHeaders().get(HttpHeaders.CONTENT_ENCODING);
            if (strings != null && strings.contains(ENCODING_VALUE)) {
                return chain.filter(new GzippedInputStreamWrapper(exchange));
            } else {
                return chain.filter(exchange);
            }
        }
    }
    
    public final class GzippedInputStreamWrapper extends ServerWebExchangeDecorator {
    
    
        private final ServerHttpRequestDecorator requestDecorator;
    
    
        protected GzippedInputStreamWrapper(ServerWebExchange delegate) {
            super(delegate);
            this.requestDecorator = new GzipRequestDecorator(delegate.getRequest());
        }
    
        @Override
        public ServerHttpRequest getRequest() {
            return this.requestDecorator;
        }
    }
    
    public class GzipRequestDecorator extends ServerHttpRequestDecorator {
    
        private final DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
    
        public GzipRequestDecorator(ServerHttpRequest delegate) {
            super(delegate);
        }
    
        @Override
        public Flux<DataBuffer> getBody() {
            return super.getBody().flatMap(dataBuffer -> {
                try {
                    final InputStream gzipInputStream = new InflaterInputStream(dataBuffer.asInputStream());
                    return DataBufferUtils.readInputStream(() -> gzipInputStream, this.dataBufferFactory, gzipInputStream.available());
                } catch (Exception e) {
                    return Flux.error(e);
                }
            });
        }
    }
    

    define the webfilter GzipFilter which intercepts every request and process for the specified header. GzipRequestDecorator does the body transformation of inflating the content and sending it to the down stream handler functions