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