1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.mortbay.servlet;
15
16 import java.io.IOException;
17 import java.io.OutputStream;
18 import java.io.OutputStreamWriter;
19 import java.io.PrintWriter;
20 import java.io.UnsupportedEncodingException;
21 import java.util.HashSet;
22 import java.util.Set;
23 import java.util.StringTokenizer;
24 import java.util.zip.GZIPOutputStream;
25 import javax.servlet.FilterChain;
26 import javax.servlet.FilterConfig;
27 import javax.servlet.ServletException;
28 import javax.servlet.ServletOutputStream;
29 import javax.servlet.ServletRequest;
30 import javax.servlet.ServletResponse;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletResponse;
33 import javax.servlet.http.HttpServletResponseWrapper;
34
35 import org.mortbay.util.ByteArrayOutputStream2;
36 import org.mortbay.util.StringUtil;
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class GzipFilter extends UserAgentFilter
64 {
65 protected Set _mimeTypes;
66 protected int _bufferSize=8192;
67 protected int _minGzipSize=0;
68 protected Set _excluded;
69
70 public void init(FilterConfig filterConfig) throws ServletException
71 {
72 super.init(filterConfig);
73
74 String tmp=filterConfig.getInitParameter("bufferSize");
75 if (tmp!=null)
76 _bufferSize=Integer.parseInt(tmp);
77
78 tmp=filterConfig.getInitParameter("minGzipSize");
79 if (tmp!=null)
80 _minGzipSize=Integer.parseInt(tmp);
81
82 tmp=filterConfig.getInitParameter("mimeTypes");
83 if (tmp!=null)
84 {
85 _mimeTypes=new HashSet();
86 StringTokenizer tok = new StringTokenizer(tmp,",",false);
87 while (tok.hasMoreTokens())
88 _mimeTypes.add(tok.nextToken());
89 }
90
91 tmp=filterConfig.getInitParameter("excludedAgents");
92 if (tmp!=null)
93 {
94 _excluded=new HashSet();
95 StringTokenizer tok = new StringTokenizer(tmp,",",false);
96 while (tok.hasMoreTokens())
97 _excluded.add(tok.nextToken());
98 }
99 }
100
101 public void destroy()
102 {
103 }
104
105 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
106 throws IOException, ServletException
107 {
108 HttpServletRequest request=(HttpServletRequest)req;
109 HttpServletResponse response=(HttpServletResponse)res;
110
111 String ae = request.getHeader("accept-encoding");
112 Boolean gzip=(Boolean)request.getAttribute("GzipFilter");
113 if (ae != null && ae.indexOf("gzip")>=0 && !response.containsHeader("Content-Encoding") &&
114 (gzip==null || gzip.booleanValue()) && !"HEAD".equalsIgnoreCase(request.getMethod()))
115 {
116 if (_excluded!=null)
117 {
118 String ua=getUserAgent(request);
119 if (_excluded.contains(ua))
120 {
121 super.doFilter(request,response,chain);
122 return;
123 }
124 }
125
126 GZIPResponseWrapper wrappedResponse=newGZIPResponseWrapper(request,response);
127
128 boolean exceptional=true;
129 try
130 {
131 super.doFilter(request,wrappedResponse,chain);
132 exceptional=false;
133 }
134 catch(RuntimeException e)
135 {
136 request.setAttribute("GzipFilter",Boolean.FALSE);
137 if (!response.isCommitted())
138 response.reset();
139 throw e;
140 }
141 finally
142 {
143 if (exceptional && !response.isCommitted())
144 {
145 wrappedResponse.resetBuffer();
146 wrappedResponse.noGzip();
147 }
148 else
149 wrappedResponse.finish();
150 }
151 }
152 else
153 {
154 super.doFilter(request,response,chain);
155 }
156 }
157
158 protected GZIPResponseWrapper newGZIPResponseWrapper(HttpServletRequest request, HttpServletResponse response)
159 {
160 return new GZIPResponseWrapper(request,response);
161 }
162
163
164
165
166 protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
167 {
168 return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
169 }
170
171 public class GZIPResponseWrapper extends HttpServletResponseWrapper
172 {
173 HttpServletRequest _request;
174 boolean _noGzip;
175 PrintWriter _writer;
176 GzipStream _gzStream;
177 long _contentLength=-1;
178
179 public GZIPResponseWrapper(HttpServletRequest request, HttpServletResponse response)
180 {
181 super(response);
182 _request=request;
183 }
184
185 public void setContentType(String ct)
186 {
187 super.setContentType(ct);
188
189 if (ct!=null)
190 {
191 int colon=ct.indexOf(";");
192 if (colon>0)
193 ct=ct.substring(0,colon);
194 }
195
196 if ((_gzStream==null || _gzStream._out==null) &&
197 (_mimeTypes==null && "application/gzip".equalsIgnoreCase(ct) ||
198 _mimeTypes!=null && (ct==null||!_mimeTypes.contains(StringUtil.asciiToLowerCase(ct)))))
199 {
200 noGzip();
201 }
202 }
203
204
205 public void setStatus(int sc, String sm)
206 {
207 super.setStatus(sc,sm);
208 if (sc<200||sc>=300)
209 noGzip();
210 }
211
212 public void setStatus(int sc)
213 {
214 super.setStatus(sc);
215 if (sc<200||sc>=300)
216 noGzip();
217 }
218
219 public void setContentLength(int length)
220 {
221 _contentLength=length;
222 if (_gzStream!=null)
223 _gzStream.setContentLength(length);
224 }
225
226 public void addHeader(String name, String value)
227 {
228 if ("content-length".equalsIgnoreCase(name))
229 {
230 _contentLength=Long.parseLong(value);
231 if (_gzStream!=null)
232 _gzStream.setContentLength(_contentLength);
233 }
234 else if ("content-type".equalsIgnoreCase(name))
235 {
236 setContentType(value);
237 }
238 else if ("content-encoding".equalsIgnoreCase(name))
239 {
240 super.addHeader(name,value);
241 if (!isCommitted())
242 {
243 noGzip();
244 }
245 }
246 else
247 super.addHeader(name,value);
248 }
249
250 public void setHeader(String name, String value)
251 {
252 if ("content-length".equalsIgnoreCase(name))
253 {
254 _contentLength=Long.parseLong(value);
255 if (_gzStream!=null)
256 _gzStream.setContentLength(_contentLength);
257 }
258 else if ("content-type".equalsIgnoreCase(name))
259 {
260 setContentType(value);
261 }
262 else if ("content-encoding".equalsIgnoreCase(name))
263 {
264 super.setHeader(name,value);
265 if (!isCommitted())
266 {
267 noGzip();
268 }
269 }
270 else
271 super.setHeader(name,value);
272 }
273
274 public void setIntHeader(String name, int value)
275 {
276 if ("content-length".equalsIgnoreCase(name))
277 {
278 _contentLength=value;
279 if (_gzStream!=null)
280 _gzStream.setContentLength(_contentLength);
281 }
282 else
283 super.setIntHeader(name,value);
284 }
285
286 public void flushBuffer() throws IOException
287 {
288 if (_writer!=null)
289 _writer.flush();
290 if (_gzStream!=null)
291 _gzStream.finish();
292 else
293 getResponse().flushBuffer();
294 }
295
296 public void reset()
297 {
298 super.reset();
299 if (_gzStream!=null)
300 _gzStream.resetBuffer();
301 _writer=null;
302 _gzStream=null;
303 _noGzip=false;
304 _contentLength=-1;
305 }
306
307 public void resetBuffer()
308 {
309 super.resetBuffer();
310 if (_gzStream!=null)
311 _gzStream.resetBuffer();
312 _writer=null;
313 _gzStream=null;
314 }
315
316 public void sendError(int sc, String msg) throws IOException
317 {
318 resetBuffer();
319 super.sendError(sc,msg);
320 }
321
322 public void sendError(int sc) throws IOException
323 {
324 resetBuffer();
325 super.sendError(sc);
326 }
327
328 public void sendRedirect(String location) throws IOException
329 {
330 resetBuffer();
331 super.sendRedirect(location);
332 }
333
334 public ServletOutputStream getOutputStream() throws IOException
335 {
336 if (_gzStream==null)
337 {
338 if (getResponse().isCommitted() || _noGzip)
339 return getResponse().getOutputStream();
340
341 _gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
342 }
343 else if (_writer!=null)
344 throw new IllegalStateException("getWriter() called");
345
346 return _gzStream;
347 }
348
349 public PrintWriter getWriter() throws IOException
350 {
351 if (_writer==null)
352 {
353 if (_gzStream!=null)
354 throw new IllegalStateException("getOutputStream() called");
355
356 if (getResponse().isCommitted() || _noGzip)
357 return getResponse().getWriter();
358
359 _gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
360 _writer=newWriter(_gzStream,getCharacterEncoding());
361 }
362 return _writer;
363 }
364
365 void noGzip()
366 {
367 _noGzip=true;
368 if (_gzStream!=null)
369 {
370 try
371 {
372 _gzStream.doNotGzip();
373 }
374 catch (IOException e)
375 {
376 throw new IllegalStateException();
377 }
378 }
379 }
380
381 void finish() throws IOException
382 {
383 if (_writer!=null && !_gzStream._closed)
384 _writer.flush();
385 if (_gzStream!=null)
386 _gzStream.finish();
387 }
388
389 protected GzipStream newGzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException
390 {
391 return new GzipStream(request,response,contentLength,bufferSize,minGzipSize);
392 }
393 }
394
395
396 public static class GzipStream extends ServletOutputStream
397 {
398 protected HttpServletRequest _request;
399 protected HttpServletResponse _response;
400 protected OutputStream _out;
401 protected ByteArrayOutputStream2 _bOut;
402 protected GZIPOutputStream _gzOut;
403 protected boolean _closed;
404 protected int _bufferSize;
405 protected int _minGzipSize;
406 protected long _contentLength;
407
408 public GzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException
409 {
410 _request=request;
411 _response=response;
412 _contentLength=contentLength;
413 _bufferSize=bufferSize;
414 _minGzipSize=minGzipSize;
415 if (minGzipSize==0)
416 doGzip();
417 }
418
419 public void resetBuffer()
420 {
421 _closed=false;
422 _out=null;
423 _bOut=null;
424 if (_gzOut!=null && !_response.isCommitted())
425 _response.setHeader("Content-Encoding",null);
426 _gzOut=null;
427 }
428
429 public void setContentLength(long length)
430 {
431 _contentLength=length;
432 }
433
434 public void flush() throws IOException
435 {
436 if (_out==null || _bOut!=null)
437 {
438 if (_contentLength>0 && _contentLength<_minGzipSize)
439 doNotGzip();
440 else
441 doGzip();
442 }
443
444 _out.flush();
445 }
446
447 public void close() throws IOException
448 {
449 if (_request.getAttribute("javax.servlet.include.request_uri")!=null)
450 flush();
451 else
452 {
453 if (_bOut!=null)
454 {
455 if (_contentLength<0)
456 _contentLength=_bOut.getCount();
457 if (_contentLength<_minGzipSize)
458 doNotGzip();
459 else
460 doGzip();
461 }
462 else if (_out==null)
463 {
464 doNotGzip();
465 }
466
467 if (_gzOut!=null)
468 _gzOut.close();
469 else
470 _out.close();
471 _closed=true;
472 }
473 }
474
475 public void finish() throws IOException
476 {
477 if (!_closed)
478 {
479 if (_out==null || _bOut!=null)
480 {
481 if (_contentLength>0 && _contentLength<_minGzipSize)
482 doNotGzip();
483 else
484 doGzip();
485 }
486
487 if (_gzOut!=null && !_closed)
488 {
489 _closed=true;
490 _gzOut.close();
491 }
492 }
493 }
494
495 public void write(int b) throws IOException
496 {
497 checkOut(1);
498 _out.write(b);
499 }
500
501 public void write(byte b[]) throws IOException
502 {
503 checkOut(b.length);
504 _out.write(b);
505 }
506
507 public void write(byte b[], int off, int len) throws IOException
508 {
509 checkOut(len);
510 _out.write(b,off,len);
511 }
512
513 protected boolean setContentEncodingGzip()
514 {
515 _response.setHeader("Content-Encoding", "gzip");
516 return _response.containsHeader("Content-Encoding");
517 }
518
519 public void doGzip() throws IOException
520 {
521 if (_gzOut==null)
522 {
523 if (_response.isCommitted())
524 throw new IllegalStateException();
525
526 if (setContentEncodingGzip())
527 {
528 _out=_gzOut=new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
529
530 if (_bOut!=null)
531 {
532 _out.write(_bOut.getBuf(),0,_bOut.getCount());
533 _bOut=null;
534 }
535 }
536 else
537 doNotGzip();
538 }
539 }
540
541 public void doNotGzip() throws IOException
542 {
543 if (_gzOut!=null)
544 throw new IllegalStateException();
545 if (_out==null || _bOut!=null )
546 {
547 _out=_response.getOutputStream();
548 if (_contentLength>=0)
549 {
550 if(_contentLength<Integer.MAX_VALUE)
551 _response.setContentLength((int)_contentLength);
552 else
553 _response.setHeader("Content-Length",Long.toString(_contentLength));
554 }
555
556 if (_bOut!=null)
557 _out.write(_bOut.getBuf(),0,_bOut.getCount());
558 _bOut=null;
559 }
560 }
561
562 private void checkOut(int length) throws IOException
563 {
564 if (_closed)
565 throw new IOException("CLOSED");
566
567 if (_out==null)
568 {
569 if (_response.isCommitted() || (_contentLength>=0 && _contentLength<_minGzipSize))
570 doNotGzip();
571 else if (length>_minGzipSize)
572 doGzip();
573 else
574 _out=_bOut=new ByteArrayOutputStream2(_bufferSize);
575 }
576 else if (_bOut!=null)
577 {
578 if (_response.isCommitted() || (_contentLength>=0 && _contentLength<_minGzipSize))
579 doNotGzip();
580 else if (length>=(_bOut.getBuf().length-_bOut.getCount()))
581 doGzip();
582 }
583 }
584 }
585 }