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