View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.servlets.gzip;
20  
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.io.OutputStreamWriter;
24  import java.io.PrintWriter;
25  import java.io.UnsupportedEncodingException;
26  import java.util.Set;
27  
28  import javax.servlet.ServletOutputStream;
29  import javax.servlet.http.HttpServletRequest;
30  import javax.servlet.http.HttpServletResponse;
31  import javax.servlet.http.HttpServletResponseWrapper;
32  
33  import org.eclipse.jetty.util.StringUtil;
34  
35  /*------------------------------------------------------------ */
36  /**
37   */
38  public abstract class CompressedResponseWrapper extends HttpServletResponseWrapper
39  {
40  
41      public static final int DEFAULT_BUFFER_SIZE = 8192;
42      public static final int DEFAULT_MIN_COMPRESS_SIZE = 256;
43  
44      private Set<String> _mimeTypes;
45      private boolean _excludeMimeTypes;
46      private int _bufferSize=DEFAULT_BUFFER_SIZE;
47      private int _minCompressSize=DEFAULT_MIN_COMPRESS_SIZE;
48      protected HttpServletRequest _request;
49  
50      private PrintWriter _writer;
51      private AbstractCompressedStream _compressedStream;
52      private String _etag;
53      private long _contentLength=-1;
54      private boolean _noCompression;
55  
56      /* ------------------------------------------------------------ */
57      public CompressedResponseWrapper(HttpServletRequest request, HttpServletResponse response)
58      {
59          super(response);
60          _request = request;
61      }
62  
63  
64      /* ------------------------------------------------------------ */
65      public long getContentLength()
66      {
67          return _contentLength;
68      }
69  
70      /* ------------------------------------------------------------ */
71      @Override
72      public int getBufferSize()
73      {
74          return _bufferSize;
75      }
76      
77      /* ------------------------------------------------------------ */
78      public int getMinCompressSize()
79      {
80          return _minCompressSize;
81      }
82      
83      /* ------------------------------------------------------------ */
84      public String getETag()
85      {
86          return _etag;
87      }
88  
89      /* ------------------------------------------------------------ */
90      public HttpServletRequest getRequest()
91      {
92          return _request;
93      }
94      
95      /* ------------------------------------------------------------ */
96      /**
97       */
98      public void setMimeTypes(Set<String> mimeTypes,boolean excludeMimeTypes)
99      {
100         _excludeMimeTypes=excludeMimeTypes;
101         _mimeTypes = mimeTypes;
102     }
103 
104     /* ------------------------------------------------------------ */
105     /**
106      */
107     @Override
108     public void setBufferSize(int bufferSize)
109     {
110         _bufferSize = bufferSize;
111         if (_compressedStream!=null)
112             _compressedStream.setBufferSize(bufferSize);
113     }
114 
115     /* ------------------------------------------------------------ */
116     /**
117      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setMinCompressSize(int)
118      */
119     public void setMinCompressSize(int minCompressSize)
120     {
121         _minCompressSize = minCompressSize;
122     }
123 
124     /* ------------------------------------------------------------ */
125     /**
126      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setContentType(java.lang.String)
127      */
128     @Override
129     public void setContentType(String ct)
130     {
131         super.setContentType(ct);
132 
133         if (!_noCompression && (_compressedStream==null || _compressedStream.getOutputStream()==null))
134         {
135             if (ct!=null)
136             {
137                 int colon=ct.indexOf(";");
138                 if (colon>0)
139                     ct=ct.substring(0,colon);
140 
141                 if (_mimeTypes.contains(StringUtil.asciiToLowerCase(ct))==_excludeMimeTypes)
142                     noCompression();
143             }
144         }
145     }
146 
147     /* ------------------------------------------------------------ */
148     /**
149      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setStatus(int, java.lang.String)
150      */
151     @SuppressWarnings("deprecation")
152     @Override
153     public void setStatus(int sc, String sm)
154     {
155         super.setStatus(sc,sm);
156         if (sc<200 || sc==204 || sc==205 || sc>=300)
157             noCompression();
158     }
159 
160     /* ------------------------------------------------------------ */
161     /**
162      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setStatus(int)
163      */
164     @Override
165     public void setStatus(int sc)
166     {
167         super.setStatus(sc);
168         if (sc<200 || sc==204 || sc==205 || sc>=300)
169             noCompression();
170     }
171 
172     /* ------------------------------------------------------------ */
173     /**
174      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setContentLength(int)
175      */
176     @Override
177     public void setContentLength(int length)
178     {
179         if (_noCompression)
180             super.setContentLength(length);
181         else
182             setContentLength((long)length);
183     }
184 
185     /* ------------------------------------------------------------ */
186     protected void setContentLength(long length)
187     {
188         _contentLength=length;
189         if (_compressedStream!=null)
190             _compressedStream.setContentLength();
191         else if (_noCompression && _contentLength>=0)
192         {
193             HttpServletResponse response = (HttpServletResponse)getResponse();
194             if(_contentLength<Integer.MAX_VALUE)
195             {
196                 response.setContentLength((int)_contentLength);
197             }
198             else
199             {
200                 response.setHeader("Content-Length", Long.toString(_contentLength));
201             }
202         }
203     }
204     
205     /* ------------------------------------------------------------ */
206     /**
207      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#addHeader(java.lang.String, java.lang.String)
208      */
209     @Override
210     public void addHeader(String name, String value)
211     {
212         if ("content-length".equalsIgnoreCase(name))
213         {
214             _contentLength=Long.parseLong(value);
215             if (_compressedStream!=null)
216                 _compressedStream.setContentLength();
217         }
218         else if ("content-type".equalsIgnoreCase(name))
219         {
220             setContentType(value);
221         }
222         else if ("content-encoding".equalsIgnoreCase(name))
223         {
224             super.addHeader(name,value);
225             if (!isCommitted())
226             {
227                 noCompression();
228             }
229         }
230         else if ("etag".equalsIgnoreCase(name))
231             _etag=value;
232         else
233             super.addHeader(name,value);
234     }
235 
236     /* ------------------------------------------------------------ */
237     /**
238      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#flushBuffer()
239      */
240     @Override
241     public void flushBuffer() throws IOException
242     {
243         if (_writer!=null)
244             _writer.flush();
245         if (_compressedStream!=null)
246             _compressedStream.flush();
247         else
248             getResponse().flushBuffer();
249     }
250 
251     /* ------------------------------------------------------------ */
252     /**
253      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#reset()
254      */
255     @Override
256     public void reset()
257     {
258         super.reset();
259         if (_compressedStream!=null)
260             _compressedStream.resetBuffer();
261         _writer=null;
262         _compressedStream=null;
263         _noCompression=false;
264         _contentLength=-1;
265     }
266 
267     /* ------------------------------------------------------------ */
268     /**
269      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#resetBuffer()
270      */
271     @Override
272     public void resetBuffer()
273     {
274         super.resetBuffer();
275         if (_compressedStream!=null)
276             _compressedStream.resetBuffer();
277         _writer=null;
278         _compressedStream=null;
279     }
280 
281     /* ------------------------------------------------------------ */
282     /**
283      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#sendError(int, java.lang.String)
284      */
285     @Override
286     public void sendError(int sc, String msg) throws IOException
287     {
288         resetBuffer();
289         super.sendError(sc,msg);
290     }
291 
292     /* ------------------------------------------------------------ */
293     /**
294      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#sendError(int)
295      */
296     @Override
297     public void sendError(int sc) throws IOException
298     {
299         resetBuffer();
300         super.sendError(sc);
301     }
302 
303     /* ------------------------------------------------------------ */
304     /**
305      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#sendRedirect(java.lang.String)
306      */
307     @Override
308     public void sendRedirect(String location) throws IOException
309     {
310         resetBuffer();
311         super.sendRedirect(location);
312     }
313 
314     /* ------------------------------------------------------------ */
315     /**
316      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#noCompression()
317      */
318     public void noCompression()
319     {
320         if (!_noCompression)
321             setDeferredHeaders();
322         _noCompression=true;
323         if (_compressedStream!=null)
324         {
325             try
326             {
327                 _compressedStream.doNotCompress(false);
328             }
329             catch (IOException e)
330             {
331                 throw new IllegalStateException(e);
332             }
333         }
334     }
335 
336     /* ------------------------------------------------------------ */
337     /**
338      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#finish()
339      */
340     public void finish() throws IOException
341     {
342         if (_writer!=null && !_compressedStream.isClosed())
343             _writer.flush();
344         if (_compressedStream!=null)
345             _compressedStream.finish();
346         else 
347             setDeferredHeaders();
348     }
349 
350     /* ------------------------------------------------------------ */
351     private void setDeferredHeaders()
352     {
353         if (!isCommitted())
354         {
355             if (_contentLength>=0)
356             {
357                 if (_contentLength < Integer.MAX_VALUE)
358                     super.setContentLength((int)_contentLength);
359                 else
360                     super.setHeader("Content-Length",Long.toString(_contentLength));
361             }
362             if(_etag!=null)
363                 super.setHeader("ETag",_etag);
364         }
365     }
366 
367     /* ------------------------------------------------------------ */
368     /**
369      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setHeader(java.lang.String, java.lang.String)
370      */
371     @Override
372     public void setHeader(String name, String value)
373     {
374         if (_noCompression)
375             super.setHeader(name,value);
376         else if ("content-length".equalsIgnoreCase(name))
377         {
378             setContentLength(Long.parseLong(value));
379         }
380         else if ("content-type".equalsIgnoreCase(name))
381         {
382             setContentType(value);
383         }
384         else if ("content-encoding".equalsIgnoreCase(name))
385         {
386             super.setHeader(name,value);
387             if (!isCommitted())
388             {
389                 noCompression();
390             }
391         }
392         else if ("etag".equalsIgnoreCase(name))
393             _etag=value;
394         else
395             super.setHeader(name,value);
396     }
397 
398     /* ------------------------------------------------------------ */
399     @Override
400     public boolean containsHeader(String name)
401     {
402         if (!_noCompression && "etag".equalsIgnoreCase(name) && _etag!=null)
403             return true;
404         return super.containsHeader(name);
405     }
406 
407     /* ------------------------------------------------------------ */
408     /**
409      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#getOutputStream()
410      */
411     @Override
412     public ServletOutputStream getOutputStream() throws IOException
413     {
414         if (_compressedStream==null)
415         {
416             if (getResponse().isCommitted() || _noCompression)
417                 return getResponse().getOutputStream();
418 
419             _compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse());
420         }
421         else if (_writer!=null)
422             throw new IllegalStateException("getWriter() called");
423 
424         return _compressedStream;
425     }
426 
427     /* ------------------------------------------------------------ */
428     /**
429      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#getWriter()
430      */
431     @Override
432     public PrintWriter getWriter() throws IOException
433     {
434         if (_writer==null)
435         {
436             if (_compressedStream!=null)
437                 throw new IllegalStateException("getOutputStream() called");
438 
439             if (getResponse().isCommitted() || _noCompression)
440                 return getResponse().getWriter();
441 
442             _compressedStream=newCompressedStream(_request,(HttpServletResponse)getResponse());
443             _writer=newWriter(_compressedStream,getCharacterEncoding());
444         }
445         return _writer;
446     }
447 
448     /* ------------------------------------------------------------ */
449     /**
450      * @see org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper#setIntHeader(java.lang.String, int)
451      */
452     @Override
453     public void setIntHeader(String name, int value)
454     {
455         if ("content-length".equalsIgnoreCase(name))
456         {
457             _contentLength=value;
458             if (_compressedStream!=null)
459                 _compressedStream.setContentLength();
460         }
461         else
462             super.setIntHeader(name,value);
463     }
464 
465     /* ------------------------------------------------------------ */
466     /**
467      * Allows derived implementations to replace PrintWriter implementation.
468      *
469      * @param out the out
470      * @param encoding the encoding
471      * @return the prints the writer
472      * @throws UnsupportedEncodingException the unsupported encoding exception
473      */
474     protected PrintWriter newWriter(OutputStream out,String encoding) throws UnsupportedEncodingException
475     {
476         return encoding==null?new PrintWriter(out):new PrintWriter(new OutputStreamWriter(out,encoding));
477     }
478 
479     /* ------------------------------------------------------------ */
480     /**
481      *@return the underlying CompressedStream implementation
482      */
483     protected abstract AbstractCompressedStream newCompressedStream(HttpServletRequest _request, HttpServletResponse response) throws IOException;
484 
485 }