Mercurial Hosting > luan
diff src/org/eclipse/jetty/server/Response.java @ 802:3428c60d7cfc
replace jetty jars with source
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 07 Sep 2016 21:15:48 -0600 |
parents | |
children | 09d518d313b7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/eclipse/jetty/server/Response.java Wed Sep 07 21:15:48 2016 -0600 @@ -0,0 +1,1307 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.server; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.Locale; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletOutputStream; +import javax.servlet.SessionTrackingMode; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.eclipse.jetty.http.HttpCookie; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpGenerator; +import org.eclipse.jetty.http.HttpHeaderValues; +import org.eclipse.jetty.http.HttpHeaders; +import org.eclipse.jetty.http.HttpSchemes; +import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.HttpURI; +import org.eclipse.jetty.http.HttpVersions; +import org.eclipse.jetty.http.MimeTypes; +import org.eclipse.jetty.io.BufferCache.CachedBuffer; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.server.handler.ErrorHandler; +import org.eclipse.jetty.util.ByteArrayISO8859Writer; +import org.eclipse.jetty.util.QuotedStringTokenizer; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.URIUtil; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + +/** Response. + * <p> + * Implements {@link javax.servlet.http.HttpServletResponse} from the <code>javax.servlet.http</code> package. + * </p> + */ +public class Response implements HttpServletResponse +{ + private static final Logger LOG = Log.getLogger(Response.class); + + + public static final int + NONE=0, + STREAM=1, + WRITER=2; + + /** + * If a header name starts with this string, the header (stripped of the prefix) + * can be set during include using only {@link #setHeader(String, String)} or + * {@link #addHeader(String, String)}. + */ + public final static String SET_INCLUDE_HEADER_PREFIX = "org.eclipse.jetty.server.include."; + + /** + * If this string is found within the comment of a cookie added with {@link #addCookie(Cookie)}, then the cookie + * will be set as HTTP ONLY. + */ + public final static String HTTP_ONLY_COMMENT="__HTTP_ONLY__"; + + + /* ------------------------------------------------------------ */ + public static Response getResponse(HttpServletResponse response) + { + if (response instanceof Response) + return (Response)response; + + return AbstractHttpConnection.getCurrentConnection().getResponse(); + } + + private final AbstractHttpConnection _connection; + private int _status=SC_OK; + private String _reason; + private Locale _locale; + private String _mimeType; + private CachedBuffer _cachedMimeType; + private String _characterEncoding; + private boolean _explicitEncoding; + private String _contentType; + private volatile int _outputState; + private PrintWriter _writer; + + /* ------------------------------------------------------------ */ + /** + * + */ + public Response(AbstractHttpConnection connection) + { + _connection=connection; + } + + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#reset() + */ + protected void recycle() + { + _status=SC_OK; + _reason=null; + _locale=null; + _mimeType=null; + _cachedMimeType=null; + _characterEncoding=null; + _explicitEncoding=false; + _contentType=null; + _writer=null; + _outputState=NONE; + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#addCookie(javax.servlet.http.Cookie) + */ + public void addCookie(HttpCookie cookie) + { + _connection.getResponseFields().addSetCookie(cookie); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#addCookie(javax.servlet.http.Cookie) + */ + public void addCookie(Cookie cookie) + { + String comment=cookie.getComment(); + boolean http_only=false; + + if (comment!=null) + { + int i=comment.indexOf(HTTP_ONLY_COMMENT); + if (i>=0) + { + http_only=true; + comment=comment.replace(HTTP_ONLY_COMMENT,"").trim(); + if (comment.length()==0) + comment=null; + } + } + _connection.getResponseFields().addSetCookie(cookie.getName(), + cookie.getValue(), + cookie.getDomain(), + cookie.getPath(), + cookie.getMaxAge(), + comment, + cookie.getSecure(), + http_only || cookie.isHttpOnly(), + cookie.getVersion()); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#containsHeader(java.lang.String) + */ + public boolean containsHeader(String name) + { + return _connection.getResponseFields().containsKey(name); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#encodeURL(java.lang.String) + */ + public String encodeURL(String url) + { + final Request request=_connection.getRequest(); + SessionManager sessionManager = request.getSessionManager(); + if (sessionManager==null) + return url; + + HttpURI uri = null; + if (sessionManager.isCheckingRemoteSessionIdEncoding() && URIUtil.hasScheme(url)) + { + uri = new HttpURI(url); + String path = uri.getPath(); + path = (path == null?"":path); + int port=uri.getPort(); + if (port<0) + port = HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme())?443:80; + if (!request.getServerName().equalsIgnoreCase(uri.getHost()) || + request.getServerPort()!=port || + !path.startsWith(request.getContextPath())) //TODO the root context path is "", with which every non null string starts + return url; + } + + String sessionURLPrefix = sessionManager.getSessionIdPathParameterNamePrefix(); + if (sessionURLPrefix==null) + return url; + + if (url==null) + return null; + + // should not encode if cookies in evidence + if ((sessionManager.isUsingCookies() && request.isRequestedSessionIdFromCookie()) || !sessionManager.isUsingURLs()) + { + int prefix=url.indexOf(sessionURLPrefix); + if (prefix!=-1) + { + int suffix=url.indexOf("?",prefix); + if (suffix<0) + suffix=url.indexOf("#",prefix); + + if (suffix<=prefix) + return url.substring(0,prefix); + return url.substring(0,prefix)+url.substring(suffix); + } + return url; + } + + // get session; + HttpSession session=request.getSession(false); + + // no session + if (session == null) + return url; + + // invalid session + if (!sessionManager.isValid(session)) + return url; + + String id=sessionManager.getNodeId(session); + + if (uri == null) + uri = new HttpURI(url); + + + // Already encoded + int prefix=url.indexOf(sessionURLPrefix); + if (prefix!=-1) + { + int suffix=url.indexOf("?",prefix); + if (suffix<0) + suffix=url.indexOf("#",prefix); + + if (suffix<=prefix) + return url.substring(0,prefix+sessionURLPrefix.length())+id; + return url.substring(0,prefix+sessionURLPrefix.length())+id+ + url.substring(suffix); + } + + // edit the session + int suffix=url.indexOf('?'); + if (suffix<0) + suffix=url.indexOf('#'); + if (suffix<0) + { + return url+ + ((HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme()) || HttpSchemes.HTTP.equalsIgnoreCase(uri.getScheme())) && uri.getPath()==null?"/":"") + //if no path, insert the root path + sessionURLPrefix+id; + } + + + return url.substring(0,suffix)+ + ((HttpSchemes.HTTPS.equalsIgnoreCase(uri.getScheme()) || HttpSchemes.HTTP.equalsIgnoreCase(uri.getScheme())) && uri.getPath()==null?"/":"")+ //if no path so insert the root path + sessionURLPrefix+id+url.substring(suffix); + } + + /* ------------------------------------------------------------ */ + /** + * @see javax.servlet.http.HttpServletResponse#encodeRedirectURL(java.lang.String) + */ + public String encodeRedirectURL(String url) + { + return encodeURL(url); + } + + /* ------------------------------------------------------------ */ + @Deprecated + public String encodeUrl(String url) + { + return encodeURL(url); + } + + /* ------------------------------------------------------------ */ + @Deprecated + public String encodeRedirectUrl(String url) + { + return encodeRedirectURL(url); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#sendError(int, java.lang.String) + */ + public void sendError(int code, String message) throws IOException + { + if (_connection.isIncluding()) + return; + + if (isCommitted()) + LOG.warn("Committed before "+code+" "+message); + + resetBuffer(); + _characterEncoding=null; + setHeader(HttpHeaders.EXPIRES,null); + setHeader(HttpHeaders.LAST_MODIFIED,null); + setHeader(HttpHeaders.CACHE_CONTROL,null); + setHeader(HttpHeaders.CONTENT_TYPE,null); + setHeader(HttpHeaders.CONTENT_LENGTH,null); + + _outputState=NONE; + setStatus(code,message); + + if (message==null) + message=HttpStatus.getMessage(code); + + // If we are allowed to have a body + if (code!=SC_NO_CONTENT && + code!=SC_NOT_MODIFIED && + code!=SC_PARTIAL_CONTENT && + code>=SC_OK) + { + Request request = _connection.getRequest(); + + ErrorHandler error_handler = null; + ContextHandler.Context context = request.getContext(); + if (context!=null) + error_handler=context.getContextHandler().getErrorHandler(); + if (error_handler==null) + error_handler = _connection.getConnector().getServer().getBean(ErrorHandler.class); + if (error_handler!=null) + { + request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(code)); + request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message); + request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI()); + request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,request.getServletName()); + error_handler.handle(null,_connection.getRequest(),_connection.getRequest(),this ); + } + else + { + setHeader(HttpHeaders.CACHE_CONTROL, "must-revalidate,no-cache,no-store"); + setContentType(MimeTypes.TEXT_HTML_8859_1); + ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(2048); + if (message != null) + { + message= StringUtil.replace(message, "&", "&"); + message= StringUtil.replace(message, "<", "<"); + message= StringUtil.replace(message, ">", ">"); + } + String uri= request.getRequestURI(); + if (uri!=null) + { + uri= StringUtil.replace(uri, "&", "&"); + uri= StringUtil.replace(uri, "<", "<"); + uri= StringUtil.replace(uri, ">", ">"); + } + + writer.write("<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n"); + writer.write("<title>Error "); + writer.write(Integer.toString(code)); + writer.write(' '); + if (message==null) + message=HttpStatus.getMessage(code); + writer.write(message); + writer.write("</title>\n</head>\n<body>\n<h2>HTTP ERROR: "); + writer.write(Integer.toString(code)); + writer.write("</h2>\n<p>Problem accessing "); + writer.write(uri); + writer.write(". Reason:\n<pre> "); + writer.write(message); + writer.write("</pre>"); + writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>"); + + for (int i= 0; i < 20; i++) + writer.write("\n "); + writer.write("\n</body>\n</html>\n"); + + writer.flush(); + setContentLength(writer.size()); + writer.writeTo(getOutputStream()); + writer.destroy(); + } + } + else if (code!=SC_PARTIAL_CONTENT) + { + _connection.getRequestFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER); + _connection.getRequestFields().remove(HttpHeaders.CONTENT_LENGTH_BUFFER); + _characterEncoding=null; + _mimeType=null; + _cachedMimeType=null; + } + + complete(); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#sendError(int) + */ + public void sendError(int sc) throws IOException + { + if (sc==102) + sendProcessing(); + else + sendError(sc,null); + } + + /* ------------------------------------------------------------ */ + /* Send a 102-Processing response. + * If the connection is a HTTP connection, the version is 1.1 and the + * request has a Expect header starting with 102, then a 102 response is + * sent. This indicates that the request still be processed and real response + * can still be sent. This method is called by sendError if it is passed 102. + * @see javax.servlet.http.HttpServletResponse#sendError(int) + */ + public void sendProcessing() throws IOException + { + if (_connection.isExpecting102Processing() && !isCommitted()) + ((HttpGenerator)_connection.getGenerator()).send1xx(HttpStatus.PROCESSING_102); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#sendRedirect(java.lang.String) + */ + public void sendRedirect(String location) throws IOException + { + if (_connection.isIncluding()) + return; + + if (location==null) + throw new IllegalArgumentException(); + + if (!URIUtil.hasScheme(location)) + { + StringBuilder buf = _connection.getRequest().getRootURL(); + if (location.startsWith("/")) + buf.append(location); + else + { + String path=_connection.getRequest().getRequestURI(); + String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path); + location=URIUtil.addPaths(parent,location); + if(location==null) + throw new IllegalStateException("path cannot be above root"); + if (!location.startsWith("/")) + buf.append('/'); + buf.append(location); + } + + location=buf.toString(); + HttpURI uri = new HttpURI(location); + String path=uri.getDecodedPath(); + String canonical=URIUtil.canonicalPath(path); + if (canonical==null) + throw new IllegalArgumentException(); + if (!canonical.equals(path)) + { + buf = _connection.getRequest().getRootURL(); + buf.append(URIUtil.encodePath(canonical)); + String param=uri.getParam(); + if (param!=null) + { + buf.append(';'); + buf.append(param); + } + String query=uri.getQuery(); + if (query!=null) + { + buf.append('?'); + buf.append(query); + } + String fragment=uri.getFragment(); + if (fragment!=null) + { + buf.append('#'); + buf.append(fragment); + } + location=buf.toString(); + } + } + + resetBuffer(); + setHeader(HttpHeaders.LOCATION,location); + setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY); + complete(); + + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#setDateHeader(java.lang.String, long) + */ + public void setDateHeader(String name, long date) + { + if (!_connection.isIncluding()) + _connection.getResponseFields().putDateField(name, date); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#addDateHeader(java.lang.String, long) + */ + public void addDateHeader(String name, long date) + { + if (!_connection.isIncluding()) + _connection.getResponseFields().addDateField(name, date); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#setHeader(java.lang.String, java.lang.String) + */ + public void setHeader(String name, String value) + { + if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) + setContentType(value); + else + { + if (_connection.isIncluding()) + { + if (name.startsWith(SET_INCLUDE_HEADER_PREFIX)) + name=name.substring(SET_INCLUDE_HEADER_PREFIX.length()); + else + return; + } + _connection.getResponseFields().put(name, value); + if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) + { + if (value==null) + _connection._generator.setContentLength(-1); + else + _connection._generator.setContentLength(Long.parseLong(value)); + } + } + } + + + /* ------------------------------------------------------------ */ + public Collection<String> getHeaderNames() + { + final HttpFields fields=_connection.getResponseFields(); + return fields.getFieldNamesCollection(); + } + + /* ------------------------------------------------------------ */ + /* + */ + public String getHeader(String name) + { + return _connection.getResponseFields().getStringField(name); + } + + /* ------------------------------------------------------------ */ + /* + */ + public Collection<String> getHeaders(String name) + { + final HttpFields fields=_connection.getResponseFields(); + Collection<String> i = fields.getValuesCollection(name); + if (i==null) + return Collections.EMPTY_LIST; + return i; + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#addHeader(java.lang.String, java.lang.String) + */ + public void addHeader(String name, String value) + { + + if (_connection.isIncluding()) + { + if (name.startsWith(SET_INCLUDE_HEADER_PREFIX)) + name=name.substring(SET_INCLUDE_HEADER_PREFIX.length()); + else + return; + } + + if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(name)) + { + setContentType(value); + return; + } + + _connection.getResponseFields().add(name, value); + if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) + _connection._generator.setContentLength(Long.parseLong(value)); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#setIntHeader(java.lang.String, int) + */ + public void setIntHeader(String name, int value) + { + if (!_connection.isIncluding()) + { + _connection.getResponseFields().putLongField(name, value); + if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) + _connection._generator.setContentLength(value); + } + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#addIntHeader(java.lang.String, int) + */ + public void addIntHeader(String name, int value) + { + if (!_connection.isIncluding()) + { + _connection.getResponseFields().addLongField(name, value); + if (HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) + _connection._generator.setContentLength(value); + } + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#setStatus(int) + */ + public void setStatus(int sc) + { + setStatus(sc,null); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.http.HttpServletResponse#setStatus(int, java.lang.String) + */ + public void setStatus(int sc, String sm) + { + if (sc<=0) + throw new IllegalArgumentException(); + if (!_connection.isIncluding()) + { + _status=sc; + _reason=sm; + } + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#getCharacterEncoding() + */ + public String getCharacterEncoding() + { + if (_characterEncoding==null) + _characterEncoding=StringUtil.__ISO_8859_1; + return _characterEncoding; + } + + /* ------------------------------------------------------------ */ + String getSetCharacterEncoding() + { + return _characterEncoding; + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#getContentType() + */ + public String getContentType() + { + return _contentType; + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#getOutputStream() + */ + public ServletOutputStream getOutputStream() throws IOException + { + if (_outputState!=NONE && _outputState!=STREAM) + throw new IllegalStateException("WRITER"); + + ServletOutputStream out = _connection.getOutputStream(); + _outputState=STREAM; + return out; + } + + /* ------------------------------------------------------------ */ + public boolean isWriting() + { + return _outputState==WRITER; + } + + /* ------------------------------------------------------------ */ + public boolean isOutputing() + { + return _outputState!=NONE; + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#getWriter() + */ + public PrintWriter getWriter() throws IOException + { + if (_outputState!=NONE && _outputState!=WRITER) + throw new IllegalStateException("STREAM"); + + /* if there is no writer yet */ + if (_writer==null) + { + /* get encoding from Content-Type header */ + String encoding = _characterEncoding; + + if (encoding==null) + { + /* implementation of educated defaults */ + if(_cachedMimeType != null) + encoding = MimeTypes.getCharsetFromContentType(_cachedMimeType); + + if (encoding==null) + encoding = StringUtil.__ISO_8859_1; + + setCharacterEncoding(encoding); + } + + /* construct Writer using correct encoding */ + _writer = _connection.getPrintWriter(encoding); + } + _outputState=WRITER; + return _writer; + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#setCharacterEncoding(java.lang.String) + */ + public void setCharacterEncoding(String encoding) + { + if (_connection.isIncluding()) + return; + + if (this._outputState==0 && !isCommitted()) + { + _explicitEncoding=true; + + if (encoding==null) + { + // Clear any encoding. + if (_characterEncoding!=null) + { + _characterEncoding=null; + if (_cachedMimeType!=null) + _contentType=_cachedMimeType.toString(); + else if (_mimeType!=null) + _contentType=_mimeType; + else + _contentType=null; + + if (_contentType==null) + _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER); + else + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else + { + // No, so just add this one to the mimetype + _characterEncoding=encoding; + if (_contentType!=null) + { + int i0=_contentType.indexOf(';'); + if (i0<0) + { + _contentType=null; + if(_cachedMimeType!=null) + { + CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding); + if (content_type!=null) + { + _contentType=content_type.toString(); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type); + } + } + + if (_contentType==null) + { + _contentType = _mimeType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else + { + int i1=_contentType.indexOf("charset=",i0); + if (i1<0) + { + _contentType = _contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); + } + else + { + int i8=i1+8; + int i2=_contentType.indexOf(" ",i8); + if (i2<0) + _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); + else + _contentType=_contentType.substring(0,i8)+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= ")+_contentType.substring(i2); + } + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + } + } + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#setContentLength(int) + */ + public void setContentLength(int len) + { + // Protect from setting after committed as default handling + // of a servlet HEAD request ALWAYS sets _content length, even + // if the getHandling committed the response! + if (isCommitted() || _connection.isIncluding()) + return; + _connection._generator.setContentLength(len); + if (len>0) + { + _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len); + if (_connection._generator.isAllContentWritten()) + { + if (_outputState==WRITER) + _writer.close(); + else if (_outputState==STREAM) + { + try + { + getOutputStream().close(); + } + catch(IOException e) + { + throw new RuntimeException(e); + } + } + } + } + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#setContentLength(int) + */ + public void setLongContentLength(long len) + { + // Protect from setting after committed as default handling + // of a servlet HEAD request ALWAYS sets _content length, even + // if the getHandling committed the response! + if (isCommitted() || _connection.isIncluding()) + return; + _connection._generator.setContentLength(len); + _connection.getResponseFields().putLongField(HttpHeaders.CONTENT_LENGTH, len); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#setContentType(java.lang.String) + */ + public void setContentType(String contentType) + { + if (isCommitted() || _connection.isIncluding()) + return; + + // Yes this method is horribly complex.... but there are lots of special cases and + // as this method is called on every request, it is worth trying to save string creation. + // + + if (contentType==null) + { + if (_locale==null) + _characterEncoding=null; + _mimeType=null; + _cachedMimeType=null; + _contentType=null; + _connection.getResponseFields().remove(HttpHeaders.CONTENT_TYPE_BUFFER); + } + else + { + // Look for encoding in contentType + int i0=contentType.indexOf(';'); + + if (i0>0) + { + // we have content type parameters + + // Extract params off mimetype + _mimeType=contentType.substring(0,i0).trim(); + _cachedMimeType=MimeTypes.CACHE.get(_mimeType); + + // Look for charset + int i1=contentType.indexOf("charset=",i0+1); + if (i1>=0) + { + _explicitEncoding=true; + int i8=i1+8; + int i2 = contentType.indexOf(' ',i8); + + if (_outputState==WRITER) + { + // strip the charset and ignore; + if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' ')) + { + if (_cachedMimeType!=null) + { + CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding); + if (content_type!=null) + { + _contentType=content_type.toString(); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type); + } + else + { + _contentType=_mimeType+";charset="+_characterEncoding; + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else + { + _contentType=_mimeType+";charset="+_characterEncoding; + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else if (i2<0) + { + _contentType=contentType.substring(0,i1)+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + else + { + _contentType=contentType.substring(0,i1)+contentType.substring(i2)+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else if ((i1==i0+1 && i2<0) || (i1==i0+2 && i2<0 && contentType.charAt(i0+1)==' ')) + { + // The params are just the char encoding + _cachedMimeType=MimeTypes.CACHE.get(_mimeType); + _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8)); + + if (_cachedMimeType!=null) + { + CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding); + if (content_type!=null) + { + _contentType=content_type.toString(); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type); + } + else + { + _contentType=contentType; + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else + { + _contentType=contentType; + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else if (i2>0) + { + _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8,i2)); + _contentType=contentType; + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + else + { + _characterEncoding = QuotedStringTokenizer.unquote(contentType.substring(i8)); + _contentType=contentType; + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else // No encoding in the params. + { + _cachedMimeType=null; + _contentType=_characterEncoding==null?contentType:contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else // No params at all + { + _mimeType=contentType; + _cachedMimeType=MimeTypes.CACHE.get(_mimeType); + + if (_characterEncoding!=null) + { + if (_cachedMimeType!=null) + { + CachedBuffer content_type = _cachedMimeType.getAssociate(_characterEncoding); + if (content_type!=null) + { + _contentType=content_type.toString(); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,content_type); + } + else + { + _contentType=_mimeType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else + { + _contentType=contentType+";charset="+QuotedStringTokenizer.quoteIfNeeded(_characterEncoding,";= "); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + else if (_cachedMimeType!=null) + { + _contentType=_cachedMimeType.toString(); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_cachedMimeType); + } + else + { + _contentType=contentType; + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + } + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#setBufferSize(int) + */ + public void setBufferSize(int size) + { + if (isCommitted() || getContentCount()>0) + throw new IllegalStateException("Committed or content written"); + _connection.getGenerator().increaseContentBufferSize(size); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#getBufferSize() + */ + public int getBufferSize() + { + return _connection.getGenerator().getContentBufferSize(); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#flushBuffer() + */ + public void flushBuffer() throws IOException + { + _connection.flushResponse(); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#reset() + */ + public void reset() + { + resetBuffer(); + fwdReset(); + _status=200; + _reason=null; + + HttpFields response_fields=_connection.getResponseFields(); + + response_fields.clear(); + String connection=_connection.getRequestFields().getStringField(HttpHeaders.CONNECTION_BUFFER); + if (connection!=null) + { + String[] values = connection.split(","); + for (int i=0;values!=null && i<values.length;i++) + { + CachedBuffer cb = HttpHeaderValues.CACHE.get(values[0].trim()); + + if (cb!=null) + { + switch(cb.getOrdinal()) + { + case HttpHeaderValues.CLOSE_ORDINAL: + response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.CLOSE_BUFFER); + break; + + case HttpHeaderValues.KEEP_ALIVE_ORDINAL: + if (HttpVersions.HTTP_1_0.equalsIgnoreCase(_connection.getRequest().getProtocol())) + response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.KEEP_ALIVE); + break; + case HttpHeaderValues.TE_ORDINAL: + response_fields.put(HttpHeaders.CONNECTION_BUFFER,HttpHeaderValues.TE); + break; + } + } + } + } + } + + + public void reset(boolean preserveCookies) + { + if (!preserveCookies) + reset(); + else + { + HttpFields response_fields=_connection.getResponseFields(); + + ArrayList<String> cookieValues = new ArrayList<String>(5); + Enumeration<String> vals = response_fields.getValues(HttpHeaders.SET_COOKIE); + while (vals.hasMoreElements()) + cookieValues.add((String)vals.nextElement()); + + reset(); + + for (String v:cookieValues) + response_fields.add(HttpHeaders.SET_COOKIE, v); + } + } + + + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#reset() + */ + public void fwdReset() + { + resetBuffer(); + + _writer=null; + _outputState=NONE; + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#resetBuffer() + */ + public void resetBuffer() + { + if (isCommitted()) + throw new IllegalStateException("Committed"); + _connection.getGenerator().resetBuffer(); + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#isCommitted() + */ + public boolean isCommitted() + { + return _connection.isResponseCommitted(); + } + + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#setLocale(java.util.Locale) + */ + public void setLocale(Locale locale) + { + if (locale == null || isCommitted() ||_connection.isIncluding()) + return; + + _locale = locale; + _connection.getResponseFields().put(HttpHeaders.CONTENT_LANGUAGE_BUFFER,locale.toString().replace('_','-')); + + if (_explicitEncoding || _outputState!=0 ) + return; + + if (_connection.getRequest().getContext()==null) + return; + + String charset = _connection.getRequest().getContext().getContextHandler().getLocaleEncoding(locale); + + if (charset!=null && charset.length()>0) + { + _characterEncoding=charset; + + /* get current MIME type from Content-Type header */ + String type=getContentType(); + if (type!=null) + { + _characterEncoding=charset; + int semi=type.indexOf(';'); + if (semi<0) + { + _mimeType=type; + _contentType= type += ";charset="+charset; + } + else + { + _mimeType=type.substring(0,semi); + _contentType= _mimeType += ";charset="+charset; + } + + _cachedMimeType=MimeTypes.CACHE.get(_mimeType); + _connection.getResponseFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,_contentType); + } + } + } + + /* ------------------------------------------------------------ */ + /* + * @see javax.servlet.ServletResponse#getLocale() + */ + public Locale getLocale() + { + if (_locale==null) + return Locale.getDefault(); + return _locale; + } + + /* ------------------------------------------------------------ */ + /** + * @return The HTTP status code that has been set for this request. This will be <code>200<code> + * ({@link HttpServletResponse#SC_OK}), unless explicitly set through one of the <code>setStatus</code> methods. + */ + public int getStatus() + { + return _status; + } + + /* ------------------------------------------------------------ */ + /** + * @return The reason associated with the current {@link #getStatus() status}. This will be <code>null</code>, + * unless one of the <code>setStatus</code> methods have been called. + */ + public String getReason() + { + return _reason; + } + + /* ------------------------------------------------------------ */ + /** + */ + public void complete() + throws IOException + { + _connection.completeResponse(); + } + + /* ------------------------------------------------------------- */ + /** + * @return the number of bytes actually written in response body + */ + public long getContentCount() + { + if (_connection==null || _connection.getGenerator()==null) + return -1; + return _connection.getGenerator().getContentWritten(); + } + + /* ------------------------------------------------------------ */ + public HttpFields getHttpFields() + { + return _connection.getResponseFields(); + } + + /* ------------------------------------------------------------ */ + @Override + public String toString() + { + return "HTTP/1.1 "+_status+" "+ (_reason==null?"":_reason) +System.getProperty("line.separator")+ + _connection.getResponseFields().toString(); + } + + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + /* ------------------------------------------------------------ */ + private static class NullOutput extends ServletOutputStream + { + @Override + public void write(int b) throws IOException + { + } + + @Override + public void print(String s) throws IOException + { + } + + @Override + public void println(String s) throws IOException + { + } + + @Override + public void write(byte[] b, int off, int len) throws IOException + { + } + + } + +}