--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock
Hi,
This is a pre-upload request to update tomcat8 to the version 8.5.14-1
in stretch. This version is mostly a bug fix release, it also integrates
the fixes for 3 vulnerabilities (CVE-2017-5647, CVE-2017-5650,
CVE-2017-5651).
The full changelog is available here:
http://tomcat.apache.org/tomcat-8.5-doc/changelog.html#Tomcat_8.5.14_(markt)
Backporting security fixes to the stable version of Tomcat has proven
to be a bit tedious in the past, so it would be appreciable to have
a version as fresh as possible in stretch to ease the maintenance work
on the long term.
Thank you,
Emmanuel Bourg
diff --git a/build.properties.default b/build.properties.default
index c5e67cdf..bfe9053d 100644
--- a/build.properties.default
+++ b/build.properties.default
@@ -25,7 +25,7 @@
# ----- Version Control Flags -----
version.major=8
version.minor=5
-version.build=12
+version.build=14
version.patch=0
version.suffix=
diff --git a/debian/changelog b/debian/changelog
index 5cbb4174..18fcdb12 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+tomcat8 (8.5.14-1) unstable; urgency=medium
+
+ * Team upload.
+ * New upstream release
+ - Removed the CVE patches (fixed in this release)
+
+ -- Emmanuel Bourg <ebourg@apache.org> Fri, 21 Apr 2017 08:38:28 +0200
+
tomcat8 (8.5.12-1) unstable; urgency=medium
* Team upload.
diff --git a/debian/patches/CVE-2017-5647.patch b/debian/patches/CVE-2017-5647.patch
deleted file mode 100644
index e746785c..00000000
--- a/debian/patches/CVE-2017-5647.patch
+++ /dev/null
@@ -1,241 +0,0 @@
-From: Markus Koschany <apo@debian.org>
-Date: Tue, 11 Apr 2017 22:18:52 +0200
-Subject: CVE-2017-5647
-
-Bug-Debian: https://bugs.debian.org/860068
-Origin: http://svn.apache.org/r1788932
----
- java/org/apache/coyote/AbstractProtocol.java | 7 ++--
- java/org/apache/coyote/http11/Http11Processor.java | 12 ++++++-
- java/org/apache/tomcat/util/net/AprEndpoint.java | 35 +++++++++++++------
- java/org/apache/tomcat/util/net/Nio2Endpoint.java | 25 +++++++++-----
- java/org/apache/tomcat/util/net/NioEndpoint.java | 26 +++++++++++----
- .../apache/tomcat/util/net/SendfileDataBase.java | 6 ++--
- .../tomcat/util/net/SendfileKeepAliveState.java | 39 ++++++++++++++++++++++
- 7 files changed, 116 insertions(+), 34 deletions(-)
- create mode 100644 java/org/apache/tomcat/util/net/SendfileKeepAliveState.java
-
---- a/java/org/apache/coyote/AbstractProtocol.java
-+++ b/java/org/apache/coyote/AbstractProtocol.java
-@@ -870,10 +870,9 @@
- wrapper.registerReadInterest();
- } else if (state == SocketState.SENDFILE) {
- // Sendfile in progress. If it fails, the socket will be
-- // closed. If it works, the socket will be re-added to the
-- // poller
-- connections.remove(socket);
-- release(processor);
-+ // closed. If it works, the socket either be added to the
-+ // poller (or equivalent) to await more data or processed
-+ // if there are any pipe-lined requests remaining.
- } else if (state == SocketState.UPGRADED) {
- // Don't add sockets back to the poller if this was a
- // non-blocking write otherwise the poller may trigger
---- a/java/org/apache/coyote/http11/Http11Processor.java
-+++ b/java/org/apache/coyote/http11/Http11Processor.java
-@@ -58,6 +58,7 @@
- import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
- import org.apache.tomcat.util.net.SSLSupport;
- import org.apache.tomcat.util.net.SendfileDataBase;
-+import org.apache.tomcat.util.net.SendfileKeepAliveState;
- import org.apache.tomcat.util.net.SocketWrapperBase;
- import org.apache.tomcat.util.res.StringManager;
-
-@@ -1601,7 +1602,16 @@
- openSocket = keepAlive;
- // Do sendfile as needed: add socket to sendfile and end
- if (sendfileData != null && !getErrorState().isError()) {
-- sendfileData.keepAlive = keepAlive;
-+ if (keepAlive) {
-+ if (available(false) == 0) {
-+ sendfileData.keepAliveState = SendfileKeepAliveState.OPEN;
-+ } else {
-+ sendfileData.keepAliveState = SendfileKeepAliveState.PIPELINED;
-+ }
-+ } else {
-+ sendfileData.keepAliveState = SendfileKeepAliveState.NONE;
-+ }
-+
- switch (socketWrapper.processSendfile(sendfileData)) {
- case DONE:
- // If sendfile is complete, no need to break keep-alive loop
---- a/java/org/apache/tomcat/util/net/AprEndpoint.java
-+++ b/java/org/apache/tomcat/util/net/AprEndpoint.java
-@@ -2138,20 +2138,33 @@
- state.length -= nw;
- if (state.length == 0) {
- remove(state);
-- if (state.keepAlive) {
-+ switch (state.keepAliveState) {
-+ case NONE: {
-+ // Close the socket since this is
-+ // the end of the not keep-alive request.
-+ closeSocket(state.socket);
-+ break;
-+ }
-+ case PIPELINED: {
- // Destroy file descriptor pool, which should close the file
- Pool.destroy(state.fdpool);
-- Socket.timeoutSet(state.socket,
-- getSoTimeout() * 1000);
-- // If all done put the socket back in the
-- // poller for processing of further requests
-- getPoller().add(
-- state.socket, getKeepAliveTimeout(),
-+ Socket.timeoutSet(state.socket, getSoTimeout() * 1000);
-+ // Process the pipelined request data
-+ if (!processSocket(state.socket, SocketEvent.OPEN_READ)) {
-+ closeSocket(state.socket);
-+ }
-+ break;
-+ }
-+ case OPEN: {
-+ // Destroy file descriptor pool, which should close the file
-+ Pool.destroy(state.fdpool);
-+ Socket.timeoutSet(state.socket, getSoTimeout() * 1000);
-+ // Put the socket back in the poller for
-+ // processing of further requests
-+ getPoller().add(state.socket, getKeepAliveTimeout(),
- Poll.APR_POLLIN);
-- } else {
-- // Close the socket since this is
-- // the end of not keep-alive request.
-- closeSocket(state.socket);
-+ break;
-+ }
- }
- }
- }
---- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java
-+++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
-@@ -536,17 +536,24 @@
- } catch (IOException e) {
- // Ignore
- }
-- if (attachment.keepAlive) {
-- if (!isInline()) {
-+ if (isInline()) {
-+ attachment.doneInline = true;
-+ } else {
-+ switch (attachment.keepAliveState) {
-+ case NONE: {
-+ getEndpoint().processSocket(Nio2SocketWrapper.this,
-+ SocketEvent.DISCONNECT, false);
-+ break;
-+ }
-+ case PIPELINED: {
-+ getEndpoint().processSocket(Nio2SocketWrapper.this,
-+ SocketEvent.OPEN_READ, true);
-+ break;
-+ }
-+ case OPEN: {
- awaitBytes();
-- } else {
-- attachment.doneInline = true;
-+ break;
- }
-- } else {
-- if (!isInline()) {
-- getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.DISCONNECT, false);
-- } else {
-- attachment.doneInline = true;
- }
- }
- return;
---- a/java/org/apache/tomcat/util/net/NioEndpoint.java
-+++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
-@@ -924,16 +924,30 @@
- // responsible for registering the socket for the
- // appropriate event(s) if sendfile completes.
- if (!calledByProcessor) {
-- if (sd.keepAlive) {
-- if (log.isDebugEnabled()) {
-- log.debug("Connection is keep alive, registering back for OP_READ");
-- }
-- reg(sk,socketWrapper,SelectionKey.OP_READ);
-- } else {
-+ switch (sd.keepAliveState) {
-+ case NONE: {
- if (log.isDebugEnabled()) {
- log.debug("Send file connection is being closed");
- }
- close(sc, sk);
-+ break;
-+ }
-+ case PIPELINED: {
-+ if (log.isDebugEnabled()) {
-+ log.debug("Connection is keep alive, processing pipe-lined data");
-+ }
-+ if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
-+ close(sc, sk);
-+ }
-+ break;
-+ }
-+ case OPEN: {
-+ if (log.isDebugEnabled()) {
-+ log.debug("Connection is keep alive, registering back for OP_READ");
-+ }
-+ reg(sk,socketWrapper,SelectionKey.OP_READ);
-+ break;
-+ }
- }
- }
- return SendfileState.DONE;
---- a/java/org/apache/tomcat/util/net/SendfileDataBase.java
-+++ b/java/org/apache/tomcat/util/net/SendfileDataBase.java
-@@ -21,10 +21,10 @@
- /**
- * Is the current request being processed on a keep-alive connection? This
- * determines if the socket is closed once the send file completes or if
-- * processing continues with the next request on the connection (or waiting
-- * for that next request to arrive).
-+ * processing continues with the next request on the connection or waiting
-+ * for that next request to arrive.
- */
-- public boolean keepAlive;
-+ public SendfileKeepAliveState keepAliveState = SendfileKeepAliveState.NONE;
-
- /**
- * The full path to the file that contains the data to be written to the
---- /dev/null
-+++ b/java/org/apache/tomcat/util/net/SendfileKeepAliveState.java
-@@ -0,0 +1,39 @@
-+/*
-+ * Licensed to the Apache Software Foundation (ASF) under one or more
-+ * contributor license agreements. See the NOTICE file distributed with
-+ * this work for additional information regarding copyright ownership.
-+ * The ASF licenses this file to You under the Apache License, Version 2.0
-+ * (the "License"); you may not use this file except in compliance with
-+ * the License. You may obtain a copy of the License at
-+ *
-+ * http://www.apache.org/licenses/LICENSE-2.0
-+ *
-+ * Unless required by applicable law or agreed to in writing, software
-+ * distributed under the License is distributed on an "AS IS" BASIS,
-+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-+ * See the License for the specific language governing permissions and
-+ * limitations under the License.
-+ */
-+package org.apache.tomcat.util.net;
-+
-+public enum SendfileKeepAliveState {
-+
-+ /**
-+ * Keep-alive is not in use. The socket can be closed when the response has
-+ * been written.
-+ */
-+ NONE,
-+
-+ /**
-+ * Keep-alive is in use and there is pipelined data in the input buffer to
-+ * be read as soon as the current response has been written.
-+ */
-+ PIPELINED,
-+
-+ /**
-+ * Keep-alive is in use. The socket should be added to the poller (or
-+ * equivalent) to await more data as soon as the current response has been
-+ * written.
-+ */
-+ OPEN
-+}
diff --git a/debian/patches/CVE-2017-5650.patch b/debian/patches/CVE-2017-5650.patch
deleted file mode 100644
index 74a88629..00000000
--- a/debian/patches/CVE-2017-5650.patch
+++ /dev/null
@@ -1,24 +0,0 @@
-From: Markus Koschany <apo@debian.org>
-Date: Wed, 12 Apr 2017 00:00:50 +0200
-Subject: CVE-2017-5650
-
-Bug-Debian: https://bugs.debian.org/860068
-Origin: http://svn.apache.org/r1788480
----
- java/org/apache/coyote/http2/Http2UpgradeHandler.java | 5 +++++
- 1 file changed, 5 insertions(+)
-
---- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
-+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
-@@ -983,6 +983,11 @@
-
- private void close() {
- connectionState.set(ConnectionState.CLOSED);
-+ for (Stream stream : streams.values()) {
-+ // The connection is closing. Close the associated streams as no
-+ // longer required.
-+ stream.receiveReset(Http2Error.CANCEL.getCode());
-+ }
- try {
- socketWrapper.close();
- } catch (IOException ioe) {
diff --git a/debian/patches/CVE-2017-5651.patch b/debian/patches/CVE-2017-5651.patch
deleted file mode 100644
index e737f688..00000000
--- a/debian/patches/CVE-2017-5651.patch
+++ /dev/null
@@ -1,155 +0,0 @@
-From: Markus Koschany <apo@debian.org>
-Date: Wed, 12 Apr 2017 00:11:24 +0200
-Subject: CVE-2017-5651
-
-Bug-Debian: https://bugs.debian.org/860068
-Origin: http://svn.apache.org/r1788546
----
- java/org/apache/coyote/http11/Http11Processor.java | 52 ++++++++++------------
- 1 file changed, 24 insertions(+), 28 deletions(-)
-
---- a/java/org/apache/coyote/http11/Http11Processor.java
-+++ b/java/org/apache/coyote/http11/Http11Processor.java
-@@ -58,6 +58,7 @@
- import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
- import org.apache.tomcat.util.net.SSLSupport;
- import org.apache.tomcat.util.net.SendfileDataBase;
-+import org.apache.tomcat.util.net.SendfileState;
- import org.apache.tomcat.util.net.SendfileKeepAliveState;
- import org.apache.tomcat.util.net.SocketWrapperBase;
- import org.apache.tomcat.util.res.StringManager;
-@@ -659,9 +660,10 @@
- openSocket = false;
- readComplete = true;
- boolean keptAlive = false;
-+ SendfileState sendfileState = SendfileState.DONE;
-
-- while (!getErrorState().isError() && keepAlive && !isAsync() &&
-- upgradeToken == null && !endpoint.isPaused()) {
-+ while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
-+ sendfileState == SendfileState.DONE && !endpoint.isPaused()) {
-
- // Parsing the request header
- try {
-@@ -850,9 +852,7 @@
-
- rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
-
-- if (breakKeepAliveLoop(socketWrapper)) {
-- break;
-- }
-+ sendfileState = processSendfile(socketWrapper);
- }
-
- rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
-@@ -864,7 +864,7 @@
- } else if (isUpgrade()) {
- return SocketState.UPGRADING;
- } else {
-- if (sendfileData != null) {
-+ if (sendfileState == SendfileState.PENDING) {
- return SocketState.SENDFILE;
- } else {
- if (openSocket) {
-@@ -940,7 +940,6 @@
- http11 = true;
- http09 = false;
- contentDelimitation = false;
-- sendfileData = null;
-
- if (endpoint.isSSLEnabled()) {
- request.scheme().setString("https");
-@@ -1147,15 +1146,14 @@
- }
-
- // Sendfile support
-- boolean sendingWithSendfile = false;
- if (endpoint.getUseSendfile()) {
-- sendingWithSendfile = prepareSendfile(outputFilters);
-+ prepareSendfile(outputFilters);
- }
-
- // Check for compression
- boolean isCompressable = false;
- boolean useCompression = false;
-- if (entityBody && (compressionLevel > 0) && !sendingWithSendfile) {
-+ if (entityBody && (compressionLevel > 0) && sendfileData == null) {
- isCompressable = isCompressable();
- if (isCompressable) {
- useCompression = useCompression();
-@@ -1297,10 +1295,12 @@
- return connection.equals(Constants.CLOSE);
- }
-
-- private boolean prepareSendfile(OutputFilter[] outputFilters) {
-+ private void prepareSendfile(OutputFilter[] outputFilters) {
- String fileName = (String) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILENAME_ATTR);
-- if (fileName != null) {
-+ if (fileName == null) {
-+ sendfileData = null;
-+ } else {
- // No entity body sent here
- outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
- contentDelimitation = true;
-@@ -1309,9 +1309,7 @@
- long end = ((Long) request.getAttribute(
- org.apache.coyote.Constants.SENDFILE_FILE_END_ATTR)).longValue();
- sendfileData = socketWrapper.createSendfileData(fileName, pos, end - pos);
-- return true;
- }
-- return false;
- }
-
- /**
-@@ -1592,14 +1590,15 @@
-
-
- /**
-- * Checks to see if the keep-alive loop should be broken, performing any
-- * processing (e.g. sendfile handling) that may have an impact on whether
-- * or not the keep-alive loop should be broken.
- *
-- * @return true if the keep-alive loop should be broken
-+ * Trigger sendfile processing if required.
-+ *
-+ * @return The state of send file processing
- */
-- private boolean breakKeepAliveLoop(SocketWrapperBase<?> socketWrapper) {
-+ private SendfileState processSendfile(SocketWrapperBase<?> socketWrapper) {
- openSocket = keepAlive;
-+ // Done is equivalent to sendfile not being used
-+ SendfileState result = SendfileState.DONE;
- // Do sendfile as needed: add socket to sendfile and end
- if (sendfileData != null && !getErrorState().isError()) {
- if (keepAlive) {
-@@ -1612,23 +1611,20 @@
- sendfileData.keepAliveState = SendfileKeepAliveState.NONE;
- }
-
-- switch (socketWrapper.processSendfile(sendfileData)) {
-- case DONE:
-- // If sendfile is complete, no need to break keep-alive loop
-- sendfileData = null;
-- return false;
-- case PENDING:
-- return true;
-+ result = socketWrapper.processSendfile(sendfileData);
-+ switch (result) {
- case ERROR:
- // Write failed
- if (log.isDebugEnabled()) {
- log.debug(sm.getString("http11processor.sendfile.error"));
- }
- setErrorState(ErrorState.CLOSE_CONNECTION_NOW, null);
-- return true;
-+ //$FALL-THROUGH$
-+ default:
-+ sendfileData = null;
- }
- }
-- return false;
-+ return result;
- }
-
-
diff --git a/debian/patches/series b/debian/patches/series
index 8aabbe86..1b369897 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -8,6 +8,3 @@
0018-fix-manager-webapp.patch
0019-add-distribution-to-error-page.patch
0021-dont-test-unsupported-ciphers.patch
-CVE-2017-5647.patch
-CVE-2017-5650.patch
-CVE-2017-5651.patch
diff --git a/java/javax/el/BeanELResolver.java b/java/javax/el/BeanELResolver.java
index 27f3d58e..93728545 100644
--- a/java/javax/el/BeanELResolver.java
+++ b/java/javax/el/BeanELResolver.java
@@ -245,6 +245,7 @@ public class BeanELResolver extends ELResolver {
this.type, pd));
}
}
+ populateFromInterfaces(ifs);
}
}
Class<?> superclass = aClass.getSuperclass();
diff --git a/java/org/apache/catalina/Pipeline.java b/java/org/apache/catalina/Pipeline.java
index c5f19dda..8a21e760 100644
--- a/java/org/apache/catalina/Pipeline.java
+++ b/java/org/apache/catalina/Pipeline.java
@@ -18,6 +18,7 @@
package org.apache.catalina;
+import java.util.Set;
/**
* <p>Interface describing a collection of Valves that should be executed
@@ -143,4 +144,14 @@ public interface Pipeline {
*/
public void setContainer(Container container);
+
+ /**
+ * Identifies the Valves, if any, in this Pipeline that do not support
+ * async.
+ *
+ * @param result The Set to which the fully qualified class names of each
+ * Valve in this Pipeline that does not support async will be
+ * added
+ */
+ public void findNonAsyncValves(Set<String> result);
}
diff --git a/java/org/apache/catalina/connector/CoyoteAdapter.java b/java/org/apache/catalina/connector/CoyoteAdapter.java
index 52064d92..66574d6b 100644
--- a/java/org/apache/catalina/connector/CoyoteAdapter.java
+++ b/java/org/apache/catalina/connector/CoyoteAdapter.java
@@ -276,8 +276,9 @@ public class CoyoteAdapter implements Adapter {
if (req.getStartTime() != -1) {
time = System.currentTimeMillis() - req.getStartTime();
}
- if (request.getMappingData().context != null) {
- request.getMappingData().context.logAccess(request, response, time, false);
+ Context context = request.getContext();
+ if (context != null) {
+ context.logAccess(request, response, time, false);
} else {
log(req, res, time);
}
@@ -390,9 +391,17 @@ public class CoyoteAdapter implements Adapter {
if (!async && postParseSuccess) {
// Log only if processing was invoked.
// If postParseRequest() failed, it has already logged it.
- request.getMappingData().context.logAccess(request, response,
+ Context context = request.getContext();
+ // If the context is null, it is likely that the endpoint was
+ // shutdown, this connection closed and the request recycled in
+ // a different thread. That thread will have updated the access
+ // log so it is OK not to update the access log here in that
+ // case.
+ if (context != null) {
+ context.logAccess(request, response,
System.currentTimeMillis() - req.getStartTime(), false);
}
+ }
req.getRequestProcessor().setWorkerThreadName(null);
@@ -446,20 +455,17 @@ public class CoyoteAdapter implements Adapter {
// Log at the lowest level available. logAccess() will be
// automatically called on parent containers.
boolean logged = false;
- if (request.mappingData != null) {
- if (request.mappingData.context != null) {
+ Context context = request.mappingData.context;
+ Host host = request.mappingData.host;
+ if (context != null) {
logged = true;
- request.mappingData.context.logAccess(
- request, response, time, true);
- } else if (request.mappingData.host != null) {
+ context.logAccess(request, response, time, true);
+ } else if (host != null) {
logged = true;
- request.mappingData.host.logAccess(
- request, response, time, true);
- }
+ host.logAccess(request, response, time, true);
}
if (!logged) {
- connector.getService().getContainer().logAccess(
- request, response, time, true);
+ connector.getService().getContainer().logAccess(request, response, time, true);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
@@ -973,24 +979,26 @@ public class CoyoteAdapter implements Adapter {
* Look for SSL session ID if required. Only look for SSL Session ID if it
* is the only tracking method enabled.
*
- * @param request The Servlet request obejct
+ * @param request The Servlet request object
*/
protected void parseSessionSslId(Request request) {
if (request.getRequestedSessionId() == null &&
SSL_ONLY.equals(request.getServletContext()
.getEffectiveSessionTrackingModes()) &&
request.connector.secure) {
- request.setRequestedSessionId(
- request.getAttribute(SSLSupport.SESSION_ID_KEY).toString());
+ String sessionId = (String) request.getAttribute(SSLSupport.SESSION_ID_KEY);
+ if (sessionId != null) {
+ request.setRequestedSessionId(sessionId);
request.setRequestedSessionSSL(true);
}
}
+ }
/**
* Parse session id in URL.
*
- * @param request The Servlet request obejct
+ * @param request The Servlet request object
*/
protected void parseSessionCookiesId(Request request) {
diff --git a/java/org/apache/catalina/connector/InputBuffer.java b/java/org/apache/catalina/connector/InputBuffer.java
index 7aee3f19..87097aed 100644
--- a/java/org/apache/catalina/connector/InputBuffer.java
+++ b/java/org/apache/catalina/connector/InputBuffer.java
@@ -34,6 +34,8 @@ import org.apache.catalina.security.SecurityUtil;
import org.apache.coyote.ActionCode;
import org.apache.coyote.ContainerThreadMarker;
import org.apache.coyote.Request;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.collections.SynchronizedStack;
@@ -56,6 +58,8 @@ public class InputBuffer extends Reader
*/
protected static final StringManager sm = StringManager.getManager(InputBuffer.class);
+ private static final Log log = LogFactory.getLog(InputBuffer.class);
+
public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
// The buffer can be used for byte[] and char[] reading
@@ -271,7 +275,10 @@ public class InputBuffer extends Reader
public boolean isReady() {
if (coyoteRequest.getReadListener() == null) {
- throw new IllegalStateException(sm.getString("inputBuffer.requiresNonBlocking"));
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("inputBuffer.requiresNonBlocking"));
+ }
+ return false;
}
if (isFinished()) {
// If this is a non-container thread, need to trigger a read
diff --git a/java/org/apache/catalina/connector/LocalStrings.properties b/java/org/apache/catalina/connector/LocalStrings.properties
index 688f68f4..02923913 100644
--- a/java/org/apache/catalina/connector/LocalStrings.properties
+++ b/java/org/apache/catalina/connector/LocalStrings.properties
@@ -15,7 +15,7 @@
coyoteAdapter.accesslogFail=Exception while attempting to add an entry to the access log
coyoteAdapter.asyncDispatch=Exception while processing an asynchronous request
coyoteAdapter.authenticate=Authenticated user [{0}] provided by connector
-coyoteAdapter.authorize=Authorizing user [{0}] using Tomcat's Realm
+coyoteAdapter.authorize=Authorizing user [{0}] using Tomcat''s Realm
coyoteAdapter.checkRecycled.request=Encountered a non-recycled request and recycled it forcedly.
coyoteAdapter.checkRecycled.response=Encountered a non-recycled response and recycled it forcedly.
coyoteAdapter.debug=The variable [{0}] has value [{1}]
@@ -52,7 +52,9 @@ coyoteRequest.authenticate.ise=Cannot call authenticate() after the response has
coyoteRequest.uploadLocationInvalid=The temporary upload location [{0}] is not valid
coyoteRequest.sessionEndAccessFail=Exception triggered ending access to session while recycling request
coyoteRequest.sendfileNotCanonical=Unable to determine canonical name of file [{0}] specified for use with sendfile
+coyoteRequest.filterAsyncSupportUnknown=Unable to determine if any filters do not support async processing
coyoteRequest.maxPostSizeExceeded=The multi-part request contained parameter data (excluding uploaded files) that exceeded the limit for maxPostSize set on the associated connector
+coyoteRequest.noAsync=Unable to start async because the following classes in the processing chain do not support async [{0}]
coyoteRequest.noMultipartConfig=Unable to process parts as no multi-part configuration has been provided
coyoteResponse.getOutputStream.ise=getWriter() has already been called for this response
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index 8d336a1f..7e8ea4b0 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -74,6 +74,7 @@ import org.apache.catalina.Realm;
import org.apache.catalina.Session;
import org.apache.catalina.TomcatPrincipal;
import org.apache.catalina.Wrapper;
+import org.apache.catalina.core.ApplicationFilterChain;
import org.apache.catalina.core.ApplicationMapping;
import org.apache.catalina.core.ApplicationPart;
import org.apache.catalina.core.ApplicationPushBuilder;
@@ -94,6 +95,7 @@ import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.http.CookieProcessor;
import org.apache.tomcat.util.http.FastHttpDateFormat;
@@ -1643,7 +1645,11 @@ public class Request implements org.apache.catalina.servlet4preview.http.HttpSer
public AsyncContext startAsync(ServletRequest request,
ServletResponse response) {
if (!isAsyncSupported()) {
- throw new IllegalStateException(sm.getString("request.asyncNotSupported"));
+ IllegalStateException ise =
+ new IllegalStateException(sm.getString("request.asyncNotSupported"));
+ log.warn(sm.getString("coyoteRequest.noAsync",
+ StringUtils.join(getNonAsyncClassNames())), ise);
+ throw ise;
}
if (asyncContext == null) {
@@ -1657,6 +1663,31 @@ public class Request implements org.apache.catalina.servlet4preview.http.HttpSer
return asyncContext;
}
+
+ private Set<String> getNonAsyncClassNames() {
+ Set<String> result = new HashSet<>();
+
+ Wrapper wrapper = getWrapper();
+ if (!wrapper.isAsyncSupported()) {
+ result.add(wrapper.getServletClass());
+ }
+
+ FilterChain filterChain = getFilterChain();
+ if (filterChain instanceof ApplicationFilterChain) {
+ ((ApplicationFilterChain) filterChain).findNonAsyncFilters(result);
+ } else {
+ result.add(sm.getString("coyoteRequest.filterAsyncSupportUnknown"));
+ }
+
+ Container c = wrapper;
+ while (c != null) {
+ c.getPipeline().findNonAsyncValves(result);
+ c = c.getParent();
+ }
+
+ return result;
+ }
+
@Override
public boolean isAsyncStarted() {
if (asyncContext == null) {
@@ -1932,7 +1963,7 @@ public class Request implements org.apache.catalina.servlet4preview.http.HttpSer
* @return A builder to use to construct the push request
*/
@Override
- public PushBuilder getPushBuilder() {
+ public PushBuilder newPushBuilder() {
AtomicBoolean result = new AtomicBoolean();
coyoteRequest.action(ActionCode.IS_PUSH_SUPPORTED, result);
if (result.get()) {
diff --git a/java/org/apache/catalina/connector/RequestFacade.java b/java/org/apache/catalina/connector/RequestFacade.java
index d11eddde..ca19d181 100644
--- a/java/org/apache/catalina/connector/RequestFacade.java
+++ b/java/org/apache/catalina/connector/RequestFacade.java
@@ -1135,7 +1135,7 @@ public class RequestFacade implements HttpServletRequest {
* removed or replaced at any time until Servlet 4.0 becomes final.
*/
@Override
- public PushBuilder getPushBuilder() {
- return request.getPushBuilder();
+ public PushBuilder newPushBuilder() {
+ return request.newPushBuilder();
}
}
diff --git a/java/org/apache/catalina/core/ApplicationContextFacade.java b/java/org/apache/catalina/core/ApplicationContextFacade.java
index 5af105c9..a8415d50 100644
--- a/java/org/apache/catalina/core/ApplicationContextFacade.java
+++ b/java/org/apache/catalina/core/ApplicationContextFacade.java
@@ -794,7 +794,7 @@ public class ApplicationContextFacade implements org.apache.catalina.servlet4pre
@Override
public void setSessionTimeout(int sessionTimeout) {
if (SecurityUtil.isPackageProtectionEnabled()) {
- doPrivileged("getSessionTimeout", new Object[] { Integer.valueOf(sessionTimeout) });
+ doPrivileged("setSessionTimeout", new Object[] { Integer.valueOf(sessionTimeout) });
} else {
context.setSessionTimeout(sessionTimeout);
}
diff --git a/java/org/apache/catalina/core/ApplicationFilterChain.java b/java/org/apache/catalina/core/ApplicationFilterChain.java
index 18e30bb1..f212c462 100644
--- a/java/org/apache/catalina/core/ApplicationFilterChain.java
+++ b/java/org/apache/catalina/core/ApplicationFilterChain.java
@@ -19,6 +19,7 @@ package org.apache.catalina.core;
import java.io.IOException;
import java.security.Principal;
import java.security.PrivilegedActionException;
+import java.util.Set;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@@ -43,7 +44,7 @@ import org.apache.tomcat.util.res.StringManager;
*
* @author Craig R. McClanahan
*/
-final class ApplicationFilterChain implements FilterChain {
+public final class ApplicationFilterChain implements FilterChain {
// Used to enforce requirements of SRV.8.2 / SRV.14.2.5.1
private static final ThreadLocal<ServletRequest> lastServicedRequest;
@@ -326,4 +327,22 @@ final class ApplicationFilterChain implements FilterChain {
void setServletSupportsAsync(boolean servletSupportsAsync) {
this.servletSupportsAsync = servletSupportsAsync;
}
+
+
+ /**
+ * Identifies the Filters, if any, in this FilterChain that do not support
+ * async.
+ *
+ * @param result The Set to which the fully qualified class names of each
+ * Filter in this FilterChain that does not support async will
+ * be added
+ */
+ public void findNonAsyncFilters(Set<String> result) {
+ for (int i = 0; i < n ; i++) {
+ ApplicationFilterConfig filter = filters[i];
+ if ("false".equalsIgnoreCase(filter.getFilterDef().getAsyncSupported())) {
+ result.add(filter.getFilterClass());
+ }
+ }
+ }
}
diff --git a/java/org/apache/catalina/core/ApplicationHttpRequest.java b/java/org/apache/catalina/core/ApplicationHttpRequest.java
index cad7091e..84b42fa4 100644
--- a/java/org/apache/catalina/core/ApplicationHttpRequest.java
+++ b/java/org/apache/catalina/core/ApplicationHttpRequest.java
@@ -634,7 +634,7 @@ class ApplicationHttpRequest
@Override
- public PushBuilder getPushBuilder() {
+ public PushBuilder newPushBuilder() {
return new ApplicationPushBuilder(this);
}
diff --git a/java/org/apache/catalina/core/ApplicationMapping.java b/java/org/apache/catalina/core/ApplicationMapping.java
index dabf55e3..e939e67f 100644
--- a/java/org/apache/catalina/core/ApplicationMapping.java
+++ b/java/org/apache/catalina/core/ApplicationMapping.java
@@ -59,7 +59,13 @@ public class ApplicationMapping {
"*" + path.substring(extIndex), mappingData.matchType, servletName);
break;
case PATH:
- mapping = new MappingImpl(mappingData.pathInfo.toString().substring(1),
+ String matchValue;
+ if (mappingData.pathInfo.isNull()) {
+ matchValue = null;
+ } else {
+ matchValue = mappingData.pathInfo.toString().substring(1);
+ }
+ mapping = new MappingImpl(matchValue,
mappingData.wrapperPath.toString() + "/*",
mappingData.matchType, servletName);
break;
diff --git a/java/org/apache/catalina/core/ApplicationPushBuilder.java b/java/org/apache/catalina/core/ApplicationPushBuilder.java
index 6d907540..22f2bd6c 100644
--- a/java/org/apache/catalina/core/ApplicationPushBuilder.java
+++ b/java/org/apache/catalina/core/ApplicationPushBuilder.java
@@ -72,11 +72,8 @@ public class ApplicationPushBuilder implements PushBuilder {
private final List<Cookie> cookies = new ArrayList<>();
private String method = "GET";
private String path;
- private String eTag;
- private String lastModified;
private String queryString;
private String sessionId;
- private boolean conditional;
public ApplicationPushBuilder(HttpServletRequest request) {
@@ -108,12 +105,8 @@ public class ApplicationPushBuilder implements PushBuilder {
// Remove the headers
headers.remove("if-match");
- if (headers.remove("if-none-match") != null) {
- conditional = true;
- }
- if (headers.remove("if-modified-since") != null) {
- conditional = true;
- }
+ headers.remove("if-none-match");
+ headers.remove("if-modified-since");
headers.remove("if-unmodified-since");
headers.remove("if-range");
headers.remove("range");
@@ -228,32 +221,6 @@ public class ApplicationPushBuilder implements PushBuilder {
@Override
- public ApplicationPushBuilder eTag(String eTag) {
- this.eTag = eTag;
- return this;
- }
-
-
- @Override
- public String getETag() {
- return eTag;
- }
-
-
- @Override
- public ApplicationPushBuilder lastModified(String lastModified) {
- this.lastModified = lastModified;
- return this;
- }
-
-
- @Override
- public String getLastModified() {
- return lastModified;
- }
-
-
- @Override
public ApplicationPushBuilder queryString(String queryString) {
this.queryString = queryString;
return this;
@@ -280,19 +247,6 @@ public class ApplicationPushBuilder implements PushBuilder {
@Override
- public ApplicationPushBuilder conditional(boolean conditional) {
- this.conditional = conditional;
- return this;
- }
-
-
- @Override
- public boolean isConditional() {
- return conditional;
- }
-
-
- @Override
public ApplicationPushBuilder addHeader(String name, String value) {
List<String> values = headers.get(name);
if (values == null) {
@@ -404,14 +358,6 @@ public class ApplicationPushBuilder implements PushBuilder {
pushTarget.queryString().setString(pushQueryString + "&" +queryString);
}
- if (conditional) {
- if (eTag != null) {
- setHeader("if-none-match", eTag);
- } else if (lastModified != null) {
- setHeader("if-modified-since", lastModified);
- }
- }
-
// Cookies
setHeader("cookie", generateCookieHeader(cookies,
catalinaRequest.getContext().getCookieProcessor()));
@@ -421,8 +367,6 @@ public class ApplicationPushBuilder implements PushBuilder {
// Reset for next call to this method
pushTarget = null;
path = null;
- eTag = null;
- lastModified = null;
headers.remove("if-none-match");
headers.remove("if-modified-since");
}
diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index b4b74506..2786bfd0 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -60,7 +60,7 @@ applicationPushBuilder.methodNotToken=HTTP methods must be tokens but [{0}] cont
applicationPushBuilder.noCoyoteRequest=Unable to find the underlying Coyote request object (which is required to create a push request) from the request of type [{0}]
applicationServletRegistration.setServletSecurity.iae=Null constraint specified for servlet [{0}] deployed to context with name [{1}]
-applicationServletRegistration.setServletSecurity.ise=Security constraints can't be added to servlet [{0}] deployed to context with name [{1}] as the context has already been initialised
+applicationServletRegistration.setServletSecurity.ise=Security constraints can''t be added to servlet [{0}] deployed to context with name [{1}] as the context has already been initialised
applicationSessionCookieConfig.ise=Property {0} cannot be added to SessionCookieConfig for context {1} as the context has been initialised
aprListener.aprInit=The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: {0}
aprListener.aprInitDebug=The APR based Apache Tomcat Native library could not be found using names [{0}] on the java.library.path [{1}]. The errors reported were [{2}]
@@ -141,9 +141,9 @@ standardContext.listenerFail=One or more listeners failed to start. Full details
standardContext.listenerStart=Exception sending context initialized event to listener instance of class {0}
standardContext.listenerStop=Exception sending context destroyed event to listener instance of class {0}
standardContext.loadOnStartup.loadException=Servlet [{1}] in web application [{0}] threw load() exception
-standardContext.loginConfig.errorPage=Form error page {0} must start with a ''/'
+standardContext.loginConfig.errorPage=Form error page {0} must start with a ''/''
standardContext.loginConfig.errorWarning=WARNING: Form error page {0} must start with a ''/'' in Servlet 2.4
-standardContext.loginConfig.loginPage=Form login page {0} must start with a ''/'
+standardContext.loginConfig.loginPage=Form login page {0} must start with a ''/''
standardContext.loginConfig.loginWarning=WARNING: Form login page {0} must start with a ''/'' in Servlet 2.4
standardContext.loginConfig.required=LoginConfig cannot be null
standardContext.manager=Configured a manager of class [{0}]
@@ -188,7 +188,7 @@ standardEngine.notHost=Child of an Engine must be a Host
standardEngine.notParent=Engine cannot have a parent Container
standardHost.asyncStateError=An asynchronous request was received for processing that was neither an async dispatch nor an error to process
standardHost.clientAbort=Remote Client Aborted Request, IOException: {0}
-standardHost.invalidErrorReportValveClass=Couldn't load specified error report valve class: {0}
+standardHost.invalidErrorReportValveClass=Couldn''t load specified error report valve class: {0}
standardHost.noContext=No Context configured to process this request
standardHost.notContext=Child of a Host must be a Context
standardHost.nullName=Host name is required
diff --git a/java/org/apache/catalina/core/LocalStrings_es.properties b/java/org/apache/catalina/core/LocalStrings_es.properties
index 9a5ef37b..077e6f18 100644
--- a/java/org/apache/catalina/core/LocalStrings_es.properties
+++ b/java/org/apache/catalina/core/LocalStrings_es.properties
@@ -88,9 +88,9 @@ standardContext.requestListener.requestInit = Una excepci\u00F3n durante el env\
standardContext.isUnavailable = Esta aplicaci\u00F3n no est\u00E1 disponible en este momento
standardContext.listenerStart = Excepci\u00F3n enviando evento inicializado de contexto a instancia de escuchador de clase {0}
standardContext.listenerStop = Excepci\u00F3n enviando evento de contexto destru\u00EDdo a instancia de escuchador de clase {0}
-standardContext.loginConfig.errorPage = La P\u00E1gina de error de Formulario {0} debe de comenzar con ''/'
+standardContext.loginConfig.errorPage = La P\u00E1gina de error de Formulario {0} debe de comenzar con ''/''
standardContext.loginConfig.errorWarning = AVISO\: La p\u00E1gina de error de Formulario {0} debe de comenzar con ''/'' en Servlet 2.4
-standardContext.loginConfig.loginPage = La p\u00E1gina de login de Formulario {0} debe de comenzar con ''/'
+standardContext.loginConfig.loginPage = La p\u00E1gina de login de Formulario {0} debe de comenzar con ''/''
standardContext.loginConfig.loginWarning = AVISO\: La p\u00E1gina de login de Formulario {0} debe de comenzar con ''/'' en Servlet 2.4
standardContext.loginConfig.required = LoginConfig no puede ser nula
standardContext.manager = Configurado un gestor de la clase [{0}]
diff --git a/java/org/apache/catalina/core/LocalStrings_fr.properties b/java/org/apache/catalina/core/LocalStrings_fr.properties
index ed425712..c0d9ec18 100644
--- a/java/org/apache/catalina/core/LocalStrings_fr.properties
+++ b/java/org/apache/catalina/core/LocalStrings_fr.properties
@@ -30,7 +30,7 @@ naming.invalidEnvEntryValue=L''entr\u00e9e environnement {0} a une valeur invali
naming.namingContextCreationFailed=La cr\u00e9ation du contexte de nommage (naming context) a \u00e9chou\u00e9 : {0}
standardContext.applicationListener=Erreur lors de la configuration de la classe d''\u00e9coute de l''application (application listener) {0}
standardContext.applicationSkipped=L''installation des \u00e9couteurs (listeners) de l''application a \u00e9t\u00e9 saut\u00e9e suite aux erreurs pr\u00e9c\u00e9dentes
-standardContext.errorPage.error=La position de la page d''erreur (ErrorPage) {0} doit commencer par un ''/'
+standardContext.errorPage.error=La position de la page d''erreur (ErrorPage) {0} doit commencer par un ''/''
standardContext.errorPage.required=La page d''erreur (ErrorPage) ne peut \u00eatre nulle
standardContext.errorPage.warning=ATTENTION: La position de la page d''erreur (ErrorPage) {0} doit commencer par un ''/'' dans l''API Servlet 2.4
standardContext.filterMap.either=L''association de filtre (filter mapping) doit indiquer soit une <url-pattern> soit une <servlet-name>
diff --git a/java/org/apache/catalina/core/StandardContext.java b/java/org/apache/catalina/core/StandardContext.java
index 397749b3..fc82f5dc 100644
--- a/java/org/apache/catalina/core/StandardContext.java
+++ b/java/org/apache/catalina/core/StandardContext.java
@@ -115,6 +115,7 @@ import org.apache.tomcat.InstanceManagerBindings;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.IntrospectionUtils;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.descriptor.XmlIdentifiers;
import org.apache.tomcat.util.descriptor.web.ApplicationParameter;
@@ -1031,17 +1032,7 @@ public class StandardContext extends ContainerBase
@Override
public String getResourceOnlyServlets() {
- StringBuilder result = new StringBuilder();
- boolean first = true;
- for (String servletName : resourceOnlyServlets) {
- if (first) {
- first = false;
- } else {
- result.append(',');
- }
- result.append(servletName);
- }
- return result.toString();
+ return StringUtils.join(resourceOnlyServlets);
}
diff --git a/java/org/apache/catalina/core/StandardPipeline.java b/java/org/apache/catalina/core/StandardPipeline.java
index 040baed8..815890d2 100644
--- a/java/org/apache/catalina/core/StandardPipeline.java
+++ b/java/org/apache/catalina/core/StandardPipeline.java
@@ -20,6 +20,7 @@ package org.apache.catalina.core;
import java.util.ArrayList;
+import java.util.Set;
import javax.management.ObjectName;
@@ -117,9 +118,20 @@ public class StandardPipeline extends LifecycleBase
}
- // ------------------------------------------------------ Contained Methods
+ @Override
+ public void findNonAsyncValves(Set<String> result) {
+ Valve valve = (first!=null) ? first : basic;
+ while (valve != null) {
+ if (!valve.isAsyncSupported()) {
+ result.add(valve.getClass().getName());
+ }
+ valve = valve.getNext();
+ }
+ }
+ // ------------------------------------------------------ Contained Methods
+
/**
* Return the Container with which this Pipeline is associated.
*/
diff --git a/java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java b/java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java
index 78324772..48b7cf6f 100644
--- a/java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java
+++ b/java/org/apache/catalina/filters/HttpHeaderSecurityFilter.java
@@ -43,6 +43,7 @@ public class HttpHeaderSecurityFilter extends FilterBase {
private boolean hstsEnabled = true;
private int hstsMaxAgeSeconds = 0;
private boolean hstsIncludeSubDomains = false;
+ private boolean hstsPreload = false;
private String hstsHeaderValue;
// Click-jacking protection
@@ -72,6 +73,9 @@ public class HttpHeaderSecurityFilter extends FilterBase {
if (hstsIncludeSubDomains) {
hstsValue.append(";includeSubDomains");
}
+ if (hstsPreload) {
+ hstsValue.append(";preload");
+ }
hstsHeaderValue = hstsValue.toString();
// Anti click-jacking
@@ -169,19 +173,26 @@ public class HttpHeaderSecurityFilter extends FilterBase {
}
+ public boolean isHstsPreload() {
+ return hstsPreload;
+ }
+
+
+ public void setHstsPreload(boolean hstsPreload) {
+ this.hstsPreload = hstsPreload;
+ }
+
public boolean isAntiClickJackingEnabled() {
return antiClickJackingEnabled;
}
-
public void setAntiClickJackingEnabled(boolean antiClickJackingEnabled) {
this.antiClickJackingEnabled = antiClickJackingEnabled;
}
-
public String getAntiClickJackingOption() {
return antiClickJackingOption.toString();
}
diff --git a/java/org/apache/catalina/ha/deploy/LocalStrings.properties b/java/org/apache/catalina/ha/deploy/LocalStrings.properties
index 5c1374f1..e7b2dee8 100644
--- a/java/org/apache/catalina/ha/deploy/LocalStrings.properties
+++ b/java/org/apache/catalina/ha/deploy/LocalStrings.properties
@@ -20,7 +20,7 @@ farmWarDeployer.deployEnd=Deployment from [{0}] finished.
farmWarDeployer.fileCopyFail=Unable to copy from [{0}] to [{1}]
farmWarDeployer.hostOnly=FarmWarDeployer can only work as host cluster subelement!
farmWarDeployer.hostParentEngine=FarmWarDeployer can only work if parent of [{0}] is an engine!
-farmWarDeployer.mbeanNameFail=Can't construct MBean object name for engine [{0}] and host [{1}]
+farmWarDeployer.mbeanNameFail=Can''t construct MBean object name for engine [{0}] and host [{1}]
farmWarDeployer.alreadyDeployed=webapp [{0}] are already deployed.
farmWarDeployer.modInstall=Installing webapp [{0}] from [{1}]
farmWarDeployer.modRemoveFail=No removal
@@ -39,7 +39,7 @@ farmWarDeployer.sendEnd=Send cluster war deployment path [{0}], war [{1}] finish
farmWarDeployer.sendFragment=Send cluster war fragment path [{0}], war [{1}] to [{2}]
farmWarDeployer.sendStart=Send cluster war deployment path [{0}], war [{1}] started.
farmWarDeployer.servicingDeploy=Application [{0}] is being serviced. Touch war file [{1}] again!
-farmWarDeployer.servicingUndeploy=Application [{0}] is being serviced and can't be removed from backup cluster node
+farmWarDeployer.servicingUndeploy=Application [{0}] is being serviced and can''t be removed from backup cluster node
farmWarDeployer.started=Cluster FarmWarDeployer started.
farmWarDeployer.stopped=Cluster FarmWarDeployer stopped.
farmWarDeployer.undeployEnd=Undeployment from [{0}] finished.
diff --git a/java/org/apache/catalina/ha/session/LocalStrings.properties b/java/org/apache/catalina/ha/session/LocalStrings.properties
index 204a94ca..6aa7f3b5 100644
--- a/java/org/apache/catalina/ha/session/LocalStrings.properties
+++ b/java/org/apache/catalina/ha/session/LocalStrings.properties
@@ -62,7 +62,7 @@ deltaRequest.ssid.null=Session Id is null for setSessionId
deltaSession.notifying=Notifying cluster of expiration primary={0} sessionId [{1}]
deltaSession.readSession=readObject() loading session [{0}]
deltaSession.writeSession=writeObject() storing session [{0}]
-jvmRoute.cannotFindSession=Can't find session [{0}]
+jvmRoute.cannotFindSession=Can''t find session [{0}]
jvmRoute.changeSession=Changed session from [{0}] to [{1}]
jvmRoute.failover=Detected a failover with different jvmRoute - orginal route: [{0}] new one: [{1}] at session id [{2}]
jvmRoute.foundManager=Found Cluster Manager {0} at {1}
@@ -80,4 +80,4 @@ backupManager.noCluster=no cluster associated with this context: [{0}]
backupManager.startUnable=Unable to start BackupManager: [{0}]
backupManager.startFailed=Failed to start BackupManager: [{0}]
backupManager.stopped=Manager [{0}] is stopping
-clusterSessionListener.noManager=Context manager doesn't exist:{0}
+clusterSessionListener.noManager=Context manager doesn''t exist:{0}
diff --git a/java/org/apache/catalina/ha/tcp/LocalStrings.properties b/java/org/apache/catalina/ha/tcp/LocalStrings.properties
index d4f368a2..df6accf3 100644
--- a/java/org/apache/catalina/ha/tcp/LocalStrings.properties
+++ b/java/org/apache/catalina/ha/tcp/LocalStrings.properties
@@ -24,7 +24,7 @@ ReplicationValve.nocluster=No cluster configured for this request.
ReplicationValve.resetDeltaRequest=Cluster is standalone: reset Session Request Delta at context {0}
ReplicationValve.send.failure=Unable to perform replication request.
ReplicationValve.send.invalid.failure=Unable to send session [id={0}] invalid message over cluster.
-ReplicationValve.session.found=Context {0}: Found session {1} but it isn't a ClusterSession.
+ReplicationValve.session.found=Context {0}: Found session {1} but it isn''t a ClusterSession.
ReplicationValve.session.indicator=Context {0}: Primarity of session {0} in request attribute {1} is {2}.
ReplicationValve.session.invalid=Context {0}: Requested session {1} is invalid, removed or not replicated at this node.
ReplicationValve.stats=Average request time= {0} ms for Cluster overhead time={1} ms for {2} requests {3} filter requests {4} send requests {5} cross context requests (Request={6} ms Cluster={7} ms).
diff --git a/java/org/apache/catalina/ha/tcp/LocalStrings_es.properties b/java/org/apache/catalina/ha/tcp/LocalStrings_es.properties
index f8d258d9..1cabdd5e 100644
--- a/java/org/apache/catalina/ha/tcp/LocalStrings_es.properties
+++ b/java/org/apache/catalina/ha/tcp/LocalStrings_es.properties
@@ -24,8 +24,7 @@ ReplicationValve.nocluster = No cluster configured for this request.
ReplicationValve.resetDeltaRequest = Cluster is standalone\: reset Session Request Delta at context {0}
ReplicationValve.send.failure = Unable to perform replication request.
ReplicationValve.send.invalid.failure = Unable to send session [id\={0}] invalid message over cluster.
-ReplicationValve.session.found = Context {0}\: Found session {1} but it isn't a ClusterSession.
+ReplicationValve.session.found = Context {0}\: Found session {1} but it isn''t a ClusterSession.
ReplicationValve.session.indicator = Context {0}\: Primarity of session {0} in request attribute {1} is {2}.
ReplicationValve.session.invalid = Context {0}\: Requested session {1} is invalid, removed or not replicated at this node.
ReplicationValve.stats = Average request time\= {0} ms for Cluster overhead time\={1} ms for {2} requests {3} filter requests {4} send requests {5} cross context requests (Request\={6} ms Cluster\={7} ms).
-
diff --git a/java/org/apache/catalina/manager/host/HostManagerServlet.java b/java/org/apache/catalina/manager/host/HostManagerServlet.java
index 267c58d0..605c1029 100644
--- a/java/org/apache/catalina/manager/host/HostManagerServlet.java
+++ b/java/org/apache/catalina/manager/host/HostManagerServlet.java
@@ -44,6 +44,7 @@ import org.apache.catalina.core.ContainerBase;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.HostConfig;
import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -519,15 +520,8 @@ public class HostManagerServlet
Host host = (Host) hosts[i];
String name = host.getName();
String[] aliases = host.findAliases();
- StringBuilder buf = new StringBuilder();
- if (aliases.length > 0) {
- buf.append(aliases[0]);
- for (int j = 1; j < aliases.length; j++) {
- buf.append(',').append(aliases[j]);
- }
- }
writer.println(smClient.getString("hostManagerServlet.listitem",
- name, buf.toString()));
+ name, StringUtils.join(aliases)));
}
}
diff --git a/java/org/apache/catalina/realm/LocalStrings.properties b/java/org/apache/catalina/realm/LocalStrings.properties
index 5b591ca5..7eb65844 100644
--- a/java/org/apache/catalina/realm/LocalStrings.properties
+++ b/java/org/apache/catalina/realm/LocalStrings.properties
@@ -29,7 +29,7 @@ jaasRealm.loginContextCreated=JAAS LoginContext created for username "{0}"
jaasRealm.checkPrincipal=Checking Principal "{0}" [{1}]
jaasRealm.userPrincipalSuccess=Principal "{0}" is a valid user class. We will use this as the user Principal.
jaasRealm.userPrincipalFailure=No valid user Principal found
-jaasRealm.rolePrincipalAdd=Adding role Principal "{0}" to this user Principal's roles
+jaasRealm.rolePrincipalAdd=Adding role Principal "{0}" to this user Principal''s roles
jaasRealm.rolePrincipalFailure=No valid role Principals found.
jaasCallback.username=Returned username "{0}"
jdbcRealm.authenticateFailure=Username {0} NOT successfully authenticated
diff --git a/java/org/apache/catalina/security/SecurityListener.java b/java/org/apache/catalina/security/SecurityListener.java
index feb056a3..782753f4 100644
--- a/java/org/apache/catalina/security/SecurityListener.java
+++ b/java/org/apache/catalina/security/SecurityListener.java
@@ -17,7 +17,6 @@
package org.apache.catalina.security;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.Locale;
import java.util.Set;
@@ -26,6 +25,7 @@ import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.res.StringManager;
public class SecurityListener implements LifecycleListener {
@@ -97,18 +97,7 @@ public class SecurityListener implements LifecycleListener {
* @return A comma separated list of operating system user names.
*/
public String getCheckedOsUsers() {
- if (checkedOsUsers.size() == 0) {
- return "";
- }
-
- StringBuilder result = new StringBuilder();
- Iterator<String> iter = checkedOsUsers.iterator();
- result.append(iter.next());
- while (iter.hasNext()) {
- result.append(',');
- result.append(iter.next());
- }
- return result.toString();
+ return StringUtils.join(checkedOsUsers);
}
diff --git a/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java b/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java
index b55ec06b..ebd1b063 100644
--- a/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java
+++ b/java/org/apache/catalina/servlet4preview/http/HttpServletRequest.java
@@ -37,5 +37,5 @@ public interface HttpServletRequest extends javax.servlet.http.HttpServletReques
*
* @since Servlet 4.0
*/
- public PushBuilder getPushBuilder();
+ public PushBuilder newPushBuilder();
}
diff --git a/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java b/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java
index 7904efa7..a5050432 100644
--- a/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java
+++ b/java/org/apache/catalina/servlet4preview/http/HttpServletRequestWrapper.java
@@ -56,12 +56,12 @@ public class HttpServletRequestWrapper extends javax.servlet.http.HttpServletReq
* {@inheritDoc}
* <p>
* The default behavior of this method is to return
- * {@link HttpServletRequest#getPushBuilder()} on the wrapped request object.
+ * {@link HttpServletRequest#newPushBuilder()} on the wrapped request object.
*
* @since Servlet 4.0
*/
@Override
- public PushBuilder getPushBuilder() {
- return this._getHttpServletRequest().getPushBuilder();
+ public PushBuilder newPushBuilder() {
+ return this._getHttpServletRequest().newPushBuilder();
}
}
diff --git a/java/org/apache/catalina/servlet4preview/http/PushBuilder.java b/java/org/apache/catalina/servlet4preview/http/PushBuilder.java
index c5ab5dbc..991d446d 100644
--- a/java/org/apache/catalina/servlet4preview/http/PushBuilder.java
+++ b/java/org/apache/catalina/servlet4preview/http/PushBuilder.java
@@ -34,9 +34,6 @@ import java.util.Set;
* <li>The referer header will be set to
* {@link HttpServletRequest#getRequestURL()} plus, if present, the query
* string from {@link HttpServletRequest#getQueryString()}.
- * <li>If either of the headers {@code If-Modified-Since} or
- * {@code If-None-Match} were present then {@link #isConditional()} will be
- * set to {@code true}.
* </ul>
*
* @since Servlet 4.0
@@ -87,17 +84,6 @@ public interface PushBuilder {
PushBuilder sessionId(String sessionId);
/**
- * Sets if the request will be conditional. If {@code true} the values from
- * {@link #getETag()} and {@link #getLastModified()} will be used to
- * construct appropriate headers.
- *
- * @param conditional Should generated push requests be conditional
- *
- * @return This builder instance
- */
- PushBuilder conditional(boolean conditional);
-
- /**
* Sets a HTTP header on the request. Any existing headers of the same name
* are first remove.
*
@@ -143,28 +129,6 @@ public interface PushBuilder {
PushBuilder path(String path);
/**
- * Sets the eTag to be used for conditional push requests. This will be
- * set to {@code null} after a call to {@link #push()} so it must be
- * explicitly set for every push request that requires it.
- *
- * @param eTag The eTag use for the push request
- *
- * @return This builder instance
- */
- PushBuilder eTag(String eTag);
-
- /**
- * Sets the last modified to be used for conditional push requests. This
- * will be set to {@code null} after a call to {@link #push()} so it must be
- * explicitly set for every push request that requires it.
- *
- * @param lastModified The last modified value to use for the push request
- *
- * @return This builder instance
- */
- PushBuilder lastModified(String lastModified);
-
- /**
* Generates the push request and sends it to the client unless pushes are
* not available for some reason. After calling this method the following
* fields are set to {@code null}:
@@ -205,14 +169,6 @@ public interface PushBuilder {
String getSessionId();
/**
- * Will push requests generated by future calls to {@code push()} be
- * conditional.
- *
- * @return {@code true} if push requests will be conditional
- */
- boolean isConditional();
-
- /**
* @return The current set of names of HTTP headers to be used the next time
* {@code push()} is called.
*/
@@ -237,21 +193,4 @@ public interface PushBuilder {
* @return The path value that will be associated with the next push request
*/
String getPath();
-
- /**
- * Obtain the eTag that will be used for the push request that will be
- * generated by the next call to {@code push()}.
- *
- * @return The eTag value that will be associated with the next push request
- */
- String getETag();
-
- /**
- * Obtain the last modified that will be used for the push request that will
- * be generated by the next call to {@code push()}.
- *
- * @return The last modified value that will be associated with the next
- * push request
- */
- String getLastModified();
}
diff --git a/java/org/apache/catalina/startup/Catalina.java b/java/org/apache/catalina/startup/Catalina.java
index 22a8d348..2c7a55fd 100644
--- a/java/org/apache/catalina/startup/Catalina.java
+++ b/java/org/apache/catalina/startup/Catalina.java
@@ -125,6 +125,7 @@ public class Catalina {
public Catalina() {
setSecurityProtection();
+ ExceptionUtils.preload();
}
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
index 012a17fd..1f6b84bd 100644
--- a/java/org/apache/catalina/startup/ContextConfig.java
+++ b/java/org/apache/catalina/startup/ContextConfig.java
@@ -142,7 +142,7 @@ public class ContextConfig implements LifecycleListener {
// Load our mapping properties for the standard authenticators
Properties props = new Properties();
try (InputStream is = ContextConfig.class.getClassLoader().getResourceAsStream(
- "org/apache/catalina/startup/Authenticators.properties");) {
+ "org/apache/catalina/startup/Authenticators.properties")) {
if (is != null) {
props.load(is);
}
diff --git a/java/org/apache/catalina/startup/LocalStrings.properties b/java/org/apache/catalina/startup/LocalStrings.properties
index bb2e3038..498faa5b 100644
--- a/java/org/apache/catalina/startup/LocalStrings.properties
+++ b/java/org/apache/catalina/startup/LocalStrings.properties
@@ -52,7 +52,7 @@ contextConfig.invalidSciHandlesTypes=Unable to load class [{0}] to check against
contextConfig.jarFile=Unable to process Jar [{0}] for annotations
contextConfig.jndiUrl=Unable to process JNDI URL [{0}] for annotations
contextConfig.jndiUrlNotDirContextConn=The connection created for URL [{0}] was not a DirContextURLConnection
-contextConfig.jspFile.error=JSP file {0} must start with a ''/'
+contextConfig.jspFile.error=JSP file {0} must start with a ''/''
contextConfig.jspFile.warning=WARNING: JSP file {0} must start with a ''/'' in Servlet 2.4
contextConfig.missingRealm=No Realm has been configured to authenticate against
contextConfig.processAnnotationsDir.debug=Scanning directory for class files with annotations [{0}]
diff --git a/java/org/apache/catalina/startup/LocalStrings_es.properties b/java/org/apache/catalina/startup/LocalStrings_es.properties
index 64907069..0fa6e4bc 100644
--- a/java/org/apache/catalina/startup/LocalStrings_es.properties
+++ b/java/org/apache/catalina/startup/LocalStrings_es.properties
@@ -44,7 +44,7 @@ contextConfig.invalidSci = No se pudo crear el ServletContentInitializer [{0}]
contextConfig.invalidSciHandlesTypes = No puedo cargar la clase [{0}] para revisar contra la anotaci\u00F3n @HandlesTypes de uno o m\u00E1s ServletContentInitializers.
contextConfig.jndiUrl = No puedo procesar la URL JNDI [{0}] para las anotaciones
contextConfig.jndiUrlNotDirContextConn = La conexi\u00F3n creada para la URL [{0}] no era una DirContextURLConnection
-contextConfig.jspFile.error = El archivo JSP {0} debe de comenzar con ''/'
+contextConfig.jspFile.error = El archivo JSP {0} debe de comenzar con ''/''
contextConfig.jspFile.warning = AVISO\: El archivo JSP {0} debe de comenzar con ''/'' en Servlet 2.4
contextConfig.missingRealm = Alg\u00FAn reino (realm) no ha sido configurado para realizar la autenticaci\u00F3n
contextConfig.resourceJarFail = Hallado JAR fallido a los procesos en URL [{0}] para recursos est\u00E1ticos a ser incluidos en contexto con nombre [{0}]
diff --git a/java/org/apache/catalina/startup/Tomcat.java b/java/org/apache/catalina/startup/Tomcat.java
index 7285c527..fe57ba05 100644
--- a/java/org/apache/catalina/startup/Tomcat.java
+++ b/java/org/apache/catalina/startup/Tomcat.java
@@ -61,6 +61,7 @@ import org.apache.catalina.core.StandardService;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.catalina.realm.RealmBase;
+import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.UriUtil;
import org.apache.tomcat.util.descriptor.web.LoginConfig;
@@ -145,7 +146,7 @@ public class Tomcat {
private final Map<String, Principal> userPrincipals = new HashMap<>();
public Tomcat() {
- // NOOP
+ ExceptionUtils.preload();
}
/**
diff --git a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
index 1626d99c..0103591d 100644
--- a/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
+++ b/java/org/apache/catalina/tribes/group/interceptors/LocalStrings.properties
@@ -42,7 +42,7 @@ tcpFailureDetector.already.disappeared=Verification complete. Member already dis
tcpFailureDetector.member.disappeared=Verification complete. Member disappeared[{0}]
tcpFailureDetector.still.alive=Verification complete. Member still alive[{0}]
tcpFailureDetector.heartbeat.failed=Unable to perform heartbeat on the TcpFailureDetector.
-tcpFailureDetector.performBasicCheck.memberAdded=Member added, even though we weren't notified:{0}
+tcpFailureDetector.performBasicCheck.memberAdded=Member added, even though we weren''t notified:{0}
tcpFailureDetector.suspectMember.dead=Suspect member, confirmed dead.[{0}]
tcpFailureDetector.suspectMember.alive=Suspect member, confirmed alive.[{0}]
tcpFailureDetector.failureDetection.failed=Unable to perform failure detection check, assuming member down.
diff --git a/java/org/apache/catalina/users/MemoryGroup.java b/java/org/apache/catalina/users/MemoryGroup.java
index a1c2acc5..e3bdc5be 100644
--- a/java/org/apache/catalina/users/MemoryGroup.java
+++ b/java/org/apache/catalina/users/MemoryGroup.java
@@ -25,6 +25,8 @@ import java.util.Iterator;
import org.apache.catalina.Role;
import org.apache.catalina.User;
import org.apache.catalina.UserDatabase;
+import org.apache.tomcat.util.buf.StringUtils;
+import org.apache.tomcat.util.buf.StringUtils.Function;
/**
@@ -200,22 +202,12 @@ public class MemoryGroup extends AbstractGroup {
synchronized (roles) {
if (roles.size() > 0) {
sb.append(" roles=\"");
- int n = 0;
- Iterator<Role> values = roles.iterator();
- while (values.hasNext()) {
- if (n > 0) {
- sb.append(',');
- }
- n++;
- sb.append((values.next()).getRolename());
- }
+ StringUtils.join(roles, ',', new Function<Role>(){
+ @Override public String apply(Role t) { return t.getRolename(); }}, sb);
sb.append("\"");
}
}
sb.append("/>");
return (sb.toString());
-
}
-
-
}
diff --git a/java/org/apache/catalina/users/MemoryUser.java b/java/org/apache/catalina/users/MemoryUser.java
index 744c8117..f4e30641 100644
--- a/java/org/apache/catalina/users/MemoryUser.java
+++ b/java/org/apache/catalina/users/MemoryUser.java
@@ -26,6 +26,8 @@ import org.apache.catalina.Group;
import org.apache.catalina.Role;
import org.apache.catalina.UserDatabase;
import org.apache.catalina.util.RequestUtil;
+import org.apache.tomcat.util.buf.StringUtils;
+import org.apache.tomcat.util.buf.StringUtils.Function;
/**
* <p>Concrete implementation of {@link org.apache.catalina.User} for the
@@ -271,38 +273,30 @@ public class MemoryUser extends AbstractUser {
synchronized (groups) {
if (groups.size() > 0) {
sb.append(" groups=\"");
- int n = 0;
- Iterator<Group> values = groups.iterator();
- while (values.hasNext()) {
- if (n > 0) {
- sb.append(',');
- }
- n++;
- sb.append(RequestUtil.filter(values.next().getGroupname()));
+ StringUtils.join(groups, ',', new Function<Group>() {
+ @Override public String apply(Group t) {
+ return RequestUtil.filter(t.getGroupname());
}
+ }, sb);
sb.append("\"");
}
}
synchronized (roles) {
if (roles.size() > 0) {
sb.append(" roles=\"");
- int n = 0;
- Iterator<Role> values = roles.iterator();
- while (values.hasNext()) {
- if (n > 0) {
- sb.append(',');
- }
- n++;
- sb.append(RequestUtil.filter(values.next().getRolename()));
+ StringUtils.join(roles, ',', new Function<Role>() {
+ @Override public String apply(Role t) {
+ return RequestUtil.filter(t.getRolename());
}
+ }, sb);
sb.append("\"");
}
}
sb.append("/>");
- return (sb.toString());
-
+ return sb.toString();
}
+
/**
* <p>Return a String representation of this user.</p>
*/
@@ -320,35 +314,25 @@ public class MemoryUser extends AbstractUser {
synchronized (groups) {
if (groups.size() > 0) {
sb.append(", groups=\"");
- int n = 0;
- Iterator<Group> values = groups.iterator();
- while (values.hasNext()) {
- if (n > 0) {
- sb.append(',');
- }
- n++;
- sb.append(RequestUtil.filter(values.next().getGroupname()));
+ StringUtils.join(groups, ',', new Function<Group>() {
+ @Override public String apply(Group t) {
+ return RequestUtil.filter(t.getGroupname());
}
+ }, sb);
sb.append("\"");
}
}
synchronized (roles) {
if (roles.size() > 0) {
sb.append(", roles=\"");
- int n = 0;
- Iterator<Role> values = roles.iterator();
- while (values.hasNext()) {
- if (n > 0) {
- sb.append(',');
- }
- n++;
- sb.append(RequestUtil.filter(values.next().getRolename()));
+ StringUtils.join(roles, ',', new Function<Role>() {
+ @Override public String apply(Role t) {
+ return RequestUtil.filter(t.getRolename());
}
+ }, sb);
sb.append("\"");
}
}
- return (sb.toString());
+ return sb.toString();
}
-
-
}
diff --git a/java/org/apache/catalina/util/ExtensionValidator.java b/java/org/apache/catalina/util/ExtensionValidator.java
index f9da4cf7..2b358027 100644
--- a/java/org/apache/catalina/util/ExtensionValidator.java
+++ b/java/org/apache/catalina/util/ExtensionValidator.java
@@ -150,9 +150,8 @@ public final class ExtensionValidator {
if (manifestResource.isFile()) {
// Primarily used for error reporting
String jarName = manifestResource.getURL().toExternalForm();
- Manifest jmanifest = null;
- try (InputStream is = manifestResource.getInputStream()) {
- jmanifest = new Manifest(is);
+ Manifest jmanifest = manifestResource.getManifest();
+ if (jmanifest != null) {
ManifestResource mre = new ManifestResource(jarName,
jmanifest, ManifestResource.APPLICATION);
appManifestResources.add(mre);
diff --git a/java/org/apache/catalina/webresources/JarWarResourceSet.java b/java/org/apache/catalina/webresources/JarWarResourceSet.java
index 7f16b63c..805d8ddb 100644
--- a/java/org/apache/catalina/webresources/JarWarResourceSet.java
+++ b/java/org/apache/catalina/webresources/JarWarResourceSet.java
@@ -107,11 +107,28 @@ public class JarWarResourceSet extends AbstractArchiveResourceSet {
try (JarInputStream jarIs = new JarInputStream(jarFileIs)) {
JarEntry entry = jarIs.getNextJarEntry();
+ boolean hasMetaInf = false;
while (entry != null) {
+ if (!hasMetaInf && entry.getName().startsWith("META-INF/")) {
+ hasMetaInf = true;
+ }
archiveEntries.put(entry.getName(), entry);
entry = jarIs.getNextJarEntry();
}
setManifest(jarIs.getManifest());
+ // Hacks to work-around JarInputStream swallowing these
+ // entries. The attributes for these entries will be
+ // incomplete. Making the attributes available would
+ // require (re-)reading the stream as a ZipInputStream
+ // and creating JarEntry objects from the ZipEntries.
+ if (hasMetaInf) {
+ JarEntry metaInfDir = new JarEntry("META-INF/");
+ archiveEntries.put(metaInfDir.getName(), metaInfDir);
+ }
+ if (jarIs.getManifest() != null) {
+ JarEntry manifest = new JarEntry("META-INF/MANIFEST.MF");
+ archiveEntries.put(manifest.getName(), manifest);
+ }
}
} catch (IOException ioe) {
// Should never happen
diff --git a/java/org/apache/catalina/webresources/LocalStrings.properties b/java/org/apache/catalina/webresources/LocalStrings.properties
index 90dace1e..2f7921a0 100644
--- a/java/org/apache/catalina/webresources/LocalStrings.properties
+++ b/java/org/apache/catalina/webresources/LocalStrings.properties
@@ -25,7 +25,7 @@ cache.backgroundEvictFail=The background cache eviction process was unable to fr
cache.objectMaxSizeTooBig=The value of [{0}]kB for objectMaxSize is larger than the limit of maxSize/20 so has been reduced to [{1}]kB
cache.objectMaxSizeTooBigBytes=The value specified for the maximum object size to cache [{0}]kB is greater than Integer.MAX_VALUE bytes which is the maximum size that can be cached. The limit will be set to Integer.MAX_VALUE bytes.
-classpathUrlStreamHandler.notFound=Unable to load the resource [{0}] using the thread context class loader or the current class's class loader
+classpathUrlStreamHandler.notFound=Unable to load the resource [{0}] using the thread context class loader or the current class''s class loader
dirResourceSet.manifestFail=Failed to read manifest from [{0}]
dirResourceSet.notDirectory=The directory specified by base and internal path [{0}]{1}[{2}] does not exist.
diff --git a/java/org/apache/coyote/AbstractProcessorLight.java b/java/org/apache/coyote/AbstractProcessorLight.java
index c199dac3..a6acd1f3 100644
--- a/java/org/apache/coyote/AbstractProcessorLight.java
+++ b/java/org/apache/coyote/AbstractProcessorLight.java
@@ -62,8 +62,12 @@ public abstract class AbstractProcessorLight implements Processor {
} else if (status == SocketEvent.OPEN_WRITE) {
// Extra write event likely after async, ignore
state = SocketState.LONG;
- } else {
+ } else if (status == SocketEvent.OPEN_READ){
state = service(socketWrapper);
+ } else {
+ // Default to closing the socket if the SocketEvent passed in
+ // is not consistent with the current state of the Processor
+ state = SocketState.CLOSED;
}
if (state != SocketState.CLOSED && isAsync()) {
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
index e69e984c..533812de 100644
--- a/java/org/apache/coyote/AbstractProtocol.java
+++ b/java/org/apache/coyote/AbstractProtocol.java
@@ -19,6 +19,7 @@ package org.apache.coyote;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -42,6 +43,8 @@ import org.apache.tomcat.util.collections.SynchronizedStack;
import org.apache.tomcat.util.modeler.Registry;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler;
+import org.apache.tomcat.util.net.SSLHostConfig;
+import org.apache.tomcat.util.net.SSLHostConfigCertificate;
import org.apache.tomcat.util.net.SocketEvent;
import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
@@ -74,6 +77,10 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
protected ObjectName tpOname = null;
+ private Set<ObjectName> sslOnames = new HashSet<>();
+ private Set<ObjectName> sslCertOnames = new HashSet<>();
+
+
/**
* Unique ID for this connector. Only used if the connector is configured
* to use a random port as the port will change if stop(), start() is
@@ -200,6 +207,40 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
return asyncTimeout;
}
+ /**
+ * Specifies whether the reason phrase will be sent in the response.
+ * By default a reason phrase will not be sent in the response.
+ *
+ * @deprecated This option will be removed in Tomcat 9. Reason phrase will
+ * not be sent.
+ */
+ @Deprecated
+ private boolean sendReasonPhrase = false;
+ /**
+ * Returns whether the reason phrase will be sent in the response.
+ * By default a reason phrase will not be sent in the response.
+ *
+ * @return whether the reason phrase will be sent
+ * @deprecated This option will be removed in Tomcat 9. Reason phrase will
+ * not be sent.
+ */
+ @Deprecated
+ public boolean getSendReasonPhrase() {
+ return sendReasonPhrase;
+ }
+ /**
+ * Specifies whether the reason phrase will be sent in the response.
+ * By default a reason phrase will not be sent in the response.
+ *
+ * @param sendReasonPhrase specifies whether the reason phrase will be sent
+ * @deprecated This option will be removed in Tomcat 9. Reason phrase will
+ * not be sent.
+ */
+ @Deprecated
+ public void setSendReasonPhrase(boolean sendReasonPhrase) {
+ this.sendReasonPhrase = sendReasonPhrase;
+ }
+
// ---------------------- Properties that are passed through to the EndPoint
@@ -536,10 +577,8 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
if (this.domain != null) {
try {
- tpOname = new ObjectName(domain + ":" +
- "type=ThreadPool,name=" + getName());
- Registry.getRegistry(null, null).registerComponent(endpoint,
- tpOname, null);
+ tpOname = new ObjectName(domain + ":type=ThreadPool,name=" + getName());
+ Registry.getRegistry(null, null).registerComponent(endpoint, tpOname, null);
} catch (Exception e) {
getLog().error(sm.getString(
"abstractProtocolHandler.mbeanRegistrationFailed",
@@ -549,6 +588,22 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
":type=GlobalRequestProcessor,name=" + getName());
Registry.getRegistry(null, null).registerComponent(
getHandler().getGlobal(), rgOname, null );
+
+ for (SSLHostConfig sslHostConfig : getEndpoint().findSslHostConfigs()) {
+ ObjectName sslOname = new ObjectName(domain + ":type=SSLHostConfig,ThreadPool=" +
+ getName() + ",name=" + sslHostConfig.getHostName());
+ Registry.getRegistry(null, null).registerComponent(sslHostConfig, sslOname, null);
+ sslOnames.add(sslOname);
+ for (SSLHostConfigCertificate sslHostConfigCert : sslHostConfig.getCertificates()) {
+ ObjectName sslCertOname = new ObjectName(domain +
+ ":type=SSLHostConfigCertificate,ThreadPool=" + getName() +
+ ",Host=" + sslHostConfig.getHostName() +
+ ",name=" + sslHostConfigCert.getType());
+ Registry.getRegistry(null, null).registerComponent(
+ sslHostConfigCert, sslCertOname, null);
+ sslCertOnames.add(sslCertOname);
+ }
+ }
}
String endpointName = getName();
@@ -568,12 +623,12 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
public void start() throws Exception {
if (getLog().isInfoEnabled())
getLog().info(sm.getString("abstractProtocolHandler.start",
- getNameInternal()));
+ getName()));
try {
endpoint.start();
} catch (Exception ex) {
getLog().error(sm.getString("abstractProtocolHandler.startError",
- getNameInternal()), ex);
+ getName()), ex);
throw ex;
}
@@ -668,11 +723,19 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
}
}
- if (tpOname != null)
+ if (tpOname != null) {
Registry.getRegistry(null, null).unregisterComponent(tpOname);
- if (rgOname != null)
+ }
+ if (rgOname != null) {
Registry.getRegistry(null, null).unregisterComponent(rgOname);
}
+ for (ObjectName sslOname : sslOnames) {
+ Registry.getRegistry(null, null).unregisterComponent(sslOname);
+ }
+ for (ObjectName sslCertOname : sslCertOnames) {
+ Registry.getRegistry(null, null).unregisterComponent(sslCertOname);
+ }
+ }
// ------------------------------------------- Connection handler base class
@@ -870,10 +933,9 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
wrapper.registerReadInterest();
} else if (state == SocketState.SENDFILE) {
// Sendfile in progress. If it fails, the socket will be
- // closed. If it works, the socket will be re-added to the
- // poller
- connections.remove(socket);
- release(processor);
+ // closed. If it works, the socket either be added to the
+ // poller (or equivalent) to await more data or processed
+ // if there are any pipe-lined requests remaining.
} else if (state == SocketState.UPGRADED) {
// Don't add sockets back to the poller if this was a
// non-blocking write otherwise the poller may trigger
diff --git a/java/org/apache/coyote/Constants.java b/java/org/apache/coyote/Constants.java
index dc48b5f6..cef86be3 100644
--- a/java/org/apache/coyote/Constants.java
+++ b/java/org/apache/coyote/Constants.java
@@ -49,6 +49,15 @@ public final class Constants {
/**
+ * If true, custom HTTP status messages will be used in headers.
+ * @deprecated This option will be removed in Tomcat 9. Reason phrase will
+ * not be sent.
+ */
+ @Deprecated
+ public static final boolean USE_CUSTOM_STATUS_MSG_IN_HEADER =
+ Boolean.getBoolean("org.apache.coyote.USE_CUSTOM_STATUS_MSG_IN_HEADER");
+
+ /**
* The request attribute that is set to the value of {@code Boolean.TRUE}
* if connector processing this request supports use of sendfile.
*/
diff --git a/java/org/apache/coyote/Response.java b/java/org/apache/coyote/Response.java
index 4cc44284..625315e5 100644
--- a/java/org/apache/coyote/Response.java
+++ b/java/org/apache/coyote/Response.java
@@ -25,6 +25,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.WriteListener;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.MimeHeaders;
@@ -45,6 +47,8 @@ public final class Response {
private static final StringManager sm = StringManager.getManager(Response.class);
+ private static final Log log = LogFactory.getLog(Response.class);
+
// ----------------------------------------------------- Class Variables
/**
@@ -632,7 +636,10 @@ public final class Response {
public boolean isReady() {
if (listener == null) {
- throw new IllegalStateException(sm.getString("response.notNonBlocking"));
+ if (log.isDebugEnabled()) {
+ log.debug(sm.getString("response.notNonBlocking"));
+ }
+ return false;
}
// Assume write is not possible
boolean ready = false;
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
index 8eac1b77..b145a4c7 100644
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
@@ -182,6 +182,7 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
}
+ @SuppressWarnings("deprecation")
@Override
protected Processor createProcessor() {
AjpProcessor processor = new AjpProcessor(getPacketSize(), getEndpoint());
@@ -192,6 +193,7 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
processor.setRequiredSecret(requiredSecret);
processor.setKeepAliveTimeout(getKeepAliveTimeout());
processor.setClientCertProvider(getClientCertProvider());
+ processor.setSendReasonPhrase(getSendReasonPhrase());
return processor;
}
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java
index c1095c89..bfbfe2fc 100644
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
@@ -40,6 +40,7 @@ import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.HexUtils;
import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.HttpMessages;
import org.apache.tomcat.util.http.MimeHeaders;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
@@ -346,6 +347,13 @@ public class AjpProcessor extends AbstractProcessor {
this.clientCertProvider = clientCertProvider;
}
+ @Deprecated
+ private boolean sendReasonPhrase = false;
+ @Deprecated
+ void setSendReasonPhrase(boolean sendReasonPhrase) {
+ this.sendReasonPhrase = sendReasonPhrase;
+ }
+
// --------------------------------------------------------- Public Methods
@@ -1008,6 +1016,7 @@ public class AjpProcessor extends AbstractProcessor {
* When committing the response, we have to validate the set of headers, as
* well as setup the response filters.
*/
+ @SuppressWarnings("deprecation")
@Override
protected final void prepareResponse() throws IOException {
@@ -1037,9 +1046,26 @@ public class AjpProcessor extends AbstractProcessor {
// HTTP header contents
responseMessage.appendInt(statusCode);
+ if (sendReasonPhrase) {
+ String message = null;
+ if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER &&
+ HttpMessages.isSafeInHttpHeader(response.getMessage())) {
+ message = response.getMessage();
+ }
+ if (message == null) {
+ message = HttpMessages.getInstance(
+ response.getLocale()).getMessage(response.getStatus());
+ }
+ if (message == null) {
+ // mod_jk + httpd 2.x fails with a null status message - bug 45026
+ message = Integer.toString(response.getStatus());
+ }
+ tmpMB.setString(message);
+ } else {
// Reason phrase is optional but mod_jk + httpd 2.x fails with a null
// reason phrase - bug 45026
tmpMB.setString(Integer.toString(response.getStatus()));
+ }
responseMessage.appendBytes(tmpMB);
// Special headers
diff --git a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
index 2c0161ac..6c7171e1 100644
--- a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
+++ b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
@@ -36,6 +36,7 @@ import org.apache.coyote.UpgradeToken;
import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.coyote.http11.upgrade.UpgradeProcessorExternal;
import org.apache.coyote.http11.upgrade.UpgradeProcessorInternal;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.SSLHostConfig;
import org.apache.tomcat.util.net.SocketWrapperBase;
@@ -142,20 +143,44 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
}
- private String compressableMimeType = "text/html,text/xml,text/plain,text/css,text/javascript,application/javascript";
- private String[] compressableMimeTypes = null;
- public String getCompressableMimeType() { return compressableMimeType; }
+ /**
+ * @return See {@link #getCompressibleMimeType()}
+ * @deprecated Use {@link #getCompressibleMimeType()}
+ */
+ @Deprecated
+ public String getCompressableMimeType() {
+ return getCompressibleMimeType();
+ }
+ /**
+ * @param valueS See {@link #setCompressibleMimeType(String)}
+ * @deprecated Use {@link #setCompressibleMimeType(String)}
+ */
+ @Deprecated
public void setCompressableMimeType(String valueS) {
- compressableMimeType = valueS;
- compressableMimeTypes = null;
+ setCompressibleMimeType(valueS);
}
+ /**
+ * @return See {@link #getCompressibleMimeTypes()}
+ * @deprecated Use {@link #getCompressibleMimeTypes()}
+ */
+ @Deprecated
public String[] getCompressableMimeTypes() {
- String[] result = compressableMimeTypes;
+ return getCompressibleMimeTypes();
+ }
+ private String compressibleMimeType = "text/html,text/xml,text/plain,text/css,text/javascript,application/javascript";
+ private String[] compressibleMimeTypes = null;
+ public String getCompressibleMimeType() { return compressibleMimeType; }
+ public void setCompressibleMimeType(String valueS) {
+ compressibleMimeType = valueS;
+ compressibleMimeTypes = null;
+ }
+ public String[] getCompressibleMimeTypes() {
+ String[] result = compressibleMimeTypes;
if (result != null) {
return result;
}
List<String> values = new ArrayList<>();
- StringTokenizer tokens = new StringTokenizer(compressableMimeType, ",");
+ StringTokenizer tokens = new StringTokenizer(compressibleMimeType, ",");
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken().trim();
if (token.length() > 0) {
@@ -163,7 +188,7 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
}
}
result = values.toArray(new String[values.size()]);
- compressableMimeTypes = result;
+ compressibleMimeTypes = result;
return result;
}
@@ -274,17 +299,7 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
// sync is unnecessary.
List<String> copy = new ArrayList<>(allowedTrailerHeaders.size());
copy.addAll(allowedTrailerHeaders);
- StringBuilder result = new StringBuilder();
- boolean first = true;
- for (String header : copy) {
- if (first) {
- first = false;
- } else {
- result.append(',');
- }
- result.append(header);
- }
- return result.toString();
+ return StringUtils.join(copy);
}
public void addAllowedTrailerHeader(String header) {
if (header != null) {
@@ -426,203 +441,353 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
}
- // TODO: All of these SSL setters can be removed once it is no longer
- // necessary to support the old configuration attributes (Tomcat 10?).
+ // TODO: All of these SSL getters and setters can be removed once it is no
+ // longer necessary to support the old configuration attributes (Tomcat 10?)
+ public String getSslEnabledProtocols() {
+ registerDefaultSSLHostConfig();
+ return StringUtils.join(defaultSSLHostConfig.getEnabledProtocols());
+ }
public void setSslEnabledProtocols(String enabledProtocols) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setProtocols(enabledProtocols);
}
+ public String getSSLProtocol() {
+ registerDefaultSSLHostConfig();
+ return StringUtils.join(defaultSSLHostConfig.getEnabledProtocols());
+ }
public void setSSLProtocol(String sslProtocol) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setProtocols(sslProtocol);
}
+ public String getKeystoreFile() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateKeystoreFile();
+ }
public void setKeystoreFile(String keystoreFile) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateKeystoreFile(keystoreFile);
}
+ public String getSSLCertificateChainFile() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateChainFile();
+ }
public void setSSLCertificateChainFile(String certificateChainFile) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateChainFile(certificateChainFile);
}
+ public String getSSLCertificateFile() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateFile();
+ }
public void setSSLCertificateFile(String certificateFile) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateFile(certificateFile);
}
+ public String getSSLCertificateKeyFile() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateKeyFile();
+ }
public void setSSLCertificateKeyFile(String certificateKeyFile) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateKeyFile(certificateKeyFile);
}
+ public String getAlgorithm() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getKeyManagerAlgorithm();
+ }
public void setAlgorithm(String keyManagerAlgorithm) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setKeyManagerAlgorithm(keyManagerAlgorithm);
}
+ public String getClientAuth() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateVerification().toString();
+ }
public void setClientAuth(String certificateVerification) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateVerification(certificateVerification);
}
+ public String getSSLVerifyClient() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateVerification().toString();
+ }
public void setSSLVerifyClient(String certificateVerification) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateVerification(certificateVerification);
}
+ public int getTrustMaxCertLength(){
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateVerificationDepth();
+ }
public void setTrustMaxCertLength(int certificateVerificationDepth){
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateVerificationDepth(certificateVerificationDepth);
}
+ public int getSSLVerifyDepth() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateVerificationDepth();
+ }
public void setSSLVerifyDepth(int certificateVerificationDepth) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateVerificationDepth(certificateVerificationDepth);
}
+ public String getUseServerCipherSuitesOrder() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getHonorCipherOrder();
+ }
public void setUseServerCipherSuitesOrder(String honorCipherOrder) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setHonorCipherOrder(honorCipherOrder);
}
+ public String getSSLHonorCipherOrder() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getHonorCipherOrder();
+ }
public void setSSLHonorCipherOrder(String honorCipherOrder) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setHonorCipherOrder(honorCipherOrder);
}
+ public String getCiphers() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCiphers();
+ }
public void setCiphers(String ciphers) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCiphers(ciphers);
}
+ public String getSSLCipherSuite() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCiphers();
+ }
public void setSSLCipherSuite(String ciphers) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCiphers(ciphers);
}
+
+ public String getKeystorePass() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateKeystorePassword();
+ }
public void setKeystorePass(String certificateKeystorePassword) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateKeystorePassword(certificateKeystorePassword);
}
+
+ public String getKeyPass() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateKeyPassword();
+ }
public void setKeyPass(String certificateKeyPassword) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateKeyPassword(certificateKeyPassword);
}
+ public String getSSLPassword() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateKeyPassword();
+ }
public void setSSLPassword(String certificateKeyPassword) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateKeyPassword(certificateKeyPassword);
}
+ public String getCrlFile(){
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateRevocationListFile();
+ }
public void setCrlFile(String certificateRevocationListFile){
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateRevocationListFile(certificateRevocationListFile);
}
+ public String getSSLCARevocationFile() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateRevocationListFile();
+ }
public void setSSLCARevocationFile(String certificateRevocationListFile) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateRevocationListFile(certificateRevocationListFile);
}
+ public String getSSLCARevocationPath() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateRevocationListPath();
+ }
public void setSSLCARevocationPath(String certificateRevocationListPath) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateRevocationListPath(certificateRevocationListPath);
}
+ public String getKeystoreType() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateKeystoreType();
+ }
public void setKeystoreType(String certificateKeystoreType) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateKeystoreType(certificateKeystoreType);
}
+ public String getKeystoreProvider() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateKeystoreProvider();
+ }
public void setKeystoreProvider(String certificateKeystoreProvider) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateKeystoreProvider(certificateKeystoreProvider);
}
+ public String getKeyAlias() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCertificateKeyAlias();
+ }
public void setKeyAlias(String certificateKeyAlias) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCertificateKeyAlias(certificateKeyAlias);
}
+ public String getTruststoreAlgorithm(){
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getTruststoreAlgorithm();
+ }
public void setTruststoreAlgorithm(String truststoreAlgorithm){
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setTruststoreAlgorithm(truststoreAlgorithm);
}
+ public String getTruststoreFile(){
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getTruststoreFile();
+ }
public void setTruststoreFile(String truststoreFile){
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setTruststoreFile(truststoreFile);
}
+ public String getTruststorePass(){
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getTruststorePassword();
+ }
public void setTruststorePass(String truststorePassword){
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setTruststorePassword(truststorePassword);
}
+ public String getTruststoreType(){
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getTruststoreType();
+ }
public void setTruststoreType(String truststoreType){
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setTruststoreType(truststoreType);
}
+ public String getTruststoreProvider(){
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getTruststoreProvider();
+ }
public void setTruststoreProvider(String truststoreProvider){
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setTruststoreProvider(truststoreProvider);
}
+ public String getSslProtocol() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getSslProtocol();
+ }
public void setSslProtocol(String sslProtocol) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setSslProtocol(sslProtocol);
}
+ public int getSessionCacheSize(){
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getSessionCacheSize();
+ }
public void setSessionCacheSize(int sessionCacheSize){
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setSessionCacheSize(sessionCacheSize);
}
+ public int getSessionTimeout(){
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getSessionTimeout();
+ }
public void setSessionTimeout(int sessionTimeout){
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setSessionTimeout(sessionTimeout);
}
+ public String getSSLCACertificatePath() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCaCertificatePath();
+ }
public void setSSLCACertificatePath(String caCertificatePath) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCaCertificatePath(caCertificatePath);
}
+ public String getSSLCACertificateFile() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getCaCertificateFile();
+ }
public void setSSLCACertificateFile(String caCertificateFile) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setCaCertificateFile(caCertificateFile);
}
+ public boolean getSSLDisableCompression() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getDisableCompression();
+ }
public void setSSLDisableCompression(boolean disableCompression) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setDisableCompression(disableCompression);
}
+ public boolean getSSLDisableSessionTickets() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getDisableSessionTickets();
+ }
public void setSSLDisableSessionTickets(boolean disableSessionTickets) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setDisableSessionTickets(disableSessionTickets);
}
+ public String getTrustManagerClassName() {
+ registerDefaultSSLHostConfig();
+ return defaultSSLHostConfig.getTrustManagerClassName();
+ }
public void setTrustManagerClassName(String trustManagerClassName) {
registerDefaultSSLHostConfig();
defaultSSLHostConfig.setTrustManagerClassName(trustManagerClassName);
@@ -631,11 +796,12 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
// ------------------------------------------------------------- Common code
+ @SuppressWarnings("deprecation")
@Override
protected Processor createProcessor() {
Http11Processor processor = new Http11Processor(getMaxHttpHeaderSize(), getEndpoint(),
getMaxTrailerSize(), allowedTrailerHeaders, getMaxExtensionSize(),
- getMaxSwallowSize(), httpUpgradeProtocols);
+ getMaxSwallowSize(), httpUpgradeProtocols, getSendReasonPhrase());
processor.setAdapter(getAdapter());
processor.setMaxKeepAliveRequests(getMaxKeepAliveRequests());
processor.setConnectionUploadTimeout(getConnectionUploadTimeout());
@@ -643,7 +809,7 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
processor.setCompressionMinSize(getCompressionMinSize());
processor.setCompression(getCompression());
processor.setNoCompressionUserAgents(getNoCompressionUserAgents());
- processor.setCompressableMimeTypes(getCompressableMimeTypes());
+ processor.setCompressibleMimeTypes(getCompressibleMimeTypes());
processor.setRestrictedUserAgents(getRestrictedUserAgents());
processor.setMaxSavePostSize(getMaxSavePostSize());
processor.setServer(getServer());
diff --git a/java/org/apache/coyote/http11/Constants.java b/java/org/apache/coyote/http11/Constants.java
index cd8aae0f..7f0ce623 100644
--- a/java/org/apache/coyote/http11/Constants.java
+++ b/java/org/apache/coyote/http11/Constants.java
@@ -107,8 +107,14 @@ public final class Constants {
public static final String KEEPALIVE = "keep-alive";
public static final byte[] KEEPALIVE_BYTES = ByteChunk.convertToBytes(KEEPALIVE);
public static final String CHUNKED = "chunked";
- public static final byte[] ACK_BYTES =
+ /**
+ * @deprecated This option will be removed in Tomcat 9. Reason phrase will
+ * not be sent.
+ */
+ @Deprecated
+ public static final byte[] ACK_BYTES_REASON =
ByteChunk.convertToBytes("HTTP/1.1 100 Continue" + CRLF + CRLF);
+ public static final byte[] ACK_BYTES = ByteChunk.convertToBytes("HTTP/1.1 100 " + CRLF + CRLF);
public static final String TRANSFERENCODING = "Transfer-Encoding";
public static final byte[] _200_BYTES = ByteChunk.convertToBytes("200");
public static final byte[] _400_BYTES = ByteChunk.convertToBytes("400");
diff --git a/java/org/apache/coyote/http11/Http11OutputBuffer.java b/java/org/apache/coyote/http11/Http11OutputBuffer.java
index 788927b8..1992df8a 100644
--- a/java/org/apache/coyote/http11/Http11OutputBuffer.java
+++ b/java/org/apache/coyote/http11/Http11OutputBuffer.java
@@ -27,6 +27,7 @@ import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.http.HttpMessages;
import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
@@ -108,9 +109,14 @@ public class Http11OutputBuffer implements OutputBuffer {
protected long byteCount = 0;
- protected Http11OutputBuffer(Response response, int headerBufferSize) {
+ @Deprecated
+ private boolean sendReasonPhrase = false;
+
+
+ protected Http11OutputBuffer(Response response, int headerBufferSize, boolean sendReasonPhrase) {
this.response = response;
+ this.sendReasonPhrase = sendReasonPhrase;
headerBuffer = ByteBuffer.allocate(headerBufferSize);
@@ -121,6 +127,11 @@ public class Http11OutputBuffer implements OutputBuffer {
responseFinished = false;
outputStreamOutputBuffer = new SocketOutputBuffer();
+
+ if (sendReasonPhrase) {
+ // Cause loading of HttpMessages
+ HttpMessages.getInstance(response.getLocale()).getMessage(200);
+ }
}
@@ -327,9 +338,14 @@ public class Http11OutputBuffer implements OutputBuffer {
}
+ @SuppressWarnings("deprecation")
public void sendAck() throws IOException {
if (!response.isCommitted()) {
+ if (sendReasonPhrase) {
+ socketWrapper.write(isBlocking(), Constants.ACK_BYTES_REASON, 0, Constants.ACK_BYTES_REASON.length);
+ } else {
socketWrapper.write(isBlocking(), Constants.ACK_BYTES, 0, Constants.ACK_BYTES.length);
+ }
if (flushBuffer(true)) {
throw new IOException(sm.getString("iob.failedwrite.ack"));
}
@@ -360,6 +376,7 @@ public class Http11OutputBuffer implements OutputBuffer {
/**
* Send the response status line.
*/
+ @SuppressWarnings("deprecation")
public void sendStatus() {
// Write protocol name
write(Constants.HTTP_11_BYTES);
@@ -383,9 +400,24 @@ public class Http11OutputBuffer implements OutputBuffer {
headerBuffer.put(Constants.SP);
+ if (sendReasonPhrase) {
+ // Write message
+ String message = null;
+ if (org.apache.coyote.Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER &&
+ HttpMessages.isSafeInHttpHeader(response.getMessage())) {
+ message = response.getMessage();
+ }
+ if (message == null) {
+ write(HttpMessages.getInstance(
+ response.getLocale()).getMessage(status));
+ } else {
+ write(message);
+ }
+ } else {
// The reason phrase is optional but the space before it is not. Skip
// sending the reason phrase. Clients should ignore it (RFC 7230) and it
// just wastes bytes.
+ }
headerBuffer.put(Constants.CR).put(Constants.LF);
}
@@ -475,6 +507,35 @@ public class Http11OutputBuffer implements OutputBuffer {
/**
+ * This method will write the contents of the specified String to the
+ * output stream, without filtering. This method is meant to be used to
+ * write the response header.
+ *
+ * @param s data to be written
+ */
+ private void write(String s) {
+ if (s == null) {
+ return;
+ }
+
+ // From the Tomcat 3.3 HTTP/1.0 connector
+ int len = s.length();
+ checkLengthBeforeWrite(len);
+ for (int i = 0; i < len; i++) {
+ char c = s.charAt (i);
+ // Note: This is clearly incorrect for many strings,
+ // but is the only consistent approach within the current
+ // servlet framework. It must suffice until servlet output
+ // streams properly encode their output.
+ if (((c <= 31) && (c != 9)) || c == 127 || c > 255) {
+ c = ' ';
+ }
+ headerBuffer.put((byte) c);
+ }
+ }
+
+
+ /**
* This method will write the specified integer to the output stream. This
* method is meant to be used to write the response header.
*
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
index 5a1c7667..696e84ff 100644
--- a/java/org/apache/coyote/http11/Http11Processor.java
+++ b/java/org/apache/coyote/http11/Http11Processor.java
@@ -58,6 +58,8 @@ import org.apache.tomcat.util.net.AbstractEndpoint;
import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.net.SendfileDataBase;
+import org.apache.tomcat.util.net.SendfileKeepAliveState;
+import org.apache.tomcat.util.net.SendfileState;
import org.apache.tomcat.util.net.SocketWrapperBase;
import org.apache.tomcat.util.res.StringManager;
@@ -179,6 +181,8 @@ public class Http11Processor extends AbstractProcessor {
/**
* List of MIMES for which compression may be enabled.
+ * Note: This is not spelled correctly but can't be changed without breaking
+ * compatibility
*/
protected String[] compressableMimeTypes;
@@ -223,7 +227,7 @@ public class Http11Processor extends AbstractProcessor {
public Http11Processor(int maxHttpHeaderSize, AbstractEndpoint<?> endpoint,int maxTrailerSize,
Set<String> allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize,
- Map<String,UpgradeProtocol> httpUpgradeProtocols) {
+ Map<String,UpgradeProtocol> httpUpgradeProtocols, boolean sendReasonPhrase) {
super(endpoint);
userDataHelper = new UserDataHelper(log);
@@ -231,7 +235,7 @@ public class Http11Processor extends AbstractProcessor {
inputBuffer = new Http11InputBuffer(request, maxHttpHeaderSize);
request.setInputBuffer(inputBuffer);
- outputBuffer = new Http11OutputBuffer(response, maxHttpHeaderSize);
+ outputBuffer = new Http11OutputBuffer(response, maxHttpHeaderSize, sendReasonPhrase);
response.setOutputBuffer(outputBuffer);
// Create and add the identity filters.
@@ -316,15 +320,27 @@ public class Http11Processor extends AbstractProcessor {
/**
+ * @param compressibleMimeTypes See
+ * {@link Http11Processor#setCompressibleMimeTypes(String[])}
+ * @deprecated Use
+ * {@link Http11Processor#setCompressibleMimeTypes(String[])}
+ */
+ @Deprecated
+ public void setCompressableMimeTypes(String[] compressibleMimeTypes) {
+ setCompressibleMimeTypes(compressibleMimeTypes);
+ }
+
+
+ /**
* Set compressible mime-type list (this method is best when used with
* a large number of connectors, where it would be better to have all of
* them referenced a single array).
*
- * @param compressableMimeTypes MIME types for which compression should be
+ * @param compressibleMimeTypes MIME types for which compression should be
* enabled
*/
- public void setCompressableMimeTypes(String[] compressableMimeTypes) {
- this.compressableMimeTypes = compressableMimeTypes;
+ public void setCompressibleMimeTypes(String[] compressibleMimeTypes) {
+ this.compressableMimeTypes = compressibleMimeTypes;
}
@@ -490,7 +506,7 @@ public class Http11Processor extends AbstractProcessor {
/**
* Check if the resource could be compressed, if the client supports it.
*/
- private boolean isCompressable() {
+ private boolean isCompressible() {
// Check if content is not already gzipped
MessageBytes contentEncodingMB =
@@ -512,8 +528,7 @@ public class Http11Processor extends AbstractProcessor {
|| (contentLength > compressionMinSize)) {
// Check for compatible MIME-TYPE
if (compressableMimeTypes != null) {
- return (startsWithStringArray(compressableMimeTypes,
- response.getContentType()));
+ return (startsWithStringArray(compressableMimeTypes, response.getContentType()));
}
}
@@ -658,9 +673,10 @@ public class Http11Processor extends AbstractProcessor {
openSocket = false;
readComplete = true;
boolean keptAlive = false;
+ SendfileState sendfileState = SendfileState.DONE;
- while (!getErrorState().isError() && keepAlive && !isAsync() &&
- upgradeToken == null && !endpoint.isPaused()) {
+ while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
+ sendfileState == SendfileState.DONE && !endpoint.isPaused()) {
// Parsing the request header
try {
@@ -849,9 +865,7 @@ public class Http11Processor extends AbstractProcessor {
rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);
- if (breakKeepAliveLoop(socketWrapper)) {
- break;
- }
+ sendfileState = processSendfile(socketWrapper);
}
rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);
@@ -863,7 +877,7 @@ public class Http11Processor extends AbstractProcessor {
} else if (isUpgrade()) {
return SocketState.UPGRADING;
} else {
- if (sendfileData != null) {
+ if (sendfileState == SendfileState.PENDING) {
return SocketState.SENDFILE;
} else {
if (openSocket) {
@@ -939,7 +953,6 @@ public class Http11Processor extends AbstractProcessor {
http11 = true;
http09 = false;
contentDelimitation = false;
- sendfileData = null;
if (endpoint.isSSLEnabled()) {
request.scheme().setString("https");
@@ -1146,17 +1159,16 @@ public class Http11Processor extends AbstractProcessor {
}
// Sendfile support
- boolean sendingWithSendfile = false;
if (endpoint.getUseSendfile()) {
- sendingWithSendfile = prepareSendfile(outputFilters);
+ prepareSendfile(outputFilters);
}
// Check for compression
- boolean isCompressable = false;
+ boolean isCompressible = false;
boolean useCompression = false;
- if (entityBody && (compressionLevel > 0) && !sendingWithSendfile) {
- isCompressable = isCompressable();
- if (isCompressable) {
+ if (entityBody && (compressionLevel > 0) && sendfileData == null) {
+ isCompressible = isCompressible();
+ if (isCompressible) {
useCompression = useCompression();
}
// Change content-length to -1 to force chunking
@@ -1209,7 +1221,7 @@ public class Http11Processor extends AbstractProcessor {
headers.setValue("Content-Encoding").setString("gzip");
}
// If it might be compressed, set the Vary header
- if (isCompressable) {
+ if (isCompressible) {
// Make Proxies happy via Vary (from mod_deflate)
MessageBytes vary = headers.getValue("Vary");
if (vary == null) {
@@ -1296,10 +1308,12 @@ public class Http11Processor extends AbstractProcessor {
return connection.equals(Constants.CLOSE);
}
- private boolean prepareSendfile(OutputFilter[] outputFilters) {
+ private void prepareSendfile(OutputFilter[] outputFilters) {
String fileName = (String) request.getAttribute(
org.apache.coyote.Constants.SENDFILE_FILENAME_ATTR);
- if (fileName != null) {
+ if (fileName == null) {
+ sendfileData = null;
+ } else {
// No entity body sent here
outputBuffer.addActiveFilter(outputFilters[Constants.VOID_FILTER]);
contentDelimitation = true;
@@ -1308,9 +1322,7 @@ public class Http11Processor extends AbstractProcessor {
long end = ((Long) request.getAttribute(
org.apache.coyote.Constants.SENDFILE_FILE_END_ATTR)).longValue();
sendfileData = socketWrapper.createSendfileData(fileName, pos, end - pos);
- return true;
}
- return false;
}
/**
@@ -1591,34 +1603,39 @@ public class Http11Processor extends AbstractProcessor {
/**
- * Checks to see if the keep-alive loop should be broken, performing any
- * processing (e.g. sendfile handling) that may have an impact on whether
- * or not the keep-alive loop should be broken.
+ * Trigger sendfile processing if required.
*
- * @return true if the keep-alive loop should be broken
+ * @return The state of send file processing
*/
- private boolean breakKeepAliveLoop(SocketWrapperBase<?> socketWrapper) {
+ private SendfileState processSendfile(SocketWrapperBase<?> socketWrapper) {
openSocket = keepAlive;
+ // Done is equivalent to sendfile not being used
+ SendfileState result = SendfileState.DONE;
// Do sendfile as needed: add socket to sendfile and end
if (sendfileData != null && !getErrorState().isError()) {
- sendfileData.keepAlive = keepAlive;
- switch (socketWrapper.processSendfile(sendfileData)) {
- case DONE:
- // If sendfile is complete, no need to break keep-alive loop
- sendfileData = null;
- return false;
- case PENDING:
- return true;
+ if (keepAlive) {
+ if (available(false) == 0) {
+ sendfileData.keepAliveState = SendfileKeepAliveState.OPEN;
+ } else {
+ sendfileData.keepAliveState = SendfileKeepAliveState.PIPELINED;
+ }
+ } else {
+ sendfileData.keepAliveState = SendfileKeepAliveState.NONE;
+ }
+ result = socketWrapper.processSendfile(sendfileData);
+ switch (result) {
case ERROR:
// Write failed
if (log.isDebugEnabled()) {
log.debug(sm.getString("http11processor.sendfile.error"));
}
setErrorState(ErrorState.CLOSE_CONNECTION_NOW, null);
- return true;
+ //$FALL-THROUGH$
+ default:
+ sendfileData = null;
}
}
- return false;
+ return result;
}
diff --git a/java/org/apache/coyote/http2/ConnectionException.java b/java/org/apache/coyote/http2/ConnectionException.java
index 59573020..db754ac3 100644
--- a/java/org/apache/coyote/http2/ConnectionException.java
+++ b/java/org/apache/coyote/http2/ConnectionException.java
@@ -23,7 +23,12 @@ public class ConnectionException extends Http2Exception {
private static final long serialVersionUID = 1L;
- public ConnectionException(String msg, Http2Error error) {
+ ConnectionException(String msg, Http2Error error) {
super(msg, error);
}
+
+
+ ConnectionException(String msg, Http2Error error, Throwable cause) {
+ super(msg, error, cause);
+ }
}
diff --git a/java/org/apache/coyote/http2/HPackHuffman.java b/java/org/apache/coyote/http2/HPackHuffman.java
index 54c6a310..637e690c 100644
--- a/java/org/apache/coyote/http2/HPackHuffman.java
+++ b/java/org/apache/coyote/http2/HPackHuffman.java
@@ -379,22 +379,27 @@ public class HPackHuffman {
assert data.remaining() >= length;
int treePos = 0;
boolean eosBits = true;
+ int eosBitCount = 0;
for (int i = 0; i < length; ++i) {
byte b = data.get();
int bitPos = 7;
while (bitPos >= 0) {
int val = DECODING_TABLE[treePos];
if (((1 << bitPos) & b) == 0) {
- eosBits = false;
//bit not set, we want the lower part of the tree
if ((val & LOW_TERMINAL_BIT) == 0) {
treePos = val & LOW_MASK;
+ eosBits = false;
+ eosBitCount = 0;
} else {
target.append((char) (val & LOW_MASK));
treePos = 0;
eosBits = true;
}
} else {
+ if (eosBits) {
+ eosBitCount++;
+ }
//bit not set, we want the lower part of the tree
if ((val & HIGH_TERMINAL_BIT) == 0) {
treePos = (val >> 16) & LOW_MASK;
@@ -407,6 +412,10 @@ public class HPackHuffman {
bitPos--;
}
}
+ if (eosBitCount > 7) {
+ throw new HpackException(sm.getString(
+ "hpackhuffman.stringLiteralTooMuchPadding"));
+ }
if (!eosBits) {
throw new HpackException(sm.getString(
"hpackhuffman.huffmanEncodedHpackValueDidNotEndWithEOS"));
diff --git a/java/org/apache/coyote/http2/HpackDecoder.java b/java/org/apache/coyote/http2/HpackDecoder.java
index d75d6418..4a623425 100644
--- a/java/org/apache/coyote/http2/HpackDecoder.java
+++ b/java/org/apache/coyote/http2/HpackDecoder.java
@@ -57,9 +57,13 @@ public class HpackDecoder {
private int currentMemorySize = 0;
/**
- * The maximum allowed memory size
+ * The maximum allowed memory size set by the container.
*/
- private int maxMemorySize;
+ private int maxMemorySizeHard;
+ /**
+ * The maximum memory size currently in use. May be less than the hard limit.
+ */
+ private int maxMemorySizeSoft;
private int maxHeaderCount = Constants.DEFAULT_MAX_HEADER_COUNT;
private int maxHeaderSize = Constants.DEFAULT_MAX_HEADER_SIZE;
@@ -71,7 +75,8 @@ public class HpackDecoder {
private final StringBuilder stringBuilder = new StringBuilder();
public HpackDecoder(int maxMemorySize) {
- this.maxMemorySize = maxMemorySize;
+ this.maxMemorySizeHard = maxMemorySize;
+ this.maxMemorySizeSoft = maxMemorySize;
headerTable = new Hpack.HeaderField[DEFAULT_RING_BUFFER_SIZE];
}
@@ -156,18 +161,24 @@ public class HpackDecoder {
}
private boolean handleMaxMemorySizeChange(ByteBuffer buffer, int originalPos) throws HpackException {
+ if (headerCount != 0) {
+ throw new HpackException(sm.getString("hpackdecoder.tableSizeUpdateNotAtStart"));
+ }
buffer.position(buffer.position() - 1); //unget the byte
int size = Hpack.decodeInteger(buffer, 5);
if (size == -1) {
buffer.position(originalPos);
return false;
}
- maxMemorySize = size;
- if (currentMemorySize > maxMemorySize) {
+ if (size > maxMemorySizeHard) {
+ throw new HpackException();
+ }
+ maxMemorySizeSoft = size;
+ if (currentMemorySize > maxMemorySizeSoft) {
int newTableSlots = filledTableSlots;
int tableLength = headerTable.length;
int newSize = currentMemorySize;
- while (newSize > maxMemorySize) {
+ while (newSize > maxMemorySizeSoft) {
int clearIndex = firstSlotPosition;
firstSlotPosition++;
if (firstSlotPosition == tableLength) {
@@ -284,7 +295,7 @@ public class HpackDecoder {
}
private void addEntryToHeaderTable(Hpack.HeaderField entry) {
- if (entry.size > maxMemorySize) {
+ if (entry.size > maxMemorySizeSoft) {
//it is to big to fit, so we just completely clear the table.
while (filledTableSlots > 0) {
headerTable[firstSlotPosition] = null;
@@ -303,7 +314,7 @@ public class HpackDecoder {
int index = (firstSlotPosition + filledTableSlots) % tableLength;
headerTable[index] = entry;
int newSize = currentMemorySize + entry.size;
- while (newSize > maxMemorySize) {
+ while (newSize > maxMemorySizeSoft) {
int clearIndex = firstSlotPosition;
firstSlotPosition++;
if (firstSlotPosition == tableLength) {
@@ -339,8 +350,10 @@ public class HpackDecoder {
*
* @param name Header name
* @param value Header value
+ * @throws HpackException If a header is received that is not compliant
+ * with the HTTP/2 specification
*/
- void emitHeader(String name, String value);
+ void emitHeader(String name, String value) throws HpackException;
/**
* Are the headers pass to the recipient so far valid? The decoder needs
@@ -381,7 +394,7 @@ public class HpackDecoder {
}
- private void emitHeader(String name, String value) {
+ private void emitHeader(String name, String value) throws HpackException {
// Header names are forced to lower case
if ("cookie".equals(name)) {
// Only count the cookie header once since HTTP/2 splits it into
@@ -447,7 +460,7 @@ public class HpackDecoder {
return currentMemorySize;
}
- int getMaxMemorySize() {
- return maxMemorySize;
+ int getMaxMemorySizeSoft() {
+ return maxMemorySizeSoft;
}
}
diff --git a/java/org/apache/coyote/http2/Http2Exception.java b/java/org/apache/coyote/http2/Http2Exception.java
index 65f7502f..5abaa9c2 100644
--- a/java/org/apache/coyote/http2/Http2Exception.java
+++ b/java/org/apache/coyote/http2/Http2Exception.java
@@ -23,13 +23,19 @@ public abstract class Http2Exception extends Exception {
private final Http2Error error;
- public Http2Exception(String msg, Http2Error error) {
+ Http2Exception(String msg, Http2Error error) {
super(msg);
this.error = error;
}
- public Http2Error getError() {
+ Http2Exception(String msg, Http2Error error, Throwable cause) {
+ super(msg, cause);
+ this.error = error;
+ }
+
+
+ Http2Error getError() {
return error;
}
}
diff --git a/java/org/apache/coyote/http2/Http2Parser.java b/java/org/apache/coyote/http2/Http2Parser.java
index 54dd43b1..5fe61b9b 100644
--- a/java/org/apache/coyote/http2/Http2Parser.java
+++ b/java/org/apache/coyote/http2/Http2Parser.java
@@ -420,7 +420,7 @@ class Http2Parser {
} catch (HpackException hpe) {
throw new ConnectionException(
sm.getString("http2Parser.processFrameHeaders.decodingFailed"),
- Http2Error.COMPRESSION_ERROR);
+ Http2Error.COMPRESSION_ERROR, hpe);
}
// switches to write mode
diff --git a/java/org/apache/coyote/http2/Http2Protocol.java b/java/org/apache/coyote/http2/Http2Protocol.java
index 988b2d18..84b1d0fb 100644
--- a/java/org/apache/coyote/http2/Http2Protocol.java
+++ b/java/org/apache/coyote/http2/Http2Protocol.java
@@ -33,6 +33,7 @@ import org.apache.coyote.UpgradeProtocol;
import org.apache.coyote.UpgradeToken;
import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.coyote.http11.upgrade.UpgradeProcessorInternal;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.net.SocketWrapperBase;
public class Http2Protocol implements UpgradeProtocol {
@@ -68,7 +69,7 @@ public class Http2Protocol implements UpgradeProtocol {
private int maxHeaderSize = Constants.DEFAULT_MAX_HEADER_SIZE;
private int maxTrailerCount = Constants.DEFAULT_MAX_TRAILER_COUNT;
private int maxTrailerSize = Constants.DEFAULT_MAX_TRAILER_SIZE;
-
+ private boolean initiatePingDisabled = false;
@Override
public String getHttpUpgradeName(boolean isSSLEnabled) {
@@ -113,6 +114,7 @@ public class Http2Protocol implements UpgradeProtocol {
result.setMaxHeaderSize(getMaxHeaderSize());
result.setMaxTrailerCount(getMaxTrailerCount());
result.setMaxTrailerSize(getMaxTrailerSize());
+ result.setInitiatePingDisabled(initiatePingDisabled);
return result;
}
@@ -224,17 +226,7 @@ public class Http2Protocol implements UpgradeProtocol {
// sync is unnecessary.
List<String> copy = new ArrayList<>(allowedTrailerHeaders.size());
copy.addAll(allowedTrailerHeaders);
- StringBuilder result = new StringBuilder();
- boolean first = true;
- for (String header : copy) {
- if (first) {
- first = false;
- } else {
- result.append(',');
- }
- result.append(header);
- }
- return result.toString();
+ return StringUtils.join(copy);
}
@@ -276,4 +268,9 @@ public class Http2Protocol implements UpgradeProtocol {
public int getMaxTrailerSize() {
return maxTrailerSize;
}
+
+
+ public void setInitiatePingDisabled(boolean initiatePingDisabled) {
+ this.initiatePingDisabled = initiatePingDisabled;
+ }
}
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
index 15c44443..097dfa89 100644
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
@@ -131,7 +131,6 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
private final Map<Integer,Stream> streams = new HashMap<>();
private final AtomicInteger activeRemoteStreamCount = new AtomicInteger(0);
- private volatile int maxRemoteStreamId = 0;
// Start at -1 so the 'add 2' logic in closeIdleStreams() works
private volatile int maxActiveRemoteStreamId = -1;
private volatile int maxProcessedStreamId;
@@ -171,7 +170,6 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
Integer key = Integer.valueOf(1);
Stream stream = new Stream(key, this, coyoteRequest);
streams.put(key, stream);
- maxRemoteStreamId = 1;
maxActiveRemoteStreamId = 1;
activeRemoteStreamCount.set(1);
maxProcessedStreamId = 1;
@@ -353,7 +351,9 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
break;
}
+ if (connectionState.get() != ConnectionState.CLOSED) {
result = SocketState.UPGRADED;
+ }
break;
case OPEN_WRITE:
@@ -539,6 +539,7 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
while (state != State.COMPLETE) {
state = getHpackEncoder().encode(coyoteResponse.getMimeHeaders(), target);
target.flip();
+ if (state == State.COMPLETE || target.limit() > 0) {
ByteUtil.setThreeBytes(header, 0, target.limit());
if (first) {
first = false;
@@ -564,6 +565,12 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
handleAppInitiatedIOException(ioe);
}
}
+ if (state == State.UNDERFLOW && target.limit() == 0) {
+ target = ByteBuffer.allocate(target.capacity() * 2);
+ } else {
+ target.clear();
+ }
+ }
}
}
@@ -955,16 +962,10 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
sm.getString("upgradeHandler.stream.even", key), Http2Error.PROTOCOL_ERROR);
}
- if (streamId <= maxRemoteStreamId) {
- throw new ConnectionException(sm.getString("upgradeHandler.stream.old", key,
- Integer.valueOf(maxRemoteStreamId)), Http2Error.PROTOCOL_ERROR);
- }
-
pruneClosedStreams();
Stream result = new Stream(key, this);
streams.put(key, result);
- maxRemoteStreamId = streamId;
return result;
}
@@ -976,13 +977,17 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
Stream result = new Stream(key, this, request);
streams.put(key, result);
- maxRemoteStreamId = streamId;
return result;
}
private void close() {
connectionState.set(ConnectionState.CLOSED);
+ for (Stream stream : streams.values()) {
+ // The connection is closing. Close the associated streams as no
+ // longer required.
+ stream.receiveReset(Http2Error.CANCEL.getCode());
+ }
try {
socketWrapper.close();
} catch (IOException ioe) {
@@ -1259,6 +1264,11 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
}
+ public void setInitiatePingDisabled(boolean initiatePingDisabled) {
+ pingManager.initiateDisabled = initiatePingDisabled;
+ }
+
+
// ----------------------------------------------- Http2Parser.Input methods
@Override
@@ -1329,6 +1339,7 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
public ByteBuffer startRequestBodyFrame(int streamId, int payloadSize) throws Http2Exception {
Stream stream = getStream(streamId, true);
stream.checkState(FrameType.DATA);
+ stream.receivedData(payloadSize);
return stream.getInputByteBuffer();
}
@@ -1369,6 +1380,11 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
if (stream == null) {
stream = createRemoteStream(streamId);
}
+ if (streamId < maxActiveRemoteStreamId) {
+ throw new ConnectionException(sm.getString("upgradeHandler.stream.old",
+ Integer.valueOf(streamId), Integer.valueOf(maxActiveRemoteStreamId)),
+ Http2Error.PROTOCOL_ERROR);
+ }
stream.checkState(FrameType.HEADERS);
stream.receivedStartOfHeaders(headersEndStream);
closeIdleStreams(streamId);
@@ -1404,6 +1420,10 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
@Override
public void reprioritise(int streamId, int parentStreamId,
boolean exclusive, int weight) throws Http2Exception {
+ if (streamId == parentStreamId) {
+ throw new ConnectionException(sm.getString("upgradeHandler.dependency.invalid",
+ getConnectionId(), Integer.valueOf(streamId)), Http2Error.PROTOCOL_ERROR);
+ }
Stream stream = getStream(streamId, false);
if (stream == null) {
stream = createRemoteStream(streamId);
@@ -1497,6 +1517,7 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
log.debug(sm.getString("upgradeHandler.goaway.debug", connectionId,
Integer.toString(lastStreamId), Long.toHexString(errorCode), debugData));
}
+ close();
}
@@ -1521,6 +1542,8 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
private class PingManager {
+ protected boolean initiateDisabled = false;
+
// 10 seconds
private final long pingIntervalNano = 10000000000L;
@@ -1538,6 +1561,9 @@ public class Http2UpgradeHandler extends AbstractStream implements InternalHttpU
* @throws IOException If an I/O issue prevents the ping from being sent
*/
public void sendPing(boolean force) throws IOException {
+ if (initiateDisabled) {
+ return;
+ }
long now = System.nanoTime();
if (force || now - lastPingNanoTime > pingIntervalNano) {
lastPingNanoTime = now;
diff --git a/java/org/apache/coyote/http2/LocalStrings.properties b/java/org/apache/coyote/http2/LocalStrings.properties
index d1ad5c44..0728051e 100644
--- a/java/org/apache/coyote/http2/LocalStrings.properties
+++ b/java/org/apache/coyote/http2/LocalStrings.properties
@@ -35,10 +35,12 @@ hpack.integerEncodedOverTooManyOctets=HPACK variable length integer encoded over
hpack.invalidCharacter=The Unicode character [{0}] at code point [{1}] cannot be encoded as it is outside the permitted range of 0 to 255.
hpackdecoder.zeroNotValidHeaderTableIndex=Zero is not a valid header table index
+hpackdecoder.tableSizeUpdateNotAtStart=Any table size update must be sent at the start of a header block
hpackEncoder.encodeHeader=Encoding header [{0}] with value [{1}]
hpackhuffman.huffmanEncodedHpackValueDidNotEndWithEOS=Huffman encoded value in HPACK headers did not end with EOS padding
+hpackhuffman.stringLiteralTooMuchPadding=More than 7 bits of EOS padding were provided at the end of an Huffman encoded string literal
http2Parser.headerLimitCount=Connection [{0}], Stream [{1}], Too many headers
http2Parser.headerLimitSize=Connection [{0}], Stream [{1}], Total header size too big
@@ -71,7 +73,14 @@ http2Parser.swallow.debug=Connection [{0}], Stream [{1}], Swallowed [{2}] bytes
pingManager.roundTripTime=Connection [{0}] Round trip time measured as [{1}]ns
stream.closed=Connection [{0}], Stream [{1}], Unable to write to stream once it has been closed
+stream.header.case=Connection [{0}], Stream [{1}], HTTP header name [{2}] must be in lower case
+stream.header.connection=Connection [{0}], Stream [{1}], HTTP header [connection] is not permitted in an HTTP/2 request
+stream.header.contentLength=Connection [{0}], Stream [{1}], The content length header value [{2}] does not agree with the size of the data received [{3}]
stream.header.debug=Connection [{0}], Stream [{1}], HTTP header [{2}], Value [{3}]
+stream.header.duplicate=Connection [{0}], Stream [{1}], received multiple [{3}] headers
+stream.header.noPath=Connection [{0}], Stream [{1}], The [:path] pseudo header was empty
+stream.header.required=Connection [{0}], Stream [{1}], One or more required headers was missing
+stream.header.te=Connection [{0}], Stream [{1}], HTTP header [te] is not permitted to have the value [{2}] in an HTTP/2 request
stream.header.unexpectedPseudoHeader=Connection [{0}], Stream [{1}], Pseudo header [{2}] received after a regular header
stream.header.unknownPseudoHeader=Connection [{0}], Stream [{1}], Unknown pseudo header [{2}] received
stream.notWritable=Connection [{0}], Stream [{1}], This stream is not writable
@@ -101,6 +110,7 @@ upgradeHandler.allocate.debug=Connection [{0}], Stream [{1}], allocated [{2}] by
upgradeHandler.allocate.left=Connection [{0}], Stream [{1}], [{2}] bytes unallocated - trying to allocate to children
upgradeHandler.allocate.recipient=Connection [{0}], Stream [{1}], potential recipient [{2}] with weight [{3}]
upgradeHandler.connectionError=Connection error
+upgradeHandler.dependency.invalid=Connection [{0}], Stream [{1}], Streams may not depend on themselves
upgradeHandler.goaway.debug=Connection [{0}], Goaway, Last stream [{1}], Error code [{2}], Debug data [{3}]
upgradeHandler.init=Connection [{0}], State [{1}]
upgradeHandler.initialWindowSize.invalid=Connection [{0}], Illegal value of [{1}] ignored for initial window size
diff --git a/java/org/apache/coyote/http2/Stream.java b/java/org/apache/coyote/http2/Stream.java
index e18e4d04..2e3ff88e 100644
--- a/java/org/apache/coyote/http2/Stream.java
+++ b/java/org/apache/coyote/http2/Stream.java
@@ -22,6 +22,7 @@ import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Iterator;
+import java.util.Locale;
import org.apache.coyote.ActionCode;
import org.apache.coyote.CloseNowException;
@@ -53,6 +54,7 @@ public class Stream extends AbstractStream implements HeaderEmitter {
}
private volatile int weight = Constants.DEFAULT_WEIGHT;
+ private volatile long contentLengthReceived = 0;
private final Http2UpgradeHandler handler;
private final StreamStateMachine state;
@@ -233,12 +235,30 @@ public class Stream extends AbstractStream implements HeaderEmitter {
@Override
- public void emitHeader(String name, String value) {
+ public final void emitHeader(String name, String value) throws HpackException {
if (log.isDebugEnabled()) {
log.debug(sm.getString("stream.header.debug", getConnectionId(), getIdentifier(),
name, value));
}
+ // Header names must be lower case
+ if (!name.toLowerCase(Locale.US).equals(name)) {
+ throw new HpackException(sm.getString("stream.header.case",
+ getConnectionId(), getIdentifier(), name));
+ }
+
+ if ("connection".equals(name)) {
+ throw new HpackException(sm.getString("stream.header.connection",
+ getConnectionId(), getIdentifier()));
+ }
+
+ if ("te".equals(name)) {
+ if (!"trailers".equals(value)) {
+ throw new HpackException(sm.getString("stream.header.te",
+ getConnectionId(), getIdentifier(), value));
+ }
+ }
+
if (headerStateErrorMsg != null) {
// Don't bother processing the header since the stream is going to
// be reset anyway
@@ -260,28 +280,49 @@ public class Stream extends AbstractStream implements HeaderEmitter {
switch(name) {
case ":method": {
+ if (coyoteRequest.method().isNull()) {
coyoteRequest.method().setString(value);
+ } else {
+ throw new HpackException(sm.getString("stream.header.duplicate",
+ getConnectionId(), getIdentifier(), ":method" ));
+ }
break;
}
case ":scheme": {
+ if (coyoteRequest.scheme().isNull()) {
coyoteRequest.scheme().setString(value);
+ } else {
+ throw new HpackException(sm.getString("stream.header.duplicate",
+ getConnectionId(), getIdentifier(), ":scheme" ));
+ }
break;
}
case ":path": {
+ if (!coyoteRequest.requestURI().isNull()) {
+ throw new HpackException(sm.getString("stream.header.duplicate",
+ getConnectionId(), getIdentifier(), ":path" ));
+ }
+ if (value.length() == 0) {
+ throw new HpackException(sm.getString("stream.header.noPath",
+ getConnectionId(), getIdentifier()));
+ }
int queryStart = value.indexOf('?');
if (queryStart == -1) {
coyoteRequest.requestURI().setString(value);
- coyoteRequest.decodedURI().setString(coyoteRequest.getURLDecoder().convert(value, false));
+ coyoteRequest.decodedURI().setString(
+ coyoteRequest.getURLDecoder().convert(value, false));
} else {
String uri = value.substring(0, queryStart);
String query = value.substring(queryStart + 1);
coyoteRequest.requestURI().setString(uri);
- coyoteRequest.decodedURI().setString(coyoteRequest.getURLDecoder().convert(uri, false));
+ coyoteRequest.decodedURI().setString(
+ coyoteRequest.getURLDecoder().convert(uri, false));
coyoteRequest.queryString().setString(query);
}
break;
}
case ":authority": {
+ if (coyoteRequest.serverName().isNull()) {
int i = value.lastIndexOf(':');
if (i > -1) {
coyoteRequest.serverName().setString(value.substring(0, i));
@@ -289,6 +330,10 @@ public class Stream extends AbstractStream implements HeaderEmitter {
} else {
coyoteRequest.serverName().setString(value);
}
+ } else {
+ throw new HpackException(sm.getString("stream.header.duplicate",
+ getConnectionId(), getIdentifier(), ":authority" ));
+ }
break;
}
case "cookie": {
@@ -331,7 +376,12 @@ public class Stream extends AbstractStream implements HeaderEmitter {
}
- final boolean receivedEndOfHeaders() {
+ final boolean receivedEndOfHeaders() throws ConnectionException {
+ if (coyoteRequest.method().isNull() || coyoteRequest.scheme().isNull() ||
+ coyoteRequest.requestURI().isNull()) {
+ throw new ConnectionException(sm.getString("stream.header.required",
+ getConnectionId(), getIdentifier()), Http2Error.PROTOCOL_ERROR);
+ }
// Cookie headers need to be concatenated into a single header
// See RFC 7540 8.1.2.5
// Can only do this once the headers are fully received
@@ -411,11 +461,28 @@ public class Stream extends AbstractStream implements HeaderEmitter {
}
- void receivedEndOfStream() {
- synchronized (inputBuffer) {
- inputBuffer.notifyAll();
+ final void receivedData(int payloadSize) throws ConnectionException {
+ contentLengthReceived += payloadSize;
+ long contentLengthHeader = coyoteRequest.getContentLengthLong();
+ if (contentLengthHeader > -1 && contentLengthReceived > contentLengthHeader) {
+ throw new ConnectionException(sm.getString("stream.header.contentLength",
+ getConnectionId(), getIdentifier(), Long.valueOf(contentLengthHeader),
+ Long.valueOf(contentLengthReceived)), Http2Error.PROTOCOL_ERROR);
+ }
+ }
+
+
+ final void receivedEndOfStream() throws ConnectionException {
+ long contentLengthHeader = coyoteRequest.getContentLengthLong();
+ if (contentLengthHeader > -1 && contentLengthReceived != contentLengthHeader) {
+ throw new ConnectionException(sm.getString("stream.header.contentLength",
+ getConnectionId(), getIdentifier(), Long.valueOf(contentLengthHeader),
+ Long.valueOf(contentLengthReceived)), Http2Error.PROTOCOL_ERROR);
}
state.receivedEndOfStream();
+ if (inputBuffer != null) {
+ inputBuffer.notifyEof();
+ }
}
@@ -749,8 +816,8 @@ public class Stream extends AbstractStream implements HeaderEmitter {
// Ensure that only one thread accesses inBuffer at a time
synchronized (inBuffer) {
- boolean canRead = isActive() && !isInputFinished();
- while (inBuffer.position() == 0 && canRead) {
+ boolean canRead = false;
+ while (inBuffer.position() == 0 && (canRead = isActive() && !isInputFinished())) {
// Need to block until some data is written
try {
if (log.isDebugEnabled()) {
@@ -806,8 +873,8 @@ public class Stream extends AbstractStream implements HeaderEmitter {
// Ensure that only one thread accesses inBuffer at a time
synchronized (inBuffer) {
- boolean canRead = isActive() && !isInputFinished();
- while (inBuffer.position() == 0 && canRead) {
+ boolean canRead = false;
+ while (inBuffer.position() == 0 && (canRead = isActive() && !isInputFinished())) {
// Need to block until some data is written
try {
if (log.isDebugEnabled()) {
@@ -937,5 +1004,13 @@ public class Stream extends AbstractStream implements HeaderEmitter {
}
}
}
+
+ private final void notifyEof() {
+ if (inBuffer != null) {
+ synchronized (inBuffer) {
+ inBuffer.notifyAll();
+ }
+ }
+ }
}
}
diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java
index 3ccfd8a6..344ad61c 100644
--- a/java/org/apache/coyote/http2/StreamProcessor.java
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
@@ -134,7 +134,11 @@ class StreamProcessor extends AbstractProcessor {
@Override
protected final void setRequestBody(ByteChunk body) {
stream.getInputBuffer().insertReplayedBody(body);
+ try {
stream.receivedEndOfStream();
+ } catch (ConnectionException e) {
+ // Exception will not be thrown in this case
+ }
}
diff --git a/java/org/apache/el/Messages.properties b/java/org/apache/el/Messages.properties
index f3d87089..e7b58c09 100644
--- a/java/org/apache/el/Messages.properties
+++ b/java/org/apache/el/Messages.properties
@@ -29,7 +29,7 @@ error.value.literal.write=ValueExpression is a literal and not writable: {0}
# ExpressionFactoryImpl
error.null=Expression cannot be null
-error.mixed=Expression cannot contain both '#{..}' and '${..}' : {0}
+error.mixed=Expression cannot contain both '#{...}' and '${...}' : {0}
error.method=Not a valid MethodExpression : {0}
error.method.nullParms=Parameter types cannot be null
error.value.expectedType=Expected type cannot be null
diff --git a/java/org/apache/el/Messages_es.properties b/java/org/apache/el/Messages_es.properties
index 3f42d0f7..7a648f98 100644
--- a/java/org/apache/el/Messages_es.properties
+++ b/java/org/apache/el/Messages_es.properties
@@ -21,7 +21,7 @@ error.resolver.unhandled = ELResolver no manej\u00F3 el tipo\: {0} con propiedad
error.resolver.unhandled.null = ELResolver no puede manejar un Objeto base nulo con identificador de ''{0}''
error.value.literal.write = ValueExpression es un literal y no un grabable\: {0}
error.null = La expresi\u00F3n no puede ser nula
-error.mixed = La expresi\u00F3n no puede contenera la vez '\#{..}' y '${..}' \: {0}
+error.mixed = La expresi\u00F3n no puede contenera la vez ''\#{..}'' y ''${..}'' \: {0}
error.method = No es una MethodExpression v\u00E1lida\: {0}
error.method.nullParms = Los tipos de par\u00E1metro no pueden ser nulo
error.value.expectedType = El tipo esperado no puede ser nulo
diff --git a/java/org/apache/el/util/ReflectionUtil.java b/java/org/apache/el/util/ReflectionUtil.java
index 0b0ff5b5..904aa8be 100644
--- a/java/org/apache/el/util/ReflectionUtil.java
+++ b/java/org/apache/el/util/ReflectionUtil.java
@@ -184,7 +184,7 @@ public class ReflectionUtil {
if (isAssignableFrom(paramTypes[j], varType)) {
assignableMatch++;
} else {
- if (paramValues == null) {
+ if (paramValues == null || j >= paramValues.length) {
noMatch = true;
break;
} else {
@@ -203,7 +203,7 @@ public class ReflectionUtil {
} else if (isAssignableFrom(paramTypes[i], mParamTypes[i])) {
assignableMatch++;
} else {
- if (paramValues == null) {
+ if (paramValues == null || i >= paramValues.length) {
noMatch = true;
break;
} else {
diff --git a/java/org/apache/jasper/compiler/Generator.java b/java/org/apache/jasper/compiler/Generator.java
index 6869fdf6..8112ebee 100644
--- a/java/org/apache/jasper/compiler/Generator.java
+++ b/java/org/apache/jasper/compiler/Generator.java
@@ -2640,9 +2640,15 @@ class Generator {
declareScriptingVars(n, VariableInfo.AT_BEGIN);
saveScriptingVars(n, VariableInfo.AT_BEGIN);
+ // Declare AT_END scripting variables
+ declareScriptingVars(n, VariableInfo.AT_END);
+
String tagHandlerClassName = tagHandlerClass.getCanonicalName();
writeNewInstance(tagHandlerVar, tagHandlerClassName);
+ out.printil("try {");
+ out.pushIndent();
+
generateSetters(n, tagHandlerVar, handlerInfo, true);
// Set the body
@@ -2682,13 +2688,19 @@ class Generator {
// Synchronize AT_BEGIN scripting variables
syncScriptingVars(n, VariableInfo.AT_BEGIN);
- // Declare and synchronize AT_END scripting variables
- declareScriptingVars(n, VariableInfo.AT_END);
+ // Synchronize AT_END scripting variables
syncScriptingVars(n, VariableInfo.AT_END);
+ out.popIndent();
+ out.printil("} finally {");
+ out.pushIndent();
+
// Resource injection
writeDestroyInstance(tagHandlerVar);
+ out.popIndent();
+ out.printil("}");
+
n.setEndJavaLine(out.getJavaLine());
}
diff --git a/java/org/apache/jasper/resources/LocalStrings.properties b/java/org/apache/jasper/resources/LocalStrings.properties
index d5789cb5..14c635e0 100644
--- a/java/org/apache/jasper/resources/LocalStrings.properties
+++ b/java/org/apache/jasper/resources/LocalStrings.properties
@@ -26,31 +26,31 @@ jsp.message.parent_class_loader_is=Parent class loader is: {0}
jsp.message.dont.modify.servlets=IMPORTANT: Do not modify the generated servlets
jsp.error.unavailable=JSP has been marked unavailable
jsp.error.usebean.duplicate=useBean: Duplicate bean name: {0}
-jsp.error.invalid.scope=Illegal value of \'scope\' attribute: {0} (must be one of \"page\", \"request\", \"session\", or \"application\")
+jsp.error.invalid.scope=Illegal value of ''scope'' attribute: {0} (must be one of \"page\", \"request\", \"session\", or \"application\")
jsp.error.classname=Can't determine classname from .class file
jsp.error.outputfolder=No output folder
jsp.error.data.file.write=Error while writing data file
-jsp.error.page.conflict.contenttype=Page directive: illegal to have multiple occurrences of 'contentType' with different values (old: {0}, new: {1})
-jsp.error.page.conflict.session=Page directive: illegal to have multiple occurrences of 'session' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.contenttype=Page directive: illegal to have multiple occurrences of ''contentType'' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.session=Page directive: illegal to have multiple occurrences of ''session'' with different values (old: {0}, new: {1})
jsp.error.page.invalid.session=Page directive: invalid value for session
-jsp.error.page.conflict.buffer=Page directive: illegal to have multiple occurrences of 'buffer' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.buffer=Page directive: illegal to have multiple occurrences of ''buffer'' with different values (old: {0}, new: {1})
jsp.error.page.invalid.buffer=Page directive: invalid value for buffer
-jsp.error.page.conflict.autoflush=Page directive: illegal to have multiple occurrences of 'autoFlush' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.autoflush=Page directive: illegal to have multiple occurrences of ''autoFlush'' with different values (old: {0}, new: {1})
jsp.error.page.invalid.import=Page directive: invalid value for import
-jsp.error.page.conflict.isthreadsafe=Page directive: illegal to have multiple occurrences of 'isThreadSafe' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.isthreadsafe=Page directive: illegal to have multiple occurrences of ''isThreadSafe'' with different values (old: {0}, new: {1})
jsp.error.page.invalid.isthreadsafe=Page directive: invalid value for isThreadSafe
-jsp.error.page.conflict.info=Page directive: illegal to have multiple occurrences of 'info' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.info=Page directive: illegal to have multiple occurrences of ''info'' with different values (old: {0}, new: {1})
jsp.error.page.invalid.info=Page directive: invalid value for info
-jsp.error.page.conflict.iserrorpage=Page directive: illegal to have multiple occurrences of 'isErrorPage' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.iserrorpage=Page directive: illegal to have multiple occurrences of ''isErrorPage'' with different values (old: {0}, new: {1})
jsp.error.page.invalid.iserrorpage=Page directive: invalid value for isErrorPage
-jsp.error.page.conflict.errorpage=Page directive: illegal to have multiple occurrences of 'errorPage' with different values (old: {0}, new: {1})
-jsp.error.page.conflict.language=Page directive: illegal to have multiple occurrences of 'language' with different values (old: {0}, new: {1})
-jsp.error.tag.conflict.language=Tag directive: illegal to have multiple occurrences of 'language' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.errorpage=Page directive: illegal to have multiple occurrences of ''errorPage'' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.language=Page directive: illegal to have multiple occurrences of ''language'' with different values (old: {0}, new: {1})
+jsp.error.tag.conflict.language=Tag directive: illegal to have multiple occurrences of ''language'' with different values (old: {0}, new: {1})
jsp.error.page.language.nonjava=Page directive: invalid language attribute
jsp.error.tag.language.nonjava=Tag directive: invalid language attribute
-jsp.error.page.conflict.extends=Page directive: illegal to have multiple occurrences of 'extends' with different values (old: {0}, new: {1})
-jsp.error.page.conflict.iselignored=Page directive: illegal to have multiple occurrences of 'isELIgnored' with different values (old: {0}, new: {1})
-jsp.error.tag.conflict.iselignored=Tag directive: illegal to have multiple occurrences of 'isELIgnored' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.extends=Page directive: illegal to have multiple occurrences of ''extends'' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.iselignored=Page directive: illegal to have multiple occurrences of ''isELIgnored'' with different values (old: {0}, new: {1})
+jsp.error.tag.conflict.iselignored=Tag directive: illegal to have multiple occurrences of ''isELIgnored'' with different values (old: {0}, new: {1})
jsp.error.page.invalid.iselignored=Page directive: invalid value for isELIgnored
jsp.error.tag.invalid.iselignored=Tag directive: invalid value for isELIgnored
jsp.error.page.multi.pageencoding=Page directive must not have multiple occurrences of pageencoding
@@ -131,7 +131,7 @@ jsp.warning.unknown.element.in.tagfile=Unknown element ({0}) in tag-file
jsp.warning.unknown.element.in.attribute=Unknown element ({0}) in attribute
jsp.warning.unknown.element.in.variable=Unknown element ({0}) in variable
jsp.warning.unknown.element.in.validator=Unknown element ({0}) in validator
-jsp.warning.unknown.element.in.initParam=Unknown element ({0}) in validator's init-param
+jsp.warning.unknown.element.in.initParam=Unknown element ({0}) in validator''s init-param
jsp.warning.unknown.element.in.function=Unknown element ({0}) in function
jsp.error.teiclass.instantiation=Failed to load or instantiate TagExtraInfo class: {0}
jsp.error.non_null_tei_and_var_subelems=Tag {0} has one or more variable subelements and a TagExtraInfo class that returns one or more VariableInfo
@@ -238,7 +238,7 @@ jsp.error.taglibDirective.both_uri_and_tagdir=Both \'uri\' and \'tagdir\' attrib
jsp.error.invalid.tagdir=Tag file directory {0} does not start with \"/WEB-INF/tags\"
#jspx.error.templateDataNotInJspCdata=Validation Error: Element <{0}> cannot have template data. Template data must be encapsulated within a <jsp:cdata> element. [JSP1.2 PFD section 5.1.9]\nTemplate data in error: {1}
#Error while processing taglib jar file {0}: {1}
-jsp.error.needAlternateJavaEncoding=Default java encoding {0} is invalid on your java platform. An alternate can be specified via the 'javaEncoding' parameter of JspServlet.
+jsp.error.needAlternateJavaEncoding=Default java encoding {0} is invalid on your java platform. An alternate can be specified via the ''javaEncoding'' parameter of JspServlet.
#Error when compiling, used for jsp line number error messages
jsp.error.single.line.number=An error occurred at line: {0} in the jsp file: {1}
jsp.error.java.line.number=An error occurred at line: [{0}] in the generated java file: [{1}]
@@ -249,7 +249,7 @@ jsp.error.jspbody.emptybody.only=The {0} tag can only have jsp:attribute in its
jsp.error.no.scriptlets=Scripting elements ( <%!, <jsp:declaration, <%=, <jsp:expression, <%, <jsp:scriptlet ) are disallowed here.
jsp.error.tld.fn.invalid.signature=Invalid syntax for function signature in TLD. Tag Library: {0}, Function: {1}
jsp.error.tld.fn.duplicate.name=Duplicate function name {0} in tag library {1}
-jsp.error.tld.fn.invalid.signature.parenexpected=Invalid syntax for function signature in TLD. Parenthesis '(' expected. Tag Library: {0}, Function: {1}.
+jsp.error.tld.fn.invalid.signature.parenexpected=Invalid syntax for function signature in TLD. Parenthesis ''('' expected. Tag Library: {0}, Function: {1}.
jsp.error.tld.mandatory.element.missing=Mandatory TLD element {0} missing or empty in TLD {1}
jsp.error.dynamic.attributes.not.implemented=The {0} tag declares that it accepts dynamic attributes but does not implement the required interface
jsp.error.attribute.noequal=equal symbol expected
@@ -364,13 +364,13 @@ jsp.error.el.template.deferred=#{...} is not allowed in template text
jsp.error.el.parse={0} : {1}
jsp.error.page.invalid.deferredsyntaxallowedasliteral=Page directive: invalid value for deferredSyntaxAllowedAsLiteral
jsp.error.tag.invalid.deferredsyntaxallowedasliteral=Tag directive: invalid value for deferredSyntaxAllowedAsLiteral
-jsp.error.page.conflict.deferredsyntaxallowedasliteral=Page directive: illegal to have multiple occurrences of 'deferredSyntaxAllowedAsLiteral' with different values (old: {0}, new: {1})
-jsp.error.tag.conflict.deferredsyntaxallowedasliteral=Tag directive: illegal to have multiple occurrences of 'deferredSyntaxAllowedAsLiteral' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.deferredsyntaxallowedasliteral=Page directive: illegal to have multiple occurrences of ''deferredSyntaxAllowedAsLiteral'' with different values (old: {0}, new: {1})
+jsp.error.tag.conflict.deferredsyntaxallowedasliteral=Tag directive: illegal to have multiple occurrences of ''deferredSyntaxAllowedAsLiteral'' with different values (old: {0}, new: {1})
jsp.error.page.invalid.trimdirectivewhitespaces=Page directive: invalid value for trimDirectiveWhitespaces
jsp.error.tag.invalid.trimdirectivewhitespaces=Tag directive: invalid value for trimDirectiveWhitespaces
-jsp.error.page.conflict.trimdirectivewhitespaces=Page directive: illegal to have multiple occurrences of 'trimDirectiveWhitespaces' with different values (old: {0}, new: {1})
-jsp.error.tag.conflict.trimdirectivewhitespaces=Tag directive: illegal to have multiple occurrences of 'trimDirectiveWhitespaces' with different values (old: {0}, new: {1})
+jsp.error.page.conflict.trimdirectivewhitespaces=Page directive: illegal to have multiple occurrences of ''trimDirectiveWhitespaces'' with different values (old: {0}, new: {1})
+jsp.error.tag.conflict.trimdirectivewhitespaces=Tag directive: illegal to have multiple occurrences of ''trimDirectiveWhitespaces'' with different values (old: {0}, new: {1})
# JSP Servlet
jsp.error.servlet.invalid.method=JSPs only permit GET POST or HEAD
diff --git a/java/org/apache/jasper/resources/LocalStrings_es.properties b/java/org/apache/jasper/resources/LocalStrings_es.properties
index baac6470..e02e82b9 100644
--- a/java/org/apache/jasper/resources/LocalStrings_es.properties
+++ b/java/org/apache/jasper/resources/LocalStrings_es.properties
@@ -25,30 +25,30 @@ jsp.message.parent_class_loader_is = El cargador de clases es\: {0}
jsp.message.dont.modify.servlets = IMPORTANTE\: No modifique los servlets generados
jsp.error.unavailable = JSP ha sido marcado como no disponible
jsp.error.usebean.duplicate = useBean\: Nombre de bean duplicado\: {0}
-jsp.error.invalid.scope = Valor ilegal de atributo 'scope'\: {0} (debe de ser uno de "page", "request", "session", o "application")
+jsp.error.invalid.scope = Valor ilegal de atributo ''scope''\: {0} (debe de ser uno de "page", "request", "session", o "application")
jsp.error.classname = No pude determinar el nombre de clase desde el fichero .class
jsp.error.outputfolder = no hay carpeta de salida
jsp.error.data.file.write = Error mientras escrib\u00EDa el archivo de datos
-jsp.error.page.conflict.contenttype = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'contentType' con valores distintos (viejo\: {0}, nuevo\: {1})
-jsp.error.page.conflict.session = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'session' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.contenttype = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''contentType'' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.session = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''session'' con valores distintos (viejo\: {0}, nuevo\: {1})
jsp.error.page.invalid.session = Directiva Page\: valor incorrecto para session
-jsp.error.page.conflict.buffer = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'buffer'con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.buffer = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''buffer'' con valores distintos (viejo\: {0}, nuevo\: {1})
jsp.error.page.invalid.buffer = Directiva Page\: valor incorrecto para b\u00FAfer
-jsp.error.page.conflict.autoflush = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'autoFlush' con valores distintos (viejo\: {0}, nuevo\: {1})
-jsp.error.page.conflict.isthreadsafe = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'isThreadSafe' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.autoflush = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''autoFlush'' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.isthreadsafe = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''isThreadSafe'' con valores distintos (viejo\: {0}, nuevo\: {1})
jsp.error.page.invalid.isthreadsafe = \=Directiva Page\: valor incorrecto para isThreadSafe
-jsp.error.page.conflict.info = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'info' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.info = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''info'' con valores distintos (viejo\: {0}, nuevo\: {1})
jsp.error.page.invalid.info = \=Directiva Page\: valor incorrecto para info
-jsp.error.page.conflict.iserrorpage = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'isErrorPage' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.iserrorpage = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''isErrorPage'' con valores distintos (viejo\: {0}, nuevo\: {1})
jsp.error.page.invalid.iserrorpage = \=Directiva Page\: valor incorrecto para isErrorPage
-jsp.error.page.conflict.errorpage = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'errorPage' con valores distintos (viejo\: {0}, nuevo\: {1})
-jsp.error.page.conflict.language = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'language' con valores distintos (viejo\: {0}, nuevo\: {1})
-jsp.error.tag.conflict.language = Directiva Tag\: es ilegal tener m\u00FAltiples ocurrencias de 'language' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.errorpage = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''errorPage'' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.language = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''language'' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.tag.conflict.language = Directiva Tag\: es ilegal tener m\u00FAltiples ocurrencias de ''language'' con valores distintos (viejo\: {0}, nuevo\: {1})
jsp.error.page.language.nonjava = Directiva Page\: atributo language incorrecto
jsp.error.tag.language.nonjava = Directiva Tag\: atributo language incorrecto
-jsp.error.page.conflict.extends = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'extends' con valores distintos (viejo\: {0}, nuevo\: {1})
-jsp.error.page.conflict.iselignored = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de 'isELIgnored' con valores distintos (viejo\: {0}, nuevo\: {1})
-jsp.error.tag.conflict.iselignored = Directiva Tag\: es ilegal tener m\u00FAltiples ocurrencias de 'isELIgnored' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.extends = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''extends'' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.iselignored = Directiva Page\: es ilegal tener m\u00FAltiples ocurrencias de ''isELIgnored'' con valores distintos (viejo\: {0}, nuevo\: {1})
+jsp.error.tag.conflict.iselignored = Directiva Tag\: es ilegal tener m\u00FAltiples ocurrencias de ''isELIgnored'' con valores distintos (viejo\: {0}, nuevo\: {1})
jsp.error.page.invalid.iselignored = Directiva Page\: valor inv\u00E1lido para isELIgnored
jsp.error.tag.invalid.iselignored = Directiva Tag\: valor incorrecto para isELIgnored
jsp.error.page.multi.pageencoding = La directiva Page no debe de tener m\u00FAltiples ocurrencias de pageencoding
@@ -229,7 +229,7 @@ jsp.error.taglibDirective.both_uri_and_tagdir = Se han especificado ambos atribu
jsp.error.invalid.tagdir = El directorio de archivo Tag {0} no comienza con "/WEB-INF/tags"
#jspx.error.templateDataNotInJspCdata=Validation Error: Element <{0}> cannot have template data. Template data must be encapsulated within a <jsp:cdata> element. [JSP1.2 PFD section 5.1.9]\nTemplate data in error: {1}
#Error while processing taglib jar file {0}: {1}
-jsp.error.needAlternateJavaEncoding = La codificaci\u00F3n java por defecto {0} es incorrecta en tu plataforma java. Se puede especificar una alternativa v\u00EDa par\u00E1metro 'javaEncoding' de JspServlet.
+jsp.error.needAlternateJavaEncoding = La codificaci\u00F3n java por defecto {0} es incorrecta en tu plataforma java. Se puede especificar una alternativa v\u00EDa par\u00E1metro ''javaEncoding'' de JspServlet.
#Error when compiling, used for jsp line number error messages
jsp.error.single.line.number = Ha tenido lugar un error en la l\u00EDnea\: {0} en el archivo jsp\: {1}
jsp.error.java.line.number = Ha tenido lugar un error en la l\u00EDnea\: [{0}] en el fichero java generado: [{1}]
@@ -240,7 +240,7 @@ jsp.error.jspbody.emptybody.only = El tag {0} s\u00F3lo puede tener jsp\:attribu
jsp.error.no.scriptlets = Los elementos de Scripting (<%\!, <jsp\:declaration, <%\=, <jsp\:expression, <%, <jsp\:scriptlet ) no est\u00E1n permitidos aqu\u00ED.
jsp.error.tld.fn.invalid.signature = Sint\u00E1xis incorrecta para firma de funci\u00F3n en TLD. Biblioteca de Tag\: {0}, Funci\u00F3n\: {1}
jsp.error.tld.fn.duplicate.name = Nombre duplicado de funci\u00F3n {0} en biblioteca de tag {1}
-jsp.error.tld.fn.invalid.signature.parenexpected = Sint\u00E1xis incorrecta para firma de funci\u00F3n en TLD. Se esperaba Par\u00E9ntesis '('. Biblioteca de Tag\: {0}, Funci\u00F3n\: {1}.
+jsp.error.tld.fn.invalid.signature.parenexpected = Sint\u00E1xis incorrecta para firma de funci\u00F3n en TLD. Se esperaba Par\u00E9ntesis ''(''. Biblioteca de Tag\: {0}, Funci\u00F3n\: {1}.
jsp.error.tld.mandatory.element.missing = Falta o est\u00E1 vac\u00EDo elemento TLD obligatorio\: {0}
jsp.error.dynamic.attributes.not.implemented = El tag {0} declara que acepta atributos din\u00E1micos pero no implementa la interfaz requerida
jsp.error.attribute.noequal = se esperaba s\u00EDmbolo igual
@@ -349,12 +349,12 @@ jsp.error.el.template.deferred = \#{..} no est\u00E1 permitido en texto de plant
jsp.error.el.parse = {0} \: {1}
jsp.error.page.invalid.deferredsyntaxallowedasliteral = Directiva de p\u00E1gina\: valor inv\u00E1lido para deferredSyntaxAllowedAsLiteral
jsp.error.tag.invalid.deferredsyntaxallowedasliteral = Directiva de marca\: valor inv\u00E1lido para deferredSyntaxAllowedAsLiteral
-jsp.error.page.conflict.deferredsyntaxallowedasliteral = Directiva de p\u00E1gina\: es ilegal tener m\u00FAltiples ocurrencias de 'deferredSyntaxAllowedAsLiteral' con diferentes valores (viejo\: {0}, nuevo\: {1})
-jsp.error.tag.conflict.deferredsyntaxallowedasliteral = Directiva de marca\: es ilegal tener m\u00FAltiples ocurrencias de 'deferredSyntaxAllowedAsLiteral' con diferentes valores (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.deferredsyntaxallowedasliteral = Directiva de p\u00E1gina\: es ilegal tener m\u00FAltiples ocurrencias de ''deferredSyntaxAllowedAsLiteral'' con diferentes valores (viejo\: {0}, nuevo\: {1})
+jsp.error.tag.conflict.deferredsyntaxallowedasliteral = Directiva de marca\: es ilegal tener m\u00FAltiples ocurrencias de ''deferredSyntaxAllowedAsLiteral'' con diferentes valores (viejo\: {0}, nuevo\: {1})
jsp.error.page.invalid.trimdirectivewhitespaces = Directiva de p\u00E1gina\: valor inv\u00E1lido para trimDirectiveWhitespaces
jsp.error.tag.invalid.trimdirectivewhitespaces = Directiva de marca\: valor inv\u00E1lido para trimDirectiveWhitespaces
-jsp.error.page.conflict.trimdirectivewhitespaces = Directiva de p\u00E1gina\: es ilegal tener m\u00FAltiples ocurrencias de 'trimDirectivewhitespaces' con diferentes valores (viejo\: {0}, nuevo\: {1})
-jsp.error.tag.conflict.trimdirectivewhitespaces = Directiva de marca\: es ilegal tener m\u00FAltiples ocurrencias de 'trimDirectivewhitespaces' con diferentes valores (viejo\: {0}, nuevo\: {1})
+jsp.error.page.conflict.trimdirectivewhitespaces = Directiva de p\u00E1gina\: es ilegal tener m\u00FAltiples ocurrencias de ''trimDirectivewhitespaces'' con diferentes valores (viejo\: {0}, nuevo\: {1})
+jsp.error.tag.conflict.trimdirectivewhitespaces = Directiva de marca\: es ilegal tener m\u00FAltiples ocurrencias de ''trimDirectivewhitespaces'' con diferentes valores (viejo\: {0}, nuevo\: {1})
jsp.warning.noJarScanner = Aviso\: No se ha puesto org.apache.tomcat.JarScanner en ServletContext. Volviendo a la implementaci\u00F3n por defecto de JarScanner.
jsp.error.bug48498 = No puedo mostrar extracto de JSP. Probablemente debido a un error de analizador XML (ver error 48498 de Tomcat para detalles).
jsp.error.duplicateqname = Se ha hallado un atributo con nombre cualificado duplicado [{0}]. Los nombres de atributos cuallificados deben de se \u00FAnicos dentro de un elemento.
diff --git a/java/org/apache/jasper/resources/LocalStrings_fr.properties b/java/org/apache/jasper/resources/LocalStrings_fr.properties
index 512b4d9e..49915bc8 100644
--- a/java/org/apache/jasper/resources/LocalStrings_fr.properties
+++ b/java/org/apache/jasper/resources/LocalStrings_fr.properties
@@ -151,7 +151,7 @@ jsp.error.no.more.content=Fin de contenu alors que l''\u00e9valution n''\u00e9ta
jsp.error.parse.xml=Erreur d''\u00e9valuation XML sur le fichier {0}
jsp.error.parse.xml.line=Erreur d''\u00e9valuation XML sur le fichier {0}: (ligne {1}, col {2})
jsp.error.parse.xml.scripting.invalid.body=Le corps de l''\u00e9l\u00e9ment {0} ne doit contenir aucun \u00e9l\u00e9ments XML
-jsp.error.internal.tldinit=Exception lors de l'initialisation de TldLocationsCache: {0}
+jsp.error.internal.tldinit=Exception lors de l''initialisation de TldLocationsCache: {0}
jsp.error.internal.filenotfound=Erreur interne: Fichier {0} introuvable
jsp.error.parse.xml.invalidPublicId=PUBLIC ID invalide: {0}
jsp.error.unsupported.encoding=Encodage non support\u00e9: {0}
@@ -161,7 +161,7 @@ jsp.error.taglibDirective.missing.location=Ni l''uri' ni l''attribut 'tagdir' n'
jsp.error.invalid.tagdir=Le r\u00e9pertoire du fichier Tag {0} ne commence pas par \"/WEB-INF/tags\"
#jspx.error.templateDataNotInJspCdata=Erreur de validation: l''\u00e9l\u00e9ment <{0}> ne peut avoir de donn\u00e9es template. Les donn\u00e9es Template doivent \u00eatre encapsul\u00e9es \u00e0 l''int\u00e9rieur d''un \u00e9l\u00e9ment <jsp:cdata>. [JSP1.2 PFD section 5.1.9]\nDonn\u00e9e Template en erreur: {1}
#Erreur lors du traitement du fichier jar de la taglib {0}: {1}
-jsp.error.needAlternateJavaEncoding=L''encodage java par d\u00e9faut {0} est incorrect sur votre environnement java. Une alternative peut \u00eatre indiqu\u00e9e via le param\u00eatre 'javaEncoding' de la JspServlet.
+jsp.error.needAlternateJavaEncoding=L''encodage java par d\u00e9faut {0} est incorrect sur votre environnement java. Une alternative peut \u00eatre indiqu\u00e9e via le param\u00eatre ''javaEncoding'' de la JspServlet.
#Erreur lors de la compilation, utilis\u00e9 pour la ligne jsp des messages d''erreur
jsp.error.single.line.number=Une erreur s''est produite \u00e0 la ligne: {0} dans le fichier jsp: {1}
jsp.error.corresponding.servlet=Erreur de servlet g\u00e9n\u00e9r\u00e9e:\n
@@ -169,7 +169,7 @@ jsp.error.jspbody.required=Doit utiliser jsp:body pour indiqu\u00e9 le corps de
jsp.error.jspbody.emptybody.only=Le tag {0} ne peut avoir que jsp:attribute dans son corps.
jsp.error.no.scriptlets=Les \u00e9l\u00e9ments de Scripting ( <%!, <jsp:declaration, <%=, <jsp:expression, <%, <jsp:scriptlet ) ne sont pas autoris\u00e9s ici.
jsp.error.tld.fn.invalid.signature=Synthaxe invalide pour la signature de fonction dans la TLD. Librairie de Tag : {0}, Fonction: {1}
-jsp.error.tld.fn.invalid.signature.parenexpected=Synthaxe invalide pour la signature de fonction dans la TLD. Parenth\u00e8se '(' attendue. Librairie de Tag: {0}, Fonction: {1}.
+jsp.error.tld.fn.invalid.signature.parenexpected=Synthaxe invalide pour la signature de fonction dans la TLD. Parenth\u00e8se ''('' attendue. Librairie de Tag: {0}, Fonction: {1}.
jsp.error.dynamic.attributes.not.implemented=Le tag {0} indique qu''il accepte des attributs dynamics mais n''impl\u00e9mente pas l''interface requise
jsp.error.attribute.noequal=Symbole \u00e9gal (equal) attendu
jsp.error.attribute.noquote=Symbole guillemet (quote) attendu
diff --git a/java/org/apache/jasper/resources/LocalStrings_ja.properties b/java/org/apache/jasper/resources/LocalStrings_ja.properties
index 1bc6aa00..2ad30b2d 100644
--- a/java/org/apache/jasper/resources/LocalStrings_ja.properties
+++ b/java/org/apache/jasper/resources/LocalStrings_ja.properties
@@ -25,28 +25,28 @@ jsp.message.parent_class_loader_is=\u89aa\u30af\u30e9\u30b9\u30ed\u30fc\u30c0: {
jsp.message.dont.modify.servlets=\u91cd\u8981: \u751f\u6210\u3055\u308c\u305f\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u3092\u5909\u66f4\u3057\u3066\u306f\u3044\u3051\u307e\u305b\u3093
jsp.error.unavailable=JSP\u306f\u5229\u7528\u4e0d\u53ef\u3068\u30de\u30fc\u30af\u3055\u308c\u3066\u3044\u307e\u3059
jsp.error.usebean.duplicate=useBean: beanName\u5c5e\u6027\u304c\u91cd\u8907\u3057\u3066\u3044\u307e\u3059: {0}
-jsp.error.invalid.scope='scope'\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059: {0} (\"page\"\u3001\"request\"\u3001\"session\"\u53c8\u306f\"application\"\u306e\u3069\u308c\u304b\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093)
+jsp.error.invalid.scope=''scope''\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059: {0} (\"page\"\u3001\"request\"\u3001\"session\"\u53c8\u306f\"application\"\u306e\u3069\u308c\u304b\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093)
jsp.error.classname=.class\u30d5\u30a1\u30a4\u30eb\u304b\u3089\u30af\u30e9\u30b9\u540d\u3092\u6c7a\u5b9a\u3067\u304d\u307e\u305b\u3093
jsp.error.data.file.write=\u30c7\u30fc\u30bf\u30d5\u30a1\u30a4\u30eb\u3092\u66f8\u304d\u8fbc\u307f\u4e2d\u306e\u30a8\u30e9\u30fc\u3067\u3059
-jsp.error.page.conflict.contenttype=page\u6307\u793a\u5b50: 'contentType'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
-jsp.error.page.conflict.session=page\u6307\u793a\u5b50: 'session'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.contenttype=page\u6307\u793a\u5b50: ''contentType''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.session=page\u6307\u793a\u5b50: ''session''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
jsp.error.page.invalid.session=page\u6307\u793a\u5b50: session\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
-jsp.error.page.conflict.buffer=page\u6307\u793a\u5b50: 'buffer'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.buffer=page\u6307\u793a\u5b50: ''buffer''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
jsp.error.page.invalid.buffer=page\u6307\u793a\u5b50: buffer\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
-jsp.error.page.conflict.autoflush=page\u6307\u793a\u5b50: 'autoFlush'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
-jsp.error.page.conflict.isthreadsafe=page\u6307\u793a\u5b50: 'isThreadSafe'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.autoflush=page\u6307\u793a\u5b50: ''autoFlush''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.isthreadsafe=page\u6307\u793a\u5b50: ''isThreadSafe''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
jsp.error.page.invalid.isthreadsafe=page\u6307\u793a\u5b50: isThreadSafe\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
-jsp.error.page.conflict.info=page\u6307\u793a\u5b50: 'info'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.info=page\u6307\u793a\u5b50: ''info''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
jsp.error.page.invalid.info=page\u6307\u793a\u5b50: info\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
-jsp.error.page.conflict.iserrorpage=page\u6307\u793a\u5b50: 'isErrorPage'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.iserrorpage=page\u6307\u793a\u5b50: ''isErrorPage''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
jsp.error.page.invalid.iserrorpage=page\u6307\u793a\u5b50: isErrorPage\u5c5e\u6027\u306e\u5024\u304c\u7121\u52b9\u3067\u3059
-jsp.error.page.conflict.errorpage=page\u6307\u793a\u5b50: 'errorPage'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.errorpage=page\u6307\u793a\u5b50: ''errorPage''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
jsp.error.page.conflict.language=page\u6307\u793a\u5b50: 'language'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
-jsp.error.tag.conflict.language=tag\u6307\u793a\u5b50: 'language'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.tag.conflict.language=tag\u6307\u793a\u5b50: ''language''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
jsp.error.page.language.nonjava=page\u6307\u793a\u5b50: \u7121\u52b9\u306alanguage\u5c5e\u6027\u3067\u3059
jsp.error.tag.language.nonjava=tag\u6307\u793a\u5b50: \u7121\u52b9\u306alanguage\u5c5e\u6027\u3067\u3059
-jsp.error.page.conflict.extends=page\u6307\u793a\u5b50: 'extends'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
-jsp.error.page.conflict.iselignored=page\u6307\u793a\u5b50: 'isELIgnored'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.extends=page\u6307\u793a\u5b50: ''extends''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
+jsp.error.page.conflict.iselignored=page\u6307\u793a\u5b50: ''isELIgnored''\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
jsp.error.tag.conflict.iselignored=tag\u6307\u793a\u5b50: 'isELIgnored'\u3092\u7570\u306a\u308b\u5024\u3067\u8907\u6570\u56de\u6307\u5b9a\u3059\u308b\u306e\u306f\u7121\u52b9\u3067\u3059 (\u65e7: {0}, \u65b0: {1})
jsp.error.page.invalid.iselignored=page\u6307\u793a\u5b50: isELIgnored\u306b\u7121\u52b9\u306a\u5024\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059
jsp.error.tag.invalid.iselignored=tag\u6307\u793a\u5b50: isELIgnored\u306b\u7121\u52b9\u306a\u5024\u304c\u6307\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059
@@ -205,7 +205,7 @@ jsp.error.taglibDirective.both_uri_and_tagdir=\'uri\'\u5c5e\u6027 \u3068 \'tagdi
jsp.error.invalid.tagdir=\u30bf\u30b0\u30d5\u30a1\u30a4\u30eb\u30c7\u30a3\u30ec\u30af\u30c8\u30ea {0} \u304c\"/WEB-INF/tags\"\u3067\u59cb\u307e\u308a\u307e\u305b\u3093
#jspx.error.templateDataNotInJspCdata=Validation Error: Element <{0}> cannot have template data. Template data must be encapsulated within a <jsp:cdata> element. [JSP1.2 PFD section 5.1.9]\nTemplate data in error: {1}
#Error while processing taglib jar file {0}: {1}
-jsp.error.needAlternateJavaEncoding=\u30c7\u30d5\u30a9\u30eb\u30c8\u306eJava\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0 {0} \u306f\u3042\u306a\u305f\u306e\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u3067\u306f\u7121\u52b9\u3067\u3059\u3002JspServlet\u306e 'javaEncoding' \u30d1\u30e9\u30e1\u30bf\u3067\u3001\u5225\u306e\u5024\u3092\u6307\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002
+jsp.error.needAlternateJavaEncoding=\u30c7\u30d5\u30a9\u30eb\u30c8\u306eJava\u30a8\u30f3\u30b3\u30fc\u30c7\u30a3\u30f3\u30b0 {0} \u306f\u3042\u306a\u305f\u306e\u30d7\u30e9\u30c3\u30c8\u30d5\u30a9\u30fc\u30e0\u3067\u306f\u7121\u52b9\u3067\u3059\u3002JspServlet\u306e ''javaEncoding'' \u30d1\u30e9\u30e1\u30bf\u3067\u3001\u5225\u306e\u5024\u3092\u6307\u5b9a\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002
#Error when compiling, used for jsp line number error messages
jsp.error.single.line.number=JSP\u30d5\u30a1\u30a4\u30eb: {1} \u306e\u4e2d\u306e{0}\u884c\u76ee\u3067\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f
jsp.error.corresponding.servlet=\u751f\u6210\u3055\u308c\u305f\u30b5\u30fc\u30d6\u30ec\u30c3\u30c8\u306e\u30a8\u30e9\u30fc\u3067\u3059:\n
@@ -214,7 +214,7 @@ jsp.error.jspbody.emptybody.only={0} \u30bf\u30b0\u306f\u3001\u305d\u306e\u30dc\
jsp.error.no.scriptlets=\u30b9\u30af\u30ea\u30d7\u30c6\u30a3\u30f3\u30b0\u8981\u7d20 ( <%!\u3001<jsp:declaration\u3001<%=\u3001<jsp:expression\u3001<%\u3001<jsp:scriptlet ) \u306f\u3053\u3053\u3067\u306f\u8a31\u3055\u308c\u307e\u305b\u3093
jsp.error.tld.fn.invalid.signature=TLD\u306e\u4e2d\u306e\u95a2\u6570\u30b7\u30b0\u30cd\u30c1\u30e3\u306b\u5bfe\u3059\u308b\u7121\u52b9\u306a\u69cb\u6587\u3067\u3059\u3002\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea: {0}\u3001\u95a2\u6570: {1}
jsp.error.tld.fn.duplicate.name=\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea {1} \u306e\u4e2d\u306e\u95a2\u6570\u540d {0} \u304c\u91cd\u8907\u3057\u3066\u3044\u307e\u3059
-jsp.error.tld.fn.invalid.signature.parenexpected=TLD\u306e\u4e2d\u306e\u95a2\u6570\u30b7\u30b0\u30cd\u30c1\u30e3\u306b\u5bfe\u3059\u308b\u7121\u52b9\u306a\u69cb\u6587\u3067\u3059\u3002\u62ec\u5f27 '(' \u304c\u3042\u308a\u307e\u305b\u3093\u3002\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea: {0}\u3001\u95a2\u6570: {1}\u3002
+jsp.error.tld.fn.invalid.signature.parenexpected=TLD\u306e\u4e2d\u306e\u95a2\u6570\u30b7\u30b0\u30cd\u30c1\u30e3\u306b\u5bfe\u3059\u308b\u7121\u52b9\u306a\u69cb\u6587\u3067\u3059\u3002\u62ec\u5f27 ''('' \u304c\u3042\u308a\u307e\u305b\u3093\u3002\u30bf\u30b0\u30e9\u30a4\u30d6\u30e9\u30ea: {0}\u3001\u95a2\u6570: {1}\u3002
jsp.error.tld.mandatory.element.missing=\u5fc5\u9808TLD\u8981\u7d20\u304c\u306a\u3044\u3001\u53c8\u306f\u7a7a\u3067\u3059: {0}
jsp.error.dynamic.attributes.not.implemented={0} \u30bf\u30b0\u306f\u305d\u308c\u304cdynamic\u5c5e\u6027\u3092\u53d7\u3051\u4ed8\u3051\u308b\u3068\u5ba3\u8a00\u3057\u3066\u3044\u307e\u3059\u304c\u3001\u305d\u308c\u306b\u5fc5\u8981\u306a\u30a4\u30f3\u30bf\u30d5\u30a7\u30fc\u30b9\u3092\u5b9f\u88c5\u3057\u3066\u3044\u307e\u305b\u3093
jsp.error.attribute.noequal=\u7b49\u53f7\u8a18\u53f7\u304c\u5fc5\u8981\u3067\u3059
diff --git a/java/org/apache/tomcat/buildutil/SignCode.java b/java/org/apache/tomcat/buildutil/SignCode.java
index e072ecbc..bde6bcd7 100644
--- a/java/org/apache/tomcat/buildutil/SignCode.java
+++ b/java/org/apache/tomcat/buildutil/SignCode.java
@@ -41,6 +41,7 @@ import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
@@ -62,7 +63,8 @@ public class SignCode extends Task {
static {
try {
- SIGNING_SERVICE_URL = new URL("https://api.ws.symantec.com/webtrust/SigningService");
+ SIGNING_SERVICE_URL = new URL(
+ "https://api-appsec-cws.ws.symantec.com/webtrust/SigningService");
} catch (MalformedURLException e) {
throw new IllegalArgumentException(e);
}
@@ -77,9 +79,12 @@ public class SignCode extends Task {
private String userName;
private String password;
private String partnerCode;
+ private String keyStore;
+ private String keyStorePassword;
private String applicationName;
private String applicationVersion;
private String signingService;
+ private boolean debug;
public void addFileset(FileSet fileset) {
filesets.add(fileset);
@@ -101,6 +106,16 @@ public class SignCode extends Task {
}
+ public void setKeyStore(String keyStore) {
+ this.keyStore = keyStore;
+ }
+
+
+ public void setKeyStorePassword(String keyStorePassword) {
+ this.keyStorePassword = keyStorePassword;
+ }
+
+
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
@@ -116,6 +131,11 @@ public class SignCode extends Task {
}
+ public void setDebug(String debug) {
+ this.debug = Boolean.parseBoolean(debug);
+ }
+
+
@Override
public void execute() throws BuildException {
@@ -135,6 +155,10 @@ public class SignCode extends Task {
}
}
+ // Set up the TLS client
+ System.setProperty("javax.net.ssl.keyStore", keyStore);
+ System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword);
+
try {
String signingSetID = makeSigningRequest(filesToSign);
downloadSignedFiles(filesToSign, signingSetID);
@@ -172,7 +196,7 @@ public class SignCode extends Task {
SOAPElement commaDelimitedFileNames =
requestSigningRequest.addChildElement("commaDelimitedFileNames", NS);
- commaDelimitedFileNames.addTextNode(listToString(fileNames));
+ commaDelimitedFileNames.addTextNode(StringUtils.join(fileNames));
SOAPElement application =
requestSigningRequest.addChildElement("application", NS);
@@ -185,6 +209,12 @@ public class SignCode extends Task {
log("Sending singing request to server and waiting for response");
SOAPMessage response = connection.call(message, SIGNING_SERVICE_URL);
+ if (debug) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(2 * 1024);
+ response.writeTo(baos);
+ log(baos.toString("UTF-8"));
+ }
+
log("Processing response");
SOAPElement responseBody = response.getSOAPBody();
@@ -214,21 +244,6 @@ public class SignCode extends Task {
}
- private String listToString(List<String> list) {
- StringBuilder sb = new StringBuilder(list.size() * 6);
- boolean doneFirst = false;
- for (String s : list) {
- if (doneFirst) {
- sb.append(',');
- } else {
- doneFirst = true;
- }
- sb.append(s);
- }
- return sb.toString();
- }
-
-
private void downloadSignedFiles(List<File> filesToSign, String id)
throws SOAPException, IOException {
diff --git a/java/org/apache/tomcat/util/ExceptionUtils.java b/java/org/apache/tomcat/util/ExceptionUtils.java
index 97e357d8..3df395fa 100644
--- a/java/org/apache/tomcat/util/ExceptionUtils.java
+++ b/java/org/apache/tomcat/util/ExceptionUtils.java
@@ -57,4 +57,15 @@ public class ExceptionUtils {
}
return t;
}
+
+
+ /**
+ * NO-OP method provided to enable simple pre-loading of this class. Since
+ * the class is used extensively in error handling, it is prudent to
+ * pre-load it to avoid any failure to load this class masking the true
+ * problem during error handling.
+ */
+ public static void preload() {
+ // NO-OP
+ }
}
diff --git a/java/org/apache/tomcat/util/buf/StringUtils.java b/java/org/apache/tomcat/util/buf/StringUtils.java
new file mode 100644
index 00000000..33bb38b9
--- /dev/null
+++ b/java/org/apache/tomcat/util/buf/StringUtils.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.buf;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * Utility methods to build a separated list from a given set (not
+ * java.util.Set) of inputs and return that list as a string or append it to an
+ * existing StringBuilder.
+ */
+public final class StringUtils {
+
+ private static final String EMPTY_STRING = "";
+
+ private StringUtils() {
+ // Utility class
+ }
+
+
+ public static String join(String[] array) {
+ return join(Arrays.asList(array));
+ }
+
+
+ public static void join(String[] array, char separator, StringBuilder sb) {
+ join(Arrays.asList(array), separator, sb);
+ }
+
+
+ public static String join(Collection<String> collection) {
+ return join(collection, ',');
+ }
+
+
+ public static String join(Collection<String> collection, char separator) {
+ // Shortcut
+ if (collection.isEmpty()) {
+ return EMPTY_STRING;
+ }
+
+ StringBuilder result = new StringBuilder();
+ join(collection, separator, result);
+ return result.toString();
+ }
+
+
+ public static void join(Iterable<String> iterable, char separator, StringBuilder sb) {
+ join(iterable, separator,
+ new Function<String>() {@Override public String apply(String t) { return t; }}, sb);
+ }
+
+
+ public static <T> void join(T[] array, char separator, Function<T> function,
+ StringBuilder sb) {
+ join(Arrays.asList(array), separator, function, sb);
+ }
+
+
+ public static <T> void join(Iterable<T> iterable, char separator, Function<T> function,
+ StringBuilder sb) {
+ boolean first = true;
+ for (T value : iterable) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(separator);
+ }
+ sb.append(function.apply(value));
+ }
+ }
+
+
+ public interface Function<T> {
+ public String apply(T t);
+ }
+}
diff --git a/java/org/apache/tomcat/util/http/HttpMessages.java b/java/org/apache/tomcat/util/http/HttpMessages.java
new file mode 100644
index 00000000..1e4431db
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/HttpMessages.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.http;
+
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Handle (internationalized) HTTP messages.
+ *
+ * @author James Duncan Davidson [duncan@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author Harish Prabandham
+ * @author costin@eng.sun.com
+ */
+public class HttpMessages {
+
+ private static final Map<Locale,HttpMessages> instances =
+ new ConcurrentHashMap<>();
+
+ private static final HttpMessages DEFAULT = new HttpMessages(
+ StringManager.getManager("org.apache.tomcat.util.http.res",
+ Locale.getDefault()));
+
+
+ // XXX move message resources in this package
+ private final StringManager sm;
+
+ private String st_200 = null;
+ private String st_302 = null;
+ private String st_400 = null;
+ private String st_404 = null;
+ private String st_500 = null;
+
+ private HttpMessages(StringManager sm) {
+ this.sm = sm;
+ }
+
+
+ /**
+ * Get the status string associated with a status code. Common messages are
+ * cached.
+ *
+ * @param status The HTTP status code to retrieve the message for
+ *
+ * @return The HTTP status string that conforms to the requirements of the
+ * HTTP specification
+ */
+ public String getMessage(int status) {
+ // method from Response.
+
+ // Does HTTP requires/allow international messages or
+ // are pre-defined? The user doesn't see them most of the time
+ switch( status ) {
+ case 200:
+ if(st_200 == null ) {
+ st_200 = sm.getString("sc.200");
+ }
+ return st_200;
+ case 302:
+ if(st_302 == null ) {
+ st_302 = sm.getString("sc.302");
+ }
+ return st_302;
+ case 400:
+ if(st_400 == null ) {
+ st_400 = sm.getString("sc.400");
+ }
+ return st_400;
+ case 404:
+ if(st_404 == null ) {
+ st_404 = sm.getString("sc.404");
+ }
+ return st_404;
+ case 500:
+ if (st_500 == null) {
+ st_500 = sm.getString("sc.500");
+ }
+ return st_500;
+ }
+ return sm.getString("sc."+ status);
+ }
+
+
+ public static HttpMessages getInstance(Locale locale) {
+ HttpMessages result = instances.get(locale);
+ if (result == null) {
+ StringManager sm = StringManager.getManager(
+ "org.apache.tomcat.util.http.res", locale);
+ if (Locale.getDefault().equals(sm.getLocale())) {
+ result = DEFAULT;
+ } else {
+ result = new HttpMessages(sm);
+ }
+ instances.put(locale, result);
+ }
+ return result;
+ }
+
+
+ /**
+ * Is the provided message safe to use in an HTTP header. Safe messages must
+ * meet the requirements of RFC2616 - i.e. must consist only of TEXT.
+ *
+ * @param msg The message to test
+ * @return <code>true</code> if the message is safe to use in an HTTP
+ * header else <code>false</code>
+ */
+ public static boolean isSafeInHttpHeader(String msg) {
+ // Nulls are fine. It is up to the calling code to address any NPE
+ // concerns
+ if (msg == null) {
+ return true;
+ }
+
+ // Reason-Phrase is defined as *<TEXT, excluding CR, LF>
+ // TEXT is defined as any OCTET except CTLs, but including LWS
+ // OCTET is defined as an 8-bit sequence of data
+ // CTL is defined as octets 0-31 and 127
+ // LWS, if we exclude CR LF pairs, is defined as SP or HT (32, 9)
+ final int len = msg.length();
+ for (int i = 0; i < len; i++) {
+ char c = msg.charAt(i);
+ if (32 <= c && c <= 126 || 128 <= c && c <= 255 || c == 9) {
+ continue;
+ }
+ return false;
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/http/LocalStrings.properties b/java/org/apache/tomcat/util/http/LocalStrings.properties
index e7893d25..4b91257c 100644
--- a/java/org/apache/tomcat/util/http/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/http/LocalStrings.properties
@@ -22,7 +22,7 @@ parameters.invalidChunk=Invalid chunk starting at byte [{0}] and ending at byte
parameters.maxCountFail=More than the maximum number of request parameters (GET plus POST) for a single request ([{0}]) were detected. Any parameters beyond this limit have been ignored. To change this limit, set the maxParameterCount attribute on the Connector.
parameters.maxCountFail.fallToDebug=\n Note: further occurrences of this error will be logged at DEBUG level.
parameters.multipleDecodingFail=Character decoding failed. A total of [{0}] failures were detected but only the first was logged. Enable debug level logging for this logger to log all failures.
-parameters.noequal=Parameter starting at position [{0}] and ending at position [{1}] with a value of [{0}] was not followed by an '=' character
+parameters.noequal=Parameter starting at position [{0}] and ending at position [{1}] with a value of [{0}] was not followed by an ''='' character
parameters.fallToDebug=\n Note: further occurrences of Parameter errors will be logged at DEBUG level.
cookies.invalidCookieToken=Cookies: Invalid cookie. Value not a token or quoted value
diff --git a/java/org/apache/tomcat/util/http/Parameters.java b/java/org/apache/tomcat/util/http/Parameters.java
index 2dc6249a..72fb2589 100644
--- a/java/org/apache/tomcat/util/http/Parameters.java
+++ b/java/org/apache/tomcat/util/http/Parameters.java
@@ -31,6 +31,7 @@ import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.log.UserDataHelper;
import org.apache.tomcat.util.res.StringManager;
@@ -513,10 +514,7 @@ public final class Parameters {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, ArrayList<String>> e : paramHashValues.entrySet()) {
sb.append(e.getKey()).append('=');
- ArrayList<String> values = e.getValue();
- for (String value : values) {
- sb.append(value).append(',');
- }
+ StringUtils.join(e.getValue(), ',', sb);
sb.append('\n');
}
return sb.toString();
diff --git a/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java b/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
index faa6ac4a..a0e54f3a 100644
--- a/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
+++ b/java/org/apache/tomcat/util/http/Rfc6265CookieProcessor.java
@@ -143,14 +143,14 @@ public class Rfc6265CookieProcessor extends CookieProcessorBase {
String domain = cookie.getDomain();
if (domain != null && domain.length() > 0) {
validateDomain(domain);
- header.append(";domain=");
+ header.append("; Domain=");
header.append(domain);
}
String path = cookie.getPath();
if (path != null && path.length() > 0) {
validatePath(path);
- header.append(";path=");
+ header.append("; Path=");
header.append(path);
}
diff --git a/java/org/apache/tomcat/util/http/res/LocalStrings.properties b/java/org/apache/tomcat/util/http/res/LocalStrings.properties
new file mode 100644
index 00000000..0fe0bbc5
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/res/LocalStrings.properties
@@ -0,0 +1,78 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# HttpMessages. The values in this file will be used in HTTP headers and as such
+# may only contain TEXT as defined by RFC 2616
+# All status codes registered with IANA can be found at
+# http://www.iana.org/assignments/http-status-codes/http-status-codes.xml
+# The list might be kept in sync with the one in
+# java/org/apache/catalina/valves/LocalStrings.properties
+sc.100=Continue
+sc.101=Switching Protocols
+sc.102=Processing
+sc.200=OK
+sc.201=Created
+sc.202=Accepted
+sc.203=Non-Authoritative Information
+sc.204=No Content
+sc.205=Reset Content
+sc.206=Partial Content
+sc.207=Multi-Status
+sc.208=Already Reported
+sc.226=IM Used
+sc.300=Multiple Choices
+sc.301=Moved Permanently
+sc.302=Found
+sc.303=See Other
+sc.304=Not Modified
+sc.305=Use Proxy
+sc.307=Temporary Redirect
+sc.308=Permanent Redirect
+sc.400=Bad Request
+sc.401=Unauthorized
+sc.402=Payment Required
+sc.403=Forbidden
+sc.404=Not Found
+sc.405=Method Not Allowed
+sc.406=Not Acceptable
+sc.407=Proxy Authentication Required
+sc.408=Request Timeout
+sc.409=Conflict
+sc.410=Gone
+sc.411=Length Required
+sc.412=Precondition Failed
+sc.413=Request Entity Too Large
+sc.414=Request-URI Too Long
+sc.415=Unsupported Media Type
+sc.416=Requested Range Not Satisfiable
+sc.417=Expectation Failed
+sc.422=Unprocessable Entity
+sc.423=Locked
+sc.424=Failed Dependency
+sc.426=Upgrade Required
+sc.428=Precondition Required
+sc.429=Too Many Requests
+sc.431=Request Header Fields Too Large
+sc.500=Internal Server Error
+sc.501=Not Implemented
+sc.502=Bad Gateway
+sc.503=Service Unavailable
+sc.504=Gateway Timeout
+sc.505=HTTP Version Not Supported
+sc.506=Variant Also Negotiates (Experimental)
+sc.507=Insufficient Storage
+sc.508=Loop Detected
+sc.510=Not Extended
+sc.511=Network Authentication Required
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/http/res/LocalStrings_es.properties b/java/org/apache/tomcat/util/http/res/LocalStrings_es.properties
new file mode 100644
index 00000000..30bb2e55
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/res/LocalStrings_es.properties
@@ -0,0 +1,61 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# HttpMessages. The values in this file will be used in HTTP headers and as such
+# may only contain TEXT as defined by RFC 2616
+sc.100 = Continuar
+sc.101 = Cambiando Protocolos
+sc.200 = OK
+sc.201 = Creado
+sc.202 = Aceptado
+sc.203 = Informaci\u00F3n No-Autorizativa
+sc.204 = Sin Contenido
+sc.205 = Reponer Contenido
+sc.206 = Contenido Parcial
+sc.207 = Multi-Estado
+sc.300 = M\u00FAltiples Elecciones
+sc.301 = Movido permanentemente
+sc.302 = Movido temporalmente
+sc.303 = Mirar Otro
+sc.304 = No Modificado
+sc.305 = Usar Proxy
+sc.307 = Redirecci\u00F3n Temporal
+sc.400 = Petici\u00F3n incorrecta
+sc.401 = No Autorizado
+sc.402 = Pago requerido
+sc.403 = Prohibido
+sc.404 = No Encontrado
+sc.405 = M\u00E9todo No Permitido
+sc.406 = No Aceptable
+sc.407 = Autentificaci\u00F3n Proxy Requerida
+sc.408 = Request Caducada
+sc.409 = Conflicto
+sc.410 = Ido
+sc.411 = Longitud Requerida
+sc.412 = Precondici\u00F3n Fallada
+sc.413 = Entidad de Request Demasiado Grande
+sc.414 = Request-URI Demasiado Larga
+sc.415 = Tipo de Medio No Soportado
+sc.416 = El Rango Pedido No Ser Satisfecho
+sc.417 = Expectativa Fallada
+sc.422 = Entidad Improcesable
+sc.423 = Bloqueado
+sc.424 = Dependencia Fallida
+sc.500 = Error Interno del Servidor
+sc.501 = No Implementado
+sc.502 = Pasarela Incorrecta
+sc.503 = Servicio no Disponible
+sc.504 = Pasarela Caducada
+sc.505 = Versi\u00F3n de HTTP No Soportada
+sc.507 = Almacenaje Insuficiente
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/http/res/LocalStrings_fr.properties b/java/org/apache/tomcat/util/http/res/LocalStrings_fr.properties
new file mode 100644
index 00000000..8df5f107
--- /dev/null
+++ b/java/org/apache/tomcat/util/http/res/LocalStrings_fr.properties
@@ -0,0 +1,61 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# HttpMessages. The values in this file will be used in HTTP headers and as such
+# may only contain TEXT as defined by RFC 2616
+sc.100=Continuer
+sc.101=Changement de Protocols
+sc.200=OK
+sc.201=Cr\u00e9e
+sc.202=Accept\u00e9
+sc.203=Information Sans-Autorit\u00e9
+sc.204=Pas de Contenu
+sc.205=Remise \u00e0 Z\u00e9ro de Contenu
+sc.206=Contenu Partiel
+sc.207=Etat Multiple
+sc.300=Choix Multiples
+sc.301=D\u00e9plac\u00e9 de fa\u00e7on Permanente
+sc.302=D\u00e9plac\u00e9 Temporairement
+sc.303=Voir Autre
+sc.304=Non Modifi\u00e9
+sc.305=Utilisation de Relais
+sc.307=Redirection Temporaire
+sc.400=Mauvaise Requ\u00eate
+sc.401=Non-Autoris\u00e9
+sc.402=Paiement N\u00e9cessaire
+sc.403=Interdit
+sc.404=Introuvable
+sc.405=M\u00e9thode Non Autoris\u00e9e
+sc.406=Inacceptable
+sc.407=Authentification de Relais N\u00e9cessaire
+sc.408=D\u00e9passement de D\u00e9lais pour la Requ\u00eate
+sc.409=Conflit
+sc.410=Parti
+sc.411=Taille Demand\u00e9e
+sc.412=Echec de Pr\u00e9-condition
+sc.413=Entit\u00e9 de Requ\u00eate Trop Grande
+sc.414=URI de Requ\u00eate Trop Grande
+sc.415=Type de Support Non Support\u00e9
+sc.416=Etendue de Requ\u00eate Irr\u00e9alisable
+sc.417=Echec d'Attente
+sc.422=Entit\u00e9 Ing\u00e9rable
+sc.424=Echec de D\u00e9pendance
+sc.500=Erreur Interne de Servlet
+sc.501=Non Impl\u00e9ment\u00e9
+sc.502=Mauvaise Passerelle
+sc.503=Service Indisponible
+sc.504=D\u00e9passement de D\u00e9lais pour la Passerelle
+sc.505=Version HTTP Non Support\u00e9e
+sc.507=Stockage Insuffisant
\ No newline at end of file
diff --git a/java/org/apache/tomcat/util/modeler/ManagedBean.java b/java/org/apache/tomcat/util/modeler/ManagedBean.java
index f5613270..f4e8531a 100644
--- a/java/org/apache/tomcat/util/modeler/ManagedBean.java
+++ b/java/org/apache/tomcat/util/modeler/ManagedBean.java
@@ -38,6 +38,9 @@ import javax.management.ReflectionException;
import javax.management.RuntimeOperationsException;
import javax.management.ServiceNotFoundException;
+import org.apache.tomcat.util.buf.StringUtils;
+import org.apache.tomcat.util.buf.StringUtils.Function;
+
/**
* <p>Internal configuration information for a managed bean (MBean)
@@ -565,26 +568,17 @@ public class ManagedBean implements java.io.Serializable {
private String createOperationKey(OperationInfo operation) {
StringBuilder key = new StringBuilder(operation.getName());
key.append('(');
- for (ParameterInfo parameterInfo: operation.getSignature()) {
- key.append(parameterInfo.getType());
- // Note: A trailing ',' does not matter in this case
- key.append(',');
- }
+ StringUtils.join(operation.getSignature(), ',', new Function<ParameterInfo>() {
+ @Override public String apply(ParameterInfo t) { return t.getType(); }}, key);
key.append(')');
-
return key.toString();
}
- private String createOperationKey(String methodName,
- String[] parameterTypes) {
+ private String createOperationKey(String methodName, String[] parameterTypes) {
StringBuilder key = new StringBuilder(methodName);
key.append('(');
- for (String parameter: parameterTypes) {
- key.append(parameter);
- // Note: A trailing ',' does not matter in this case
- key.append(',');
- }
+ StringUtils.join(parameterTypes, ',', key);
key.append(')');
return key.toString();
diff --git a/java/org/apache/tomcat/util/net/AbstractEndpoint.java b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
index b0e677ed..bc461b66 100644
--- a/java/org/apache/tomcat/util/net/AbstractEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AbstractEndpoint.java
@@ -208,7 +208,7 @@ public abstract class AbstractEndpoint<S> {
throw new IllegalArgumentException(sm.getString("endpoint.noSslHostName"));
}
sslHostConfig.setConfigType(getSslConfigType());
- if (bindState != BindState.UNBOUND) {
+ if (bindState != BindState.UNBOUND && isSSLEnabled()) {
try {
createSSLContext(sslHostConfig);
} catch (Exception e) {
@@ -780,14 +780,13 @@ public abstract class AbstractEndpoint<S> {
*/
protected void unlockAccept() {
// Only try to unlock the acceptor if it is necessary
- boolean unlockRequired = false;
+ int unlocksRequired = 0;
for (Acceptor acceptor : acceptors) {
if (acceptor.getState() == AcceptorState.RUNNING) {
- unlockRequired = true;
- break;
+ unlocksRequired++;
}
}
- if (!unlockRequired) {
+ if (unlocksRequired == 0) {
return;
}
@@ -796,18 +795,17 @@ public abstract class AbstractEndpoint<S> {
try {
localAddress = getLocalAddress();
} catch (IOException ioe) {
- // TODO i18n
- getLog().debug("Unable to determine local address for " + getName(), ioe);
+ getLog().debug(sm.getString("endpoint.debug.unlock.localFail", getName()), ioe);
}
if (localAddress == null) {
- // TODO i18n
- getLog().warn("Failed to unlock acceptor for " + getName() + " because the local address was not available.");
+ getLog().warn(sm.getString("endpoint.debug.unlock.localNone", getName()));
return;
}
try {
unlockAddress = getUnlockAddress(localAddress);
+ for (int i = 0; i < unlocksRequired; i++) {
try (java.net.Socket s = new java.net.Socket()) {
int stmo = 2 * 1000;
int utmo = 2 * 1000;
@@ -837,7 +835,8 @@ public abstract class AbstractEndpoint<S> {
if (getLog().isDebugEnabled()) {
getLog().debug("Socket unlock completed for:" + unlockAddress);
}
-
+ }
+ }
// Wait for upto 1000ms acceptor threads to unlock
long waitLeft = 1000;
for (Acceptor acceptor : acceptors) {
@@ -847,10 +846,9 @@ public abstract class AbstractEndpoint<S> {
waitLeft -= 50;
}
}
- }
} catch(Exception e) {
if (getLog().isDebugEnabled()) {
- getLog().debug(sm.getString("endpoint.debug.unlock", "" + getPort()), e);
+ getLog().debug(sm.getString("endpoint.debug.unlock.fail", "" + getPort()), e);
}
}
}
diff --git a/java/org/apache/tomcat/util/net/AprEndpoint.java b/java/org/apache/tomcat/util/net/AprEndpoint.java
index d89ad975..5e5ba370 100644
--- a/java/org/apache/tomcat/util/net/AprEndpoint.java
+++ b/java/org/apache/tomcat/util/net/AprEndpoint.java
@@ -1695,15 +1695,21 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack {
pollerSpace[i] += rv;
connectionCount.addAndGet(-rv);
for (int n = 0; n < rv; n++) {
- long timeout = timeouts.remove(desc[n*2+1]);
- AprSocketWrapper wrapper = connections.get(
- Long.valueOf(desc[n*2+1]));
if (getLog().isDebugEnabled()) {
log.debug(sm.getString(
"endpoint.debug.pollerProcess",
Long.valueOf(desc[n*2+1]),
Long.valueOf(desc[n*2])));
}
+ long timeout = timeouts.remove(desc[n*2+1]);
+ AprSocketWrapper wrapper = connections.get(
+ Long.valueOf(desc[n*2+1]));
+ if (wrapper == null) {
+ // Socket was closed in another thread while still in
+ // the Poller but wasn't removed from the Poller before
+ // new data arrived.
+ continue;
+ }
wrapper.pollerFlags = wrapper.pollerFlags & ~((int) desc[n*2]);
// Check for failed sockets and hand this socket off to a worker
if (((desc[n*2] & Poll.APR_POLLHUP) == Poll.APR_POLLHUP)
@@ -2138,21 +2144,34 @@ public class AprEndpoint extends AbstractEndpoint<Long> implements SNICallBack {
state.length -= nw;
if (state.length == 0) {
remove(state);
- if (state.keepAlive) {
+ switch (state.keepAliveState) {
+ case NONE: {
+ // Close the socket since this is
+ // the end of the not keep-alive request.
+ closeSocket(state.socket);
+ break;
+ }
+ case PIPELINED: {
// Destroy file descriptor pool, which should close the file
Pool.destroy(state.fdpool);
- Socket.timeoutSet(state.socket,
- getSoTimeout() * 1000);
- // If all done put the socket back in the
- // poller for processing of further requests
- getPoller().add(
- state.socket, getKeepAliveTimeout(),
- Poll.APR_POLLIN);
- } else {
- // Close the socket since this is
- // the end of not keep-alive request.
+ Socket.timeoutSet(state.socket, getSoTimeout() * 1000);
+ // Process the pipelined request data
+ if (!processSocket(state.socket, SocketEvent.OPEN_READ)) {
closeSocket(state.socket);
}
+ break;
+ }
+ case OPEN: {
+ // Destroy file descriptor pool, which should close the file
+ Pool.destroy(state.fdpool);
+ Socket.timeoutSet(state.socket, getSoTimeout() * 1000);
+ // Put the socket back in the poller for
+ // processing of further requests
+ getPoller().add(state.socket, getKeepAliveTimeout(),
+ Poll.APR_POLLIN);
+ break;
+ }
+ }
}
}
} else if (rv < 0) {
diff --git a/java/org/apache/tomcat/util/net/LocalStrings.properties b/java/org/apache/tomcat/util/net/LocalStrings.properties
index 6acf4bbd..7b65b6f9 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings.properties
@@ -37,7 +37,9 @@ endpoint.debug.pollerRemoved=Removed [{0}] from poller
endpoint.debug.socket=socket [{0}]
endpoint.debug.socketCloseFail=Failed to close socket
endpoint.debug.socketTimeout=Timing out [{0}]
-endpoint.debug.unlock=Caught exception trying to unlock accept on port {0}
+endpoint.debug.unlock.fail=Caught exception trying to unlock accept on port [{0}]
+endpoint.debug.unlock.localFail=Unable to determine local address for [{0}]
+endpoint.debug.unlock.localNone=Failed to unlock acceptor for [{0}] because the local address was not available
endpoint.accept.fail=Socket accept failed
endpoint.alpn.fail=Failed to configure endpoint for ALPN using {0}
endpoint.alpn.negotiated=Negotiated [{0}] protocol using ALPN
diff --git a/java/org/apache/tomcat/util/net/LocalStrings_es.properties b/java/org/apache/tomcat/util/net/LocalStrings_es.properties
index fa530ba1..0df35588 100644
--- a/java/org/apache/tomcat/util/net/LocalStrings_es.properties
+++ b/java/org/apache/tomcat/util/net/LocalStrings_es.properties
@@ -15,7 +15,7 @@
# net resources
endpoint.err.handshake = Acuerdo fallido
endpoint.err.unexpected = Error inesperado al procesar conector
-endpoint.debug.unlock = Excepci\u00F3n cogida intentando desbloquear aceptaci\u00F3n en puerto {0}
+endpoint.debug.unlock.fail = Excepci\u00F3n cogida intentando desbloquear aceptaci\u00F3n en puerto {0}
endpoint.err.close = Excepci\u00F3n cogida intentando cerrar conector
endpoint.init.bind = Ligado de conector fall\u00F3\: [{0}] {1}
endpoint.init.listen = Escucha de conector fall\u00F3\: [{0}] {1}
diff --git a/java/org/apache/tomcat/util/net/Nio2Endpoint.java b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
index 73605e8c..a8231bf4 100644
--- a/java/org/apache/tomcat/util/net/Nio2Endpoint.java
+++ b/java/org/apache/tomcat/util/net/Nio2Endpoint.java
@@ -536,17 +536,24 @@ public class Nio2Endpoint extends AbstractJsseEndpoint<Nio2Channel> {
} catch (IOException e) {
// Ignore
}
- if (attachment.keepAlive) {
- if (!isInline()) {
- awaitBytes();
- } else {
+ if (isInline()) {
attachment.doneInline = true;
- }
} else {
- if (!isInline()) {
- getEndpoint().processSocket(Nio2SocketWrapper.this, SocketEvent.DISCONNECT, false);
- } else {
- attachment.doneInline = true;
+ switch (attachment.keepAliveState) {
+ case NONE: {
+ getEndpoint().processSocket(Nio2SocketWrapper.this,
+ SocketEvent.DISCONNECT, false);
+ break;
+ }
+ case PIPELINED: {
+ getEndpoint().processSocket(Nio2SocketWrapper.this,
+ SocketEvent.OPEN_READ, true);
+ break;
+ }
+ case OPEN: {
+ awaitBytes();
+ break;
+ }
}
}
return;
diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java b/java/org/apache/tomcat/util/net/NioEndpoint.java
index 0439844d..b6d9bed6 100644
--- a/java/org/apache/tomcat/util/net/NioEndpoint.java
+++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
@@ -924,17 +924,31 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel> {
// responsible for registering the socket for the
// appropriate event(s) if sendfile completes.
if (!calledByProcessor) {
- if (sd.keepAlive) {
+ switch (sd.keepAliveState) {
+ case NONE: {
if (log.isDebugEnabled()) {
- log.debug("Connection is keep alive, registering back for OP_READ");
+ log.debug("Send file connection is being closed");
}
- reg(sk,socketWrapper,SelectionKey.OP_READ);
- } else {
+ close(sc, sk);
+ break;
+ }
+ case PIPELINED: {
if (log.isDebugEnabled()) {
- log.debug("Send file connection is being closed");
+ log.debug("Connection is keep alive, processing pipe-lined data");
}
+ if (!processSocket(socketWrapper, SocketEvent.OPEN_READ, true)) {
close(sc, sk);
}
+ break;
+ }
+ case OPEN: {
+ if (log.isDebugEnabled()) {
+ log.debug("Connection is keep alive, registering back for OP_READ");
+ }
+ reg(sk,socketWrapper,SelectionKey.OP_READ);
+ break;
+ }
+ }
}
return SendfileState.DONE;
} else {
diff --git a/java/org/apache/tomcat/util/net/SSLHostConfig.java b/java/org/apache/tomcat/util/net/SSLHostConfig.java
index bec3adf7..76f02b2e 100644
--- a/java/org/apache/tomcat/util/net/SSLHostConfig.java
+++ b/java/org/apache/tomcat/util/net/SSLHostConfig.java
@@ -131,6 +131,10 @@ public class SSLHostConfig implements Serializable {
}
+ // Expose in String form for JMX
+ public String getConfigType() {
+ return configType.name();
+ }
public void setConfigType(Type configType) {
this.configType = configType;
if (configType == Type.EITHER) {
@@ -253,6 +257,10 @@ public class SSLHostConfig implements Serializable {
// TODO: This certificate setter can be removed once it is no longer
// necessary to support the old configuration attributes (Tomcat 10?).
+ public String getCertificateKeyPassword() {
+ registerDefaultCertificate();
+ return defaultCertificate.getCertificateKeyPassword();
+ }
public void setCertificateKeyPassword(String certificateKeyPassword) {
registerDefaultCertificate();
defaultCertificate.setCertificateKeyPassword(certificateKeyPassword);
@@ -446,30 +454,50 @@ public class SSLHostConfig implements Serializable {
// TODO: These certificate setters can be removed once it is no longer
// necessary to support the old configuration attributes (Tomcat 10?).
+ public String getCertificateKeyAlias() {
+ registerDefaultCertificate();
+ return defaultCertificate.getCertificateKeyAlias();
+ }
public void setCertificateKeyAlias(String certificateKeyAlias) {
registerDefaultCertificate();
defaultCertificate.setCertificateKeyAlias(certificateKeyAlias);
}
+ public String getCertificateKeystoreFile() {
+ registerDefaultCertificate();
+ return defaultCertificate.getCertificateKeystoreFile();
+ }
public void setCertificateKeystoreFile(String certificateKeystoreFile) {
registerDefaultCertificate();
defaultCertificate.setCertificateKeystoreFile(certificateKeystoreFile);
}
+ public String getCertificateKeystorePassword() {
+ registerDefaultCertificate();
+ return defaultCertificate.getCertificateKeystorePassword();
+ }
public void setCertificateKeystorePassword(String certificateKeystorePassword) {
registerDefaultCertificate();
defaultCertificate.setCertificateKeystorePassword(certificateKeystorePassword);
}
+ public String getCertificateKeystoreProvider() {
+ registerDefaultCertificate();
+ return defaultCertificate.getCertificateKeystoreProvider();
+ }
public void setCertificateKeystoreProvider(String certificateKeystoreProvider) {
registerDefaultCertificate();
defaultCertificate.setCertificateKeystoreProvider(certificateKeystoreProvider);
}
+ public String getCertificateKeystoreType() {
+ registerDefaultCertificate();
+ return defaultCertificate.getCertificateKeystoreType();
+ }
public void setCertificateKeystoreType(String certificateKeystoreType) {
registerDefaultCertificate();
defaultCertificate.setCertificateKeystoreType(certificateKeystoreType);
@@ -655,18 +683,30 @@ public class SSLHostConfig implements Serializable {
// TODO: These certificate setters can be removed once it is no longer
// necessary to support the old configuration attributes (Tomcat 10?).
+ public String getCertificateChainFile() {
+ registerDefaultCertificate();
+ return defaultCertificate.getCertificateChainFile();
+ }
public void setCertificateChainFile(String certificateChainFile) {
registerDefaultCertificate();
defaultCertificate.setCertificateChainFile(certificateChainFile);
}
+ public String getCertificateFile() {
+ registerDefaultCertificate();
+ return defaultCertificate.getCertificateFile();
+ }
public void setCertificateFile(String certificateFile) {
registerDefaultCertificate();
defaultCertificate.setCertificateFile(certificateFile);
}
+ public String getCertificateKeyFile() {
+ registerDefaultCertificate();
+ return defaultCertificate.getCertificateKeyFile();
+ }
public void setCertificateKeyFile(String certificateKeyFile) {
registerDefaultCertificate();
defaultCertificate.setCertificateKeyFile(certificateKeyFile);
diff --git a/java/org/apache/tomcat/util/net/SendfileDataBase.java b/java/org/apache/tomcat/util/net/SendfileDataBase.java
index fc89b119..ca0ee3b2 100644
--- a/java/org/apache/tomcat/util/net/SendfileDataBase.java
+++ b/java/org/apache/tomcat/util/net/SendfileDataBase.java
@@ -21,10 +21,10 @@ public abstract class SendfileDataBase {
/**
* Is the current request being processed on a keep-alive connection? This
* determines if the socket is closed once the send file completes or if
- * processing continues with the next request on the connection (or waiting
- * for that next request to arrive).
+ * processing continues with the next request on the connection or waiting
+ * for that next request to arrive.
*/
- public boolean keepAlive;
+ public SendfileKeepAliveState keepAliveState = SendfileKeepAliveState.NONE;
/**
* The full path to the file that contains the data to be written to the
diff --git a/java/org/apache/tomcat/util/net/SendfileKeepAliveState.java b/java/org/apache/tomcat/util/net/SendfileKeepAliveState.java
new file mode 100644
index 00000000..b27a9f14
--- /dev/null
+++ b/java/org/apache/tomcat/util/net/SendfileKeepAliveState.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.util.net;
+
+public enum SendfileKeepAliveState {
+
+ /**
+ * Keep-alive is not in use. The socket can be closed when the response has
+ * been written.
+ */
+ NONE,
+
+ /**
+ * Keep-alive is in use and there is pipelined data in the input buffer to
+ * be read as soon as the current response has been written.
+ */
+ PIPELINED,
+
+ /**
+ * Keep-alive is in use. The socket should be added to the poller (or
+ * equivalent) to await more data as soon as the current response has been
+ * written.
+ */
+ OPEN
+}
diff --git a/java/org/apache/tomcat/util/net/SocketWrapperBase.java b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
index e0d25c28..5a3b6566 100644
--- a/java/org/apache/tomcat/util/net/SocketWrapperBase.java
+++ b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
@@ -721,9 +721,9 @@ public abstract class SocketWrapperBase<E> {
/**
* Starts the sendfile process. It is expected that if the sendfile process
- * does not complete during this call that the caller <b>will not</b> add
- * the socket to the poller (or equivalent). That is the responsibility of
- * this method.
+ * does not complete during this call and does not report an error, that the
+ * caller <b>will not</b> add the socket to the poller (or equivalent). That
+ * is the responsibility of this method.
*
* @param sendfileData Data representing the file to send
*
diff --git a/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java b/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java
index 717b7156..5f38973c 100644
--- a/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java
+++ b/java/org/apache/tomcat/util/net/openssl/ciphers/Cipher.java
@@ -2728,7 +2728,7 @@ public enum Cipher {
Authentication.ECDH,
Encryption.RC4,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.MEDIUM,
false,
@@ -2796,7 +2796,7 @@ public enum Cipher {
Authentication.ECDSA,
Encryption.eNULL,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.STRONG_NONE,
true,
@@ -2830,7 +2830,7 @@ public enum Cipher {
Authentication.ECDSA,
Encryption.TRIPLE_DES,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.MEDIUM,
true,
@@ -2847,7 +2847,7 @@ public enum Cipher {
Authentication.ECDSA,
Encryption.AES128,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.HIGH,
true,
@@ -2864,7 +2864,7 @@ public enum Cipher {
Authentication.ECDSA,
Encryption.AES256,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.HIGH,
true,
@@ -2966,7 +2966,7 @@ public enum Cipher {
Authentication.RSA,
Encryption.eNULL,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.STRONG_NONE,
true,
@@ -2983,7 +2983,7 @@ public enum Cipher {
Authentication.RSA,
Encryption.RC4,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.MEDIUM,
false,
@@ -3000,7 +3000,7 @@ public enum Cipher {
Authentication.RSA,
Encryption.TRIPLE_DES,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.MEDIUM,
true,
@@ -3017,7 +3017,7 @@ public enum Cipher {
Authentication.RSA,
Encryption.AES128,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.HIGH,
true,
@@ -3034,7 +3034,7 @@ public enum Cipher {
Authentication.RSA,
Encryption.AES256,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.HIGH,
true,
@@ -3051,7 +3051,7 @@ public enum Cipher {
Authentication.aNULL,
Encryption.eNULL,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.STRONG_NONE,
true,
@@ -3068,7 +3068,7 @@ public enum Cipher {
Authentication.aNULL,
Encryption.RC4,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.MEDIUM,
false,
@@ -3085,7 +3085,7 @@ public enum Cipher {
Authentication.aNULL,
Encryption.TRIPLE_DES,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.MEDIUM,
true,
@@ -3102,7 +3102,7 @@ public enum Cipher {
Authentication.aNULL,
Encryption.AES128,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.HIGH,
true,
@@ -3119,7 +3119,7 @@ public enum Cipher {
Authentication.aNULL,
Encryption.AES256,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.HIGH,
true,
@@ -3564,7 +3564,7 @@ public enum Cipher {
Authentication.PSK,
Encryption.RC4,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.MEDIUM,
false,
@@ -3581,7 +3581,7 @@ public enum Cipher {
Authentication.PSK,
Encryption.TRIPLE_DES,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.MEDIUM,
true,
@@ -3598,7 +3598,7 @@ public enum Cipher {
Authentication.PSK,
Encryption.AES128,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.HIGH,
true,
@@ -3615,7 +3615,7 @@ public enum Cipher {
Authentication.PSK,
Encryption.AES256,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.HIGH,
true,
@@ -3664,7 +3664,7 @@ public enum Cipher {
Authentication.PSK,
Encryption.eNULL,
MessageDigest.SHA1,
- Protocol.SSLv3,
+ Protocol.TLSv1,
false,
EncryptionLevel.STRONG_NONE,
true,
diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
index b6b993a7..846cd4ff 100644
--- a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+++ b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
@@ -67,6 +67,7 @@ import javax.websocket.WebSocketContainer;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
+import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.apache.tomcat.util.collections.CaseInsensitiveKeyMap;
import org.apache.tomcat.util.res.StringManager;
@@ -628,21 +629,13 @@ public class WsWebSocketContainer implements WebSocketContainer, BackgroundProce
private static void addHeader(ByteBuffer result, String key, List<String> values) {
- StringBuilder sb = new StringBuilder();
-
- Iterator<String> iter = values.iterator();
- if (!iter.hasNext()) {
+ if (values.isEmpty()) {
return;
}
- sb.append(iter.next());
- while (iter.hasNext()) {
- sb.append(',');
- sb.append(iter.next());
- }
result.put(key.getBytes(StandardCharsets.ISO_8859_1));
result.put(": ".getBytes(StandardCharsets.ISO_8859_1));
- result.put(sb.toString().getBytes(StandardCharsets.ISO_8859_1));
+ result.put(StringUtils.join(values).getBytes(StandardCharsets.ISO_8859_1));
result.put(crlf);
}
diff --git a/modules/jdbc-pool/doc/jdbc-pool.xml b/modules/jdbc-pool/doc/jdbc-pool.xml
index 3f4043c6..f675de4f 100644
--- a/modules/jdbc-pool/doc/jdbc-pool.xml
+++ b/modules/jdbc-pool/doc/jdbc-pool.xml
@@ -551,6 +551,13 @@
The default value is <code>false</code>.
</p>
</attribute>
+ <attribute name="useStatementFacade" required="false">
+ <p>(boolean) Set this to true if you wish to wrap statements in order to
+ enable <code>equals()</code> and <code>hashCode()</code> methods to be
+ called on the closed statements if any statement proxy is set.
+ Default value is <code>true</code>.
+ </p>
+ </attribute>
</attributes>
</subsection>
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
index 856501da..8a69d699 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/ConnectionPool.java
@@ -327,7 +327,10 @@ public class ConnectionPool {
next = next.getNext();
}
}
-
+ // setup statement proxy
+ if (getPoolProperties().getUseStatementFacade()) {
+ handler = new StatementFacade(handler);
+ }
try {
getProxyConstructor(con.getXAConnection() != null);
//create the proxy
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
index 945436c5..d5827f76 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceFactory.java
@@ -124,6 +124,8 @@ public class DataSourceFactory implements ObjectFactory {
protected static final String PROP_IGNOREEXCEPTIONONPRELOAD = "ignoreExceptionOnPreLoad";
+ protected static final String PROP_USESTATEMENTFACADE = "useStatementFacade";
+
public static final int UNKNOWN_TRANSACTIONISOLATION = -1;
public static final String OBJECT_NAME = "object_name";
@@ -179,7 +181,8 @@ public class DataSourceFactory implements ObjectFactory {
PROP_USEDISPOSABLECONNECTIONFACADE,
PROP_LOGVALIDATIONERRORS,
PROP_PROPAGATEINTERRUPTSTATE,
- PROP_IGNOREEXCEPTIONONPRELOAD
+ PROP_IGNOREEXCEPTIONONPRELOAD,
+ PROP_USESTATEMENTFACADE
};
// -------------------------------------------------- ObjectFactory Methods
@@ -527,7 +530,10 @@ public class DataSourceFactory implements ObjectFactory {
if (value != null) {
poolProperties.setIgnoreExceptionOnPreLoad(Boolean.parseBoolean(value));
}
-
+ value = properties.getProperty(PROP_USESTATEMENTFACADE);
+ if (value != null) {
+ poolProperties.setUseStatementFacade(Boolean.parseBoolean(value));
+ }
return poolProperties;
}
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
index d3539ecc..a5407dfb 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DataSourceProxy.java
@@ -1458,6 +1458,22 @@ public class DataSourceProxy implements PoolConfiguration {
getPoolProperties().setIgnoreExceptionOnPreLoad(ignoreExceptionOnPreLoad);
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean getUseStatementFacade() {
+ return getPoolProperties().getUseStatementFacade();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUseStatementFacade(boolean useStatementFacade) {
+ getPoolProperties().setUseStatementFacade(useStatementFacade);
+ }
+
public void purge() {
try {
createPool().purge();
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java
index 3ad572c5..f6cea312 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/DisposableConnectionFacade.java
@@ -22,7 +22,7 @@ import java.sql.SQLException;
/**
* A DisposableConnectionFacade object is the top most interceptor that wraps an
- * object of type {@link PooledConnection}. The ProxyCutOffConnection intercepts
+ * object of type {@link PooledConnection}. The DisposableConnectionFacade intercepts
* two methods:
* <ul>
* <li>{@link java.sql.Connection#close()} - returns the connection to the
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
index b8168fca..839d46fe 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolConfiguration.java
@@ -894,4 +894,18 @@ public interface PoolConfiguration {
*/
public boolean isIgnoreExceptionOnPreLoad();
+ /**
+ * Set this to true if you wish to wrap statements in order to enable equals() and hashCode()
+ * methods to be called on the closed statements if any statement proxy is set.
+ * @param useStatementFacade set to <code>true</code> to wrap statements
+ */
+ public void setUseStatementFacade(boolean useStatementFacade);
+
+ /**
+ * Returns <code>true</code> if this connection pool is configured to wrap statements in order
+ * to enable equals() and hashCode() methods to be called on the closed statements if any
+ * statement proxy is set.
+ * @return <code>true</code> if the statements are wrapped
+ */
+ public boolean getUseStatementFacade();
}
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
index 6a459ce1..96178e1d 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/PoolProperties.java
@@ -89,6 +89,7 @@ public class PoolProperties implements PoolConfiguration, Cloneable, Serializabl
private volatile boolean logValidationErrors = false;
private volatile boolean propagateInterruptState = false;
private volatile boolean ignoreExceptionOnPreLoad = false;
+ private volatile boolean useStatementFacade = true;
/**
* {@inheritDoc}
@@ -1301,6 +1302,22 @@ public class PoolProperties implements PoolConfiguration, Cloneable, Serializabl
this.ignoreExceptionOnPreLoad = ignoreExceptionOnPreLoad;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean getUseStatementFacade() {
+ return useStatementFacade;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUseStatementFacade(boolean useStatementFacade) {
+ this.useStatementFacade = useStatementFacade;
+ }
+
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/StatementFacade.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/StatementFacade.java
new file mode 100644
index 00000000..eac84838
--- /dev/null
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/StatementFacade.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.tomcat.jdbc.pool;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.sql.CallableStatement;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.jdbc.pool.interceptor.AbstractCreateStatementInterceptor;
+
+public class StatementFacade extends AbstractCreateStatementInterceptor {
+
+ private static final Log logger = LogFactory.getLog(StatementFacade.class);
+
+ protected StatementFacade(JdbcInterceptor interceptor) {
+ setUseEquals(interceptor.isUseEquals());
+ setNext(interceptor);
+ }
+
+ @Override
+ public void closeInvoked() {
+ // nothing to do
+ }
+
+ /**
+ * Creates a statement interceptor to monitor query response times
+ */
+ @Override
+ public Object createStatement(Object proxy, Method method, Object[] args, Object statement, long time) {
+ try {
+ String name = method.getName();
+ Constructor<?> constructor = null;
+ String sql = null;
+ if (compare(CREATE_STATEMENT, name)) {
+ // createStatement
+ constructor = getConstructor(CREATE_STATEMENT_IDX, Statement.class);
+ } else if (compare(PREPARE_STATEMENT, name)) {
+ // prepareStatement
+ constructor = getConstructor(PREPARE_STATEMENT_IDX, PreparedStatement.class);
+ sql = (String)args[0];
+ } else if (compare(PREPARE_CALL, name)) {
+ // prepareCall
+ constructor = getConstructor(PREPARE_CALL_IDX, CallableStatement.class);
+ sql = (String)args[0];
+ } else {
+ // do nothing
+ return statement;
+ }
+ return constructor.newInstance(new Object[] { new StatementProxy(statement,sql) });
+ } catch (Exception x) {
+ logger.warn("Unable to create statement proxy.", x);
+ }
+ return statement;
+ }
+
+ /**
+ * Class to measure query execute time.
+ */
+ protected class StatementProxy implements InvocationHandler {
+ protected boolean closed = false;
+ protected Object delegate;
+ protected final String query;
+ public StatementProxy(Object parent, String query) {
+ this.delegate = parent;
+ this.query = query;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ if (compare(TOSTRING_VAL,method)) {
+ return toString();
+ }
+ if (compare(EQUALS_VAL, method)) {
+ return Boolean.valueOf(
+ this.equals(Proxy.getInvocationHandler(args[0])));
+ }
+ if (compare(HASHCODE_VAL, method)) {
+ return Integer.valueOf(this.hashCode());
+ }
+ if (compare(CLOSE_VAL, method)) {
+ if (delegate == null) return null;
+ }
+ if (compare(ISCLOSED_VAL, method)) {
+ if (delegate == null) return Boolean.TRUE;
+ }
+ if (delegate == null) throw new SQLException("Statement closed.");
+ Object result = null;
+ try {
+ //invoke next
+ result = method.invoke(delegate,args);
+ } catch (Throwable t) {
+ if (t instanceof InvocationTargetException && t.getCause() != null) {
+ throw t.getCause();
+ } else {
+ throw t;
+ }
+ }
+ //perform close cleanup
+ if (compare(CLOSE_VAL, method)) {
+ delegate = null;
+ }
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return this==obj;
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer buf = new StringBuffer(StatementProxy.class.getName());
+ buf.append("[Proxy=");
+ buf.append(hashCode());
+ buf.append("; Query=");
+ buf.append(query);
+ buf.append("; Delegate=");
+ buf.append(delegate);
+ buf.append("]");
+ return buf.toString();
+ }
+ }
+
+}
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
index 11fc655d..521886e9 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractCreateStatementInterceptor.java
@@ -16,7 +16,10 @@
*/
package org.apache.tomcat.jdbc.pool.interceptor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.JdbcInterceptor;
@@ -46,6 +49,12 @@ public abstract class AbstractCreateStatementInterceptor extends JdbcIntercepto
protected static final String[] EXECUTE_TYPES = {EXECUTE, EXECUTE_QUERY, EXECUTE_UPDATE, EXECUTE_BATCH};
+ /**
+ * the constructors that are used to create statement proxies
+ */
+ protected static final Constructor<?>[] constructors =
+ new Constructor[AbstractCreateStatementInterceptor.STATEMENT_TYPE_COUNT];
+
public AbstractCreateStatementInterceptor() {
super();
}
@@ -73,6 +82,25 @@ public abstract class AbstractCreateStatementInterceptor extends JdbcIntercepto
}
/**
+ * Creates a constructor for a proxy class, if one doesn't already exist
+ *
+ * @param idx
+ * - the index of the constructor
+ * @param clazz
+ * - the interface that the proxy will implement
+ * @return - returns a constructor used to create new instances
+ * @throws NoSuchMethodException Constructor not found
+ */
+ protected Constructor<?> getConstructor(int idx, Class<?> clazz) throws NoSuchMethodException {
+ if (constructors[idx] == null) {
+ Class<?> proxyClass = Proxy.getProxyClass(AbstractCreateStatementInterceptor.class.getClassLoader(),
+ new Class[] { clazz });
+ constructors[idx] = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
+ }
+ return constructors[idx];
+ }
+
+ /**
* This method will be invoked after a successful statement creation. This method can choose to return a wrapper
* around the statement or return the statement itself.
* If this method returns a wrapper then it should return a wrapper object that implements one of the following interfaces.
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
index 7d379c29..4c5b28be 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/AbstractQueryReport.java
@@ -21,7 +21,6 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@@ -43,13 +42,6 @@ public abstract class AbstractQueryReport extends AbstractCreateStatementInterce
*/
protected long threshold = 1000; //don't report queries less than this
- /**
- * the constructors that are used to create statement proxies
- */
- protected static final Constructor<?>[] constructors =
- new Constructor[AbstractCreateStatementInterceptor.STATEMENT_TYPE_COUNT];
-
-
public AbstractQueryReport() {
super();
}
@@ -144,21 +136,6 @@ public abstract class AbstractQueryReport extends AbstractCreateStatementInterce
}
/**
- * Creates a constructor for a proxy class, if one doesn't already exist
- * @param idx - the index of the constructor
- * @param clazz - the interface that the proxy will implement
- * @return - returns a constructor used to create new instances
- * @throws NoSuchMethodException Constructor not found
- */
- protected Constructor<?> getConstructor(int idx, Class<?> clazz) throws NoSuchMethodException {
- if (constructors[idx]==null) {
- Class<?> proxyClass = Proxy.getProxyClass(SlowQueryReport.class.getClassLoader(), new Class[] {clazz});
- constructors[idx] = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
- }
- return constructors[idx];
- }
-
- /**
* Creates a statement interceptor to monitor query response times
*/
@Override
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
index c8179d61..51ae9df0 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/interceptor/StatementDecoratorInterceptor.java
@@ -47,11 +47,6 @@ public class StatementDecoratorInterceptor extends AbstractCreateStatementInterc
protected static final String[] RESULTSET_TYPES = {EXECUTE_QUERY, GET_GENERATED_KEYS, GET_RESULTSET};
/**
- * the constructors that are used to create statement proxies
- */
- protected static final Constructor<?>[] constructors = new Constructor[AbstractCreateStatementInterceptor.STATEMENT_TYPE_COUNT];
-
- /**
* the constructor to create the resultSet proxies
*/
protected static Constructor<?> resultSetConstructor = null;
@@ -61,25 +56,6 @@ public class StatementDecoratorInterceptor extends AbstractCreateStatementInterc
// nothing to do
}
- /**
- * Creates a constructor for a proxy class, if one doesn't already exist
- *
- * @param idx
- * - the index of the constructor
- * @param clazz
- * - the interface that the proxy will implement
- * @return - returns a constructor used to create new instances
- * @throws NoSuchMethodException Constructor not found
- */
- protected Constructor<?> getConstructor(int idx, Class<?> clazz) throws NoSuchMethodException {
- if (constructors[idx] == null) {
- Class<?> proxyClass = Proxy.getProxyClass(StatementDecoratorInterceptor.class.getClassLoader(),
- new Class[] { clazz });
- constructors[idx] = proxyClass.getConstructor(new Class[] { InvocationHandler.class });
- }
- return constructors[idx];
- }
-
protected Constructor<?> getResultSetConstructor() throws NoSuchMethodException {
if (resultSetConstructor == null) {
Class<?> proxyClass = Proxy.getProxyClass(StatementDecoratorInterceptor.class.getClassLoader(),
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
index 0dfb6f72..5d443ebb 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/jmx/ConnectionPool.java
@@ -919,6 +919,22 @@ public class ConnectionPool extends NotificationBroadcasterSupport implements Co
* {@inheritDoc}
*/
@Override
+ public boolean getUseStatementFacade() {
+ return getPoolProperties().getUseStatementFacade();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setUseStatementFacade(boolean useStatementFacade) {
+ getPoolProperties().setUseStatementFacade(useStatementFacade);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
public void purge() {
pool.purge();
diff --git a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml
index 335aaff0..7dfd1c3f 100644
--- a/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml
+++ b/modules/jdbc-pool/src/main/java/org/apache/tomcat/jdbc/pool/mbeans-descriptors.xml
@@ -330,6 +330,12 @@
is="true"
writeable="false"/>
+ <attribute name="useStatementFacade"
+ description="If true, connection pool is configured to wrap statements."
+ type="java.lang.Boolean"
+ is="false"
+ writeable="false"/>
+
<attribute name="borrowedCount"
description="The total number of connections borrowed from this pool"
type="java.lang.Long"
diff --git a/res/findbugs/filter-false-positives.xml b/res/findbugs/filter-false-positives.xml
index 8fe71524..1c22623c 100644
--- a/res/findbugs/filter-false-positives.xml
+++ b/res/findbugs/filter-false-positives.xml
@@ -1061,6 +1061,12 @@
<Bug code="DE" />
</Match>
<Match>
+ <!-- Concrete Map type not affected -->
+ <Class name="org.apache.catalina.util.TestParameterMap" />
+ <Method name="testEntrySetImmutabilityAfterLocked" />
+ <Bug pattern="DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS" />
+ </Match>
+ <Match>
<!-- Code is deliberately unused -->
<Or>
<Class name="org.apache.catalina.webresources.AbstractTestFileResourceSet" />
diff --git a/res/maven/mvn.properties.default b/res/maven/mvn.properties.default
index d0260b11..b3c9332e 100644
--- a/res/maven/mvn.properties.default
+++ b/res/maven/mvn.properties.default
@@ -35,7 +35,7 @@ maven.asf.release.repo.url=https://repository.apache.org/service/local/staging/d
maven.asf.release.repo.repositoryId=apache.releases
# Release version info
-maven.asf.release.deploy.version=8.5.12
+maven.asf.release.deploy.version=8.5.14
#Where do we load the libraries from
tomcat.lib.path=../../output/build/lib
diff --git a/test/org/apache/catalina/core/TestApplicationMapping.java b/test/org/apache/catalina/core/TestApplicationMapping.java
index f6cff588..41dcd6bf 100644
--- a/test/org/apache/catalina/core/TestApplicationMapping.java
+++ b/test/org/apache/catalina/core/TestApplicationMapping.java
@@ -59,6 +59,16 @@ public class TestApplicationMapping extends TomcatBaseTest {
}
@Test
+ public void testContextNonRootMappingPathNone() throws Exception {
+ doTestMapping("/dummy", "/foo/bar/*", "/foo/bar", null, "PATH");
+ }
+
+ @Test
+ public void testContextNonRootMappingPathSeparatorOnly() throws Exception {
+ doTestMapping("/dummy", "/foo/bar/*", "/foo/bar/", "", "PATH");
+ }
+
+ @Test
public void testContextNonRootMappingPath() throws Exception {
doTestMapping("/dummy", "/foo/bar/*", "/foo/bar/foo2", "foo2", "PATH");
}
diff --git a/test/org/apache/catalina/webresources/TestJarWarResourceSet.java b/test/org/apache/catalina/webresources/TestJarWarResourceSet.java
new file mode 100644
index 00000000..be5d98f8
--- /dev/null
+++ b/test/org/apache/catalina/webresources/TestJarWarResourceSet.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.catalina.webresources;
+
+import java.io.File;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.WebResource;
+import org.apache.catalina.startup.Tomcat;
+import org.apache.catalina.startup.TomcatBaseTest;
+
+public class TestJarWarResourceSet extends TomcatBaseTest {
+
+ @Before
+ public void register() {
+ TomcatURLStreamHandlerFactory.register();
+ }
+
+
+ @Test
+ public void testJarWarMetaInf() throws LifecycleException {
+ Tomcat tomcat = getTomcatInstance();
+
+ File warFile = new File("test/webresources/war-url-connection.war");
+ Context ctx = tomcat.addContext("", warFile.getAbsolutePath());
+
+ tomcat.start();
+
+ StandardRoot root = (StandardRoot) ctx.getResources();
+
+ WebResource[] results = root.getClassLoaderResources("/META-INF");
+
+ Assert.assertNotNull(results);
+ Assert.assertEquals(1, results.length);
+ Assert.assertNotNull(results[0].getURL());
+ }
+}
diff --git a/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java b/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java
index 7a05c5da..fd12888a 100644
--- a/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java
+++ b/test/org/apache/coyote/http11/filters/TesterOutputBuffer.java
@@ -38,7 +38,7 @@ public class TesterOutputBuffer extends Http11OutputBuffer {
public TesterOutputBuffer(Response response, int headerBufferSize) {
- super(response, headerBufferSize);
+ super(response, headerBufferSize, false);
outputStreamOutputBuffer = new OutputStreamOutputBuffer();
}
diff --git a/test/org/apache/coyote/http2/Http2TestBase.java b/test/org/apache/coyote/http2/Http2TestBase.java
index fa509baf..ca276c90 100644
--- a/test/org/apache/coyote/http2/Http2TestBase.java
+++ b/test/org/apache/coyote/http2/Http2TestBase.java
@@ -27,6 +27,7 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+import java.util.Random;
import javax.net.SocketFactory;
import javax.servlet.ServletException;
@@ -59,6 +60,8 @@ public abstract class Http2TestBase extends TomcatBaseTest {
// response now included a date header
protected static final String DEFAULT_DATE = "Wed, 11 Nov 2015 19:18:42 GMT";
+ private static final String HEADER_IGNORED = "x-ignore";
+
static final String DEFAULT_CONNECTION_HEADER_VALUE = "Upgrade, HTTP2-Settings";
private static final byte[] EMPTY_SETTINGS_FRAME =
{ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
@@ -177,6 +180,7 @@ public abstract class Http2TestBase extends TomcatBaseTest {
int streamId, String url) {
List<Header> headers = new ArrayList<>(3);
headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":scheme", "http"));
headers.add(new Header(":path", url));
headers.add(new Header(":authority", "localhost:" + getPort()));
@@ -215,6 +219,7 @@ public abstract class Http2TestBase extends TomcatBaseTest {
int streamId) {
List<Header> headers = new ArrayList<>(3);
headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":scheme", "http"));
headers.add(new Header(":path", "/simple"));
buildSimpleGetRequestPart1(frameHeader, headersPayload, headers, streamId);
@@ -306,6 +311,7 @@ public abstract class Http2TestBase extends TomcatBaseTest {
byte[] trailersFrameHeader, ByteBuffer trailersPayload, int streamId) {
MimeHeaders headers = new MimeHeaders();
headers.addValue(":method").setString("POST");
+ headers.addValue(":scheme").setString("http");
headers.addValue(":path").setString("/simple");
headers.addValue(":authority").setString("localhost:" + getPort());
if (useExpectation) {
@@ -925,8 +931,13 @@ public abstract class Http2TestBase extends TomcatBaseTest {
if ("date".equals(name)) {
value = DEFAULT_DATE;
}
+ // Some header values vary so ignore them
+ if (HEADER_IGNORED.equals(name)) {
+ trace.append(lastStreamId + "-Header-[" + name + "]-[...]\n");
+ } else {
trace.append(lastStreamId + "-Header-[" + name + "]-[" + value + "]\n");
}
+ }
@Override
@@ -1139,6 +1150,26 @@ public abstract class Http2TestBase extends TomcatBaseTest {
}
+ static class LargeHeaderServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+ resp.setContentType("text/plain");
+ resp.setCharacterEncoding("UTF-8");
+ StringBuilder headerValue = new StringBuilder();
+ Random random = new Random();
+ while (headerValue.length() < 2048) {
+ headerValue.append(Long.toString(random.nextLong()));
+ }
+ resp.setHeader(HEADER_IGNORED, headerValue.toString());
+ resp.getWriter().print("OK");
+ }
+ }
+
+
static class SettingValue {
private final int setting;
private final long value;
diff --git a/test/org/apache/coyote/http2/TestHpack.java b/test/org/apache/coyote/http2/TestHpack.java
index 5c4bca2a..7d199804 100644
--- a/test/org/apache/coyote/http2/TestHpack.java
+++ b/test/org/apache/coyote/http2/TestHpack.java
@@ -105,6 +105,25 @@ public class TestHpack {
}
}
+ @Test(expected=HpackException.class)
+ public void testExcessiveStringLiteralPadding() throws Exception {
+ MimeHeaders headers = new MimeHeaders();
+ headers.setValue("X-test").setString("foobar");
+ ByteBuffer output = ByteBuffer.allocate(512);
+ HpackEncoder encoder = new HpackEncoder();
+ encoder.encode(headers, output);
+ // Hack the output buffer to extend the EOS marker for the header value
+ // by another byte
+ output.array()[7] = (byte) -122;
+ output.put((byte) -1);
+ output.flip();
+ MimeHeaders headers2 = new MimeHeaders();
+ HpackDecoder decoder = new HpackDecoder();
+ decoder.setHeaderEmitter(new HeadersListener(headers2));
+ decoder.decode(output);
+ }
+
+
private void doTestHeaderValueBug60451(String filename) throws HpackException {
String headerName = "Content-Disposition";
String headerValue = "attachment;filename=\"" + filename + "\"";
diff --git a/test/org/apache/coyote/http2/TestHttp2Limits.java b/test/org/apache/coyote/http2/TestHttp2Limits.java
index 9af34a37..817dcfb7 100644
--- a/test/org/apache/coyote/http2/TestHttp2Limits.java
+++ b/test/org/apache/coyote/http2/TestHttp2Limits.java
@@ -41,15 +41,15 @@ public class TestHttp2Limits extends Http2TestBase {
@Test
public void testHeaderLimits100x32() throws Exception {
// Just within default maxHeaderCount
- // Note request has 3 standard headers
- doTestHeaderLimits(97, 32, 0);
+ // Note request has 4 standard headers
+ doTestHeaderLimits(96, 32, 0);
}
@Test
public void testHeaderLimits101x32() throws Exception {
// Just above default maxHeaderCount
- doTestHeaderLimits(98, 32, 1);
+ doTestHeaderLimits(97, 32, 1);
}
@@ -61,17 +61,17 @@ public class TestHttp2Limits extends Http2TestBase {
@Test
- public void testHeaderLimits8x1001() throws Exception {
+ public void testHeaderLimits8x1144() throws Exception {
// Just within default maxHttpHeaderSize
- // per header overhead plus standard 2 headers
- doTestHeaderLimits(8, 1001, 0);
+ // per header overhead plus standard 3 headers
+ doTestHeaderLimits(7, 1144, 0);
}
@Test
- public void testHeaderLimits8x1002() throws Exception {
+ public void testHeaderLimits8x1145() throws Exception {
// Just above default maxHttpHeaderSize
- doTestHeaderLimits(8, 1002, 1);
+ doTestHeaderLimits(7, 1145, 1);
}
@@ -263,6 +263,7 @@ public class TestHttp2Limits extends Http2TestBase {
String path) throws Exception {
MimeHeaders headers = new MimeHeaders();
headers.addValue(":method").setString("GET");
+ headers.addValue(":scheme").setString("http");
headers.addValue(":path").setString(path);
headers.addValue(":authority").setString("localhost:" + getPort());
for (String[] customHeader : customHeaders) {
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_8_1.java b/test/org/apache/coyote/http2/TestHttp2Section_8_1.java
index 4b248f05..b44590b7 100644
--- a/test/org/apache/coyote/http2/TestHttp2Section_8_1.java
+++ b/test/org/apache/coyote/http2/TestHttp2Section_8_1.java
@@ -141,8 +141,9 @@ public class TestHttp2Section_8_1 extends Http2TestBase {
@Test
public void testUndefinedPseudoHeader() throws Exception {
- List<Header> headers = new ArrayList<>(3);
+ List<Header> headers = new ArrayList<>(5);
headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":scheme", "http"));
headers.add(new Header(":path", "/simple"));
headers.add(new Header(":authority", "localhost:" + getPort()));
headers.add(new Header(":foo", "bar"));
@@ -153,8 +154,9 @@ public class TestHttp2Section_8_1 extends Http2TestBase {
@Test
public void testInvalidPseudoHeader() throws Exception {
- List<Header> headers = new ArrayList<>(3);
+ List<Header> headers = new ArrayList<>(5);
headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":scheme", "http"));
headers.add(new Header(":path", "/simple"));
headers.add(new Header(":authority", "localhost:" + getPort()));
headers.add(new Header(":status", "200"));
@@ -170,8 +172,9 @@ public class TestHttp2Section_8_1 extends Http2TestBase {
http2Connect();
- List<Header> headers = new ArrayList<>(3);
+ List<Header> headers = new ArrayList<>(4);
headers.add(new Header(":method", "GET"));
+ headers.add(new Header(":scheme", "http"));
headers.add(new Header(":path", "/simple"));
headers.add(new Header("x-test", "test"));
diff --git a/test/org/apache/coyote/http2/TestHttp2UpgradeHandler.java b/test/org/apache/coyote/http2/TestHttp2UpgradeHandler.java
new file mode 100644
index 00000000..d12aba5f
--- /dev/null
+++ b/test/org/apache/coyote/http2/TestHttp2UpgradeHandler.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.coyote.http2;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+
+public class TestHttp2UpgradeHandler extends Http2TestBase {
+
+ // https://bz.apache.org/bugzilla/show_bug.cgi?id=60970
+ @Test
+ public void testLargeHeader() throws Exception {
+ enableHttp2();
+
+ Tomcat tomcat = getTomcatInstance();
+
+ Context ctxt = tomcat.addContext("", null);
+ Tomcat.addServlet(ctxt, "simple", new SimpleServlet());
+ ctxt.addServletMappingDecoded("/simple", "simple");
+ Tomcat.addServlet(ctxt, "large", new LargeHeaderServlet());
+ ctxt.addServletMappingDecoded("/large", "large");
+
+ tomcat.start();
+
+ openClientConnection();
+ doHttpUpgrade();
+ sendClientPreface();
+ validateHttp2InitialResponse();
+
+ byte[] frameHeader = new byte[9];
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
+ buildGetRequest(frameHeader, headersPayload, null, 3, "/large");
+ writeFrame(frameHeader, headersPayload);
+
+ // Headers
+ parser.readFrame(true);
+ parser.readFrame(true);
+ // Body
+ parser.readFrame(true);
+
+ Assert.assertEquals(
+ "3-HeadersStart\n" +
+ "3-Header-[:status]-[200]\n" +
+ "3-Header-[x-ignore]-[...]\n" +
+ "3-Header-[content-type]-[text/plain;charset=UTF-8]\n" +
+ "3-Header-[date]-[Wed, 11 Nov 2015 19:18:42 GMT]\n" +
+ "3-HeadersEnd\n" +
+ "3-Body-2\n" +
+ "3-EndOfStream\n", output.getTrace());
+ }
+
+}
diff --git a/test/org/apache/el/TestMethodExpressionImpl.java b/test/org/apache/el/TestMethodExpressionImpl.java
index ee71edcf..6a810da5 100644
--- a/test/org/apache/el/TestMethodExpressionImpl.java
+++ b/test/org/apache/el/TestMethodExpressionImpl.java
@@ -531,4 +531,11 @@ public class TestMethodExpressionImpl {
assertEquals("aaa, bbb", r.toString());
}
+
+ @Test(expected=IllegalArgumentException.class)
+ public void testBug60844() {
+ MethodExpression me2 = factory.createMethodExpression(context,
+ "${beanC.sayHello}", null , new Class[]{ TesterBeanA.class, TesterBeanB.class});
+ me2.invoke(context, new Object[] { new Object() });
+ }
}
diff --git a/test/org/apache/tomcat/util/buf/TestByteChunk.java b/test/org/apache/tomcat/util/buf/TestByteChunk.java
index d2f818ed..0838804a 100644
--- a/test/org/apache/tomcat/util/buf/TestByteChunk.java
+++ b/test/org/apache/tomcat/util/buf/TestByteChunk.java
@@ -32,7 +32,7 @@ public class TestByteChunk {
@Test
public void testConvertToBytes() throws UnsupportedEncodingException {
- String string = "HTTP/1.1 100 Continue\r\n";
+ String string = "HTTP/1.1 100 \r\n\r\n";
byte[] bytes = ByteChunk.convertToBytes(string);
byte[] expected = string.getBytes("ISO-8859-1");
assertTrue(Arrays.equals(bytes, expected));
diff --git a/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java b/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java
index 6c03e7f5..f8a1d582 100644
--- a/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java
+++ b/test/org/apache/tomcat/util/http/TestCookieProcessorGeneration.java
@@ -187,7 +187,8 @@ public class TestCookieProcessorGeneration {
@Test
public void v1TestMaxAgeZero() {
- doV1TestMaxAge(0, "foo=bar; Version=1; Max-Age=0", "foo=bar;Max-Age=0;Expires=Thu, 01-Jan-1970 00:00:10 GMT");
+ doV1TestMaxAge(0, "foo=bar; Version=1; Max-Age=0",
+ "foo=bar; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:10 GMT");
}
@Test
@@ -198,13 +199,13 @@ public class TestCookieProcessorGeneration {
@Test
public void v1TestDomainValid01() {
doV1TestDomain("example.com", "foo=bar; Version=1; Domain=example.com",
- "foo=bar;domain=example.com");
+ "foo=bar; Domain=example.com");
}
@Test
public void v1TestDomainValid02() {
doV1TestDomain("exa-mple.com", "foo=bar; Version=1; Domain=exa-mple.com",
- "foo=bar;domain=exa-mple.com");
+ "foo=bar; Domain=exa-mple.com");
}
@Test
@@ -245,7 +246,7 @@ public class TestCookieProcessorGeneration {
@Test
public void v1TestPathValid() {
doV1TestPath("/example", "foo=bar; Version=1; Path=/example",
- "foo=bar;path=/example");
+ "foo=bar; Path=/example");
}
@Test
diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java
index 74dd1be2..b6e7ebcc 100644
--- a/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java
+++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TestCipher.java
@@ -643,14 +643,14 @@ public class TestCipher {
"ECDHE-ECDSA-CAMELLIA256-SHA384+TLSv1.2",
"ECDHE-ECDSA-CHACHA20-POLY1305+TLSv1.2",
"ECDHE-PSK-3DES-EDE-CBC-SHA+SSLv3",
- "ECDHE-PSK-AES128-CBC-SHA+SSLv3",
+ "ECDHE-PSK-AES128-CBC-SHA+TLSv1",
"ECDHE-PSK-AES128-CBC-SHA256+TLSv1",
- "ECDHE-PSK-AES256-CBC-SHA+SSLv3",
+ "ECDHE-PSK-AES256-CBC-SHA+TLSv1",
"ECDHE-PSK-AES256-CBC-SHA384+TLSv1",
"ECDHE-PSK-CAMELLIA128-SHA256+TLSv1",
"ECDHE-PSK-CAMELLIA256-SHA384+TLSv1",
"ECDHE-PSK-CHACHA20-POLY1305+TLSv1.2",
- "ECDHE-PSK-NULL-SHA+SSLv3",
+ "ECDHE-PSK-NULL-SHA+TLSv1",
"ECDHE-PSK-NULL-SHA256+TLSv1",
"ECDHE-PSK-NULL-SHA384+TLSv1",
"ECDHE-PSK-RC4-SHA+SSLv3",
diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java
index 3f257e56..62b203d6 100644
--- a/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java
+++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TestOpenSSLCipherConfigurationParser.java
@@ -306,7 +306,11 @@ public class TestOpenSSLCipherConfigurationParser {
@Test
public void testSSLv3() throws Exception {
- testSpecification("SSLv3");
+ // In OpenSSL 1.1.0-dev, TLSv1 refers to those ciphers that require
+ // TLSv1 rather than being an alias for SSLv3
+ if (TesterOpenSSL.VERSION < 10100) {
+ testSpecification("SSLv3:TLSv1");
+ }
}
diff --git a/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java b/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java
index fbbc4646..447c8538 100644
--- a/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java
+++ b/test/org/apache/tomcat/util/net/openssl/ciphers/TesterOpenSSL.java
@@ -20,8 +20,10 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.apache.catalina.util.IOTools;
@@ -33,6 +35,8 @@ public class TesterOpenSSL {
public static final Set<Cipher> OPENSSL_UNIMPLEMENTED_CIPHERS;
+ public static final Map<String,String> OPENSSL_RENAMED_CIPHERS;
+
static {
// Note: The following lists are intended to be aligned with the most
// recent release of each OpenSSL release branch. Running the unit
@@ -299,6 +303,29 @@ public class TesterOpenSSL {
unimplemented.add(Cipher.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA);
}
OPENSSL_UNIMPLEMENTED_CIPHERS = Collections.unmodifiableSet(unimplemented);
+
+ Map<String,String> renamed = new HashMap<>();
+ renamed.put("ECDH-ECDSA-RC4-SHA+SSLv3", "ECDH-ECDSA-RC4-SHA+TLSv1");
+ renamed.put("ECDHE-ECDSA-NULL-SHA+SSLv3", "ECDHE-ECDSA-NULL-SHA+TLSv1");
+ renamed.put("ECDHE-ECDSA-DES-CBC3-SHA+SSLv3", "ECDHE-ECDSA-DES-CBC3-SHA+TLSv1");
+ renamed.put("ECDHE-ECDSA-AES128-SHA+SSLv3", "ECDHE-ECDSA-AES128-SHA+TLSv1");
+ renamed.put("ECDHE-ECDSA-AES256-SHA+SSLv3", "ECDHE-ECDSA-AES256-SHA+TLSv1");
+ renamed.put("ECDHE-RSA-NULL-SHA+SSLv3", "ECDHE-RSA-NULL-SHA+TLSv1");
+ renamed.put("ECDHE-RSA-RC4-SHA+SSLv3", "ECDHE-RSA-RC4-SHA+TLSv1");
+ renamed.put("ECDHE-RSA-DES-CBC3-SHA+SSLv3", "ECDHE-RSA-DES-CBC3-SHA+TLSv1");
+ renamed.put("ECDHE-RSA-AES128-SHA+SSLv3", "ECDHE-RSA-AES128-SHA+TLSv1");
+ renamed.put("ECDHE-RSA-AES256-SHA+SSLv3", "ECDHE-RSA-AES256-SHA+TLSv1");
+ renamed.put("AECDH-NULL-SHA+SSLv3", "AECDH-NULL-SHA+TLSv1");
+ renamed.put("AECDH-RC4-SHA+SSLv3", "AECDH-RC4-SHA+TLSv1");
+ renamed.put("AECDH-DES-CBC3-SHA+SSLv3", "AECDH-DES-CBC3-SHA+TLSv1");
+ renamed.put("AECDH-AES128-SHA+SSLv3", "AECDH-AES128-SHA+TLSv1");
+ renamed.put("AECDH-AES256-SHA+SSLv3", "AECDH-AES256-SHA+TLSv1");
+ renamed.put("ECDHE-PSK-RC4-SHA+SSLv3", "ECDHE-PSK-RC4-SHA+TLSv1");
+ renamed.put("ECDHE-PSK-3DES-EDE-CBC-SHA+SSLv3", "ECDHE-PSK-3DES-EDE-CBC-SHA+TLSv1");
+ renamed.put("ECDHE-PSK-AES128-CBC-SHA+SSLv3", "ECDHE-PSK-AES128-CBC-SHA+TLSv1");
+ renamed.put("ECDHE-PSK-AES256-CBC-SHA+SSLv3", "ECDHE-PSK-AES256-CBC-SHA+TLSv1");
+ renamed.put("ECDHE-PSK-NULL-SHA+SSLv3", "ECDHE-PSK-NULL-SHA+TLSv1");
+ OPENSSL_RENAMED_CIPHERS = Collections.unmodifiableMap(renamed);
}
@@ -343,9 +370,11 @@ public class TesterOpenSSL {
} else {
output.append(':');
}
+ StringBuilder name = new StringBuilder();
+
// Name is first part
int i = cipher.indexOf(' ');
- output.append(cipher.substring(0, i));
+ name.append(cipher.substring(0, i));
// Advance i past the space
while (Character.isWhitespace(cipher.charAt(i))) {
@@ -354,8 +383,15 @@ public class TesterOpenSSL {
// Protocol is the second
int j = cipher.indexOf(' ', i);
- output.append('+');
- output.append(cipher.substring(i, j));
+ name.append('+');
+ name.append(cipher.substring(i, j));
+
+ // More renames
+ if (OPENSSL_RENAMED_CIPHERS.containsKey(name.toString())) {
+ output.append(OPENSSL_RENAMED_CIPHERS.get(name.toString()));
+ } else {
+ output.append(name.toString());
+ }
}
return output.toString();
}
diff --git a/test/webapp/WEB-INF/tags/bug42390.tag b/test/webapp/WEB-INF/tags/bug42390.tag
index 97619ed5..bf6a398d 100644
--- a/test/webapp/WEB-INF/tags/bug42390.tag
+++ b/test/webapp/WEB-INF/tags/bug42390.tag
@@ -14,5 +14,5 @@
See the License for the specific language governing permissions and
limitations under the License.
--%>
-<%@ variable name-given="X" scope="AT_BEGIN" %>
+<%@ variable name-given="X" scope="AT_END" %>
<jsp:doBody/>
\ No newline at end of file
diff --git a/test/webapp/bug48nnn/bug48616b.jsp b/test/webapp/bug48nnn/bug48616b.jsp
index 31476ce6..715d8773 100644
--- a/test/webapp/bug48nnn/bug48616b.jsp
+++ b/test/webapp/bug48nnn/bug48616b.jsp
@@ -26,3 +26,6 @@
<bugs:Bug48616b />
</bugs:Bug46816a>
</tags:bug42390>
+<%
+ out.println(X);
+%>
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index ac98485c..3312a4f1 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -44,7 +44,240 @@
They eventually become mixed with the numbered issues. (I.e., numbered
issues do not "pop up" wrt. others).
-->
-<section name="Tomcat 8.5.12 (markt)">
+<section name="Tomcat 8.5.14 (markt)">
+ <subsection name="Catalina">
+ <changelog>
+ <fix>
+ <bug>59825</bug>: Log a message that lists the components in the
+ processing chain that do not support async processing when a call to
+ <code>ServletRequest.startAsync()</code> fails. (markt)
+ </fix>
+ <fix>
+ <bug>60926</bug>: Ensure
+ <code>o.a.c.core.ApplicationContextFacade#setSessionTimeout</code> will
+ invoke the correct method when running Tomcat with security manager.
+ (markt)
+ </fix>
+ <update>
+ Update the early access Servlet 4.0 API implementation to reflect the
+ change in method name from <code>getPushBuilder()</code> to
+ <code>newPushBuilder()</code>. (markt)
+ </update>
+ <fix>
+ Correct a regression in the X to comma refactoring that broke JMX
+ operations that take parameters. (markt)
+ </fix>
+ <fix>
+ Avoid a <code>NullPointerException</code> when reading attributes for a
+ running HTTP connector where TLS is not enabled. (markt)
+ </fix>
+ <fix>
+ <bug>60940</bug>: Improve the handling of the <code>META-INF/</code> and
+ <code>META-INF/MANIFEST.MF</code> entries for Jar files located in
+ <code>/WEB-INF/lib</code> when running a web application from a packed
+ WAR file. (markt)
+ </fix>
+ <fix>
+ Pre-load the <code>ExceptionUtils</code> class. Since the class is used
+ extensively in error handling, it is prudent to pre-load it to avoid any
+ failure to load this class masking the true problem during error
+ handling. (markt)
+ </fix>
+ <fix>
+ Avoid potential <code>NullPointerException</code>s related to access
+ logging during shutdown, some of which have been observed when running
+ the unit tests. (markt)
+ </fix>
+ <fix>
+ When there is no <code>javax.servlet.WriteListener</code> registered
+ then a call to <code>javax.servlet.ServletOutputStream#isReady</code>
+ will return <code>false</code> instead of throwing
+ <code>IllegalStateException</code>. (violetagg)
+ </fix>
+ <fix>
+ When there is no <code>javax.servlet.ReadListener</code> registered
+ then a call to <code>javax.servlet.ServletInputStream#isReady</code>
+ will return <code>false</code> instead of throwing
+ <code>IllegalStateException</code>. (violetagg)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Coyote">
+ <changelog>
+ <fix>
+ Align cipher configuration parsing with current OpenSSL master. (markt)
+ </fix>
+ <fix>
+ <bug>60970</bug>: Fix infinite loop if application tries to write a
+ large header to the response when using HTTP/2. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Jasper">
+ <changelog>
+ <fix>
+ <bug>60925</bug>: Improve the handling of access to properties defined
+ by interfaces when a <code>BeanELResolver</code> is used under a
+ <code>SecurityManager</code>. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="jdbc-pool">
+ <changelog>
+ <scode>
+ Refactor the creating a constructor for a proxy class to reduce
+ duplicate code. (kfujino)
+ </scode>
+ <fix>
+ In <code>StatementFacade</code>, the method call on the statements that
+ have been closed throw <code>SQLException</code> rather than
+ <code>NullPointerException</code>. (kfujino)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Other">
+ <changelog>
+ <fix>
+ Correct comments about Java 8 in <code>Jre8Compat</code>.
+ Patch provided by fibbers via Github. (violetagg)
+ </fix>
+ <fix>
+ <bug>60932</bug>: Correctly escape single quotes when used in i18n
+ messages. Based on a patch by Michael Osipov. (markt)
+ </fix>
+ <fix>
+ Update the custom Ant task that integrates with the Symantec code
+ signing service to use the now mandatory 2-factor authentication.
+ (markt)
+ </fix>
+ </changelog>
+ </subsection>
+</section>
+<section name="Tomcat 8.5.13 (markt)" rtext="2017-03-30">
+ <subsection name="Catalina">
+ <changelog>
+ <add>
+ <bug>54618</bug>: Add support to the
+ <code>HttpHeaderSecurityFilter</code> for the HSTS preload parameter.
+ (markt)
+ </add>
+ <fix>
+ <bug>60853</bug>: Expose the <code>SSLHostConfig</code> and
+ <code>SSLHostConfigCertificate</code> objects via JMX. (markt)
+ </fix>
+ <fix>
+ <bug>60876</bug>: Ensure that <code>Set-Cookie</code> headers generated
+ by the <code>Rfc6265CookieProcessor</code> are aligned with the
+ specification. Patch provided by Jim Griswold. (markt)
+ </fix>
+ <fix>
+ <bug>60882</bug>: Fix a <code>NullPointerException</code> when obtaining
+ a <code>RequestDispatcher</code> for a request that will not have any
+ pathInfo associated with it. This was a regression in the changes in
+ 8.5.12 for the Servlet 4.0 API early preview changes. (markt)
+ </fix>
+ <update>
+ Align <code>PushBuilder</code> API with changes from Servlet expert
+ group. (markt)
+ </update>
+ <scode>
+ Refactor the various implementations of X to comma separated list to a
+ single utility class and update the code to use the new utility class.
+ (markt)
+ </scode>
+ <fix>
+ <bug>60911</bug>: Ensure NPE will not be thrown when looking for SSL
+ session ID. Based on a patch by Didier Gutacker. (violetagg)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Coyote">
+ <changelog>
+ <add>
+ <bug>60362</bug>: Add a new Connector configuration
+ <code>sendReasonPhrase</code>. When this attribute is set to
+ <code>true</code>, a reason phrase will be sent with the response.
+ By default a reason phrase will not be sent. This option is deprecated
+ and is not available in Tomcat 9. (violetagg)
+ </add>
+ <fix>
+ Fix HTTP/2 incorrect input unblocking on EOF. (remm)
+ </fix>
+ <fix>
+ Close the connection sooner if an event occurs for a current connection
+ that is not consistent with the current state of that connection.
+ (markt)
+ </fix>
+ <fix>
+ Speed up shutdown when using multiple acceptor threads by ensuring that
+ the code that unlocks the acceptor threads correctly handles the case
+ where there are multiple threads. (markt)
+ </fix>
+ <fix>
+ <bug>60852</bug>: Correctly spell compressible when used in
+ configuration attributes and internal code. Based on a patch by Michael
+ Osipov. (markt)
+ </fix>
+ <fix>
+ <bug>60900</bug>: Avoid a <code>NullPointerException</code> in the APR
+ Poller if a connection is closed at the same time as new data arrives on
+ that connection. (markt)
+ </fix>
+ <fix>
+ Improve HPACK specification compliance by fixing some test failures
+ reported by the h2spec tool written by Moto Ishizawa. (markt)
+ </fix>
+ <fix>
+ Improve HTTP/2 specification compliance by fixing some test failures
+ reported by the h2spec tool written by Moto Ishizawa. (markt)
+ </fix>
+ <fix>
+ <bug>60918</bug>: Fix sendfile processing error that could lead to
+ subsequent requests experiencing an <code>IllegalStateException</code>.
+ (markt)
+ </fix>
+ <fix>
+ Improve sendfile handling when requests are pipelined. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Jasper">
+ <changelog>
+ <fix>
+ Improve the error handling for simple tags to ensure that the tag is
+ released and destroyed once used. (remm, violetagg)
+ </fix>
+ <fix>
+ <bug>60844</bug>: Correctly handle the error when fewer parameter values
+ than required by the method are used to invoke an EL method expression.
+ Patch provided by Daniel Gray. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="jdbc-pool">
+ <changelog>
+ <fix>
+ <bug>60764</bug>: Implement <code>equals()</code> and
+ <code>hashCode()</code> in the <code>StatementFacade</code> in order to
+ enable these methods to be called on the closed statements if any
+ statement proxy is set. This behavior can be changed with
+ <code>useStatementFacade</code> attribute. (kfujino)
+ </fix>
+ </changelog>
+ </subsection>
+ <subsection name="Other">
+ <changelog>
+ <fix>
+ Refactor the build script and the NSIS installer script so that either
+ NSIS 2.x or NSIS 3.x can be used to build the installer. This is
+ primarily to re-enable building the installer on the Linux based CI
+ system where the combination of NSIS 3.x and wine leads to failed
+ installer builds. (markt)
+ </fix>
+ </changelog>
+ </subsection>
+</section>
+<section name="Tomcat 8.5.12 (markt)" rtext="2017-03-13">
<subsection name="Catalina">
<changelog>
<fix>
@@ -180,12 +413,6 @@
<strong>Connector</strong>. (markt)
</fix>
<fix>
- <bug>60627</bug>: Modify the <code>Rfc6265CookieProcessor</code> so that
- in addition to cookie headers that start with an explicit RFC 2109
- <code>$Version=1</code>, cookies that start with <code>$Version=0</code>
- are also parsed as RFC 2109 cookies. (markt)
- </fix>
- <fix>
Include the value of <code>SslHostConfig.truststoreAlgorithm</code> when
warning that the algorithm does not support the
<code>certificateVerificationDepth</code> configuration option. (markt)
@@ -201,6 +428,12 @@
(csutherl)
</add>
<fix>
+ <bug>60627</bug>: Modify the <code>Rfc6265CookieProcessor</code> so that
+ in addition to cookie headers that start with an explicit RFC 2109
+ <code>$Version=1</code>, cookies that start with <code>$Version=0</code>
+ are also parsed as RFC 2109 cookies. (markt)
+ </fix>
+ <fix>
<bug>60716</bug>: Add a new JSSE specific attribute,
<code>revocationEnabled</code>, to <code>SSLHostConfig</code> to permit
JSSE provider revocation checks to be enabled when no
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
index 160acf39..68c7620b 100644
--- a/webapps/docs/config/ajp.xml
+++ b/webapps/docs/config/ajp.xml
@@ -236,6 +236,14 @@
The default value is <code>false</code>.</p>
</attribute>
+ <attribute name="sendReasonPhrase" required="false">
+ <p>Set this attribute to <code>true</code> if you wish to have
+ a reason phrase in the response.
+ The default value is <code>false</code>.</p>
+ <p><strong>Note:</strong> This option is deprecated and will be removed
+ in Tomcat 9. The reason phrase will not be sent.</p>
+ </attribute>
+
<attribute name="URIEncoding" required="false">
<p>This specifies the character encoding used to decode the URI bytes,
after %xx decoding the URL. If not specified, UTF-8 will be used unless
diff --git a/webapps/docs/config/filter.xml b/webapps/docs/config/filter.xml
index ef34436a..77d6537f 100644
--- a/webapps/docs/config/filter.xml
+++ b/webapps/docs/config/filter.xml
@@ -899,6 +899,13 @@ FINE: Request "/docs/config/manager.html" with response status "200"
be used.</p>
</attribute>
+ <attribute name="hstsPreload" required="false">
+ <p>Should the preload parameter be included in the HSTS header. If not
+ specified, the default value of <code>false</code> will be used. See
+ <a href="https://hstspreload.org/">https://hstspreload.org</a> for
+ important information about this parameter.</p>
+ </attribute>
+
<attribute name="antiClickJackingEnabled" required="false">
<p>Should the anti click-jacking header (<code>X-Frame-Options</code>)
be set on the response. Any anti click-jacking header already present
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
index d726c0c1..076c6289 100644
--- a/webapps/docs/config/http.xml
+++ b/webapps/docs/config/http.xml
@@ -236,6 +236,14 @@
The default value is <code>false</code>.</p>
</attribute>
+ <attribute name="sendReasonPhrase" required="false">
+ <p>Set this attribute to <code>true</code> if you wish to have
+ a reason phrase in the response.
+ The default value is <code>false</code>.</p>
+ <p><strong>Note:</strong> This option is deprecated and will be removed
+ in Tomcat 9. The reason phrase will not be sent.</p>
+ </attribute>
+
<attribute name="URIEncoding" required="false">
<p>This specifies the character encoding used to decode the URI bytes,
after %xx decoding the URL. If not specified, UTF-8 will be used unless
@@ -347,7 +355,7 @@
provider will be used.</p>
</attribute>
- <attribute name="compressableMimeType" required="false">
+ <attribute name="compressibleMimeType" required="false">
<p>The value is a comma separated list of MIME types for which HTTP
compression may be used.
The default value is
diff --git a/webapps/docs/config/systemprops.xml b/webapps/docs/config/systemprops.xml
index 38a2e097..3d93231f 100644
--- a/webapps/docs/config/systemprops.xml
+++ b/webapps/docs/config/systemprops.xml
@@ -553,6 +553,17 @@
<properties>
+ <property
+ name="org.apache.coyote. USE_CUSTOM_STATUS_MSG_IN_HEADER"><p>If this is
+ <code>true</code>, custom HTTP status messages will be used within HTTP
+ headers. If a custom message is specified that is not valid for use in an
+ HTTP header (as defined by RFC2616) then the custom message will be
+ ignored and the default message used.</p>
+ <p>If not specified, the default value of <code>false</code> will be used.</p>
+ <p><strong>Note:</strong> This option is deprecated and will be removed
+ in Tomcat 9. The reason phrase will not be sent.</p>
+ </property>
+
<property name="catalina.useNaming">
<p>If this is <code>false</code> it will override the
<code>useNaming</code> attribute for all <a href="context.html">
diff --git a/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java b/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
index 6bb2dae8..c78819f5 100644
--- a/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
+++ b/webapps/examples/WEB-INF/classes/http2/SimpleImagePush.java
@@ -34,13 +34,16 @@ public class SimpleImagePush extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
- PushBuilder pb = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
- req).getPushBuilder().path("servlets/images/code.gif");
- pb.push();
-
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html");
PrintWriter pw = resp.getWriter();
+
+ PushBuilder pb = ((org.apache.catalina.servlet4preview.http.HttpServletRequest)
+ req).newPushBuilder();
+
+ if (pb != null) {
+ pb.path("servlets/images/code.gif");
+ pb.push();
pw.println("<html>");
pw.println("<body>");
pw.println("<p>The following image was provided via a push request.</p>");
@@ -48,5 +51,12 @@ public class SimpleImagePush extends HttpServlet {
pw.println("</body>");
pw.println("</html>");
pw.flush();
+ } else {
+ pw.println("<html>");
+ pw.println("<body>");
+ pw.println("<p>Server push requests are not supported by this protocol.</p>");
+ pw.println("</body>");
+ pw.println("</html>");
+ }
}
}
--- End Message ---