package org.realityforge.gwt.cache_filter;

import java.io.File;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/**
 * Filter to redirect all files to their corresponding .gz file, if it exists, and serve
 * it with the proper encoding / content type. Works best if called after GWTCacheControlFilter
 */
public class GWTGzipControlFilter
  extends AbstractFilter
{
  private static String gzExt = ".gz";

  public static void setGzipExtension( String newExt )
  {
    if ( newExt != null )
    {
      gzExt = newExt;
    }
  }

  @Override
  public void doFilter( final ServletRequest servletRequest,
                        final ServletResponse servletResponse,
                        final FilterChain filterChain )
    throws IOException, ServletException
  {
    final HttpServletRequest request = (HttpServletRequest) servletRequest;
    final HttpServletResponse response = (HttpServletResponse) servletResponse;

    if ( !acceptsGzip( request ) )
    {
      filterChain.doFilter( request, response );
    }
    else
    {
      final String reqUrl = request.getRequestURI();
      final String realPath = request.getServletContext().getRealPath( reqUrl );
      final File file = null == realPath ? null : new File( realPath );
      if ( null == file || !file.exists() || file.isDirectory() )
      {
        filterChain.doFilter( request, response );
      }
      else if ( reqUrl.endsWith( gzExt ) )
      {
        final String mimeType = getMimeType( request, reqUrl );
        /***
         Just using response.setContentType() doesn't work, it gets reset
         when filterChain.doFilter() is called. A custom response wrapper has
         to be made, which would force the content type. See:
         http://stackoverflow.com/a/24846284/49153
         ****/
        if ( null != mimeType )
        {
          ForcableContentTypeWrapper newResponse = new ForcableContentTypeWrapper( response );
          newResponse.setHeader( "Content-Encoding", "gzip" );
          newResponse.forceContentType( mimeType );
          filterChain.doFilter( request, newResponse );
        }
        else
        {
          filterChain.doFilter( request, response );
        }
      }
      else
      {
        final String gzippedPath = realPath + gzExt;
        final File gzippedFile = new File( gzippedPath );

        if ( !gzippedFile.exists() || gzippedFile.isDirectory() )
        {
          filterChain.doFilter( request, servletResponse );
        }
        else
        {
          final String gzippedUrl = reqUrl + gzExt;
          response.sendRedirect( gzippedUrl );
          filterChain.doFilter( request, response );
        }
      }
    }
  }

  public static boolean acceptsGzip( final HttpServletRequest request )
  {
    final String header = request.getHeader( "Accept-Encoding" );
    return null != header && header.contains( "gzip" );
  }

  public static String getMimeType( final HttpServletRequest request, String filePath )
  {
    filePath = filePath.substring( 0, filePath.length() - gzExt.length() );
    final String ext = filePath.substring( filePath.lastIndexOf( "." ), filePath.length() );
    return request.getServletContext().getMimeType( ext );
  }

  private class ForcableContentTypeWrapper
    extends HttpServletResponseWrapper
  {
    public ForcableContentTypeWrapper( final HttpServletResponse response )
    {
      super( response );
    }

    @Override
    public void setContentType( final String type )
    {
    }

    public void forceContentType( final String type )
    {
      super.setContentType( type );
    }
  }
}

@@ -120,16 +120,56 @@
 rChain;%0A
+import javax.servlet.RequestDispatcher;%0A
 import j
@@ -266,32 +266,75 @@
 ervletResponse;%0A
+import javax.servlet.annotation.WebFilter;%0A
 import javax.ser
@@ -413,62 +413,8 @@
 nse;
-%0Aimport javax.servlet.http.HttpServletResponseWrapper;
 %0A%0A/*
@@ -604,16 +604,66 @@
 ter%0A */%0A
+@WebFilter( filterName = %22GWTGzipControlFilter%22 )%0A
 public c
@@ -1405,20 +1405,26 @@
 tring re
-qUrl
+sourcePath
  = reque
@@ -1433,18 +1433,19 @@
 .get
-RequestURI
+ServletPath
 ();%0A
@@ -1517,20 +1517,26 @@
 Path( re
-qUrl
+sourcePath
  );%0A    
@@ -1756,822 +1756,41 @@
 f ( 
-reqUrl.endsWith( gzExt ) )%0A      %7B%0A        final String mimeType = getMimeType( request, reqUrl );%0A        /***%0A         Just using response.setContentType() doesn't work, it gets reset%0A         when filterChain.doFilter() is called. A custom response wrapper has%0A         to be made, which would force the content type. See:%0A         http://stackoverflow.com/a/24846284/49153%0A         ****/%0A        if ( null != mimeType )%0A        %7B%0A          ForcableContentTypeWrapper newResponse = new ForcableContentTypeWrapper( response );%0A          newResponse.setHeader( %22Content-Encoding%22, %22gzip%22 );%0A          newResponse.forceContentType( mimeType );%0A          filterChain.doFilter( request, newResponse );%0A        %7D%0A        else%0A        %7B%0A          filterChain.doFilter( request, response );%0A        %7D%0A      %7D%0A      else
+!resourcePath.endsWith( gzExt ) )
 %0A   
@@ -2095,42 +2095,121 @@
 nal 
-String gzippedUrl = reqUrl
+RequestDispatcher dispatcher =%0A            request.getServletContext().getRequestDispatcher( resourcePath
  + gzExt
 ;%0A  
@@ -2196,32 +2196,34 @@
 urcePath + gzExt
+ )
 ;%0A          resp
@@ -2233,64 +2233,75 @@
 e.se
-ndRedirect( gzippedUrl );%0A          filterChain.doFilter
+tHeader( %22Content-Encoding%22, %22gzip%22 );%0A          dispatcher.include
 ( re
@@ -2553,718 +2553,7 @@
 %0A  %7D
-%0A%0A  public static String getMimeType( final HttpServletRequest request, String filePath )%0A  %7B%0A    filePath = filePath.substring( 0, filePath.length() - gzExt.length() );%0A    final String ext = filePath.substring( filePath.lastIndexOf( %22.%22 ), filePath.length() );%0A    return request.getServletContext().getMimeType( ext );%0A  %7D%0A%0A  private class ForcableContentTypeWrapper%0A    extends HttpServletResponseWrapper%0A  %7B%0A    public ForcableContentTypeWrapper( final HttpServletResponse response )%0A    %7B%0A      super( response );%0A    %7D%0A%0A    @Override%0A    public void setContentType( final String type )%0A    %7B%0A    %7D%0A%0A    public void forceContentType( final String type )%0A    %7B%0A      super.setContentType( type );%0A    %7D%0A  %7D
 %0A%7D%0A

Use a request dispatcher to forward on the gzipped content rather than doing a redirection dance. Reduces the number of http requests.