diff --git a/build.gradle b/build.gradle
index cd4f0256dba8..f816fc2d62c5 100644
--- a/build.gradle
+++ b/build.gradle
@@ -85,7 +85,6 @@ ext {
project(':h2o-webserver-iface'),
project(':h2o-jetty-8'),
project(':h2o-jetty-9'),
- project(':h2o-jetty-9-ext'),
project(':h2o-jetty-9-minimal'),
project(':h2o-ext-jython-cfunc'),
project(':h2o-hive'),
@@ -133,7 +132,6 @@ ext {
project(':h2o-webserver-iface'),
project(':h2o-jetty-8'),
project(':h2o-jetty-9'),
- project(':h2o-jetty-9-ext'),
project(':h2o-jetty-9-minimal'),
project(':h2o-ext-jython-cfunc'),
project(':h2o-hive'),
diff --git a/gradle.properties b/gradle.properties
index 38feaa430945..756e6c7739e5 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -52,16 +52,17 @@ httpClientVersion=4.5.2
defaultParquetVersion=1.12.3
# Default Hadoop client version
-defaultHadoopVersion=3.3.5
+defaultHadoopVersion=3.3.6
defaultHdfsDependency=hadoop-hdfs-client
defaultWebserverModule=h2o-jetty-9
-# default module to be included in assemblies
-defaultExtWebserverModule=h2o-jetty-9-ext
# jetty 8 version is used by Hadoop 2.x builds
jetty8version=8.2.0.v20160908
-# jetty 9 version is used in the main standalone assembly
+# jetty 9 version is used for compilation of h2o-jetty-9 module and serves as base jetty hadoop version for hadoop 3.0
+# packages. Each hadoop package can upgrade its jetty version.
jetty9version=9.4.11.v20180605
+# jetty 9 version is used in the main standalone assembly
+jetty9MainVersion=9.4.51.v20230217
# jetty-minimal 9 version is used in the minimal standalone assembly
jetty9MinimalVersion=9.4.51.v20230217
servletApiVersion=3.1.0
diff --git a/h2o-assemblies/main/build.gradle b/h2o-assemblies/main/build.gradle
index 8351fde0fb65..ec63f9c00e73 100644
--- a/h2o-assemblies/main/build.gradle
+++ b/h2o-assemblies/main/build.gradle
@@ -9,7 +9,6 @@ apply from: '../standalone_assembly.gradle'
dependencies {
api project(":h2o-app")
api project(":h2o-logging-impl-log4j2")
- runtimeOnly project(":${defaultExtWebserverModule}")
api project(":h2o-web")
api project(":h2o-avro-parser")
api project(":h2o-persist-gcs")
@@ -35,6 +34,15 @@ dependencies {
exclude group: "org.apache.zookeeper"
exclude group: "org.eclipse.jetty"
}
+
+ // Upgrade dependencies of h2o-jetty-9
+ api "org.eclipse.jetty:jetty-server:${jetty9MainVersion}"
+ api "org.eclipse.jetty:jetty-servlets:${jetty9MainVersion}"
+ api "org.eclipse.jetty:jetty-jaas:${jetty9MainVersion}"
+ api "org.eclipse.jetty:jetty-proxy:${jetty9MainVersion}"
+ api "org.eclipse.jetty:jetty-servlet:${jetty9MainVersion}"
+ api "org.eclipse.jetty.websocket:websocket-api:${jetty9MainVersion}"
+ api "org.eclipse.jetty.websocket:websocket-server:${jetty9MainVersion}"
// Need to a newer org.apache.hadoop.hive.shims.ShimLoader to make older hive JDBC drivers work on Hadoop 3.
implementation 'org.apache.hive.shims:hive-shims-common:2.3.9'
diff --git a/h2o-jetty-9-ext/build.gradle b/h2o-jetty-9-ext/build.gradle
deleted file mode 100644
index a8448a109798..000000000000
--- a/h2o-jetty-9-ext/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-dependencies {
- api project(":h2o-jetty-9")
-}
-
-if ("${jetty9version}".toString() != "9.4.11.v20180605") {
- throw new IllegalStateException(
- "This module expects Jetty version 9.4.11.v20180605, configured version is ${jetty9version}. " +
- "Please locate Response class (modified from Jetty 9.4.11), upgrade it to your version, port our custom changes." +
- "Once you've done that you can modify this check.")
-}
diff --git a/h2o-jetty-9-ext/src/main/java/org/eclipse/jetty/server/Response.java b/h2o-jetty-9-ext/src/main/java/org/eclipse/jetty/server/Response.java
deleted file mode 100644
index 4abdcc24dbda..000000000000
--- a/h2o-jetty-9-ext/src/main/java/org/eclipse/jetty/server/Response.java
+++ /dev/null
@@ -1,1493 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2018 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.nio.channels.IllegalSelectorException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.function.Supplier;
-import java.util.stream.Collectors;
-
-import javax.servlet.RequestDispatcher;
-import javax.servlet.ServletOutputStream;
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-import org.eclipse.jetty.http.CookieCompliance;
-import org.eclipse.jetty.http.DateGenerator;
-import org.eclipse.jetty.http.HttpContent;
-import org.eclipse.jetty.http.HttpCookie;
-import org.eclipse.jetty.http.HttpField;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpGenerator;
-import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpHeaderValue;
-import org.eclipse.jetty.http.HttpScheme;
-import org.eclipse.jetty.http.HttpStatus;
-import org.eclipse.jetty.http.HttpURI;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.http.MetaData;
-import org.eclipse.jetty.http.MimeTypes;
-import org.eclipse.jetty.http.PreEncodedHttpField;
-import org.eclipse.jetty.http.Syntax;
-import org.eclipse.jetty.io.RuntimeIOException;
-import org.eclipse.jetty.server.handler.ContextHandler;
-import org.eclipse.jetty.server.handler.ErrorHandler;
-import org.eclipse.jetty.server.session.SessionHandler;
-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;
-import water.webserver.jetty9.H2OHttpConfiguration;
-
-/**
- *
{@link Response} provides the implementation for {@link HttpServletResponse}.
- */
-public class Response implements HttpServletResponse
-{
- private static final Logger LOG = Log.getLogger(Response.class);
- private static final String __COOKIE_DELIM="\",;\\ \t";
- private static final String __01Jan1970_COOKIE = DateGenerator.formatCookieDate(0).trim();
- private static final int __MIN_BUFFER_SIZE = 1;
- private static final HttpField __EXPIRES_01JAN1970 = new PreEncodedHttpField(HttpHeader.EXPIRES,DateGenerator.__01Jan1970);
- // Cookie building buffer. Reduce garbage for cookie using applications
- private static final ThreadLocal __cookieBuilder = ThreadLocal.withInitial(() -> new StringBuilder(128));
-
- public enum OutputType
- {
- NONE, STREAM, WRITER
- }
-
- /**
- * 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__";
-
- private final HttpChannel _channel;
- private final HttpFields _fields = new HttpFields();
- private final AtomicInteger _include = new AtomicInteger();
- private final HttpOutput _out;
- private int _status = HttpStatus.OK_200;
- private String _reason;
- private Locale _locale;
- private MimeTypes.Type _mimeType;
- private String _characterEncoding;
- private EncodingFrom _encodingFrom=EncodingFrom.NOT_SET;
- private String _contentType;
- private OutputType _outputType = OutputType.NONE;
- private ResponseWriter _writer;
- private long _contentLength = -1;
- private Supplier trailers;
-
- private enum EncodingFrom { NOT_SET, INFERRED, SET_LOCALE, SET_CONTENT_TYPE, SET_CHARACTER_ENCODING }
- private static final EnumSet __localeOverride = EnumSet.of(EncodingFrom.NOT_SET,EncodingFrom.INFERRED);
- private static final EnumSet __explicitCharset = EnumSet.of(EncodingFrom.SET_LOCALE,EncodingFrom.SET_CHARACTER_ENCODING);
-
- public Response(HttpChannel channel, HttpOutput out)
- {
- _channel = channel;
- _out = out;
- }
-
- public HttpChannel getHttpChannel()
- {
- return _channel;
- }
-
- protected void recycle()
- {
- _status = HttpStatus.OK_200;
- _reason = null;
- _locale = null;
- _mimeType = null;
- _characterEncoding = null;
- _contentType = null;
- _outputType = OutputType.NONE;
- _contentLength = -1;
- _out.recycle();
- _fields.clear();
- _encodingFrom=EncodingFrom.NOT_SET;
- }
-
- public HttpOutput getHttpOutput()
- {
- return _out;
- }
-
- public boolean isIncluding()
- {
- return _include.get() > 0;
- }
-
- public void include()
- {
- _include.incrementAndGet();
- }
-
- public void included()
- {
- _include.decrementAndGet();
- if (_outputType == OutputType.WRITER)
- {
- _writer.reopen();
- }
- _out.reopen();
- }
-
- public void addCookie(HttpCookie cookie)
- {
- if (StringUtil.isBlank(cookie.getName()))
- {
- throw new IllegalArgumentException("Cookie.name cannot be blank/null");
- }
-
- if (getHttpChannel().getHttpConfiguration().isCookieCompliance(CookieCompliance.RFC2965))
- addSetRFC2965Cookie(
- cookie.getName(),
- cookie.getValue(),
- cookie.getDomain(),
- cookie.getPath(),
- cookie.getMaxAge(),
- cookie.getComment(),
- cookie.isSecure(),
- cookie.isHttpOnly(),
- cookie.getVersion());
- else
- addSetRFC6265Cookie(
- cookie.getName(),
- cookie.getValue(),
- cookie.getDomain(),
- cookie.getPath(),
- cookie.getMaxAge(),
- cookie.isSecure(),
- cookie.isHttpOnly());
- }
-
- @Override
- public void addCookie(Cookie cookie)
- {
- String comment = cookie.getComment();
- boolean httpOnly = false;
-
- if (comment != null)
- {
- int i = comment.indexOf(HTTP_ONLY_COMMENT);
- if (i >= 0)
- {
- httpOnly = true;
- comment = comment.replace(HTTP_ONLY_COMMENT, "").trim();
- if (comment.length() == 0)
- comment = null;
- }
- }
-
- if (StringUtil.isBlank(cookie.getName()))
- {
- throw new IllegalArgumentException("Cookie.name cannot be blank/null");
- }
-
- if (getHttpChannel().getHttpConfiguration().isCookieCompliance(CookieCompliance.RFC2965))
- addSetRFC2965Cookie(cookie.getName(),
- cookie.getValue(),
- cookie.getDomain(),
- cookie.getPath(),
- cookie.getMaxAge(),
- comment,
- cookie.getSecure(),
- httpOnly || cookie.isHttpOnly(),
- cookie.getVersion());
- else
- addSetRFC6265Cookie(cookie.getName(),
- cookie.getValue(),
- cookie.getDomain(),
- cookie.getPath(),
- cookie.getMaxAge(),
- cookie.getSecure(),
- httpOnly || cookie.isHttpOnly());
- }
-
-
- /**
- * Format a set cookie value by RFC6265
- *
- * @param name the name
- * @param value the value
- * @param domain the domain
- * @param path the path
- * @param maxAge the maximum age
- * @param isSecure true if secure cookie
- * @param isHttpOnly true if for http only
- */
- public void addSetRFC6265Cookie(
- final String name,
- final String value,
- final String domain,
- final String path,
- final long maxAge,
- final boolean isSecure,
- final boolean isHttpOnly)
- {
- // Check arguments
- if (name == null || name.length() == 0)
- throw new IllegalArgumentException("Bad cookie name");
-
- // Name is checked for legality by servlet spec, but can also be passed directly so check again for quoting
- // Per RFC6265, Cookie.name follows RFC2616 Section 2.2 token rules
- Syntax.requireValidRFC2616Token(name, "RFC6265 Cookie name");
- // Ensure that Per RFC6265, Cookie.value follows syntax rules
- Syntax.requireValidRFC6265CookieValue(value);
-
- // Format value and params
- StringBuilder buf = __cookieBuilder.get();
- buf.setLength(0);
- buf.append(name).append('=').append(value==null?"":value);
-
- // Append path
- if (path!=null && path.length()>0)
- buf.append(";Path=").append(path);
-
- // Append domain
- if (domain!=null && domain.length()>0)
- buf.append(";Domain=").append(domain);
-
- // Handle max-age and/or expires
- if (maxAge >= 0)
- {
- // Always use expires
- // This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
- buf.append(";Expires=");
- if (maxAge == 0)
- buf.append(__01Jan1970_COOKIE);
- else
- DateGenerator.formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
-
- buf.append(";Max-Age=");
- buf.append(maxAge);
- }
-
- // add the other fields
- if (isSecure)
- buf.append(";Secure");
- if (isHttpOnly)
- buf.append(";HttpOnly");
-
- // add the set cookie
- _fields.add(HttpHeader.SET_COOKIE, buf.toString());
-
- // Expire responses with set-cookie headers so they do not get cached.
- _fields.put(__EXPIRES_01JAN1970);
-
- }
-
- /**
- * Format a set cookie value
- *
- * @param name the name
- * @param value the value
- * @param domain the domain
- * @param path the path
- * @param maxAge the maximum age
- * @param comment the comment (only present on versions > 0)
- * @param isSecure true if secure cookie
- * @param isHttpOnly true if for http only
- * @param version version of cookie logic to use (0 == default behavior)
- */
- public void addSetRFC2965Cookie(
- final String name,
- final String value,
- final String domain,
- final String path,
- final long maxAge,
- final String comment,
- final boolean isSecure,
- final boolean isHttpOnly,
- int version)
- {
- // Check arguments
- if (name == null || name.length() == 0)
- throw new IllegalArgumentException("Bad cookie name");
-
- // 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('=');
-
- // Append the value
- boolean quote_value=isQuoteNeededForCookie(value);
- quoteOnlyOrAppend(buf,value,quote_value);
-
- // Look for domain and path fields and check if they need to be quoted
- boolean has_domain = domain!=null && domain.length()>0;
- 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) ||
- QuotedStringTokenizer.isQuoted(path) || QuotedStringTokenizer.isQuoted(domain)))
- version=1;
-
- // Append version
- if (version==1)
- 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)
- {
- buf.append(";Domain=");
- quoteOnlyOrAppend(buf,domain,quote_domain);
- }
-
- // Handle max-age and/or expires
- if (maxAge >= 0)
- {
- // Always use expires
- // This is required as some browser (M$ this means you!) don't handle max-age even with v1 cookies
- buf.append(";Expires=");
- if (maxAge == 0)
- buf.append(__01Jan1970_COOKIE);
- else
- DateGenerator.formatCookieDate(buf, System.currentTimeMillis() + 1000L * maxAge);
-
- // for v1 cookies, also send max-age
- if (version>=1)
- {
- buf.append(";Max-Age=");
- buf.append(maxAge);
- }
- }
-
- // add the other fields
- if (isSecure)
- buf.append(";Secure");
- if (isHttpOnly)
- buf.append(";HttpOnly");
- if (comment != null)
- {
- buf.append(";Comment=");
- quoteOnlyOrAppend(buf,comment,isQuoteNeededForCookie(comment));
- }
-
- // add the set cookie
- _fields.add(HttpHeader.SET_COOKIE, buf.toString());
-
- // Expire responses with set-cookie headers so they do not get cached.
- _fields.put(__EXPIRES_01JAN1970);
- }
-
-
- /* ------------------------------------------------------------ */
- /** Does a cookie value need to be quoted?
- * @param s value string
- * @return true if quoted;
- * @throws IllegalArgumentException If there a control characters in the string
- */
- private static boolean isQuoteNeededForCookie(String s)
- {
- if (s==null || s.length()==0)
- return true;
-
- if (QuotedStringTokenizer.isQuoted(s))
- return false;
-
- for (int i=0;i=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)
- QuotedStringTokenizer.quoteOnly(buf,s);
- else
- buf.append(s);
- }
-
- @Override
- public boolean containsHeader(String name)
- {
- return _fields.containsKey(name);
- }
-
- @Override
- public String encodeURL(String url)
- {
- final Request request = _channel.getRequest();
- SessionHandler sessionManager = request.getSessionHandler();
-
- 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 = HttpScheme.HTTPS.asString().equalsIgnoreCase(uri.getScheme()) ? 443 : 80;
-
- // Is it the same server?
- if (!request.getServerName().equalsIgnoreCase(uri.getHost()))
- return url;
- if (request.getServerPort() != port)
- return url;
- if (!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.getExtendedId(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 +
- ((HttpScheme.HTTPS.is(uri.getScheme()) || HttpScheme.HTTP.is(uri.getScheme())) && uri.getPath() == null ? "/" : "") + //if no path, insert the root path
- sessionURLPrefix + id;
- }
-
-
- return url.substring(0, suffix) +
- ((HttpScheme.HTTPS.is(uri.getScheme()) || HttpScheme.HTTP.is(uri.getScheme())) && uri.getPath() == null ? "/" : "") + //if no path so insert the root path
- sessionURLPrefix + id + url.substring(suffix);
- }
-
- @Override
- public String encodeRedirectURL(String url)
- {
- return encodeURL(url);
- }
-
- @Override
- @Deprecated
- public String encodeUrl(String url)
- {
- return encodeURL(url);
- }
-
- @Override
- @Deprecated
- public String encodeRedirectUrl(String url)
- {
- return encodeRedirectURL(url);
- }
-
- @Override
- public void sendError(int sc) throws IOException
- {
- sendError(sc, null);
- }
-
- @Override
- public void sendError(int code, String message) throws IOException
- {
- if (isIncluding())
- return;
-
- if (isCommitted())
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Aborting on sendError on committed response {} {}",code,message);
- code=-1;
- }
- else
- resetBuffer();
-
-
- switch(code)
- {
- case -1:
- _channel.abort(new IOException());
- return;
- case 102:
- sendProcessing();
- return;
- default:
- break;
- }
-
-
- _outputType = OutputType.NONE;
- setContentType(null);
- setCharacterEncoding(null);
- setHeader(HttpHeader.EXPIRES,null);
- setHeader(HttpHeader.LAST_MODIFIED,null);
- setHeader(HttpHeader.CACHE_CONTROL,null);
- setHeader(HttpHeader.CONTENT_TYPE,null);
- setHeader(HttpHeader.CONTENT_LENGTH, null);
-
- setStatus(code);
-
- Request request = _channel.getRequest();
- Throwable cause = (Throwable)request.getAttribute(Dispatcher.ERROR_EXCEPTION);
- if (message==null)
- {
- _reason=HttpStatus.getMessage(code);
- message=cause==null?_reason:cause.toString();
- }
- else
- _reason=message;
-
- // 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)
- {
- ContextHandler.Context context = request.getContext();
- ContextHandler contextHandler = context == null ? _channel.getState().getContextHandler() : context.getContextHandler();
- 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());
- ErrorHandler error_handler = ErrorHandler.getErrorHandler(_channel.getServer(), contextHandler);
- if (error_handler!=null)
- error_handler.handle(null, request, request, this);
- }
- if (!request.isAsyncStarted())
- closeOutput();
- }
-
- /**
- * Sends 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.
- * @throws IOException if unable to send the 102 response
- * @see javax.servlet.http.HttpServletResponse#sendError(int)
- */
- public void sendProcessing() throws IOException
- {
- if (_channel.isExpecting102Processing() && !isCommitted())
- {
- _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
- * @param location the location to send in {@code Location} headers
- * @throws IOException if unable to send the redirect
- */
- public void sendRedirect(int code, String location) throws IOException
- {
- if ((code < HttpServletResponse.SC_MULTIPLE_CHOICES) || (code >= HttpServletResponse.SC_BAD_REQUEST))
- throw new IllegalArgumentException("Not a 3xx redirect code");
-
- if (isIncluding())
- return;
-
- if (location == null)
- throw new IllegalArgumentException();
-
- if (!URIUtil.hasScheme(location))
- {
- StringBuilder buf = ((H2OHttpConfiguration) _channel.getHttpConfiguration()).isRelativeRedirectAllowed()
- ? new StringBuilder()
- : _channel.getRequest().getRootURL();
- if (location.startsWith("/"))
- {
- // absolute in context
- location=URIUtil.canonicalEncodedPath(location);
- }
- else
- {
- // relative to request
- String path=_channel.getRequest().getRequestURI();
- String parent=(path.endsWith("/"))?path:URIUtil.parentPath(path);
- location=URIUtil.canonicalEncodedPath(URIUtil.addEncodedPaths(parent,location));
- if (location!=null && !location.startsWith("/"))
- buf.append('/');
- }
-
- if(location==null)
- throw new IllegalStateException("path cannot be above root");
- buf.append(location);
-
- location=buf.toString();
- }
-
- resetBuffer();
- setHeader(HttpHeader.LOCATION, location);
- setStatus(code);
- closeOutput();
- }
-
- @Override
- public void sendRedirect(String location) throws IOException
- {
- sendRedirect(HttpServletResponse.SC_MOVED_TEMPORARILY, location);
- }
-
- @Override
- public void setDateHeader(String name, long date)
- {
- if (!isIncluding())
- _fields.putDateField(name, date);
- }
-
- @Override
- public void addDateHeader(String name, long date)
- {
- if (!isIncluding())
- _fields.addDateField(name, date);
- }
-
- public void setHeader(HttpHeader name, String value)
- {
- if (HttpHeader.CONTENT_TYPE == name)
- setContentType(value);
- else
- {
- if (isIncluding())
- return;
-
- _fields.put(name, value);
-
- if (HttpHeader.CONTENT_LENGTH == name)
- {
- if (value == null)
- _contentLength = -1L;
- else
- _contentLength = Long.parseLong(value);
- }
- }
- }
-
- @Override
- public void setHeader(String name, String value)
- {
- if (HttpHeader.CONTENT_TYPE.is(name))
- setContentType(value);
- else
- {
- if (isIncluding())
- {
- if (name.startsWith(SET_INCLUDE_HEADER_PREFIX))
- name = name.substring(SET_INCLUDE_HEADER_PREFIX.length());
- else
- return;
- }
- _fields.put(name, value);
- if (HttpHeader.CONTENT_LENGTH.is(name))
- {
- if (value == null)
- _contentLength = -1L;
- else
- _contentLength = Long.parseLong(value);
- }
- }
- }
-
- @Override
- public Collection getHeaderNames()
- {
- return _fields.getFieldNamesCollection();
- }
-
- @Override
- public String getHeader(String name)
- {
- return _fields.get(name);
- }
-
- @Override
- public Collection getHeaders(String name)
- {
- Collection i = _fields.getValuesList(name);
- if (i == null)
- return Collections.emptyList();
- return i;
- }
-
- @Override
- public void addHeader(String name, String value)
- {
- if (isIncluding())
- {
- if (name.startsWith(SET_INCLUDE_HEADER_PREFIX))
- name = name.substring(SET_INCLUDE_HEADER_PREFIX.length());
- else
- return;
- }
-
- if (HttpHeader.CONTENT_TYPE.is(name))
- {
- setContentType(value);
- return;
- }
-
- if (HttpHeader.CONTENT_LENGTH.is(name))
- {
- setHeader(name,value);
- return;
- }
-
- _fields.add(name, value);
- }
-
- @Override
- public void setIntHeader(String name, int value)
- {
- if (!isIncluding())
- {
- _fields.putLongField(name, value);
- if (HttpHeader.CONTENT_LENGTH.is(name))
- _contentLength = value;
- }
- }
-
- @Override
- public void addIntHeader(String name, int value)
- {
- if (!isIncluding())
- {
- _fields.add(name, Integer.toString(value));
- if (HttpHeader.CONTENT_LENGTH.is(name))
- _contentLength = value;
- }
- }
-
- @Override
- public void setStatus(int sc)
- {
- if (sc <= 0)
- throw new IllegalArgumentException();
- if (!isIncluding())
- {
- _status = sc;
- _reason = null;
- }
- }
-
- @Override
- @Deprecated
- public void setStatus(int sc, String sm)
- {
- setStatusWithReason(sc,sm);
- }
-
- public void setStatusWithReason(int sc, String sm)
- {
- if (sc <= 0)
- throw new IllegalArgumentException();
- if (!isIncluding())
- {
- _status = sc;
- _reason = sm;
- }
- }
-
- @Override
- public String getCharacterEncoding()
- {
- if (_characterEncoding == null)
- {
- String encoding = MimeTypes.getCharsetAssumedFromContentType(_contentType);
- if (encoding!=null)
- return encoding;
- encoding = MimeTypes.getCharsetInferredFromContentType(_contentType);
- if (encoding!=null)
- return encoding;
- return StringUtil.__ISO_8859_1;
- }
- return _characterEncoding;
- }
-
- @Override
- public String getContentType()
- {
- return _contentType;
- }
-
- @Override
- public ServletOutputStream getOutputStream() throws IOException
- {
- if (_outputType == OutputType.WRITER)
- throw new IllegalStateException("WRITER");
- _outputType = OutputType.STREAM;
- return _out;
- }
-
- public boolean isWriting()
- {
- return _outputType == OutputType.WRITER;
- }
-
- @Override
- public PrintWriter getWriter() throws IOException
- {
- if (_outputType == OutputType.STREAM)
- throw new IllegalStateException("STREAM");
-
- if (_outputType == OutputType.NONE)
- {
- /* get encoding from Content-Type header */
- String encoding = _characterEncoding;
- if (encoding == null)
- {
- if (_mimeType!=null && _mimeType.isCharsetAssumed())
- encoding=_mimeType.getCharsetString();
- else
- {
- encoding = MimeTypes.getCharsetAssumedFromContentType(_contentType);
- if (encoding == null)
- {
- encoding = MimeTypes.getCharsetInferredFromContentType(_contentType);
- if (encoding == null)
- encoding = StringUtil.__ISO_8859_1;
- setCharacterEncoding(encoding,EncodingFrom.INFERRED);
- }
- }
- }
-
- Locale locale = getLocale();
-
- if (_writer != null && _writer.isFor(locale,encoding))
- _writer.reopen();
- else
- {
- if (StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
- _writer = new ResponseWriter(new Iso88591HttpWriter(_out),locale,encoding);
- else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
- _writer = new ResponseWriter(new Utf8HttpWriter(_out),locale,encoding);
- 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;
- }
- return _writer;
- }
-
- @Override
- 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() || isIncluding())
- return;
-
- if (len>0)
- {
- long written = _out.getWritten();
- if (written > len)
- throw new IllegalArgumentException("setContentLength(" + len + ") when already written " + written);
-
- _contentLength = len;
- _fields.putLongField(HttpHeader.CONTENT_LENGTH, len);
- if (isAllContentWritten(written))
- {
- try
- {
- closeOutput();
- }
- catch(IOException e)
- {
- throw new RuntimeIOException(e);
- }
- }
- }
- else if (len==0)
- {
- long written = _out.getWritten();
- if (written > 0)
- throw new IllegalArgumentException("setContentLength(0) when already written " + written);
- _contentLength = len;
- _fields.put(HttpHeader.CONTENT_LENGTH, "0");
- }
- else
- {
- _contentLength = len;
- _fields.remove(HttpHeader.CONTENT_LENGTH);
- }
- }
-
- public long getContentLength()
- {
- return _contentLength;
- }
-
- public boolean isAllContentWritten(long written)
- {
- return (_contentLength >= 0 && written >= _contentLength);
- }
-
- public boolean isContentComplete(long written)
- {
- return (_contentLength < 0 || written >= _contentLength);
- }
-
- public void closeOutput() throws IOException
- {
- switch (_outputType)
- {
- case WRITER:
- _writer.close();
- if (!_out.isClosed())
- _out.close();
- break;
- case STREAM:
- if (!_out.isClosed())
- getOutputStream().close();
- break;
- default:
- if (!_out.isClosed())
- _out.close();
- }
- }
-
- public long getLongContentLength()
- {
- return _contentLength;
- }
-
- 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() || isIncluding())
- return;
- _contentLength = len;
- _fields.putLongField(HttpHeader.CONTENT_LENGTH.toString(), len);
- }
-
- @Override
- public void setContentLengthLong(long length)
- {
- setLongContentLength(length);
- }
-
- @Override
- public void setCharacterEncoding(String encoding)
- {
- setCharacterEncoding(encoding,EncodingFrom.SET_CHARACTER_ENCODING);
- }
-
- private void setCharacterEncoding(String encoding, EncodingFrom from)
- {
- if (isIncluding() || isWriting())
- return;
-
- if (_outputType != OutputType.WRITER && !isCommitted())
- {
- if (encoding == null)
- {
- _encodingFrom=EncodingFrom.NOT_SET;
-
- // Clear any encoding.
- if (_characterEncoding != null)
- {
- _characterEncoding = null;
-
- if (_mimeType!=null)
- {
- _mimeType=_mimeType.getBaseType();
- _contentType=_mimeType.asString();
- _fields.put(_mimeType.getContentTypeField());
- }
- else if (_contentType != null)
- {
- _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType);
- _fields.put(HttpHeader.CONTENT_TYPE, _contentType);
- }
- }
- }
- else
- {
- // No, so just add this one to the mimetype
- _encodingFrom = from;
- _characterEncoding = HttpGenerator.__STRICT?encoding:StringUtil.normalizeCharset(encoding);
- if (_mimeType!=null)
- {
- _contentType=_mimeType.getBaseType().asString()+ ";charset=" + _characterEncoding;
- _mimeType = MimeTypes.CACHE.get(_contentType);
- if (_mimeType==null || HttpGenerator.__STRICT)
- _fields.put(HttpHeader.CONTENT_TYPE, _contentType);
- else
- _fields.put(_mimeType.getContentTypeField());
- }
- else if (_contentType != null)
- {
- _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType) + ";charset=" + _characterEncoding;
- _fields.put(HttpHeader.CONTENT_TYPE, _contentType);
- }
- }
- }
- }
-
- @Override
- public void setContentType(String contentType)
- {
- if (isCommitted() || isIncluding())
- return;
-
- if (contentType == null)
- {
- if (isWriting() && _characterEncoding != null)
- throw new IllegalSelectorException();
-
- if (_locale == null)
- _characterEncoding = null;
- _mimeType = null;
- _contentType = null;
- _fields.remove(HttpHeader.CONTENT_TYPE);
- }
- else
- {
- _contentType = contentType;
- _mimeType = MimeTypes.CACHE.get(contentType);
-
- String charset;
- if (_mimeType!=null && _mimeType.getCharset()!=null && !_mimeType.isCharsetAssumed())
- charset=_mimeType.getCharsetString();
- else
- charset = MimeTypes.getCharsetFromContentType(contentType);
-
- if (charset == null)
- {
- switch (_encodingFrom)
- {
- case NOT_SET:
- break;
- case INFERRED:
- case SET_CONTENT_TYPE:
- if (isWriting())
- {
- _mimeType=null;
- _contentType = _contentType + ";charset=" + _characterEncoding;
- }
- else
- {
- _encodingFrom=EncodingFrom.NOT_SET;
- _characterEncoding=null;
- }
- break;
- case SET_LOCALE:
- case SET_CHARACTER_ENCODING:
- {
- _contentType = contentType + ";charset=" + _characterEncoding;
- _mimeType = null;
- }
- }
- }
- else if (isWriting() && !charset.equalsIgnoreCase(_characterEncoding))
- {
- // too late to change the character encoding;
- _mimeType = null;
- _contentType = MimeTypes.getContentTypeWithoutCharset(_contentType);
- if (_characterEncoding != null)
- _contentType = _contentType + ";charset=" + _characterEncoding;
- }
- else
- {
- _characterEncoding = charset;
- _encodingFrom = EncodingFrom.SET_CONTENT_TYPE;
- }
-
- if (HttpGenerator.__STRICT || _mimeType==null)
- _fields.put(HttpHeader.CONTENT_TYPE, _contentType);
- else
- {
- _contentType=_mimeType.asString();
- _fields.put(_mimeType.getContentTypeField());
- }
- }
- }
-
- @Override
- public void setBufferSize(int size)
- {
- if (isCommitted())
- throw new IllegalStateException("cannot set buffer size after response is in committed state");
- if (getContentCount() > 0)
- throw new IllegalStateException("cannot set buffer size after response has " + getContentCount() + " bytes already written");
- if (size < __MIN_BUFFER_SIZE)
- size = __MIN_BUFFER_SIZE;
- _out.setBufferSize(size);
- }
-
- @Override
- public int getBufferSize()
- {
- return _out.getBufferSize();
- }
-
- @Override
- public void flushBuffer() throws IOException
- {
- if (!_out.isClosed())
- _out.flush();
- }
-
- @Override
- public void reset()
- {
- reset(false);
- }
-
- public void reset(boolean preserveCookies)
- {
- resetForForward();
- _status = 200;
- _reason = null;
- _contentLength = -1;
-
- List cookies = preserveCookies
- ?_fields.stream()
- .filter(f->f.getHeader()==HttpHeader.SET_COOKIE)
- .collect(Collectors.toList()):null;
-
- _fields.clear();
-
- String connection = _channel.getRequest().getHeader(HttpHeader.CONNECTION.asString());
- if (connection != null)
- {
- for (String value: StringUtil.csvSplit(null,connection,0,connection.length()))
- {
- HttpHeaderValue cb = HttpHeaderValue.CACHE.get(value);
-
- if (cb != null)
- {
- switch (cb)
- {
- case CLOSE:
- _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.toString());
- break;
-
- case KEEP_ALIVE:
- if (HttpVersion.HTTP_1_0.is(_channel.getRequest().getProtocol()))
- _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.toString());
- break;
- case TE:
- _fields.put(HttpHeader.CONNECTION, HttpHeaderValue.TE.toString());
- break;
- default:
- }
- }
- }
- }
-
- if (preserveCookies)
- cookies.forEach(_fields::add);
- else
- {
- Request request = getHttpChannel().getRequest();
- HttpSession session = request.getSession(false);
- if (session!=null && session.isNew())
- {
- SessionHandler sh = request.getSessionHandler();
- if (sh!=null)
- {
- HttpCookie c=sh.getSessionCookie(session,request.getContextPath(),request.isSecure());
- if (c!=null)
- addCookie(c);
- }
- }
- }
- }
-
- public void resetForForward()
- {
- resetBuffer();
- _outputType = OutputType.NONE;
- }
-
- @Override
- public void resetBuffer()
- {
- _out.resetBuffer();
- }
-
- public void setTrailers(Supplier trailers)
- {
- this.trailers = trailers;
- }
-
- public Supplier getTrailers()
- {
- return trailers;
- }
-
- protected MetaData.Response newResponseMetaData()
- {
- MetaData.Response info = new MetaData.Response(_channel.getRequest().getHttpVersion(), getStatus(), getReason(), _fields, getLongContentLength());
- info.setTrailerSupplier(getTrailers());
- return info;
- }
-
- /** Get the MetaData.Response committed for this response.
- * 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
- * for logging purposes.
- * @return The committed MetaData or a {@link #newResponseMetaData()}
- * if not yet committed.
- */
- public MetaData.Response getCommittedMetaData()
- {
- MetaData.Response meta = _channel.getCommittedMetaData();
- if (meta==null)
- return newResponseMetaData();
- return meta;
- }
-
- @Override
- public boolean isCommitted()
- {
- return _channel.isCommitted();
- }
-
- @Override
- public void setLocale(Locale locale)
- {
- if (locale == null || isCommitted() || isIncluding())
- return;
-
- _locale = locale;
- _fields.put(HttpHeader.CONTENT_LANGUAGE, locale.toString().replace('_', '-'));
-
- if (_outputType != OutputType.NONE)
- return;
-
- if (_channel.getRequest().getContext() == null)
- return;
-
- String charset = _channel.getRequest().getContext().getContextHandler().getLocaleEncoding(locale);
-
- if (charset != null && charset.length() > 0 && __localeOverride.contains(_encodingFrom))
- setCharacterEncoding(charset,EncodingFrom.SET_LOCALE);
- }
-
- @Override
- public Locale getLocale()
- {
- if (_locale == null)
- return Locale.getDefault();
- return _locale;
- }
-
- @Override
- public int getStatus()
- {
- return _status;
- }
-
- public String getReason()
- {
- return _reason;
- }
-
- public HttpFields getHttpFields()
- {
- return _fields;
- }
-
- public long getContentCount()
- {
- return _out.getWritten();
- }
-
- @Override
- public String toString()
- {
- 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)
- {
- HttpField lm = content.getLastModified();
- if (lm!=null)
- _fields.put(lm);
-
- if (contentLength==0)
- {
- _fields.put(content.getContentLength());
- _contentLength=content.getContentLengthValue();
- }
- else if (contentLength>0)
- {
- _fields.putLongField(HttpHeader.CONTENT_LENGTH,contentLength);
- _contentLength=contentLength;
- }
-
- HttpField ct=content.getContentType();
- if (ct!=null)
- {
- if (_characterEncoding!=null &&
- content.getCharacterEncoding()==null &&
- content.getContentTypeValue()!=null &&
- __explicitCharset.contains(_encodingFrom))
- {
- setContentType(MimeTypes.getContentTypeWithoutCharset(content.getContentTypeValue()));
- }
- else
- {
- _fields.put(ct);
- _contentType=ct.getValue();
- _characterEncoding=content.getCharacterEncoding();
- _mimeType=content.getMimeType();
- }
- }
-
- HttpField ce=content.getContentEncoding();
- if (ce!=null)
- _fields.put(ce);
-
- if (etag)
- {
- HttpField et = content.getETag();
- if (et!=null)
- _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);
-
- if (contentLength==0)
- contentLength=content.getContentLengthValue();
- if (contentLength >=0)
- {
- if (contentLength