1 // ========================================================================
2 // Copyright (c) Webtide LLC
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
12 // ========================================================================
13
14
15 package org.eclipse.jetty.http.gzip;
16
17 import java.io.IOException;
18 import java.io.OutputStream;
19 import java.io.OutputStreamWriter;
20 import java.io.PrintWriter;
21 import java.io.UnsupportedEncodingException;
22 import java.util.Set;
23
24 import javax.servlet.ServletOutputStream;
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27 import javax.servlet.http.HttpServletResponseWrapper;
28
29 import org.eclipse.jetty.util.StringUtil;
30
31
32 /* ------------------------------------------------------------ */
33 /**
34 */
35 public class GzipResponseWrapper extends HttpServletResponseWrapper
36 {
37 private HttpServletRequest _request;
38 private Set<String> _mimeTypes;
39 private int _bufferSize=8192;
40 private int _minGzipSize=256;
41
42 private PrintWriter _writer;
43 private GzipStream _gzStream;
44 private long _contentLength=-1;
45 private boolean _noGzip;
46
47 /**
48 * Instantiates a new gzip response wrapper.
49 *
50 * @param request the request
51 * @param response the response
52 */
53 public GzipResponseWrapper(HttpServletRequest request, HttpServletResponse response)
54 {
55 super(response);
56 _request=request;
57 }
58
59 /* ------------------------------------------------------------ */
60 /**
61 * Sets the mime types.
62 *
63 * @param mimeTypes the new mime types
64 */
65 public void setMimeTypes(Set<String> mimeTypes)
66 {
67 _mimeTypes = mimeTypes;
68 }
69
70 /* ------------------------------------------------------------ */
71 /**
72 * @see javax.servlet.ServletResponseWrapper#setBufferSize(int)
73 */
74 public void setBufferSize(int bufferSize)
75 {
76 _bufferSize = bufferSize;
77 }
78
79 /* ------------------------------------------------------------ */
80 /**
81 * Sets the min gzip size.
82 *
83 * @param minGzipSize the new min gzip size
84 */
85 public void setMinGzipSize(int minGzipSize)
86 {
87 _minGzipSize = minGzipSize;
88 }
89
90 /* ------------------------------------------------------------ */
91 /**
92 * @see javax.servlet.ServletResponseWrapper#setContentType(java.lang.String)
93 */
94 public void setContentType(String ct)
95 {
96 super.setContentType(ct);
97
98 if (ct!=null)
99 {
100 int colon=ct.indexOf(";");
101 if (colon>0)
102 ct=ct.substring(0,colon);
103 }
104
105 if ((_gzStream==null || _gzStream._out==null) &&
106 (_mimeTypes==null && "application/gzip".equalsIgnoreCase(ct) ||
107 _mimeTypes!=null && (ct==null||!_mimeTypes.contains(StringUtil.asciiToLowerCase(ct)))))
108 {
109 noGzip();
110 }
111 }
112
113 /* ------------------------------------------------------------ */
114 /**
115 * @see javax.servlet.http.HttpServletResponseWrapper#setStatus(int, java.lang.String)
116 */
117 public void setStatus(int sc, String sm)
118 {
119 super.setStatus(sc,sm);
120 if (sc<200||sc>=300)
121 noGzip();
122 }
123
124 /* ------------------------------------------------------------ */
125 /**
126 * @see javax.servlet.http.HttpServletResponseWrapper#setStatus(int)
127 */
128 public void setStatus(int sc)
129 {
130 super.setStatus(sc);
131 if (sc<200||sc>=300)
132 noGzip();
133 }
134
135 /* ------------------------------------------------------------ */
136 /**
137 * @see javax.servlet.ServletResponseWrapper#setContentLength(int)
138 */
139 public void setContentLength(int length)
140 {
141 _contentLength=length;
142 if (_gzStream!=null)
143 _gzStream.setContentLength(length);
144 }
145
146 /* ------------------------------------------------------------ */
147 /**
148 * @see javax.servlet.http.HttpServletResponseWrapper#addHeader(java.lang.String, java.lang.String)
149 */
150 public void addHeader(String name, String value)
151 {
152 if ("content-length".equalsIgnoreCase(name))
153 {
154 _contentLength=Long.parseLong(value);
155 if (_gzStream!=null)
156 _gzStream.setContentLength(_contentLength);
157 }
158 else if ("content-type".equalsIgnoreCase(name))
159 {
160 setContentType(value);
161 }
162 else if ("content-encoding".equalsIgnoreCase(name))
163 {
164 super.addHeader(name,value);
165 if (!isCommitted())
166 {
167 noGzip();
168 }
169 }
170 else
171 super.addHeader(name,value);
172 }
173
174 /* ------------------------------------------------------------ */
175 /**
176 * @see javax.servlet.http.HttpServletResponseWrapper#setHeader(java.lang.String, java.lang.String)
177 */
178 public void setHeader(String name, String value)
179 {
180 if ("content-length".equalsIgnoreCase(name))
181 {
182 _contentLength=Long.parseLong(value);
183 if (_gzStream!=null)
184 _gzStream.setContentLength(_contentLength);
185 }
186 else if ("content-type".equalsIgnoreCase(name))
187 {
188 setContentType(value);
189 }
190 else if ("content-encoding".equalsIgnoreCase(name))
191 {
192 super.setHeader(name,value);
193 if (!isCommitted())
194 {
195 noGzip();
196 }
197 }
198 else
199 super.setHeader(name,value);
200 }
201
202 /* ------------------------------------------------------------ */
203 /**
204 * @see javax.servlet.http.HttpServletResponseWrapper#setIntHeader(java.lang.String, int)
205 */
206 public void setIntHeader(String name, int value)
207 {
208 if ("content-length".equalsIgnoreCase(name))
209 {
210 _contentLength=value;
211 if (_gzStream!=null)
212 _gzStream.setContentLength(_contentLength);
213 }
214 else
215 super.setIntHeader(name,value);
216 }
217
218 /* ------------------------------------------------------------ */
219 /**
220 * @see javax.servlet.ServletResponseWrapper#flushBuffer()
221 */
222 public void flushBuffer() throws IOException
223 {
224 if (_writer!=null)
225 _writer.flush();
226 if (_gzStream!=null)
227 _gzStream.finish();
228 else
229 getResponse().flushBuffer();
230 }
231
232 /* ------------------------------------------------------------ */
233 /**
234 * @see javax.servlet.ServletResponseWrapper#reset()
235 */
236 public void reset()
237 {
238 super.reset();
239 if (_gzStream!=null)
240 _gzStream.resetBuffer();
241 _writer=null;
242 _gzStream=null;
243 _noGzip=false;
244 _contentLength=-1;
245 }
246
247 /* ------------------------------------------------------------ */
248 /**
249 * @see javax.servlet.ServletResponseWrapper#resetBuffer()
250 */
251 public void resetBuffer()
252 {
253 super.resetBuffer();
254 if (_gzStream!=null)
255 _gzStream.resetBuffer();
256 _writer=null;
257 _gzStream=null;
258 }
259
260 /* ------------------------------------------------------------ */
261 /**
262 * @see javax.servlet.http.HttpServletResponseWrapper#sendError(int, java.lang.String)
263 */
264 public void sendError(int sc, String msg) throws IOException
265 {
266 resetBuffer();
267 super.sendError(sc,msg);
268 }
269
270 /* ------------------------------------------------------------ */
271 /**
272 * @see javax.servlet.http.HttpServletResponseWrapper#sendError(int)
273 */
274 public void sendError(int sc) throws IOException
275 {
276 resetBuffer();
277 super.sendError(sc);
278 }
279
280 /* ------------------------------------------------------------ */
281 /**
282 * @see javax.servlet.http.HttpServletResponseWrapper#sendRedirect(java.lang.String)
283 */
284 public void sendRedirect(String location) throws IOException
285 {
286 resetBuffer();
287 super.sendRedirect(location);
288 }
289
290 /* ------------------------------------------------------------ */
291 /**
292 * @see javax.servlet.ServletResponseWrapper#getOutputStream()
293 */
294 public ServletOutputStream getOutputStream() throws IOException
295 {
296 if (_gzStream==null)
297 {
298 if (getResponse().isCommitted() || _noGzip)
299 return getResponse().getOutputStream();
300
301 _gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
302 }
303 else if (_writer!=null)
304 throw new IllegalStateException("getWriter() called");
305
306 return _gzStream;
307 }
308
309 /* ------------------------------------------------------------ */
310 /**
311 * @see javax.servlet.ServletResponseWrapper#getWriter()
312 */
313 public PrintWriter getWriter() throws IOException
314 {
315 if (_writer==null)
316 {
317 if (_gzStream!=null)
318 throw new IllegalStateException("getOutputStream() called");
319
320 if (getResponse().isCommitted() || _noGzip)
321 return getResponse().getWriter();
322
323 _gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
324 _writer=newWriter(_gzStream,getCharacterEncoding());
325 }
326 return _writer;
327 }
328
329 /* ------------------------------------------------------------ */
330 /**
331 * No gzip.
332 */
333 public void noGzip()
334 {
335 _noGzip=true;
336 if (_gzStream!=null)
337 {
338 try
339 {
340 _gzStream.doNotGzip();
341 }
342 catch (IOException e)
343 {
344 throw new IllegalStateException(e);
345 }
346 }
347 }
348
349 /* ------------------------------------------------------------ */
350 /**
351 * Finish.
352 *
353 * @throws IOException Signals that an I/O exception has occurred.
354 */
355 public void finish() throws IOException
356 {
357 if (_writer!=null && !_gzStream._closed)
358 _writer.flush();
359 if (_gzStream!=null)
360 _gzStream.finish();
361 }
362
363 /* ------------------------------------------------------------ */
364 /**
365 * Allows derived implementations to replace GzipStream implementation.
366 *
367 * @param request the request
368 * @param response the response
369 * @param contentLength the content length
370 * @param bufferSize the buffer size
371 * @param minGzipSize the min gzip size
372 * @return the gzip stream
373 * @throws IOException Signals that an I/O exception has occurred.
374 */
375 protected GzipStream newGzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException
376 {
377 return new GzipStream(request,response,contentLength,bufferSize,minGzipSize);
378 }
379
380 /* ------------------------------------------------------------ */
381 /**
382 * Allows derived implementations to replace PrintWriter implementation.
383 *
384 * @param out the out
385 * @param encoding the encoding
386 * @return the prints the writer
387 * @throws UnsupportedEncodingException the unsupported encoding exception
388 */
389 protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
390 {
391 return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
392 }
393 }
394