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.server; 20 21 import java.io.IOException; 22 import java.io.PrintWriter; 23 import java.util.Collection; 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.EnumSet; 27 import java.util.Enumeration; 28 import java.util.Locale; 29 30 import javax.servlet.RequestDispatcher; 31 import javax.servlet.ServletOutputStream; 32 import javax.servlet.SessionTrackingMode; 33 import javax.servlet.http.Cookie; 34 import javax.servlet.http.HttpServletResponse; 35 import javax.servlet.http.HttpSession; 36 37 import org.eclipse.jetty.http.HttpCookie; 38 import org.eclipse.jetty.http.HttpFields; 39 import org.eclipse.jetty.http.HttpGenerator; 40 import org.eclipse.jetty.http.HttpHeaderValues; 41 import org.eclipse.jetty.http.HttpHeaders; 42 import org.eclipse.jetty.http.HttpSchemes; 43 import org.eclipse.jetty.http.HttpStatus; 44 import org.eclipse.jetty.http.HttpURI; 45 import org.eclipse.jetty.http.HttpVersions; 46 import org.eclipse.jetty.http.MimeTypes; 47 import org.eclipse.jetty.io.BufferCache.CachedBuffer; 48 import org.eclipse.jetty.server.handler.ContextHandler; 49 import org.eclipse.jetty.server.handler.ErrorHandler; 50 import org.eclipse.jetty.util.ByteArrayISO8859Writer; 51 import org.eclipse.jetty.util.QuotedStringTokenizer; 52 import org.eclipse.jetty.util.StringUtil; 53 import org.eclipse.jetty.util.URIUtil; 54 import org.eclipse.jetty.util.log.Log; 55 import org.eclipse.jetty.util.log.Logger; 56 57 /** Response. 58 * <p> 59 * Implements {@link javax.servlet.http.HttpServletResponse} from the <code>javax.servlet.http</code> package. 60 * </p> 61 */ 62 public class Response implements HttpServletResponse 63 { 64 private static final Logger LOG = Log.getLogger(Response.class); 65 66 67 public static final int 68 NONE=0, 69 STREAM=1, 70 WRITER=2; 71 72 /** 73 * If a header name starts with this string, the header (stripped of the prefix) 74 * can be set during include using only {@link #setHeader(String, String)} or 75 * {@link #addHeader(String, String)}. 76 */ 77 public final static String SET_INCLUDE_HEADER_PREFIX = "org.eclipse.jetty.server.include."; 78 79 /** 80 * If this string is found within the comment of a cookie added with {@link #addCookie(Cookie)}, then the cookie 81 * will be set as HTTP ONLY. 82 */ 83 public final static String HTTP_ONLY_COMMENT="__HTTP_ONLY__"; 84 85 86 /* ------------------------------------------------------------ */ getResponse(HttpServletResponse response)87 public static Response getResponse(HttpServletResponse response) 88 { 89 if (response instanceof Response) 90 return (Response)response; 91 92 return AbstractHttpConnection.getCurrentConnection().getResponse(); 93 } 94 95 private final AbstractHttpConnection _connection; 96 private int _status=SC_OK; 97 private String _reason; 98 private Locale _locale; 99 private String _mimeType; 100 private CachedBuffer _cachedMimeType; 101 private String _characterEncoding; 102 private boolean _explicitEncoding; 103 private String _contentType; 104 private volatile int _outputState; 105 private PrintWriter _writer; 106 107 /* ------------------------------------------------------------ */ 108 /** 109 * 110 */ Response(AbstractHttpConnection connection)111 public Response(AbstractHttpConnection connection) 112 { 113 _connection=connection; 114 } 115 116 117 /* ------------------------------------------------------------ */ 118 /* 119 * @see javax.servlet.ServletResponse#reset() 120 */ recycle()121 protected void recycle() 122 { 123 _status=SC_OK; 124 _reason=null; 125 _locale=null; 126 _mimeType=null; 127 _cachedMimeType=null; 128 _characterEncoding=null; 129 _explicitEncoding=false; 130 _contentType=null; 131 _writer=null; 132 _outputState=NONE; 133 } 134 135 /* ------------------------------------------------------------ */ 136 /* 137 * @see javax.servlet.http.HttpServletResponse#addCookie(javax.servlet.http.Cookie) 138 */ addCookie(HttpCookie cookie)139 public void addCookie(HttpCookie cookie) 140 { 141 _connection.getResponseFields().addSetCookie(cookie); 142 } 143 144 /* ------------------------------------------------------------ */ 145 /* 146 * @see javax.servlet.http.HttpServletResponse#addCookie(javax.servlet.http.Cookie) 147 */ addCookie(Cookie cookie)148 public void addCookie(Cookie cookie) 149 { 150 String comment=cookie.getComment(); 151 boolean http_only=false; 152 153 if (comment!=null) 154 { 155 int i=comment.indexOf(HTTP_ONLY_COMMENT); 156 if (i>=0) 157 { 158 http_only=true; 159 comment=comment.replace(HTTP_ONLY_COMMENT,"").trim(); 160 if (comment.length()==0) 161 comment=null; 162 } 163 } 164 _connection.getResponseFields().addSetCookie(cookie.getName(), 165 cookie.getValue(), 166 cookie.getDomain(), 167 cookie.getPath(), 168 cookie.getMaxAge(), 169 comment, 170 cookie.getSecure(), 171 http_only || cookie.isHttpOnly(), 172 cookie.getVersion()); 173 } 174 175 /* ------------------------------------------------------------ */ 176 /* 177 * @see javax.servlet.http.HttpServletResponse#containsHeader(java.lang.String) 178 */ containsHeader(String name)179 public boolean containsHeader(String name) 180 { 181 return _connection.getResponseFields().containsKey(name); 182 } 183 184 /* ------------------------------------------------------------ */ 185 /* 186 * @see javax.servlet.http.HttpServletResponse#encodeURL(java.lang.String) 187 */ encodeURL(String url)188 public String encodeURL(String url) 189 { 190 final Request request=_connection.getRequest(); 191 SessionManager sessionManager = request.getSessionManager(); 192 if (sessionManager==null) 193 return url; 194 195 HttpURI uri = null; 196 if (sessionManager.isCheckingRemoteSessionIdEncoding() && URIUtil.hasScheme(url)) 197 { 198 uri = new HttpURI(url); 199 String path = uri.getPath(); 200 path = (path == null?"":path); 201 int port=uri.getPort(); 202 if (port<0) 203 port = HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme())?443:80; 204 if (!request.getServerName().equalsIgnoreCase(uri.getHost()) || 205 request.getServerPort()!=port || 206 !path.startsWith(request.getContextPath())) //TODO the root context path is "", with which every non null string starts 207 return url; 208 } 209 210 String sessionURLPrefix = sessionManager.getSessionIdPathParameterNamePrefix(); 211 if (sessionURLPrefix==null) 212 return url; 213 214 if (url==null) 215 return null; 216 217 // should not encode if cookies in evidence 218 if ((sessionManager.isUsingCookies() && request.isRequestedSessionIdFromCookie()) || !sessionManager.isUsingURLs()) 219 { 220 int prefix=url.indexOf(sessionURLPrefix); 221 if (prefix!=-1) 222 { 223 int suffix=url.indexOf("?",prefix); 224 if (suffix<0) 225 suffix=url.indexOf("#",prefix); 226 227 if (suffix<=prefix) 228 return url.substring(0,prefix); 229 return url.substring(0,prefix)+url.substring(suffix); 230 } 231 return url; 232 } 233 234 // get session; 235 HttpSession session=request.getSession(false); 236 237 // no session 238 if (session == null) 239 return url; 240 241 // invalid session 242 if (!sessionManager.isValid(session)) 243 return url; 244 245 String id=sessionManager.getNodeId(session); 246 247 if (uri == null) 248 uri = new HttpURI(url); 249 250 251 // Already encoded 252 int prefix=url.indexOf(sessionURLPrefix); 253 if (prefix!=-1) 254 { 255 int suffix=url.indexOf("?",prefix); 256 if (suffix<0) 257 suffix=url.indexOf("#",prefix); 258 259 if (suffix<=prefix) 260 return url.substring(0,prefix+sessionURLPrefix.length())+id; 261 return url.substring(0,prefix+sessionURLPrefix.length())+id+ 262 url.substring(suffix); 263 } 264 265 // edit the session 266 int suffix=url.indexOf('?'); 267 if (suffix<0) 268 suffix=url.indexOf('#'); 269 if (suffix<0) 270 { 271 return url+ 272 ((HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme()) || HttpSchemes.HTTP.equalsIgnoreCase(uri.getScheme())) && uri.getPath()==null?"/":"") + //if no path, insert the root path 273 sessionURLPrefix+id; 274 } 275 276 277 return url.substring(0,suffix)+ 278 ((HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme()) || HttpSchemes.HTTP.equalsIgnoreCase(uri.getScheme())) && uri.getPath()==null?"/":"")+ //if no path so insert the root path 279 sessionURLPrefix+id+url.substring(suffix); 280 } 281 282 /* ------------------------------------------------------------ */ 283 /** 284 * @see javax.servlet.http.HttpServletResponse#encodeRedirectURL(java.lang.String) 285 */ encodeRedirectURL(String url)286 public String encodeRedirectURL(String url) 287 { 288 return encodeURL(url); 289 } 290 291 /* ------------------------------------------------------------ */ 292 @Deprecated encodeUrl(String url)293 public String encodeUrl(String url) 294 { 295 return encodeURL(url); 296 } 297 298 /* ------------------------------------------------------------ */ 299 @Deprecated encodeRedirectUrl(String url)300 public String encodeRedirectUrl(String url) 301 { 302 return encodeRedirectURL(url); 303 } 304 305 /* ------------------------------------------------------------ */ 306 /* 307 * @see javax.servlet.http.HttpServletResponse#sendError(int, java.lang.String) 308 */ sendError(int code, String message)309 public void sendError(int code, String message) throws IOException 310 { 311 if (_connection.isIncluding()) 312 return; 313 314 if (isCommitted()) 315 LOG.warn("Committed before "+code+" "+message); 316 317 resetBuffer(); 318 _characterEncoding=null; 319 setHeader(HttpHeaders.EXPIRES,null); 320 setHeader(HttpHeaders.LAST_MODIFIED,null); 321 setHeader(HttpHeaders.CACHE_CONTROL,null); 322 setHeader(HttpHeaders.CONTENT_TYPE,null); 323 setHeader(HttpHeaders.CONTENT_LENGTH,null); 324 325 _outputState=NONE; 326 setStatus(code,message); 327 328 if (message==null) 329 message=HttpStatus.getMessage(code); 330 331 // If we are allowed to have a body 332 if (code!=SC_NO_CONTENT && 333 code!=SC_NOT_MODIFIED && 334 code!=SC_PARTIAL_CONTENT && 335 code>=SC_OK) 336 { 337 Request request = _connection.getRequest(); 338 339 ErrorHandler error_handler = null; 340 ContextHandler.Context context = request.getContext(); 341 if (context!=null) 342 error_handler=context.getContextHandler().getErrorHandler(); 343 if (error_handler==null) 344 error_handler = _connection.getConnector().getServer().getBean(ErrorHandler.class); 345 if (error_handler!=null) 346 { 347 request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(code)); 348 request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message); 349 request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI()); 350 request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,request.getServletName()); 351 error_handler.handle(null,_connection.getRequest(),_connection.getRequest(),this ); 352 } 353 else 354 { 355 setHeader(HttpHeaders.CACHE_CONTROL, "must-revalidate,no-cache,no-store"); 356 setContentType(MimeTypes.TEXT_HTML_8859_1); 357 ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(2048); 358 if (message != null) 359 { 360 message= StringUtil.replace(message, "&", "&"); 361 message= StringUtil.replace(message, "<", "<"); 362 message= StringUtil.replace(message, ">", ">"); 363 } 364 String uri= request.getRequestURI(); 365 if (uri!=null) 366 { 367 uri= StringUtil.replace(uri, "&", "&"); 368 uri= StringUtil.replace(uri, "<", "<"); 369 uri= StringUtil.replace(uri, ">", ">"); 370 } 371 372 writer.write("<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n"); 373 writer.write("<title>Error "); 374 writer.write(Integer.toString(code)); 375 writer.write(' '); 376 if (message==null) 377 message=HttpStatus.getMessage(code); 378 writer.write(message); 379 writer.write("</title>\n</head>\n<body>\n<h2>HTTP ERROR: "); 380 writer.write(Integer.toString(code)); 381 writer.write("</h2>\n<p>Problem accessing "); 382 writer.write(uri); 383 writer.write(". Reason:\n<pre> "); 384 writer.write(message); 385 writer.write("</pre>"); 386 writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>"); 387 388 for (int i= 0; i < 20; i++) 389 writer.write("\n "); 390 writer.write("\n</body>\n</html>\n"); 391 392 writer.flush(); 393 setContentLength(writer.size()); 394 writer.writeTo(getOutputStream()); 395 writer.destroy(); 396 } 397 } 398 else if (code!=SC_PARTIAL_CONTENT) 399 { 400 _connection.getRequestFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER); 401 _connection.getRequestFields().remove(HttpHeaders.CONTENT_LENGTH_BUFFER); 402 _characterEncoding=null; 403 _mimeType=null; 404 _cachedMimeType=null; 405 } 406 407 complete(); 408 } 409 410 /* ------------------------------------------------------------ */ 411 /* 412 * @see javax.servlet.http.HttpServletResponse#sendError(int) 413 */ sendError(int sc)414 public void sendError(int sc) throws IOException 415 { 416 if (sc==102) 417 sendProcessing(); 418 else 419 sendError(sc,null); 420 } 421 422 /* ------------------------------------------------------------ */ 423 /* Send a 102-Processing response. 424 * If the connection is a HTTP connection, the version is 1.1 and the 425 * request has a Expect header starting with 102, then a 102 response is 426 * sent. This indicates that the request still be processed and real response 427 * can still be sent. This method is called by sendError if it is passed 102. 428 * @see javax.servlet.http.HttpServletResponse#sendError(int) 429 */ sendProcessing()430 public void sendProcessing() throws IOException 431 { 432 if (_connection.isExpecting102Processing() && !isCommitted()) 433 ((HttpGenerator)_connection.getGenerator()).send1xx(HttpStatus.PROCESSING_102); 434 } 435 436 /* ------------------------------------------------------------ */ 437 /* 438 * @see javax.servlet.http.HttpServletResponse#sendRedirect(java.lang.String) 439 */ sendRedirect(String location)440 public void sendRedirect(String location) throws IOException 441 { 442 if (_connection.isIncluding()) 443 return; 444 445 if (location==null) 446 throw new IllegalArgumentException(); 447 448 if (!URIUtil.hasScheme(location)) 449 { 450 StringBuilder buf = _connection.getRequest().getRootURL(); 451 if (location.startsWith("/")) 452 buf.append(location); 453 else 454 { 455 String path=_connection.getRequest().getRequestURI(); 456 String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path); 457 location=URIUtil.addPaths(parent,location); 458 if(location==null) 459 throw new IllegalStateException("path cannot be above root"); 460 if (!location.startsWith("/")) 461 buf.append('/'); 462 buf.append(location); 463 } 464 465 location=buf.toString(); 466 HttpURI uri = new HttpURI(location); 467 String path=uri.getDecodedPath(); 468 String canonical=URIUtil.canonicalPath(path); 469 if (canonical==null) 470 throw new IllegalArgumentException(); 471 if (!canonical.equals(path)) 472 { 473 buf = _connection.getRequest().getRootURL(); 474 buf.append(URIUtil.encodePath(canonical)); 475 String param=uri.getParam(); 476 if (param!=null) 477 { 478 buf.append(';'); 479 buf.append(param); 480 } 481 String query=uri.getQuery(); 482 if (query!=null) 483 { 484 buf.append('?'); 485 buf.append(query); 486 } 487 String fragment=uri.getFragment(); 488 if (fragment!=null) 489 { 490 buf.append('#'); 491 buf.append(fragment); 492 } 493 location=buf.toString(); 494 } 495 } 496 497 resetBuffer(); 498 setHeader(HttpHeaders.LOCATION,location); 499 setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); 500 complete(); 501 502 } 503 504 /* ------------------------------------------------------------ */ 505 /* 506 * @see javax.servlet.http.HttpServletResponse#setDateHeader(java.lang.String, long) 507 */ setDateHeader(String name, long date)508 public void setDateHeader(String name, long date) 509 { 510 if (!_connection.isIncluding()) 511 _connection.getResponseFields().putDateField(name, date); 512 } 513 514 /* ------------------------------------------------------------ */ 515 /* 516 * @see javax.servlet.http.HttpServletResponse#addDateHeader(java.lang.String, long) 517 */ addDateHeader(String name, long date)518 public void addDateHeader(String name, long date) 519 { 520 if (!_connection.isIncluding()) 521 _connection.getResponseFields().addDateField(name, date); 522 } 523 524 /* ------------------------------------------------------------ */ 525 /* 526 * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String) 527 */ setHeader(String name, String value)528 public void setHeader(String name, String value) 529 { 530 if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) 531 setContentType(value); 532 else 533 { 534 if (_connection.isIncluding()) 535 { 536 if (name.startsWith(SET_INCLUDE_HEADER_PREFIX)) 537 name=name.substring(SET_INCLUDE_HEADER_PREFIX.length()); 538 else 539 return; 540 } 541 _connection.getResponseFields().put(name, value); 542 if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) 543 { 544 if (value==null) 545 _connection._generator.setContentLength(-1); 546 else 547 _connection._generator.setContentLength(Long.parseLong(value)); 548 } 549 } 550 } 551 552 553 /* ------------------------------------------------------------ */ getHeaderNames()554 public Collection<String> getHeaderNames() 555 { 556 final HttpFields fields=_connection.getResponseFields(); 557 return fields.getFieldNamesCollection(); 558 } 559 560 /* ------------------------------------------------------------ */ 561 /* 562 */ getHeader(String name)563 public String getHeader(String name) 564 { 565 return _connection.getResponseFields().getStringField(name); 566 } 567 568 /* ------------------------------------------------------------ */ 569 /* 570 */ getHeaders(String name)571 public Collection<String> getHeaders(String name) 572 { 573 final HttpFields fields=_connection.getResponseFields(); 574 Collection<String> i = fields.getValuesCollection(name); 575 if (i==null) 576 return Collections.EMPTY_LIST; 577 return i; 578 } 579 580 /* ------------------------------------------------------------ */ 581 /* 582 * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String) 583 */ addHeader(String name, String value)584 public void addHeader(String name, String value) 585 { 586 587 if (_connection.isIncluding()) 588 { 589 if (name.startsWith(SET_INCLUDE_HEADER_PREFIX)) 590 name=name.substring(SET_INCLUDE_HEADER_PREFIX.length()); 591 else 592 return; 593 } 594 595 if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) 596 { 597 setContentType(value); 598 return; 599 } 600 601 _connection.getResponseFields().add(name, value); 602 if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) 603 _connection._generator.setContentLength(Long.parseLong(value)); 604 } 605 606 /* ------------------------------------------------------------ */ 607 /* 608 * @see javax.servlet.http.HttpServletResponse#setIntHeader(java.lang.String, int) 609 */ setIntHeader(String name, int value)610 public void setIntHeader(String name, int value) 611 { 612 if (!_connection.isIncluding()) 613 { 614 _connection.getResponseFields().putLongField(name, value); 615 if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) 616 _connection._generator.setContentLength(value); 617 } 618 } 619 620 /* ------------------------------------------------------------ */ 621 /* 622 * @see javax.servlet.http.HttpServletResponse#addIntHeader(java.lang.String, int) 623 */ addIntHeader(String name, int value)624 public void addIntHeader(String name, int value) 625 { 626 if (!_connection.isIncluding()) 627 { 628 _connection.getResponseFields().addLongField(name, value); 629 if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) 630 _connection._generator.setContentLength(value); 631 } 632 } 633 634 /* ------------------------------------------------------------ */ 635 /* 636 * @see javax.servlet.http.HttpServletResponse#setStatus(int) 637 */ setStatus(int sc)638 public void setStatus(int sc) 639 { 640 setStatus(sc,null); 641 } 642 643 /* ------------------------------------------------------------ */ 644 /* 645 * @see javax.servlet.http.HttpServletResponse#setStatus(int, java.lang.String) 646 */ setStatus(int sc, String sm)647 public void setStatus(int sc, String sm) 648 { 649 if (sc<=0) 650 throw new IllegalArgumentException(); 651 if (!_connection.isIncluding()) 652 { 653 _status=sc; 654 _reason=sm; 655 } 656 } 657 658 /* ------------------------------------------------------------ */ 659 /* 660 * @see javax.servlet.ServletResponse#getCharacterEncoding() 661 */ getCharacterEncoding()662 public String getCharacterEncoding() 663 { 664 if (_characterEncoding==null) 665 _characterEncoding=StringUtil.__ISO_8859_1; 666 return _characterEncoding; 667 } 668 669 /* ------------------------------------------------------------ */ getSetCharacterEncoding()670 String getSetCharacterEncoding() 671 { 672 return _characterEncoding; 673 } 674 675 /* ------------------------------------------------------------ */ 676 /* 677 * @see javax.servlet.ServletResponse#getContentType() 678 */ getContentType()679 public String getContentType() 680 { 681 return _contentType; 682 } 683 684 /* ------------------------------------------------------------ */ 685 /* 686 * @see javax.servlet.ServletResponse#getOutputStream() 687 */ getOutputStream()688 public ServletOutputStream getOutputStream() throws IOException 689 { 690 if (_outputState!=NONE && _outputState!=STREAM) 691 throw new IllegalStateException("WRITER"); 692 693 ServletOutputStream out = _connection.getOutputStream(); 694 _outputState=STREAM; 695 return out; 696 } 697 698 /* ------------------------------------------------------------ */ isWriting()699 public boolean isWriting() 700 { 701 return _outputState==WRITER; 702 } 703 704 /* ------------------------------------------------------------ */ isOutputing()705 public boolean isOutputing() 706 { 707 return _outputState!=NONE; 708 } 709 710 /* ------------------------------------------------------------ */ 711 /* 712 * @see javax.servlet.ServletResponse#getWriter() 713 */ getWriter()714 public PrintWriter getWriter() throws IOException 715 { 716 if (_outputState!=NONE && _outputState!=WRITER) 717 throw new IllegalStateException("STREAM"); 718 719 /* if there is no writer yet */ 720 if (_writer==null) 721 { 722 /* get encoding from Content-Type header */ 723 String encoding = _characterEncoding; 724 725 if (encoding==null) 726 { 727 /* implementation of educated defaults */ 728 if(_cachedMimeType != null) 729 encoding = MimeTypes.getCharsetFromContentType(_cachedMimeType); 730 731 if (encoding==null) 732 encoding = StringUtil.__ISO_8859_1; 733 734 setCharacterEncoding(encoding); 735 } 736 737 /* construct Writer using correct encoding */ 738 _writer = _connection.getPrintWriter(encoding); 739 } 740 _outputState=WRITER; 741 return _writer; 742 } 743 744 /* ------------------------------------------------------------ */ 745 /* 746 * @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String) 747 */ setCharacterEncoding(String encoding)748 public void setCharacterEncoding(String encoding) 749 { 750 if (_connection.isIncluding()) 751 return; 752 753 if (this._outputState==0 && !isCommitted()) 754 { 755 _explicitEncoding=true; 756 757 if (encoding==null) 758 { 759 // Clear any encoding. 760 if (_characterEncoding!=null) 761 { 762 _characterEncoding=null; 763 if (_cachedMimeType!=null) 764 _contentType=_cachedMimeType.toString(); 765 else if (_mimeType!=null) 766 _contentType=_mimeType; 767 else 768 _contentType=null; 769 770 if (_contentType==null) 771 _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER); 772 else 773 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 774 } 775 } 776 else 777 { 778 // No, so just add this one to the mimetype 779 _characterEncoding=encoding; 780 if (_contentType!=null) 781 { 782 int i0=_contentType.indexOf(';'); 783 if (i0<0) 784 { 785 _contentType=null; 786 if(_cachedMimeType!=null) 787 { 788 CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding); 789 if (content_type!=null) 790 { 791 _contentType=content_type.toString(); 792 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type); 793 } 794 } 795 796 if (_contentType==null) 797 { 798 _contentType = _mimeType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); 799 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 800 } 801 } 802 else 803 { 804 int i1=_contentType.indexOf("charset=",i0); 805 if (i1<0) 806 { 807 _contentType = _contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); 808 } 809 else 810 { 811 int i8=i1+8; 812 int i2=_contentType.indexOf(" ",i8); 813 if (i2<0) 814 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); 815 else 816 _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ")+_contentType.substring(i2); 817 } 818 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 819 } 820 } 821 } 822 } 823 } 824 825 /* ------------------------------------------------------------ */ 826 /* 827 * @see javax.servlet.ServletResponse#setContentLength(int) 828 */ setContentLength(int len)829 public void setContentLength(int len) 830 { 831 // Protect from setting after committed as default handling 832 // of a servlet HEAD request ALWAYS sets _content length, even 833 // if the getHandling committed the response! 834 if (isCommitted() || _connection.isIncluding()) 835 return; 836 _connection._generator.setContentLength(len); 837 if (len>0) 838 { 839 _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len); 840 if (_connection._generator.isAllContentWritten()) 841 { 842 if (_outputState==WRITER) 843 _writer.close(); 844 else if (_outputState==STREAM) 845 { 846 try 847 { 848 getOutputStream().close(); 849 } 850 catch(IOException e) 851 { 852 throw new RuntimeException(e); 853 } 854 } 855 } 856 } 857 } 858 859 /* ------------------------------------------------------------ */ 860 /* 861 * @see javax.servlet.ServletResponse#setContentLength(int) 862 */ setLongContentLength(long len)863 public void setLongContentLength(long len) 864 { 865 // Protect from setting after committed as default handling 866 // of a servlet HEAD request ALWAYS sets _content length, even 867 // if the getHandling committed the response! 868 if (isCommitted() || _connection.isIncluding()) 869 return; 870 _connection._generator.setContentLength(len); 871 _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len); 872 } 873 874 /* ------------------------------------------------------------ */ 875 /* 876 * @see javax.servlet.ServletResponse#setContentType(java.lang.String) 877 */ setContentType(String contentType)878 public void setContentType(String contentType) 879 { 880 if (isCommitted() || _connection.isIncluding()) 881 return; 882 883 // Yes this method is horribly complex.... but there are lots of special cases and 884 // as this method is called on every request, it is worth trying to save string creation. 885 // 886 887 if (contentType==null) 888 { 889 if (_locale==null) 890 _characterEncoding=null; 891 _mimeType=null; 892 _cachedMimeType=null; 893 _contentType=null; 894 _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER); 895 } 896 else 897 { 898 // Look for encoding in contentType 899 int i0=contentType.indexOf(';'); 900 901 if (i0>0) 902 { 903 // we have content type parameters 904 905 // Extract params off mimetype 906 _mimeType=contentType.substring(0,i0).trim(); 907 _cachedMimeType=MimeTypes.CACHE.get(_mimeType); 908 909 // Look for charset 910 int i1=contentType.indexOf("charset=",i0+1); 911 if (i1>=0) 912 { 913 _explicitEncoding=true; 914 int i8=i1+8; 915 int i2 = contentType.indexOf(' ',i8); 916 917 if (_outputState==WRITER) 918 { 919 // strip the charset and ignore; 920 if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' ')) 921 { 922 if (_cachedMimeType!=null) 923 { 924 CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding); 925 if (content_type!=null) 926 { 927 _contentType=content_type.toString(); 928 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type); 929 } 930 else 931 { 932 _contentType=_mimeType+";charset="+_characterEncoding; 933 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 934 } 935 } 936 else 937 { 938 _contentType=_mimeType+";charset="+_characterEncoding; 939 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 940 } 941 } 942 else if (i2<0) 943 { 944 _contentType=contentType.substring(0,i1)+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); 945 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 946 } 947 else 948 { 949 _contentType=contentType.substring(0,i1)+contentType.substring(i2)+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); 950 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 951 } 952 } 953 else if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' ')) 954 { 955 // The params are just the char encoding 956 _cachedMimeType=MimeTypes.CACHE.get(_mimeType); 957 _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8)); 958 959 if (_cachedMimeType!=null) 960 { 961 CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding); 962 if (content_type!=null) 963 { 964 _contentType=content_type.toString(); 965 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type); 966 } 967 else 968 { 969 _contentType=contentType; 970 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 971 } 972 } 973 else 974 { 975 _contentType=contentType; 976 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 977 } 978 } 979 else if (i2>0) 980 { 981 _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8,i2)); 982 _contentType=contentType; 983 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 984 } 985 else 986 { 987 _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8)); 988 _contentType=contentType; 989 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 990 } 991 } 992 else // No encoding in the params. 993 { 994 _cachedMimeType=null; 995 _contentType=_characterEncoding==null?contentType:contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); 996 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 997 } 998 } 999 else // No params at all 1000 { 1001 _mimeType=contentType; 1002 _cachedMimeType=MimeTypes.CACHE.get(_mimeType); 1003 1004 if (_characterEncoding!=null) 1005 { 1006 if (_cachedMimeType!=null) 1007 { 1008 CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding); 1009 if (content_type!=null) 1010 { 1011 _contentType=content_type.toString(); 1012 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type); 1013 } 1014 else 1015 { 1016 _contentType=_mimeType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); 1017 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 1018 } 1019 } 1020 else 1021 { 1022 _contentType=contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); 1023 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 1024 } 1025 } 1026 else if (_cachedMimeType!=null) 1027 { 1028 _contentType=_cachedMimeType.toString(); 1029 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType); 1030 } 1031 else 1032 { 1033 _contentType=contentType; 1034 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 1035 } 1036 } 1037 } 1038 } 1039 1040 /* ------------------------------------------------------------ */ 1041 /* 1042 * @see javax.servlet.ServletResponse#setBufferSize(int) 1043 */ setBufferSize(int size)1044 public void setBufferSize(int size) 1045 { 1046 if (isCommitted() || getContentCount()>0) 1047 throw new IllegalStateException("Committed or content written"); 1048 _connection.getGenerator().increaseContentBufferSize(size); 1049 } 1050 1051 /* ------------------------------------------------------------ */ 1052 /* 1053 * @see javax.servlet.ServletResponse#getBufferSize() 1054 */ getBufferSize()1055 public int getBufferSize() 1056 { 1057 return _connection.getGenerator().getContentBufferSize(); 1058 } 1059 1060 /* ------------------------------------------------------------ */ 1061 /* 1062 * @see javax.servlet.ServletResponse#flushBuffer() 1063 */ flushBuffer()1064 public void flushBuffer() throws IOException 1065 { 1066 _connection.flushResponse(); 1067 } 1068 1069 /* ------------------------------------------------------------ */ 1070 /* 1071 * @see javax.servlet.ServletResponse#reset() 1072 */ reset()1073 public void reset() 1074 { 1075 resetBuffer(); 1076 fwdReset(); 1077 _status=200; 1078 _reason=null; 1079 1080 HttpFields response_fields=_connection.getResponseFields(); 1081 1082 response_fields.clear(); 1083 String connection=_connection.getRequestFields().getStringField(HttpHeaders.CONNECTION_BUFFER); 1084 if (connection!=null) 1085 { 1086 String[] values = connection.split(","); 1087 for (int i=0;values!=null && i<values.length;i++) 1088 { 1089 CachedBuffer cb = HttpHeaderValues.CACHE.get(values[0].trim()); 1090 1091 if (cb!=null) 1092 { 1093 switch(cb.getOrdinal()) 1094 { 1095 case HttpHeaderValues.CLOSE_ORDINAL: 1096 response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER); 1097 break; 1098 1099 case HttpHeaderValues.KEEP_ALIVE_ORDINAL: 1100 if (HttpVersions.HTTP_1_0.equalsIgnoreCase(_connection.getRequest().getProtocol())) 1101 response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE); 1102 break; 1103 case HttpHeaderValues.TE_ORDINAL: 1104 response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.TE); 1105 break; 1106 } 1107 } 1108 } 1109 } 1110 } 1111 1112 reset(boolean preserveCookies)1113 public void reset(boolean preserveCookies) 1114 { 1115 if (!preserveCookies) 1116 reset(); 1117 else 1118 { 1119 HttpFields response_fields=_connection.getResponseFields(); 1120 1121 ArrayList<String> cookieValues = new ArrayList<String>(5); 1122 Enumeration<String> vals = response_fields.getValues(HttpHeaders.SET_COOKIE); 1123 while (vals.hasMoreElements()) 1124 cookieValues.add((String)vals.nextElement()); 1125 1126 reset(); 1127 1128 for (String v:cookieValues) 1129 response_fields.add(HttpHeaders.SET_COOKIE, v); 1130 } 1131 } 1132 1133 1134 1135 /* ------------------------------------------------------------ */ 1136 /* 1137 * @see javax.servlet.ServletResponse#reset() 1138 */ fwdReset()1139 public void fwdReset() 1140 { 1141 resetBuffer(); 1142 1143 _writer=null; 1144 _outputState=NONE; 1145 } 1146 1147 /* ------------------------------------------------------------ */ 1148 /* 1149 * @see javax.servlet.ServletResponse#resetBuffer() 1150 */ resetBuffer()1151 public void resetBuffer() 1152 { 1153 if (isCommitted()) 1154 throw new IllegalStateException("Committed"); 1155 _connection.getGenerator().resetBuffer(); 1156 } 1157 1158 /* ------------------------------------------------------------ */ 1159 /* 1160 * @see javax.servlet.ServletResponse#isCommitted() 1161 */ isCommitted()1162 public boolean isCommitted() 1163 { 1164 return _connection.isResponseCommitted(); 1165 } 1166 1167 1168 /* ------------------------------------------------------------ */ 1169 /* 1170 * @see javax.servlet.ServletResponse#setLocale(java.util.Locale) 1171 */ setLocale(Locale locale)1172 public void setLocale(Locale locale) 1173 { 1174 if (locale == null || isCommitted() ||_connection.isIncluding()) 1175 return; 1176 1177 _locale = locale; 1178 _connection.getResponseFields().put(HttpHeaders.CONTENT_LANGUAGE_BUFFER,locale.toString().replace('_','-')); 1179 1180 if (_explicitEncoding || _outputState!=0 ) 1181 return; 1182 1183 if (_connection.getRequest().getContext()==null) 1184 return; 1185 1186 String charset = _connection.getRequest().getContext().getContextHandler().getLocaleEncoding(locale); 1187 1188 if (charset!=null && charset.length()>0) 1189 { 1190 _characterEncoding=charset; 1191 1192 /* get current MIME type from Content-Type header */ 1193 String type=getContentType(); 1194 if (type!=null) 1195 { 1196 _characterEncoding=charset; 1197 int semi=type.indexOf(';'); 1198 if (semi<0) 1199 { 1200 _mimeType=type; 1201 _contentType= type += ";charset="+charset; 1202 } 1203 else 1204 { 1205 _mimeType=type.substring(0,semi); 1206 _contentType= _mimeType += ";charset="+charset; 1207 } 1208 1209 _cachedMimeType=MimeTypes.CACHE.get(_mimeType); 1210 _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); 1211 } 1212 } 1213 } 1214 1215 /* ------------------------------------------------------------ */ 1216 /* 1217 * @see javax.servlet.ServletResponse#getLocale() 1218 */ getLocale()1219 public Locale getLocale() 1220 { 1221 if (_locale==null) 1222 return Locale.getDefault(); 1223 return _locale; 1224 } 1225 1226 /* ------------------------------------------------------------ */ 1227 /** 1228 * @return The HTTP status code that has been set for this request. This will be <code>200<code> 1229 * ({@link HttpServletResponse#SC_OK}), unless explicitly set through one of the <code>setStatus</code> methods. 1230 */ getStatus()1231 public int getStatus() 1232 { 1233 return _status; 1234 } 1235 1236 /* ------------------------------------------------------------ */ 1237 /** 1238 * @return The reason associated with the current {@link #getStatus() status}. This will be <code>null</code>, 1239 * unless one of the <code>setStatus</code> methods have been called. 1240 */ getReason()1241 public String getReason() 1242 { 1243 return _reason; 1244 } 1245 1246 /* ------------------------------------------------------------ */ 1247 /** 1248 */ complete()1249 public void complete() 1250 throws IOException 1251 { 1252 _connection.completeResponse(); 1253 } 1254 1255 /* ------------------------------------------------------------- */ 1256 /** 1257 * @return the number of bytes actually written in response body 1258 */ getContentCount()1259 public long getContentCount() 1260 { 1261 if (_connection==null || _connection.getGenerator()==null) 1262 return -1; 1263 return _connection.getGenerator().getContentWritten(); 1264 } 1265 1266 /* ------------------------------------------------------------ */ getHttpFields()1267 public HttpFields getHttpFields() 1268 { 1269 return _connection.getResponseFields(); 1270 } 1271 1272 /* ------------------------------------------------------------ */ 1273 @Override toString()1274 public String toString() 1275 { 1276 return "HTTP/1.1 "+_status+" "+ (_reason==null?"":_reason) +System.getProperty("line.separator")+ 1277 _connection.getResponseFields().toString(); 1278 } 1279 1280 /* ------------------------------------------------------------ */ 1281 /* ------------------------------------------------------------ */ 1282 /* ------------------------------------------------------------ */ 1283 private static class NullOutput extends ServletOutputStream 1284 { 1285 @Override write(int b)1286 public void write(int b) throws IOException 1287 { 1288 } 1289 1290 @Override print(String s)1291 public void print(String s) throws IOException 1292 { 1293 } 1294 1295 @Override println(String s)1296 public void println(String s) throws IOException 1297 { 1298 } 1299 1300 @Override write(byte[] b, int off, int len)1301 public void write(byte[] b, int off, int len) throws IOException 1302 { 1303 } 1304 1305 } 1306 1307 } 1308