1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.servlets;
20
21import java.io.IOException;
22import java.io.OutputStream;
23import java.io.OutputStreamWriter;
24import java.io.PrintWriter;
25import java.io.UnsupportedEncodingException;
26import java.util.zip.Deflater;
27import java.util.zip.DeflaterOutputStream;
28import java.util.zip.GZIPOutputStream;
29
30import javax.servlet.FilterConfig;
31import javax.servlet.ServletException;
32import javax.servlet.http.HttpServletRequest;
33import javax.servlet.http.HttpServletResponse;
34
35import org.eclipse.jetty.http.gzip.CompressedResponseWrapper;
36import org.eclipse.jetty.http.gzip.AbstractCompressedStream;
37import org.eclipse.jetty.io.UncheckedPrintWriter;
38
39/* ------------------------------------------------------------ */
40/** Includable GZip Filter.
41 * This extension to the {@link GzipFilter} that uses Jetty features to allow
42 * headers to be set during calls to
43 * {@link javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}.
44 * This allows the gzip filter to function correct during includes and to make a decision to gzip or not
45 * at the time the buffer fills and on the basis of all response headers.
46 *
47 * If the init parameter "uncheckedPrintWriter" is set to "true", then the PrintWriter used by
48 * the wrapped getWriter will be {@link UncheckedPrintWriter}.
49 *
50 */
51public class IncludableGzipFilter extends GzipFilter
52{
53    boolean _uncheckedPrintWriter=false;
54
55    @Override
56    public void init(FilterConfig filterConfig) throws ServletException
57    {
58        super.init(filterConfig);
59
60        String tmp=filterConfig.getInitParameter("uncheckedPrintWriter");
61        if (tmp!=null)
62            _uncheckedPrintWriter=Boolean.valueOf(tmp).booleanValue();
63    }
64
65    /* ------------------------------------------------------------ */
66    /**
67     * @see org.eclipse.jetty.servlets.GzipFilter#createWrappedResponse(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.String)
68     */
69    @Override
70    protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType)
71    {
72        CompressedResponseWrapper wrappedResponse = null;
73        if (compressionType==null)
74        {
75            wrappedResponse = new IncludableResponseWrapper(request,response)
76            {
77                @Override
78                protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
79                {
80                    return new AbstractCompressedStream(null,request,this,_vary)
81                    {
82                        @Override
83                        protected DeflaterOutputStream createStream() throws IOException
84                        {
85                            return null;
86                        }
87                    };
88                }
89            };
90        }
91        else if (compressionType.equals(GZIP))
92        {
93            wrappedResponse = new IncludableResponseWrapper(request,response)
94            {
95                @Override
96                protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
97                {
98                    return new AbstractCompressedStream(compressionType,request,this,_vary)
99                    {
100                        @Override
101                        protected DeflaterOutputStream createStream() throws IOException
102                        {
103                            return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
104                        }
105                    };
106                }
107            };
108        }
109        else if (compressionType.equals(DEFLATE))
110        {
111            wrappedResponse = new IncludableResponseWrapper(request,response)
112            {
113                @Override
114                protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
115                {
116                    return new AbstractCompressedStream(compressionType,request,this,_vary)
117                    {
118                        @Override
119                        protected DeflaterOutputStream createStream() throws IOException
120                        {
121                            return new DeflaterOutputStream(_response.getOutputStream(),new Deflater(_deflateCompressionLevel, _deflateNoWrap));
122                        }
123                    };
124                }
125            };
126        }
127        else
128        {
129            throw new IllegalStateException(compressionType + " not supported");
130        }
131        configureWrappedResponse(wrappedResponse);
132        return wrappedResponse;
133    }
134
135
136    // Extend CompressedResponseWrapper to be able to set headers during include and to create unchecked printwriters
137    private abstract class IncludableResponseWrapper extends CompressedResponseWrapper
138    {
139        public IncludableResponseWrapper(HttpServletRequest request, HttpServletResponse response)
140        {
141            super(request,response);
142        }
143
144        @Override
145        public void setHeader(String name,String value)
146        {
147            super.setHeader(name,value);
148            HttpServletResponse response = (HttpServletResponse)getResponse();
149            if (!response.containsHeader(name))
150                response.setHeader("org.eclipse.jetty.server.include."+name,value);
151        }
152
153        @Override
154        public void addHeader(String name, String value)
155        {
156            super.addHeader(name, value);
157            HttpServletResponse response = (HttpServletResponse)getResponse();
158            if (!response.containsHeader(name))
159                setHeader(name,value);
160        }
161
162        @Override
163        protected PrintWriter newWriter(OutputStream out, String encoding) throws UnsupportedEncodingException
164        {
165            if (_uncheckedPrintWriter)
166                return encoding == null?new UncheckedPrintWriter(out):new UncheckedPrintWriter(new OutputStreamWriter(out,encoding));
167            return super.newWriter(out,encoding);
168        }
169    }
170
171}
172