/*
Copyright 2011-2014 Red Hat, Inc
This file is part of PressGang CCMS.
PressGang CCMS is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
PressGang CCMS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with PressGang CCMS. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jboss.pressgang.ccms.server.servlet.filter;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GZIPResponseWrapper extends HttpServletResponseWrapper {
private GZIPServletOutputStream GZIPStream = null;
private ServletOutputStream outputStream = null;
private PrintWriter printWriter = null;
private final HttpServletResponse response;
private final Set<Pattern> mimeTypes;
public GZIPResponseWrapper(final HttpServletResponse response, final Set<Pattern> mimeTypes) {
super(response);
this.response = response;
this.mimeTypes = mimeTypes;
}
public void finish() throws IOException {
if (this.printWriter != null) {
this.printWriter.close();
}
if (this.outputStream != null) {
this.outputStream.close();
}
}
@Override
public void flushBuffer() throws IOException {
if (this.printWriter != null) {
this.printWriter.flush();
}
if (this.outputStream != null) {
this.outputStream.flush();
}
super.flushBuffer();
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
final String contentType = response.getContentType();
// Check if the response should be compressed based on the mime-type
if (isCompressible(contentType)) {
if (this.printWriter != null) throw new IllegalStateException("Print Writer already defined");
if (this.outputStream == null) {
initGZIPStream();
this.outputStream = this.GZIPStream;
}
} else {
this.outputStream = response.getOutputStream();
}
return this.outputStream;
}
@Override
public PrintWriter getWriter() throws IOException {
final String contentType = response.getContentType();
// Check if the response should be compressed based on the mime-type
if (isCompressible(contentType)) {
if (this.outputStream != null) throw new IllegalStateException("Print Writer already defined");
if (this.printWriter == null) {
initGZIPStream();
this.printWriter = new PrintWriter(new OutputStreamWriter(this.GZIPStream, getResponse().getCharacterEncoding()));
}
} else {
this.printWriter = super.getWriter();
}
return this.printWriter;
}
/**
* Initialise the GZIP Stream to be used to encode the data, if it hasn't already been initialised.
*
* @throws IOException
*/
private void initGZIPStream() throws IOException {
if (this.GZIPStream == null) {
this.GZIPStream = new GZIPServletOutputStream(response);
}
}
/**
* Check if the response should be compressed based on the MIME type.
*
* @param contentType The content type of the response.
* @return True if the response should be compressed otherwise false.
*/
protected boolean isCompressible(final String contentType) {
if (contentType == null) {
return true;
}
// Strip away any extra details that are after the mime type
final String stripped = stripParams(contentType);
// Iterate over the mime types to see if any match the response mime type
final Iterator<Pattern> it = mimeTypes.iterator();
while (it.hasNext()) {
final Pattern mimeTypePattern = it.next();
final Matcher matcher = mimeTypePattern.matcher(stripped);
if (matcher.matches()) return true;
}
return false;
}
/**
* String away any extra parameters from a response Content-Type to find the MIME type.
*
* @param contentType The content type of the response.
* @return The MIME type of the response stripped of any extra variables.
*/
protected String stripParams(final String contentType) {
int firstSemicolon = contentType.indexOf(";");
if (firstSemicolon != -1) {
return contentType.substring(0, firstSemicolon);
}
return contentType;
}
}