/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library 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 2.1 of the License, or (at your option) * any later version. * * This library 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. */ package com.liferay.portal.servlet.filters.gzip; import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.servlet.BrowserSnifferUtil; import com.liferay.portal.kernel.servlet.HttpHeaders; import com.liferay.portal.kernel.servlet.MetaInfoCacheServletResponse; import com.liferay.portal.kernel.servlet.ServletOutputStreamAdapter; import com.liferay.portal.kernel.util.ContentTypes; import com.liferay.portal.kernel.util.UnsyncPrintWriterPool; import com.liferay.portal.util.PropsValues; import com.liferay.util.RSSThreadLocal; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author Jayson Falkner * @author Brian Wing Shun Chan * @author Shuyang Zhou */ public class GZipResponse extends MetaInfoCacheServletResponse { public GZipResponse( HttpServletRequest request, HttpServletResponse response) { super(response); _response = response; // Clear previous content length setting. GZip response does not buffer // output to get final content length. The response will be chunked // unless an outer filter calculates the content length. _response.setContentLength(-1); // Setting the header after finishResponse is too late _response.addHeader(HttpHeaders.CONTENT_ENCODING, _GZIP); _firefox = BrowserSnifferUtil.isFirefox(request); } @Override public void finishResponse(boolean reapplyMetaData) throws IOException { // Is the response committed? if (!isCommitted()) { // Has the content been GZipped yet? if ((_servletOutputStream == null) || ((_servletOutputStream != null) && (_unsyncByteArrayOutputStream != null) && (_unsyncByteArrayOutputStream.size() == 0))) { // Reset the wrapped response to clear out the GZip header _response.reset(); // Reapply meta data super.finishResponse(reapplyMetaData); } } try { if (_printWriter != null) { _printWriter.close(); } else if (_servletOutputStream != null) { _servletOutputStream.close(); } } catch (IOException ioe) { } if (_unsyncByteArrayOutputStream != null) { _response.setContentLength(_unsyncByteArrayOutputStream.size()); _unsyncByteArrayOutputStream.writeTo(_response.getOutputStream()); } } @Override public void flushBuffer() throws IOException { if (_servletOutputStream != null) { _servletOutputStream.flush(); } } @Override public ServletOutputStream getOutputStream() throws IOException { if (_printWriter != null) { throw new IllegalStateException(); } if (_servletOutputStream == null) { if (_isGZipContentType()) { _servletOutputStream = _response.getOutputStream(); } else { if (_firefox && RSSThreadLocal.isExportRSS()) { _unsyncByteArrayOutputStream = new UnsyncByteArrayOutputStream(); _servletOutputStream = _createGZipServletOutputStream( _unsyncByteArrayOutputStream); } else { _servletOutputStream = _createGZipServletOutputStream( _response.getOutputStream()); } } } return _servletOutputStream; } @Override public PrintWriter getWriter() throws IOException { if (_printWriter != null) { return _printWriter; } if (_servletOutputStream != null) { throw new IllegalStateException(); } if (_log.isWarnEnabled()) { _log.warn("Use getOutputStream for optimum performance"); } _servletOutputStream = getOutputStream(); _printWriter = UnsyncPrintWriterPool.borrow( _servletOutputStream, getCharacterEncoding()); return _printWriter; } @Override public void setContentLength(int contentLength) { } @Override public void setHeader(String name, String value) { if (HttpHeaders.CONTENT_LENGTH.equals(name)) { return; } super.setHeader(name, value); } private ServletOutputStream _createGZipServletOutputStream( OutputStream outputStream) throws IOException { GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream) { { def.setLevel(PropsValues.GZIP_COMPRESSION_LEVEL); } }; return new ServletOutputStreamAdapter(gzipOutputStream); } private boolean _isGZipContentType() { String contentType = getContentType(); if (contentType != null) { if (contentType.equals(ContentTypes.APPLICATION_GZIP) || contentType.equals(ContentTypes.APPLICATION_X_GZIP)) { return true; } } return false; } private static final String _GZIP = "gzip"; private static final Log _log = LogFactoryUtil.getLog(GZipResponse.class); private final boolean _firefox; private PrintWriter _printWriter; private final HttpServletResponse _response; private ServletOutputStream _servletOutputStream; private UnsyncByteArrayOutputStream _unsyncByteArrayOutputStream; }