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