Hi,

while writing an input filter, that decompresses its input, I have
encountered a problem. My filter (see complete source below) contains
roughly the following code:

  private HttpServletRequest
      getServletRequest(final HttpServletRequest pRequest) {
          String contentEncoding =
              pRequest.getHeader("Content-Encoding");
          if (!isUsingGzipEncoding(contentEncoding)) {
              return pRequest;
          }
          return new HttpServletRequestWrapper(pRequest) {
              public ServletInputStream getInputStream()
                      throws IOException {
                  final InputStream rin = pRequest.getInputStream();
                  final InputStream in = new GZIPInputStream(rin);
                  return new ServletInputStream(){
                      public int read() throws IOException {
                          return in.read();
                      }
                      public void close() throws IOException {
                          in.close();
                      }
                  };
             }
             public BufferedReader getReader() throws IOException {
                final String enc = getCharacterEncoding();
                final InputStream istream = getInputStream();
                final Reader r = new InputStreamReader(istream, enc);
                return new BufferedReader(istream);
             }
          };
      }

This works fine in most cases, with one important exception: If the
request is using the POST method with a content type
"x-www-form-urlencoded", then the following occurs:

* My Servlet invokes getParameterNames() on my request wrapper.
* The request wrapper invokes getParameterNames() on the RequestFacade.
* The request facade invokes getParameterNames() on the Request object.
* Which finally invokes getInputStream(), but not on my request wrapper,
  but on the Request object. In other words, the compressed input stream
  is read.


Any suggestions for a possible workaround?


Regards,

Jochen



package de.sag.dms.common.servlet;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletOutputStream;
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.servlet.http.HttpServletResponseWrapper;


/** <p>The <code>GzipFilter</code> is a servlet filter, that enables use of
 * gzip compression for both incoming and outgoing data.</p>
 * <p>Servlet filters can be added to arbitrary servlets. Whether they are
 * active or not, is a matter of configuration in the <code>web.xml</code>
 * file. For example, to activate the gzip filter for the URI /DSIServlet,
 * you would add the following lines to web.xml:</p>
 * <pre>
 *   &lt;filter&gt;
 *     &lt;filter-name&gt;GZIPFilter&lt;/filter-name&gt;
 *
&lt;filter-class&gt;de.sag.dms.common.servlet.GzipFilter&lt;/filter-class&gt;
 *   &lt;/filter&gt;
 *
 *   &lt;filter-mapping&gt;
 *     &lt;filter-name&gt;GZIPFilter&lt;/filter-name&gt;
 *     &lt;url-pattern&gt;/DSIServlet&lt;/url-pattern&gt;
 *   &lt;/filter-mapping&gt;
 * </pre>
 * <p>Note, that the context name will always be prepended to the URI.
 * In other words, if your web application is accessible below
 * "/DSI", then the actual URI for the above mapping would be
 * "/DSI/DSIServlet".</p>
 * <p>If a client wants to send compressed data, then it should
 * behave according to section 14.11 of RFC 2616: The client <em>must</em>
 * compress the whole body and it <em>must</em> set the
 * <em>request header</em> * "Content-Encoding" to a proper value,
 * typically "gzip". See
 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html";>
 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html</a> for details
 * on RFC 2616.</p>
 * <p>If a client wants the server to reply with compressed data, then
 * it should again follow RFC 2616 and set the <em>request header</em>
 * "Accept-Encoding". The value will typically be a comma separated list
 * of words. The word "gzip" indicates, that the client would like to
 * accept gzip compressed data. For example, browsers will typically
 * send "gzip, deflate".</p>
 * <p>If the server detects, that the client wants a compressed response,
 * then it will reply by setting the <em>response header</em>
 * "Content-Encoding" to the value "gzip". Consequently, the
 * whole response body will be compressed using gzip.</p>
 * <p>To verify, whether the filter works, do the following: Create a
 * request file "/tmp/request.txt", for example like this:</p>
 * <pre>
 * -----------------------------21
 * Content-Disposition: form-data; name="Command"
 *
 * Ping
 * -----------------------------21
 * Content-Disposition: form-data; name="password"
 * DsiHero9)
 * -----------------------------21
 * Content-Disposition: form-data; name="cpassword"
 *
 * -----------------------------21
 * Content-Disposition: form-data; name="user"
 *
 * root
 * -----------------------------21--
 * </pre>
 * Send the file to the server with a proper utility. For example,
 * use the perl utility "GET":
 * <pre>
 *   cat /tmp/request.txt |
 *   GET -m POST
 *     -c "multipart/form-data; boundary=-----------------------------21"
 *     http://127.0.0.1:8080/DSI/DSIServlet
 * </pre>
 * <p>(The above command is edited for readability and must be entered
 * on a single line, of course.) The program should display a response
 * like the following:</p>
 * <pre>
 *   &lt;result success="YES" resultcode="0"&gt;
 *     &lt;resulttext&gt;DSI-Server alive
 *       &lt;version patchlevel='0'
date='2005-04-29'&gt;4.2.1&lt;/version&gt;
 *       &lt;specification date='2005-04-15'&gt;4.2.1&lt;/specification&gt;
 *       &lt;api-version date='2005-04-15'&gt;3.0.21&lt;/api-version&gt;
 *       &lt;timestamp ms='1114587247628' date='2005-04-27 09:34:07
CEST'/&gt;
 *     &lt;/resulttext&gt;
 *   &lt;/result&gt;
 * </pre>
 * <p>Now modify the command to request gzip encoding:</p>
 * <pre>
 *   cat /tmp/request.txt |
 *   GET -m POST -H "Accept-Encoding: gzip"
 *     -c "multipart/form-data; boundary=-----------------------------21"
 *     http://127.0.0.1:8080/DSI/DSIServlet |
 *   gzip -cd
 * </pre>
 * <p>The result should remain the same.</p>
 */
