Java Servlet Proxy with Compression Filter

Aim
The aim of this tutorial is to create a http compressing proxy in java. We will be using the http proxy servlet created in a previous article Simple Proxy Servlet and a modified Compression Filter example in the your tomcat installation. The Compression Filter in the example needs modification because it is used to compress content served from tomcat and therefore tries to compress already compressed content that is served up from the proxy. A Proxy server/web server can compress based on the Accept-Encoding: gzip, deflate header sent by the Browser.

Assumptions
This article assumes that you have Glassfish or Tomcat or an equivalent java container installed and configured. This is a basic proxy and does not cover some features such as https etc. You can download the entire source code in the link below with the arrow

Versions used in this example
Sofware/ComponentImage
Windows XP SP2N/A
GlassfishN/A
JDK 1.5.0N/A
Links to these files can be found here

For the purposes of this tutorial the files are stored in the structure below. The CompressionServletProxy directory is the working directory. You can ignore the buil.xml and passwords.txt file as these are not required to compile the example. However, if you have glassfish and ant installed you can use the build.xml file.

CompressionServletProxy
_|_web_bin
_|__|_WEB-INF
_|__|__|_web.xml
_|__|__|_classes
_|_web_src
_|__|_compressionfilter
_|__|__|_CompressionFilter.java
_|__|__|_CompressionResponseStream.java
_|__|__|_CompressionServletResponseWrapper.java
_|__|_myproxy
_|__|__|_MyProxy.java
_|_build.xml
_|_passwords.txt

You can download the zipped example here.

The compression filter files are modified versions of the files that come with your tomcat installation. This is your tomcat installation D:\downloads\apache-tomcat-6.0.18\webapps\examples\WEB-INF\classes\compressionFilters

Create and deploy the Service
  1. Save the MyProxy.java code. This is available in the previous example Simple Proxy Servlet. This is the MyProxy servlet class. It recreates the url and uses HttpURLConnection to fetch the data from the requested host. As you can see the servlet request headers are copied to the outgoing HttpURLConnection headers, and incomming HttpURLConnection headers are copied back to the servlet response. This way we don't have to mess around with any headers.

  2. Now cd into the web_src directory and compile the source. Replace the path to Glassfish's lib directory with your local path. This could be lib in tomcat or any servlet container.

    ..CompressionServletProxy\web_src>javac myproxy\MyProxy.java -d ..\web_bin\WEB-INF\classes -extdirs d:\downloads\glassfish\lib

  3. Next copy the modified compression filter code to the web_src/compressionfilters directory. All of the changes are to the CompressionServletResponseWrapper.java file. These changes are to make sure we do not compress if the data from the proxy is already compressed. Here we check for gzip and deflate.
    public ServletOutputStream createOutputStream() throws IOException {
            if (debug > 1) {
                System.out.println("createOutputStream gets called");
            }
    
            CompressionResponseStream stream = new CompressionResponseStream(origResponse);
            stream.setDebugLevel(debug);
            stream.setBuffer(threshold);
    
            String s = getHeader("Content-Encoding");
            if(s!=null && (s.indexOf("gzip")!=-1 || s.indexOf("deflate")!=-1)){
                if(debug>1){
                    System.out.println("Data is already compressed so ignore and return original Stream");
                }
                return origResponse.getOutputStream();
            }
            else{
                return stream;
            }
        }

    These are the functions and overrides we need to implement the above.
    public void setHeader(String name, String value){
            headerMap.put(name, new String(value));
            super.setHeader(name, value);
        }
        public String getHeader(String name){
            Object o = headerMap.get(name);
            return  o==null?null:o.toString();
        }
        public String displayHeaders(){
            String tmp="";
            String x="";
            for( Iterator i = headerMap.keySet().iterator() ; i.hasNext() ; ){
                x = i.next().toString();
                tmp+=x +"  "+ headerMap.get(x) +"\n"; 
            }
            return tmp;
        }

  4. Now cd into the web_src directory and compile the source. Replace the path to Glassfish's lib directory with your local path. This could be lib in tomcat or any servlet container.

    ..CompressionServletProxy\web_src>javac compressionfilter\*.java -d ..\web_bin\WEB-INF\classes -extdirs d:\downloads\glassfish\lib
    You would have noticed that this proxy compresses everything! Even images. You can modify it to compress on 'Content-Type.'

  5. Write the web.xml file and save in under the web_bin/WEB-INF directory.
     1. <?xml version="1.0" encoding="ISO-8859-1"?>
     2. <web-app>
     3.     <filter>
     4.         <filter-name>My Compression Filter</filter-name>
     5.         <filter-class>compressionFilters.CompressionFilter</filter-class>
     6.            <init-param>
     7.             <param-name>debug</param-name>
     8.             <param-value>0</param-value>
     9.         </init-param>
    10.         <init-param>
    11.             <param-name>compressionThreshold</param-name>
    12.             <param-value>256</param-value>
    13.         </init-param>
    14.     </filter>
    15.     <servlet>
    16.         <servlet-name>My Proxy</servlet-name>
    17.         <servlet-class>myproxy.MyProxy</servlet-class>
    18.     </servlet>
    19. 
    20.     <filter-mapping>
    21.         <filter-name>My Compression Filter</filter-name>
    22.         <url-pattern>/*</url-pattern>
    23.     </filter-mapping>
    24.     <servlet-mapping>
    25.         <servlet-name>My Proxy</servlet-name>
    26.         <url-pattern>/</url-pattern>
    27.     </servlet-mapping>
    28. </web-app>
    Hide line numbers

  6. Now create the war file. Cd into the web_bin directory and jar it all up.

    ..ServletProxy\web_bin>jar -cvf proxywala.war *

  7. Finally deploy this war file in the servlet container of your choice. Remember it has to be in the ROOT context! .

    For glassfish you can specify the root context in the admin console. The steps for glassfish are.
    1. Login to the glassfish console
    2. Navigate to Applications -> Web Applications.
    3. Select deploy.
    4. Under "location" navigate to the war file we created proxywala.war.
    5. Under "Context Root" type in "/".
    6. Click Ok.

    For tomcat the steps are
    1. Copy the contents of the web_bin directory. Make sure it's the contents only such as WEB-INF and web.xml etc. Do not copy the web_bin directory itself.
    2. Now paste the contents into the tomcat webapps/ROOT directory.
    3. Restart Tomcat - if required.

Testing the filter
  1. First change your proxy settings to point to the proxy. In this example the servlet is deployed on the localhost at port 80.
    Proxy http://localhost
    proxy Port 80
  2. Now navigate to a some sites in your brower. You will be going to the web from your proxy. This proxy needs direct internet access.