/* =============================================================================== * * Part of the InfoGlue Content Management Platform (www.infoglue.org) * * =============================================================================== * * Copyright (C) * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License version 2, as published by the * Free Software Foundation. See the file LICENSE.html for more information. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY, including the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc. / 59 Temple * Place, Suite 330 / Boston, MA 02111-1307 / USA. * * =============================================================================== */ package org.infoglue.deliver.applications.filters; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import org.apache.log4j.Logger; /** * Wraps Response Stream for GZipFilter * * @author Matt Raible * @version $Revision: 1.2 $ $Date: 2004/05/22 12:24:23 $ */ public class GZIPResponseStream extends ServletOutputStream { public final static Logger logger = Logger.getLogger(GZIPResponseStream.class.getName()); // abstraction of the output stream used for compression protected OutputStream bufferedOutput = null; // state keeping variable for if close() has been called protected boolean closed = false; // reference to original response protected HttpServletResponse response = null; // reference to the output stream to the client's browser protected ServletOutputStream output = null; // default size of the in-memory buffer private int bufferSize = 50000; private File tempFile; public GZIPResponseStream(HttpServletResponse response) throws IOException { super(); closed = false; this.response = response; this.output = response.getOutputStream(); bufferedOutput = new ByteArrayOutputStream(); } public void close() throws IOException { if (closed) { throw new IOException("This output stream has already been closed"); } try { // if we buffered everything in memory, gzip it if (bufferedOutput instanceof ByteArrayOutputStream) { // get the content ByteArrayOutputStream baos = (ByteArrayOutputStream) bufferedOutput; // prepare a gzip stream ByteArrayOutputStream compressedContent = new ByteArrayOutputStream(); GZIPOutputStream gzipstream = new GZIPOutputStream(compressedContent); byte[] bytes = baos.toByteArray(); gzipstream.write(bytes); gzipstream.finish(); // get the compressed content byte[] compressedBytes = compressedContent.toByteArray(); // set appropriate HTTP headers response.setContentLength(compressedBytes.length); response.addHeader("Content-Encoding", "gzip"); output.write(compressedBytes); output.flush(); output.close(); closed = true; } // if things were not buffered in memory, finish the GZIP stream and // response else if (bufferedOutput instanceof GZIPOutputStream) { // cast to appropriate type GZIPOutputStream gzipstream = (GZIPOutputStream) bufferedOutput; // finish the compression gzipstream.finish(); gzipstream.flush(); gzipstream.close(); response.setContentLength((int)tempFile.length()); FileInputStream tempFileStream = new FileInputStream(tempFile); IOUtils.copy(tempFileStream, output); // finish the response output.flush(); output.close(); closed = true; } } finally { if (tempFile != null) { tempFile.delete(); tempFile = null; } } } public void flush() throws IOException { if (closed) { throw new IOException("Cannot flush a closed output stream"); } bufferedOutput.flush(); } public void write(int b) throws IOException { if (closed) { throw new IOException("Cannot write to a closed output stream"); } // make sure we aren't over the buffer's limit checkBufferSize(1); // write the byte to the temporary output bufferedOutput.write((byte) b); } private void switchToFilebacking(byte[] initialBytes) throws FileNotFoundException, IOException { if (logger.isDebugEnabled()) { logger.debug("Resource exceeds buffer size. Switching to file-backed mode."); } tempFile = File.createTempFile("gzip-", ".tmp"); // make new gzip stream using the response output stream OutputStream gzipstream = new GZIPOutputStream(new FileOutputStream(tempFile)); gzipstream.write(initialBytes); // we are no longer buffering, send content via gzipstream bufferedOutput = gzipstream; response.addHeader("Content-Encoding", "gzip"); } private void checkBufferSize(int length) throws IOException { // check if we are buffering too large of a file if (bufferedOutput instanceof ByteArrayOutputStream) { ByteArrayOutputStream baos = (ByteArrayOutputStream) bufferedOutput; if ((baos.size() + length) > bufferSize) { // files too large to keep in memory are sent to the client without // Content-Length specified switchToFilebacking(baos.toByteArray()); } } } public void write(byte[] b) throws IOException { write(b, 0, b.length); } public void write(byte[] b, int off, int len) throws IOException { if (closed) { throw new IOException("Cannot write to a closed output stream"); } // make sure we aren't over the buffer's limit checkBufferSize(len); // write the content to the buffer bufferedOutput.write(b, off, len); } public boolean closed() { return (this.closed); } public void reset() { //noop } }