001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.servlet.filters.gzip;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.servlet.BrowserSnifferUtil;
021    import com.liferay.portal.kernel.servlet.HttpHeaders;
022    import com.liferay.portal.kernel.servlet.MetaInfoCacheServletResponse;
023    import com.liferay.portal.kernel.servlet.ServletOutputStreamAdapter;
024    import com.liferay.portal.kernel.util.ContentTypes;
025    import com.liferay.portal.kernel.util.UnsyncPrintWriterPool;
026    import com.liferay.portal.util.PropsValues;
027    import com.liferay.util.RSSThreadLocal;
028    
029    import java.io.IOException;
030    import java.io.OutputStream;
031    import java.io.PrintWriter;
032    
033    import java.util.zip.GZIPOutputStream;
034    
035    import javax.servlet.ServletOutputStream;
036    import javax.servlet.http.HttpServletRequest;
037    import javax.servlet.http.HttpServletResponse;
038    
039    /**
040     * @author Jayson Falkner
041     * @author Brian Wing Shun Chan
042     * @author Shuyang Zhou
043     */
044    public class GZipResponse extends MetaInfoCacheServletResponse {
045    
046            public GZipResponse(
047                    HttpServletRequest request, HttpServletResponse response) {
048    
049                    super(response);
050    
051                    _response = response;
052    
053                    // Clear previous content length setting. GZip response does not buffer
054                    // output to get final content length. The response will be chunked
055                    // unless an outer filter calculates the content length.
056    
057                    _response.setContentLength(-1);
058    
059                    // Setting the header after finishResponse is too late
060    
061                    _response.addHeader(HttpHeaders.CONTENT_ENCODING, _GZIP);
062    
063                    _firefox = BrowserSnifferUtil.isFirefox(request);
064            }
065    
066            /**
067             * @deprecated As of 7.0.0, replaced by {@link #finishResponse(boolean)}}
068             */
069            @Deprecated
070            @Override
071            public void finishResponse() throws IOException {
072                    finishResponse(false);
073            }
074    
075            @Override
076            public void finishResponse(boolean reapplyMetaData) throws IOException {
077    
078                    // Is the response committed?
079    
080                    if (!isCommitted()) {
081    
082                            // Has the content been GZipped yet?
083    
084                            if ((_servletOutputStream == null) ||
085                                    ((_servletOutputStream != null) &&
086                                     (_unsyncByteArrayOutputStream != null) &&
087                                     (_unsyncByteArrayOutputStream.size() == 0))) {
088    
089                                    // Reset the wrapped response to clear out the GZip header
090    
091                                    _response.reset();
092    
093                                    // Reapply meta data
094    
095                                    super.finishResponse(reapplyMetaData);
096                            }
097                    }
098    
099                    try {
100                            if (_printWriter != null) {
101                                    _printWriter.close();
102                            }
103                            else if (_servletOutputStream != null) {
104                                    _servletOutputStream.close();
105                            }
106                    }
107                    catch (IOException ioe) {
108                    }
109    
110                    if (_unsyncByteArrayOutputStream != null) {
111                            _response.setContentLength(_unsyncByteArrayOutputStream.size());
112    
113                            _unsyncByteArrayOutputStream.writeTo(_response.getOutputStream());
114                    }
115            }
116    
117            @Override
118            public void flushBuffer() throws IOException {
119                    if (_servletOutputStream != null) {
120                            _servletOutputStream.flush();
121                    }
122            }
123    
124            @Override
125            public ServletOutputStream getOutputStream() throws IOException {
126                    if (_printWriter != null) {
127                            throw new IllegalStateException();
128                    }
129    
130                    if (_servletOutputStream == null) {
131                            if (_isGZipContentType()) {
132                                    _servletOutputStream = _response.getOutputStream();
133                            }
134                            else {
135                                    if (_firefox && RSSThreadLocal.isExportRSS()) {
136                                            _unsyncByteArrayOutputStream =
137                                                    new UnsyncByteArrayOutputStream();
138    
139                                            _servletOutputStream = _createGZipServletOutputStream(
140                                                    _unsyncByteArrayOutputStream);
141                                    }
142                                    else {
143                                            _servletOutputStream = _createGZipServletOutputStream(
144                                                    _response.getOutputStream());
145                                    }
146                            }
147                    }
148    
149                    return _servletOutputStream;
150            }
151    
152            @Override
153            public PrintWriter getWriter() throws IOException {
154                    if (_printWriter != null) {
155                            return _printWriter;
156                    }
157    
158                    if (_servletOutputStream != null) {
159                            throw new IllegalStateException();
160                    }
161    
162                    if (_log.isWarnEnabled()) {
163                            _log.warn("Use getOutputStream for optimum performance");
164                    }
165    
166                    _servletOutputStream = getOutputStream();
167    
168                    _printWriter = UnsyncPrintWriterPool.borrow(
169                            _servletOutputStream, getCharacterEncoding());
170    
171                    return _printWriter;
172            }
173    
174            @Override
175            public void setContentLength(int contentLength) {
176            }
177    
178            @Override
179            public void setHeader(String name, String value) {
180                    if (HttpHeaders.CONTENT_LENGTH.equals(name)) {
181                            return;
182                    }
183    
184                    super.setHeader(name, value);
185            }
186    
187            private ServletOutputStream _createGZipServletOutputStream(
188                            OutputStream outputStream)
189                    throws IOException {
190    
191                    GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream) {
192    
193                            {
194                                    def.setLevel(PropsValues.GZIP_COMPRESSION_LEVEL);
195                            }
196    
197                    };
198    
199                    return new ServletOutputStreamAdapter(gzipOutputStream);
200            }
201    
202            private boolean _isGZipContentType() {
203                    String contentType = getContentType();
204    
205                    if (contentType != null) {
206                            if (contentType.equals(ContentTypes.APPLICATION_GZIP) ||
207                                    contentType.equals(ContentTypes.APPLICATION_X_GZIP)) {
208    
209                                    return true;
210                            }
211                    }
212    
213                    return false;
214            }
215    
216            private static final String _GZIP = "gzip";
217    
218            private static Log _log = LogFactoryUtil.getLog(GZipResponse.class);
219    
220            private boolean _firefox;
221            private PrintWriter _printWriter;
222            private HttpServletResponse _response;
223            private ServletOutputStream _servletOutputStream;
224            private UnsyncByteArrayOutputStream _unsyncByteArrayOutputStream;
225    
226    }