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