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.util.HashSet;
21 import java.util.Set;
22 import java.util.StringTokenizer;
23 import java.util.zip.GZIPOutputStream;
24
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()))
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 wrappedResponse.noGzip();
138 throw e;
139 }
140 finally
141 {
142 if (exceptional && !response.isCommitted())
143 {
144 wrappedResponse.resetBuffer();
145 wrappedResponse.noGzip();
146 }
147 else
148 wrappedResponse.finish();
149 }
150 }
151 else
152 {
153 super.doFilter(request,response,chain);
154 }
155 }
156
157 protected GZIPResponseWrapper newGZIPResponseWrapper(HttpServletRequest request, HttpServletResponse response)
158 {
159 return new GZIPResponseWrapper(request,response);
160 }
161
162
163 public class GZIPResponseWrapper extends HttpServletResponseWrapper
164 {
165 HttpServletRequest _request;
166 boolean _noGzip;
167 PrintWriter _writer;
168 GzipStream _gzStream;
169 long _contentLength=-1;
170
171 public GZIPResponseWrapper(HttpServletRequest request, HttpServletResponse response)
172 {
173 super(response);
174 _request=request;
175 }
176
177 public void setContentType(String ct)
178 {
179 super.setContentType(ct);
180 int colon=ct.indexOf(";");
181 if (colon>0)
182 ct=ct.substring(0,colon);
183
184 if ((_gzStream==null || _gzStream._out==null) &&
185 (_mimeTypes==null && "application/gzip".equalsIgnoreCase(ct) ||
186 _mimeTypes!=null && !_mimeTypes.contains(StringUtil.asciiToLowerCase(ct))))
187 {
188 noGzip();
189 }
190 }
191
192
193 public void setStatus(int sc, String sm)
194 {
195 super.setStatus(sc,sm);
196 if (sc<200||sc>=300)
197 noGzip();
198 }
199
200 public void setStatus(int sc)
201 {
202 super.setStatus(sc);
203 if (sc<200||sc>=300)
204 noGzip();
205 }
206
207 public void setContentLength(int length)
208 {
209 _contentLength=length;
210 if (_gzStream!=null)
211 _gzStream.setContentLength(length);
212 }
213
214 public void addHeader(String name, String value)
215 {
216 if ("content-length".equalsIgnoreCase(name))
217 {
218 _contentLength=Long.parseLong(value);
219 if (_gzStream!=null)
220 _gzStream.setContentLength(_contentLength);
221 }
222 else if ("content-type".equalsIgnoreCase(name))
223 {
224 setContentType(value);
225 }
226 else if ("content-encoding".equalsIgnoreCase(name))
227 {
228 super.addHeader(name,value);
229 if (!isCommitted())
230 {
231 noGzip();
232 }
233 }
234 else
235 super.addHeader(name,value);
236 }
237
238 public void setHeader(String name, String value)
239 {
240 if ("content-length".equalsIgnoreCase(name))
241 {
242 _contentLength=Long.parseLong(value);
243 if (_gzStream!=null)
244 _gzStream.setContentLength(_contentLength);
245 }
246 else if ("content-type".equalsIgnoreCase(name))
247 {
248 setContentType(value);
249 }
250 else if ("content-encoding".equalsIgnoreCase(name))
251 {
252 super.setHeader(name,value);
253 if (!isCommitted())
254 {
255 noGzip();
256 }
257 }
258 else
259 super.setHeader(name,value);
260 }
261
262 public void setIntHeader(String name, int value)
263 {
264 if ("content-length".equalsIgnoreCase(name))
265 {
266 _contentLength=value;
267 if (_gzStream!=null)
268 _gzStream.setContentLength(_contentLength);
269 }
270 else
271 super.setIntHeader(name,value);
272 }
273
274 public void flushBuffer() throws IOException
275 {
276 if (_writer!=null)
277 _writer.flush();
278 if (_gzStream!=null)
279 _gzStream.finish();
280 else
281 getResponse().flushBuffer();
282 }
283
284 public void reset()
285 {
286 super.reset();
287 if (_gzStream!=null)
288 _gzStream.resetBuffer();
289 _writer=null;
290 _gzStream=null;
291 _noGzip=false;
292 _contentLength=-1;
293 }
294
295 public void resetBuffer()
296 {
297 super.resetBuffer();
298 if (_gzStream!=null)
299 _gzStream.resetBuffer();
300 _writer=null;
301 _gzStream=null;
302 }
303
304 public void sendError(int sc, String msg) throws IOException
305 {
306 resetBuffer();
307 super.sendError(sc,msg);
308 }
309
310 public void sendError(int sc) throws IOException
311 {
312 resetBuffer();
313 super.sendError(sc);
314 }
315
316 public void sendRedirect(String location) throws IOException
317 {
318 resetBuffer();
319 super.sendRedirect(location);
320 }
321
322 public ServletOutputStream getOutputStream() throws IOException
323 {
324 if (_gzStream==null)
325 {
326 if (getResponse().isCommitted() || _noGzip)
327 return getResponse().getOutputStream();
328
329 _gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
330 }
331 else if (_writer!=null)
332 throw new IllegalStateException("getWriter() called");
333
334 return _gzStream;
335 }
336
337 public PrintWriter getWriter() throws IOException
338 {
339 if (_writer==null)
340 {
341 if (_gzStream!=null)
342 throw new IllegalStateException("getOutputStream() called");
343
344 if (getResponse().isCommitted() || _noGzip)
345 return getResponse().getWriter();
346
347 _gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
348 String encoding = getCharacterEncoding();
349 _writer=encoding==null?new PrintWriter(_gzStream):new PrintWriter(new OutputStreamWriter(_gzStream,encoding));
350 }
351 return _writer;
352 }
353
354 void noGzip()
355 {
356 _noGzip=true;
357 if (_gzStream!=null)
358 {
359 try
360 {
361 _gzStream.doNotGzip();
362 }
363 catch (IOException e)
364 {
365 throw new IllegalStateException();
366 }
367 }
368 }
369
370 void finish() throws IOException
371 {
372 if (_writer!=null)
373 _writer.flush();
374 if (_gzStream!=null)
375 _gzStream.finish();
376 }
377
378 protected GzipStream newGzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException
379 {
380 return new GzipStream(request,response,contentLength,bufferSize,minGzipSize);
381 }
382 }
383
384
385 public static class GzipStream extends ServletOutputStream
386 {
387 protected HttpServletRequest _request;
388 protected HttpServletResponse _response;
389 protected OutputStream _out;
390 protected ByteArrayOutputStream2 _bOut;
391 protected GZIPOutputStream _gzOut;
392 protected boolean _closed;
393 protected int _bufferSize;
394 protected int _minGzipSize;
395 protected long _contentLength;
396
397 public GzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException
398 {
399 _request=request;
400 _response=response;
401 _contentLength=contentLength;
402 _bufferSize=bufferSize;
403 _minGzipSize=minGzipSize;
404 if (minGzipSize==0)
405 doGzip();
406 }
407
408 public void resetBuffer()
409 {
410 _closed=false;
411 _out=null;
412 _bOut=null;
413 if (_gzOut!=null && !_response.isCommitted())
414 _response.setHeader("Content-Encoding",null);
415 _gzOut=null;
416 }
417
418 public void setContentLength(long length)
419 {
420 _contentLength=length;
421 }
422
423 public void flush() throws IOException
424 {
425 if (_out==null || _bOut!=null)
426 {
427 if (_contentLength>0 && _contentLength<_minGzipSize)
428 doNotGzip();
429 else
430 doGzip();
431 }
432
433 _out.flush();
434 }
435
436 public void close() throws IOException
437 {
438 if (_request.getAttribute("javax.servlet.include.request_uri")!=null)
439 flush();
440 else
441 {
442 if (_bOut!=null)
443 {
444 if (_contentLength<0)
445 _contentLength=_bOut.getCount();
446 if (_contentLength<_minGzipSize)
447 doNotGzip();
448 else
449 doGzip();
450 }
451 else if (_out==null)
452 {
453 doNotGzip();
454 }
455
456 if (_gzOut!=null)
457 _gzOut.finish();
458 _out.close();
459 _closed=true;
460 }
461 }
462
463 public void finish() throws IOException
464 {
465 if (!_closed)
466 {
467 if (_out==null || _bOut!=null)
468 {
469 if (_contentLength>0 && _contentLength<_minGzipSize)
470 doNotGzip();
471 else
472 doGzip();
473 }
474
475 if (_gzOut!=null)
476 _gzOut.finish();
477 }
478 }
479
480 public void write(int b) throws IOException
481 {
482 checkOut(1);
483 _out.write(b);
484 }
485
486 public void write(byte b[]) throws IOException
487 {
488 checkOut(b.length);
489 _out.write(b);
490 }
491
492 public void write(byte b[], int off, int len) throws IOException
493 {
494 checkOut(len);
495 _out.write(b,off,len);
496 }
497
498 protected boolean setContentEncodingGzip()
499 {
500 _response.setHeader("Content-Encoding", "gzip");
501 return _response.containsHeader("Content-Encoding");
502 }
503
504 public void doGzip() throws IOException
505 {
506 if (_gzOut==null)
507 {
508 if (_response.isCommitted())
509 throw new IllegalStateException();
510
511 if (setContentEncodingGzip())
512 {
513 _out=_gzOut=new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
514
515 if (_bOut!=null)
516 {
517 _out.write(_bOut.getBuf(),0,_bOut.getCount());
518 _bOut=null;
519 }
520 }
521 else
522 doNotGzip();
523 }
524 }
525
526 public void doNotGzip() throws IOException
527 {
528 if (_gzOut!=null)
529 throw new IllegalStateException();
530 if (_out==null || _bOut!=null )
531 {
532 _out=_response.getOutputStream();
533 if (_contentLength>=0)
534 {
535 if(_contentLength<Integer.MAX_VALUE)
536 _response.setContentLength((int)_contentLength);
537 else
538 _response.setHeader("Content-Length",Long.toString(_contentLength));
539 }
540
541 if (_bOut!=null)
542 _out.write(_bOut.getBuf(),0,_bOut.getCount());
543 _bOut=null;
544 }
545 }
546
547 private void checkOut(int length) throws IOException
548 {
549 if (_closed)
550 {
551 new Throwable().printStackTrace();
552 throw new IOException("CLOSED");
553 }
554
555 if (_out==null)
556 {
557 if (_response.isCommitted() || (_contentLength>=0 && _contentLength<_minGzipSize))
558 doNotGzip();
559 else if (length>_minGzipSize)
560 doGzip();
561 else
562 _out=_bOut=new ByteArrayOutputStream2(_bufferSize);
563 }
564 else if (_bOut!=null)
565 {
566 if (_response.isCommitted() || (_contentLength>=0 && _contentLength<_minGzipSize))
567 doNotGzip();
568 else if (length>=(_bOut.size()-_bOut.getCount()))
569 doGzip();
570 }
571 }
572 }
573 }