public class GzipFilter implements Filter {
        private FilterConfig config;

        public void init(FilterConfig pConfig) throws ServletException {
                config = pConfig;
        }
        public void destroy() {
                config = null;
        }

        private HttpServletRequest getServletRequest(final HttpServletRequest
pRequest) {
                if
(!HttpUtil.isUsingGzipEncoding(pRequest.getHeader("Content-Encoding"))) {
                        return pRequest;
                }
                return new HttpServletRequestWrapper(pRequest){
                        public int getContentLength() {
                                /* Even if we have a content length, it is now 
invalid,
                                 * because it would be the content length of 
the compressed
                                 * request.
                                 */
                                return -1;
                        }
                        public ServletInputStream getInputStream() throws 
IOException {
                                final InputStream in = new 
GZIPInputStream(pRequest.getInputStream());
                                return new ServletInputStream(){
                                        public int read() throws IOException {
                                                return in.read();
                                        }
                                        public void close() throws IOException {
                                                in.close();
                                        }
                                };
                        }
                        public BufferedReader getReader() throws IOException {
                                return new BufferedReader(new 
InputStreamReader(getInputStream(),
getCharacterEncoding()));
                        }
                };
        }

        private HttpServletResponse getServletResponse(final HttpServletRequest
pRequest,
                                                                                
                   final HttpServletResponse pResponse) {
                if
(!HttpUtil.isUsingGzipEncoding(pRequest.getHeader("Accept-Encoding"))) {
                        return pResponse;
                }
                return new HttpServletResponseWrapper(pResponse){
                        boolean created;
                        public ServletOutputStream getOutputStream() throws 
IOException {
                                if (created) {
                                        throw new 
IllegalStateException("Already created");
                                }
                                created = true;
                                pResponse.setHeader("Content-Encoding", "gzip");
                                final OutputStream gzo = new
GZIPOutputStream(pResponse.getOutputStream());
                                return new ServletOutputStream(){
                                        public void write(int b) throws 
IOException {
                                                gzo.write(b);
                                        }
                                        public void flush() throws IOException {
                                                gzo.flush();
                                        }
                                        public void close() throws IOException {
                                                gzo.close();
                                        }
                                };
                        }
                        public PrintWriter getWriter() throws IOException {
                                return new PrintWriter(getOutputStream());
                        }
                        public void setContentLength(int pArg) {
                                /* Whatever the content length, we ignore it: 
It would be
                                 * the content length of the uncompressed 
stream, which
                                 * is currently meaningless.
                                 */
                                pArg = -1;      // This line and the following 
just to suppress
                                pResponse.setContentLength(pArg); // a "not 
used" warning.
                        }
                };
        }

        public void doFilter(ServletRequest pRequest, ServletResponse pResponse,
                                                 FilterChain pChain) throws 
IOException, ServletException {
                if (config == null) { return; }
                HttpServletRequest req = (HttpServletRequest) pRequest;
                HttpServletResponse res = (HttpServletResponse) pResponse;
                req = getServletRequest(req);
                res = getServletResponse(req, res);
                pChain.doFilter(req, res);
        }
}


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to