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