/******************************************************************************* * Copyright (c) 2011, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.orion.server.servlets; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.HashSet; import java.util.StringTokenizer; import java.util.zip.GZIPOutputStream; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; /** * A filter that gzips all contents except excluded extensions and server-side includes. */ public class ExcludedExtensionGzipFilter implements Filter { public static class ServletOutputStreamWrapper extends ServletOutputStream { private OutputStream _outputStream; public ServletOutputStreamWrapper(OutputStream outputStream) throws IOException { super(); _outputStream = outputStream; } public void write(int b) throws IOException { _outputStream.write(b); } public void write(byte[] b) throws IOException { _outputStream.write(b); } public void write(byte[] b, int off, int len) throws IOException { _outputStream.write(b, off, len); } public void flush() throws IOException { _outputStream.flush(); } public void close() throws IOException { _outputStream.close(); } } public static class GZipServletResponse extends HttpServletResponseWrapper { private PrintWriter _printWriter; private ServletOutputStream _servletOutputStream; public GZipServletResponse(HttpServletResponse response) { super(response); } @Override public ServletOutputStream getOutputStream() throws IOException { if (_printWriter != null) { throw new IllegalStateException(); } if (_servletOutputStream == null) { ((HttpServletResponse) getResponse()).setHeader("Content-Encoding", "gzip"); _servletOutputStream = new ServletOutputStreamWrapper(new GZIPOutputStream(getResponse().getOutputStream())); } return _servletOutputStream; } @Override public PrintWriter getWriter() throws IOException { if (_printWriter == null) { if (_servletOutputStream != null) { throw new IllegalStateException(); } HttpServletResponse response = (HttpServletResponse) getResponse(); response.setHeader("Content-Encoding", "gzip"); _servletOutputStream = new ServletOutputStreamWrapper(new GZIPOutputStream(response.getOutputStream())); _printWriter = new PrintWriter(new OutputStreamWriter(_servletOutputStream, response.getCharacterEncoding())); } return _printWriter; } @Override public void flushBuffer() throws IOException { if (_printWriter != null) { _printWriter.flush(); } else if (_servletOutputStream != null) { _servletOutputStream.flush(); } } @Override public void setContentLength(int len) { // do not set this header } public void close() throws IOException { if (_printWriter != null) { _printWriter.close(); } else if (_servletOutputStream != null) { _servletOutputStream.close(); } } } static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri"; //$NON-NLS-1$ static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri"; //$NON-NLS-1$ private HashSet<String> _excludedExtensions = new HashSet<String>(); public void init(FilterConfig filterConfig) throws ServletException { String excludedExtensionsParam = filterConfig.getInitParameter("excludedExtensions"); if (excludedExtensionsParam != null) { StringTokenizer tokenizer = new StringTokenizer(excludedExtensionsParam, ",", false); while (tokenizer.hasMoreTokens()) { _excludedExtensions.add(tokenizer.nextToken().trim()); } } } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (isApplicable((HttpServletRequest) request)) { GZipServletResponse gzipResponse = new GZipServletResponse((HttpServletResponse) response); chain.doFilter(request, gzipResponse); gzipResponse.close(); } else { chain.doFilter(request, response); } } private boolean isApplicable(HttpServletRequest req) { if (req.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null || req.getAttribute(FORWARD_REQUEST_URI_ATTRIBUTE) != null) { return false; } String acceptEncoding = req.getHeader("Accept-Encoding"); if (acceptEncoding == null || !acceptEncoding.contains("gzip")) { return false; } String pathInfo = req.getPathInfo(); if (pathInfo == null || _excludedExtensions.isEmpty()) { return true; } int dot = pathInfo.lastIndexOf('.'); if (dot != -1) { String extension = pathInfo.substring(dot + 1).toLowerCase(); if (_excludedExtensions.contains(extension)) { return false; } } return true; } public void destroy() { } }