package com.netflix.eureka; import javax.inject.Singleton; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.HttpHeaders; import java.io.IOException; import java.util.Enumeration; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicReference; /** * Originally Eureka supported non-compressed responses only. For large registries it was extremely * inefficient, so gzip encoding was added. As nowadays all modern HTTP clients support gzip HTTP response * transparently, there is no longer need to maintain uncompressed content. By adding this filter, Eureka * server will accept only GET requests that explicitly support gzip encoding replies. In the coming minor release * non-compressed replies will be dropped altogether, so this filter will become required. * * @author Tomasz Bak */ @Singleton public class GzipEncodingEnforcingFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; if ("GET".equals(httpRequest.getMethod())) { String acceptEncoding = httpRequest.getHeader(HttpHeaders.ACCEPT_ENCODING); if (acceptEncoding == null) { chain.doFilter(addGzipAcceptEncoding(httpRequest), response); return; } if (!acceptEncoding.contains("gzip")) { ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); return; } } chain.doFilter(request, response); } @Override public void destroy() { } private static HttpServletRequest addGzipAcceptEncoding(HttpServletRequest request) { return new HttpServletRequestWrapper(request) { @Override public Enumeration<String> getHeaders(String name) { if (HttpHeaders.ACCEPT_ENCODING.equals(name)) { return new EnumWrapper<String>("gzip"); } return new EnumWrapper<String>(super.getHeaders(name), HttpHeaders.ACCEPT_ENCODING); } @Override public Enumeration<String> getHeaderNames() { return new EnumWrapper<String>(super.getHeaderNames(), HttpHeaders.ACCEPT_ENCODING); } @Override public String getHeader(String name) { if (HttpHeaders.ACCEPT_ENCODING.equals(name)) { return "gzip"; } return super.getHeader(name); } }; } private static class EnumWrapper<E> implements Enumeration<E> { private final Enumeration<E> delegate; private final AtomicReference<E> extraElementRef; private EnumWrapper(E extraElement) { this(null, extraElement); } private EnumWrapper(Enumeration<E> delegate, E extraElement) { this.delegate = delegate; this.extraElementRef = new AtomicReference<>(extraElement); } @Override public boolean hasMoreElements() { return extraElementRef.get() != null || delegate != null && delegate.hasMoreElements(); } @Override public E nextElement() { E extra = extraElementRef.getAndSet(null); if (extra != null) { return extra; } if (delegate == null) { throw new NoSuchElementException(); } return delegate.nextElement(); } } }