001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
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            @Override
067            public void finishResponse(boolean reapplyMetaData) throws IOException {
068    
069                    // Is the response committed?
070    
071                    if (!isCommitted()) {
072    
073                            // Has the content been GZipped yet?
074    
075                            if ((_servletOutputStream == null) ||
076                                    ((_servletOutputStream != null) &&
077                                     (_unsyncByteArrayOutputStream != null) &&
078                                     (_unsyncByteArrayOutputStream.size() == 0))) {
079    
080                                    // Reset the wrapped response to clear out the GZip header
081    
082                                    _response.reset();
083    
084                                    // Reapply meta data
085    
086                                    super.finishResponse(reapplyMetaData);
087                            }
088                    }
089    
090                    try {
091                            if (_printWriter != null) {
092                                    _printWriter.close();
093                            }
094                            else if (_servletOutputStream != null) {
095                                    _servletOutputStream.close();
096                            }
097                    }
098                    catch (IOException ioe) {
099                    }
100    
101                    if (_unsyncByteArrayOutputStream != null) {
102                            _response.setContentLength(_unsyncByteArrayOutputStream.size());
103    
104                            _unsyncByteArrayOutputStream.writeTo(_response.getOutputStream());
105                    }
106            }
107    
108            @Override
109            public void flushBuffer() throws IOException {
110                    if (_servletOutputStream != null) {
111                            _servletOutputStream.flush();
112                    }
113            }
114    
115            @Override
116            public ServletOutputStream getOutputStream() throws IOException {
117                    if (_printWriter != null) {
118                            throw new IllegalStateException();
119                    }
120    
121                    if (_servletOutputStream == null) {
122                            if (_isGZipContentType()) {
123                                    _servletOutputStream = _response.getOutputStream();
124                            }
125                            else {
126                                    if (_firefox && RSSThreadLocal.isExportRSS()) {
127                                            _unsyncByteArrayOutputStream =
128                                                    new UnsyncByteArrayOutputStream();
129    
130                                            _servletOutputStream = _createGZipServletOutputStream(
131                                                    _unsyncByteArrayOutputStream);
132                                    }
133                                    else {
134                                            _servletOutputStream = _createGZipServletOutputStream(
135                                                    _response.getOutputStream());
136                                    }
137                            }
138                    }
139    
140                    return _servletOutputStream;
141            }
142    
143            @Override
144            public PrintWriter getWriter() throws IOException {
145                    if (_printWriter != null) {
146                            return _printWriter;
147                    }
148    
149                    if (_servletOutputStream != null) {
150                            throw new IllegalStateException();
151                    }
152    
153                    if (_log.isWarnEnabled()) {
154                            _log.warn("Use getOutputStream for optimum performance");
155                    }
156    
157                    _servletOutputStream = getOutputStream();
158    
159                    _printWriter = UnsyncPrintWriterPool.borrow(
160                            _servletOutputStream, getCharacterEncoding());
161    
162                    return _printWriter;
163            }
164    
165            @Override
166            public void setContentLength(int contentLength) {
167            }
168    
169            @Override
170            public void setHeader(String name, String value) {
171                    if (HttpHeaders.CONTENT_LENGTH.equals(name)) {
172                            return;
173                    }
174    
175                    super.setHeader(name, value);
176            }
177    
178            private ServletOutputStream _createGZipServletOutputStream(
179                            OutputStream outputStream)
180                    throws IOException {
181    
182                    GZIPOutputStream gzipOutputStream = new GZIPOutputStream(outputStream) {
183    
184                            {
185                                    def.setLevel(PropsValues.GZIP_COMPRESSION_LEVEL);
186                            }
187    
188                    };
189    
190                    return new ServletOutputStreamAdapter(gzipOutputStream);
191            }
192    
193            private boolean _isGZipContentType() {
194                    String contentType = getContentType();
195    
196                    if (contentType != null) {
197                            if (contentType.equals(ContentTypes.APPLICATION_GZIP) ||
198                                    contentType.equals(ContentTypes.APPLICATION_X_GZIP)) {
199    
200                                    return true;
201                            }
202                    }
203    
204                    return false;
205            }
206    
207            private static final String _GZIP = "gzip";
208    
209            private static final Log _log = LogFactoryUtil.getLog(GZipResponse.class);
210    
211            private final boolean _firefox;
212            private PrintWriter _printWriter;
213            private final HttpServletResponse _response;
214            private ServletOutputStream _servletOutputStream;
215            private UnsyncByteArrayOutputStream _unsyncByteArrayOutputStream;
216    
217    }