blob: 790390cb93dc54c18eb0a751a0d40ac02542a410 [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
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
19package org.eclipse.jetty.http.gzip;
20
21import java.io.IOException;
22import java.io.OutputStream;
23import java.io.OutputStreamWriter;
24import java.io.PrintWriter;
25import java.io.UnsupportedEncodingException;
26import java.util.Set;
27
28import javax.servlet.ServletOutputStream;
29import javax.servlet.http.HttpServletRequest;
30import javax.servlet.http.HttpServletResponse;
31import javax.servlet.http.HttpServletResponseWrapper;
32
33import org.eclipse.jetty.util.StringUtil;
34
35/*------------------------------------------------------------ */
36/**
37 */
38public 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}