View Javadoc

1   /**
2    *  Copyright 2003-2007 Greg Luck
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache.constructs.web.filter;
18  
19  import net.sf.ehcache.constructs.web.GenericResponseWrapper;
20  import net.sf.ehcache.constructs.web.ResponseUtil;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  import javax.servlet.FilterChain;
25  import javax.servlet.http.HttpServletRequest;
26  import javax.servlet.http.HttpServletResponse;
27  import java.io.ByteArrayOutputStream;
28  import java.util.zip.GZIPOutputStream;
29  
30  /**
31   * Provides GZIP compression of responses.
32   * <p/>
33   * See the filter-mappings.xml entry for the gzip filter for the URL patterns
34   * which will be gzipped. At present this includes .jsp, .js and .css.
35   * <p/>
36   * @author <a href="mailto:gluck@thoughtworks.com">Greg Luck</a>
37   * @author <a href="mailto:amurdoch@thoughtworks.com">Adam Murdoch</a>
38   * @version $Id: GzipFilter.java 512 2007-07-10 09:18:45Z gregluck $
39   */
40  public class GzipFilter extends Filter {
41  
42      private static final Log LOG = LogFactory.getLog(GzipFilter.class.getName());
43  
44      /**
45       * Performs initialisation.
46       */
47      protected void doInit() throws Exception {
48      }
49  
50      /**
51       * A template method that performs any Filter specific destruction tasks.
52       * Called from {@link #destroy()}
53       */
54      protected void doDestroy() {
55          //noop
56      }
57  
58      /**
59       * Performs the filtering for a request.
60       */
61      protected void doFilter(final HttpServletRequest request, final HttpServletResponse response,
62                              final FilterChain chain) throws Exception {
63          if (!isIncluded(request) && acceptsEncoding(request, "gzip")) {
64              // Client accepts zipped content
65              if (LOG.isDebugEnabled()) {
66                  LOG.debug(request.getRequestURL() + ". Writing with gzip compression");
67              }
68  
69              // Create a gzip stream
70              final ByteArrayOutputStream compressed = new ByteArrayOutputStream();
71              final GZIPOutputStream gzout = new GZIPOutputStream(compressed);
72  
73              // Handle the request
74              final GenericResponseWrapper wrapper = new GenericResponseWrapper(response, gzout);
75              chain.doFilter(request, wrapper);
76              wrapper.flush();
77  
78              gzout.close();
79  
80              //return on error or redirect code, because response is already committed
81              int statusCode = wrapper.getStatus();
82              if (statusCode != HttpServletResponse.SC_OK) {
83                  return;
84              }
85  
86              //Saneness checks
87              byte[] compressedBytes = compressed.toByteArray();
88              boolean shouldGzippedBodyBeZero = ResponseUtil.shouldGzippedBodyBeZero(compressedBytes, request);
89              boolean shouldBodyBeZero = ResponseUtil.shouldBodyBeZero(request, wrapper.getStatus());
90              if (shouldGzippedBodyBeZero || shouldBodyBeZero) {
91                  compressedBytes = new byte[0];
92              }
93  
94              // Write the zipped body
95              ResponseUtil.addGzipHeader(response);
96              response.setContentLength(compressedBytes.length);
97  
98  
99              response.getOutputStream().write(compressedBytes);
100         } else {
101             // Client does not accept zipped content - don't bother zipping
102             if (LOG.isDebugEnabled()) {
103                 LOG.debug(request.getRequestURL()
104                         + ". Writing without gzip compression because the request does not accept gzip.");
105             }
106             chain.doFilter(request, response);
107         }
108     }
109 
110 
111     /**
112      * Checks if the request uri is an include.
113      * These cannot be gzipped.
114      */
115     private boolean isIncluded(final HttpServletRequest request) {
116         final String uri = (String) request.getAttribute("javax.servlet.include.request_uri");
117         final boolean includeRequest = !(uri == null);
118 
119         if (includeRequest && LOG.isDebugEnabled()) {
120             LOG.debug(request.getRequestURL() + " resulted in an include request. This is unusable, because" +
121                     "the response will be assembled into the overrall response. Not gzipping.");
122         }
123         return includeRequest;
124     }
125 
126     /**
127      * Determine whether the user agent accepts GZIP encoding. This feature is part of HTTP1.1.
128      * If a browser accepts GZIP encoding it will advertise this by including in its HTTP header:
129      * <p/>
130      * <code>
131      * Accept-Encoding: gzip
132      * </code>
133      * <p/>
134      * Requests which do not accept GZIP encoding fall into the following categories:
135      * <ul>
136      * <li>Old browsers, notably IE 5 on Macintosh.
137      * <li>Internet Explorer through a proxy. By default HTTP1.1 is enabled but disabled when going
138      * through a proxy. 90% of non gzip requests seen on the Internet are caused by this.
139      * </ul>
140      * As of September 2004, about 34% of Internet requests do not accept GZIP encoding.
141      *
142      * @param request
143      * @return true, if the User Agent request accepts GZIP encoding
144      */
145     protected boolean acceptsGzipEncoding(HttpServletRequest request) {
146         return acceptsEncoding(request, "gzip");
147     }
148 
149 
150 }