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>
* <filter>
* <filter-name>GZIPFilter</filter-name>
*
<filter-class>de.sag.dms.common.servlet.GzipFilter</filter-class>
* </filter>
*
* <filter-mapping>
* <filter-name>GZIPFilter</filter-name>
* <url-pattern>/DSIServlet</url-pattern>
* </filter-mapping>
* </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>
* <result success="YES" resultcode="0">
* <resulttext>DSI-Server alive
* <version patchlevel='0'
date='2005-04-29'>4.2.1</version>
* <specification date='2005-04-15'>4.2.1</specification>
* <api-version date='2005-04-15'>3.0.21</api-version>
* <timestamp ms='1114587247628' date='2005-04-27 09:34:07
CEST'/>
* </resulttext>
* </result>
* </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]