diff options
Diffstat (limited to 'jetty-server/src/main/java/org/eclipse/jetty/server/Response.java')
-rw-r--r-- | jetty-server/src/main/java/org/eclipse/jetty/server/Response.java | 202 |
1 files changed, 77 insertions, 125 deletions
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index 0ece821e28..fdd4cb3e54 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -51,9 +51,8 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.io.RuntimeIOException; +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.Jetty; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; @@ -65,12 +64,12 @@ import org.eclipse.jetty.util.log.Logger; */ public class Response implements HttpServletResponse { - private static final Logger LOG = Log.getLogger(Response.class); + private static final Logger LOG = Log.getLogger(Response.class); private static final String __COOKIE_DELIM="\",;\\ \t"; private final static String __01Jan1970_COOKIE = DateGenerator.formatCookieDate(0).trim(); private final static int __MIN_BUFFER_SIZE = 1; private final static HttpField __EXPIRES_01JAN1970 = new PreEncodedHttpField(HttpHeader.EXPIRES,DateGenerator.__01Jan1970); - + // Cookie building buffer. Reduce garbage for cookie using applications private static final ThreadLocal<StringBuilder> __cookieBuilder = new ThreadLocal<StringBuilder>() @@ -81,7 +80,7 @@ public class Response implements HttpServletResponse return new StringBuilder(128); } }; - + public enum OutputType { NONE, STREAM, WRITER @@ -114,7 +113,7 @@ public class Response implements HttpServletResponse private OutputType _outputType = OutputType.NONE; private ResponseWriter _writer; private long _contentLength = -1; - + public Response(HttpChannel channel, HttpOutput out) { @@ -141,7 +140,7 @@ public class Response implements HttpServletResponse _fields.clear(); _explicitEncoding=false; } - + public HttpOutput getHttpOutput() { return _out; @@ -178,7 +177,7 @@ public class Response implements HttpServletResponse cookie.getComment(), cookie.isSecure(), cookie.isHttpOnly(), - cookie.getVersion());; + cookie.getVersion()); } @Override @@ -241,13 +240,13 @@ public class Response implements HttpServletResponse // Format value and params StringBuilder buf = __cookieBuilder.get(); buf.setLength(0); - + // Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting boolean quote_name=isQuoteNeededForCookie(name); quoteOnlyOrAppend(buf,name,quote_name); - + buf.append('='); - + // Remember name= part to look for other matching set-cookie String name_equals=buf.toString(); @@ -260,7 +259,7 @@ public class Response implements HttpServletResponse boolean quote_domain = has_domain && isQuoteNeededForCookie(domain); boolean has_path = path!=null && path.length()>0; boolean quote_path = has_path && isQuoteNeededForCookie(path); - + // Upgrade the version if we have a comment or we need to quote value/path/domain or if they were already quoted if (version==0 && ( comment!=null || quote_name || quote_value || quote_domain || quote_path || QuotedStringTokenizer.isQuoted(name) || QuotedStringTokenizer.isQuoted(value) || @@ -272,14 +271,14 @@ public class Response implements HttpServletResponse buf.append (";Version=1"); else if (version>1) buf.append (";Version=").append(version); - + // Append path if (has_path) { buf.append(";Path="); quoteOnlyOrAppend(buf,path,quote_path); } - + // Append domain if (has_domain) { @@ -297,7 +296,7 @@ public class Response implements HttpServletResponse buf.append(__01Jan1970_COOKIE); else DateGenerator.formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge); - + // for v1 cookies, also send max-age if (version>=1) { @@ -336,7 +335,7 @@ public class Response implements HttpServletResponse } } } - + // add the set cookie _fields.add(HttpHeader.SET_COOKIE.toString(), buf.toString()); @@ -355,7 +354,7 @@ public class Response implements HttpServletResponse { if (s==null || s.length()==0) return true; - + if (QuotedStringTokenizer.isQuoted(s)) return false; @@ -364,15 +363,15 @@ public class Response implements HttpServletResponse char c = s.charAt(i); if (__COOKIE_DELIM.indexOf(c)>=0) return true; - + if (c<0x20 || c>=0x7f) throw new IllegalArgumentException("Illegal character in cookie value"); } return false; } - - + + private static void quoteOnlyOrAppend(StringBuilder buf, String s, boolean quote) { if (quote) @@ -380,7 +379,7 @@ public class Response implements HttpServletResponse else buf.append(s); } - + @Override public boolean containsHeader(String name) { @@ -404,7 +403,7 @@ public class Response implements HttpServletResponse int port = uri.getPort(); if (port < 0) port = HttpScheme.HTTPS.asString().equalsIgnoreCase(uri.getScheme()) ? 443 : 80; - + // Is it the same server? if (!request.getServerName().equalsIgnoreCase(uri.getHost())) return url; @@ -422,7 +421,7 @@ public class Response implements HttpServletResponse return null; // should not encode if cookies in evidence - if ((sessionManager.isUsingCookies() && request.isRequestedSessionIdFromCookie()) || !sessionManager.isUsingURLs()) + if ((sessionManager.isUsingCookies() && request.isRequestedSessionIdFromCookie()) || !sessionManager.isUsingURLs()) { int prefix = url.indexOf(sessionURLPrefix); if (prefix != -1) @@ -449,7 +448,7 @@ public class Response implements HttpServletResponse if (!sessionManager.isValid(session)) return url; - String id = sessionManager.getNodeId(session); + String id = sessionManager.getExtendedId(session); if (uri == null) uri = new HttpURI(url); @@ -524,7 +523,7 @@ public class Response implements HttpServletResponse LOG.debug("Aborting on sendError on committed response {} {}",code,message); code=-1; } - + switch(code) { case -1: @@ -534,91 +533,44 @@ public class Response implements HttpServletResponse sendProcessing(); return; default: + break; } - if (isCommitted()) - LOG.warn("Committed before "+code+" "+message); - resetBuffer(); + _mimeType=null; _characterEncoding=null; + _outputType = OutputType.NONE; setHeader(HttpHeader.EXPIRES,null); setHeader(HttpHeader.LAST_MODIFIED,null); setHeader(HttpHeader.CACHE_CONTROL,null); setHeader(HttpHeader.CONTENT_TYPE,null); - setHeader(HttpHeader.CONTENT_LENGTH,null); + setHeader(HttpHeader.CONTENT_LENGTH, null); - _outputType = OutputType.NONE; setStatus(code); - _reason=message; Request request = _channel.getRequest(); Throwable cause = (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION); if (message==null) - message=cause==null?HttpStatus.getMessage(code):cause.toString(); + { + _reason=HttpStatus.getMessage(code); + message=cause==null?_reason:cause.toString(); + } + else + _reason=message; - // If we are allowed to have a body - if (code!=SC_NO_CONTENT && - code!=SC_NOT_MODIFIED && - code!=SC_PARTIAL_CONTENT && - code>=SC_OK) - { - ErrorHandler error_handler = ErrorHandler.getErrorHandler(_channel.getServer(),request.getContext()==null?null:request.getContext().getContextHandler()); - 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,_channel.getRequest(),_channel.getRequest(),this ); - } - else - { - setHeader(HttpHeader.CACHE_CONTROL, "must-revalidate,no-cache,no-store"); - setContentType(MimeTypes.Type.TEXT_HTML_8859_1.toString()); - try (ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(2048);) - { - message=StringUtil.sanitizeXmlString(message); - String uri= request.getRequestURI(); - uri=StringUtil.sanitizeXmlString(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) - 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 />"); - - getHttpChannel().getHttpConfiguration().writePoweredBy(writer,null,"<hr/>"); - writer.write("\n</body>\n</html>\n"); - - writer.flush(); - setContentLength(writer.size()); - try (ServletOutputStream outputStream = getOutputStream()) - { - writer.writeTo(outputStream); - writer.destroy(); - } - } - } - } - else if (code!=SC_PARTIAL_CONTENT) + // If we are allowed to have a body, then produce the error page. + if (code != SC_NO_CONTENT && code != SC_NOT_MODIFIED && + code != SC_PARTIAL_CONTENT && code >= SC_OK) { - // TODO work out why this is required? - _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_TYPE); - _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_LENGTH); - _characterEncoding=null; - _mimeType=null; + ContextHandler.Context context = request.getContext(); + ContextHandler contextHandler = context == null ? _channel.getState().getContextHandler() : context.getContextHandler(); + ErrorHandler error_handler = ErrorHandler.getErrorHandler(_channel.getServer(), contextHandler); + request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 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, request, request, this); } - - closeOutput(); } /** @@ -637,7 +589,7 @@ public class Response implements HttpServletResponse _channel.sendResponse(HttpGenerator.PROGRESS_102_INFO, null, true); } } - + /** * Sends a response with one of the 300 series redirection codes. * @param code the redirect status code @@ -648,7 +600,7 @@ public class Response implements HttpServletResponse { if ((code < HttpServletResponse.SC_MULTIPLE_CHOICES) || (code >= HttpServletResponse.SC_BAD_REQUEST)) throw new IllegalArgumentException("Not a 3xx redirect code"); - + if (isIncluding()) return; @@ -672,11 +624,11 @@ public class Response implements HttpServletResponse if (!location.startsWith("/")) buf.append('/'); } - + if(location==null) throw new IllegalStateException("path cannot be above root"); buf.append(location); - + location=buf.toString(); } @@ -791,13 +743,13 @@ public class Response implements HttpServletResponse setContentType(value); return; } - + if (HttpHeader.CONTENT_LENGTH.is(name)) { setHeader(name,value); return; } - + _fields.add(name, value); } @@ -822,7 +774,7 @@ public class Response implements HttpServletResponse _contentLength = value; } } - + @Override public void setStatus(int sc) { @@ -841,7 +793,7 @@ public class Response implements HttpServletResponse { setStatusWithReason(sc,sm); } - + public void setStatusWithReason(int sc, String sm) { if (sc <= 0) @@ -903,9 +855,9 @@ public class Response implements HttpServletResponse setCharacterEncoding(encoding,false); } } - + Locale locale = getLocale(); - + if (_writer != null && _writer.isFor(locale,encoding)) _writer.reopen(); else @@ -917,7 +869,7 @@ public class Response implements HttpServletResponse else _writer = new ResponseWriter(new EncodingHttpWriter(_out, encoding),locale,encoding); } - + // Set the output type at the end, because setCharacterEncoding() checks for it _outputType = OutputType.WRITER; } @@ -939,7 +891,7 @@ public class Response implements HttpServletResponse long written = _out.getWritten(); if (written > len) throw new IllegalArgumentException("setContentLength(" + len + ") when already written " + written); - + _fields.putLongField(HttpHeader.CONTENT_LENGTH, len); if (isAllContentWritten(written)) { @@ -963,7 +915,7 @@ public class Response implements HttpServletResponse else _fields.remove(HttpHeader.CONTENT_LENGTH); } - + public long getContentLength() { return _contentLength; @@ -1006,7 +958,7 @@ public class Response implements HttpServletResponse _contentLength = len; _fields.putLongField(HttpHeader.CONTENT_LENGTH.toString(), len); } - + @Override public void setContentLengthLong(long length) { @@ -1018,7 +970,7 @@ public class Response implements HttpServletResponse { setCharacterEncoding(encoding,true); } - + private void setCharacterEncoding(String encoding, boolean explicit) { if (isIncluding() || isWriting()) @@ -1029,12 +981,12 @@ public class Response implements HttpServletResponse if (encoding == null) { _explicitEncoding=false; - + // Clear any encoding. if (_characterEncoding != null) { _characterEncoding = null; - + if (_mimeType!=null) { _mimeType=_mimeType.getBaseType(); @@ -1070,7 +1022,7 @@ public class Response implements HttpServletResponse } } } - + @Override public void setContentType(String contentType) { @@ -1092,7 +1044,7 @@ public class Response implements HttpServletResponse { _contentType = contentType; _mimeType = MimeTypes.CACHE.get(contentType); - + String charset; if (_mimeType!=null && _mimeType.getCharset()!=null && !_mimeType.isCharsetAssumed()) charset=_mimeType.getCharsetString(); @@ -1129,7 +1081,7 @@ public class Response implements HttpServletResponse _fields.put(_mimeType.getContentTypeField()); } } - + } @Override @@ -1165,7 +1117,7 @@ public class Response implements HttpServletResponse _fields.clear(); String connection = _channel.getRequest().getHeader(HttpHeader.CONNECTION.asString()); - + if (connection != null) { for (String value: StringUtil.csvSplit(null,connection,0,connection.length())) @@ -1195,12 +1147,12 @@ public class Response implements HttpServletResponse } public void reset(boolean preserveCookies) - { + { if (!preserveCookies) reset(); else { - ArrayList<String> cookieValues = new ArrayList<String>(5); + ArrayList<String> cookieValues = new ArrayList<>(5); Enumeration<String> vals = _fields.getValues(HttpHeader.SET_COOKIE.asString()); while (vals.hasMoreElements()) cookieValues.add(vals.nextElement()); @@ -1229,11 +1181,11 @@ public class Response implements HttpServletResponse { return new MetaData.Response(_channel.getRequest().getHttpVersion(), getStatus(), getReason(), _fields, getLongContentLength()); } - + /** Get the MetaData.Response committed for this response. - * This may differ from the meta data in this response for + * This may differ from the meta data in this response for * exceptional responses (eg 4xx and 5xx responses generated - * by the container) and the committedMetaData should be used + * by the container) and the committedMetaData should be used * for logging purposes. * @return The committed MetaData or a {@link #newResponseMetaData()} * if not yet committed. @@ -1307,7 +1259,7 @@ public class Response implements HttpServletResponse { return String.format("%s %d %s%n%s", _channel.getRequest().getHttpVersion(), _status, _reason == null ? "" : _reason, _fields); } - + public void putHeaders(HttpContent content,long contentLength, boolean etag) { @@ -1334,11 +1286,11 @@ public class Response implements HttpServletResponse _characterEncoding=content.getCharacterEncoding(); _mimeType=content.getMimeType(); } - + HttpField ce=content.getContentEncoding(); if (ce!=null) _fields.put(ce); - + if (etag) { HttpField et = content.getETag(); @@ -1346,9 +1298,9 @@ public class Response implements HttpServletResponse _fields.put(et); } } - + public static void putHeaders(HttpServletResponse response, HttpContent content, long contentLength, boolean etag) - { + { long lml=content.getResource().lastModified(); if (lml>=0) response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(),lml); @@ -1370,7 +1322,7 @@ public class Response implements HttpServletResponse String ce=content.getContentEncodingValue(); if (ce!=null) response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),ce); - + if (etag) { String et=content.getETagValue(); |