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 setHeader(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.setHeader(name,value);
221 if (!isCommitted())
222 {
223 noGzip();
224 }
225 }
226 else
227 super.setHeader(name,value);
228 }
229
230 public void setIntHeader(String name, int value)
231 {
232 if ("content-length".equalsIgnoreCase(name))
233 {
234 _contentLength=value;
235 if (_gzStream!=null)
236 _gzStream.setContentLength(_contentLength);
237 }
238 else
239 super.setIntHeader(name,value);
240 }
241
242 public void flushBuffer() throws IOException
243 {
244 if (_writer!=null)
245 _writer.flush();
246 if (_gzStream!=null)
247 _gzStream.finish();
248 else
249 getResponse().flushBuffer();
250 }
251
252 public void reset()
253 {
254 super.reset();
255 if (_gzStream!=null)
256 _gzStream.resetBuffer();
257 _writer=null;
258 _gzStream=null;
259 _noGzip=false;
260 _contentLength=-1;
261 }
262
263 public void resetBuffer()
264 {
265 super.resetBuffer();
266 if (_gzStream!=null)
267 _gzStream.resetBuffer();
268 _writer=null;
269 _gzStream=null;
270 }
271
272 public void sendError(int sc, String msg) throws IOException
273 {
274 resetBuffer();
275 super.sendError(sc,msg);
276 }
277
278 public void sendError(int sc) throws IOException
279 {
280 resetBuffer();
281 super.sendError(sc);
282 }
283
284 public void sendRedirect(String location) throws IOException
285 {
286 resetBuffer();
287 super.sendRedirect(location);
288 }
289
290 public ServletOutputStream getOutputStream() throws IOException
291 {
292 if (_gzStream==null)
293 {
294 if (getResponse().isCommitted() || _noGzip)
295 return getResponse().getOutputStream();
296
297 _gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
298 }
299 else if (_writer!=null)
300 throw new IllegalStateException("getWriter() called");
301
302 return _gzStream;
303 }
304
305 public PrintWriter getWriter() throws IOException
306 {
307 if (_writer==null)
308 {
309 if (_gzStream!=null)
310 throw new IllegalStateException("getOutputStream() called");
311
312 if (getResponse().isCommitted() || _noGzip)
313 return getResponse().getWriter();
314
315 _gzStream=newGzipStream(_request,(HttpServletResponse)getResponse(),_contentLength,_bufferSize,_minGzipSize);
316 String encoding = getCharacterEncoding();
317 _writer=encoding==null?new PrintWriter(_gzStream):new PrintWriter(new OutputStreamWriter(_gzStream,encoding));
318 }
319 return _writer;
320 }
321
322 void noGzip()
323 {
324 _noGzip=true;
325 if (_gzStream!=null)
326 {
327 try
328 {
329 _gzStream.doNotGzip();
330 }
331 catch (IOException e)
332 {
333 throw new IllegalStateException();
334 }
335 }
336 }
337
338 void finish() throws IOException
339 {
340 if (_writer!=null)
341 _writer.flush();
342 if (_gzStream!=null)
343 _gzStream.finish();
344 }
345
346 protected GzipStream newGzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException
347 {
348 return new GzipStream(request,response,contentLength,bufferSize,minGzipSize);
349 }
350 }
351
352
353 public static class GzipStream extends ServletOutputStream
354 {
355 protected HttpServletRequest _request;
356 protected HttpServletResponse _response;
357 protected OutputStream _out;
358 protected ByteArrayOutputStream2 _bOut;
359 protected GZIPOutputStream _gzOut;
360 protected boolean _closed;
361 protected int _bufferSize;
362 protected int _minGzipSize;
363 protected long _contentLength;
364
365 public GzipStream(HttpServletRequest request,HttpServletResponse response,long contentLength,int bufferSize, int minGzipSize) throws IOException
366 {
367 _request=request;
368 _response=response;
369 _contentLength=contentLength;
370 _bufferSize=bufferSize;
371 _minGzipSize=minGzipSize;
372 if (minGzipSize==0)
373 doGzip();
374 }
375
376 public void resetBuffer()
377 {
378 _closed=false;
379 _out=null;
380 _bOut=null;
381 if (_gzOut!=null && !_response.isCommitted())
382 _response.setHeader("Content-Encoding",null);
383 _gzOut=null;
384 }
385
386 public void setContentLength(long length)
387 {
388 _contentLength=length;
389 }
390
391 public void flush() throws IOException
392 {
393 if (_out==null || _bOut!=null)
394 {
395 if (_contentLength>0 && _contentLength<_minGzipSize)
396 doNotGzip();
397 else
398 doGzip();
399 }
400
401 _out.flush();
402 }
403
404 public void close() throws IOException
405 {
406 if (_request.getAttribute("javax.servlet.include.request_uri")!=null)
407 flush();
408 else
409 {
410 if (_bOut!=null)
411 {
412 if (_contentLength<0)
413 _contentLength=_bOut.getCount();
414 if (_contentLength<_minGzipSize)
415 doNotGzip();
416 else
417 doGzip();
418 }
419 else if (_out==null)
420 {
421 doNotGzip();
422 }
423
424 if (_gzOut!=null)
425 _gzOut.finish();
426 _out.close();
427 _closed=true;
428 }
429 }
430
431 public void finish() throws IOException
432 {
433 if (!_closed)
434 {
435 if (_out==null || _bOut!=null)
436 {
437 if (_contentLength>0 && _contentLength<_minGzipSize)
438 doNotGzip();
439 else
440 doGzip();
441 }
442
443 if (_gzOut!=null)
444 _gzOut.finish();
445 }
446 }
447
448 public void write(int b) throws IOException
449 {
450 checkOut(1);
451 _out.write(b);
452 }
453
454 public void write(byte b[]) throws IOException
455 {
456 checkOut(b.length);
457 _out.write(b);
458 }
459
460 public void write(byte b[], int off, int len) throws IOException
461 {
462 checkOut(len);
463 _out.write(b,off,len);
464 }
465
466 protected boolean setContentEncodingGzip()
467 {
468 _response.setHeader("Content-Encoding", "gzip");
469 return _response.containsHeader("Content-Encoding");
470 }
471
472 public void doGzip() throws IOException
473 {
474 if (_gzOut==null)
475 {
476 if (_response.isCommitted())
477 throw new IllegalStateException();
478
479 if (setContentEncodingGzip())
480 {
481 _out=_gzOut=new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
482
483 if (_bOut!=null)
484 {
485 _out.write(_bOut.getBuf(),0,_bOut.getCount());
486 _bOut=null;
487 }
488 }
489 else
490 doNotGzip();
491 }
492 }
493
494 public void doNotGzip() throws IOException
495 {
496 if (_gzOut!=null)
497 throw new IllegalStateException();
498 if (_out==null || _bOut!=null )
499 {
500 _out=_response.getOutputStream();
501 if (_contentLength>=0)
502 {
503 if(_contentLength<Integer.MAX_VALUE)
504 _response.setContentLength((int)_contentLength);
505 else
506 _response.setHeader("Content-Length",Long.toString(_contentLength));
507 }
508
509 if (_bOut!=null)
510 _out.write(_bOut.getBuf(),0,_bOut.getCount());
511 _bOut=null;
512 }
513 }
514
515 private void checkOut(int length) throws IOException
516 {
517 if (_closed)
518 {
519 new Throwable().printStackTrace();
520 throw new IOException("CLOSED");
521 }
522
523 if (_out==null)
524 {
525 if (_response.isCommitted() || (_contentLength>=0 && _contentLength<_minGzipSize))
526 doNotGzip();
527 else if (length>_minGzipSize)
528 doGzip();
529 else
530 _out=_bOut=new ByteArrayOutputStream2(_bufferSize);
531 }
532 else if (_bOut!=null)
533 {
534 if (_response.isCommitted() || (_contentLength>=0 && _contentLength<_minGzipSize))
535 doNotGzip();
536 else if (length>=(_bOut.size()-_bOut.getCount()))
537 doGzip();
538 }
539 }
540 }
541 }