• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 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     /* ------------------------------------------------------------ */
CompressedResponseWrapper(HttpServletRequest request, HttpServletResponse response)56     public CompressedResponseWrapper(HttpServletRequest request, HttpServletResponse response)
57     {
58         super(response);
59         _request = request;
60     }
61 
62 
63     /* ------------------------------------------------------------ */
getContentLength()64     public long getContentLength()
65     {
66         return _contentLength;
67     }
68 
69     /* ------------------------------------------------------------ */
getBufferSize()70     public int getBufferSize()
71     {
72         return _bufferSize;
73     }
74 
75     /* ------------------------------------------------------------ */
getMinCompressSize()76     public int getMinCompressSize()
77     {
78         return _minCompressSize;
79     }
80 
81     /* ------------------------------------------------------------ */
getETag()82     public String getETag()
83     {
84         return _etag;
85     }
86 
87     /* ------------------------------------------------------------ */
getRequest()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      */
setMimeTypes(Set<String> mimeTypes)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
setBufferSize(int bufferSize)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      */
setMinCompressSize(int minCompressSize)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
setContentType(String ct)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
setStatus(int sc, String sm)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
setStatus(int sc)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
setContentLength(int length)179     public void setContentLength(int length)
180     {
181         if (_noCompression)
182             super.setContentLength(length);
183         else
184             setContentLength((long)length);
185     }
186 
187     /* ------------------------------------------------------------ */
setContentLength(long length)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
addHeader(String name, String value)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
flushBuffer()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
reset()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
resetBuffer()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
sendError(int sc, String msg)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
sendError(int sc)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
sendRedirect(String location)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      */
noCompression()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      */
finish()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     /* ------------------------------------------------------------ */
setDeferredHeaders()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
setHeader(String name, String value)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
containsHeader(String name)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
getOutputStream()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
getWriter()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
setIntHeader(String name, int value)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      */
newWriter(OutputStream out,String encoding)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      */
newCompressedStream(HttpServletRequest _request, HttpServletResponse response)485     protected abstract AbstractCompressedStream newCompressedStream(HttpServletRequest _request, HttpServletResponse response) throws IOException;
486 
487 }
488