View Javadoc

1   package com.atlassian.gzipfilter;
2   
3   import java.io.File;
4   import java.io.IOException;
5   
6   import javax.servlet.FilterChain;
7   import javax.servlet.FilterConfig;
8   import javax.servlet.ServletContext;
9   import javax.servlet.ServletException;
10  import javax.servlet.ServletRequest;
11  import javax.servlet.ServletResponse;
12  import javax.servlet.http.HttpServletRequest;
13  import javax.servlet.http.HttpServletResponse;
14  
15  import com.atlassian.gzipfilter.integration.GzipFilterIntegration;
16  import com.atlassian.gzipfilter.selector.GzipCompatibilitySelector;
17  import com.atlassian.gzipfilter.selector.GzipCompatibilitySelectorFactory;
18  import com.atlassian.gzipfilter.selector.NoGzipCompatibilitySelector;
19  import com.atlassian.gzipfilter.selector.UserAgentBasedGzipSelectorFactory;
20  
21  import org.slf4j.Logger;
22  import org.slf4j.LoggerFactory;
23  
24  /**
25   * This filter works in conjunction with a UserAgentBasedGzipSelectorFactory to determine whether to return noop
26   * selector or gzip-ing one. It relies on UserAgentBasedGzipSelectorFactory which check request browser and response
27   * mime types.
28   * <p/>
29   * Default gzippable mime types are set to {@link com.atlassian.gzipfilter.selector.UserAgentBasedGzipSelectorFactory#DEFAULT_COMPRESSABLE_MIME_TYPES}.
30   * <p/>
31   * Default non-gzippable user agents are set to {@link com.atlassian.gzipfilter.selector.UserAgentBasedGzipSelectorFactory#NO_COMPRESSION_USER_AGENTS_PARAM_NAME}.
32   * <p/>
33   * It's possible to override any of these settings via {@link com.atlassian.gzipfilter.selector.UserAgentBasedGzipSelectorFactory#COMPRESSABLE_MIME_TYPES_PARAM_NAME}
34   * {@link com.atlassian.gzipfilter.selector.UserAgentBasedGzipSelectorFactory#NO_COMPRESSION_USER_AGENTS_PARAM_NAME}
35   * init params respectively
36   * <p/>
37   * <p/>
38   * Please note that historically gzip check was based on url-rewrite and this will now check for the existence of
39   * /WEB-INF/urlrewrite-gzip.xml or urlrewrite.configfile init param in order to fail fast if any are found.
40   */
41  public class GzipFilter extends AbstractFilter
42  {
43      private static final Logger log = LoggerFactory.getLogger(GzipFilter.class);
44      private static final String ALREADY_FILTERED = GzipFilter.class.getName() + "_already_filtered";
45  
46      private static final GzipCompatibilitySelector NO_GZIP_SELECTOR = new NoGzipCompatibilitySelector();
47  
48      private GzipCompatibilitySelectorFactory factory;
49      private final GzipFilterIntegration integration;
50      private FilterConfig filterConfig;
51  
52      protected static final String LEGACY_INIT_PARAM = "urlrewrite.configfile";
53  
54      protected static final String LEGACY_CONFIG_FILE = "urlrewrite-gzip.xml";
55  
56      public GzipFilter(GzipFilterIntegration integration)
57      {
58          this.integration = integration;
59      }
60  
61      public void init(FilterConfig filterConfig) throws ServletException
62      {
63  
64          if (filterConfig.getInitParameter(LEGACY_INIT_PARAM) != null)
65          {
66              throw new IllegalArgumentException("Url rewrite is no longer used in gzip filter, "
67                      + "you provided " + LEGACY_INIT_PARAM + " as init param");
68          }
69  
70          final ServletContext servletContext = filterConfig.getServletContext();
71          if (servletContext != null)
72          {
73              final String path = servletContext.getRealPath("/WEB-INF/" + LEGACY_CONFIG_FILE);
74              if (new File(path).exists())
75              {
76                  throw new IllegalArgumentException("Url rewrite is no longer used in gzip filter, "
77                          + "but you have " + LEGACY_CONFIG_FILE + " in web-inf directory");
78              }
79  
80          }
81  
82          this.filterConfig = filterConfig;
83          this.factory = new UserAgentBasedGzipSelectorFactory(filterConfig);
84          super.init(filterConfig);
85      }
86  
87      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
88              throws IOException, ServletException
89      {
90          if (req.getAttribute(ALREADY_FILTERED) == null)
91          {
92              doFilterInternal(req, res, chain);
93          }
94          else
95          {
96              chain.doFilter(req, res);
97          }
98      }
99  
100     private void doFilterInternal(ServletRequest req, ServletResponse res, FilterChain chain)
101             throws IOException, ServletException
102     {
103         req.setAttribute(ALREADY_FILTERED, Boolean.TRUE);
104 
105         if (req instanceof HttpServletRequest && integration.useGzip() && isTopLevelRequest(req))
106         {
107             HttpServletRequest request = (HttpServletRequest) req;
108             HttpServletResponse response = (HttpServletResponse) res;
109             GzipCompatibilitySelector selector = getCompatibilitySelector(request);
110             if (selector.shouldGzip())
111             {
112                 log.debug("GZIP supported, compressing.");
113 
114                 SelectingResponseWrapper wrappedResponse = new SelectingResponseWrapper(response, selector, integration.getResponseEncoding(request));
115                 chain.doFilter(req, wrappedResponse);
116                 wrappedResponse.finishResponse();
117                 return;
118             }
119         }
120 
121         chain.doFilter(req, res);
122     }
123 
124     private GzipCompatibilitySelector getCompatibilitySelector(HttpServletRequest request)
125     {
126         String acceptEncodingHeader = request.getHeader("accept-encoding");
127         if (acceptEncodingHeader == null || acceptEncodingHeader.indexOf("gzip") == -1)
128         {
129             return NO_GZIP_SELECTOR;
130         }
131 
132         return getFactory().getSelector(filterConfig, request);
133     }
134 
135     protected GzipCompatibilitySelectorFactory getFactory()
136     {
137         return factory;
138     }
139 
140     private boolean isTopLevelRequest(ServletRequest request)
141     {
142         return request.getAttribute("javax.servlet.include.servlet_path") == null;
143     }
144 
145 }