Compare commits
10 Commits
cf37ce3b48
...
6184ee62ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6184ee62ec | ||
|
|
e2a9a8ab35 | ||
|
|
64f5c6745b | ||
|
|
d853d477ac | ||
|
|
ffe463b088 | ||
|
|
c2c21090ae | ||
|
|
af5f386bce | ||
|
|
8c89e47abb | ||
|
|
c299570657 | ||
|
|
5342785c69 |
BIN
9.0.10.tar.gz
BIN
9.0.10.tar.gz
Binary file not shown.
@ -1,27 +0,0 @@
|
||||
--- a/webapps/docs/changelog.xml 2018-06-20 13:35:40.000000000 -0400
|
||||
+++ b/webapps/docs/changelog_1.xml 2019-06-24 08:35:44.801000000 -0400
|
||||
@@ -164,6 +164,10 @@
|
||||
the authenticated Subject to include at least one Principal of a type
|
||||
specified by <code>userClassNames</code>. (markt)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ When generating a redirect to a directory in the Default Servlet, avoid
|
||||
+ generating a protocol relative redirect. (markt)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
<subsection name="Coyote">
|
||||
--- a/java/org/apache/catalina/servlets/DefaultServlet.java 2018-06-20 13:35:34.000000000 -0400
|
||||
+++ b/java/org/apache/catalina/servlets/DefaultServlet_1.java 2019-06-24 08:40:08.699000000 -0400
|
||||
@@ -1324,6 +1324,10 @@ public class DefaultServlet extends Http
|
||||
location.append('?');
|
||||
location.append(request.getQueryString());
|
||||
}
|
||||
+ // Avoid protocol relative redirects
|
||||
+ while (location.length() > 1 && location.charAt(1) == '/') {
|
||||
+ location.deleteCharAt(0);
|
||||
+ }
|
||||
response.sendRedirect(response.encodeRedirectURL(location.toString()));
|
||||
}
|
||||
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
--- tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:17:08 1852697
|
||||
+++ tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:01 1852698
|
||||
@@ -42,8 +42,10 @@
|
||||
public class Http2Protocol implements UpgradeProtocol {
|
||||
|
||||
static final long DEFAULT_READ_TIMEOUT = 10000;
|
||||
- static final long DEFAULT_KEEP_ALIVE_TIMEOUT = -1;
|
||||
static final long DEFAULT_WRITE_TIMEOUT = 10000;
|
||||
+ static final long DEFAULT_KEEP_ALIVE_TIMEOUT = -1;
|
||||
+ static final long DEFAULT_STREAM_READ_TIMEOUT = 20000;
|
||||
+ static final long DEFAULT_STREAM_WRITE_TIMEOUT = 20000;
|
||||
// The HTTP/2 specification recommends a minimum default of 100
|
||||
static final long DEFAULT_MAX_CONCURRENT_STREAMS = 200;
|
||||
// Maximum amount of streams which can be concurrently executed over
|
||||
@@ -57,9 +59,14 @@
|
||||
private static final byte[] ALPN_IDENTIFIER = ALPN_NAME.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
// All timeouts in milliseconds
|
||||
+ // These are the socket level timeouts
|
||||
private long readTimeout = DEFAULT_READ_TIMEOUT;
|
||||
- private long keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
|
||||
private long writeTimeout = DEFAULT_WRITE_TIMEOUT;
|
||||
+ private long keepAliveTimeout = DEFAULT_KEEP_ALIVE_TIMEOUT;
|
||||
+ // These are the stream level timeouts
|
||||
+ private long streamReadTimeout = DEFAULT_STREAM_READ_TIMEOUT;
|
||||
+ private long streamWriteTimeout = DEFAULT_STREAM_WRITE_TIMEOUT;
|
||||
+
|
||||
private long maxConcurrentStreams = DEFAULT_MAX_CONCURRENT_STREAMS;
|
||||
private int maxConcurrentStreamExecution = DEFAULT_MAX_CONCURRENT_STREAM_EXECUTION;
|
||||
// If a lower initial value is required, set it here but DO NOT change the
|
||||
@@ -145,6 +152,16 @@
|
||||
}
|
||||
|
||||
|
||||
+ public long getWriteTimeout() {
|
||||
+ return writeTimeout;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ public void setWriteTimeout(long writeTimeout) {
|
||||
+ this.writeTimeout = writeTimeout;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
public long getKeepAliveTimeout() {
|
||||
return keepAliveTimeout;
|
||||
}
|
||||
@@ -155,13 +172,23 @@
|
||||
}
|
||||
|
||||
|
||||
- public long getWriteTimeout() {
|
||||
- return writeTimeout;
|
||||
+ public long getStreamReadTimeout() {
|
||||
+ return streamReadTimeout;
|
||||
}
|
||||
|
||||
|
||||
- public void setWriteTimeout(long writeTimeout) {
|
||||
- this.writeTimeout = writeTimeout;
|
||||
+ public void setStreamReadTimeout(long streamReadTimeout) {
|
||||
+ this.streamReadTimeout = streamReadTimeout;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ public long getStreamWriteTimeout() {
|
||||
+ return streamWriteTimeout;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ public void setStreamWriteTimeout(long streamWriteTimeout) {
|
||||
+ this.streamWriteTimeout = streamWriteTimeout;
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java
|
||||
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java 2019-06-09 21:03:54.790000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java 2019-06-09 21:05:12.133000000 -0400
|
||||
@@ -905,7 +905,7 @@ class Stream extends AbstractStream impl
|
||||
throw new IOException(sm.getString("stream.inputBuffer.reset"));
|
||||
}
|
||||
|
||||
- if (inBuffer.position() == 0) {
|
||||
+ if (inBuffer.position() == 0 && isActive() && !isInputFinished()) {
|
||||
String msg = sm.getString("stream.inputBuffer.readTimeout");
|
||||
StreamException se = new StreamException(
|
||||
msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
--- a/java/org/apache/coyote/http2/Stream.java 2019-06-11 21:24:19.998000000 -0400
|
||||
+++ b/java/org/apache/coyote/http2/Stream_1.java 2019-06-11 21:26:18.329000000 -0400
|
||||
@@ -221,7 +221,7 @@ class Stream extends AbstractStream impl
|
||||
if (windowSize == 0) {
|
||||
String msg = sm.getString("stream.writeTimeout");
|
||||
StreamException se = new StreamException(
|
||||
- msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
|
||||
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdentifier().intValue());
|
||||
// Prevent the application making further writes
|
||||
streamOutputBuffer.closed = true;
|
||||
// Prevent Tomcat's error handling trying to write
|
||||
@@ -908,7 +908,7 @@ class Stream extends AbstractStream impl
|
||||
if (inBuffer.position() == 0 && isActive() && !isInputFinished()) {
|
||||
String msg = sm.getString("stream.inputBuffer.readTimeout");
|
||||
StreamException se = new StreamException(
|
||||
- msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
|
||||
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdentifier().intValue());
|
||||
// Trigger a reset once control returns to Tomcat
|
||||
coyoteResponse.setError();
|
||||
streamOutputBuffer.reset = se;
|
||||
|
||||
@ -1,246 +0,0 @@
|
||||
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/catalina/connector/OutputBuffer.java apache-tomcat-9.0.10-src-bak/java/org/apache/catalina/connector/OutputBuffer.java
|
||||
--- apache-tomcat-9.0.10-src/java/org/apache/catalina/connector/OutputBuffer.java 2018-06-20 13:35:33.000000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/catalina/connector/OutputBuffer.java 2019-06-09 20:28:02.836000000 -0400
|
||||
@@ -33,6 +33,7 @@ import javax.servlet.http.HttpServletRes
|
||||
|
||||
import org.apache.catalina.Globals;
|
||||
import org.apache.coyote.ActionCode;
|
||||
+import org.apache.coyote.CloseNowException;
|
||||
import org.apache.coyote.Response;
|
||||
import org.apache.tomcat.util.buf.C2BConverter;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
@@ -326,6 +327,13 @@ public class OutputBuffer extends Writer
|
||||
// real write to the adapter
|
||||
try {
|
||||
coyoteResponse.doWrite(buf);
|
||||
+ } catch (CloseNowException e) {
|
||||
+ // Catch this sub-class as it requires specific handling.
|
||||
+ // Examples where this exception is thrown:
|
||||
+ // - HTTP/2 stream timeout
|
||||
+ // Prevent further output for this response
|
||||
+ closed = true;
|
||||
+ throw e;
|
||||
} catch (IOException e) {
|
||||
// An IOException on a write is almost always due to
|
||||
// the remote client aborting the request. Wrap this
|
||||
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/catalina/core/StandardWrapperValve.java apache-tomcat-9.0.10-src-bak/java/org/apache/catalina/core/StandardWrapperValve.java
|
||||
--- apache-tomcat-9.0.10-src/java/org/apache/catalina/core/StandardWrapperValve.java 2018-06-20 13:35:34.000000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/catalina/core/StandardWrapperValve.java 2019-06-09 20:33:27.596000000 -0400
|
||||
@@ -36,6 +36,7 @@ import org.apache.catalina.connector.Cli
|
||||
import org.apache.catalina.connector.Request;
|
||||
import org.apache.catalina.connector.Response;
|
||||
import org.apache.catalina.valves.ValveBase;
|
||||
+import org.apache.coyote.CloseNowException;
|
||||
import org.apache.tomcat.util.ExceptionUtils;
|
||||
import org.apache.tomcat.util.buf.MessageBytes;
|
||||
import org.apache.tomcat.util.log.SystemLogHandler;
|
||||
@@ -201,7 +202,7 @@ final class StandardWrapperValve
|
||||
}
|
||||
|
||||
}
|
||||
- } catch (ClientAbortException e) {
|
||||
+ } catch (ClientAbortException | CloseNowException e) {
|
||||
throwable = e;
|
||||
exception(request, response, e);
|
||||
} catch (IOException e) {
|
||||
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties
|
||||
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties 2018-06-20 13:35:35.000000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties 2019-06-09 20:34:32.307000000 -0400
|
||||
@@ -93,6 +93,7 @@ stream.reset.fail=Connection [{0}], Stre
|
||||
stream.reset.receive=Connection [{0}], Stream [{1}], Reset received due to [{2}]
|
||||
stream.reset.send=Connection [{0}], Stream [{1}], Reset sent due to [{2}]
|
||||
stream.trailerHeader.noEndOfStream=Connection [{0}], Stream [{1}], The trailer headers did not include the end of stream flag
|
||||
+stream.writeTimeout=Timeout waiting for client to increase flow control window to permit stream data to be written
|
||||
|
||||
stream.inputBuffer.copy=Copying [{0}] bytes from inBuffer to outBuffer
|
||||
stream.inputBuffer.dispatch=Data added to inBuffer when read interest is registered. Triggering a read dispatch
|
||||
@@ -149,4 +150,4 @@ upgradeHandler.writeHeaders=Connection [
|
||||
upgradeHandler.writePushHeaders=Connection [{0}], Stream [{1}], Pushed stream [{2}], EndOfStream [{3}]
|
||||
|
||||
writeStateMachine.endWrite.ise=It is illegal to specify [{0}] for the new state once a write has completed
|
||||
-writeStateMachine.ise=It is illegal to call [{0}()] in state [{1}]
|
||||
\ No newline at end of file
|
||||
+writeStateMachine.ise=It is illegal to call [{0}()] in state [{1}]
|
||||
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java
|
||||
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java 2018-06-20 13:35:35.000000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:38:30.109000000 -0400
|
||||
@@ -211,7 +211,21 @@ class Stream extends AbstractStream impl
|
||||
}
|
||||
try {
|
||||
if (block) {
|
||||
- wait();
|
||||
+ wait(handler.getProtocol().getStreamWriteTimeout());
|
||||
+ windowSize = getWindowSize();
|
||||
+ if (windowSize == 0) {
|
||||
+ String msg = sm.getString("stream.writeTimeout");
|
||||
+ StreamException se = new StreamException(
|
||||
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
|
||||
+ // Prevent the application making further writes
|
||||
+ streamOutputBuffer.closed = true;
|
||||
+ // Prevent Tomcat's error handling trying to write
|
||||
+ coyoteResponse.setError();
|
||||
+ coyoteResponse.setErrorReported();
|
||||
+ // Trigger a reset once control returns to Tomcat
|
||||
+ streamOutputBuffer.reset = se;
|
||||
+ throw new CloseNowException(msg, se);
|
||||
+ }
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
@@ -221,7 +235,6 @@ class Stream extends AbstractStream impl
|
||||
// Stream.
|
||||
throw new IOException(e);
|
||||
}
|
||||
- windowSize = getWindowSize();
|
||||
}
|
||||
int allocation;
|
||||
if (windowSize < reservation) {
|
||||
@@ -660,6 +673,9 @@ class Stream extends AbstractStream impl
|
||||
return !streamOutputBuffer.endOfStreamSent;
|
||||
}
|
||||
|
||||
+ StreamException getResetException() {
|
||||
+ return streamOutputBuffer.reset;
|
||||
+ }
|
||||
|
||||
private static void push(final Http2UpgradeHandler handler, final Request request,
|
||||
final Stream stream) throws IOException {
|
||||
@@ -707,6 +723,7 @@ class Stream extends AbstractStream impl
|
||||
private final ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);
|
||||
private volatile long written = 0;
|
||||
private volatile boolean closed = false;
|
||||
+ private volatile StreamException reset = null;
|
||||
private volatile boolean endOfStreamSent = false;
|
||||
|
||||
/* The write methods are synchronized to ensure that only one thread at
|
||||
@@ -800,9 +817,14 @@ class Stream extends AbstractStream impl
|
||||
|
||||
@Override
|
||||
public final void end() throws IOException {
|
||||
- closed = true;
|
||||
- flush(true);
|
||||
- writeTrailers();
|
||||
+ if (reset != null) {
|
||||
+ throw new CloseNowException(reset);
|
||||
+ }
|
||||
+ if (!closed) {
|
||||
+ closed = true;
|
||||
+ flush(true);
|
||||
+ writeTrailers();
|
||||
+ }
|
||||
}
|
||||
|
||||
/**
|
||||
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/StreamProcessor.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/StreamProcessor.java
|
||||
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/StreamProcessor.java 2018-06-20 13:35:35.000000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/StreamProcessor.java 2019-06-09 20:40:08.789000000 -0400
|
||||
@@ -78,10 +78,13 @@ class StreamProcessor extends AbstractPr
|
||||
stream.getIdentifier()), Http2Error.INTERNAL_ERROR);
|
||||
stream.close(ce);
|
||||
} else if (!getErrorState().isIoAllowed()) {
|
||||
- StreamException se = new StreamException(sm.getString(
|
||||
- "streamProcessor.error.stream", stream.getConnectionId(),
|
||||
- stream.getIdentifier()), Http2Error.INTERNAL_ERROR,
|
||||
- stream.getIdentifier().intValue());
|
||||
+ StreamException se = stream.getResetException();
|
||||
+ if (se == null) {
|
||||
+ se = new StreamException(sm.getString(
|
||||
+ "streamProcessor.error.stream", stream.getConnectionId(),
|
||||
+ stream.getIdentifier()), Http2Error.INTERNAL_ERROR,
|
||||
+ stream.getIdentifier().intValue());
|
||||
+ }
|
||||
stream.close(se);
|
||||
}
|
||||
}
|
||||
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java
|
||||
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java 2018-06-20 13:35:38.000000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java 2019-06-09 20:41:45.113000000 -0400
|
||||
@@ -486,8 +486,10 @@ public abstract class Http2TestBase exte
|
||||
Http2Protocol http2Protocol = new Http2Protocol();
|
||||
// Short timeouts for now. May need to increase these for CI systems.
|
||||
http2Protocol.setReadTimeout(2000);
|
||||
- http2Protocol.setKeepAliveTimeout(5000);
|
||||
http2Protocol.setWriteTimeout(2000);
|
||||
+ http2Protocol.setKeepAliveTimeout(5000);
|
||||
+ http2Protocol.setStreamReadTimeout(1000);
|
||||
+ http2Protocol.setStreamWriteTimeout(1000);
|
||||
http2Protocol.setMaxConcurrentStreams(maxConcurrentStreams);
|
||||
connector.addUpgradeProtocol(http2Protocol);
|
||||
}
|
||||
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java
|
||||
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java 1969-12-31 19:00:00.000000000 -0500
|
||||
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java 2019-06-09 20:42:38.095000000 -0400
|
||||
@@ -0,0 +1,73 @@
|
||||
+/*
|
||||
+ * 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 org.junit.Assert;
|
||||
+import org.junit.Before;
|
||||
+import org.junit.Test;
|
||||
+
|
||||
+public class TestHttp2Timeouts extends Http2TestBase {
|
||||
+
|
||||
+ @Override
|
||||
+ @Before
|
||||
+ public void http2Connect() throws Exception {
|
||||
+ super.http2Connect();
|
||||
+ sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ /*
|
||||
+ * Simple request won't fill buffer so timeout will occur in Tomcat internal
|
||||
+ * code during response completion.
|
||||
+ */
|
||||
+ @Test
|
||||
+ public void testClientWithEmptyWindow() throws Exception {
|
||||
+ sendSimpleGetRequest(3);
|
||||
+
|
||||
+ // Settings
|
||||
+ parser.readFrame(false);
|
||||
+ // Headers
|
||||
+ parser.readFrame(false);
|
||||
+
|
||||
+ output.clearTrace();
|
||||
+
|
||||
+ parser.readFrame(false);
|
||||
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ /*
|
||||
+ * Large request will fill buffer so timeout will occur in application code
|
||||
+ * during response write (when Tomcat commits the response and flushes the
|
||||
+ * buffer as a result of the buffer filling).
|
||||
+ */
|
||||
+ @Test
|
||||
+ public void testClientWithEmptyWindowLargeResponse() throws Exception {
|
||||
+ sendLargeGetRequest(3);
|
||||
+
|
||||
+ // Settings
|
||||
+ parser.readFrame(false);
|
||||
+ // Headers
|
||||
+ parser.readFrame(false);
|
||||
+
|
||||
+ output.clearTrace();
|
||||
+
|
||||
+ parser.readFrame(false);
|
||||
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
@ -1,202 +0,0 @@
|
||||
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties
|
||||
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/LocalStrings.properties 2019-06-09 20:45:15.320000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/LocalStrings.properties 2019-06-09 20:46:36.793000000 -0400
|
||||
@@ -98,6 +98,7 @@ stream.writeTimeout=Timeout waiting for
|
||||
stream.inputBuffer.copy=Copying [{0}] bytes from inBuffer to outBuffer
|
||||
stream.inputBuffer.dispatch=Data added to inBuffer when read interest is registered. Triggering a read dispatch
|
||||
stream.inputBuffer.empty=The Stream input buffer is empty. Waiting for more data
|
||||
+stream.inputBuffer.readTimeout=Timeout waiting to read data from client
|
||||
stream.inputBuffer.reset=Stream reset
|
||||
stream.inputBuffer.signal=Data added to inBuffer when read thread is waiting. Signalling that thread to continue
|
||||
|
||||
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java
|
||||
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:45:15.321000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:48:21.509000000 -0400
|
||||
@@ -888,10 +888,22 @@ class Stream extends AbstractStream impl
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("stream.inputBuffer.empty"));
|
||||
}
|
||||
- inBuffer.wait();
|
||||
+
|
||||
+ inBuffer.wait(handler.getProtocol().getStreamReadTimeout());
|
||||
+
|
||||
if (reset) {
|
||||
throw new IOException(sm.getString("stream.inputBuffer.reset"));
|
||||
}
|
||||
+
|
||||
+ if (inBuffer.position() == 0) {
|
||||
+ String msg = sm.getString("stream.inputBuffer.readTimeout");
|
||||
+ StreamException se = new StreamException(
|
||||
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdAsInt());
|
||||
+ // Trigger a reset once control returns to Tomcat
|
||||
+ coyoteResponse.setError();
|
||||
+ streamOutputBuffer.reset = se;
|
||||
+ throw new CloseNowException(msg, se);
|
||||
+ }
|
||||
} catch (InterruptedException e) {
|
||||
// Possible shutdown / rst or similar. Use an
|
||||
// IOException to signal to the client that further I/O
|
||||
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java
|
||||
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/Http2TestBase.java 2019-06-09 20:45:15.323000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/Http2TestBase.java 2019-06-09 20:53:54.809000000 -0400
|
||||
@@ -28,6 +28,7 @@ import java.nio.charset.StandardCharsets
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
+import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
@@ -300,6 +301,22 @@ public abstract class Http2TestBase exte
|
||||
}
|
||||
}
|
||||
|
||||
+ protected void sendParameterPostRequest(int streamId, byte[] padding, String body,
|
||||
+ long contentLength, boolean useExpectation) throws IOException {
|
||||
+ byte[] headersFrameHeader = new byte[9];
|
||||
+ ByteBuffer headersPayload = ByteBuffer.allocate(128);
|
||||
+ byte[] dataFrameHeader = new byte[9];
|
||||
+ ByteBuffer dataPayload = ByteBuffer.allocate(128);
|
||||
+
|
||||
+ buildPostRequest(headersFrameHeader, headersPayload, useExpectation,
|
||||
+ "application/x-www-form-urlencoded", contentLength, "/parameter", dataFrameHeader,
|
||||
+ dataPayload, padding, null, null, streamId);
|
||||
+ writeFrame(headersFrameHeader, headersPayload);
|
||||
+ if (body != null) {
|
||||
+ dataPayload.put(body.getBytes(StandardCharsets.ISO_8859_1));
|
||||
+ writeFrame(dataFrameHeader, dataPayload);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
|
||||
boolean useExpectation, byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
|
||||
@@ -311,14 +328,29 @@ public abstract class Http2TestBase exte
|
||||
protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
|
||||
boolean useExpectation, byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
|
||||
byte[] trailersFrameHeader, ByteBuffer trailersPayload, int streamId) {
|
||||
+ buildPostRequest(headersFrameHeader, headersPayload, useExpectation, null, -1, "/simple",
|
||||
+ dataFrameHeader, dataPayload, padding, trailersFrameHeader, trailersPayload, streamId);
|
||||
+ }
|
||||
+
|
||||
+ protected void buildPostRequest(byte[] headersFrameHeader, ByteBuffer headersPayload,
|
||||
+ boolean useExpectation, String contentType, long contentLength, String path,
|
||||
+ byte[] dataFrameHeader, ByteBuffer dataPayload, byte[] padding,
|
||||
+ 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(":path").setString(path);
|
||||
headers.addValue(":authority").setString("localhost:" + getPort());
|
||||
if (useExpectation) {
|
||||
headers.addValue("expect").setString("100-continue");
|
||||
}
|
||||
+ if (contentType != null) {
|
||||
+ headers.addValue("content-type").setString(contentType);
|
||||
+ }
|
||||
+ if (contentLength > -1) {
|
||||
+ headers.addValue("content-length").setLong(contentLength);
|
||||
+ }
|
||||
hpackEncoder.encode(headers, headersPayload);
|
||||
|
||||
headersPayload.flip();
|
||||
@@ -507,6 +539,8 @@ public abstract class Http2TestBase exte
|
||||
ctxt.addServletMappingDecoded("/large", "large");
|
||||
Tomcat.addServlet(ctxt, "cookie", new CookieServlet());
|
||||
ctxt.addServletMappingDecoded("/cookie", "cookie");
|
||||
+ Tomcat.addServlet(ctxt, "parameter", new ParameterServlet());
|
||||
+ ctxt.addServletMappingDecoded("/parameter", "parameter");
|
||||
|
||||
tomcat.start();
|
||||
}
|
||||
@@ -1205,6 +1239,24 @@ public abstract class Http2TestBase exte
|
||||
}
|
||||
}
|
||||
|
||||
+
|
||||
+ static class ParameterServlet extends HttpServlet {
|
||||
+
|
||||
+ private static final long serialVersionUID = 1L;
|
||||
+
|
||||
+ @Override
|
||||
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
|
||||
+ throws ServletException, IOException {
|
||||
+
|
||||
+ Map<String,String[]> params = req.getParameterMap();
|
||||
+
|
||||
+ resp.setContentType("text/plain");
|
||||
+ resp.setCharacterEncoding("UTF-8");
|
||||
+
|
||||
+ resp.getWriter().print(params.size());
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
|
||||
static class SettingValue {
|
||||
private final int setting;
|
||||
diff -Nurp apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java
|
||||
--- apache-tomcat-9.0.10-src/test/org/apache/coyote/http2/TestHttp2Timeouts.java 2019-06-09 20:45:15.323000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/test/org/apache/coyote/http2/TestHttp2Timeouts.java 2019-06-09 20:57:22.652000000 -0400
|
||||
@@ -26,7 +26,6 @@ public class TestHttp2Timeouts extends H
|
||||
@Before
|
||||
public void http2Connect() throws Exception {
|
||||
super.http2Connect();
|
||||
- sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +35,7 @@ public class TestHttp2Timeouts extends H
|
||||
*/
|
||||
@Test
|
||||
public void testClientWithEmptyWindow() throws Exception {
|
||||
- sendSimpleGetRequest(3);
|
||||
+ sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
|
||||
|
||||
// Settings
|
||||
parser.readFrame(false);
|
||||
@@ -57,6 +56,7 @@ public class TestHttp2Timeouts extends H
|
||||
*/
|
||||
@Test
|
||||
public void testClientWithEmptyWindowLargeResponse() throws Exception {
|
||||
+ sendSettings(0, false, new SettingValue(Setting.INITIAL_WINDOW_SIZE.getId(), 0));
|
||||
sendLargeGetRequest(3);
|
||||
|
||||
// Settings
|
||||
@@ -70,4 +70,36 @@ public class TestHttp2Timeouts extends H
|
||||
Assert.assertEquals("3-RST-[11]\n", output.getTrace());
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Timeout with app reading request body directly.
|
||||
+ */
|
||||
+ @Test
|
||||
+ public void testClientPostsNoBody() throws Exception {
|
||||
+ sendSimplePostRequest(3, null, false);
|
||||
+
|
||||
+ // Headers
|
||||
+ parser.readFrame(false);
|
||||
+ output.clearTrace();
|
||||
+
|
||||
+ parser.readFrame(false);
|
||||
+
|
||||
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ /*
|
||||
+ * Timeout with app processing parameters.
|
||||
+ */
|
||||
+ @Test
|
||||
+ public void testClientPostsNoParameters() throws Exception {
|
||||
+ sendParameterPostRequest(3, null, null, 10, false);
|
||||
+
|
||||
+ // Headers
|
||||
+ parser.readFrame(false);
|
||||
+ output.clearTrace();
|
||||
+
|
||||
+ parser.readFrame(false);
|
||||
+
|
||||
+ Assert.assertEquals("3-RST-[11]\n", output.getTrace());
|
||||
+ }
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
--- tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:14 1852700
|
||||
+++ tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:18 1852701
|
||||
@@ -41,9 +41,9 @@
|
||||
|
||||
public class Http2Protocol implements UpgradeProtocol {
|
||||
|
||||
- static final long DEFAULT_READ_TIMEOUT = 10000;
|
||||
- static final long DEFAULT_WRITE_TIMEOUT = 10000;
|
||||
- static final long DEFAULT_KEEP_ALIVE_TIMEOUT = -1;
|
||||
+ static final long DEFAULT_READ_TIMEOUT = 5000;
|
||||
+ static final long DEFAULT_WRITE_TIMEOUT = 5000;
|
||||
+ static final long DEFAULT_KEEP_ALIVE_TIMEOUT = 20000;
|
||||
static final long DEFAULT_STREAM_READ_TIMEOUT = 20000;
|
||||
static final long DEFAULT_STREAM_WRITE_TIMEOUT = 20000;
|
||||
// The HTTP/2 specification recommends a minimum default of 100
|
||||
--- tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:14 1852700
|
||||
+++ tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:18 1852701
|
||||
@@ -329,9 +329,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
- // No more frames to read so switch to the keep-alive
|
||||
- // timeout.
|
||||
- socketWrapper.setReadTimeout(protocol.getKeepAliveTimeout());
|
||||
+
|
||||
+ if (activeRemoteStreamCount.get() == 0) {
|
||||
+ // No streams currently active. Use the keep-alive
|
||||
+ // timeout for the connection.
|
||||
+ socketWrapper.setReadTimeout(protocol.getKeepAliveTimeout());
|
||||
+ } else {
|
||||
+ // Streams currently active. Individual streams have
|
||||
+ // timeouts so keep the connection open.
|
||||
+ socketWrapper.setReadTimeout(-1);
|
||||
+ }
|
||||
} catch (Http2Exception ce) {
|
||||
// Really ConnectionException
|
||||
if (log.isDebugEnabled()) {
|
||||
|
||||
@ -1,143 +0,0 @@
|
||||
--- tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:18 1852701
|
||||
+++ tomcat/java/org/apache/coyote/http2/Http2Protocol.java 2019/02/01 10:28:22 1852702
|
||||
@@ -54,6 +54,8 @@
|
||||
// This default is defined by the HTTP/2 specification
|
||||
static final int DEFAULT_INITIAL_WINDOW_SIZE = (1 << 16) - 1;
|
||||
|
||||
+ static final int DEFAULT_OVERHEAD_COUNT_FACTOR = 1;
|
||||
+
|
||||
private static final String HTTP_UPGRADE_NAME = "h2c";
|
||||
private static final String ALPN_NAME = "h2";
|
||||
private static final byte[] ALPN_IDENTIFIER = ALPN_NAME.getBytes(StandardCharsets.UTF_8);
|
||||
@@ -79,6 +81,8 @@
|
||||
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 int overheadCountFactor = DEFAULT_OVERHEAD_COUNT_FACTOR;
|
||||
+
|
||||
private boolean initiatePingDisabled = false;
|
||||
private boolean useSendfile = true;
|
||||
// Compression
|
||||
@@ -306,6 +310,16 @@
|
||||
}
|
||||
|
||||
|
||||
+ public int getOverheadCountFactor() {
|
||||
+ return overheadCountFactor;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ public void setOverheadCountFactor(int overheadCountFactor) {
|
||||
+ this.overheadCountFactor = overheadCountFactor;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
public void setInitiatePingDisabled(boolean initiatePingDisabled) {
|
||||
this.initiatePingDisabled = initiatePingDisabled;
|
||||
}
|
||||
--- tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:18 1852701
|
||||
+++ tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:22 1852702
|
||||
@@ -30,6 +30,7 @@
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
+import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.servlet.http.WebConnection;
|
||||
@@ -139,6 +140,9 @@
|
||||
private AtomicInteger streamConcurrency = null;
|
||||
private Queue<StreamRunnable> queuedRunnable = null;
|
||||
|
||||
+ // Track 'overhead' frames vs 'request/response' frames
|
||||
+ private final AtomicLong overheadCount = new AtomicLong(-10);
|
||||
+
|
||||
|
||||
Http2UpgradeHandler(Http2Protocol protocol, Adapter adapter, Request coyoteRequest) {
|
||||
super (STREAM_ID_ZERO);
|
||||
@@ -330,6 +334,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ if (overheadCount.get() > 0) {
|
||||
+ throw new ConnectionException("Too much overhead", Http2Error.ENHANCE_YOUR_CALM);
|
||||
+ }
|
||||
+
|
||||
if (activeRemoteStreamCount.get() == 0) {
|
||||
// No streams currently active. Use the keep-alive
|
||||
// timeout for the connection.
|
||||
@@ -638,6 +646,9 @@
|
||||
log.debug(sm.getString("upgradeHandler.writeBody", connectionId, stream.getIdentifier(),
|
||||
Integer.toString(len)));
|
||||
}
|
||||
+
|
||||
+ reduceOverheadCount();
|
||||
+
|
||||
// Need to check this now since sending end of stream will change this.
|
||||
boolean writeable = stream.canWrite();
|
||||
byte[] header = new byte[9];
|
||||
@@ -1193,6 +1204,16 @@
|
||||
}
|
||||
|
||||
|
||||
+ private void reduceOverheadCount() {
|
||||
+ overheadCount.decrementAndGet();
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private void increaseOverheadCount() {
|
||||
+ overheadCount.addAndGet(getProtocol().getOverheadCountFactor());
|
||||
+ }
|
||||
+
|
||||
+
|
||||
// ----------------------------------------------- Http2Parser.Input methods
|
||||
|
||||
@Override
|
||||
@@ -1247,6 +1268,7 @@
|
||||
|
||||
@Override
|
||||
public ByteBuffer startRequestBodyFrame(int streamId, int payloadSize) throws Http2Exception {
|
||||
+ reduceOverheadCount();
|
||||
Stream stream = getStream(streamId, true);
|
||||
stream.checkState(FrameType.DATA);
|
||||
stream.receivedData(payloadSize);
|
||||
@@ -1291,6 +1313,8 @@
|
||||
// determines if a new stream is created or if this stream is ignored.
|
||||
checkPauseState();
|
||||
|
||||
+ reduceOverheadCount();
|
||||
+
|
||||
if (connectionState.get().isNewStreamAllowed()) {
|
||||
Stream stream = getStream(streamId, false);
|
||||
if (stream == null) {
|
||||
@@ -1340,6 +1364,9 @@
|
||||
throw new ConnectionException(sm.getString("upgradeHandler.dependency.invalid",
|
||||
getConnectionId(), Integer.valueOf(streamId)), Http2Error.PROTOCOL_ERROR);
|
||||
}
|
||||
+
|
||||
+ increaseOverheadCount();
|
||||
+
|
||||
Stream stream = getStream(streamId, false);
|
||||
if (stream == null) {
|
||||
stream = createRemoteStream(streamId);
|
||||
@@ -1384,6 +1411,9 @@
|
||||
|
||||
@Override
|
||||
public void setting(Setting setting, long value) throws ConnectionException {
|
||||
+
|
||||
+ increaseOverheadCount();
|
||||
+
|
||||
// Special handling required
|
||||
if (setting == Setting.INITIAL_WINDOW_SIZE) {
|
||||
long oldValue = remoteSettings.getInitialWindowSize();
|
||||
@@ -1425,6 +1455,9 @@
|
||||
|
||||
@Override
|
||||
public void pingReceive(byte[] payload, boolean ack) throws IOException {
|
||||
+ if (!ack) {
|
||||
+ increaseOverheadCount();
|
||||
+ }
|
||||
pingManager.receivePing(payload, ack);
|
||||
}
|
||||
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
--- tomcat/webapps/docs/config/http2.xml 2019/02/01 10:28:22 1852702
|
||||
+++ tomcat/webapps/docs/config/http2.xml 2019/02/01 10:28:26 1852703
|
||||
@@ -125,9 +125,9 @@
|
||||
|
||||
<attribute name="keepAliveTimeout" required="false">
|
||||
<p>The time, in milliseconds, that Tomcat will wait between HTTP/2 frames
|
||||
- before closing the connection. Negative values will be treated as an
|
||||
- infinite timeout. If not specified, a default value of <code>-1</code>
|
||||
- will be used.</p>
|
||||
+ when there is no active Stream before closing the connection. Negative
|
||||
+ values will be treated as an infinite timeout. If not specified, a default
|
||||
+ value of <code>20000</code> will be used.</p>
|
||||
</attribute>
|
||||
|
||||
<attribute name="maxConcurrentStreamExecution" required="false">
|
||||
@@ -192,7 +192,24 @@
|
||||
<p>The time, in milliseconds, that Tomcat will wait for additional data
|
||||
when a partial HTTP/2 frame has been received. Negative values will be
|
||||
treated as an infinite timeout. If not specified, a default value of
|
||||
- <code>10000</code> will be used.</p>
|
||||
+ <code>5000</code> will be used.</p>
|
||||
+ </attribute>
|
||||
+
|
||||
+ <attribute name="streamReadTimeout" required="false">
|
||||
+ <p>The time, in milliseconds, that Tomcat will wait for additional data
|
||||
+ frames to arrive for the stream when an application is performing a
|
||||
+ blocking I/O read and additional data is required. Negative values will be
|
||||
+ treated as an infinite timeout. If not specified, a default value of
|
||||
+ <code>20000</code> will be used.</p>
|
||||
+ </attribute>
|
||||
+
|
||||
+ <attribute name="streamWriteTimeout" required="false">
|
||||
+ <p>The time, in milliseconds, that Tomcat will wait for additional window
|
||||
+ update frames to arrive for the stream and/or conenction when an
|
||||
+ application is performing a blocking I/O write and the stream and/or
|
||||
+ connection flow control window is too small for the write to complete.
|
||||
+ Negative values will be treated as an infinite timeout. If not specified,
|
||||
+ a default value of <code>20000</code> will be used.</p>
|
||||
</attribute>
|
||||
|
||||
<attribute name="useSendfile" required="false">
|
||||
@@ -204,7 +221,7 @@
|
||||
<p>The time, in milliseconds, that Tomcat will wait to write additional
|
||||
data when an HTTP/2 frame has been partially written. Negative values will
|
||||
be treated as an infinite timeout. If not specified, a default value of
|
||||
- <code>10000</code> will be used.</p>
|
||||
+ <code>5000</code> will be used.</p>
|
||||
</attribute>
|
||||
|
||||
</attributes>
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
diff -Nurp apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java
|
||||
--- apache-tomcat-9.0.10-src/java/org/apache/coyote/http2/Stream.java 2019-06-09 20:59:53.027000000 -0400
|
||||
+++ apache-tomcat-9.0.10-src-bak/java/org/apache/coyote/http2/Stream.java 2019-06-09 21:02:31.878000000 -0400
|
||||
@@ -211,7 +211,12 @@ class Stream extends AbstractStream impl
|
||||
}
|
||||
try {
|
||||
if (block) {
|
||||
- wait(handler.getProtocol().getStreamWriteTimeout());
|
||||
+ long writeTimeout = handler.getProtocol().getStreamWriteTimeout();
|
||||
+ if (writeTimeout < 0) {
|
||||
+ wait();
|
||||
+ } else {
|
||||
+ wait(writeTimeout);
|
||||
+ }
|
||||
windowSize = getWindowSize();
|
||||
if (windowSize == 0) {
|
||||
String msg = sm.getString("stream.writeTimeout");
|
||||
@@ -889,7 +894,12 @@ class Stream extends AbstractStream impl
|
||||
log.debug(sm.getString("stream.inputBuffer.empty"));
|
||||
}
|
||||
|
||||
- inBuffer.wait(handler.getProtocol().getStreamReadTimeout());
|
||||
+ long readTimeout = handler.getProtocol().getStreamReadTimeout();
|
||||
+ if (readTimeout < 0) {
|
||||
+ inBuffer.wait();
|
||||
+ } else {
|
||||
+ inBuffer.wait(readTimeout);
|
||||
+ }
|
||||
|
||||
if (reset) {
|
||||
throw new IOException(sm.getString("stream.inputBuffer.reset"));
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
--- tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:30 1852704
|
||||
+++ tomcat/java/org/apache/coyote/http2/Http2UpgradeHandler.java 2019/02/01 10:28:34 1852705
|
||||
@@ -335,7 +335,9 @@
|
||||
}
|
||||
|
||||
if (overheadCount.get() > 0) {
|
||||
- throw new ConnectionException("Too much overhead", Http2Error.ENHANCE_YOUR_CALM);
|
||||
+ throw new ConnectionException(
|
||||
+ sm.getString("upgradeHandler.tooMuchOverhead", connectionId),
|
||||
+ Http2Error.ENHANCE_YOUR_CALM);
|
||||
}
|
||||
|
||||
if (activeRemoteStreamCount.get() == 0) {
|
||||
--- tomcat/java/org/apache/coyote/http2/LocalStrings.properties 2019/02/01 10:28:30 1852704
|
||||
+++ tomcat/java/org/apache/coyote/http2/LocalStrings.properties 2019/02/01 10:28:34 1852705
|
||||
@@ -141,6 +141,7 @@
|
||||
upgradeHandler.stream.notWritable=Connection [{0}], Stream [{1}], This stream is not writable
|
||||
upgradeHandler.stream.old=A new remote stream ID of [{0}] was requested but the most recent stream was [{1}]
|
||||
upgradeHandler.tooManyRemoteStreams=The client attempted to use more than [{0}] active streams
|
||||
+upgradeHandler.tooMuchOverhead=Connection [{0}], Too much overhead so the connection will be closed
|
||||
upgradeHandler.unexpectedAck=Connection [{0}], Stream [{1}], A settings acknowledgement was received when not expected
|
||||
upgradeHandler.unexpectedEos=Unexpected end of stream
|
||||
upgradeHandler.upgrade=Connection [{0}], HTTP/1.1 upgrade to stream [1]
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
--- tomcat/webapps/docs/config/http2.xml 2019/02/01 10:28:34 1852705
|
||||
+++ tomcat/webapps/docs/config/http2.xml 2019/02/01 10:28:38 1852706
|
||||
@@ -188,6 +188,20 @@
|
||||
The default value is an empty String (regexp matching disabled).</p>
|
||||
</attribute>
|
||||
|
||||
+ <attribute name="overheadCountFactor" required="false">
|
||||
+ <p>The factor to apply when counting overhead frames to determine if a
|
||||
+ connection has too high an overhead and should be closed. The overhead
|
||||
+ count starts at <code>-10</code>. The count is decreased for each
|
||||
+ data frame sent or received and each headers frame received. The count is
|
||||
+ increased by the <code>overheadCountFactor</code>for each setting
|
||||
+ received, priority frame received and ping received. If the overhead count
|
||||
+ exceeds zero, the connection is closed. A value of less than
|
||||
+ <code>1</code> disables this protection. In normal usage a value of
|
||||
+ <code>3</code> or more will close the connection before any streams can
|
||||
+ complete. If not specified, a default value of <code>1</code> will be
|
||||
+ used.</p>
|
||||
+ </attribute>
|
||||
+
|
||||
<attribute name="readTimeout" required="false">
|
||||
<p>The time, in milliseconds, that Tomcat will wait for additional data
|
||||
when a partial HTTP/2 frame has been received. Negative values will be
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
From 15fcd166ea2c1bb79e8541b8e1a43da9c452ceea Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 11 Mar 2019 11:33:03 +0000
|
||||
Subject: [PATCH] Escape debug output to aid readability
|
||||
|
||||
reason: Escape debug output to aid readability, fix CVE CVE-2019-0221
|
||||
https://github.com/apache/tomcat/commit/15fcd16
|
||||
|
||||
---
|
||||
java/org/apache/catalina/ssi/SSIPrintenv.java | 3 +--
|
||||
webapps/docs/changelog.xml | 3 +++
|
||||
2 files changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/ssi/SSIPrintenv.java b/java/org/apache/catalina/ssi/SSIPrintenv.java
|
||||
index 97470b2..092542f 100644
|
||||
--- a/java/org/apache/catalina/ssi/SSIPrintenv.java
|
||||
+++ b/java/org/apache/catalina/ssi/SSIPrintenv.java
|
||||
@@ -41,8 +41,7 @@ public class SSIPrintenv implements SSICommand {
|
||||
} else {
|
||||
Collection<String> variableNames = ssiMediator.getVariableNames();
|
||||
for (String variableName : variableNames) {
|
||||
- String variableValue = ssiMediator
|
||||
- .getVariableValue(variableName);
|
||||
+ String variableValue = ssiMediator.getVariableValue(variableName, "entity");
|
||||
//This shouldn't happen, since all the variable names must
|
||||
// have values
|
||||
if (variableValue == null) {
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 697cf07..cbd3961 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -52,6 +52,9 @@
|
||||
<code>Expires</code> header as required by HTTP specification
|
||||
(RFC 7231, 7234). (kkolinko)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ Encode the output of the SSI <code>printenv</code> command. (markt)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
</section>
|
||||
--
|
||||
1.8.3.1
|
||||
|
||||
@ -1,129 +0,0 @@
|
||||
From 7f748eb6bfaba5207c89dbd7d5adf50fae847145 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 30 Apr 2019 22:18:12 +0100
|
||||
Subject: [PATCH] Expand HTTP/2 timeout handling to connection window
|
||||
exhaustion on write.
|
||||
|
||||
https://github.com/apache/tomcat/commit/7f748eb
|
||||
---
|
||||
.../coyote/http2/Http2UpgradeHandler.java | 32 +++++++++++++++++--
|
||||
java/org/apache/coyote/http2/Stream.java | 27 +++++++++-------
|
||||
webapps/docs/changelog.xml | 4 +++
|
||||
3 files changed, 50 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
index 1d8770a..ab0369a 100644
|
||||
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
@@ -794,7 +794,26 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
}
|
||||
if (allocation == 0) {
|
||||
try {
|
||||
- stream.wait();
|
||||
+ // Connection level window is empty. Although this
|
||||
+ // request is for a stream, use the connection
|
||||
+ // timeout
|
||||
+ long writeTimeout = protocol.getWriteTimeout();
|
||||
+ if (writeTimeout < 0) {
|
||||
+ stream.wait();
|
||||
+ } else {
|
||||
+ stream.wait(writeTimeout);
|
||||
+ }
|
||||
+ // Has this stream been granted an allocation
|
||||
+ int[] value = backLogStreams.get(stream);
|
||||
+ if (value[1] == 0) {
|
||||
+ // No allocation
|
||||
+ // Close the connection. Do this first since
|
||||
+ // closing the stream will raise an exception
|
||||
+ close();
|
||||
+ // Close the stream (in app code so need to
|
||||
+ // signal to app stream is closing)
|
||||
+ stream.doWriteTimeout();
|
||||
+ }
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException(sm.getString(
|
||||
"upgradeHandler.windowSizeReservationInterrupted", connectionId,
|
||||
@@ -985,11 +1004,20 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
|
||||
|
||||
private void close() {
|
||||
- connectionState.set(ConnectionState.CLOSED);
|
||||
+ ConnectionState previous = connectionState.getAndSet(ConnectionState.CLOSED);
|
||||
+ if (previous == ConnectionState.CLOSED) {
|
||||
+ // Already closed
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
for (Stream stream : streams.values()) {
|
||||
// The connection is closing. Close the associated streams as no
|
||||
// longer required.
|
||||
stream.receiveReset(Http2Error.CANCEL.getCode());
|
||||
+ // Release any streams waiting for an allocation
|
||||
+ synchronized (stream) {
|
||||
+ stream.notifyAll();
|
||||
+ }
|
||||
}
|
||||
try {
|
||||
socketWrapper.close();
|
||||
diff --git a/java/org/apache/coyote/http2/Stream.java b/java/org/apache/coyote/http2/Stream.java
|
||||
index 2c4f67e..8b87b12 100644
|
||||
--- a/java/org/apache/coyote/http2/Stream.java
|
||||
+++ b/java/org/apache/coyote/http2/Stream.java
|
||||
@@ -219,17 +219,7 @@ class Stream extends AbstractStream implements HeaderEmitter {
|
||||
}
|
||||
windowSize = getWindowSize();
|
||||
if (windowSize == 0) {
|
||||
- String msg = sm.getString("stream.writeTimeout");
|
||||
- StreamException se = new StreamException(
|
||||
- msg, Http2Error.ENHANCE_YOUR_CALM, getIdentifier().intValue());
|
||||
- // Prevent the application making further writes
|
||||
- streamOutputBuffer.closed = true;
|
||||
- // Prevent Tomcat's error handling trying to write
|
||||
- coyoteResponse.setError();
|
||||
- coyoteResponse.setErrorReported();
|
||||
- // Trigger a reset once control returns to Tomcat
|
||||
- streamOutputBuffer.reset = se;
|
||||
- throw new CloseNowException(msg, se);
|
||||
+ doWriteTimeout();
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
@@ -252,6 +242,21 @@ class Stream extends AbstractStream implements HeaderEmitter {
|
||||
}
|
||||
|
||||
|
||||
+ void doWriteTimeout() throws CloseNowException {
|
||||
+ String msg = sm.getString("stream.writeTimeout");
|
||||
+ StreamException se = new StreamException(
|
||||
+ msg, Http2Error.ENHANCE_YOUR_CALM, getIdentifier().intValue());
|
||||
+ // Prevent the application making further writes
|
||||
+ streamOutputBuffer.closed = true;
|
||||
+ // Prevent Tomcat's error handling trying to write
|
||||
+ coyoteResponse.setError();
|
||||
+ coyoteResponse.setErrorReported();
|
||||
+ // Trigger a reset once control returns to Tomcat
|
||||
+ streamOutputBuffer.reset = se;
|
||||
+ throw new CloseNowException(msg, se);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
@Override
|
||||
public final void emitHeader(String name, String value) throws HpackException {
|
||||
if (log.isDebugEnabled()) {
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index a8abf2d..5665df4 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -362,6 +362,10 @@
|
||||
<update>
|
||||
Update the internal fork of Commons DBCP 2 to 2.4.0. (markt)
|
||||
</update>
|
||||
+ <fix>
|
||||
+ Expand HTTP/2 timeout handling to include connection window exhaustion
|
||||
+ on write. (markt)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
</section>
|
||||
--
|
||||
2.19.1
|
||||
@ -1,28 +0,0 @@
|
||||
From ada725a50a60867af3422c8e612aecaeea856a9a Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Fri, 3 May 2019 21:52:41 +0100
|
||||
Subject: [PATCH] Fix test failures. Handle full allocation case.
|
||||
|
||||
https://github.com/apache/tomcat/commit/ada725a
|
||||
---
|
||||
java/org/apache/coyote/http2/Http2UpgradeHandler.java | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
index ab0369a..cadae44 100644
|
||||
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
@@ -804,8 +804,10 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
stream.wait(writeTimeout);
|
||||
}
|
||||
// Has this stream been granted an allocation
|
||||
+ // Note: If the stream in not in this Map then the
|
||||
+ // requested write has been fully allocated
|
||||
int[] value = backLogStreams.get(stream);
|
||||
- if (value[1] == 0) {
|
||||
+ if (value != null && value[1] == 0) {
|
||||
// No allocation
|
||||
// Close the connection. Do this first since
|
||||
// closing the stream will raise an exception
|
||||
--
|
||||
2.19.1
|
||||
@ -1,112 +0,0 @@
|
||||
From 1fc9f589dbdd8295cf313b2667ab041c425f99c3 Mon Sep 17 00:00:00 2001
|
||||
From: remm <remm@apache.org>
|
||||
Date: Thu, 14 Nov 2019 13:39:31 +0100
|
||||
Subject: [PATCH] Refactor JMX remote RMI registry creation
|
||||
|
||||
---
|
||||
.../mbeans/JmxRemoteLifecycleListener.java | 65 ++++++++++++++-----
|
||||
1 file changed, 49 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
|
||||
index ae04294..e832935 100644
|
||||
--- a/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
|
||||
+++ b/java/org/apache/catalina/mbeans/JmxRemoteLifecycleListener.java
|
||||
@@ -25,10 +25,11 @@ import java.net.MalformedURLException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
+import java.rmi.AccessException;
|
||||
import java.rmi.AlreadyBoundException;
|
||||
+import java.rmi.NotBoundException;
|
||||
+import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
-import java.rmi.registry.LocateRegistry;
|
||||
-import java.rmi.registry.Registry;
|
||||
import java.rmi.server.RMIClientSocketFactory;
|
||||
import java.rmi.server.RMIServerSocketFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
@@ -301,18 +302,6 @@ public class JmxRemoteLifecycleListener implements LifecycleListener {
|
||||
RMIClientSocketFactory registryCsf, RMIServerSocketFactory registrySsf,
|
||||
RMIClientSocketFactory serverCsf, RMIServerSocketFactory serverSsf) {
|
||||
|
||||
- // Create the RMI registry
|
||||
- Registry registry;
|
||||
- try {
|
||||
- registry = LocateRegistry.createRegistry(
|
||||
- theRmiRegistryPort, registryCsf, registrySsf);
|
||||
- } catch (RemoteException e) {
|
||||
- log.error(sm.getString(
|
||||
- "jmxRemoteLifecycleListener.createRegistryFailed",
|
||||
- serverName, Integer.toString(theRmiRegistryPort)), e);
|
||||
- return null;
|
||||
- }
|
||||
-
|
||||
if (bindAddress == null) {
|
||||
bindAddress = "localhost";
|
||||
}
|
||||
@@ -333,11 +322,20 @@ public class JmxRemoteLifecycleListener implements LifecycleListener {
|
||||
cs = new RMIConnectorServer(serviceUrl, theEnv, server,
|
||||
ManagementFactory.getPlatformMBeanServer());
|
||||
cs.start();
|
||||
- registry.bind("jmxrmi", server.toStub());
|
||||
+ Remote jmxServer = server.toStub();
|
||||
+ // Create the RMI registry
|
||||
+ try {
|
||||
+ new JmxRegistry(theRmiRegistryPort, registryCsf, registrySsf, "jmxrmi", jmxServer);
|
||||
+ } catch (RemoteException e) {
|
||||
+ log.error(sm.getString(
|
||||
+ "jmxRemoteLifecycleListener.createRegistryFailed",
|
||||
+ serverName, Integer.toString(theRmiRegistryPort)), e);
|
||||
+ return null;
|
||||
+ }
|
||||
log.info(sm.getString("jmxRemoteLifecycleListener.start",
|
||||
Integer.toString(theRmiRegistryPort),
|
||||
Integer.toString(theRmiServerPort), serverName));
|
||||
- } catch (IOException | AlreadyBoundException e) {
|
||||
+ } catch (IOException e) {
|
||||
log.error(sm.getString(
|
||||
"jmxRemoteLifecycleListener.createServerFailed",
|
||||
serverName), e);
|
||||
@@ -493,4 +491,39 @@ public class JmxRemoteLifecycleListener implements LifecycleListener {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+
|
||||
+
|
||||
+ private static class JmxRegistry extends sun.rmi.registry.RegistryImpl {
|
||||
+ private static final long serialVersionUID = -3772054804656428217L;
|
||||
+ private final String jmxName;
|
||||
+ private final Remote jmxServer;
|
||||
+ public JmxRegistry(int port, RMIClientSocketFactory csf,
|
||||
+ RMIServerSocketFactory ssf, String jmxName, Remote jmxServer) throws RemoteException {
|
||||
+ super(port, csf, ssf);
|
||||
+ this.jmxName = jmxName;
|
||||
+ this.jmxServer = jmxServer;
|
||||
+ }
|
||||
+ @Override
|
||||
+ public Remote lookup(String name)
|
||||
+ throws RemoteException, NotBoundException {
|
||||
+ return (jmxName.equals(name)) ? jmxServer : null;
|
||||
+ }
|
||||
+ @Override
|
||||
+ public void bind(String name, Remote obj)
|
||||
+ throws RemoteException, AlreadyBoundException, AccessException {
|
||||
+ }
|
||||
+ @Override
|
||||
+ public void unbind(String name)
|
||||
+ throws RemoteException, NotBoundException, AccessException {
|
||||
+ }
|
||||
+ @Override
|
||||
+ public void rebind(String name, Remote obj)
|
||||
+ throws RemoteException, AccessException {
|
||||
+ }
|
||||
+ @Override
|
||||
+ public String[] list() throws RemoteException {
|
||||
+ return new String[] { jmxName };
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
}
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,137 +0,0 @@
|
||||
From fabfa49abf917e126dbcf299fed40a1ab96d6f7a Mon Sep 17 00:00:00 2001
|
||||
From: wang_yue111 <wangyue92@huawei.com>
|
||||
Date: Fri, 15 May 2020 17:17:57 +0800
|
||||
Subject: [PATCH] 2
|
||||
|
||||
---
|
||||
.../authenticator/AuthenticatorBase.java | 7 ++--
|
||||
.../catalina/authenticator/Constants.java | 3 ++
|
||||
.../authenticator/FormAuthenticator.java | 36 +++++--------------
|
||||
3 files changed, 16 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/authenticator/AuthenticatorBase.java b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
|
||||
index 880ebde..47d562b 100644
|
||||
--- a/java/org/apache/catalina/authenticator/AuthenticatorBase.java
|
||||
+++ b/java/org/apache/catalina/authenticator/AuthenticatorBase.java
|
||||
@@ -1021,10 +1021,11 @@ public abstract class AuthenticatorBase extends ValveBase
|
||||
}
|
||||
|
||||
// Cache the authentication information in our session, if any
|
||||
- if (cache) {
|
||||
- if (session != null) {
|
||||
+ if (session != null) {
|
||||
+ if (cache) {
|
||||
session.setAuthType(authType);
|
||||
session.setPrincipal(principal);
|
||||
+ } else {
|
||||
if (username != null) {
|
||||
session.setNote(Constants.SESS_USERNAME_NOTE, username);
|
||||
} else {
|
||||
diff --git a/java/org/apache/catalina/authenticator/Constants.java b/java/org/apache/catalina/authenticator/Constants.java
|
||||
index 452a4f0..c9580d6 100644
|
||||
--- a/java/org/apache/catalina/authenticator/Constants.java
|
||||
+++ b/java/org/apache/catalina/authenticator/Constants.java
|
||||
@@ -93,7 +93,10 @@ public class Constants {
|
||||
|
||||
/**
|
||||
* The previously authenticated principal (if caching is disabled).
|
||||
+ *
|
||||
+ * @deprecated Unused. Will be removed in Tomcat 10.
|
||||
*/
|
||||
+ @Deprecated
|
||||
public static final String FORM_PRINCIPAL_NOTE =
|
||||
"org.apache.catalina.authenticator.PRINCIPAL";
|
||||
|
||||
diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java b/java/org/apache/catalina/authenticator/FormAuthenticator.java
|
||||
index 1b54ddd..44c783e 100644
|
||||
--- a/java/org/apache/catalina/authenticator/FormAuthenticator.java
|
||||
+++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java
|
||||
@@ -133,10 +133,6 @@ public class FormAuthenticator
|
||||
protected boolean doAuthenticate(Request request, HttpServletResponse response)
|
||||
throws IOException {
|
||||
|
||||
- if (checkForCachedAuthentication(request, response, true)) {
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
// References to objects we will need later
|
||||
Session session = null;
|
||||
Principal principal = null;
|
||||
@@ -158,11 +154,8 @@ public class FormAuthenticator
|
||||
principal =
|
||||
context.getRealm().authenticate(username, password);
|
||||
if (principal != null) {
|
||||
- session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
|
||||
+ register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
|
||||
if (!matchRequest(request)) {
|
||||
- register(request, response, principal,
|
||||
- HttpServletRequest.FORM_AUTH,
|
||||
- username, password);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -181,17 +174,6 @@ public class FormAuthenticator
|
||||
+ session.getIdInternal()
|
||||
+ "'");
|
||||
}
|
||||
- principal = (Principal)
|
||||
- session.getNote(Constants.FORM_PRINCIPAL_NOTE);
|
||||
- register(request, response, principal, HttpServletRequest.FORM_AUTH,
|
||||
- (String) session.getNote(Constants.SESS_USERNAME_NOTE),
|
||||
- (String) session.getNote(Constants.SESS_PASSWORD_NOTE));
|
||||
- // If we're caching principals we no longer need the username
|
||||
- // and password in the session, so remove them
|
||||
- if (cache) {
|
||||
- session.removeNote(Constants.SESS_USERNAME_NOTE);
|
||||
- session.removeNote(Constants.SESS_PASSWORD_NOTE);
|
||||
- }
|
||||
if (restoreRequest(request, session)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Proceed to restored request");
|
||||
@@ -206,6 +188,12 @@ public class FormAuthenticator
|
||||
}
|
||||
}
|
||||
|
||||
+ // This check has to be after the previous check for a matching request
|
||||
+ // because that matching request may also include a cached Principal.
|
||||
+ if (checkForCachedAuthentication(request, response, true)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
// Acquire references to objects we will need to evaluate
|
||||
String contextPath = request.getContextPath();
|
||||
String requestURI = request.getDecodedRequestURI();
|
||||
@@ -297,12 +285,7 @@ public class FormAuthenticator
|
||||
return false;
|
||||
}
|
||||
|
||||
- // Save the authenticated Principal in our session
|
||||
- session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
|
||||
-
|
||||
- // Save the username and password as well
|
||||
- session.setNote(Constants.SESS_USERNAME_NOTE, username);
|
||||
- session.setNote(Constants.SESS_PASSWORD_NOTE, password);
|
||||
+ register(request, response, principal, HttpServletRequest.FORM_AUTH, username, password);
|
||||
|
||||
// Redirect the user to the original request URI (which will cause
|
||||
// the original request to be restored)
|
||||
@@ -510,7 +493,7 @@ public class FormAuthenticator
|
||||
}
|
||||
|
||||
// Is there a saved principal?
|
||||
- if (session.getNote(Constants.FORM_PRINCIPAL_NOTE) == null) {
|
||||
+ if (cache && session.getPrincipal() == null || !cache && request.getPrincipal() == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -541,7 +524,6 @@ public class FormAuthenticator
|
||||
SavedRequest saved = (SavedRequest)
|
||||
session.getNote(Constants.FORM_REQUEST_NOTE);
|
||||
session.removeNote(Constants.FORM_REQUEST_NOTE);
|
||||
- session.removeNote(Constants.FORM_PRINCIPAL_NOTE);
|
||||
if (saved == null) {
|
||||
return false;
|
||||
}
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,100 +0,0 @@
|
||||
From 9a0231683a77e2957cea0fdee88b193b30b0c976 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Fri, 22 May 2020 11:27:49 +0100
|
||||
Subject: [PATCH] Fix BZ 64467. Improve performance of closing idle streams
|
||||
|
||||
---
|
||||
.../coyote/http2/Http2UpgradeHandler.java | 10 +++---
|
||||
.../coyote/http2/TestHttp2Section_5_1.java | 31 ++++++++++++++++---
|
||||
webapps/docs/changelog.xml | 4 +++
|
||||
3 files changed, 36 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
index bd836940fb..f0d5f27bda 100644
|
||||
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
@@ -1343,11 +1343,11 @@ public HeaderEmitter headersStart(int streamId, boolean headersEndStream)
|
||||
}
|
||||
|
||||
|
||||
- private void closeIdleStreams(int newMaxActiveRemoteStreamId) throws Http2Exception {
|
||||
- for (int i = maxActiveRemoteStreamId + 2; i < newMaxActiveRemoteStreamId; i += 2) {
|
||||
- Stream stream = getStream(i, false);
|
||||
- if (stream != null) {
|
||||
- stream.closeIfIdle();
|
||||
+ private void closeIdleStreams(int newMaxActiveRemoteStreamId) {
|
||||
+ for (Entry<Integer,Stream> entry : streams.entrySet()) {
|
||||
+ if (entry.getKey().intValue() > maxActiveRemoteStreamId &&
|
||||
+ entry.getKey().intValue() < newMaxActiveRemoteStreamId) {
|
||||
+ entry.getValue().closeIfIdle();
|
||||
}
|
||||
}
|
||||
maxActiveRemoteStreamId = newMaxActiveRemoteStreamId;
|
||||
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_5_1.java b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
|
||||
index 2a466814e1..f878653ecf 100644
|
||||
--- a/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
|
||||
+++ b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
|
||||
@@ -147,21 +147,44 @@ public void testClientSendOldStream() throws Exception {
|
||||
|
||||
@Test
|
||||
public void testImplicitClose() throws Exception {
|
||||
+ doTestImplicitClose(5);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ // https://bz.apache.org/bugzilla/show_bug.cgi?id=64467
|
||||
+ @Test
|
||||
+ public void testImplicitCloseLargeId() throws Exception {
|
||||
+ doTestImplicitClose(Integer.MAX_VALUE - 8);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private void doTestImplicitClose(int lastStreamId) throws Exception {
|
||||
+
|
||||
+ long startFirst = System.nanoTime();
|
||||
http2Connect();
|
||||
+ long durationFirst = System.nanoTime() - startFirst;
|
||||
|
||||
sendPriority(3, 0, 16);
|
||||
- sendPriority(5, 0, 16);
|
||||
+ sendPriority(lastStreamId, 0, 16);
|
||||
|
||||
- sendSimpleGetRequest(5);
|
||||
+ long startSecond = System.nanoTime();
|
||||
+ sendSimpleGetRequest(lastStreamId);
|
||||
readSimpleGetResponse();
|
||||
- Assert.assertEquals(getSimpleResponseTrace(5), output.getTrace());
|
||||
+ long durationSecond = System.nanoTime() - startSecond;
|
||||
+
|
||||
+ Assert.assertEquals(getSimpleResponseTrace(lastStreamId), output.getTrace());
|
||||
output.clearTrace();
|
||||
|
||||
+ // Allow second request to take up to 5 times first request or up to 1 second - whichever is the larger - mainly
|
||||
+ // to allow for CI systems under load that can exhibit significant timing variation.
|
||||
+ Assert.assertTrue("First request took [" + durationFirst/1000000 + "ms], second request took [" +
|
||||
+ durationSecond/1000000 + "ms]", durationSecond < 1000000000 || durationSecond < durationFirst * 3);
|
||||
+
|
||||
// Should trigger an error since stream 3 should have been implicitly
|
||||
// closed.
|
||||
sendSimpleGetRequest(3);
|
||||
|
||||
- handleGoAwayResponse(5);
|
||||
+ handleGoAwayResponse(lastStreamId);
|
||||
}
|
||||
|
||||
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 5665df4..7b81937 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -1803,6 +1803,10 @@
|
||||
HTTP 205 responses. Additional fix to r1795278. Based on a patch
|
||||
provided by Alexandr Saperov. (violetagg)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ <bug>64467</bug>: Improve performance of closing idle HTTP/2 streams.
|
||||
+ (markt)
|
||||
+ </fix>
|
||||
<update>
|
||||
<bug>61345</bug>: Add a server listener that can be used to do system
|
||||
property replacement from the property source configured in the
|
||||
@ -1,53 +0,0 @@
|
||||
From 172977f04a5215128f1e278a688983dcd230f399 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Fri, 26 Jun 2020 12:49:50 +0100
|
||||
Subject: [PATCH] Ensure HTTP/1.1 processor is recycled after a direct h2c
|
||||
connection
|
||||
|
||||
---
|
||||
java/org/apache/coyote/AbstractProtocol.java | 9 ++++++---
|
||||
webapps/docs/changelog.xml | 4 ++++
|
||||
2 files changed, 10 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
|
||||
index cb326dc12e..5bc2212549 100644
|
||||
--- a/java/org/apache/coyote/AbstractProtocol.java
|
||||
+++ b/java/org/apache/coyote/AbstractProtocol.java
|
||||
@@ -772,8 +772,10 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
|
||||
// Assume direct HTTP/2 connection
|
||||
UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c");
|
||||
if (upgradeProtocol != null) {
|
||||
- processor = upgradeProtocol.getProcessor(
|
||||
- wrapper, getProtocol().getAdapter());
|
||||
+ // Release the Http11 processor to be re-used
|
||||
+ release(processor);
|
||||
+ // Create the upgrade processor
|
||||
+ processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
|
||||
wrapper.unRead(leftOverInput);
|
||||
// Associate with the processor with the connection
|
||||
connections.put(socket, processor);
|
||||
@@ -785,7 +785,8 @@ public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
|
||||
"abstractConnectionHandler.negotiatedProcessor.fail",
|
||||
"h2c"));
|
||||
}
|
||||
- return SocketState.CLOSED;
|
||||
+ // Exit loop and trigger appropriate clean-up
|
||||
+ state = SocketState.CLOSED;
|
||||
}
|
||||
} else {
|
||||
HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler();
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 5665df4..60cd317 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -232,6 +236,10 @@
|
||||
<fix>
|
||||
Avoid unnecessary processing of async timeouts. (markt)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ Ensure that the HTTP/1.1 processor is correctly recycled when a direct
|
||||
+ connection to h2c is made. (markt)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
<subsection name="Jasper">
|
||||
@ -1,61 +0,0 @@
|
||||
From 40fa74c74822711ab878079d0a69f7357926723d Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 29 Jun 2020 14:02:59 +0100
|
||||
Subject: [PATCH] Fix BZ 64563 - additional payload length validation
|
||||
|
||||
https://bz.apache.org/bugzilla/show_bug.cgi?id=64563
|
||||
---
|
||||
java/org/apache/tomcat/websocket/LocalStrings.properties | 1 +
|
||||
java/org/apache/tomcat/websocket/WsFrameBase.java | 7 +++++++
|
||||
webapps/docs/changelog.xml | 8 ++++++++
|
||||
3 files changed, 16 insertions(+)
|
||||
|
||||
diff --git a/java/org/apache/tomcat/websocket/LocalStrings.properties b/java/org/apache/tomcat/websocket/LocalStrings.properties
|
||||
index 9412ffeb61..929822d94c 100644
|
||||
--- a/java/org/apache/tomcat/websocket/LocalStrings.properties
|
||||
+++ b/java/org/apache/tomcat/websocket/LocalStrings.properties
|
||||
@@ -70,6 +70,7 @@ wsFrame.noContinuation=A new message was started when a continuation frame was e
|
||||
wsFrame.notMasked=The client frame was not masked but all client frames must be masked
|
||||
wsFrame.oneByteCloseCode=The client sent a close frame with a single byte payload which is not valid
|
||||
wsFrame.partialHeaderComplete=WebSocket frame received. fin [{0}], rsv [{1}], OpCode [{2}], payload length [{3}]
|
||||
+wsFrame.payloadMsbInvalid=An invalid WebSocket frame was received - the most significant bit of a 64-bit payload was illegally set
|
||||
wsFrame.sessionClosed=The client data cannot be processed because the session has already been closed
|
||||
wsFrame.suspendRequested=Suspend of the message receiving has already been requested.
|
||||
wsFrame.textMessageTooBig=The decoded text message was too big for the output buffer and the endpoint does not support partial messages
|
||||
diff --git a/java/org/apache/tomcat/websocket/WsFrameBase.java b/java/org/apache/tomcat/websocket/WsFrameBase.java
|
||||
index 28cdc30036..4afad67534 100644
|
||||
--- a/java/org/apache/tomcat/websocket/WsFrameBase.java
|
||||
+++ b/java/org/apache/tomcat/websocket/WsFrameBase.java
|
||||
@@ -261,6 +261,13 @@ private boolean processRemainingHeader() throws IOException {
|
||||
} else if (payloadLength == 127) {
|
||||
payloadLength = byteArrayToLong(inputBuffer.array(),
|
||||
inputBuffer.arrayOffset() + inputBuffer.position(), 8);
|
||||
+ // The most significant bit of those 8 bytes is required to be zero
|
||||
+ // (see RFC 6455, section 5.2). If the most significant bit is set,
|
||||
+ // the resulting payload length will be negative so test for that.
|
||||
+ if (payloadLength < 0) {
|
||||
+ throw new WsIOException(
|
||||
+ new CloseReason(CloseCodes.PROTOCOL_ERROR, sm.getString("wsFrame.payloadMsbInvalid")));
|
||||
+ }
|
||||
inputBuffer.position(inputBuffer.position() + 8);
|
||||
}
|
||||
if (Util.isControl(opCode)) {
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index e75f367171..1d1a735c7e 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -127,6 +127,14 @@
|
||||
</fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
+ <subsection name="WebSocket">
|
||||
+ <changelog>
|
||||
+ <fix>
|
||||
+ <bug>64563</bug>: Add additional validation of payload length for
|
||||
+ WebSocket messages. (markt)
|
||||
+ </fix>
|
||||
+ </changelog>
|
||||
+ </subsection>
|
||||
<subsection name="Other">
|
||||
<changelog>
|
||||
<fix>
|
||||
@ -1,97 +0,0 @@
|
||||
From 863b18e34f12085820ad02e86ca0ef7e961bb471 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 7 Aug 2019 18:59:07 +0100
|
||||
Subject: [PATCH 1/5] Extend the timeout functionality to internal upgrade
|
||||
processors
|
||||
|
||||
---
|
||||
.../coyote/http11/upgrade/InternalHttpUpgradeHandler.java | 2 ++
|
||||
.../coyote/http11/upgrade/UpgradeProcessorInternal.java | 6 ++++++
|
||||
java/org/apache/coyote/http2/Http2UpgradeHandler.java | 6 ++++++
|
||||
.../tomcat/websocket/server/WsHttpUpgradeHandler.java | 6 ++++++
|
||||
.../coyote/http11/upgrade/TestUpgradeInternalHandler.java | 5 +++++
|
||||
5 files changed, 25 insertions(+)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java b/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
|
||||
index 936784e20c..426b1bdb89 100644
|
||||
--- a/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
|
||||
+++ b/java/org/apache/coyote/http11/upgrade/InternalHttpUpgradeHandler.java
|
||||
@@ -32,6 +32,8 @@ public interface InternalHttpUpgradeHandler extends HttpUpgradeHandler {
|
||||
|
||||
SocketState upgradeDispatch(SocketEvent status);
|
||||
|
||||
+ void timeoutAsync(long now);
|
||||
+
|
||||
void setSocketWrapper(SocketWrapperBase<?> wrapper);
|
||||
|
||||
void setSslSupport(SSLSupport sslSupport);
|
||||
diff --git a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
|
||||
index 6397a72a53..f0f546072d 100644
|
||||
--- a/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
|
||||
+++ b/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java
|
||||
@@ -73,6 +73,12 @@ public class UpgradeProcessorInternal extends UpgradeProcessorBase {
|
||||
}
|
||||
|
||||
|
||||
+ @Override
|
||||
+ public void timeoutAsync(long now) {
|
||||
+ internalHttpUpgradeHandler.timeoutAsync(now);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
// --------------------------------------------------- AutoCloseable methods
|
||||
|
||||
@Override
|
||||
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
index 6206f5f5f2..da724652aa 100644
|
||||
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
@@ -388,6 +388,12 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
}
|
||||
|
||||
|
||||
+ @Override
|
||||
+ public void timeoutAsync(long now) {
|
||||
+ // TODO: Implement improved connection timeouts
|
||||
+ }
|
||||
+
|
||||
+
|
||||
ConnectionSettingsRemote getRemoteSettings() {
|
||||
return remoteSettings;
|
||||
}
|
||||
diff --git a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
|
||||
index 0cde0e3672..5dd1c5a68c 100644
|
||||
--- a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
|
||||
+++ b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
|
||||
@@ -189,6 +189,12 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
|
||||
}
|
||||
|
||||
|
||||
+ @Override
|
||||
+ public void timeoutAsync(long now) {
|
||||
+ // NO-OP
|
||||
+ }
|
||||
+
|
||||
+
|
||||
@Override
|
||||
public void pause() {
|
||||
// NO-OP
|
||||
diff --git a/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java b/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java
|
||||
index 367f06f5b3..183a8dac7f 100644
|
||||
--- a/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java
|
||||
+++ b/test/org/apache/coyote/http11/upgrade/TestUpgradeInternalHandler.java
|
||||
@@ -256,6 +256,11 @@ public class TestUpgradeInternalHandler extends TomcatBaseTest {
|
||||
return SocketState.UPGRADED;
|
||||
}
|
||||
|
||||
+ @Override
|
||||
+ public void timeoutAsync(long now) {
|
||||
+ // NO-OP
|
||||
+ }
|
||||
+
|
||||
@Override
|
||||
public void setSocketWrapper(SocketWrapperBase<?> wrapper) {
|
||||
this.wrapper = wrapper;
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
From 38ef1f624aaf045458b6fe055742fa680a96a9e1 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Thu, 7 Mar 2019 10:50:05 +0000
|
||||
Subject: [PATCH 2/5] Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63223
|
||||
|
||||
---
|
||||
java/org/apache/coyote/http2/Http2UpgradeHandler.java | 8 ++++++++
|
||||
java/org/apache/coyote/http2/Stream.java | 5 +++++
|
||||
java/org/apache/coyote/http2/StreamStateMachine.java | 8 +++++++-
|
||||
3 files changed, 20 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
index da724652aa..2330d12e09 100644
|
||||
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
@@ -555,6 +555,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
synchronized (socketWrapper) {
|
||||
doWriteHeaders(stream, pushedStreamId, mimeHeaders, endOfStream, payloadSize);
|
||||
}
|
||||
+ stream.sentHeaders();
|
||||
if (endOfStream) {
|
||||
stream.sentEndOfStream();
|
||||
}
|
||||
@@ -1178,6 +1179,13 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
|
||||
|
||||
void push(Request request, Stream associatedStream) throws IOException {
|
||||
+ if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
|
||||
+ // If there are too many open streams, simply ignore the push
|
||||
+ // request.
|
||||
+ activeRemoteStreamCount.decrementAndGet();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
Stream pushStream;
|
||||
|
||||
// Synchronized since PUSH_PROMISE frames have to be sent in order. Once
|
||||
diff --git a/java/org/apache/coyote/http2/Stream.java b/java/org/apache/coyote/http2/Stream.java
|
||||
index 43aee9d656..629d0210b4 100644
|
||||
--- a/java/org/apache/coyote/http2/Stream.java
|
||||
+++ b/java/org/apache/coyote/http2/Stream.java
|
||||
@@ -561,6 +561,11 @@ class Stream extends AbstractStream implements HeaderEmitter {
|
||||
}
|
||||
|
||||
|
||||
+ final void sentHeaders() {
|
||||
+ state.sentHeaders();
|
||||
+ }
|
||||
+
|
||||
+
|
||||
final void sentEndOfStream() {
|
||||
streamOutputBuffer.endOfStreamSent = true;
|
||||
state.sentEndOfStream();
|
||||
diff --git a/java/org/apache/coyote/http2/StreamStateMachine.java b/java/org/apache/coyote/http2/StreamStateMachine.java
|
||||
index 3b67f865d3..d19bb0a255 100644
|
||||
--- a/java/org/apache/coyote/http2/StreamStateMachine.java
|
||||
+++ b/java/org/apache/coyote/http2/StreamStateMachine.java
|
||||
@@ -53,6 +53,12 @@ class StreamStateMachine {
|
||||
}
|
||||
|
||||
|
||||
+ final synchronized void sentHeaders() {
|
||||
+ // No change if currently OPEN
|
||||
+ stateChange(State.RESERVED_LOCAL, State.HALF_CLOSED_REMOTE);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
final synchronized void receivedStartOfHeaders() {
|
||||
stateChange(State.IDLE, State.OPEN);
|
||||
stateChange(State.RESERVED_REMOTE, State.HALF_CLOSED_LOCAL);
|
||||
@@ -170,7 +176,7 @@ class StreamStateMachine {
|
||||
Http2Error.PROTOCOL_ERROR, FrameType.PRIORITY,
|
||||
FrameType.RST,
|
||||
FrameType.WINDOW_UPDATE),
|
||||
- RESERVED_REMOTE (false, false, true, true,
|
||||
+ RESERVED_REMOTE (false, true, true, true,
|
||||
Http2Error.PROTOCOL_ERROR, FrameType.HEADERS,
|
||||
FrameType.PRIORITY,
|
||||
FrameType.RST),
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,220 +0,0 @@
|
||||
From 5d7f2eac857cc75757cfc58d003fbf17a23c2720 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 7 Aug 2019 17:02:37 +0100
|
||||
Subject: [PATCH 3/5] Improve HTTP/2 connection timeout handling
|
||||
|
||||
---
|
||||
.../http2/Http2AsyncUpgradeHandler.java | 6 +-
|
||||
.../coyote/http2/Http2UpgradeHandler.java | 93 ++++++++++++++-----
|
||||
2 files changed, 73 insertions(+), 26 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java b/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java
|
||||
index ba49986b5b..107d4bedd2 100644
|
||||
--- a/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java
|
||||
+++ b/java/org/apache/coyote/http2/Http2AsyncUpgradeHandler.java
|
||||
@@ -191,7 +191,7 @@ public class Http2AsyncUpgradeHandler extends Http2UpgradeHandler {
|
||||
header[4] = FLAG_END_OF_STREAM;
|
||||
stream.sentEndOfStream();
|
||||
if (!stream.isActive()) {
|
||||
- activeRemoteStreamCount.decrementAndGet();
|
||||
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
|
||||
}
|
||||
}
|
||||
if (writeable) {
|
||||
@@ -297,7 +297,7 @@ public class Http2AsyncUpgradeHandler extends Http2UpgradeHandler {
|
||||
header[4] = FLAG_END_OF_STREAM;
|
||||
sendfile.stream.sentEndOfStream();
|
||||
if (!sendfile.stream.isActive()) {
|
||||
- activeRemoteStreamCount.decrementAndGet();
|
||||
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
|
||||
}
|
||||
}
|
||||
if (writeable) {
|
||||
@@ -358,7 +358,7 @@ public class Http2AsyncUpgradeHandler extends Http2UpgradeHandler {
|
||||
header[4] = FLAG_END_OF_STREAM;
|
||||
sendfile.stream.sentEndOfStream();
|
||||
if (!sendfile.stream.isActive()) {
|
||||
- activeRemoteStreamCount.decrementAndGet();
|
||||
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
|
||||
}
|
||||
}
|
||||
if (writeable) {
|
||||
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
index 2330d12e09..9c18bf0ca8 100644
|
||||
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
@@ -133,6 +133,9 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
// Tracking for when the connection is blocked (windowSize < 1)
|
||||
private final Map<AbstractStream,int[]> backLogStreams = new ConcurrentHashMap<>();
|
||||
private long backLogSize = 0;
|
||||
+ // The time at which the connection will timeout unless data arrives before
|
||||
+ // then. -1 means no timeout.
|
||||
+ private volatile long connectionTimeout = -1;
|
||||
|
||||
// Stream concurrency control
|
||||
private AtomicInteger streamConcurrency = null;
|
||||
@@ -313,8 +316,10 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
case OPEN_READ:
|
||||
try {
|
||||
// There is data to read so use the read timeout while
|
||||
- // reading frames.
|
||||
+ // reading frames ...
|
||||
socketWrapper.setReadTimeout(protocol.getReadTimeout());
|
||||
+ // ... and disable the connection timeout
|
||||
+ setConnectionTimeout(-1);
|
||||
while (true) {
|
||||
try {
|
||||
if (!parser.readFrame(false)) {
|
||||
@@ -330,23 +335,22 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
stream.close(se);
|
||||
}
|
||||
}
|
||||
+ if (overheadCount.get() > 0) {
|
||||
+ throw new ConnectionException(
|
||||
+ sm.getString("upgradeHandler.tooMuchOverhead", connectionId),
|
||||
+ Http2Error.ENHANCE_YOUR_CALM);
|
||||
+ }
|
||||
}
|
||||
|
||||
- if (overheadCount.get() > 0) {
|
||||
- throw new ConnectionException(
|
||||
- sm.getString("upgradeHandler.tooMuchOverhead", connectionId),
|
||||
- Http2Error.ENHANCE_YOUR_CALM);
|
||||
- }
|
||||
+ // Need to know the correct timeout before starting the read
|
||||
+ // but that may not be known at this time if one or more
|
||||
+ // requests are currently being processed so don't set a
|
||||
+ // timeout for the socket...
|
||||
+ socketWrapper.setReadTimeout(-1);
|
||||
+
|
||||
+ // ...set a timeout on the connection
|
||||
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.get());
|
||||
|
||||
- if (activeRemoteStreamCount.get() == 0) {
|
||||
- // No streams currently active. Use the keep-alive
|
||||
- // timeout for the connection.
|
||||
- socketWrapper.setReadTimeout(protocol.getKeepAliveTimeout());
|
||||
- } else {
|
||||
- // Streams currently active. Individual streams have
|
||||
- // timeouts so keep the connection open.
|
||||
- socketWrapper.setReadTimeout(-1);
|
||||
- }
|
||||
} catch (Http2Exception ce) {
|
||||
// Really ConnectionException
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -367,9 +371,12 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
result = SocketState.UPGRADED;
|
||||
break;
|
||||
|
||||
+ case TIMEOUT:
|
||||
+ closeConnection(null);
|
||||
+ break;
|
||||
+
|
||||
case DISCONNECT:
|
||||
case ERROR:
|
||||
- case TIMEOUT:
|
||||
case STOP:
|
||||
close();
|
||||
break;
|
||||
@@ -388,9 +395,41 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
}
|
||||
|
||||
|
||||
+ /*
|
||||
+ * Sets the connection timeout based on the current number of active
|
||||
+ * streams.
|
||||
+ */
|
||||
+ protected void setConnectionTimeoutForStreamCount(int streamCount) {
|
||||
+ if (streamCount == 0) {
|
||||
+ // No streams currently active. Use the keep-alive
|
||||
+ // timeout for the connection.
|
||||
+ long keepAliveTimeout = protocol.getKeepAliveTimeout();
|
||||
+ if (keepAliveTimeout == -1) {
|
||||
+ setConnectionTimeout(-1);
|
||||
+ } else {
|
||||
+ setConnectionTimeout(System.currentTimeMillis() + keepAliveTimeout);
|
||||
+ }
|
||||
+ } else {
|
||||
+ // Streams currently active. Individual streams have
|
||||
+ // timeouts so keep the connection open.
|
||||
+ setConnectionTimeout(-1);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private void setConnectionTimeout(long connectionTimeout) {
|
||||
+ this.connectionTimeout = connectionTimeout;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
@Override
|
||||
public void timeoutAsync(long now) {
|
||||
- // TODO: Implement improved connection timeouts
|
||||
+ long connectionTimeout = this.connectionTimeout;
|
||||
+ if (now == -1 || connectionTimeout > -1 && now > connectionTimeout) {
|
||||
+ // Have to dispatch as this will be executed from a non-container
|
||||
+ // thread.
|
||||
+ socketWrapper.processSocket(SocketEvent.TIMEOUT, true);
|
||||
+ }
|
||||
}
|
||||
|
||||
|
||||
@@ -499,9 +538,17 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
|
||||
|
||||
void closeConnection(Http2Exception ce) {
|
||||
+ long code;
|
||||
+ byte[] msg;
|
||||
+ if (ce == null) {
|
||||
+ code = Http2Error.NO_ERROR.getCode();
|
||||
+ msg = null;
|
||||
+ } else {
|
||||
+ code = ce.getError().getCode();
|
||||
+ msg = ce.getMessage().getBytes(StandardCharsets.UTF_8);
|
||||
+ }
|
||||
try {
|
||||
- writeGoAwayFrame(maxProcessedStreamId, ce.getError().getCode(),
|
||||
- ce.getMessage().getBytes(StandardCharsets.UTF_8));
|
||||
+ writeGoAwayFrame(maxProcessedStreamId, code, msg);
|
||||
} catch (IOException ioe) {
|
||||
// Ignore. GOAWAY is sent on a best efforts basis and the original
|
||||
// error has already been logged.
|
||||
@@ -665,7 +712,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
header[4] = FLAG_END_OF_STREAM;
|
||||
stream.sentEndOfStream();
|
||||
if (!stream.isActive()) {
|
||||
- activeRemoteStreamCount.decrementAndGet();
|
||||
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
|
||||
}
|
||||
}
|
||||
if (writeable) {
|
||||
@@ -1182,7 +1229,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
|
||||
// If there are too many open streams, simply ignore the push
|
||||
// request.
|
||||
- activeRemoteStreamCount.decrementAndGet();
|
||||
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1301,7 +1348,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
if (stream != null) {
|
||||
stream.receivedEndOfStream();
|
||||
if (!stream.isActive()) {
|
||||
- activeRemoteStreamCount.decrementAndGet();
|
||||
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1340,7 +1387,7 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
stream.receivedStartOfHeaders(headersEndStream);
|
||||
closeIdleStreams(streamId);
|
||||
if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
|
||||
- activeRemoteStreamCount.decrementAndGet();
|
||||
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
|
||||
throw new StreamException(sm.getString("upgradeHandler.tooManyRemoteStreams",
|
||||
Long.toString(localSettings.getMaxConcurrentStreams())),
|
||||
Http2Error.REFUSED_STREAM, streamId);
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,114 +0,0 @@
|
||||
From 902356dc34097b50eb6ca3a74443be466e991a77 Mon Sep 17 00:00:00 2001
|
||||
From: wangxiao65 <287608437@qq.com>
|
||||
Date: Wed, 2 Dec 2020 11:07:21 +0800
|
||||
Subject: [PATCH] CVE-2020-13943
|
||||
|
||||
---
|
||||
java/org/apache/coyote/http2/Http2Parser.java | 2 +-
|
||||
.../coyote/http2/Http2UpgradeHandler.java | 20 ++++++++++++-------
|
||||
.../coyote/http2/TestHttp2Section_5_1.java | 20 ++++++++++++-------
|
||||
3 files changed, 27 insertions(+), 15 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http2/Http2Parser.java b/java/org/apache/coyote/http2/Http2Parser.java
|
||||
index f5e19865b2..6ec659d1e0 100644
|
||||
--- a/java/org/apache/coyote/http2/Http2Parser.java
|
||||
+++ b/java/org/apache/coyote/http2/Http2Parser.java
|
||||
@@ -721,7 +721,7 @@ class Http2Parser {
|
||||
// Header frames
|
||||
HeaderEmitter headersStart(int streamId, boolean headersEndStream)
|
||||
throws Http2Exception, IOException;
|
||||
- void headersEnd(int streamId) throws ConnectionException;
|
||||
+ void headersEnd(int streamId) throws Http2Exception;
|
||||
|
||||
// Priority frames (also headers)
|
||||
void reprioritise(int streamId, int parentStreamId, boolean exclusive, int weight)
|
||||
diff --git a/java/org/apache/coyote/http2/Http2UpgradeHandler.java b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
index 9c18bf0ca8..af10cd0838 100644
|
||||
--- a/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
+++ b/java/org/apache/coyote/http2/Http2UpgradeHandler.java
|
||||
@@ -1386,12 +1386,6 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
stream.checkState(FrameType.HEADERS);
|
||||
stream.receivedStartOfHeaders(headersEndStream);
|
||||
closeIdleStreams(streamId);
|
||||
- if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
|
||||
- setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
|
||||
- throw new StreamException(sm.getString("upgradeHandler.tooManyRemoteStreams",
|
||||
- Long.toString(localSettings.getMaxConcurrentStreams())),
|
||||
- Http2Error.REFUSED_STREAM, streamId);
|
||||
- }
|
||||
return stream;
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -1439,12 +1433,24 @@ class Http2UpgradeHandler extends AbstractStream implements InternalHttpUpgradeH
|
||||
|
||||
|
||||
@Override
|
||||
- public void headersEnd(int streamId) throws ConnectionException {
|
||||
+ public void headersEnd(int streamId) throws Http2Exception {
|
||||
Stream stream = getStream(streamId, connectionState.get().isNewStreamAllowed());
|
||||
if (stream != null) {
|
||||
setMaxProcessedStream(streamId);
|
||||
if (stream.isActive()) {
|
||||
if (stream.receivedEndOfHeaders()) {
|
||||
+
|
||||
+ if (localSettings.getMaxConcurrentStreams() < activeRemoteStreamCount.incrementAndGet()) {
|
||||
+ setConnectionTimeoutForStreamCount(activeRemoteStreamCount.decrementAndGet());
|
||||
+ // Ignoring maxConcurrentStreams increases the overhead count
|
||||
+ increaseOverheadCount();
|
||||
+ throw new StreamException(sm.getString("upgradeHandler.tooManyRemoteStreams",
|
||||
+ Long.toString(localSettings.getMaxConcurrentStreams())),
|
||||
+ Http2Error.REFUSED_STREAM, streamId);
|
||||
+ }
|
||||
+ // Valid new stream reduces the overhead count
|
||||
+ reduceOverheadCount();
|
||||
+
|
||||
processStreamOnContainerThread(stream);
|
||||
}
|
||||
}
|
||||
diff --git a/test/org/apache/coyote/http2/TestHttp2Section_5_1.java b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
|
||||
index f878653ecf..0b937a9fb9 100644
|
||||
--- a/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
|
||||
+++ b/test/org/apache/coyote/http2/TestHttp2Section_5_1.java
|
||||
@@ -222,11 +222,11 @@ public class TestHttp2Section_5_1 extends Http2TestBase {
|
||||
// Expecting
|
||||
// 1 * headers
|
||||
// 56k-1 of body (7 * ~8k)
|
||||
- // 1 * error (could be in any order)
|
||||
- for (int i = 0; i < 8; i++) {
|
||||
+ // 1 * error
|
||||
+ // for a total of 9 frames (could be in any order)
|
||||
+ for (int i = 0; i < 9; i++) {
|
||||
parser.readFrame(true);
|
||||
}
|
||||
- parser.readFrame(true);
|
||||
|
||||
Assert.assertTrue(output.getTrace(),
|
||||
output.getTrace().contains("5-RST-[" +
|
||||
@@ -238,14 +238,20 @@ public class TestHttp2Section_5_1 extends Http2TestBase {
|
||||
|
||||
// Release the remaining body
|
||||
sendWindowUpdate(0, (1 << 31) - 2);
|
||||
- // Allow for the 8k still in the stream window
|
||||
+ // Allow for the ~8k still in the stream window
|
||||
sendWindowUpdate(3, (1 << 31) - 8193);
|
||||
|
||||
- // 192k of body (24 * 8k)
|
||||
- // 1 * error (could be in any order)
|
||||
- for (int i = 0; i < 24; i++) {
|
||||
+ // Read until the end of stream 3
|
||||
+ while (!output.getTrace().contains("3-EndOfStream")) {
|
||||
parser.readFrame(true);
|
||||
}
|
||||
+ output.clearTrace();
|
||||
+
|
||||
+ // Confirm another request can be sent once concurrency falls back below limit
|
||||
+ sendSimpleGetRequest(7);
|
||||
+ parser.readFrame(true);
|
||||
+ parser.readFrame(true);
|
||||
+ Assert.assertEquals(getSimpleResponseTrace(7), output.getTrace());
|
||||
}
|
||||
|
||||
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
From d56293f816d6dc9e2b47107f208fa9e95db58c65 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 9 Nov 2020 19:23:12 +0000
|
||||
Subject: [PATCH] Fix BZ 64830 - concurrency issue in HPACK decoder
|
||||
|
||||
https://bz.apache.org/bugzilla/show_bug.cgi?id=64830
|
||||
---
|
||||
java/org/apache/coyote/http2/HpackDecoder.java | 12 ++++--------
|
||||
1 files changed, 4 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http2/HpackDecoder.java b/java/org/apache/coyote/http2/HpackDecoder.java
|
||||
index 551101b33a..517dc0b4ae 100644
|
||||
--- a/java/org/apache/coyote/http2/HpackDecoder.java
|
||||
+++ b/java/org/apache/coyote/http2/HpackDecoder.java
|
||||
@@ -72,8 +72,6 @@
|
||||
private volatile boolean countedCookie;
|
||||
private volatile int headerSize = 0;
|
||||
|
||||
- private final StringBuilder stringBuilder = new StringBuilder();
|
||||
-
|
||||
HpackDecoder(int maxMemorySize) {
|
||||
this.maxMemorySizeHard = maxMemorySize;
|
||||
this.maxMemorySizeSoft = maxMemorySize;
|
||||
@@ -222,19 +220,17 @@ private String readHpackString(ByteBuffer buffer) throws HpackException {
|
||||
if (huffman) {
|
||||
return readHuffmanString(length, buffer);
|
||||
}
|
||||
+ StringBuilder stringBuilder = new StringBuilder(length);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
stringBuilder.append((char) buffer.get());
|
||||
}
|
||||
- String ret = stringBuilder.toString();
|
||||
- stringBuilder.setLength(0);
|
||||
- return ret;
|
||||
+ return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private String readHuffmanString(int length, ByteBuffer buffer) throws HpackException {
|
||||
+ StringBuilder stringBuilder = new StringBuilder(length);
|
||||
HPackHuffman.decode(buffer, length, stringBuilder);
|
||||
- String ret = stringBuilder.toString();
|
||||
- stringBuilder.setLength(0);
|
||||
- return ret;
|
||||
+ return stringBuilder.toString();
|
||||
}
|
||||
|
||||
private String handleIndexedHeaderName(int index) throws HpackException {
|
||||
@ -1,443 +0,0 @@
|
||||
From 8bfb0ff7f25fe7555a5eb2f7984f73546c11aa26 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 6 Jan 2020 20:53:25 +0000
|
||||
Subject: [PATCH] Stricter header value parsing
|
||||
|
||||
---
|
||||
.../coyote/http11/AbstractHttp11Protocol.java | 51 ++++++++++---
|
||||
.../coyote/http11/Http11InputBuffer.java | 51 +++++++++----
|
||||
.../apache/coyote/http11/Http11Processor.java | 2 +-
|
||||
.../apache/tomcat/util/http/MimeHeaders.java | 2 +-
|
||||
.../tomcat/util/http/parser/HttpParser.java | 11 +++
|
||||
.../coyote/http11/TestHttp11InputBuffer.java | 74 +++++++++++++++----
|
||||
webapps/docs/config/http.xml | 11 ++-
|
||||
8 files changed, 164 insertions(+), 43 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
|
||||
index e5ab885..9d10cbf 100644
|
||||
--- a/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
|
||||
+++ b/java/org/apache/coyote/http11/AbstractHttp11Protocol.java
|
||||
@@ -136,27 +136,56 @@ public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
|
||||
}
|
||||
|
||||
|
||||
- private boolean rejectIllegalHeaderName = true;
|
||||
+ private boolean rejectIllegalHeader = true;
|
||||
/**
|
||||
- * If an HTTP request is received that contains an illegal header name (i.e.
|
||||
- * the header name is not a token) will the request be rejected (with a 400
|
||||
- * response) or will the illegal header be ignored.
|
||||
+ * If an HTTP request is received that contains an illegal header name or
|
||||
+ * value (e.g. the header name is not a token) will the request be rejected
|
||||
+ * (with a 400 response) or will the illegal header be ignored?
|
||||
*
|
||||
* @return {@code true} if the request will be rejected or {@code false} if
|
||||
* the header will be ignored
|
||||
*/
|
||||
- public boolean getRejectIllegalHeaderName() { return rejectIllegalHeaderName; }
|
||||
+ public boolean getRejectIllegalHeader() { return rejectIllegalHeader; }
|
||||
/**
|
||||
- * If an HTTP request is received that contains an illegal header name (i.e.
|
||||
- * the header name is not a token) should the request be rejected (with a
|
||||
- * 400 response) or should the illegal header be ignored.
|
||||
+ * If an HTTP request is received that contains an illegal header name or
|
||||
+ * value (e.g. the header name is not a token) should the request be
|
||||
+ * rejected (with a 400 response) or should the illegal header be ignored?
|
||||
+ *
|
||||
+ * @param rejectIllegalHeader {@code true} to reject requests with illegal
|
||||
+ * header names or values, {@code false} to
|
||||
+ * ignore the header
|
||||
+ */
|
||||
+ public void setRejectIllegalHeader(boolean rejectIllegalHeader) {
|
||||
+ this.rejectIllegalHeader = rejectIllegalHeader;
|
||||
+ }
|
||||
+ /**
|
||||
+ * If an HTTP request is received that contains an illegal header name or
|
||||
+ * value (e.g. the header name is not a token) will the request be rejected
|
||||
+ * (with a 400 response) or will the illegal header be ignored?
|
||||
+ *
|
||||
+ * @return {@code true} if the request will be rejected or {@code false} if
|
||||
+ * the header will be ignored
|
||||
+ *
|
||||
+ * @deprecated Now an alias for {@link #getRejectIllegalHeader()}. Will be
|
||||
+ * removed in Tomcat 10 onwards.
|
||||
+ */
|
||||
+ @Deprecated
|
||||
+ public boolean getRejectIllegalHeaderName() { return rejectIllegalHeader; }
|
||||
+ /**
|
||||
+ * If an HTTP request is received that contains an illegal header name or
|
||||
+ * value (e.g. the header name is not a token) should the request be
|
||||
+ * rejected (with a 400 response) or should the illegal header be ignored?
|
||||
*
|
||||
* @param rejectIllegalHeaderName {@code true} to reject requests with
|
||||
- * illegal header names, {@code false} to
|
||||
- * ignore the header
|
||||
+ * illegal header names or values,
|
||||
+ * {@code false} to ignore the header
|
||||
+ *
|
||||
+ * @deprecated Now an alias for {@link #setRejectIllegalHeader(boolean)}.
|
||||
+ * Will be removed in Tomcat 10 onwards.
|
||||
*/
|
||||
+ @Deprecated
|
||||
public void setRejectIllegalHeaderName(boolean rejectIllegalHeaderName) {
|
||||
- this.rejectIllegalHeaderName = rejectIllegalHeaderName;
|
||||
+ this.rejectIllegalHeader = rejectIllegalHeaderName;
|
||||
}
|
||||
|
||||
|
||||
diff --git a/java/org/apache/coyote/http11/Http11InputBuffer.java b/java/org/apache/coyote/http11/Http11InputBuffer.java
|
||||
index 2dc7c17..57c670e 100644
|
||||
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
|
||||
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
|
||||
@@ -64,7 +64,7 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
|
||||
private final MimeHeaders headers;
|
||||
|
||||
|
||||
- private final boolean rejectIllegalHeaderName;
|
||||
+ private final boolean rejectIllegalHeader;
|
||||
|
||||
/**
|
||||
* State.
|
||||
@@ -150,13 +150,13 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
|
||||
// ----------------------------------------------------------- Constructors
|
||||
|
||||
public Http11InputBuffer(Request request, int headerBufferSize,
|
||||
- boolean rejectIllegalHeaderName, HttpParser httpParser) {
|
||||
+ boolean rejectIllegalHeader, HttpParser httpParser) {
|
||||
|
||||
this.request = request;
|
||||
headers = request.getMimeHeaders();
|
||||
|
||||
this.headerBufferSize = headerBufferSize;
|
||||
- this.rejectIllegalHeaderName = rejectIllegalHeaderName;
|
||||
+ this.rejectIllegalHeader = rejectIllegalHeader;
|
||||
this.httpParser = httpParser;
|
||||
|
||||
filterLibrary = new InputFilter[0];
|
||||
@@ -752,6 +752,8 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
|
||||
//
|
||||
|
||||
byte chr = 0;
|
||||
+ byte prevChr = 0;
|
||||
+
|
||||
while (headerParsePos == HeaderParsePosition.HEADER_START) {
|
||||
|
||||
// Read new bytes if needed
|
||||
@@ -762,17 +764,23 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
|
||||
}
|
||||
}
|
||||
|
||||
+ prevChr = chr;
|
||||
chr = byteBuffer.get();
|
||||
|
||||
- if (chr == Constants.CR) {
|
||||
- // Skip
|
||||
- } else if (chr == Constants.LF) {
|
||||
+ if (chr == Constants.CR && prevChr != Constants.CR) {
|
||||
+ // Possible start of CRLF - process the next byte.
|
||||
+ } else if (prevChr == Constants.CR && chr == Constants.LF) {
|
||||
return HeaderParseStatus.DONE;
|
||||
} else {
|
||||
- byteBuffer.position(byteBuffer.position() - 1);
|
||||
+ if (prevChr == 0) {
|
||||
+ // Must have only read one byte
|
||||
+ byteBuffer.position(byteBuffer.position() - 1);
|
||||
+ } else {
|
||||
+ // Must have read two bytes (first was CR, second was not LF)
|
||||
+ byteBuffer.position(byteBuffer.position() - 2);
|
||||
+ }
|
||||
break;
|
||||
}
|
||||
-
|
||||
}
|
||||
|
||||
if (headerParsePos == HeaderParsePosition.HEADER_START) {
|
||||
@@ -868,11 +876,22 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
|
||||
}
|
||||
}
|
||||
|
||||
+ prevChr = chr;
|
||||
chr = byteBuffer.get();
|
||||
if (chr == Constants.CR) {
|
||||
- // Skip
|
||||
- } else if (chr == Constants.LF) {
|
||||
+ // Possible start of CRLF - process the next byte.
|
||||
+ } else if (prevChr == Constants.CR && chr == Constants.LF) {
|
||||
eol = true;
|
||||
+ } else if (prevChr == Constants.CR) {
|
||||
+ // Invalid value
|
||||
+ // Delete the header (it will be the most recent one)
|
||||
+ headers.removeHeader(headers.size() - 1);
|
||||
+ return skipLine();
|
||||
+ } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
|
||||
+ // Invalid value
|
||||
+ // Delete the header (it will be the most recent one)
|
||||
+ headers.removeHeader(headers.size() - 1);
|
||||
+ return skipLine();
|
||||
} else if (chr == Constants.SP || chr == Constants.HT) {
|
||||
byteBuffer.put(headerData.realPos, chr);
|
||||
headerData.realPos++;
|
||||
@@ -924,6 +943,9 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
|
||||
headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
|
||||
boolean eol = false;
|
||||
|
||||
+ byte chr = 0;
|
||||
+ byte prevChr = 0;
|
||||
+
|
||||
// Reading bytes until the end of the line
|
||||
while (!eol) {
|
||||
|
||||
@@ -935,21 +957,22 @@ public class Http11InputBuffer implements InputBuffer, ApplicationBufferHandler
|
||||
}
|
||||
|
||||
int pos = byteBuffer.position();
|
||||
- byte chr = byteBuffer.get();
|
||||
+ prevChr = chr;
|
||||
+ chr = byteBuffer.get();
|
||||
if (chr == Constants.CR) {
|
||||
// Skip
|
||||
- } else if (chr == Constants.LF) {
|
||||
+ } else if (prevChr == Constants.CR && chr == Constants.LF) {
|
||||
eol = true;
|
||||
} else {
|
||||
headerData.lastSignificantChar = pos;
|
||||
}
|
||||
}
|
||||
- if (rejectIllegalHeaderName || log.isDebugEnabled()) {
|
||||
+ if (rejectIllegalHeader || log.isDebugEnabled()) {
|
||||
String message = sm.getString("iib.invalidheader",
|
||||
new String(byteBuffer.array(), headerData.start,
|
||||
headerData.lastSignificantChar - headerData.start + 1,
|
||||
StandardCharsets.ISO_8859_1));
|
||||
- if (rejectIllegalHeaderName) {
|
||||
+ if (rejectIllegalHeader) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
log.debug(message);
|
||||
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
|
||||
index df002e0..86556ec 100644
|
||||
--- a/java/org/apache/coyote/http11/Http11Processor.java
|
||||
+++ b/java/org/apache/coyote/http11/Http11Processor.java
|
||||
@@ -153,7 +153,7 @@ public class Http11Processor extends AbstractProcessor {
|
||||
protocol.getRelaxedQueryChars());
|
||||
|
||||
inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
|
||||
- protocol.getRejectIllegalHeaderName(), httpParser);
|
||||
+ protocol.getRejectIllegalHeader(), httpParser);
|
||||
request.setInputBuffer(inputBuffer);
|
||||
|
||||
outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize());
|
||||
diff --git a/java/org/apache/tomcat/util/http/MimeHeaders.java b/java/org/apache/tomcat/util/http/MimeHeaders.java
|
||||
index 59504ee..b76b274 100644
|
||||
--- a/java/org/apache/tomcat/util/http/MimeHeaders.java
|
||||
+++ b/java/org/apache/tomcat/util/http/MimeHeaders.java
|
||||
@@ -396,7 +396,7 @@ public class MimeHeaders {
|
||||
* reset and swap with last header
|
||||
* @param idx the index of the header to remove.
|
||||
*/
|
||||
- private void removeHeader(int idx) {
|
||||
+ public void removeHeader(int idx) {
|
||||
MimeHeaderField mh = headers[idx];
|
||||
|
||||
mh.recycle();
|
||||
diff --git a/java/org/apache/tomcat/util/http/parser/HttpParser.java b/java/org/apache/tomcat/util/http/parser/HttpParser.java
|
||||
index 827f2c5..644b1d1 100644
|
||||
--- a/java/org/apache/tomcat/util/http/parser/HttpParser.java
|
||||
+++ b/java/org/apache/tomcat/util/http/parser/HttpParser.java
|
||||
@@ -317,6 +317,17 @@ public class HttpParser {
|
||||
}
|
||||
|
||||
|
||||
+ public static boolean isControl(int c) {
|
||||
+ // Fast for valid control characters, slower for some incorrect
|
||||
+ // ones
|
||||
+ try {
|
||||
+ return IS_CONTROL[c];
|
||||
+ } catch (ArrayIndexOutOfBoundsException ex) {
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
// Skip any LWS and position to read the next character. The next character
|
||||
// is returned as being able to 'peek()' it allows a small optimisation in
|
||||
// some cases.
|
||||
diff --git a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
|
||||
index 131fa21..e60a474 100644
|
||||
--- a/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
|
||||
+++ b/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
|
||||
@@ -163,13 +163,13 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
|
||||
|
||||
|
||||
@Test
|
||||
- public void testBug51557Separators() throws Exception {
|
||||
+ public void testBug51557SeparatorsInName() throws Exception {
|
||||
char httpSeparators[] = new char[] {
|
||||
'\t', ' ', '\"', '(', ')', ',', '/', ':', ';', '<',
|
||||
'=', '>', '?', '@', '[', '\\', ']', '{', '}' };
|
||||
|
||||
for (char s : httpSeparators) {
|
||||
- doTestBug51557Char(s);
|
||||
+ doTestBug51557CharInName(s);
|
||||
tearDown();
|
||||
setUp();
|
||||
}
|
||||
@@ -177,13 +177,38 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
|
||||
|
||||
|
||||
@Test
|
||||
- public void testBug51557Ctl() throws Exception {
|
||||
+ public void testBug51557CtlInName() throws Exception {
|
||||
for (int i = 0; i < 31; i++) {
|
||||
- doTestBug51557Char((char) i);
|
||||
+ doTestBug51557CharInName((char) i);
|
||||
+ tearDown();
|
||||
+ setUp();
|
||||
+ }
|
||||
+ doTestBug51557CharInName((char) 127);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testBug51557CtlInValue() throws Exception {
|
||||
+ for (int i = 0; i < 31; i++) {
|
||||
+ if (i == '\t') {
|
||||
+ // TAB is allowed
|
||||
+ continue;
|
||||
+ }
|
||||
+ doTestBug51557InvalidCharInValue((char) i);
|
||||
+ tearDown();
|
||||
+ setUp();
|
||||
+ }
|
||||
+ doTestBug51557InvalidCharInValue((char) 127);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testBug51557ObsTextInValue() throws Exception {
|
||||
+ for (int i = 128; i < 255; i++) {
|
||||
+ doTestBug51557ValidCharInValue((char) i);
|
||||
tearDown();
|
||||
setUp();
|
||||
}
|
||||
- doTestBug51557Char((char) 127);
|
||||
}
|
||||
|
||||
|
||||
@@ -226,7 +251,7 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
|
||||
}
|
||||
|
||||
|
||||
- private void doTestBug51557Char(char s) {
|
||||
+ private void doTestBug51557CharInName(char s) {
|
||||
Bug51557Client client =
|
||||
new Bug51557Client("X-Bug" + s + "51557", "invalid");
|
||||
|
||||
@@ -236,6 +261,29 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
|
||||
Assert.assertTrue(client.isResponseBodyOK());
|
||||
}
|
||||
|
||||
+
|
||||
+ private void doTestBug51557InvalidCharInValue(char s) {
|
||||
+ Bug51557Client client =
|
||||
+ new Bug51557Client("X-Bug51557-Invalid", "invalid" + s + "invalid");
|
||||
+
|
||||
+ client.doRequest();
|
||||
+ Assert.assertTrue("Testing [" + (int) s + "]", client.isResponse200());
|
||||
+ Assert.assertEquals("Testing [" + (int) s + "]", "abcd", client.getResponseBody());
|
||||
+ Assert.assertTrue(client.isResponseBodyOK());
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private void doTestBug51557ValidCharInValue(char s) {
|
||||
+ Bug51557Client client =
|
||||
+ new Bug51557Client("X-Bug51557-Valid", "valid" + s + "valid");
|
||||
+
|
||||
+ client.doRequest();
|
||||
+ Assert.assertTrue("Testing [" + (int) s + "]", client.isResponse200());
|
||||
+ Assert.assertEquals("Testing [" + (int) s + "]", "valid" + s + "validabcd", client.getResponseBody());
|
||||
+ Assert.assertTrue(client.isResponseBodyOK());
|
||||
+ }
|
||||
+
|
||||
+
|
||||
/**
|
||||
* Bug 51557 test client.
|
||||
*/
|
||||
@@ -243,12 +291,12 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
|
||||
|
||||
private final String headerName;
|
||||
private final String headerLine;
|
||||
- private final boolean rejectIllegalHeaderName;
|
||||
+ private final boolean rejectIllegalHeader;
|
||||
|
||||
public Bug51557Client(String headerName) {
|
||||
this.headerName = headerName;
|
||||
this.headerLine = headerName;
|
||||
- this.rejectIllegalHeaderName = false;
|
||||
+ this.rejectIllegalHeader = false;
|
||||
}
|
||||
|
||||
public Bug51557Client(String headerName, String headerValue) {
|
||||
@@ -256,10 +304,10 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
|
||||
}
|
||||
|
||||
public Bug51557Client(String headerName, String headerValue,
|
||||
- boolean rejectIllegalHeaderName) {
|
||||
+ boolean rejectIllegalHeader) {
|
||||
this.headerName = headerName;
|
||||
this.headerLine = headerName + ": " + headerValue;
|
||||
- this.rejectIllegalHeaderName = rejectIllegalHeaderName;
|
||||
+ this.rejectIllegalHeader = rejectIllegalHeader;
|
||||
}
|
||||
|
||||
private Exception doRequest() {
|
||||
@@ -273,8 +321,8 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
|
||||
|
||||
try {
|
||||
Connector connector = tomcat.getConnector();
|
||||
- connector.setProperty("rejectIllegalHeaderName",
|
||||
- Boolean.toString(rejectIllegalHeaderName));
|
||||
+ connector.setProperty("rejectIllegalHeader",
|
||||
+ Boolean.toString(rejectIllegalHeader));
|
||||
tomcat.start();
|
||||
setPort(connector.getLocalPort());
|
||||
|
||||
@@ -548,7 +596,7 @@ public class TestHttp11InputBuffer extends TomcatBaseTest {
|
||||
|
||||
try {
|
||||
Connector connector = tomcat.getConnector();
|
||||
- connector.setProperty("rejectIllegalHeaderName", "false");
|
||||
+ connector.setProperty("rejectIllegalHeader", "false");
|
||||
tomcat.start();
|
||||
setPort(connector.getLocalPort());
|
||||
|
||||
diff --git a/webapps/docs/config/http.xml b/webapps/docs/config/http.xml
|
||||
index ebb277d..3902c9a 100644
|
||||
--- a/webapps/docs/config/http.xml
|
||||
+++ b/webapps/docs/config/http.xml
|
||||
@@ -551,14 +551,19 @@
|
||||
expected concurrent requests (synchronous and asynchronous).</p>
|
||||
</attribute>
|
||||
|
||||
- <attribute name="rejectIllegalHeaderName" required="false">
|
||||
- <p>If an HTTP request is received that contains an illegal header name
|
||||
- (i.e. the header name is not a token) this setting determines if the
|
||||
+ <attribute name="rejectIllegalHeader" required="false">
|
||||
+ <p>If an HTTP request is received that contains an illegal header name or
|
||||
+ value (e.g. the header name is not a token) this setting determines if the
|
||||
request will be rejected with a 400 response (<code>true</code>) or if the
|
||||
illegal header be ignored (<code>false</code>). The default value is
|
||||
<code>true</code> which will cause the request to be rejected.</p>
|
||||
</attribute>
|
||||
|
||||
+ <attribute name="rejectIllegalHeaderName" required="false">
|
||||
+ <p>This attribute is deprecated. It will be removed in Tomcat 10 onwards.
|
||||
+ It is now an alias for <strong>rejectIllegalHeader</strong>.</p>
|
||||
+ </attribute>
|
||||
+
|
||||
<attribute name="relaxedPathChars" required="false">
|
||||
<p>The <a href="https://tools.ietf.org/rfc/rfc7230.txt">HTTP/1.1
|
||||
specification</a> requires that certain characters are %nn encoded when
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
From 0e8a50f0a5958744bea1fd6768c862e04d3b7e75 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 21 Jan 2020 13:02:13 +0000
|
||||
Subject: [PATCH] Change the default bind address for AJP to the loopback
|
||||
address
|
||||
|
||||
---
|
||||
java/org/apache/coyote/ajp/AbstractAjpProtocol.java | 4 ++++
|
||||
webapps/docs/changelog.xml | 4 ++++
|
||||
webapps/docs/config/ajp.xml | 5 +----
|
||||
3 files changed, 9 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
index 2500abd7ad..8e0593b771 100644
|
||||
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
@@ -16,6 +16,8 @@
|
||||
*/
|
||||
package org.apache.coyote.ajp;
|
||||
|
||||
+import java.net.InetAddress;
|
||||
+
|
||||
import org.apache.coyote.AbstractProtocol;
|
||||
import org.apache.coyote.Processor;
|
||||
import org.apache.coyote.UpgradeProtocol;
|
||||
@@ -46,6 +48,8 @@ public AbstractAjpProtocol(AbstractEndpoint<S,?> endpoint) {
|
||||
setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
|
||||
// AJP does not use Send File
|
||||
getEndpoint().setUseSendfile(false);
|
||||
+ // AJP listens on loopback by default
|
||||
+ getEndpoint().setAddress(InetAddress.getLoopbackAddress());
|
||||
ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
|
||||
setHandler(cHandler);
|
||||
getEndpoint().setHandler(cHandler);
|
||||
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
|
||||
index c70af91eae..5535a062e7 100644
|
||||
--- a/webapps/docs/config/ajp.xml
|
||||
+++ b/webapps/docs/config/ajp.xml
|
||||
@@ -308,10 +308,7 @@
|
||||
<attribute name="address" required="false">
|
||||
<p>For servers with more than one IP address, this attribute
|
||||
specifies which address will be used for listening on the specified
|
||||
- port. By default, this port will be used on all IP addresses
|
||||
- associated with the server. A value of <code>127.0.0.1</code>
|
||||
- indicates that the Connector will only listen on the loopback
|
||||
- interface.</p>
|
||||
+ port. By default, the loopback address will be used.</p>
|
||||
</attribute>
|
||||
|
||||
<attribute name="bindOnInit" required="false">
|
||||
@ -1,160 +0,0 @@
|
||||
From 9ac90532e9a7d239f90952edb229b07c80a9a3eb Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 21 Jan 2020 14:24:33 +0000
|
||||
Subject: [PATCH] Rename requiredSecret to secret and add secretRequired
|
||||
|
||||
AJP Connector will not start if secretRequired="true" and secret is set
|
||||
to null or zero length String.
|
||||
---
|
||||
.../coyote/ajp/AbstractAjpProtocol.java | 49 +++++++++++++++++--
|
||||
java/org/apache/coyote/ajp/AjpProcessor.java | 12 ++---
|
||||
.../apache/coyote/ajp/LocalStrings.properties | 1 +
|
||||
webapps/docs/config/ajp.xml | 12 ++++-
|
||||
5 files changed, 72 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
index 8e0593b771..81da7da6d1 100644
|
||||
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
@@ -143,17 +143,48 @@ public void setTomcatAuthorization(boolean tomcatAuthorization) {
|
||||
}
|
||||
|
||||
|
||||
- private String requiredSecret = null;
|
||||
+ private String secret = null;
|
||||
+ /**
|
||||
+ * Set the secret that must be included with every request.
|
||||
+ *
|
||||
+ * @param secret The required secret
|
||||
+ */
|
||||
+ public void setSecret(String secret) {
|
||||
+ this.secret = secret;
|
||||
+ }
|
||||
+ protected String getSecret() {
|
||||
+ return secret;
|
||||
+ }
|
||||
/**
|
||||
* Set the required secret that must be included with every request.
|
||||
*
|
||||
* @param requiredSecret The required secret
|
||||
+ *
|
||||
+ * @deprecated Replaced by {@link #setSecret(String)}.
|
||||
+ * Will be removed in Tomcat 11 onwards
|
||||
*/
|
||||
+ @Deprecated
|
||||
public void setRequiredSecret(String requiredSecret) {
|
||||
- this.requiredSecret = requiredSecret;
|
||||
+ setSecret(requiredSecret);
|
||||
}
|
||||
+ /**
|
||||
+ * @return The current secret
|
||||
+ *
|
||||
+ * @deprecated Replaced by {@link #getSecret()}.
|
||||
+ * Will be removed in Tomcat 11 onwards
|
||||
+ */
|
||||
+ @Deprecated
|
||||
protected String getRequiredSecret() {
|
||||
- return requiredSecret;
|
||||
+ return getSecret();
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private boolean secretRequired = true;
|
||||
+ public void setSecretRequired(boolean secretRequired) {
|
||||
+ this.secretRequired = secretRequired;
|
||||
+ }
|
||||
+ public boolean getSecretRequired() {
|
||||
+ return secretRequired;
|
||||
}
|
||||
|
||||
|
||||
@@ -210,4 +241,16 @@ protected Processor createUpgradeProcessor(SocketWrapperBase<?> socket,
|
||||
throw new IllegalStateException(sm.getString("ajpprotocol.noUpgradeHandler",
|
||||
upgradeToken.getHttpUpgradeHandler().getClass().getName()));
|
||||
}
|
||||
+
|
||||
+
|
||||
+ @Override
|
||||
+ public void init() throws Exception {
|
||||
+ if (getSecretRequired()) {
|
||||
+ String secret = getSecret();
|
||||
+ if (secret == null || secret.length() == 0) {
|
||||
+ throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
|
||||
+ }
|
||||
+ }
|
||||
+ super.init();
|
||||
+ }
|
||||
}
|
||||
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java
|
||||
index a3e628d2eb..d466de2c09 100644
|
||||
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
|
||||
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
|
||||
@@ -698,8 +698,8 @@ private void prepareRequest() {
|
||||
}
|
||||
|
||||
// Decode extra attributes
|
||||
- String requiredSecret = protocol.getRequiredSecret();
|
||||
- boolean secret = false;
|
||||
+ String secret = protocol.getSecret();
|
||||
+ boolean secretPresentInRequest = false;
|
||||
byte attributeCode;
|
||||
while ((attributeCode = requestHeaderMessage.getByte())
|
||||
!= Constants.SC_A_ARE_DONE) {
|
||||
@@ -801,9 +801,9 @@ private void prepareRequest() {
|
||||
|
||||
case Constants.SC_A_SECRET:
|
||||
requestHeaderMessage.getBytes(tmpMB);
|
||||
- if (requiredSecret != null) {
|
||||
- secret = true;
|
||||
- if (!tmpMB.equals(requiredSecret)) {
|
||||
+ if (secret != null) {
|
||||
+ secretPresentInRequest = true;
|
||||
+ if (!tmpMB.equals(secret)) {
|
||||
response.setStatus(403);
|
||||
setErrorState(ErrorState.CLOSE_CLEAN, null);
|
||||
}
|
||||
@@ -819,7 +819,7 @@ private void prepareRequest() {
|
||||
}
|
||||
|
||||
// Check if secret was submitted if required
|
||||
- if ((requiredSecret != null) && !secret) {
|
||||
+ if ((secret != null) && !secretPresentInRequest) {
|
||||
response.setStatus(403);
|
||||
setErrorState(ErrorState.CLOSE_CLEAN, null);
|
||||
}
|
||||
diff --git a/java/org/apache/coyote/ajp/LocalStrings.properties b/java/org/apache/coyote/ajp/LocalStrings.properties
|
||||
index 9b569bbc6e..01de92a424 100644
|
||||
--- a/java/org/apache/coyote/ajp/LocalStrings.properties
|
||||
+++ b/java/org/apache/coyote/ajp/LocalStrings.properties
|
||||
@@ -28,6 +28,7 @@ ajpprocessor.request.prepare=Error preparing request
|
||||
# limitations under the License.
|
||||
|
||||
ajpprotocol.noSSL=SSL is not supported with AJP. The SSL host configuration for [{0}] was ignored
|
||||
+ajpprotocol.nosecret=The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
|
||||
ajpprotocol.noUpgrade=Upgrade is not supported with AJP. The UpgradeProtocol configuration for [{0}] was ignored
|
||||
ajpprotocol.noUpgradeHandler=Upgrade is not supported with AJP. The HttpUpgradeHandler [{0}] can not be processed
|
||||
|
||||
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
|
||||
index 5535a062e7..3999a13e66 100644
|
||||
--- a/webapps/docs/config/ajp.xml
|
||||
+++ b/webapps/docs/config/ajp.xml
|
||||
@@ -428,8 +428,18 @@
|
||||
expected concurrent requests (synchronous and asynchronous).</p>
|
||||
</attribute>
|
||||
|
||||
- <attribute name="requiredSecret" required="false">
|
||||
+ <attribute name="secret" required="false">
|
||||
<p>Only requests from workers with this secret keyword will be accepted.
|
||||
+ The default value is <code>null</code>. This attrbute must be specified
|
||||
+ with a non-null, non-zero length value unless
|
||||
+ <strong>secretRequired</strong> is explicitly configured to be
|
||||
+ <code>false</code>.</p>
|
||||
+ </attribute>
|
||||
+
|
||||
+ <attribute name="secretRequired" required="false">
|
||||
+ <p>If this attribute is <code>true</code>, the AJP Connector will only
|
||||
+ start if the <strong>secret</strong> attribute is configured with a
|
||||
+ non-null, non-zero length value. The default value is <code>true</code>.
|
||||
</p>
|
||||
</attribute>
|
||||
|
||||
@ -1,142 +0,0 @@
|
||||
From 64fa5b99442589ef0bf2a7fcd71ad2bc68b35fad Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 21 Jan 2020 15:04:12 +0000
|
||||
Subject: [PATCH] Add new AJP attribute allowedArbitraryRequestAttributes
|
||||
|
||||
Requests with unrecognised attributes will be blocked with a 403
|
||||
---
|
||||
.../coyote/ajp/AbstractAjpProtocol.java | 13 +++++++
|
||||
java/org/apache/coyote/ajp/AjpProcessor.java | 36 ++++++++++++++++++-
|
||||
webapps/docs/config/ajp.xml | 19 ++++++++++
|
||||
4 files changed, 72 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
index 81da7da6d1..a2f5e2844a 100644
|
||||
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.apache.coyote.ajp;
|
||||
|
||||
import java.net.InetAddress;
|
||||
+import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.coyote.AbstractProtocol;
|
||||
import org.apache.coyote.Processor;
|
||||
@@ -188,6 +189,18 @@ public boolean getSecretRequired() {
|
||||
}
|
||||
|
||||
|
||||
+ private Pattern allowedArbitraryRequestAttributesPattern;
|
||||
+ public void setAllowedArbitraryRequestAttributes(String allowedArbitraryRequestAttributes) {
|
||||
+ this.allowedArbitraryRequestAttributesPattern = Pattern.compile(allowedArbitraryRequestAttributes);
|
||||
+ }
|
||||
+ public String getAllowedArbitraryRequestAttributes() {
|
||||
+ return allowedArbitraryRequestAttributesPattern.pattern();
|
||||
+ }
|
||||
+ protected Pattern getAllowedArbitraryRequestAttributesPattern() {
|
||||
+ return allowedArbitraryRequestAttributesPattern;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
/**
|
||||
* AJP packet size.
|
||||
*/
|
||||
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java
|
||||
index d466de2c09..f3d783f546 100644
|
||||
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
|
||||
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
|
||||
@@ -25,6 +25,11 @@
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
+import java.util.Collections;
|
||||
+import java.util.HashSet;
|
||||
+import java.util.Set;
|
||||
+import java.util.regex.Matcher;
|
||||
+import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -78,6 +83,9 @@
|
||||
private static final byte[] pongMessageArray;
|
||||
|
||||
|
||||
+ private static final Set<String> javaxAttributes;
|
||||
+
|
||||
+
|
||||
static {
|
||||
// Allocate the end message array
|
||||
AjpMessage endMessage = new AjpMessage(16);
|
||||
@@ -118,6 +126,14 @@
|
||||
pongMessageArray = new byte[pongMessage.getLen()];
|
||||
System.arraycopy(pongMessage.getBuffer(), 0, pongMessageArray,
|
||||
0, pongMessage.getLen());
|
||||
+
|
||||
+ // Build the Set of javax attributes
|
||||
+ Set<String> s = new HashSet<>();
|
||||
+ s.add("javax.servlet.request.cipher_suite");
|
||||
+ s.add("javax.servlet.request.key_size");
|
||||
+ s.add("javax.servlet.request.ssl_session");
|
||||
+ s.add("javax.servlet.request.X509Certificate");
|
||||
+ javaxAttributes= Collections.unmodifiableSet(s);
|
||||
}
|
||||
|
||||
|
||||
@@ -728,8 +744,26 @@ private void prepareRequest() {
|
||||
}
|
||||
} else if(n.equals(Constants.SC_A_SSL_PROTOCOL)) {
|
||||
request.setAttribute(SSLSupport.PROTOCOL_VERSION_KEY, v);
|
||||
+ } else if (n.equals("JK_LB_ACTIVATION")) {
|
||||
+ request.setAttribute(n, v);
|
||||
+ } else if (javaxAttributes.contains(n)) {
|
||||
+ request.setAttribute(n, v);
|
||||
} else {
|
||||
- request.setAttribute(n, v );
|
||||
+ // All 'known' attributes will be processed by the previous
|
||||
+ // blocks. Any remaining attribute is an 'arbitrary' one.
|
||||
+ Pattern pattern = protocol.getAllowedArbitraryRequestAttributesPattern();
|
||||
+ if (pattern == null) {
|
||||
+ response.setStatus(403);
|
||||
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
|
||||
+ } else {
|
||||
+ Matcher m = pattern.matcher(n);
|
||||
+ if (m.matches()) {
|
||||
+ request.setAttribute(n, v);
|
||||
+ } else {
|
||||
+ response.setStatus(403);
|
||||
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
break;
|
||||
|
||||
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
|
||||
index 3999a13e66..69348a18e2 100644
|
||||
--- a/webapps/docs/config/ajp.xml
|
||||
+++ b/webapps/docs/config/ajp.xml
|
||||
@@ -311,6 +311,25 @@
|
||||
port. By default, the loopback address will be used.</p>
|
||||
</attribute>
|
||||
|
||||
+ <attribute name="allowedArbitraryRequestAttributes" required="false">
|
||||
+ <p>The AJP protocol passes some information from the reverse proxy to the
|
||||
+ AJP connector using request attributes. These attributes are:</p>
|
||||
+ <ul>
|
||||
+ <li>javax.servlet.request.cipher_suite</li>
|
||||
+ <li>javax.servlet.request.key_size</li>
|
||||
+ <li>javax.servlet.request.ssl_session</li>
|
||||
+ <li>javax.servlet.request.X509Certificate</li>
|
||||
+ <li>AJP_LOCAL_ADDR</li>
|
||||
+ <li>AJP_REMOTE_PORT</li>
|
||||
+ <li>AJP_SSL_PROTOCOL</li>
|
||||
+ <li>JK_LB_ACTIVATION</li>
|
||||
+ </ul>
|
||||
+ <p>The AJP protocol supports the passing of arbitrary request attributes.
|
||||
+ Requests containing arbitrary request attributes will be rejected with a
|
||||
+ 403 response unless the entire attribute name matches this regular
|
||||
+ expression. If not specified, the default value is <code>null</code>.</p>
|
||||
+ </attribute>
|
||||
+
|
||||
<attribute name="bindOnInit" required="false">
|
||||
<p>Controls when the socket used by the connector is bound. By default it
|
||||
is bound when the connector is initiated and unbound when the connector is
|
||||
@ -1,86 +0,0 @@
|
||||
From 5716044b61cb5b638d8f0de848ac64df03184bc7 Mon Sep 17 00:00:00 2001
|
||||
From: wang_yue111 <wangyue92@huawei.com>
|
||||
Date: Mon, 18 May 2020 15:23:19 +0800
|
||||
Subject: [PATCH] 3
|
||||
|
||||
---
|
||||
conf/server.xml | 5 ++++-
|
||||
.../apache/coyote/ajp/AbstractAjpProtocol.java | 18 +++++++++---------
|
||||
java/org/apache/coyote/ajp/AjpProcessor.java | 2 +-
|
||||
webapps/docs/config/ajp.xml | 2 +-
|
||||
4 files changed, 15 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/conf/server.xml b/conf/server.xml
|
||||
index fce8922..81a4e16 100644
|
||||
--- a/conf/server.xml
|
||||
+++ b/conf/server.xml
|
||||
@@ -113,7 +113,10 @@
|
||||
-->
|
||||
|
||||
<!-- Define an AJP 1.3 Connector on port 8009 -->
|
||||
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
|
||||
+ <Connector protocol="AJP/1.3"
|
||||
+ address="::1"
|
||||
+ port="8009"
|
||||
+ redirectPort="8443" />
|
||||
|
||||
|
||||
<!-- An Engine represents the entry point (within Catalina) that processes
|
||||
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
index a2f5e28..0bbd1e6 100644
|
||||
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
@@ -189,15 +189,15 @@ public abstract class AbstractAjpProtocol<S> extends AbstractProtocol<S> {
|
||||
}
|
||||
|
||||
|
||||
- private Pattern allowedArbitraryRequestAttributesPattern;
|
||||
- public void setAllowedArbitraryRequestAttributes(String allowedArbitraryRequestAttributes) {
|
||||
- this.allowedArbitraryRequestAttributesPattern = Pattern.compile(allowedArbitraryRequestAttributes);
|
||||
- }
|
||||
- public String getAllowedArbitraryRequestAttributes() {
|
||||
- return allowedArbitraryRequestAttributesPattern.pattern();
|
||||
- }
|
||||
- protected Pattern getAllowedArbitraryRequestAttributesPattern() {
|
||||
- return allowedArbitraryRequestAttributesPattern;
|
||||
+ private Pattern allowedRequestAttributesPattern;
|
||||
+ public void setAllowedRequestAttributesPattern(String allowedRequestAttributesPattern) {
|
||||
+ this.allowedRequestAttributesPattern = Pattern.compile(allowedRequestAttributesPattern);
|
||||
+ }
|
||||
+ public String getAllowedRequestAttributesPattern() {
|
||||
+ return allowedRequestAttributesPattern.pattern();
|
||||
+ }
|
||||
+ protected Pattern getAllowedRequestAttributesPatternInternal() {
|
||||
+ return allowedRequestAttributesPattern;
|
||||
}
|
||||
|
||||
|
||||
diff --git a/java/org/apache/coyote/ajp/AjpProcessor.java b/java/org/apache/coyote/ajp/AjpProcessor.java
|
||||
index cc11a20..bf2cf86 100644
|
||||
--- a/java/org/apache/coyote/ajp/AjpProcessor.java
|
||||
+++ b/java/org/apache/coyote/ajp/AjpProcessor.java
|
||||
@@ -746,7 +746,7 @@ public class AjpProcessor extends AbstractProcessor {
|
||||
} else {
|
||||
// All 'known' attributes will be processed by the previous
|
||||
// blocks. Any remaining attribute is an 'arbitrary' one.
|
||||
- Pattern pattern = protocol.getAllowedArbitraryRequestAttributesPattern();
|
||||
+ Pattern pattern = protocol.getAllowedRequestAttributesPatternInternal();
|
||||
if (pattern == null) {
|
||||
response.setStatus(403);
|
||||
setErrorState(ErrorState.CLOSE_CLEAN, null);
|
||||
diff --git a/webapps/docs/config/ajp.xml b/webapps/docs/config/ajp.xml
|
||||
index 17107e4..622e7ca 100644
|
||||
--- a/webapps/docs/config/ajp.xml
|
||||
+++ b/webapps/docs/config/ajp.xml
|
||||
@@ -311,7 +311,7 @@
|
||||
port. By default, the loopback address will be used.</p>
|
||||
</attribute>
|
||||
|
||||
- <attribute name="allowedArbitraryRequestAttributes" required="false">
|
||||
+ <attribute name="allowedRequestAttributesPattern" required="false">
|
||||
<p>The AJP protocol passes some information from the reverse proxy to the
|
||||
AJP connector using request attributes. These attributes are:</p>
|
||||
<ul>
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
From 49ad3f954f69c6e838c8cd112ad79aa5fa8e7153 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 5 Feb 2020 14:47:48 +0000
|
||||
Subject: [PATCH] Refactor secret check
|
||||
|
||||
Moving the check to start allows invalid configurations to be fixed via
|
||||
JMX and changes to be made followed by stop()/start() for those changes
|
||||
to take effect.
|
||||
---
|
||||
java/org/apache/coyote/ajp/AbstractAjpProtocol.java | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
index 63ff6c5a63..7cfdf0accf 100644
|
||||
--- a/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
+++ b/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
|
||||
@@ -257,13 +257,13 @@ protected Processor createUpgradeProcessor(SocketWrapperBase<?> socket,
|
||||
|
||||
|
||||
@Override
|
||||
- public void init() throws Exception {
|
||||
+ public void start() throws Exception {
|
||||
if (getSecretRequired()) {
|
||||
String secret = getSecret();
|
||||
if (secret == null || secret.length() == 0) {
|
||||
throw new IllegalArgumentException(sm.getString("ajpprotocol.nosecret"));
|
||||
}
|
||||
}
|
||||
- super.init();
|
||||
+ super.start();
|
||||
}
|
||||
}
|
||||
@ -1,84 +0,0 @@
|
||||
From 3aa8f28db7efb311cdd1b6fe15a9cd3b167a2222 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 5 May 2020 15:50:15 +0100
|
||||
Subject: [PATCH] Improve validation of storage location when using FileStore.
|
||||
|
||||
---
|
||||
.../apache/catalina/session/FileStore.java | 19 +++++++++++++++++--
|
||||
.../catalina/session/LocalStrings.properties | 1 +
|
||||
webapps/docs/changelog.xml | 3 +++
|
||||
3 files changed, 21 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/session/FileStore.java b/java/org/apache/catalina/session/FileStore.java
|
||||
index 066d6035f1..cf3ea880fa 100644
|
||||
--- a/java/org/apache/catalina/session/FileStore.java
|
||||
+++ b/java/org/apache/catalina/session/FileStore.java
|
||||
@@ -33,6 +33,8 @@
|
||||
import org.apache.catalina.Globals;
|
||||
import org.apache.catalina.Session;
|
||||
import org.apache.juli.logging.Log;
|
||||
+import org.apache.juli.logging.LogFactory;
|
||||
+import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* Concrete implementation of the <b>Store</b> interface that utilizes
|
||||
@@ -43,6 +45,10 @@
|
||||
*/
|
||||
public final class FileStore extends StoreBase {
|
||||
|
||||
+ private static final Log log = LogFactory.getLog(FileStore.class);
|
||||
+ private static final StringManager sm = StringManager.getManager(FileStore.class);
|
||||
+
|
||||
+
|
||||
// ----------------------------------------------------- Constants
|
||||
|
||||
/**
|
||||
@@ -341,11 +347,20 @@ private File directory() throws IOException {
|
||||
* used in the file naming.
|
||||
*/
|
||||
private File file(String id) throws IOException {
|
||||
- if (this.directory == null) {
|
||||
+ File storageDir = directory();
|
||||
+ if (storageDir == null) {
|
||||
return null;
|
||||
}
|
||||
+
|
||||
String filename = id + FILE_EXT;
|
||||
- File file = new File(directory(), filename);
|
||||
+ File file = new File(storageDir, filename);
|
||||
+
|
||||
+ // Check the file is within the storage directory
|
||||
+ if (!file.getCanonicalPath().startsWith(storageDir.getCanonicalPath())) {
|
||||
+ log.warn(sm.getString("fileStore.invalid", file.getPath(), id));
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
return file;
|
||||
}
|
||||
}
|
||||
diff --git a/java/org/apache/catalina/session/LocalStrings.properties b/java/org/apache/catalina/session/LocalStrings.properties
|
||||
index 5815915..d72bee4 100644
|
||||
--- a/java/org/apache/catalina/session/LocalStrings.properties
|
||||
+++ b/java/org/apache/catalina/session/LocalStrings.properties
|
||||
@@ -16,6 +16,7 @@
|
||||
fileStore.saving=Saving Session [{0}] to file [{1}]
|
||||
fileStore.loading=Loading Session [{0}] from file [{1}]
|
||||
fileStore.removing=Removing Session [{0}] at file [{1}]
|
||||
+fileStore.invalid=Invalid persistence file [{0}] for session ID [{1}]
|
||||
fileStore.createFailed=Unable to create directory [{0}] for the storage of session data
|
||||
fileStore.deleteFailed=Unable to delete file [{0}] which is preventing the creation of the session storage location
|
||||
fileStore.deleteSessionFailed=Unable to delete file [{0}] which is no longer required
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 5665df4..a384d62 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -171,6 +171,9 @@
|
||||
When generating a redirect to a directory in the Default Servlet, avoid
|
||||
generating a protocol relative redirect. (markt)
|
||||
</fix>
|
||||
+ <add>
|
||||
+ Improve validation of storage location when using FileStore. (markt)
|
||||
+ </add>
|
||||
</changelog>
|
||||
</subsection>
|
||||
<subsection name="Coyote">
|
||||
@ -1,73 +0,0 @@
|
||||
From 935fc5582dc25ae10bab6f9d5629ff8d996cb533 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Fri, 6 Nov 2020 19:03:57 +0000
|
||||
Subject: [PATCH] Fix BZ 64871. Log if file access is blocked due to symlinks
|
||||
|
||||
https://bz.apache.org/bugzilla/show_bug.cgi?id=64871
|
||||
---
|
||||
.../webresources/AbstractFileResourceSet.java | 19 ++++++++++++++++++-
|
||||
.../webresources/LocalStrings.properties | 2 ++
|
||||
2 files changed, 20 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/webresources/AbstractFileResourceSet.java b/java/org/apache/catalina/webresources/AbstractFileResourceSet.java
|
||||
index c7993411e9..59fc77157f 100644
|
||||
--- a/java/org/apache/catalina/webresources/AbstractFileResourceSet.java
|
||||
+++ b/java/org/apache/catalina/webresources/AbstractFileResourceSet.java
|
||||
@@ -22,11 +22,15 @@
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
+import org.apache.juli.logging.Log;
|
||||
+import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.compat.JrePlatform;
|
||||
import org.apache.tomcat.util.http.RequestUtil;
|
||||
|
||||
public abstract class AbstractFileResourceSet extends AbstractResourceSet {
|
||||
|
||||
+ private static final Log log = LogFactory.getLog(AbstractFileResourceSet.class);
|
||||
+
|
||||
protected static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private File fileBase;
|
||||
@@ -128,6 +132,19 @@ protected final File file(String name, boolean mustExist) {
|
||||
canPath = normalize(canPath);
|
||||
}
|
||||
if (!canPath.equals(absPath)) {
|
||||
+ if (!canPath.equalsIgnoreCase(absPath)) {
|
||||
+ // Typically means symlinks are in use but being ignored. Given
|
||||
+ // the symlink was likely created for a reason, log a warning
|
||||
+ // that it was ignored.
|
||||
+ String msg = sm.getString("abstractFileResourceSet.canonicalfileCheckFailed",
|
||||
+ getRoot().getContext().getName(), absPath, canPath);
|
||||
+ // Log issues with configuration files at a higher level
|
||||
+ if(absPath.startsWith("/META-INF/") || absPath.startsWith("/WEB-INF/")) {
|
||||
+ log.error(msg);
|
||||
+ } else {
|
||||
+ log.warn(msg);
|
||||
+ }
|
||||
+ }
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -144,7 +161,7 @@ private boolean isInvalidWindowsFilename(String name) {
|
||||
// expression irrespective of input length.
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = name.charAt(i);
|
||||
- if (c == '\"' || c == '<' || c == '>') {
|
||||
+ if (c == '\"' || c == '<' || c == '>' || c == ':') {
|
||||
// These characters are disallowed in Windows file names and
|
||||
// there are known problems for file names with these characters
|
||||
// when using File#getCanonicalPath().
|
||||
diff --git a/java/org/apache/catalina/webresources/LocalStrings.properties b/java/org/apache/catalina/webresources/LocalStrings.properties
|
||||
index fb9badc120..af9f9fe797 100644
|
||||
--- a/java/org/apache/catalina/webresources/LocalStrings.properties
|
||||
+++ b/java/org/apache/catalina/webresources/LocalStrings.properties
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
abstractArchiveResourceSet.setReadOnlyFalse=Archive based WebResourceSets such as those based on JARs are hard-coded to be read-only and may not be configured to be read-write
|
||||
|
||||
+abstractFileResourceSet.canonicalfileCheckFailed=Resource for web application [{0}] at path [{1}] was not loaded as the canonical path [{2}] did not match. Use of symlinks is one possible cause.
|
||||
+
|
||||
abstractResource.getContentFail=Unable to return [{0}] as a byte array
|
||||
abstractResource.getContentTooLarge=Unable to return [{0}] as a byte array since the resource is [{1}] bytes in size which is larger than the maximum size of a byte array
|
||||
|
||||
@ -1,53 +0,0 @@
|
||||
From 995115a24bb868d1204a796f5b3170f62618a6bb Mon Sep 17 00:00:00 2001
|
||||
From: wang_yue111 <648774160@qq.com>
|
||||
Date: Thu, 11 Mar 2021 18:35:41 +0800
|
||||
Subject: [PATCH] SocketWrapper.upgraded is no longer used
|
||||
|
||||
It used to be used to determine if the processor should be recycled. It
|
||||
has been replaced by a flag on the processor.
|
||||
|
||||
---
|
||||
java/org/apache/coyote/AbstractProtocol.java | 2 --
|
||||
.../apache/tomcat/util/net/SocketWrapperBase.java | 12 ++++++++++++
|
||||
2 files changed, 12 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
|
||||
index 09ed0a9..9f83906 100644
|
||||
--- a/java/org/apache/coyote/AbstractProtocol.java
|
||||
+++ b/java/org/apache/coyote/AbstractProtocol.java
|
||||
@@ -799,8 +799,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
|
||||
processor, wrapper));
|
||||
}
|
||||
wrapper.unRead(leftOverInput);
|
||||
- // Mark the connection as upgraded
|
||||
- wrapper.setUpgraded(true);
|
||||
// Associate with the processor with the connection
|
||||
connections.put(socket, processor);
|
||||
// Initialise the upgrade handler (which may trigger
|
||||
diff --git a/java/org/apache/tomcat/util/net/SocketWrapperBase.java b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
|
||||
index 2479d6d..f8a79db 100644
|
||||
--- a/java/org/apache/tomcat/util/net/SocketWrapperBase.java
|
||||
+++ b/java/org/apache/tomcat/util/net/SocketWrapperBase.java
|
||||
@@ -138,7 +138,19 @@ public abstract class SocketWrapperBase<E> {
|
||||
}
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * @return {@code true} if the connection has been upgraded.
|
||||
+ *
|
||||
+ * @deprecated Unused. Will be removed in Tomcat 10.
|
||||
+ */
|
||||
+ @Deprecated
|
||||
public boolean isUpgraded() { return upgraded; }
|
||||
+ /**
|
||||
+ * @param upgraded {@code true} if the connection has been upgraded.
|
||||
+ *
|
||||
+ * @deprecated Unused. Will be removed in Tomcat 10.
|
||||
+ */
|
||||
+ @Deprecated
|
||||
public void setUpgraded(boolean upgraded) { this.upgraded = upgraded; }
|
||||
public boolean isSecure() { return secure; }
|
||||
public void setSecure(boolean secure) { this.secure = secure; }
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
From 7b8b7134813a356595eacf01fd9e8ea6b3752c8b Mon Sep 17 00:00:00 2001
|
||||
From: wang_yue111 <648774160@qq.com>
|
||||
Date: Thu, 11 Mar 2021 18:42:09 +0800
|
||||
Subject: [PATCH] Simplify the code and fix an edge case for BZ 64830
|
||||
|
||||
https://bz.apache.org/bugzilla/show_bug.cgi?id=64830
|
||||
---
|
||||
java/org/apache/coyote/AbstractProtocol.java | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/AbstractProtocol.java b/java/org/apache/coyote/AbstractProtocol.java
|
||||
index 9f83906..b5c4d5b 100644
|
||||
--- a/java/org/apache/coyote/AbstractProtocol.java
|
||||
+++ b/java/org/apache/coyote/AbstractProtocol.java
|
||||
@@ -766,8 +766,10 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
|
||||
if (state == SocketState.UPGRADING) {
|
||||
// Get the HTTP upgrade handler
|
||||
UpgradeToken upgradeToken = processor.getUpgradeToken();
|
||||
- // Retrieve leftover input
|
||||
+ // Restore leftover input to the wrapper so the upgrade
|
||||
+ // processor can process it.
|
||||
ByteBuffer leftOverInput = processor.getLeftoverInput();
|
||||
+ wrapper.unRead(leftOverInput);
|
||||
if (upgradeToken == null) {
|
||||
// Assume direct HTTP/2 connection
|
||||
UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c");
|
||||
@@ -776,7 +778,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
|
||||
release(processor);
|
||||
// Create the upgrade processor
|
||||
processor = upgradeProtocol.getProcessor(wrapper, getProtocol().getAdapter());
|
||||
- wrapper.unRead(leftOverInput);
|
||||
// Associate with the processor with the connection
|
||||
connections.put(socket, processor);
|
||||
} else {
|
||||
@@ -798,7 +799,6 @@ public abstract class AbstractProtocol<S> implements ProtocolHandler,
|
||||
getLog().debug(sm.getString("abstractConnectionHandler.upgradeCreate",
|
||||
processor, wrapper));
|
||||
}
|
||||
- wrapper.unRead(leftOverInput);
|
||||
// Associate with the processor with the connection
|
||||
connections.put(socket, processor);
|
||||
// Initialise the upgrade handler (which may trigger
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
From d63695a656f04e39bd1ad4dee0f2339b0e3b898f Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 3 Oct 2018 11:16:07 +0000
|
||||
Subject: [PATCH] Ensure that a canonical path is always used for the docBase
|
||||
of a Context to ensure consistent behaviour.
|
||||
|
||||
git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1842702 13f79535-47bb-0310-9956-ffa450edef68
|
||||
---
|
||||
java/org/apache/catalina/startup/ContextConfig.java | 2 +-
|
||||
2 files changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
|
||||
index be74f29168..f3935038fc 100644
|
||||
--- a/java/org/apache/catalina/startup/ContextConfig.java
|
||||
+++ b/java/org/apache/catalina/startup/ContextConfig.java
|
||||
@@ -579,7 +579,7 @@ protected void fixDocBase() throws IOException {
|
||||
|
||||
File file = new File(docBase);
|
||||
if (!file.isAbsolute()) {
|
||||
- docBase = (new File(appBase, docBase)).getPath();
|
||||
+ docBase = (new File(appBase, docBase)).getCanonicalPath();
|
||||
} else {
|
||||
docBase = file.getCanonicalPath();
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
From ad60947e42e666dc9c9d77315787ea9bb567e3fd Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 13 Mar 2019 11:18:45 +0000
|
||||
Subject: [PATCH] Always process the docBase using absolute path during
|
||||
deployment
|
||||
|
||||
Use absolute path to determine the Context name, deployment type,
|
||||
whether the docBase is located within the appBase etc.
|
||||
---
|
||||
java/org/apache/catalina/startup/ContextConfig.java | 4 ++--
|
||||
2 files changed, 8 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
|
||||
index ebd3d8221f..0c67af3bf4 100644
|
||||
--- a/java/org/apache/catalina/startup/ContextConfig.java
|
||||
+++ b/java/org/apache/catalina/startup/ContextConfig.java
|
||||
@@ -582,9 +582,9 @@ protected void fixDocBase() throws IOException {
|
||||
|
||||
File file = new File(docBase);
|
||||
if (!file.isAbsolute()) {
|
||||
- docBase = (new File(appBase, docBase)).getCanonicalPath();
|
||||
+ docBase = (new File(appBase, docBase)).getAbsolutePath();
|
||||
} else {
|
||||
- docBase = file.getCanonicalPath();
|
||||
+ docBase = file.getAbsolutePath();
|
||||
}
|
||||
file = new File(docBase);
|
||||
String origDocBase = docBase;
|
||||
@ -1,144 +0,0 @@
|
||||
From 2c5066316f6b138c4130a87cae4db05d75afe150 Mon Sep 17 00:00:00 2001
|
||||
From: wang_yue111 <648774160@qq.com>
|
||||
Date: Fri, 12 Mar 2021 09:44:04 +0800
|
||||
Subject: [PATCH] 2
|
||||
|
||||
---
|
||||
.../catalina/startup/ContextConfig.java | 75 ++++++++++---------
|
||||
1 file changed, 41 insertions(+), 34 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
|
||||
index 89eb8d3..a4210f8 100644
|
||||
--- a/java/org/apache/catalina/startup/ContextConfig.java
|
||||
+++ b/java/org/apache/catalina/startup/ContextConfig.java
|
||||
@@ -566,25 +566,29 @@ public class ContextConfig implements LifecycleListener {
|
||||
Host host = (Host) context.getParent();
|
||||
File appBase = host.getAppBaseFile();
|
||||
|
||||
- String docBase = context.getDocBase();
|
||||
- if (docBase == null) {
|
||||
+ // This could be blank, relative, absolute or canonical
|
||||
+ String docBaseConfigured = context.getDocBase();
|
||||
+ // If there is no explicit docBase, derive it from the path and version
|
||||
+ if (docBaseConfigured == null) {
|
||||
// Trying to guess the docBase according to the path
|
||||
String path = context.getPath();
|
||||
if (path == null) {
|
||||
return;
|
||||
}
|
||||
ContextName cn = new ContextName(path, context.getWebappVersion());
|
||||
- docBase = cn.getBaseName();
|
||||
+ docBaseConfigured = cn.getBaseName();
|
||||
}
|
||||
|
||||
- File file = new File(docBase);
|
||||
- if (!file.isAbsolute()) {
|
||||
- docBase = (new File(appBase, docBase)).getAbsolutePath();
|
||||
- } else {
|
||||
- docBase = file.getAbsolutePath();
|
||||
- }
|
||||
- file = new File(docBase);
|
||||
- String origDocBase = docBase;
|
||||
+ // Obtain the absolute docBase in String and File form
|
||||
+ String docBaseAbsolute;
|
||||
+ File docBaseConfiguredFile = new File(docBaseConfigured);
|
||||
+ if (!docBaseConfiguredFile.isAbsolute()) {
|
||||
+ docBaseAbsolute = (new File(appBase, docBaseConfigured)).getAbsolutePath();
|
||||
+ } else {
|
||||
+ docBaseAbsolute = docBaseConfiguredFile.getAbsolutePath();
|
||||
+ }
|
||||
+ File docBaseAbsoluteFile = new File(docBaseAbsolute);
|
||||
+ String originalDocBase = docBaseAbsolute;
|
||||
|
||||
ContextName cn = new ContextName(context.getPath(), context.getWebappVersion());
|
||||
String pathName = cn.getBaseName();
|
||||
@@ -597,28 +601,29 @@ public class ContextConfig implements LifecycleListener {
|
||||
}
|
||||
}
|
||||
|
||||
- boolean docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
|
||||
-
|
||||
- if (docBase.toLowerCase(Locale.ENGLISH).endsWith(".war") && !file.isDirectory()) {
|
||||
- URL war = UriUtil.buildJarUrl(new File(docBase));
|
||||
+ // At this point we need to determine if we have a WAR file in the
|
||||
+ // appBase that needs to be expanded. Therefore we consider the absolute
|
||||
+ // docBase NOT the canonical docBase. This is because some users symlink
|
||||
+ // WAR files into the appBase and we want this to work correctly.
|
||||
+ boolean docBaseAbsoluteInAppBase = docBaseAbsolute.startsWith(appBase.getPath() + File.separatorChar);
|
||||
+ if (docBaseAbsolute.toLowerCase(Locale.ENGLISH).endsWith(".war") && !docBaseAbsoluteFile.isDirectory()) {
|
||||
+ URL war = UriUtil.buildJarUrl(docBaseAbsoluteFile);
|
||||
if (unpackWARs) {
|
||||
- docBase = ExpandWar.expand(host, war, pathName);
|
||||
- file = new File(docBase);
|
||||
- docBase = file.getCanonicalPath();
|
||||
+ docBaseAbsolute = ExpandWar.expand(host, war, pathName);
|
||||
+ docBaseAbsoluteFile = new File(docBaseAbsolute);
|
||||
if (context instanceof StandardContext) {
|
||||
- ((StandardContext) context).setOriginalDocBase(origDocBase);
|
||||
+ ((StandardContext) context).setOriginalDocBase(originalDocBase);
|
||||
}
|
||||
} else {
|
||||
ExpandWar.validate(host, war, pathName);
|
||||
}
|
||||
} else {
|
||||
- File docDir = new File(docBase);
|
||||
- File warFile = new File(docBase + ".war");
|
||||
+ File docBaseAbsoluteFileWar = new File(docBaseAbsolute + ".war");
|
||||
URL war = null;
|
||||
- if (warFile.exists() && docBaseInAppBase) {
|
||||
- war = UriUtil.buildJarUrl(warFile);
|
||||
+ if (docBaseAbsoluteFileWar.exists() && docBaseAbsoluteInAppBase) {
|
||||
+ war = UriUtil.buildJarUrl(docBaseAbsoluteFileWar);
|
||||
}
|
||||
- if (docDir.exists()) {
|
||||
+ if (docBaseAbsoluteFile.exists()) {
|
||||
if (war != null && unpackWARs) {
|
||||
// Check if WAR needs to be re-expanded (e.g. if it has
|
||||
// changed). Note: HostConfig.deployWar() takes care of
|
||||
@@ -629,31 +634,33 @@ public class ContextConfig implements LifecycleListener {
|
||||
} else {
|
||||
if (war != null) {
|
||||
if (unpackWARs) {
|
||||
- docBase = ExpandWar.expand(host, war, pathName);
|
||||
- file = new File(docBase);
|
||||
- docBase = file.getCanonicalPath();
|
||||
+ docBaseAbsolute = ExpandWar.expand(host, war, pathName);
|
||||
+ docBaseAbsoluteFile = new File(docBaseAbsolute);
|
||||
} else {
|
||||
- docBase = warFile.getCanonicalPath();
|
||||
+ docBaseAbsolute = docBaseAbsoluteFileWar.getAbsolutePath();
|
||||
+ docBaseAbsoluteFile = docBaseAbsoluteFileWar;
|
||||
ExpandWar.validate(host, war, pathName);
|
||||
}
|
||||
}
|
||||
if (context instanceof StandardContext) {
|
||||
- ((StandardContext) context).setOriginalDocBase(origDocBase);
|
||||
+ ((StandardContext) context).setOriginalDocBase(originalDocBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- // Re-calculate now docBase is a canonical path
|
||||
- docBaseInAppBase = docBase.startsWith(appBase.getPath() + File.separatorChar);
|
||||
+ String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath();
|
||||
|
||||
- if (docBaseInAppBase) {
|
||||
- docBase = docBase.substring(appBase.getPath().length());
|
||||
+ // Re-calculate now docBase is a canonical path
|
||||
+ boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar);
|
||||
+ String docBase;
|
||||
+ if (docBaseCanonicalInAppBase) {
|
||||
+ docBase = docBaseCanonical.substring(appBase.getPath().length());
|
||||
docBase = docBase.replace(File.separatorChar, '/');
|
||||
if (docBase.startsWith("/")) {
|
||||
docBase = docBase.substring(1);
|
||||
}
|
||||
} else {
|
||||
- docBase = docBase.replace(File.separatorChar, '/');
|
||||
+ docBase = docBaseCanonical.replace(File.separatorChar, '/');
|
||||
}
|
||||
|
||||
context.setDocBase(docBase);
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,157 +0,0 @@
|
||||
From 48590d3fc54100031ba9d8c4f6362afb15c6697f Mon Sep 17 00:00:00 2001
|
||||
From: wang_yue111 <648774160@qq.com>
|
||||
Date: Fri, 12 Mar 2021 09:53:00 +0800
|
||||
Subject: [PATCH] Use java.nio.file.Path for consistent sub-directory
|
||||
checking
|
||||
|
||||
---
|
||||
.../catalina/servlets/DefaultServlet.java | 2 +-
|
||||
.../apache/catalina/session/FileStore.java | 2 +-
|
||||
.../catalina/startup/ContextConfig.java | 3 ++-
|
||||
.../apache/catalina/startup/ExpandWar.java | 21 +++++++------------
|
||||
.../apache/catalina/startup/HostConfig.java | 3 +--
|
||||
webapps/docs/changelog.xml | 4 ++++
|
||||
6 files changed, 16 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/servlets/DefaultServlet.java b/java/org/apache/catalina/servlets/DefaultServlet.java
|
||||
index 8b453bf..5ad60ec 100644
|
||||
--- a/java/org/apache/catalina/servlets/DefaultServlet.java
|
||||
+++ b/java/org/apache/catalina/servlets/DefaultServlet.java
|
||||
@@ -1992,7 +1992,7 @@ public class DefaultServlet extends HttpServlet {
|
||||
|
||||
// First check that the resulting path is under the provided base
|
||||
try {
|
||||
- if (!candidate.getCanonicalPath().startsWith(base.getCanonicalPath())) {
|
||||
+ if (!candidate.getCanonicalFile().toPath().startsWith(base.getCanonicalFile().toPath())) {
|
||||
return null;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
diff --git a/java/org/apache/catalina/session/FileStore.java b/java/org/apache/catalina/session/FileStore.java
|
||||
index 0c7f728..f77b46a 100644
|
||||
--- a/java/org/apache/catalina/session/FileStore.java
|
||||
+++ b/java/org/apache/catalina/session/FileStore.java
|
||||
@@ -356,7 +356,7 @@ public final class FileStore extends StoreBase {
|
||||
File file = new File(storageDir, filename);
|
||||
|
||||
// Check the file is within the storage directory
|
||||
- if (!file.getCanonicalPath().startsWith(storageDir.getCanonicalPath())) {
|
||||
+ if (!file.getCanonicalFile().toPath().startsWith(storageDir.getCanonicalFile().toPath())) {
|
||||
log.warn(sm.getString("fileStore.invalid", file.getPath(), id));
|
||||
return null;
|
||||
}
|
||||
diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
|
||||
index a4210f8..5202253 100644
|
||||
--- a/java/org/apache/catalina/startup/ContextConfig.java
|
||||
+++ b/java/org/apache/catalina/startup/ContextConfig.java
|
||||
@@ -651,7 +651,8 @@ public class ContextConfig implements LifecycleListener {
|
||||
String docBaseCanonical = docBaseAbsoluteFile.getCanonicalPath();
|
||||
|
||||
// Re-calculate now docBase is a canonical path
|
||||
- boolean docBaseCanonicalInAppBase = docBaseCanonical.startsWith(appBase.getPath() + File.separatorChar);
|
||||
+ boolean docBaseCanonicalInAppBase =
|
||||
+ docBaseAbsoluteFile.getCanonicalFile().toPath().startsWith(appBase.toPath());
|
||||
String docBase;
|
||||
if (docBaseCanonicalInAppBase) {
|
||||
docBase = docBaseCanonical.substring(appBase.getPath().length());
|
||||
diff --git a/java/org/apache/catalina/startup/ExpandWar.java b/java/org/apache/catalina/startup/ExpandWar.java
|
||||
index 7fd7144..55fe1f5 100644
|
||||
--- a/java/org/apache/catalina/startup/ExpandWar.java
|
||||
+++ b/java/org/apache/catalina/startup/ExpandWar.java
|
||||
@@ -26,6 +26,7 @@ import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.channels.FileChannel;
|
||||
+import java.nio.file.Path;
|
||||
import java.util.Enumeration;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
@@ -116,10 +117,7 @@ public class ExpandWar {
|
||||
}
|
||||
|
||||
// Expand the WAR into the new document base directory
|
||||
- String canonicalDocBasePrefix = docBase.getCanonicalPath();
|
||||
- if (!canonicalDocBasePrefix.endsWith(File.separator)) {
|
||||
- canonicalDocBasePrefix += File.separator;
|
||||
- }
|
||||
+ Path canonicalDocBasePath = docBase.getCanonicalFile().toPath();
|
||||
|
||||
// Creating war tracker parent (normally META-INF)
|
||||
File warTrackerParent = warTracker.getParentFile();
|
||||
@@ -134,14 +132,13 @@ public class ExpandWar {
|
||||
JarEntry jarEntry = jarEntries.nextElement();
|
||||
String name = jarEntry.getName();
|
||||
File expandedFile = new File(docBase, name);
|
||||
- if (!expandedFile.getCanonicalPath().startsWith(
|
||||
- canonicalDocBasePrefix)) {
|
||||
+ if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) {
|
||||
// Trying to expand outside the docBase
|
||||
// Throw an exception to stop the deployment
|
||||
throw new IllegalArgumentException(
|
||||
sm.getString("expandWar.illegalPath",war, name,
|
||||
expandedFile.getCanonicalPath(),
|
||||
- canonicalDocBasePrefix));
|
||||
+ canonicalDocBasePath));
|
||||
}
|
||||
int last = name.lastIndexOf('/');
|
||||
if (last >= 0) {
|
||||
@@ -217,10 +214,7 @@ public class ExpandWar {
|
||||
File docBase = new File(host.getAppBaseFile(), pathname);
|
||||
|
||||
// Calculate the document base directory
|
||||
- String canonicalDocBasePrefix = docBase.getCanonicalPath();
|
||||
- if (!canonicalDocBasePrefix.endsWith(File.separator)) {
|
||||
- canonicalDocBasePrefix += File.separator;
|
||||
- }
|
||||
+ Path canonicalDocBasePath = docBase.getCanonicalFile().toPath();
|
||||
JarURLConnection juc = (JarURLConnection) war.openConnection();
|
||||
juc.setUseCaches(false);
|
||||
try (JarFile jarFile = juc.getJarFile()) {
|
||||
@@ -229,14 +223,13 @@ public class ExpandWar {
|
||||
JarEntry jarEntry = jarEntries.nextElement();
|
||||
String name = jarEntry.getName();
|
||||
File expandedFile = new File(docBase, name);
|
||||
- if (!expandedFile.getCanonicalPath().startsWith(
|
||||
- canonicalDocBasePrefix)) {
|
||||
+ if (!expandedFile.getCanonicalFile().toPath().startsWith(canonicalDocBasePath)) {
|
||||
// Entry located outside the docBase
|
||||
// Throw an exception to stop the deployment
|
||||
throw new IllegalArgumentException(
|
||||
sm.getString("expandWar.illegalPath",war, name,
|
||||
expandedFile.getCanonicalPath(),
|
||||
- canonicalDocBasePrefix));
|
||||
+ canonicalDocBasePath));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
diff --git a/java/org/apache/catalina/startup/HostConfig.java b/java/org/apache/catalina/startup/HostConfig.java
|
||||
index a4dad6f..d7bf6a2 100644
|
||||
--- a/java/org/apache/catalina/startup/HostConfig.java
|
||||
+++ b/java/org/apache/catalina/startup/HostConfig.java
|
||||
@@ -597,8 +597,7 @@ public class HostConfig implements LifecycleListener {
|
||||
docBase = new File(host.getAppBaseFile(), context.getDocBase());
|
||||
}
|
||||
// If external docBase, register .xml as redeploy first
|
||||
- if (!docBase.getCanonicalPath().startsWith(
|
||||
- host.getAppBaseFile().getAbsolutePath() + File.separator)) {
|
||||
+ if (!docBase.getCanonicalFile().toPath().startsWith(host.getAppBaseFile().toPath())) {
|
||||
isExternal = true;
|
||||
deployedApp.redeployResources.put(
|
||||
contextXml.getAbsolutePath(),
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 1fc4907..bc37288 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -947,6 +947,10 @@
|
||||
Update the NSIS Installer used to build the Windows installer to version
|
||||
3.03. (kkolinko)
|
||||
</update>
|
||||
+ <scode>
|
||||
+ Use <code>java.nio.file.Path</code> to test for one directory being a
|
||||
+ sub-directory of another in a consistent way. (markt)
|
||||
+ </scode>
|
||||
</changelog>
|
||||
</subsection>
|
||||
</section>
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,204 +0,0 @@
|
||||
From 6a719704236d3ce02100606290ff59b6a11f6b20 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 13 Apr 2021 11:12:02 +0100
|
||||
Subject: [PATCH] Add attribute value escaping to support user names containing ';'
|
||||
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 79 ++++++++++++++++-
|
||||
.../TestJNDIRealmAttributeValueEscape.java | 86 +++++++++++++++++++
|
||||
2 files changed, 163 insertions(+), 2 deletions(-)
|
||||
create mode 100644 test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index 19fa704..54921dc 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -1603,8 +1603,11 @@ public class JNDIRealm extends RealmBase {
|
||||
if (username == null || userPatternArray[curUserPattern] == null)
|
||||
return null;
|
||||
|
||||
- // Form the dn from the user pattern
|
||||
- String dn = connection.userPatternFormatArray[curUserPattern].format(new String[] { username });
|
||||
+ // Form the DistinguishedName from the user pattern.
|
||||
+ // Escape in case username contains a character with special meaning in
|
||||
+ // an attribute value.
|
||||
+ String dn = connection.userPatternFormatArray[curUserPattern].format(
|
||||
+ new String[] { doAttributeValueEscaping(username) });
|
||||
|
||||
try {
|
||||
user = getUserByPattern(connection.context, username, attrIds, dn);
|
||||
@@ -2820,6 +2823,78 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
}
|
||||
|
||||
|
||||
+ /**
|
||||
+ * Implements the necessary escaping to represent an attribute value as a
|
||||
+ * String as per RFC 4514.
|
||||
+ *
|
||||
+ * @param input The original attribute value
|
||||
+ * @return The string representation of the attribute value
|
||||
+ */
|
||||
+ protected String doAttributeValueEscaping(String input) {
|
||||
+ int len = input.length();
|
||||
+ StringBuilder result = new StringBuilder();
|
||||
+
|
||||
+ for (int i = 0; i < len; i++) {
|
||||
+ char c = input.charAt(i);
|
||||
+ switch (c) {
|
||||
+ case ' ': {
|
||||
+ if (i == 0 || i == (len -1)) {
|
||||
+ result.append("\\20");
|
||||
+ } else {
|
||||
+ result.append(c);
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ case '#': {
|
||||
+ if (i == 0 ) {
|
||||
+ result.append("\\23");
|
||||
+ } else {
|
||||
+ result.append(c);
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ case '\"': {
|
||||
+ result.append("\\22");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '+': {
|
||||
+ result.append("\\2B");
|
||||
+ break;
|
||||
+ }
|
||||
+ case ',': {
|
||||
+ result.append("\\2C");
|
||||
+ break;
|
||||
+ }
|
||||
+ case ';': {
|
||||
+ result.append("\\3B");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '<': {
|
||||
+ result.append("\\3C");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '>': {
|
||||
+ result.append("\\3E");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '\\': {
|
||||
+ result.append("\\5C");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '\u0000': {
|
||||
+ result.append("\\00");
|
||||
+ break;
|
||||
+ }
|
||||
+ default:
|
||||
+ result.append(c);
|
||||
+ }
|
||||
+
|
||||
+ }
|
||||
+
|
||||
+ return result.toString();
|
||||
+ }
|
||||
+
|
||||
+
|
||||
protected static String convertToHexEscape(String input) {
|
||||
if (input.indexOf('\\') == -1) {
|
||||
// No escaping present. Return original.
|
||||
diff --git a/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java b/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
|
||||
new file mode 100644
|
||||
index 0000000..677bcc5
|
||||
--- /dev/null
|
||||
+++ b/test/org/apache/catalina/realm/TestJNDIRealmAttributeValueEscape.java
|
||||
@@ -0,0 +1,86 @@
|
||||
+/*
|
||||
+ * 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.realm;
|
||||
+
|
||||
+import java.util.ArrayList;
|
||||
+import java.util.Collection;
|
||||
+import java.util.List;
|
||||
+
|
||||
+import org.junit.Assert;
|
||||
+import org.junit.Test;
|
||||
+import org.junit.runner.RunWith;
|
||||
+import org.junit.runners.Parameterized;
|
||||
+import org.junit.runners.Parameterized.Parameter;
|
||||
+
|
||||
+@RunWith(Parameterized.class)
|
||||
+public class TestJNDIRealmAttributeValueEscape {
|
||||
+
|
||||
+ @Parameterized.Parameters(name = "{index}: in[{0}], out[{1}]")
|
||||
+ public static Collection<Object[]> parameters() {
|
||||
+ List<Object[]> parameterSets = new ArrayList<>();
|
||||
+
|
||||
+ // No escaping required
|
||||
+ parameterSets.add(new String[] { "none", "none" });
|
||||
+ // Simple cases (same order as RFC 4512 section 2)
|
||||
+ // Each appearing at the beginning, middle and ent
|
||||
+ parameterSets.add(new String[] { " test", "\\20test" });
|
||||
+ parameterSets.add(new String[] { "te st", "te st" });
|
||||
+ parameterSets.add(new String[] { "test ", "test\\20" });
|
||||
+ parameterSets.add(new String[] { "#test", "\\23test" });
|
||||
+ parameterSets.add(new String[] { "te#st", "te#st" });
|
||||
+ parameterSets.add(new String[] { "test#", "test#" });
|
||||
+ parameterSets.add(new String[] { "\"test", "\\22test" });
|
||||
+ parameterSets.add(new String[] { "te\"st", "te\\22st" });
|
||||
+ parameterSets.add(new String[] { "test\"", "test\\22" });
|
||||
+ parameterSets.add(new String[] { "+test", "\\2Btest" });
|
||||
+ parameterSets.add(new String[] { "te+st", "te\\2Bst" });
|
||||
+ parameterSets.add(new String[] { "test+", "test\\2B" });
|
||||
+ parameterSets.add(new String[] { ",test", "\\2Ctest" });
|
||||
+ parameterSets.add(new String[] { "te,st", "te\\2Cst" });
|
||||
+ parameterSets.add(new String[] { "test,", "test\\2C" });
|
||||
+ parameterSets.add(new String[] { ";test", "\\3Btest" });
|
||||
+ parameterSets.add(new String[] { "te;st", "te\\3Bst" });
|
||||
+ parameterSets.add(new String[] { "test;", "test\\3B" });
|
||||
+ parameterSets.add(new String[] { "<test", "\\3Ctest" });
|
||||
+ parameterSets.add(new String[] { "te<st", "te\\3Cst" });
|
||||
+ parameterSets.add(new String[] { "test<", "test\\3C" });
|
||||
+ parameterSets.add(new String[] { ">test", "\\3Etest" });
|
||||
+ parameterSets.add(new String[] { "te>st", "te\\3Est" });
|
||||
+ parameterSets.add(new String[] { "test>", "test\\3E" });
|
||||
+ parameterSets.add(new String[] { "\\test", "\\5Ctest" });
|
||||
+ parameterSets.add(new String[] { "te\\st", "te\\5Cst" });
|
||||
+ parameterSets.add(new String[] { "test\\", "test\\5C" });
|
||||
+ parameterSets.add(new String[] { "\u0000test", "\\00test" });
|
||||
+ parameterSets.add(new String[] { "te\u0000st", "te\\00st" });
|
||||
+ parameterSets.add(new String[] { "test\u0000", "test\\00" });
|
||||
+ return parameterSets;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Parameter(0)
|
||||
+ public String in;
|
||||
+ @Parameter(1)
|
||||
+ public String out;
|
||||
+
|
||||
+ private JNDIRealm realm = new JNDIRealm();
|
||||
+
|
||||
+ @Test
|
||||
+ public void testConvertToHexEscape() throws Exception {
|
||||
+ String result = realm.doAttributeValueEscaping(in);
|
||||
+ Assert.assertEquals(out, result);
|
||||
+ }
|
||||
+}
|
||||
\ No newline at end of file
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
From f9a89674c08b55677424df7bd41685e72316e6bf Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 13 Apr 2021 11:35:07 +0100
|
||||
Subject: [PATCH] Rename for clarity
|
||||
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 30 +++++++++++++++++--
|
||||
1 file changed, 28 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index 54921dc..b60f393 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -1942,7 +1942,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
return list;
|
||||
|
||||
// Set up parameters for an appropriate search
|
||||
- String filter = connection.roleFormat.format(new String[] { doRFC2254Encoding(dn), username, userRoleId });
|
||||
+ String filter = connection.roleFormat.format(new String[] { doFilterEscaping(dn), username, userRoleId });
|
||||
SearchControls controls = new SearchControls();
|
||||
if (roleSubtree)
|
||||
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
@@ -2010,7 +2010,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
Map<String, String> newThisRound = new HashMap<>(); // Stores the groups we find in this iteration
|
||||
|
||||
for (Entry<String, String> group : newGroups.entrySet()) {
|
||||
- filter = connection.roleFormat.format(new String[] { doRFC2254Encoding(group.getKey()),
|
||||
+ filter = connection.roleFormat.format(new String[] { doFilterEscaping(group.getKey()),
|
||||
group.getValue(), group.getValue() });
|
||||
|
||||
if (containerLog.isTraceEnabled()) {
|
||||
@@ -2730,10 +2730,36 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
* ) -> \29
|
||||
* \ -> \5c
|
||||
* \0 -> \00
|
||||
+ *
|
||||
* @param inString string to escape according to RFC 2254 guidelines
|
||||
+ *
|
||||
* @return String the escaped/encoded result
|
||||
+ *
|
||||
+ * @deprecated Will be removed in Tomcat 10.1.x onwards
|
||||
*/
|
||||
+ @Deprecated
|
||||
protected String doRFC2254Encoding(String inString) {
|
||||
+ return doFilterEscaping(inString);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
+ * Given an LDAP search string, returns the string with certain characters
|
||||
+ * escaped according to RFC 2254 guidelines.
|
||||
+ * The character mapping is as follows:
|
||||
+ * char -> Replacement
|
||||
+ * ---------------------------
|
||||
+ * * -> \2a
|
||||
+ * ( -> \28
|
||||
+ * ) -> \29
|
||||
+ * \ -> \5c
|
||||
+ * \0 -> \00
|
||||
+ *
|
||||
+ * @param inString string to escape according to RFC 2254 guidelines
|
||||
+ *
|
||||
+ * @return String the escaped/encoded result
|
||||
+ */
|
||||
+ protected String doFilterEscaping(String inString) {
|
||||
StringBuilder buf = new StringBuilder(inString.length());
|
||||
for (int i = 0; i < inString.length(); i++) {
|
||||
char c = inString.charAt(i);
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
From 2e3924d0a8372ced148b42016432c038dd1ae487 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 13 Apr 2021 11:43:51 +0100
|
||||
Subject: [PATCH] Expand tests and fix escaping issue when searching for users by filter
|
||||
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index b60f393..dcec473 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -1648,7 +1648,9 @@ public class JNDIRealm extends RealmBase {
|
||||
return null;
|
||||
|
||||
// Form the search filter
|
||||
- String filter = connection.userSearchFormat.format(new String[] { username });
|
||||
+ // Escape in case username contains a character with special meaning in
|
||||
+ // a search filter.
|
||||
+ String filter = connection.userSearchFormat.format(new String[] { doFilterEscaping(username) });
|
||||
|
||||
// Set up the search controls
|
||||
SearchControls constraints = new SearchControls();
|
||||
@@ -1913,6 +1915,8 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
if (user == null)
|
||||
return null;
|
||||
|
||||
+ // This is returned from the directory so will be attribute value
|
||||
+ // escaped if required
|
||||
String dn = user.getDN();
|
||||
String username = user.getUserName();
|
||||
String userRoleId = user.getUserRoleId();
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
From 954eb10e9957055f60ee1e427baabfa32fc3d78b Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 13 Apr 2021 12:11:35 +0100
|
||||
Subject: [PATCH] Expand tests and fix an issue in escaping for group search
|
||||
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index dcec473..1021ce8 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -1918,6 +1918,8 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
// This is returned from the directory so will be attribute value
|
||||
// escaped if required
|
||||
String dn = user.getDN();
|
||||
+ // This is the name the user provided to the authentication process so
|
||||
+ // it will not be escaped
|
||||
String username = user.getUserName();
|
||||
String userRoleId = user.getUserRoleId();
|
||||
|
||||
@@ -1946,7 +1948,10 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
return list;
|
||||
|
||||
// Set up parameters for an appropriate search
|
||||
- String filter = connection.roleFormat.format(new String[] { doFilterEscaping(dn), username, userRoleId });
|
||||
+ String filter = connection.roleFormat.format(new String[] {
|
||||
+ doFilterEscaping(dn),
|
||||
+ doFilterEscaping(doAttributeValueEscaping(username)),
|
||||
+ userRoleId });
|
||||
SearchControls controls = new SearchControls();
|
||||
if (roleSubtree)
|
||||
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
From a13034d94c927286a7f4e17ab4f662727fbe6e9f Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 13 Apr 2021 12:20:06 +0100
|
||||
Subject: [PATCH] Expand tests and fix escaping issue in userRoleAttribute filter
|
||||
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 6 ++++--
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index 1021ce8..a3b6f86 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -1947,11 +1947,13 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
if ((connection.roleFormat == null) || (roleName == null))
|
||||
return list;
|
||||
|
||||
- // Set up parameters for an appropriate search
|
||||
+ // Set up parameters for an appropriate search filter
|
||||
+ // The dn is already attribute value escaped but the others are not
|
||||
+ // This is a filter so all input will require filter escaping
|
||||
String filter = connection.roleFormat.format(new String[] {
|
||||
doFilterEscaping(dn),
|
||||
doFilterEscaping(doAttributeValueEscaping(username)),
|
||||
- userRoleId });
|
||||
+ doFilterEscaping(doAttributeValueEscaping(userRoleId)) });
|
||||
SearchControls controls = new SearchControls();
|
||||
if (roleSubtree)
|
||||
controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
From fd48ca875aaa46920b6d94fe737420d3985ad7d4 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 13 Apr 2021 12:54:24 +0100
|
||||
Subject: [PATCH] Expanded tests to cover nested roles and fix escaping issues in search
|
||||
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 9 +++++++--
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index a3b6f86..cfe1c15 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -2021,8 +2021,13 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
Map<String, String> newThisRound = new HashMap<>(); // Stores the groups we find in this iteration
|
||||
|
||||
for (Entry<String, String> group : newGroups.entrySet()) {
|
||||
- filter = connection.roleFormat.format(new String[] { doFilterEscaping(group.getKey()),
|
||||
- group.getValue(), group.getValue() });
|
||||
+ // Group key is already value escaped if required
|
||||
+ // Group value is not value escaped
|
||||
+ // Everything needs to be filter escaped
|
||||
+ filter = connection.roleFormat.format(new String[] {
|
||||
+ doFilterEscaping(group.getKey()),
|
||||
+ doFilterEscaping(doAttributeValueEscaping(group.getValue())),
|
||||
+ doFilterEscaping(doAttributeValueEscaping(group.getValue())) });
|
||||
|
||||
if (containerLog.isTraceEnabled()) {
|
||||
containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter);
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
From 3383668c05becf01fe175aba928177b648f327ec Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 13 Apr 2021 14:47:07 +0100
|
||||
Subject: [PATCH] Expand testing to cover substitution in roleBase. Fix bugs.
|
||||
|
||||
The code incorrectly referred to the original roleBase rather than the local version that includes the substituted value(s).
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index cfe1c15..c78068b 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -1988,7 +1988,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
Attributes attrs = result.getAttributes();
|
||||
if (attrs == null)
|
||||
continue;
|
||||
- String dname = getDistinguishedName(connection.context, roleBase, result);
|
||||
+ String dname = getDistinguishedName(connection.context, base, result);
|
||||
String name = getAttributeValue(roleName, attrs);
|
||||
if (name != null && dname != null) {
|
||||
groupMap.put(dname, name);
|
||||
@@ -2033,7 +2033,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter);
|
||||
}
|
||||
|
||||
- results = searchAsUser(connection.context, user, roleBase, filter, controls,
|
||||
+ results = searchAsUser(connection.context, user, base, filter, controls,
|
||||
isRoleSearchAsUser());
|
||||
|
||||
try {
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
From c703ec491aca94cb17853808c7ce0c4fd99992bb Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 13 Apr 2021 15:19:31 +0100
|
||||
Subject: [PATCH] Expand tests to cover escaping of substituted roleBaes values
|
||||
|
||||
While the UnboundedID LDAP SDK doesn't appear to have a preference some servers (Windows AD, OpenLDAP) do appear to.
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index c78068b..7a8c5f6 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -1967,7 +1967,9 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
Name name = np.parse(dn);
|
||||
String nameParts[] = new String[name.size()];
|
||||
for (int i = 0; i < name.size(); i++) {
|
||||
- nameParts[i] = name.get(i);
|
||||
+ // May have been returned with \<char> escaping rather than
|
||||
+ // \<hex><hex>. Make sure it is \<hex><hex>.
|
||||
+ nameParts[i] = convertToHexEscape(name.get(i));
|
||||
}
|
||||
base = connection.roleBaseFormat.format(nameParts);
|
||||
} else {
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
From 700d26b69df3f1003ce8443d5569911c36b113de Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 5 Mar 2019 19:19:32 +0000
|
||||
Subject: [PATCH] Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63213
|
||||
|
||||
Ensure the correct escaping of group names when searching for nested
|
||||
groups when the JNDIRealm is configured with roleNested set to true.
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 3 ++-
|
||||
webapps/docs/changelog.xml | 5 +++++
|
||||
2 files changed, 7 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index e980bdf..034c0f0 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -2010,7 +2010,8 @@ public class JNDIRealm extends RealmBase {
|
||||
Map<String, String> newThisRound = new HashMap<>(); // Stores the groups we find in this iteration
|
||||
|
||||
for (Entry<String, String> group : newGroups.entrySet()) {
|
||||
- filter = roleFormat.format(new String[] { group.getKey(), group.getValue(), group.getValue() });
|
||||
+ filter = roleFormat.format(new String[] { doRFC2254Encoding(group.getKey()),
|
||||
+ group.getValue(), group.getValue() });
|
||||
|
||||
if (containerLog.isTraceEnabled()) {
|
||||
containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter);
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 35b8eab..f088e0d 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -55,6 +55,11 @@
|
||||
<fix>
|
||||
Encode the output of the SSI <code>printenv</code> command. (markt)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ <bug>63213</bug>: Ensure the correct escaping of group names when
|
||||
+ searching for nested groups when the JNDIRealm is configured with
|
||||
+ <code>roleNested</code> set to <code>true</code>. (markt)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
</section>
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
From 824c531393aa030f161e1ec352a65b7e9302d6b6 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Fri, 26 Jul 2019 14:59:57 +0100
|
||||
Subject: [PATCH] Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=63550
|
||||
|
||||
Only use the alternateURL for the JNDIRealm when it has been specified
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 4 ++++
|
||||
webapps/docs/changelog.xml | 4 ++++
|
||||
2 files changed, 8 insertions(+)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index 034c0f0..505dd13 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -2378,6 +2378,10 @@ public class JNDIRealm extends RealmBase {
|
||||
context = createDirContext(getDirectoryContextEnvironment());
|
||||
|
||||
} catch (Exception e) {
|
||||
+ if (alternateURL == null || alternateURL.length() == 0) {
|
||||
+ // No alternate URL. Re-throw the exception.
|
||||
+ throw e;
|
||||
+ }
|
||||
|
||||
connectionAttempt = 1;
|
||||
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index f088e0d..7bcc3d9 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -248,6 +248,10 @@
|
||||
</subsection>
|
||||
<subsection name="Jasper">
|
||||
<changelog>
|
||||
+ <fix>
|
||||
+ <bug>63550</bug>: Only try the <code>alternateURL</code> in the
|
||||
+ <code>JNDIRealm</code> if one has been specified. (markt)
|
||||
+ </fix>
|
||||
<add>
|
||||
<bug>50234</bug>: Add the capability to generate a web-fragment.xml file
|
||||
to JspC. (markt)
|
||||
--
|
||||
2.23.0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,53 +0,0 @@
|
||||
From 36710841d24807a6837757a24952ab5e6ced6ec8 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 23 Jan 2019 15:09:37 +0000
|
||||
Subject: [PATCH] Refactor to simplify the fix for BZ 63026
|
||||
|
||||
git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc8.5.x/trunk@1851939 13f79535-47bb-0310-9956-ffa450edef68
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index b624c5b..5714496 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -2763,6 +2763,7 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
// we need to composite a name with the base name, the context name, and
|
||||
// the result name. For non-relative names, use the returned name.
|
||||
String resultName = result.getName();
|
||||
+ Name name;
|
||||
if (result.isRelative()) {
|
||||
if (containerLog.isTraceEnabled()) {
|
||||
containerLog.trace(" search returned relative name: " + resultName);
|
||||
@@ -2774,9 +2775,8 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
// Bugzilla 32269
|
||||
Name entryName = parser.parse(new CompositeName(resultName).get(0));
|
||||
|
||||
- Name name = contextName.addAll(baseName);
|
||||
+ name = contextName.addAll(baseName);
|
||||
name = name.addAll(entryName);
|
||||
- return name.toString();
|
||||
} else {
|
||||
if (containerLog.isTraceEnabled()) {
|
||||
containerLog.trace(" search returned absolute name: " + resultName);
|
||||
@@ -2792,14 +2792,14 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
"Search returned unparseable absolute name: " +
|
||||
resultName );
|
||||
}
|
||||
- Name name = parser.parse(pathComponent.substring(1));
|
||||
- return name.toString();
|
||||
+ name = parser.parse(pathComponent.substring(1));
|
||||
} catch ( URISyntaxException e ) {
|
||||
throw new InvalidNameException(
|
||||
"Search returned unparseable absolute name: " +
|
||||
resultName );
|
||||
}
|
||||
}
|
||||
+ return name.toString();
|
||||
}
|
||||
|
||||
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,250 +0,0 @@
|
||||
From 4bee1e769bce86cd53ce80eb18c15449ea0df34b Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 23 Jan 2019 15:11:07 +0000
|
||||
Subject: [PATCH] Add a new attribute, forceDnHexEscape, to the JNDIRealm that
|
||||
forces escaping in the String representation of a distinguished name to use
|
||||
the \nn form. This may avoid issues with realms using Active Directory which
|
||||
appears to be more tolerant of optional escaping when the \nn form is used.
|
||||
|
||||
git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc8.5.x/trunk@1851941 13f79535-47bb-0310-9956-ffa450edef68
|
||||
---
|
||||
java/org/apache/catalina/realm/JNDIRealm.java | 95 ++++++++++++++++++-
|
||||
.../TestJNDIRealmConvertToHexEscape.java | 70 ++++++++++++++
|
||||
webapps/docs/changelog.xml | 8 ++
|
||||
webapps/docs/config/realm.xml | 9 ++
|
||||
4 files changed, 181 insertions(+), 1 deletion(-)
|
||||
create mode 100644 test/org/apache/catalina/realm/TestJNDIRealmConvertToHexEscape.java
|
||||
|
||||
diff --git a/java/org/apache/catalina/realm/JNDIRealm.java b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
index 5714496..19fa704 100644
|
||||
--- a/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
+++ b/java/org/apache/catalina/realm/JNDIRealm.java
|
||||
@@ -487,8 +487,19 @@ public class JNDIRealm extends RealmBase {
|
||||
protected int connectionPoolSize = 1;
|
||||
|
||||
|
||||
+ private boolean forceDnHexEscape = false;
|
||||
+
|
||||
+
|
||||
// ------------------------------------------------------------- Properties
|
||||
|
||||
+ public boolean getForceDnHexEscape() {
|
||||
+ return forceDnHexEscape;
|
||||
+ }
|
||||
+
|
||||
+ public void setForceDnHexEscape(boolean forceDnHexEscape) {
|
||||
+ this.forceDnHexEscape = forceDnHexEscape;
|
||||
+ }
|
||||
+
|
||||
/**
|
||||
* @return the type of authentication to use.
|
||||
*/
|
||||
@@ -2799,7 +2810,89 @@ System.out.println("userRoleName " + userRoleName + " " + attrs.get(userRoleName
|
||||
resultName );
|
||||
}
|
||||
}
|
||||
- return name.toString();
|
||||
+
|
||||
+ if (getForceDnHexEscape()) {
|
||||
+ // Bug 63026
|
||||
+ return convertToHexEscape(name.toString());
|
||||
+ } else {
|
||||
+ return name.toString();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ protected static String convertToHexEscape(String input) {
|
||||
+ if (input.indexOf('\\') == -1) {
|
||||
+ // No escaping present. Return original.
|
||||
+ return input;
|
||||
+ }
|
||||
+
|
||||
+ // +6 allows for 3 escaped characters by default
|
||||
+ StringBuilder result = new StringBuilder(input.length() + 6);
|
||||
+ boolean previousSlash = false;
|
||||
+ for (int i = 0; i < input.length(); i++) {
|
||||
+ char c = input.charAt(i);
|
||||
+
|
||||
+ if (previousSlash) {
|
||||
+ switch (c) {
|
||||
+ case ' ': {
|
||||
+ result.append("\\20");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '\"': {
|
||||
+ result.append("\\22");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '#': {
|
||||
+ result.append("\\23");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '+': {
|
||||
+ result.append("\\2B");
|
||||
+ break;
|
||||
+ }
|
||||
+ case ',': {
|
||||
+ result.append("\\2C");
|
||||
+ break;
|
||||
+ }
|
||||
+ case ';': {
|
||||
+ result.append("\\3B");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '<': {
|
||||
+ result.append("\\3C");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '=': {
|
||||
+ result.append("\\3D");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '>': {
|
||||
+ result.append("\\3E");
|
||||
+ break;
|
||||
+ }
|
||||
+ case '\\': {
|
||||
+ result.append("\\5C");
|
||||
+ break;
|
||||
+ }
|
||||
+ default:
|
||||
+ result.append('\\');
|
||||
+ result.append(c);
|
||||
+ }
|
||||
+ previousSlash = false;
|
||||
+ } else {
|
||||
+ if (c == '\\') {
|
||||
+ previousSlash = true;
|
||||
+ } else {
|
||||
+ result.append(c);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (previousSlash) {
|
||||
+ result.append('\\');
|
||||
+ }
|
||||
+
|
||||
+ return result.toString();
|
||||
}
|
||||
|
||||
|
||||
diff --git a/test/org/apache/catalina/realm/TestJNDIRealmConvertToHexEscape.java b/test/org/apache/catalina/realm/TestJNDIRealmConvertToHexEscape.java
|
||||
new file mode 100644
|
||||
index 0000000..8c610a3
|
||||
--- /dev/null
|
||||
+++ b/test/org/apache/catalina/realm/TestJNDIRealmConvertToHexEscape.java
|
||||
@@ -0,0 +1,70 @@
|
||||
+/*
|
||||
+ * 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.realm;
|
||||
+
|
||||
+import java.util.ArrayList;
|
||||
+import java.util.Collection;
|
||||
+import java.util.List;
|
||||
+
|
||||
+import org.junit.Assert;
|
||||
+import org.junit.Test;
|
||||
+import org.junit.runner.RunWith;
|
||||
+import org.junit.runners.Parameterized;
|
||||
+import org.junit.runners.Parameterized.Parameter;
|
||||
+
|
||||
+@RunWith(Parameterized.class)
|
||||
+public class TestJNDIRealmConvertToHexEscape {
|
||||
+
|
||||
+ @Parameterized.Parameters(name = "{index}: in[{0}], out[{1}]")
|
||||
+ public static Collection<Object[]> parameters() {
|
||||
+ List<Object[]> parameterSets = new ArrayList<>();
|
||||
+
|
||||
+ parameterSets.add(new String[] { "none", "none" });
|
||||
+ parameterSets.add(new String[] { "\\", "\\" });
|
||||
+ parameterSets.add(new String[] { "\\\\", "\\5C" });
|
||||
+ parameterSets.add(new String[] { "\\5C", "\\5C" });
|
||||
+ parameterSets.add(new String[] { "\\ ", "\\20" });
|
||||
+ parameterSets.add(new String[] { "\\20", "\\20" });
|
||||
+ parameterSets.add(new String[] { "\\ foo", "\\20foo" });
|
||||
+ parameterSets.add(new String[] { "\\20foo", "\\20foo" });
|
||||
+ parameterSets.add(new String[] { "\\ foo", "\\20 foo" });
|
||||
+ parameterSets.add(new String[] { "\\20 foo", "\\20 foo" });
|
||||
+ parameterSets.add(new String[] { "\\ \\ foo", "\\20\\20foo" });
|
||||
+ parameterSets.add(new String[] { "\\20\\20foo", "\\20\\20foo" });
|
||||
+ parameterSets.add(new String[] { "foo\\ ", "foo\\20" });
|
||||
+ parameterSets.add(new String[] { "foo\\20", "foo\\20" });
|
||||
+ parameterSets.add(new String[] { "foo \\ ", "foo \\20" });
|
||||
+ parameterSets.add(new String[] { "foo \\20", "foo \\20" });
|
||||
+ parameterSets.add(new String[] { "foo\\ \\ ", "foo\\20\\20" });
|
||||
+ parameterSets.add(new String[] { "foo\\20\\20", "foo\\20\\20" });
|
||||
+
|
||||
+ return parameterSets;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Parameter(0)
|
||||
+ public String in;
|
||||
+ @Parameter(1)
|
||||
+ public String out;
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testConvertToHexEscape() throws Exception {
|
||||
+ String result = JNDIRealm.convertToHexEscape(in);
|
||||
+ Assert.assertEquals(out, result);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 173c209..a7bb52c 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -794,6 +794,14 @@
|
||||
a plain text response. Based on a suggestion from Muthukumar Marikani.
|
||||
(markt)
|
||||
</add>
|
||||
+ <add>
|
||||
+ <bug>63026</bug>: Add a new attribute, <code>forceDnHexEscape</code>, to
|
||||
+ the <code>JNDIRealm</code> that forces escaping in the String
|
||||
+ representation of a distinguished name to use the <code>\nn</code> form.
|
||||
+ This may avoid issues with realms using Active Directory which appears
|
||||
+ to be more tolerant of optional escaping when the <code>\nn</code> form
|
||||
+ is used. (markt)
|
||||
+ </add>
|
||||
</changelog>
|
||||
</subsection>
|
||||
</section>
|
||||
diff --git a/webapps/docs/config/realm.xml b/webapps/docs/config/realm.xml
|
||||
index a4bc5ef..715ceb7 100644
|
||||
--- a/webapps/docs/config/realm.xml
|
||||
+++ b/webapps/docs/config/realm.xml
|
||||
@@ -463,6 +463,15 @@
|
||||
"finding" and "searching". If not specified, "always" is used.</p>
|
||||
</attribute>
|
||||
|
||||
+ <attribute name="forceDnHexEscape" required="false">
|
||||
+ <p>A setting of <code>true</code> forces escaping in the String
|
||||
+ representation of a distinguished name to use the <code>\nn</code> form.
|
||||
+ This may avoid issues with realms using Active Directory which appears
|
||||
+ to be more tolerant of optional escaping when the <code>\nn</code> form
|
||||
+ is used. If not specified, the default of <code>false</code> will be
|
||||
+ used.</p>
|
||||
+ </attribute>
|
||||
+
|
||||
<attribute name="hostnameVerifierClassName" required="false">
|
||||
<p>The name of the class to use for hostname verification when
|
||||
using StartTLS for securing the connection to the ldap server.
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,160 +0,0 @@
|
||||
From 94a5f7b95adee95fbc945767a71c27e329970a80 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 10 May 2021 21:20:46 +0100
|
||||
Subject: [PATCH] Remove support for the identity T-E header value
|
||||
|
||||
---
|
||||
.../apache/coyote/http11/Http11Processor.java | 7 +-
|
||||
.../coyote/http11/TestHttp11Processor.java | 95 ++++++++++++++-----
|
||||
webapps/docs/changelog.xml | 6 ++
|
||||
3 files changed, 78 insertions(+), 30 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
|
||||
index 86556ec..c840c83 100644
|
||||
--- a/java/org/apache/coyote/http11/Http11Processor.java
|
||||
+++ b/java/org/apache/coyote/http11/Http11Processor.java
|
||||
@@ -243,11 +243,8 @@ public class Http11Processor extends AbstractProcessor {
|
||||
// encoding names are case insensitive. (RFC2616, section 3.6)
|
||||
encodingName = encodingName.trim().toLowerCase(Locale.ENGLISH);
|
||||
|
||||
- if (encodingName.equals("identity")) {
|
||||
- // Skip
|
||||
- } else if (encodingName.equals("chunked")) {
|
||||
- inputBuffer.addActiveFilter
|
||||
- (inputFilters[Constants.CHUNKED_FILTER]);
|
||||
+ if (encodingName.equals("chunked")) {
|
||||
+ inputBuffer.addActiveFilter(inputFilters[Constants.CHUNKED_FILTER]);
|
||||
contentDelimitation = true;
|
||||
} else {
|
||||
for (int i = pluggableFilterIndex; i < inputFilters.length; i++) {
|
||||
diff --git a/test/org/apache/coyote/http11/TestHttp11Processor.java b/test/org/apache/coyote/http11/TestHttp11Processor.java
|
||||
index 5357526..1d12007 100644
|
||||
--- a/test/org/apache/coyote/http11/TestHttp11Processor.java
|
||||
+++ b/test/org/apache/coyote/http11/TestHttp11Processor.java
|
||||
@@ -249,31 +249,6 @@ public class TestHttp11Processor extends TomcatBaseTest {
|
||||
}
|
||||
|
||||
|
||||
- @Test
|
||||
- public void testWithTEIdentity() throws Exception {
|
||||
- getTomcatInstanceTestWebapp(false, true);
|
||||
-
|
||||
- String request =
|
||||
- "POST /test/echo-params.jsp HTTP/1.1" + SimpleHttpClient.CRLF +
|
||||
- "Host: any" + SimpleHttpClient.CRLF +
|
||||
- "Transfer-encoding: identity" + SimpleHttpClient.CRLF +
|
||||
- "Content-Length: 9" + SimpleHttpClient.CRLF +
|
||||
- "Content-Type: application/x-www-form-urlencoded" +
|
||||
- SimpleHttpClient.CRLF +
|
||||
- "Connection: close" + SimpleHttpClient.CRLF +
|
||||
- SimpleHttpClient.CRLF +
|
||||
- "test=data";
|
||||
-
|
||||
- Client client = new Client(getPort());
|
||||
- client.setRequest(new String[] {request});
|
||||
-
|
||||
- client.connect();
|
||||
- client.processRequest();
|
||||
- Assert.assertTrue(client.isResponse200());
|
||||
- Assert.assertTrue(client.getResponseBody().contains("test - data"));
|
||||
- }
|
||||
-
|
||||
-
|
||||
@Test
|
||||
public void testWithTESavedRequest() throws Exception {
|
||||
getTomcatInstanceTestWebapp(false, true);
|
||||
@@ -1308,4 +1283,74 @@ public class TestHttp11Processor extends TomcatBaseTest {
|
||||
// Expected response is a 200 response.
|
||||
Assert.assertTrue(client.isResponse200());
|
||||
}
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testTEHeaderUnknown01() throws Exception {
|
||||
+ doTestTEHeaderUnknown("identity");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testTEHeaderUnknown02() throws Exception {
|
||||
+ doTestTEHeaderUnknown("identity, chunked");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testTEHeaderUnknown03() throws Exception {
|
||||
+ doTestTEHeaderUnknown("unknown, chunked");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testTEHeaderUnknown04() throws Exception {
|
||||
+ doTestTEHeaderUnknown("void");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testTEHeaderUnknown05() throws Exception {
|
||||
+ doTestTEHeaderUnknown("void, chunked");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testTEHeaderUnknown06() throws Exception {
|
||||
+ doTestTEHeaderUnknown("void, identity");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testTEHeaderUnknown07() throws Exception {
|
||||
+ doTestTEHeaderUnknown("identity, void");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private void doTestTEHeaderUnknown(String headerValue) throws Exception {
|
||||
+ Tomcat tomcat = getTomcatInstance();
|
||||
+
|
||||
+ // No file system docBase required
|
||||
+ Context ctx = tomcat.addContext("", null);
|
||||
+
|
||||
+ // Add servlet
|
||||
+ Tomcat.addServlet(ctx, "TesterServlet", new TesterServlet(false));
|
||||
+ ctx.addServletMappingDecoded("/foo", "TesterServlet");
|
||||
+
|
||||
+ tomcat.start();
|
||||
+
|
||||
+ String request =
|
||||
+ "GET /foo HTTP/1.1" + SimpleHttpClient.CRLF +
|
||||
+ "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
|
||||
+ "Transfer-Encoding: " + headerValue + SimpleHttpClient.CRLF +
|
||||
+ SimpleHttpClient.CRLF;
|
||||
+
|
||||
+ Client client = new Client(tomcat.getConnector().getLocalPort());
|
||||
+ client.setRequest(new String[] {request});
|
||||
+
|
||||
+ client.connect();
|
||||
+ client.processRequest(false);
|
||||
+
|
||||
+ Assert.assertTrue(client.isResponse501());
|
||||
+ }
|
||||
}
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index bc37288..94a0d94 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -309,6 +309,12 @@
|
||||
Enable host name verification when using TLS with the WebSocket client.
|
||||
(markt)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ Remove support for the <code>identity</code> transfer encoding. The
|
||||
+ inclusion of this encoding in RFC 2616 was an error that was corrected
|
||||
+ in 2001. Requests using this transfer encoding will now receive a 501
|
||||
+ response. (markt)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
<subsection name="Web applications">
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,81 +0,0 @@
|
||||
From 66bd71277cedd04af2772942c697e15d5c401de9 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 10 May 2021 21:59:44 +0100
|
||||
Subject: [PATCH] Process T-E header from both HTTP 1.0 and HTTP 1.1.clients
|
||||
|
||||
---
|
||||
.../apache/coyote/http11/Http11Processor.java | 4 ++-
|
||||
.../coyote/http11/TestHttp11Processor.java | 28 +++++++++++++++++++
|
||||
webapps/docs/changelog.xml | 4 +++
|
||||
3 files changed, 35 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
|
||||
index c840c83..4021355 100644
|
||||
--- a/java/org/apache/coyote/http11/Http11Processor.java
|
||||
+++ b/java/org/apache/coyote/http11/Http11Processor.java
|
||||
@@ -766,7 +766,9 @@ public class Http11Processor extends AbstractProcessor {
|
||||
InputFilter[] inputFilters = inputBuffer.getFilters();
|
||||
|
||||
// Parse transfer-encoding header
|
||||
- if (http11) {
|
||||
+ // HTTP specs say an HTTP 1.1 server should accept any recognised
|
||||
+ // HTTP 1.x header from a 1.x client unless the specs says otherwise.
|
||||
+ if (!http09) {
|
||||
MessageBytes transferEncodingValueMB = headers.getValue("transfer-encoding");
|
||||
if (transferEncodingValueMB != null) {
|
||||
String transferEncodingValue = transferEncodingValueMB.toString();
|
||||
diff --git a/test/org/apache/coyote/http11/TestHttp11Processor.java b/test/org/apache/coyote/http11/TestHttp11Processor.java
|
||||
index 1d12007..84fdd42 100644
|
||||
--- a/test/org/apache/coyote/http11/TestHttp11Processor.java
|
||||
+++ b/test/org/apache/coyote/http11/TestHttp11Processor.java
|
||||
@@ -1353,4 +1353,32 @@ public class TestHttp11Processor extends TomcatBaseTest {
|
||||
|
||||
Assert.assertTrue(client.isResponse501());
|
||||
}
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testWithTEChunkedHttp10() throws Exception {
|
||||
+
|
||||
+ getTomcatInstanceTestWebapp(false, true);
|
||||
+
|
||||
+ String request =
|
||||
+ "POST /test/echo-params.jsp HTTP/1.0" + SimpleHttpClient.CRLF +
|
||||
+ "Host: any" + SimpleHttpClient.CRLF +
|
||||
+ "Transfer-encoding: chunked" + SimpleHttpClient.CRLF +
|
||||
+ "Content-Type: application/x-www-form-urlencoded" +
|
||||
+ SimpleHttpClient.CRLF +
|
||||
+ "Connection: close" + SimpleHttpClient.CRLF +
|
||||
+ SimpleHttpClient.CRLF +
|
||||
+ "9" + SimpleHttpClient.CRLF +
|
||||
+ "test=data" + SimpleHttpClient.CRLF +
|
||||
+ "0" + SimpleHttpClient.CRLF +
|
||||
+ SimpleHttpClient.CRLF;
|
||||
+
|
||||
+ Client client = new Client(getPort());
|
||||
+ client.setRequest(new String[] {request});
|
||||
+
|
||||
+ client.connect();
|
||||
+ client.processRequest();
|
||||
+ Assert.assertTrue(client.isResponse200());
|
||||
+ Assert.assertTrue(client.getResponseBody().contains("test - data"));
|
||||
+ }
|
||||
}
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 94a0d94..e47f3d6 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -315,6 +315,10 @@
|
||||
in 2001. Requests using this transfer encoding will now receive a 501
|
||||
response. (markt)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ Process transfer encoding headers from both HTTP 1.0 and HTTP 1.1
|
||||
+ clients. (markt)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
<subsection name="Web applications">
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,137 +0,0 @@
|
||||
From 74888576a60ec58ee99454e4202a0eb1a7720d98 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 10 May 2021 22:14:18 +0100
|
||||
Subject: [PATCH] Ensure chunked, if present, is the last encoding in the list
|
||||
|
||||
---
|
||||
.../apache/coyote/http11/Http11Processor.java | 13 ++++++++-
|
||||
.../coyote/http11/TestHttp11Processor.java | 28 +++++++++++++------
|
||||
webapps/docs/changelog.xml | 5 ++++
|
||||
3 files changed, 36 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/coyote/http11/Http11Processor.java b/java/org/apache/coyote/http11/Http11Processor.java
|
||||
index 4021355..17932b9 100644
|
||||
--- a/java/org/apache/coyote/http11/Http11Processor.java
|
||||
+++ b/java/org/apache/coyote/http11/Http11Processor.java
|
||||
@@ -238,11 +238,22 @@ public class Http11Processor extends AbstractProcessor {
|
||||
* supported, a 501 response will be returned to the client.
|
||||
*/
|
||||
private void addInputFilter(InputFilter[] inputFilters, String encodingName) {
|
||||
+ if (contentDelimitation) {
|
||||
+ // Chunked has already been specified and it must be the final
|
||||
+ // encoding.
|
||||
+ // 400 - Bad request
|
||||
+ response.setStatus(400);
|
||||
+ setErrorState(ErrorState.CLOSE_CLEAN, null);
|
||||
+ if (log.isDebugEnabled()) {
|
||||
+ log.debug(sm.getString("http11processor.request.prepare") +
|
||||
+ " Tranfer encoding lists chunked before [" + encodingName + "]");
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
|
||||
// Trim provided encoding name and convert to lower case since transfer
|
||||
// encoding names are case insensitive. (RFC2616, section 3.6)
|
||||
encodingName = encodingName.trim().toLowerCase(Locale.ENGLISH);
|
||||
-
|
||||
if (encodingName.equals("chunked")) {
|
||||
inputBuffer.addActiveFilter(inputFilters[Constants.CHUNKED_FILTER]);
|
||||
contentDelimitation = true;
|
||||
diff --git a/test/org/apache/coyote/http11/TestHttp11Processor.java b/test/org/apache/coyote/http11/TestHttp11Processor.java
|
||||
index 84fdd42..ceb2601 100644
|
||||
--- a/test/org/apache/coyote/http11/TestHttp11Processor.java
|
||||
+++ b/test/org/apache/coyote/http11/TestHttp11Processor.java
|
||||
@@ -1287,47 +1287,53 @@ public class TestHttp11Processor extends TomcatBaseTest {
|
||||
|
||||
@Test
|
||||
public void testTEHeaderUnknown01() throws Exception {
|
||||
- doTestTEHeaderUnknown("identity");
|
||||
+ doTestTEHeaderInvalid("identity", false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTEHeaderUnknown02() throws Exception {
|
||||
- doTestTEHeaderUnknown("identity, chunked");
|
||||
+ doTestTEHeaderInvalid("identity, chunked", false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTEHeaderUnknown03() throws Exception {
|
||||
- doTestTEHeaderUnknown("unknown, chunked");
|
||||
+ doTestTEHeaderInvalid("unknown, chunked", false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTEHeaderUnknown04() throws Exception {
|
||||
- doTestTEHeaderUnknown("void");
|
||||
+ doTestTEHeaderInvalid("void", false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTEHeaderUnknown05() throws Exception {
|
||||
- doTestTEHeaderUnknown("void, chunked");
|
||||
+ doTestTEHeaderInvalid("void, chunked", false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTEHeaderUnknown06() throws Exception {
|
||||
- doTestTEHeaderUnknown("void, identity");
|
||||
+ doTestTEHeaderInvalid("void, identity", false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTEHeaderUnknown07() throws Exception {
|
||||
- doTestTEHeaderUnknown("identity, void");
|
||||
+ doTestTEHeaderInvalid("identity, void", false);
|
||||
}
|
||||
|
||||
|
||||
- private void doTestTEHeaderUnknown(String headerValue) throws Exception {
|
||||
+ @Test
|
||||
+ public void testTEHeaderChunkedNotLast01() throws Exception {
|
||||
+ doTestTEHeaderInvalid("chunked, void", true);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private void doTestTEHeaderInvalid(String headerValue, boolean badRequest) throws Exception {
|
||||
Tomcat tomcat = getTomcatInstance();
|
||||
|
||||
// No file system docBase required
|
||||
@@ -1351,7 +1357,11 @@ public class TestHttp11Processor extends TomcatBaseTest {
|
||||
client.connect();
|
||||
client.processRequest(false);
|
||||
|
||||
- Assert.assertTrue(client.isResponse501());
|
||||
+ if (badRequest) {
|
||||
+ Assert.assertTrue(client.isResponse400());
|
||||
+ } else {
|
||||
+ Assert.assertTrue(client.isResponse501());
|
||||
+ }
|
||||
}
|
||||
|
||||
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index e47f3d6..35b8eab 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -319,6 +319,11 @@
|
||||
Process transfer encoding headers from both HTTP 1.0 and HTTP 1.1
|
||||
clients. (markt)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ Ensure that if the transfer encoding header contains the
|
||||
+ <code>chunked</code>, that the <code>chunked</code> encoding is the
|
||||
+ final encoding listed. (markt)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
<subsection name="Web applications">
|
||||
--
|
||||
2.23.0
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
From d4b340fa8feaf55831f9a59350578f7b6ca048b8 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 3 Mar 2021 12:00:46 +0000
|
||||
Subject: [PATCH] Improve robustness
|
||||
|
||||
---
|
||||
.../apache/tomcat/util/net/openssl/LocalStrings.properties | 1 +
|
||||
java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java | 6 ++++--
|
||||
2 files changed, 5 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties b/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties
|
||||
index 1919159..1ab5f43 100644
|
||||
--- a/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties
|
||||
+++ b/java/org/apache/tomcat/util/net/openssl/LocalStrings.properties
|
||||
@@ -52,6 +52,7 @@ engine.nullCipherSuite=Null cipher suite
|
||||
engine.unsupportedCipher=Unsupported cipher suite: [{0}] [{1}]
|
||||
engine.emptyCipherSuite=Empty cipher suite
|
||||
engine.failedCipherSuite=Failed to enable cipher suite [{0}]
|
||||
+engine.failedToReadAvailableBytes=There are plain text bytes available to read but no bytes were read
|
||||
engine.unsupportedProtocol=Protocol [{0}] is not supported
|
||||
engine.unverifiedPeer=Peer unverified
|
||||
engine.noSession=SSL session ID not available
|
||||
diff --git a/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java b/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java
|
||||
index 15c6f56..b837fd6 100644
|
||||
--- a/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java
|
||||
+++ b/java/org/apache/tomcat/util/net/openssl/OpenSSLEngine.java
|
||||
@@ -593,8 +593,10 @@ public final class OpenSSLEngine extends SSLEngine implements SSLUtil.ProtocolIn
|
||||
throw new SSLException(e);
|
||||
}
|
||||
|
||||
- if (bytesRead == 0) {
|
||||
- break;
|
||||
+ if (bytesRead <= 0) {
|
||||
+ // This should not be possible. pendingApp is positive
|
||||
+ // therefore the read should have read at least one byte.
|
||||
+ throw new IllegalStateException(sm.getString("engine.failedToReadAvailableBytes"));
|
||||
}
|
||||
|
||||
bytesProduced += bytesRead;
|
||||
--
|
||||
2.27.0
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
From cd2150ff02c592c1ab6da219302ff80f589559fe Mon Sep 17 00:00:00 2001
|
||||
From: remm <remm@apache.org>
|
||||
Date: Thu, 28 Oct 2021 11:32:47 +0800
|
||||
Subject: [PATCH] Close WebConnection
|
||||
|
||||
---
|
||||
.../tomcat/websocket/server/WsHttpUpgradeHandler.java | 3 +++
|
||||
webapps/docs/changelog.xml | 8 ++++++++
|
||||
2 files changed, 11 insertions(+)
|
||||
|
||||
diff --git a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
|
||||
index 5dd1c5a..703f17a 100644
|
||||
--- a/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
|
||||
+++ b/java/org/apache/tomcat/websocket/server/WsHttpUpgradeHandler.java
|
||||
@@ -99,6 +99,7 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
|
||||
|
||||
@Override
|
||||
public void init(WebConnection connection) {
|
||||
+ this.connection = connection;
|
||||
if (ep == null) {
|
||||
throw new IllegalStateException(
|
||||
sm.getString("wsHttpUpgradeHandler.noPreInit"));
|
||||
@@ -203,7 +204,9 @@ public class WsHttpUpgradeHandler implements InternalHttpUpgradeHandler {
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
+ WebConnection connection = this.connection;
|
||||
if (connection != null) {
|
||||
+ this.connection = null;
|
||||
try {
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index a7bb52c..a97e15d 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -338,6 +338,14 @@
|
||||
</fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
+ <subsection name="WebSocket">
|
||||
+ <changelog>
|
||||
+ <fix>
|
||||
+ The internal upgrade handler should close the associated
|
||||
+ <code>WebConnection</code> on destroy. (remm)
|
||||
+ </fix>
|
||||
+ </changelog>
|
||||
+ </subsection>
|
||||
<subsection name="Web applications">
|
||||
<changlog>
|
||||
<fix>
|
||||
--
|
||||
2.27.0
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
From 1385c624b4a1e994426e810075c850edc38a700e Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 12 Jan 2022 11:11:29 +0000
|
||||
Subject: [PATCH] Make calculation of session storage location more robust
|
||||
|
||||
---
|
||||
java/org/apache/catalina/session/FileStore.java | 5 +++--
|
||||
1 files changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/session/FileStore.java b/java/org/apache/catalina/session/FileStore.java
|
||||
index cac6027abdc..e42a72a4c87 100644
|
||||
--- a/java/org/apache/catalina/session/FileStore.java
|
||||
+++ b/java/org/apache/catalina/session/FileStore.java
|
||||
@@ -349,13 +349,14 @@ private File file(String id) throws IOException {
|
||||
|
||||
String filename = id + FILE_EXT;
|
||||
File file = new File(storageDir, filename);
|
||||
+ File canonicalFile = file.getCanonicalFile();
|
||||
|
||||
// Check the file is within the storage directory
|
||||
- if (!file.getCanonicalFile().toPath().startsWith(storageDir.getCanonicalFile().toPath())) {
|
||||
+ if (!canonicalFile.toPath().startsWith(storageDir.getCanonicalFile().toPath())) {
|
||||
log.warn(sm.getString("fileStore.invalid", file.getPath(), id));
|
||||
return null;
|
||||
}
|
||||
|
||||
- return file;
|
||||
+ return canonicalFile;
|
||||
}
|
||||
}
|
||||
@ -1,127 +0,0 @@
|
||||
diff -upr tomcat-9.0.10_back/java/org/apache/coyote/http11/Http11InputBuffer.java tomcat-9.0.10/java/org/apache/coyote/http11/Http11InputBuffer.java
|
||||
--- tomcat-9.0.10_back/java/org/apache/coyote/http11/Http11InputBuffer.java 2022-12-14 10:39:12.917000000 +0800
|
||||
+++ tomcat-9.0.10/java/org/apache/coyote/http11/Http11InputBuffer.java 2022-12-14 10:48:31.180863424 +0800
|
||||
@@ -821,7 +821,7 @@ public class Http11InputBuffer implement
|
||||
headerData.lastSignificantChar = pos;
|
||||
byteBuffer.position(byteBuffer.position() - 1);
|
||||
// skipLine() will handle the error
|
||||
- return skipLine();
|
||||
+ return skipLine(false);
|
||||
}
|
||||
|
||||
// chr is next byte of header name. Convert to lowercase.
|
||||
@@ -832,7 +832,7 @@ public class Http11InputBuffer implement
|
||||
|
||||
// Skip the line and ignore the header
|
||||
if (headerParsePos == HeaderParsePosition.HEADER_SKIPLINE) {
|
||||
- return skipLine();
|
||||
+ return skipLine(false);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -883,15 +883,11 @@ public class Http11InputBuffer implement
|
||||
} else if (prevChr == Constants.CR && chr == Constants.LF) {
|
||||
eol = true;
|
||||
} else if (prevChr == Constants.CR) {
|
||||
- // Invalid value
|
||||
- // Delete the header (it will be the most recent one)
|
||||
- headers.removeHeader(headers.size() - 1);
|
||||
- return skipLine();
|
||||
+ // Invalid value - also need to delete header
|
||||
+ return skipLine(true);
|
||||
} else if (chr != Constants.HT && HttpParser.isControl(chr)) {
|
||||
- // Invalid value
|
||||
- // Delete the header (it will be the most recent one)
|
||||
- headers.removeHeader(headers.size() - 1);
|
||||
- return skipLine();
|
||||
+ // Invalid value - also need to delete header
|
||||
+ return skipLine(true);
|
||||
} else if (chr == Constants.SP || chr == Constants.HT) {
|
||||
byteBuffer.put(headerData.realPos, chr);
|
||||
headerData.realPos++;
|
||||
@@ -939,7 +935,27 @@ public class Http11InputBuffer implement
|
||||
}
|
||||
|
||||
|
||||
- private HeaderParseStatus skipLine() throws IOException {
|
||||
+ private HeaderParseStatus skipLine(boolean deleteHeader) throws IOException {
|
||||
+ boolean rejectThisHeader = rejectIllegalHeader;
|
||||
+ // Check if rejectIllegalHeader is disabled and needs to be overridden
|
||||
+ // for this header. The header name is required to determine if this
|
||||
+ // override is required. The header name is only available once the
|
||||
+ // header has been created. If the header has been created then
|
||||
+ // deleteHeader will be true.
|
||||
+ if (!rejectThisHeader && deleteHeader) {
|
||||
+ if (headers.getName(headers.size() - 1).equalsIgnoreCase("content-length")) {
|
||||
+ // Malformed content-length headers must always be rejected
|
||||
+ // RFC 9112, section 6.3, bullet 5.
|
||||
+ rejectThisHeader = true;
|
||||
+ } else {
|
||||
+ // Only need to delete the header if the request isn't going to
|
||||
+ // be rejected (it will be the most recent one)
|
||||
+ headers.removeHeader(headers.size() - 1);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Parse the rest of the invalid header so we can construct a useful
|
||||
+ // exception and/or debug message.
|
||||
headerParsePos = HeaderParsePosition.HEADER_SKIPLINE;
|
||||
boolean eol = false;
|
||||
|
||||
@@ -967,12 +983,12 @@ public class Http11InputBuffer implement
|
||||
headerData.lastSignificantChar = pos;
|
||||
}
|
||||
}
|
||||
- if (rejectIllegalHeader || log.isDebugEnabled()) {
|
||||
+ if (rejectThisHeader || log.isDebugEnabled()) {
|
||||
String message = sm.getString("iib.invalidheader",
|
||||
new String(byteBuffer.array(), headerData.start,
|
||||
headerData.lastSignificantChar - headerData.start + 1,
|
||||
StandardCharsets.ISO_8859_1));
|
||||
- if (rejectIllegalHeader) {
|
||||
+ if (rejectThisHeader) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
log.debug(message);
|
||||
diff -upr tomcat-9.0.10_back/test/org/apache/coyote/http11/TestHttp11InputBuffer.java tomcat-9.0.10/test/org/apache/coyote/http11/TestHttp11InputBuffer.java
|
||||
--- tomcat-9.0.10_back/test/org/apache/coyote/http11/TestHttp11InputBuffer.java 2022-12-14 10:39:12.971000000 +0800
|
||||
+++ tomcat-9.0.10/test/org/apache/coyote/http11/TestHttp11InputBuffer.java 2022-12-14 10:51:16.845501479 +0800
|
||||
@@ -643,6 +643,38 @@ public class TestHttp11InputBuffer exten
|
||||
Assert.assertTrue(client.isResponseBodyOK());
|
||||
}
|
||||
|
||||
+ @Test
|
||||
+ public void testInvalidContentLength01() {
|
||||
+ doTestInvalidContentLength(false);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testInvalidContentLength02() {
|
||||
+ doTestInvalidContentLength(true);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private void doTestInvalidContentLength(boolean rejectIllegalHeader) {
|
||||
+ getTomcatInstance().getConnector().setProperty("rejectIllegalHeader", Boolean.toString(rejectIllegalHeader));
|
||||
+
|
||||
+ String[] request = new String[1];
|
||||
+ request[0] =
|
||||
+ "POST /test HTTP/1.1" + CRLF +
|
||||
+ "Host: localhost:8080" + CRLF +
|
||||
+ "Content-Length: 12\u000734" + CRLF +
|
||||
+ "Connection: close" + CRLF +
|
||||
+ CRLF;
|
||||
+
|
||||
+ InvalidClient client = new InvalidClient(request);
|
||||
+
|
||||
+ client.doRequest();
|
||||
+ Assert.assertTrue(client.getResponseLine(), client.isResponse400());
|
||||
+ Assert.assertTrue(client.isResponseBodyOK());
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+
|
||||
|
||||
/**
|
||||
* Bug 48839 test client.
|
||||
@ -1,238 +0,0 @@
|
||||
From 09e214c09c78a48ea96b0137555b3c2a98a1bfab Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 31 Mar 2020 14:03:17 +0100
|
||||
Subject: [PATCH] Make the HTTP/2 connection ID and stream Id available to
|
||||
applications
|
||||
Origin: https://github.com/apache/tomcat/commit/09e214c09c78a48ea96b0137555b3c2a98a1bfab
|
||||
|
||||
---
|
||||
java/org/apache/catalina/Globals.java | 16 ++++++++
|
||||
.../apache/catalina/connector/Request.java | 27 +++++++++++++
|
||||
java/org/apache/coyote/AbstractProcessor.java | 39 +++++++++++++++++++
|
||||
java/org/apache/coyote/ActionCode.java | 14 ++++++-
|
||||
.../apache/coyote/http2/StreamProcessor.java | 12 ++++++
|
||||
webapps/docs/changelog.xml | 5 +++
|
||||
webapps/docs/config/http2.xml | 9 +++++
|
||||
7 files changed, 121 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/Globals.java b/java/org/apache/catalina/Globals.java
|
||||
index 994902b..c19d69c 100644
|
||||
--- a/java/org/apache/catalina/Globals.java
|
||||
+++ b/java/org/apache/catalina/Globals.java
|
||||
@@ -112,6 +112,22 @@ public final class Globals {
|
||||
"org.apache.catalina.NAMED";
|
||||
|
||||
|
||||
+ /**
|
||||
+ * The request attribute used to expose the current connection ID associated
|
||||
+ * with the request, if any. Used with multiplexing protocols such as
|
||||
+ * HTTTP/2.
|
||||
+ */
|
||||
+ public static final String CONNECTION_ID = "org.apache.coyote.connectionID";
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
+ * The request attribute used to expose the current stream ID associated
|
||||
+ * with the request, if any. Used with multiplexing protocols such as
|
||||
+ * HTTTP/2.
|
||||
+ */
|
||||
+ public static final String STREAM_ID = "org.apache.coyote.streamID";
|
||||
+
|
||||
+
|
||||
/**
|
||||
* The servlet context attribute under which we store a flag used
|
||||
* to mark this request as having been processed by the SSIServlet.
|
||||
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
|
||||
index c4cc26a..94065ef 100644
|
||||
--- a/java/org/apache/catalina/connector/Request.java
|
||||
+++ b/java/org/apache/catalina/connector/Request.java
|
||||
@@ -40,6 +40,7 @@ import java.util.TimeZone;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
+import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.naming.NamingException;
|
||||
import javax.security.auth.Subject;
|
||||
@@ -3487,6 +3488,32 @@ public class Request implements HttpServletRequest {
|
||||
// NO-OP
|
||||
}
|
||||
});
|
||||
+ specialAttributes.put(Globals.CONNECTION_ID,
|
||||
+ new SpecialAttributeAdapter() {
|
||||
+ @Override
|
||||
+ public Object get(Request request, String name) {
|
||||
+ AtomicReference<Object> result = new AtomicReference<>();
|
||||
+ request.getCoyoteRequest().action(ActionCode.CONNECTION_ID, result);
|
||||
+ return result.get();
|
||||
+ }
|
||||
+ @Override
|
||||
+ public void set(Request request, String name, Object value) {
|
||||
+ // NO-OP
|
||||
+ }
|
||||
+ });
|
||||
+ specialAttributes.put(Globals.STREAM_ID,
|
||||
+ new SpecialAttributeAdapter() {
|
||||
+ @Override
|
||||
+ public Object get(Request request, String name) {
|
||||
+ AtomicReference<Object> result = new AtomicReference<>();
|
||||
+ request.getCoyoteRequest().action(ActionCode.STREAM_ID, result);
|
||||
+ return result.get();
|
||||
+ }
|
||||
+ @Override
|
||||
+ public void set(Request request, String name, Object value) {
|
||||
+ // NO-OP
|
||||
+ }
|
||||
+ });
|
||||
|
||||
for (SimpleDateFormat sdf : formatsTemplate) {
|
||||
sdf.setTimeZone(GMT_ZONE);
|
||||
diff --git a/java/org/apache/coyote/AbstractProcessor.java b/java/org/apache/coyote/AbstractProcessor.java
|
||||
index 5be2cb8..b351cb7 100644
|
||||
--- a/java/org/apache/coyote/AbstractProcessor.java
|
||||
+++ b/java/org/apache/coyote/AbstractProcessor.java
|
||||
@@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
+import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import javax.servlet.RequestDispatcher;
|
||||
|
||||
@@ -589,6 +590,20 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
|
||||
result.set(isTrailerFieldsSupported());
|
||||
break;
|
||||
}
|
||||
+
|
||||
+ // Identifiers associated with multiplexing protocols like HTTP/2
|
||||
+ case CONNECTION_ID: {
|
||||
+ @SuppressWarnings("unchecked")
|
||||
+ AtomicReference<Object> result = (AtomicReference<Object>) param;
|
||||
+ result.set(getConnectionID());
|
||||
+ break;
|
||||
+ }
|
||||
+ case STREAM_ID: {
|
||||
+ @SuppressWarnings("unchecked")
|
||||
+ AtomicReference<Object> result = (AtomicReference<Object>) param;
|
||||
+ result.set(getStreamID());
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -889,6 +904,30 @@ public abstract class AbstractProcessor extends AbstractProcessorLight implement
|
||||
}
|
||||
|
||||
|
||||
+ /**
|
||||
+ * Protocols that support multiplexing (e.g. HTTP/2) should override this
|
||||
+ * method and return the appropriate ID.
|
||||
+ *
|
||||
+ * @return The stream ID associated with this request or {@code null} if a
|
||||
+ * multiplexing protocol is not being used
|
||||
+ */
|
||||
+ protected Object getConnectionID() {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ /**
|
||||
+ * Protocols that support multiplexing (e.g. HTTP/2) should override this
|
||||
+ * method and return the appropriate ID.
|
||||
+ *
|
||||
+ * @return The stream ID associated with this request or {@code null} if a
|
||||
+ * multiplexing protocol is not being used
|
||||
+ */
|
||||
+ protected Object getStreamID() {
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
/**
|
||||
* Flush any pending writes. Used during non-blocking writes to flush any
|
||||
* remaining data from a previous incomplete write.
|
||||
diff --git a/java/org/apache/coyote/ActionCode.java b/java/org/apache/coyote/ActionCode.java
|
||||
index 3ff4c21..5c5af4f 100644
|
||||
--- a/java/org/apache/coyote/ActionCode.java
|
||||
+++ b/java/org/apache/coyote/ActionCode.java
|
||||
@@ -265,5 +265,17 @@ public enum ActionCode {
|
||||
* once an HTTP/1.1 response has been committed, it will no longer support
|
||||
* trailer fields.
|
||||
*/
|
||||
- IS_TRAILER_FIELDS_SUPPORTED
|
||||
+ IS_TRAILER_FIELDS_SUPPORTED,
|
||||
+
|
||||
+ /**
|
||||
+ * Obtain the connection identifier for the request. Used with multiplexing
|
||||
+ * protocols such as HTTP/2.
|
||||
+ */
|
||||
+ CONNECTION_ID,
|
||||
+
|
||||
+ /**
|
||||
+ * Obtain the stream identifier for the request. Used with multiplexing
|
||||
+ * protocols such as HTTP/2.
|
||||
+ */
|
||||
+ STREAM_ID
|
||||
}
|
||||
diff --git a/java/org/apache/coyote/http2/StreamProcessor.java b/java/org/apache/coyote/http2/StreamProcessor.java
|
||||
index d9c1c82..fd833ec 100644
|
||||
--- a/java/org/apache/coyote/http2/StreamProcessor.java
|
||||
+++ b/java/org/apache/coyote/http2/StreamProcessor.java
|
||||
@@ -300,6 +300,18 @@ class StreamProcessor extends AbstractProcessor {
|
||||
}
|
||||
|
||||
|
||||
+ @Override
|
||||
+ protected Object getConnectionID() {
|
||||
+ return stream.getConnectionId();
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Override
|
||||
+ protected Object getStreamID() {
|
||||
+ return stream.getIdentifier().toString();
|
||||
+ }
|
||||
+
|
||||
+
|
||||
@Override
|
||||
public final void recycle() {
|
||||
// StreamProcessor instances are not re-used.
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index a97e15d..8837628 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -247,6 +247,11 @@
|
||||
Ensure that the HTTP/1.1 processor is correctly recycled when a direct
|
||||
connection to h2c is made. (markt)
|
||||
</fix>
|
||||
+ <add>
|
||||
+ Expose the HTTP/2 connection ID and stream ID to applications via the
|
||||
+ request attributes <code>org.apache.coyote.connectionID</code> and
|
||||
+ <code>org.apache.coyote.streamID</code> respectively. (markt)
|
||||
+ </add>
|
||||
</changelog>
|
||||
</subsection>
|
||||
<subsection name="Jasper">
|
||||
diff --git a/webapps/docs/config/http2.xml b/webapps/docs/config/http2.xml
|
||||
index 83b8acd..145a184 100644
|
||||
--- a/webapps/docs/config/http2.xml
|
||||
+++ b/webapps/docs/config/http2.xml
|
||||
@@ -44,6 +44,15 @@
|
||||
the Servlet API is fundamentally blocking, each HTTP/2 stream requires a
|
||||
dedicated container thread for the duration of that stream.</p>
|
||||
|
||||
+ <p>Requests processed using HTTP/2 will have the following additional request
|
||||
+ attributes available:</p>
|
||||
+ <ul>
|
||||
+ <li><code>org.apache.coyote.connectionID</code> will return the HTTP/2
|
||||
+ connection ID</li>
|
||||
+ <li><code>org.apache.coyote.streamID</code> will return the HTTP/2 stream
|
||||
+ ID</li>
|
||||
+ </ul>
|
||||
+
|
||||
</section>
|
||||
|
||||
|
||||
--
|
||||
2.33.0
|
||||
|
||||
@ -1,232 +0,0 @@
|
||||
From 3b51230764da595bb19e8d0962dd8c69ab40dfab Mon Sep 17 00:00:00 2001
|
||||
From: lihan <lihan@apache.org>
|
||||
Date: Fri, 10 Feb 2023 10:01:27 +0800
|
||||
Subject: [PATCH] Fix BZ 66471 - JSessionId secure attribute missing with
|
||||
RemoteIpFilter and X-Forwarded-Proto set to https
|
||||
|
||||
https://bz.apache.org/bugzilla/show_bug.cgi?id=66471
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/3b51230764da595bb19e8d0962dd8c69ab40dfab
|
||||
---
|
||||
java/org/apache/catalina/Globals.java | 8 ++
|
||||
.../apache/catalina/connector/Request.java | 14 +++
|
||||
.../catalina/filters/RemoteIpFilter.java | 7 +-
|
||||
.../catalina/filters/TestRemoteIpFilter.java | 96 ++++++++++++++-----
|
||||
webapps/docs/changelog.xml | 5 +
|
||||
5 files changed, 101 insertions(+), 29 deletions(-)
|
||||
|
||||
diff --git a/java/org/apache/catalina/Globals.java b/java/org/apache/catalina/Globals.java
|
||||
index c19d69c..2e9a377 100644
|
||||
--- a/java/org/apache/catalina/Globals.java
|
||||
+++ b/java/org/apache/catalina/Globals.java
|
||||
@@ -160,6 +160,14 @@ public final class Globals {
|
||||
org.apache.coyote.Constants.SENDFILE_SUPPORTED_ATTR;
|
||||
|
||||
|
||||
+ /**
|
||||
+ * The request attribute that is set to the value of {@code Boolean.TRUE}
|
||||
+ * if {@link org.apache.catalina.filters.RemoteIpFilter} determines
|
||||
+ * that this request was submitted via a secure channel.
|
||||
+ */
|
||||
+ public static final String REMOTE_IP_FILTER_SECURE = "org.apache.catalina.filters.RemoteIpFilter.secure";
|
||||
+
|
||||
+
|
||||
/**
|
||||
* The request attribute that can be used by a servlet to pass
|
||||
* to the connector the name of the file that is to be served
|
||||
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
|
||||
index 94065ef..889d5e7 100644
|
||||
--- a/java/org/apache/catalina/connector/Request.java
|
||||
+++ b/java/org/apache/catalina/connector/Request.java
|
||||
@@ -3501,6 +3501,20 @@ public class Request implements HttpServletRequest {
|
||||
// NO-OP
|
||||
}
|
||||
});
|
||||
+ specialAttributes.put(Globals.REMOTE_IP_FILTER_SECURE,
|
||||
+ new SpecialAttributeAdapter() {
|
||||
+ @Override
|
||||
+ public Object get(Request request, String name) {
|
||||
+ return Boolean.valueOf(request.isSecure());
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void set(Request request, String name, Object value) {
|
||||
+ if (value instanceof Boolean) {
|
||||
+ request.setSecure(((Boolean) value).booleanValue());
|
||||
+ }
|
||||
+ }
|
||||
+ });
|
||||
specialAttributes.put(Globals.STREAM_ID,
|
||||
new SpecialAttributeAdapter() {
|
||||
@Override
|
||||
diff --git a/java/org/apache/catalina/filters/RemoteIpFilter.java b/java/org/apache/catalina/filters/RemoteIpFilter.java
|
||||
index b9f6655..e978cfb 100644
|
||||
--- a/java/org/apache/catalina/filters/RemoteIpFilter.java
|
||||
+++ b/java/org/apache/catalina/filters/RemoteIpFilter.java
|
||||
@@ -577,11 +577,6 @@ public class RemoteIpFilter extends GenericFilter {
|
||||
return serverPort;
|
||||
}
|
||||
|
||||
- @Override
|
||||
- public boolean isSecure() {
|
||||
- return secure;
|
||||
- }
|
||||
-
|
||||
public void removeHeader(String name) {
|
||||
Map.Entry<String, List<String>> header = getHeaderEntry(name);
|
||||
if (header != null) {
|
||||
@@ -617,7 +612,7 @@ public class RemoteIpFilter extends GenericFilter {
|
||||
}
|
||||
|
||||
public void setSecure(boolean secure) {
|
||||
- this.secure = secure;
|
||||
+ super.getRequest().setAttribute(Globals.REMOTE_IP_FILTER_SECURE, Boolean.valueOf(secure));
|
||||
}
|
||||
|
||||
public void setServerPort(int serverPort) {
|
||||
diff --git a/test/org/apache/catalina/filters/TestRemoteIpFilter.java b/test/org/apache/catalina/filters/TestRemoteIpFilter.java
|
||||
index f7f2093..109fdd2 100644
|
||||
--- a/test/org/apache/catalina/filters/TestRemoteIpFilter.java
|
||||
+++ b/test/org/apache/catalina/filters/TestRemoteIpFilter.java
|
||||
@@ -81,15 +81,21 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
- private transient HttpServletRequest request;
|
||||
-
|
||||
- public HttpServletRequest getRequest() {
|
||||
- return request;
|
||||
- }
|
||||
+ public String remoteAddr;
|
||||
+ public String remoteHost;
|
||||
+ public String scheme;
|
||||
+ public String serverName;
|
||||
+ public int serverPort;
|
||||
+ public boolean isSecure;
|
||||
|
||||
@Override
|
||||
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
- this.request = request;
|
||||
+ this.isSecure = request.isSecure();
|
||||
+ this.remoteAddr = request.getRemoteAddr();
|
||||
+ this.remoteHost = request.getRemoteHost();
|
||||
+ this.scheme = request.getScheme();
|
||||
+ this.serverName = request.getServerName();
|
||||
+ this.serverPort = request.getServerPort();
|
||||
PrintWriter writer = response.getWriter();
|
||||
|
||||
writer.println("request.remoteAddr=" + request.getRemoteAddr());
|
||||
@@ -127,16 +133,6 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
|
||||
getCoyoteRequest().scheme().setString(scheme);
|
||||
}
|
||||
|
||||
- @Override
|
||||
- public void setAttribute(String name, Object value) {
|
||||
- getCoyoteRequest().getAttributes().put(name, value);
|
||||
- }
|
||||
-
|
||||
- @Override
|
||||
- public Object getAttribute(String name) {
|
||||
- return getCoyoteRequest().getAttributes().get(name);
|
||||
- }
|
||||
-
|
||||
@Override
|
||||
public String getServerName() {
|
||||
return "localhost";
|
||||
@@ -667,16 +663,70 @@ public class TestRemoteIpFilter extends TomcatBaseTest {
|
||||
|
||||
// VALIDATE
|
||||
Assert.assertEquals(HttpURLConnection.HTTP_OK, httpURLConnection.getResponseCode());
|
||||
- HttpServletRequest request = mockServlet.getRequest();
|
||||
- Assert.assertNotNull(request);
|
||||
|
||||
// VALIDATE X-FORWARDED-FOR
|
||||
- Assert.assertEquals(expectedRemoteAddr, request.getRemoteAddr());
|
||||
- Assert.assertEquals(expectedRemoteAddr, request.getRemoteHost());
|
||||
+ Assert.assertEquals(expectedRemoteAddr, mockServlet.remoteAddr);
|
||||
+ Assert.assertEquals(expectedRemoteAddr, mockServlet.remoteHost);
|
||||
|
||||
// VALIDATE X-FORWARDED-PROTO
|
||||
- Assert.assertTrue(request.isSecure());
|
||||
- Assert.assertEquals("https", request.getScheme());
|
||||
- Assert.assertEquals(443, request.getServerPort());
|
||||
+ Assert.assertTrue(mockServlet.isSecure);
|
||||
+ Assert.assertEquals("https", mockServlet.scheme);
|
||||
+ Assert.assertEquals(443, mockServlet.serverPort);
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testJSessionIdSecureAttributeMissing() throws Exception {
|
||||
+
|
||||
+ // mostly default configuration : enable "x-forwarded-proto"
|
||||
+ Map<String, String> remoteIpFilterParameter = new HashMap<>();
|
||||
+ remoteIpFilterParameter.put("protocolHeader", "x-forwarded-proto");
|
||||
+
|
||||
+ // SETUP
|
||||
+ Tomcat tomcat = getTomcatInstance();
|
||||
+ Context root = tomcat.addContext("", TEMP_DIR);
|
||||
+
|
||||
+ FilterDef filterDef = new FilterDef();
|
||||
+ filterDef.getParameterMap().putAll(remoteIpFilterParameter);
|
||||
+ filterDef.setFilterClass(RemoteIpFilter.class.getName());
|
||||
+ filterDef.setFilterName(RemoteIpFilter.class.getName());
|
||||
+
|
||||
+ root.addFilterDef(filterDef);
|
||||
+
|
||||
+ FilterMap filterMap = new FilterMap();
|
||||
+ filterMap.setFilterName(RemoteIpFilter.class.getName());
|
||||
+ filterMap.addURLPatternDecoded("*");
|
||||
+ root.addFilterMap(filterMap);
|
||||
+
|
||||
+ Bug66471Servlet bug66471Servlet = new Bug66471Servlet();
|
||||
+
|
||||
+ Tomcat.addServlet(root, bug66471Servlet.getClass().getName(), bug66471Servlet);
|
||||
+ root.addServletMappingDecoded("/test", bug66471Servlet.getClass().getName());
|
||||
+
|
||||
+ getTomcatInstance().start();
|
||||
+
|
||||
+ Map<String, List<String>> resHeaders = new HashMap<>();
|
||||
+ Map<String, List<String>> reqHeaders = new HashMap<>();
|
||||
+ String expectedRemoteAddr = "my-remote-addr";
|
||||
+ List<String> forwardedFor = new ArrayList<>(1);
|
||||
+ forwardedFor.add(expectedRemoteAddr);
|
||||
+ List<String> forwardedProto = new ArrayList<>(1);
|
||||
+ forwardedProto.add("https");
|
||||
+ reqHeaders.put("x-forwarded-for", forwardedFor);
|
||||
+ reqHeaders.put("x-forwarded-proto", forwardedProto);
|
||||
+
|
||||
+ getUrl("http://localhost:" + tomcat.getConnector().getLocalPort() +
|
||||
+ "/test", null, reqHeaders, resHeaders);
|
||||
+ String setCookie = resHeaders.get("Set-Cookie").get(0);
|
||||
+ Assert.assertTrue(setCookie.contains("Secure"));
|
||||
+ Assert.assertTrue(bug66471Servlet.isSecure.booleanValue());
|
||||
+ }
|
||||
+ public static class Bug66471Servlet extends HttpServlet {
|
||||
+ private static final long serialVersionUID = 1L;
|
||||
+ public Boolean isSecure;
|
||||
+ @Override
|
||||
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
+ req.getSession();
|
||||
+ isSecure = (Boolean) req.getAttribute(Globals.REMOTE_IP_FILTER_SECURE);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 8837628..15be3ed 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -478,6 +478,11 @@
|
||||
Improve handling of overflow in the UTF-8 decoder with supplementary
|
||||
characters. (markt)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ <bug>66471</bug>: Fix JSessionId secure attribute missing When
|
||||
+ <code>RemoteIpFilter</code> determines that this request was submitted
|
||||
+ via a secure channel. (lihan)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
<subsection name="Coyote">
|
||||
--
|
||||
2.33.0
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
From 77c0ce2d169efa248b64b992e547aad549ec906b Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 22 Aug 2023 11:31:23 -0700
|
||||
Subject: [PATCH] Avoid protocol relative redirects
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/77c0ce2d169efa248b64b992e547aad549ec906b
|
||||
|
||||
---
|
||||
.../apache/catalina/authenticator/FormAuthenticator.java | 6 ++++++
|
||||
webapps/docs/changelog.xml | 3 +++
|
||||
2 files changed, 9 insertions(+)
|
||||
|
||||
diff --git a/java/org/apache/catalina/authenticator/FormAuthenticator.java b/java/org/apache/catalina/authenticator/FormAuthenticator.java
|
||||
index a57db51776b..d54cc62182e 100644
|
||||
--- a/java/org/apache/catalina/authenticator/FormAuthenticator.java
|
||||
+++ b/java/org/apache/catalina/authenticator/FormAuthenticator.java
|
||||
@@ -747,6 +747,12 @@ protected String savedRequestURL(Session session) {
|
||||
sb.append('?');
|
||||
sb.append(saved.getQueryString());
|
||||
}
|
||||
+
|
||||
+ // Avoid protocol relative redirects
|
||||
+ while (sb.length() > 1 && sb.charAt(1) == '/') {
|
||||
+ sb.deleteCharAt(0);
|
||||
+ }
|
||||
+
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@ -1,89 +0,0 @@
|
||||
Description: Align processing of trailer headers with standard processing
|
||||
Origin: upstream, https://github.com/apache/tomcat/commit/59583245639d8c42ae0009f4a4a70464d3ea70a0
|
||||
--- a/java/org/apache/coyote/http11/Http11InputBuffer.java
|
||||
+++ b/java/org/apache/coyote/http11/Http11InputBuffer.java
|
||||
@@ -818,6 +818,12 @@
|
||||
*/
|
||||
private HeaderParseStatus parseHeader() throws IOException {
|
||||
|
||||
+ /*
|
||||
+ * Implementation note: Any changes to this method probably need to be echoed in
|
||||
+ * ChunkedInputFilter.parseHeader(). Why not use a common implementation? In short, this code uses non-blocking
|
||||
+ * reads whereas ChunkedInputFilter using blocking reads. The code is just different enough that a common
|
||||
+ * implementation wasn't viewed as practical.
|
||||
+ */
|
||||
//
|
||||
// Check for blank line
|
||||
//
|
||||
|
||||
byte chr = 0;
|
||||
byte prevChr = 0;
|
||||
|
||||
while (headerParsePos == HeaderParsePosition.HEADER_START) {
|
||||
|
||||
// Read new bytes if needed
|
||||
@@ -950,7 +956,7 @@
|
||||
} else if (prevChr == Constants.CR) {
|
||||
// Invalid value - also need to delete header
|
||||
return skipLine(true);
|
||||
- } else if (chr != Constants.HT && HttpParser.isControl(chr)) {
|
||||
+ } else if (HttpParser.isControl(chr) && chr != Constants.HT) {
|
||||
// Invalid value - also need to delete header
|
||||
return skipLine(true);
|
||||
} else if (chr == Constants.SP || chr == Constants.HT) {
|
||||
--- a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
|
||||
+++ b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
|
||||
@@ -30,6 +30,7 @@
|
||||
import org.apache.coyote.http11.InputFilter;
|
||||
import org.apache.tomcat.util.buf.ByteChunk;
|
||||
import org.apache.tomcat.util.buf.HexUtils;
|
||||
+import org.apache.tomcat.util.http.parser.HttpParser;
|
||||
import org.apache.tomcat.util.net.ApplicationBufferHandler;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
@@ -443,6 +444,13 @@
|
||||
|
||||
private boolean parseHeader() throws IOException {
|
||||
|
||||
+ /*
|
||||
+ * Implementation note: Any changes to this method probably need to be echoed in
|
||||
+ * Http11InputBuffer.parseHeader(). Why not use a common implementation? In short, this code uses blocking
|
||||
+ * reads whereas Http11InputBuffer using non-blocking reads. The code is just different enough that a common
|
||||
+ * implementation wasn't viewed as practical.
|
||||
+ */
|
||||
+
|
||||
Map<String,String> headers = request.getTrailerFields();
|
||||
|
||||
byte chr = 0;
|
||||
@@ -489,6 +497,9 @@
|
||||
|
||||
if (chr == Constants.COLON) {
|
||||
colon = true;
|
||||
+ } else if (!HttpParser.isToken(chr)) {
|
||||
+ // Non-token characters are illegal in header names
|
||||
+ throw new IOException(sm.getString("chunkedInputFilter.invalidTrailerHeaderName"));
|
||||
} else {
|
||||
trailingHeaders.append(chr);
|
||||
}
|
||||
@@ -550,7 +561,9 @@
|
||||
if (chr == Constants.CR || chr == Constants.LF) {
|
||||
parseCRLF(true);
|
||||
eol = true;
|
||||
- } else if (chr == Constants.SP) {
|
||||
+ } else if (HttpParser.isControl(chr) && chr != Constants.HT) {
|
||||
+ throw new IOException(sm.getString("chunkedInputFilter.invalidTrailerHeaderValue"));
|
||||
+ } else if (chr == Constants.SP || chr == Constants.HT) {
|
||||
trailingHeaders.append(chr);
|
||||
} else {
|
||||
trailingHeaders.append(chr);
|
||||
--- a/java/org/apache/coyote/http11/filters/LocalStrings.properties
|
||||
+++ b/java/org/apache/coyote/http11/filters/LocalStrings.properties
|
||||
@@ -21,6 +21,8 @@
|
||||
chunkedInputFilter.invalidCrlfNoCR=Invalid end of line sequence (No CR before LF)
|
||||
chunkedInputFilter.invalidCrlfNoData=Invalid end of line sequence (no data available to read)
|
||||
chunkedInputFilter.invalidHeader=Invalid chunk header
|
||||
+chunkedInputFilter.invalidTrailerHeaderName=Invalid trailer header name (non-token character in name)
|
||||
+chunkedInputFilter.invalidTrailerHeaderValue=Invalid trailer header value (control character in value)
|
||||
chunkedInputFilter.maxExtension=maxExtensionSize exceeded
|
||||
chunkedInputFilter.maxTrailer=maxTrailerSize exceeded
|
||||
|
||||
435
CVE-2024-50379-1.patch
Normal file
435
CVE-2024-50379-1.patch
Normal file
@ -0,0 +1,435 @@
|
||||
From 43b507ebac9d268b1ea3d908e296cc6e46795c00 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Fri, 1 Nov 2024 16:35:17 +0000
|
||||
Subject: [PATCH] Fix inconsistent resource metadata with current GET and
|
||||
PUT/DELETE
|
||||
Origin: https://github.com/apache/tomcat/commit/43b507ebac9d268b1ea3d908e296cc6e46795c00
|
||||
|
||||
Concurrent reads and writes (e.g. HTTP GET and PUT / DELETE) for the
|
||||
same path cause corruption of the FileResource where some of the fields
|
||||
are set as if the file exists and some as set as if it does not.
|
||||
---
|
||||
.../apache/catalina/WebResourceLockSet.java | 73 +++++++
|
||||
.../catalina/webresources/DirResourceSet.java | 199 +++++++++++++++---
|
||||
.../catalina/webresources/FileResource.java | 29 ++-
|
||||
.../webresources/LocalStrings.properties | 1 +
|
||||
4 files changed, 273 insertions(+), 29 deletions(-)
|
||||
create mode 100644 java/org/apache/catalina/WebResourceLockSet.java
|
||||
|
||||
diff --git a/java/org/apache/catalina/WebResourceLockSet.java b/java/org/apache/catalina/WebResourceLockSet.java
|
||||
new file mode 100644
|
||||
index 000000000000..142472c33cd6
|
||||
--- /dev/null
|
||||
+++ b/java/org/apache/catalina/WebResourceLockSet.java
|
||||
@@ -0,0 +1,73 @@
|
||||
+/*
|
||||
+ * 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;
|
||||
+
|
||||
+import java.util.concurrent.atomic.AtomicInteger;
|
||||
+import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
+
|
||||
+/**
|
||||
+ * Interface implemented by {@link WebResourceSet} implementations that wish to provide locking functionality.
|
||||
+ */
|
||||
+public interface WebResourceLockSet {
|
||||
+
|
||||
+ /**
|
||||
+ * Lock the resource at the provided path for reading. The resource is not required to exist. Read locks are not
|
||||
+ * exclusive.
|
||||
+ *
|
||||
+ * @param path The path to the resource to be locked for reading
|
||||
+ *
|
||||
+ * @return The {@link ResourceLock} that must be passed to {@link #unlockForRead(ResourceLock)} to release the lock
|
||||
+ */
|
||||
+ ResourceLock lockForRead(String path);
|
||||
+
|
||||
+ /**
|
||||
+ * Release a read lock from the resource associated with the given {@link ResourceLock}.
|
||||
+ *
|
||||
+ * @param resourceLock The {@link ResourceLock} associated with the resource for which a read lock should be
|
||||
+ * released
|
||||
+ */
|
||||
+ void unlockForRead(ResourceLock resourceLock);
|
||||
+
|
||||
+ /**
|
||||
+ * Lock the resource at the provided path for writing. The resource is not required to exist. Write locks are
|
||||
+ * exclusive.
|
||||
+ *
|
||||
+ * @param path The path to the resource to be locked for writing
|
||||
+ *
|
||||
+ * @return The {@link ResourceLock} that must be passed to {@link #unlockForWrite(ResourceLock)} to release the lock
|
||||
+ */
|
||||
+ ResourceLock lockForWrite(String path);
|
||||
+
|
||||
+ /**
|
||||
+ * Release the write lock from the resource associated with the given {@link ResourceLock}.
|
||||
+ *
|
||||
+ * @param resourceLock The {@link ResourceLock} associated with the resource for which the write lock should be
|
||||
+ * released
|
||||
+ */
|
||||
+ void unlockForWrite(ResourceLock resourceLock);
|
||||
+
|
||||
+
|
||||
+ class ResourceLock {
|
||||
+ public final AtomicInteger count = new AtomicInteger(0);
|
||||
+ public final ReentrantReadWriteLock reentrantLock = new ReentrantReadWriteLock();
|
||||
+ public final String key;
|
||||
+
|
||||
+ public ResourceLock(String key) {
|
||||
+ this.key = key;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/java/org/apache/catalina/webresources/DirResourceSet.java b/java/org/apache/catalina/webresources/DirResourceSet.java
|
||||
index a221c3f1c33d..02ddc6ec887c 100644
|
||||
--- a/java/org/apache/catalina/webresources/DirResourceSet.java
|
||||
+++ b/java/org/apache/catalina/webresources/DirResourceSet.java
|
||||
@@ -22,24 +22,35 @@
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
+import java.util.HashMap;
|
||||
+import java.util.Locale;
|
||||
+import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.WebResource;
|
||||
+import org.apache.catalina.WebResourceLockSet;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.catalina.WebResourceRoot.ResourceSetType;
|
||||
import org.apache.catalina.util.ResourceSet;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
+import org.apache.tomcat.util.http.RequestUtil;
|
||||
|
||||
/**
|
||||
* Represents a {@link org.apache.catalina.WebResourceSet} based on a directory.
|
||||
*/
|
||||
-public class DirResourceSet extends AbstractFileResourceSet {
|
||||
+public class DirResourceSet extends AbstractFileResourceSet implements WebResourceLockSet {
|
||||
|
||||
private static final Log log = LogFactory.getLog(DirResourceSet.class);
|
||||
|
||||
+ private boolean caseSensitive = true;
|
||||
+
|
||||
+ private Map<String,ResourceLock> resourceLocksByPath = new HashMap<>();
|
||||
+ private Object resourceLocksByPathLock = new Object();
|
||||
+
|
||||
+
|
||||
/**
|
||||
* A no argument constructor is required for this to work with the digester.
|
||||
*/
|
||||
@@ -91,22 +102,33 @@ public WebResource getResource(String path) {
|
||||
String webAppMount = getWebAppMount();
|
||||
WebResourceRoot root = getRoot();
|
||||
if (path.startsWith(webAppMount)) {
|
||||
- File f = file(path.substring(webAppMount.length()), false);
|
||||
- if (f == null) {
|
||||
- return new EmptyResource(root, path);
|
||||
- }
|
||||
- if (!f.exists()) {
|
||||
- return new EmptyResource(root, path, f);
|
||||
- }
|
||||
- if (f.isDirectory() && path.charAt(path.length() - 1) != '/') {
|
||||
- path = path + '/';
|
||||
+ /*
|
||||
+ * Lock the path for reading until the WebResource has been constructed. The lock prevents concurrent reads
|
||||
+ * and writes (e.g. HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource
|
||||
+ * where some of the fields are set as if the file exists and some as set as if it does not.
|
||||
+ */
|
||||
+ ResourceLock lock = lockForRead(path);
|
||||
+ try {
|
||||
+ File f = file(path.substring(webAppMount.length()), false);
|
||||
+ if (f == null) {
|
||||
+ return new EmptyResource(root, path);
|
||||
+ }
|
||||
+ if (!f.exists()) {
|
||||
+ return new EmptyResource(root, path, f);
|
||||
+ }
|
||||
+ if (f.isDirectory() && path.charAt(path.length() - 1) != '/') {
|
||||
+ path = path + '/';
|
||||
+ }
|
||||
+ return new FileResource(root, path, f, isReadOnly(), getManifest(), this, lock.key);
|
||||
+ } finally {
|
||||
+ unlockForRead(lock);
|
||||
}
|
||||
- return new FileResource(root, path, f, isReadOnly(), getManifest());
|
||||
} else {
|
||||
return new EmptyResource(root, path);
|
||||
}
|
||||
}
|
||||
|
||||
+
|
||||
@Override
|
||||
public String[] list(String path) {
|
||||
checkPath(path);
|
||||
@@ -246,32 +268,42 @@ public boolean write(String path, InputStream is, boolean overwrite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
- File dest = null;
|
||||
String webAppMount = getWebAppMount();
|
||||
- if (path.startsWith(webAppMount)) {
|
||||
+ if (!path.startsWith(webAppMount)) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ File dest = null;
|
||||
+ /*
|
||||
+ * Lock the path for writing until the write is complete. The lock prevents concurrent reads and writes (e.g.
|
||||
+ * HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource where some of the fields
|
||||
+ * are set as if the file exists and some as set as if it does not.
|
||||
+ */
|
||||
+ ResourceLock lock = lockForWrite(path);
|
||||
+ try {
|
||||
dest = file(path.substring(webAppMount.length()), false);
|
||||
if (dest == null) {
|
||||
return false;
|
||||
}
|
||||
- } else {
|
||||
- return false;
|
||||
- }
|
||||
|
||||
- if (dest.exists() && !overwrite) {
|
||||
- return false;
|
||||
- }
|
||||
+ if (dest.exists() && !overwrite) {
|
||||
+ return false;
|
||||
+ }
|
||||
|
||||
- try {
|
||||
- if (overwrite) {
|
||||
- Files.copy(is, dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
- } else {
|
||||
- Files.copy(is, dest.toPath());
|
||||
+ try {
|
||||
+ if (overwrite) {
|
||||
+ Files.copy(is, dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
+ } else {
|
||||
+ Files.copy(is, dest.toPath());
|
||||
+ }
|
||||
+ } catch (IOException ioe) {
|
||||
+ return false;
|
||||
}
|
||||
- } catch (IOException ioe) {
|
||||
- return false;
|
||||
- }
|
||||
|
||||
- return true;
|
||||
+ return true;
|
||||
+ } finally {
|
||||
+ unlockForWrite(lock);
|
||||
+ }
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -286,6 +318,7 @@ protected void checkType(File file) {
|
||||
@Override
|
||||
protected void initInternal() throws LifecycleException {
|
||||
super.initInternal();
|
||||
+ caseSensitive = isCaseSensitive();
|
||||
// Is this an exploded web application?
|
||||
if (getWebAppMount().equals("")) {
|
||||
// Look for a manifest
|
||||
@@ -299,4 +332,114 @@ protected void initInternal() throws LifecycleException {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+
|
||||
+ /*
|
||||
+ * Determines if this ResourceSet is based on a case sensitive file system or not.
|
||||
+ */
|
||||
+ private boolean isCaseSensitive() {
|
||||
+ try {
|
||||
+ String canonicalPath = getFileBase().getCanonicalPath();
|
||||
+ File upper = new File(canonicalPath.toUpperCase(Locale.ENGLISH));
|
||||
+ if (!canonicalPath.equals(upper.getCanonicalPath())) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ File lower = new File(canonicalPath.toLowerCase(Locale.ENGLISH));
|
||||
+ if (!canonicalPath.equals(lower.getCanonicalPath())) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ /*
|
||||
+ * Both upper and lower case versions of the current fileBase have the same canonical path so the file
|
||||
+ * system must be case insensitive.
|
||||
+ */
|
||||
+ } catch (IOException ioe) {
|
||||
+ log.warn(sm.getString("dirResourceSet.isCaseSensitive.fail", getFileBase().getAbsolutePath()), ioe);
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private String getLockKey(String path) {
|
||||
+ // Normalize path to ensure that the same key is used for the same path.
|
||||
+ String normalisedPath = RequestUtil.normalize(path);
|
||||
+ if (caseSensitive) {
|
||||
+ return normalisedPath;
|
||||
+ }
|
||||
+ return normalisedPath.toLowerCase(Locale.ENGLISH);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Override
|
||||
+ public ResourceLock lockForRead(String path) {
|
||||
+ String key = getLockKey(path);
|
||||
+ ResourceLock resourceLock = null;
|
||||
+ synchronized (resourceLocksByPathLock) {
|
||||
+ /*
|
||||
+ * Obtain the ResourceLock and increment the usage count inside the sync to ensure that that map always has
|
||||
+ * a consistent view of the currently "in-use" ResourceLocks.
|
||||
+ */
|
||||
+ resourceLock = resourceLocksByPath.get(key);
|
||||
+ if (resourceLock == null) {
|
||||
+ resourceLock = new ResourceLock(key);
|
||||
+ }
|
||||
+ resourceLock.count.incrementAndGet();
|
||||
+ }
|
||||
+ // Obtain the lock outside the sync as it will block if there is a current write lock.
|
||||
+ resourceLock.reentrantLock.readLock().lock();
|
||||
+ return resourceLock;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Override
|
||||
+ public void unlockForRead(ResourceLock resourceLock) {
|
||||
+ // Unlock outside the sync as there is no need to do it inside.
|
||||
+ resourceLock.reentrantLock.readLock().unlock();
|
||||
+ synchronized (resourceLocksByPathLock) {
|
||||
+ /*
|
||||
+ * Decrement the usage count and remove ResourceLocks no longer required inside the sync to ensure that that
|
||||
+ * map always has a consistent view of the currently "in-use" ResourceLocks.
|
||||
+ */
|
||||
+ if (resourceLock.count.decrementAndGet() == 0) {
|
||||
+ resourceLocksByPath.remove(resourceLock.key);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Override
|
||||
+ public ResourceLock lockForWrite(String path) {
|
||||
+ String key = getLockKey(path);
|
||||
+ ResourceLock resourceLock = null;
|
||||
+ synchronized (resourceLocksByPathLock) {
|
||||
+ /*
|
||||
+ * Obtain the ResourceLock and increment the usage count inside the sync to ensure that that map always has
|
||||
+ * a consistent view of the currently "in-use" ResourceLocks.
|
||||
+ */
|
||||
+ resourceLock = resourceLocksByPath.get(key);
|
||||
+ if (resourceLock == null) {
|
||||
+ resourceLock = new ResourceLock(key);
|
||||
+ }
|
||||
+ resourceLock.count.incrementAndGet();
|
||||
+ }
|
||||
+ // Obtain the lock outside the sync as it will block if there are any other current locks.
|
||||
+ resourceLock.reentrantLock.writeLock().lock();
|
||||
+ return resourceLock;
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Override
|
||||
+ public void unlockForWrite(ResourceLock resourceLock) {
|
||||
+ // Unlock outside the sync as there is no need to do it inside.
|
||||
+ resourceLock.reentrantLock.writeLock().unlock();
|
||||
+ synchronized (resourceLocksByPathLock) {
|
||||
+ /*
|
||||
+ * Decrement the usage count and remove ResourceLocks no longer required inside the sync to ensure that that
|
||||
+ * map always has a consistent view of the currently "in-use" ResourceLocks.
|
||||
+ */
|
||||
+ if (resourceLock.count.decrementAndGet() == 0) {
|
||||
+ resourceLocksByPath.remove(resourceLock.key);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
diff --git a/java/org/apache/catalina/webresources/FileResource.java b/java/org/apache/catalina/webresources/FileResource.java
|
||||
index 32ce5bfa62c3..354022f9096d 100644
|
||||
--- a/java/org/apache/catalina/webresources/FileResource.java
|
||||
+++ b/java/org/apache/catalina/webresources/FileResource.java
|
||||
@@ -31,6 +31,8 @@
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
+import org.apache.catalina.WebResourceLockSet;
|
||||
+import org.apache.catalina.WebResourceLockSet.ResourceLock;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
@@ -62,10 +64,20 @@ public class FileResource extends AbstractResource {
|
||||
private final boolean readOnly;
|
||||
private final Manifest manifest;
|
||||
private final boolean needConvert;
|
||||
+ private final WebResourceLockSet lockSet;
|
||||
+ private final String lockKey;
|
||||
|
||||
public FileResource(WebResourceRoot root, String webAppPath, File resource, boolean readOnly, Manifest manifest) {
|
||||
+ this(root, webAppPath, resource, readOnly, manifest, null, null);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ public FileResource(WebResourceRoot root, String webAppPath, File resource, boolean readOnly, Manifest manifest,
|
||||
+ WebResourceLockSet lockSet, String lockKey) {
|
||||
super(root, webAppPath);
|
||||
this.resource = resource;
|
||||
+ this.lockSet = lockSet;
|
||||
+ this.lockKey = lockKey;
|
||||
|
||||
if (webAppPath.charAt(webAppPath.length() - 1) == '/') {
|
||||
String realName = resource.getName() + '/';
|
||||
@@ -117,7 +129,22 @@ public boolean delete() {
|
||||
if (readOnly) {
|
||||
return false;
|
||||
}
|
||||
- return resource.delete();
|
||||
+ /*
|
||||
+ * Lock the path for writing until the delete is complete. The lock prevents concurrent reads and writes (e.g.
|
||||
+ * HTTP GET and PUT / DELETE) for the same path causing corruption of the FileResource where some of the fields
|
||||
+ * are set as if the file exists and some as set as if it does not.
|
||||
+ */
|
||||
+ ResourceLock lock = null;
|
||||
+ if (lockSet != null) {
|
||||
+ lock = lockSet.lockForWrite(lockKey);
|
||||
+ }
|
||||
+ try {
|
||||
+ return resource.delete();
|
||||
+ } finally {
|
||||
+ if (lockSet != null) {
|
||||
+ lockSet.unlockForWrite(lock);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/java/org/apache/catalina/webresources/LocalStrings.properties b/java/org/apache/catalina/webresources/LocalStrings.properties
|
||||
index 67d76d3b3b32..301c13eb8063 100644
|
||||
--- a/java/org/apache/catalina/webresources/LocalStrings.properties
|
||||
+++ b/java/org/apache/catalina/webresources/LocalStrings.properties
|
||||
@@ -36,6 +36,7 @@ cachedResource.invalidURL=Unable to create an instance of CachedResourceURLStrea
|
||||
|
||||
classpathUrlStreamHandler.notFound=Unable to load the resource [{0}] using the thread context class loader or the current class''s class loader
|
||||
|
||||
+dirResourceSet.isCaseSensitive.fail=Error trying to determine if file system at [{0}] is case sensitive so assuming it is not case sensitive
|
||||
dirResourceSet.manifestFail=Failed to read manifest from [{0}]
|
||||
dirResourceSet.notDirectory=The directory specified by base and internal path [{0}]{1}[{2}] does not exist.
|
||||
dirResourceSet.writeNpe=The input stream may not be null
|
||||
32
CVE-2024-50379-2.patch
Normal file
32
CVE-2024-50379-2.patch
Normal file
@ -0,0 +1,32 @@
|
||||
From 631500b0c9b2a2a2abb707e3de2e10a5936e5d41 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Fri, 15 Nov 2024 15:55:37 +0000
|
||||
Subject: [PATCH] Fix for inconsistent metadata was incomplete
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/631500b0c9b2a2a2abb707e3de2e10a5936e5d41
|
||||
Need to add key to map once created so it is visible to other threads
|
||||
---
|
||||
java/org/apache/catalina/webresources/DirResourceSet.java | 2 ++
|
||||
webapps/docs/changelog.xml | 4 ++++
|
||||
2 files changed, 6 insertions(+)
|
||||
|
||||
diff --git a/java/org/apache/catalina/webresources/DirResourceSet.java b/java/org/apache/catalina/webresources/DirResourceSet.java
|
||||
index 02ddc6ec887c..f1d4e86199ee 100644
|
||||
--- a/java/org/apache/catalina/webresources/DirResourceSet.java
|
||||
+++ b/java/org/apache/catalina/webresources/DirResourceSet.java
|
||||
@@ -382,6 +382,7 @@ public ResourceLock lockForRead(String path) {
|
||||
resourceLock = resourceLocksByPath.get(key);
|
||||
if (resourceLock == null) {
|
||||
resourceLock = new ResourceLock(key);
|
||||
+ resourceLocksByPath.put(key, resourceLock);
|
||||
}
|
||||
resourceLock.count.incrementAndGet();
|
||||
}
|
||||
@@ -419,6 +420,7 @@ public ResourceLock lockForWrite(String path) {
|
||||
resourceLock = resourceLocksByPath.get(key);
|
||||
if (resourceLock == null) {
|
||||
resourceLock = new ResourceLock(key);
|
||||
+ resourceLocksByPath.put(key, resourceLock);
|
||||
}
|
||||
resourceLock.count.incrementAndGet();
|
||||
}
|
||||
164
CVE-2024-52318.patch
Normal file
164
CVE-2024-52318.patch
Normal file
@ -0,0 +1,164 @@
|
||||
From 9813c5dd3259183f659bbb83312a5cf673cc1ebf Mon Sep 17 00:00:00 2001
|
||||
From: remm <remm@apache.org>
|
||||
Date: Tue, 15 Oct 2024 21:51:33 +0200
|
||||
Subject: [PATCH] Fix JSP tag release
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/9813c5dd3259183f659bbb83312a5cf673cc1ebf
|
||||
|
||||
BZ 69399: Fix regression caused by the improvement 69333 which caused
|
||||
the tag release() to be called when using tag pooling, and to be
|
||||
skipped when not using it.
|
||||
Patch submitted by Michal Sobkiewicz.
|
||||
---
|
||||
.../org/apache/jasper/compiler/Generator.java | 2 +-
|
||||
.../apache/jasper/compiler/TestGenerator.java | 51 +++++++++++++++++++
|
||||
test/webapp/WEB-INF/bugs.tld | 5 ++
|
||||
test/webapp/jsp/generator/release.jsp | 18 +++++++
|
||||
webapps/docs/changelog.xml | 10 ++++
|
||||
5 files changed, 85 insertions(+), 1 deletion(-)
|
||||
create mode 100644 test/webapp/jsp/generator/release.jsp
|
||||
|
||||
diff --git a/java/org/apache/jasper/compiler/Generator.java b/java/org/apache/jasper/compiler/Generator.java
|
||||
index 814c8bb9fe50..5df52c3d7adc 100644
|
||||
--- a/java/org/apache/jasper/compiler/Generator.java
|
||||
+++ b/java/org/apache/jasper/compiler/Generator.java
|
||||
@@ -2603,7 +2603,7 @@ private void generateCustomEnd(Node.CustomTag n, String tagHandlerVar,
|
||||
out.print(".reuse(");
|
||||
out.print(tagHandlerVar);
|
||||
out.println(");");
|
||||
-
|
||||
+ } else {
|
||||
// Clean-up
|
||||
out.printin("org.apache.jasper.runtime.JspRuntimeLibrary.releaseTag(");
|
||||
out.print(tagHandlerVar);
|
||||
diff --git a/test/org/apache/jasper/compiler/TestGenerator.java b/test/org/apache/jasper/compiler/TestGenerator.java
|
||||
index f7e3223e331a..087936cd6eb2 100644
|
||||
--- a/test/org/apache/jasper/compiler/TestGenerator.java
|
||||
+++ b/test/org/apache/jasper/compiler/TestGenerator.java
|
||||
@@ -526,6 +526,25 @@ public void setData(String data) {
|
||||
}
|
||||
}
|
||||
|
||||
+ private static boolean tagTesterTagReleaseReleased = false;
|
||||
+
|
||||
+ public static class TesterTagRelease extends TesterTag {
|
||||
+ private String data;
|
||||
+
|
||||
+ public String getData() {
|
||||
+ return data;
|
||||
+ }
|
||||
+
|
||||
+ public void setData(String data) {
|
||||
+ this.data = data;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void release() {
|
||||
+ tagTesterTagReleaseReleased = true;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
public static class DataPropertyEditor extends PropertyEditorSupport {
|
||||
}
|
||||
|
||||
@@ -947,6 +966,38 @@ public void testBug65390() throws Exception {
|
||||
Assert.assertEquals(body.toString(), HttpServletResponse.SC_OK, rc);
|
||||
}
|
||||
|
||||
+ @Test
|
||||
+ public void testTagReleaseWithPooling() throws Exception {
|
||||
+ doTestTagRelease(true);
|
||||
+ }
|
||||
+
|
||||
+ @Test
|
||||
+ public void testTagReleaseWithoutPooling() throws Exception {
|
||||
+ doTestTagRelease(false);
|
||||
+ }
|
||||
+
|
||||
+ public void doTestTagRelease(boolean enablePooling) throws Exception {
|
||||
+ tagTesterTagReleaseReleased = false;
|
||||
+ Tomcat tomcat = getTomcatInstance();
|
||||
+
|
||||
+ File appDir = new File("test/webapp");
|
||||
+ Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
|
||||
+ ctxt.addServletContainerInitializer(new JasperInitializer(), null);
|
||||
+
|
||||
+ Tomcat.initWebappDefaults(ctxt);
|
||||
+ Wrapper w = (Wrapper) ctxt.findChild("jsp");
|
||||
+ w.addInitParameter("enablePooling", String.valueOf(enablePooling));
|
||||
+
|
||||
+ tomcat.start();
|
||||
+
|
||||
+ getUrl("http://localhost:" + getPort() + "/jsp/generator/release.jsp");
|
||||
+ if (enablePooling) {
|
||||
+ Assert.assertFalse(tagTesterTagReleaseReleased);
|
||||
+ } else {
|
||||
+ Assert.assertTrue(tagTesterTagReleaseReleased);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
private void doTestJsp(String jspName) throws Exception {
|
||||
doTestJsp(jspName, HttpServletResponse.SC_OK);
|
||||
}
|
||||
diff --git a/test/webapp/WEB-INF/bugs.tld b/test/webapp/WEB-INF/bugs.tld
|
||||
index 81d050e284fa..a4e496a83357 100644
|
||||
--- a/test/webapp/WEB-INF/bugs.tld
|
||||
+++ b/test/webapp/WEB-INF/bugs.tld
|
||||
@@ -108,6 +108,11 @@
|
||||
<tag-class>org.apache.jasper.compiler.TestGenerator$TesterTagA</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
</tag>
|
||||
+ <tag>
|
||||
+ <name>TesterTagRelease</name>
|
||||
+ <tag-class>org.apache.jasper.compiler.TestGenerator$TesterTagRelease</tag-class>
|
||||
+ <body-content>JSP</body-content>
|
||||
+ </tag>
|
||||
<tag>
|
||||
<name>TesterScriptingTag</name>
|
||||
<tag-class>org.apache.jasper.compiler.TestGenerator$TesterScriptingTag</tag-class>
|
||||
diff --git a/test/webapp/jsp/generator/release.jsp b/test/webapp/jsp/generator/release.jsp
|
||||
new file mode 100644
|
||||
index 000000000000..ae2d1d19f09a
|
||||
--- /dev/null
|
||||
+++ b/test/webapp/jsp/generator/release.jsp
|
||||
@@ -0,0 +1,18 @@
|
||||
+<%--
|
||||
+ 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.
|
||||
+--%>
|
||||
+<%@ taglib uri="http://tomcat.apache.org/bugs" prefix="bugs" %>
|
||||
+<bugs:TesterTagRelease/>
|
||||
\ No newline at end of file
|
||||
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
|
||||
index 72932e81a5c2..4d34ec5008b5 100644
|
||||
--- a/webapps/docs/changelog.xml
|
||||
+++ b/webapps/docs/changelog.xml
|
||||
@@ -173,6 +173,16 @@
|
||||
</fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
+ <subsection name="Jasper">
|
||||
+ <changelog>
|
||||
+ <fix>
|
||||
+ <bug>69399</bug>: Fix regression caused by the improvement
|
||||
+ <bug>69333</bug> which caused the tag <code>release</code> to be called
|
||||
+ when using tag pooling, and to be skipped when not using it.
|
||||
+ Patch submitted by Michal Sobkiewicz. (remm)
|
||||
+ </fix>
|
||||
+ </changelog>
|
||||
+ </subsection>
|
||||
<subsection name="Other">
|
||||
<changelog>
|
||||
<update>
|
||||
48
CVE-2024-54677-1.patch
Normal file
48
CVE-2024-54677-1.patch
Normal file
@ -0,0 +1,48 @@
|
||||
From 1d88dd3ffaed76188dd4ee32ce77709ce6e153cd Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 2 Dec 2024 16:36:31 +0000
|
||||
Subject: [PATCH] Obfuscate session cookie values for JSON output as well as
|
||||
HTML
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/1d88dd3ffaed76188dd4ee32ce77709ce6e153cd
|
||||
---
|
||||
webapps/docs/changelog.xml | 4 ++++
|
||||
.../WEB-INF/classes/RequestHeaderExample.java | 18 +++++++++++++++---
|
||||
2 files changed, 19 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
|
||||
index b01c84f33e48..e32f8c233674 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
|
||||
@@ -73,7 +73,7 @@ protected boolean prefersJSON(String acceptHeader) {
|
||||
|
||||
// text/html, application/html, etc.
|
||||
if (accept.contains("html")) {
|
||||
- return false;
|
||||
+ return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -138,8 +138,20 @@ protected void renderJSON(HttpServletRequest request, HttpServletResponse respon
|
||||
String headerName = e.nextElement();
|
||||
String headerValue = request.getHeader(headerName);
|
||||
|
||||
- out.append("{\"").append(JSONFilter.escape(headerName)).append("\":\"")
|
||||
- .append(JSONFilter.escape(headerValue)).append("\"}");
|
||||
+ out.append("{\"").append(JSONFilter.escape(headerName)).append("\":\"");
|
||||
+
|
||||
+
|
||||
+ if (headerName.toLowerCase(Locale.ENGLISH).contains("cookie")) {
|
||||
+ HttpSession session = request.getSession(false);
|
||||
+ String sessionId = null;
|
||||
+ if (session != null) {
|
||||
+ sessionId = session.getId();
|
||||
+ }
|
||||
+ out.append(JSONFilter.escape(CookieFilter.filter(headerValue, sessionId)));
|
||||
+ } else {
|
||||
+ out.append(JSONFilter.escape(headerValue));
|
||||
+ }
|
||||
+ out.append("\"}");
|
||||
|
||||
if (e.hasMoreElements()) {
|
||||
out.append(',');
|
||||
61
CVE-2024-54677-2.patch
Normal file
61
CVE-2024-54677-2.patch
Normal file
@ -0,0 +1,61 @@
|
||||
From 721544ea28e92549824b106be954a9f411867a1c Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 2 Dec 2024 17:39:05 +0000
|
||||
Subject: [PATCH] Add the ability to delete session attributes.
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/721544ea28e92549824b106be954a9f411867a1c
|
||||
---
|
||||
webapps/docs/changelog.xml | 4 ++++
|
||||
.../examples/WEB-INF/classes/SessionExample.java | 14 +++++++++++---
|
||||
2 files changed, 15 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/SessionExample.java b/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
index 407e2b3c0b0f..b3de2f866956 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
@@ -16,6 +17,8 @@
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
+import java.net.URLEncoder;
|
||||
+import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.ResourceBundle;
|
||||
@@ -75,7 +78,7 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
|
||||
|
||||
String dataName = request.getParameter("dataname");
|
||||
String dataValue = request.getParameter("datavalue");
|
||||
- if (dataName != null && dataValue != null) {
|
||||
+ if (dataName != null) {
|
||||
session.setAttribute(dataName, dataValue);
|
||||
}
|
||||
|
||||
@@ -85,7 +88,12 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
|
||||
while (names.hasMoreElements()) {
|
||||
String name = names.nextElement();
|
||||
String value = session.getAttribute(name).toString();
|
||||
- out.println(HTMLFilter.filter(name) + " = " + HTMLFilter.filter(value) + "<br>");
|
||||
+ out.println(HTMLFilter.filter(name) + " = " + HTMLFilter.filter(value));
|
||||
+ out.print("<a href=\"");
|
||||
+ out.print(HTMLFilter.filter(
|
||||
+ response.encodeURL("SessionExample?dataname=" + URLEncoder.encode(name, StandardCharsets.UTF_8))));
|
||||
+ out.println("\" >delete</a>");
|
||||
+ out.println("<br>");
|
||||
}
|
||||
|
||||
out.println("<P>");
|
||||
@@ -117,7 +125,7 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
|
||||
out.println("</form>");
|
||||
|
||||
out.print("<p><a href=\"");
|
||||
- out.print(HTMLFilter.filter(response.encodeURL("SessionExample?dataname=foo&datavalue=bar")));
|
||||
+ out.print(HTMLFilter.filter(response.encodeURL("SessionExample?dataname=exampleName&datavalue=exampleValue")));
|
||||
out.println("\" >URL encoded </a>");
|
||||
|
||||
out.println("</body>");
|
||||
137
CVE-2024-54677-3.patch
Normal file
137
CVE-2024-54677-3.patch
Normal file
@ -0,0 +1,137 @@
|
||||
From 84065e26ca4555e63a922bb29b13b0a1c86b7654 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 2 Dec 2024 18:09:44 +0000
|
||||
Subject: [PATCH] Add a limit of 10 attributes per session to the session
|
||||
example
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/84065e26ca4555e63a922bb29b13b0a1c86b7654
|
||||
---
|
||||
webapps/docs/changelog.xml | 4 +
|
||||
.../WEB-INF/classes/SessionExample.java | 94 ++++++++++++-------
|
||||
2 files changed, 64 insertions(+), 34 deletions(-)
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/SessionExample.java b/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
index b3de2f866956..60eaa2e03e4b 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
@@ -41,6 +41,9 @@ public class SessionExample extends HttpServlet {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
+ private static final int SESSION_ATTRIBUTE_COUNT_LIMIT = 10;
|
||||
+
|
||||
+
|
||||
@Override
|
||||
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
|
||||
@@ -76,15 +79,34 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
|
||||
out.println(rb.getString("sessions.lastaccessed") + " ");
|
||||
out.println(new Date(session.getLastAccessedTime()));
|
||||
|
||||
+ // Count the existing attributes
|
||||
+ int sessionAttributeCount = 0;
|
||||
+ Enumeration<String> names = session.getAttributeNames();
|
||||
+ while (names.hasMoreElements()) {
|
||||
+ names.nextElement();
|
||||
+ sessionAttributeCount++;
|
||||
+ }
|
||||
+
|
||||
String dataName = request.getParameter("dataname");
|
||||
String dataValue = request.getParameter("datavalue");
|
||||
if (dataName != null) {
|
||||
- session.setAttribute(dataName, dataValue);
|
||||
+ if (dataValue == null) {
|
||||
+ session.removeAttribute(dataName);
|
||||
+ sessionAttributeCount--;
|
||||
+ } else if (sessionAttributeCount < SESSION_ATTRIBUTE_COUNT_LIMIT) {
|
||||
+ session.setAttribute(dataName, dataValue);
|
||||
+ sessionAttributeCount++;
|
||||
+ } else {
|
||||
+ out.print("<p> Session attribute [");
|
||||
+ out.print(HTMLFilter.filter(dataName));
|
||||
+ out.print("] not added as there are already "+ SESSION_ATTRIBUTE_COUNT_LIMIT + " attributes in the ");
|
||||
+ out.println("session. Delete an attribute before adding another.");
|
||||
+ }
|
||||
}
|
||||
|
||||
- out.println("<P>");
|
||||
+ out.println("<p>");
|
||||
out.println(rb.getString("sessions.data") + "<br>");
|
||||
- Enumeration<String> names = session.getAttributeNames();
|
||||
+ names = session.getAttributeNames();
|
||||
while (names.hasMoreElements()) {
|
||||
String name = names.nextElement();
|
||||
String value = session.getAttribute(name).toString();
|
||||
@@ -96,37 +118,41 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
|
||||
out.println("<br>");
|
||||
}
|
||||
|
||||
- out.println("<P>");
|
||||
- out.print("<form action=\"");
|
||||
- out.print(response.encodeURL("SessionExample"));
|
||||
- out.print("\" ");
|
||||
- out.println("method=POST>");
|
||||
- out.println(rb.getString("sessions.dataname"));
|
||||
- out.println("<input type=text size=20 name=dataname>");
|
||||
- out.println("<br>");
|
||||
- out.println(rb.getString("sessions.datavalue"));
|
||||
- out.println("<input type=text size=20 name=datavalue>");
|
||||
- out.println("<br>");
|
||||
- out.println("<input type=submit>");
|
||||
- out.println("</form>");
|
||||
-
|
||||
- out.println("<P>GET based form:<br>");
|
||||
- out.print("<form action=\"");
|
||||
- out.print(response.encodeURL("SessionExample"));
|
||||
- out.print("\" ");
|
||||
- out.println("method=GET>");
|
||||
- out.println(rb.getString("sessions.dataname"));
|
||||
- out.println("<input type=text size=20 name=dataname>");
|
||||
- out.println("<br>");
|
||||
- out.println(rb.getString("sessions.datavalue"));
|
||||
- out.println("<input type=text size=20 name=datavalue>");
|
||||
- out.println("<br>");
|
||||
- out.println("<input type=submit>");
|
||||
- out.println("</form>");
|
||||
-
|
||||
- out.print("<p><a href=\"");
|
||||
- out.print(HTMLFilter.filter(response.encodeURL("SessionExample?dataname=exampleName&datavalue=exampleValue")));
|
||||
- out.println("\" >URL encoded </a>");
|
||||
+ if (sessionAttributeCount < SESSION_ATTRIBUTE_COUNT_LIMIT) {
|
||||
+ out.println("<p>");
|
||||
+ out.print("<form action=\"");
|
||||
+ out.print(response.encodeURL("SessionExample"));
|
||||
+ out.print("\" ");
|
||||
+ out.println("method=POST>");
|
||||
+ out.println(rb.getString("sessions.dataname"));
|
||||
+ out.println("<input type=text size=20 name=dataname>");
|
||||
+ out.println("<br>");
|
||||
+ out.println(rb.getString("sessions.datavalue"));
|
||||
+ out.println("<input type=text size=20 name=datavalue>");
|
||||
+ out.println("<br>");
|
||||
+ out.println("<input type=submit>");
|
||||
+ out.println("</form>");
|
||||
+
|
||||
+ out.println("<p>GET based form:<br>");
|
||||
+ out.print("<form action=\"");
|
||||
+ out.print(response.encodeURL("SessionExample"));
|
||||
+ out.print("\" ");
|
||||
+ out.println("method=GET>");
|
||||
+ out.println(rb.getString("sessions.dataname"));
|
||||
+ out.println("<input type=text size=20 name=dataname>");
|
||||
+ out.println("<br>");
|
||||
+ out.println(rb.getString("sessions.datavalue"));
|
||||
+ out.println("<input type=text size=20 name=datavalue>");
|
||||
+ out.println("<br>");
|
||||
+ out.println("<input type=submit>");
|
||||
+ out.println("</form>");
|
||||
+
|
||||
+ out.print("<p><a href=\"");
|
||||
+ out.print(HTMLFilter.filter(response.encodeURL("SessionExample?dataname=exampleName&datavalue=exampleValue")));
|
||||
+ out.println("\" >URL encoded </a>");
|
||||
+ } else {
|
||||
+ out.print("<p>You may not add more than " + SESSION_ATTRIBUTE_COUNT_LIMIT + " attributes to this session.");
|
||||
+ }
|
||||
|
||||
out.println("</body>");
|
||||
out.println("</html>");
|
||||
31
CVE-2024-54677-4.patch
Normal file
31
CVE-2024-54677-4.patch
Normal file
@ -0,0 +1,31 @@
|
||||
From 3315a9027a7eaab18f42625b97b569940ff1365d Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 2 Dec 2024 18:13:07 +0000
|
||||
Subject: [PATCH] Fix backprot
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/3315a9027a7eaab18f42625b97b569940ff1365d
|
||||
---
|
||||
webapps/examples/WEB-INF/classes/SessionExample.java | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/SessionExample.java b/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
index 60eaa2e03e4b..14e7c9701c84 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
@@ -18,7 +18,6 @@
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URLEncoder;
|
||||
-import java.nio.charset.StandardCharsets;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.ResourceBundle;
|
||||
@@ -113,7 +112,7 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
|
||||
out.println(HTMLFilter.filter(name) + " = " + HTMLFilter.filter(value));
|
||||
out.print("<a href=\"");
|
||||
out.print(HTMLFilter.filter(
|
||||
- response.encodeURL("SessionExample?dataname=" + URLEncoder.encode(name, StandardCharsets.UTF_8))));
|
||||
+ response.encodeURL("SessionExample?dataname=" + URLEncoder.encode(name, "UTF-8"))));
|
||||
out.println("\" >delete</a>");
|
||||
out.println("<br>");
|
||||
}
|
||||
97
CVE-2024-54677-5.patch
Normal file
97
CVE-2024-54677-5.patch
Normal file
@ -0,0 +1,97 @@
|
||||
From c2f7ce21c3fb12caefee87c517a8bb4f80700044 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Tue, 3 Dec 2024 17:45:03 +0000
|
||||
Subject: [PATCH] Limit to 10 attributes. Add option to delete attribute.
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/c2f7ce21c3fb12caefee87c517a8bb4f80700044
|
||||
---
|
||||
webapps/docs/changelog.xml | 5 ++
|
||||
.../examples/jsp/security/protected/index.jsp | 49 ++++++++++++++++---
|
||||
2 files changed, 46 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/webapps/examples/jsp/security/protected/index.jsp b/webapps/examples/jsp/security/protected/index.jsp
|
||||
index 09c23e721910..987a30fd1878 100644
|
||||
--- a/webapps/examples/jsp/security/protected/index.jsp
|
||||
+++ b/webapps/examples/jsp/security/protected/index.jsp
|
||||
@@ -14,8 +14,10 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
--%>
|
||||
-<%@ page import="java.util.Enumeration" %>
|
||||
+<%@ page import="java.net.URLEncoder" %>
|
||||
+<%@ page import="java.nio.charset.StandardCharsets" %>
|
||||
<%@ page import="java.security.Principal" %>
|
||||
+<%@ page import="java.util.Enumeration" %>
|
||||
<%@ page import="org.apache.catalina.TomcatPrincipal" %>
|
||||
<%
|
||||
if (request.getParameter("logoff") != null) {
|
||||
@@ -121,31 +123,62 @@ enter it here:
|
||||
%>
|
||||
<br><br>
|
||||
|
||||
+<%
|
||||
+ // Count the existing attributes
|
||||
+ int sessionAttributeCount = 0;
|
||||
+ Enumeration<String> names = session.getAttributeNames();
|
||||
+ while (names.hasMoreElements()) {
|
||||
+ names.nextElement();
|
||||
+ sessionAttributeCount++;
|
||||
+ }
|
||||
+
|
||||
+ String dataName = request.getParameter("dataName");
|
||||
+ String dataValue = request.getParameter("dataValue");
|
||||
+ if (dataName != null) {
|
||||
+ if (dataValue == null) {
|
||||
+ session.removeAttribute(dataName);
|
||||
+ sessionAttributeCount--;
|
||||
+ } else if (sessionAttributeCount < 10) {
|
||||
+ session.setAttribute(dataName, dataValue);
|
||||
+ sessionAttributeCount++;
|
||||
+ } else {
|
||||
+%>
|
||||
+<p>Session attribute [<%= util.HTMLFilter.filter(dataName) %>] not added as there are already 10 attributes in the
|
||||
+session. Delete an attribute before adding another.</p>
|
||||
+<%
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (sessionAttributeCount < 10) {
|
||||
+%>
|
||||
To add some data to the authenticated session, enter it here:
|
||||
<form method="GET" action='<%= response.encodeURL("index.jsp") %>'>
|
||||
<input type="text" name="dataName">
|
||||
<input type="text" name="dataValue">
|
||||
<input type="submit" >
|
||||
</form>
|
||||
-<br><br>
|
||||
-
|
||||
<%
|
||||
- String dataName = request.getParameter("dataName");
|
||||
- if (dataName != null) {
|
||||
- session.setAttribute(dataName, request.getParameter("dataValue"));
|
||||
+ } else {
|
||||
+%>
|
||||
+<p>You may not add more than 10 attributes to this session.</p>
|
||||
+<%
|
||||
}
|
||||
%>
|
||||
+<br><br>
|
||||
+
|
||||
<p>The authenticated session contains the following attributes:</p>
|
||||
<table>
|
||||
<tr><th>Name</th><th>Value</th></tr>
|
||||
<%
|
||||
- Enumeration<String> names = session.getAttributeNames();
|
||||
+ names = session.getAttributeNames();
|
||||
while (names.hasMoreElements()) {
|
||||
String name = names.nextElement();
|
||||
+ String value = session.getAttribute(name).toString();
|
||||
%>
|
||||
<tr>
|
||||
<td><%= util.HTMLFilter.filter(name) %></td>
|
||||
- <td><%= util.HTMLFilter.filter(String.valueOf(session.getAttribute(name))) %></td>
|
||||
+ <td><%= util.HTMLFilter.filter(value) %></td>
|
||||
+ <td><a href='<%= response.encodeURL("index.jsp?dataName=" + URLEncoder.encode(name, StandardCharsets.UTF_8)) %>'>delete</a></td>
|
||||
</tr>
|
||||
<%
|
||||
}
|
||||
23
CVE-2024-54677-6.patch
Normal file
23
CVE-2024-54677-6.patch
Normal file
@ -0,0 +1,23 @@
|
||||
From 75ff7e8622edcc024b268677aa789ee8f0880ecc Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 4 Dec 2024 07:27:40 +0000
|
||||
Subject: [PATCH] Fix back-port
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/75ff7e8622edcc024b268677aa789ee8f0880ecc
|
||||
---
|
||||
webapps/examples/jsp/security/protected/index.jsp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/webapps/examples/jsp/security/protected/index.jsp b/webapps/examples/jsp/security/protected/index.jsp
|
||||
index 987a30fd1878..f4c90b8b715c 100644
|
||||
--- a/webapps/examples/jsp/security/protected/index.jsp
|
||||
+++ b/webapps/examples/jsp/security/protected/index.jsp
|
||||
@@ -178,7 +178,7 @@ To add some data to the authenticated session, enter it here:
|
||||
<tr>
|
||||
<td><%= util.HTMLFilter.filter(name) %></td>
|
||||
<td><%= util.HTMLFilter.filter(value) %></td>
|
||||
- <td><a href='<%= response.encodeURL("index.jsp?dataName=" + URLEncoder.encode(name, StandardCharsets.UTF_8)) %>'>delete</a></td>
|
||||
+ <td><a href='<%= response.encodeURL("index.jsp?dataName=" + URLEncoder.encode(name, "UTF-8")) %>'>delete</a></td>
|
||||
</tr>
|
||||
<%
|
||||
}
|
||||
270
CVE-2024-54677-7.patch
Normal file
270
CVE-2024-54677-7.patch
Normal file
@ -0,0 +1,270 @@
|
||||
From 4d5cc6538d91386f950373ac8120e98c2c78ed3a Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 4 Dec 2024 15:14:17 +0000
|
||||
Subject: [PATCH] Limit shopping cart to pre-defined items
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/4d5cc6538d91386f950373ac8120e98c2c78ed3a
|
||||
---
|
||||
webapps/docs/changelog.xml | 4 ++
|
||||
.../WEB-INF/classes/sessions/DummyCart.java | 42 +++++++++++--------
|
||||
.../WEB-INF/classes/sessions/Item.java | 37 ++++++++++++++++
|
||||
webapps/examples/jsp/index.html | 2 +-
|
||||
webapps/examples/jsp/sessions/carts.jsp | 8 ++--
|
||||
webapps/examples/jsp/sessions/crt.html | 7 +++-
|
||||
.../jsp/sessions/{carts.html => shopping.jsp} | 41 +++++++++---------
|
||||
7 files changed, 95 insertions(+), 46 deletions(-)
|
||||
create mode 100644 webapps/examples/WEB-INF/classes/sessions/Item.java
|
||||
rename webapps/examples/jsp/sessions/{carts.html => shopping.jsp} (75%)
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/sessions/DummyCart.java b/webapps/examples/WEB-INF/classes/sessions/DummyCart.java
|
||||
index 44decc98a9a2..7088557b9595 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/sessions/DummyCart.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/sessions/DummyCart.java
|
||||
@@ -16,42 +16,50 @@
|
||||
*/
|
||||
package sessions;
|
||||
|
||||
-import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
-import java.util.List;
|
||||
+import java.util.HashSet;
|
||||
+import java.util.Set;
|
||||
|
||||
public class DummyCart {
|
||||
- final List<String> items = Collections.synchronizedList(new ArrayList<>());
|
||||
+ final Set<Item> items = Collections.synchronizedSet(new HashSet<>());
|
||||
+ int itemId = -1;
|
||||
String submit = null;
|
||||
- String item = null;
|
||||
|
||||
- private void addItem(String name) {
|
||||
- items.add(name);
|
||||
+ public void setItemId(int itemId) {
|
||||
+ this.itemId = itemId;
|
||||
}
|
||||
|
||||
- private void removeItem(String name) {
|
||||
- items.remove(name);
|
||||
+ public void setSubmit(String s) {
|
||||
+ submit = s;
|
||||
}
|
||||
|
||||
- public void setItem(String name) {
|
||||
- item = name;
|
||||
+ private void addItem(int itemId) {
|
||||
+ try {
|
||||
+ items.add(Item.values()[itemId]);
|
||||
+ } catch (ArrayIndexOutOfBoundsException e) {
|
||||
+ // Ignore. Can only happen if user edits URL directly.
|
||||
+ }
|
||||
}
|
||||
|
||||
- public void setSubmit(String s) {
|
||||
- submit = s;
|
||||
+ private void removeItem(int itemId) {
|
||||
+ try {
|
||||
+ items.remove(Item.values()[itemId]);
|
||||
+ } catch (ArrayIndexOutOfBoundsException e) {
|
||||
+ // Ignore. Can only happen if user edits URL directly.
|
||||
+ }
|
||||
}
|
||||
|
||||
- public String[] getItems() {
|
||||
- return items.toArray(new String[0]);
|
||||
+ public Item[] getItems() {
|
||||
+ return items.toArray(new Item[0]);
|
||||
}
|
||||
|
||||
public void processRequest() {
|
||||
// null value for submit - user hit enter instead of clicking on
|
||||
// "add" or "remove"
|
||||
if (submit == null || submit.equals("add")) {
|
||||
- addItem(item);
|
||||
+ addItem(itemId);
|
||||
} else if (submit.equals("remove")) {
|
||||
- removeItem(item);
|
||||
+ removeItem(itemId);
|
||||
}
|
||||
|
||||
// reset at the end of the request
|
||||
@@ -61,6 +69,6 @@ public void processRequest() {
|
||||
// reset
|
||||
private void reset() {
|
||||
submit = null;
|
||||
- item = null;
|
||||
+ itemId = -1;
|
||||
}
|
||||
}
|
||||
diff --git a/webapps/examples/WEB-INF/classes/sessions/Item.java b/webapps/examples/WEB-INF/classes/sessions/Item.java
|
||||
new file mode 100644
|
||||
index 000000000000..447d04443744
|
||||
--- /dev/null
|
||||
+++ b/webapps/examples/WEB-INF/classes/sessions/Item.java
|
||||
@@ -0,0 +1,37 @@
|
||||
+/*
|
||||
+ * 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 sessions;
|
||||
+
|
||||
+public enum Item {
|
||||
+
|
||||
+ VIDEO("Beavis & Butt-head Video collection"),
|
||||
+ MOVIE("X-files movie"),
|
||||
+ TAPES("Twin peaks tapes"),
|
||||
+ CD("NIN CD"),
|
||||
+ BOOK("JSP Book"),
|
||||
+ TICKETS("Concert tickets");
|
||||
+
|
||||
+ private final String title;
|
||||
+
|
||||
+ Item(String title) {
|
||||
+ this.title = title;
|
||||
+ }
|
||||
+
|
||||
+ public String getTitle() {
|
||||
+ return title;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/webapps/examples/jsp/index.html b/webapps/examples/jsp/index.html
|
||||
index b2371207cfe7..dc25005b2710 100644
|
||||
--- a/webapps/examples/jsp/index.html
|
||||
+++ b/webapps/examples/jsp/index.html
|
||||
@@ -228,7 +228,7 @@ <h2>JSP 1.2 Examples</h2>
|
||||
<tr>
|
||||
<td>Carts</td>
|
||||
|
||||
-<td style="width: 30%;"><a href="sessions/carts.html"><img src="images/execute.gif" alt=""></a><a href="sessions/carts.html">Execute</a></td>
|
||||
+<td style="width: 30%;"><a href="sessions/shopping.jsp"><img src="images/execute.gif" alt=""></a><a href="sessions/shopping.jsp">Execute</a></td>
|
||||
|
||||
<td style="width: 30%;"><a href="sessions/crt.html"><img src="images/code.gif" alt=""></a><a href="sessions/crt.html">Source</a></td>
|
||||
</tr>
|
||||
diff --git a/webapps/examples/jsp/sessions/carts.jsp b/webapps/examples/jsp/sessions/carts.jsp
|
||||
index dc51495c4bec..fa5624dee6bd 100644
|
||||
--- a/webapps/examples/jsp/sessions/carts.jsp
|
||||
+++ b/webapps/examples/jsp/sessions/carts.jsp
|
||||
@@ -27,10 +27,10 @@
|
||||
<br> You have the following items in your cart:
|
||||
<ol>
|
||||
<%
|
||||
- String[] items = cart.getItems();
|
||||
- for (String item : items) {
|
||||
+ Item[] items = cart.getItems();
|
||||
+ for (Item item : items) {
|
||||
%>
|
||||
-<li> <% out.print(util.HTMLFilter.filter(item)); %>
|
||||
+<li> <% out.print(util.HTMLFilter.filter(item.getTitle())); %>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
@@ -39,5 +39,5 @@
|
||||
</FONT>
|
||||
|
||||
<hr>
|
||||
-<%@ include file ="carts.html" %>
|
||||
+<%@ include file ="shopping.jsp" %>
|
||||
</html>
|
||||
diff --git a/webapps/examples/jsp/sessions/crt.html b/webapps/examples/jsp/sessions/crt.html
|
||||
index 11e6edafe0e9..28ace66012c7 100644
|
||||
--- a/webapps/examples/jsp/sessions/crt.html
|
||||
+++ b/webapps/examples/jsp/sessions/crt.html
|
||||
@@ -22,9 +22,12 @@
|
||||
</head>
|
||||
|
||||
<body bgcolor="#FFFFFF">
|
||||
-<p><font color="#0000FF"><a href="carts.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
|
||||
+<p><font color="#0000FF"><a href="shopping.jsp"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
|
||||
|
||||
-<h3><a href="carts.jsp.html">Source Code for Cart Example<font color="#0000FF"></a>
|
||||
+<h3><a href="shopping.jsp.html">Source Code for Cart Example Shopping Page<font color="#0000FF"></a>
|
||||
+ </font> </h3>
|
||||
+
|
||||
+<h3><a href="carts.jsp.html">Source Code for Cart Example Cart Page<font color="#0000FF"></a>
|
||||
</font> </h3>
|
||||
|
||||
<h3><a href="DummyCart.html">Property Sheet for DummyCart
|
||||
diff --git a/webapps/examples/jsp/sessions/carts.html b/webapps/examples/jsp/sessions/shopping.jsp
|
||||
similarity index 75%
|
||||
rename from webapps/examples/jsp/sessions/carts.html
|
||||
rename to webapps/examples/jsp/sessions/shopping.jsp
|
||||
index 834ee0a594fd..3487b1029547 100644
|
||||
--- a/webapps/examples/jsp/sessions/carts.html
|
||||
+++ b/webapps/examples/jsp/sessions/shopping.jsp
|
||||
@@ -1,5 +1,4 @@
|
||||
-<html>
|
||||
-<!--
|
||||
+<%--
|
||||
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.
|
||||
@@ -14,33 +13,31 @@
|
||||
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.
|
||||
--->
|
||||
-
|
||||
+--%>
|
||||
+<%@ page import="sessions.Item" %>
|
||||
+<html>
|
||||
<head>
|
||||
- <title>carts</title>
|
||||
+<title>Shopping Cart Example</title>
|
||||
</head>
|
||||
|
||||
- <body bgcolor="white">
|
||||
+<body bgcolor="white">
|
||||
<font size = 5 color="#CC0000">
|
||||
|
||||
<form type=POST action=carts.jsp>
|
||||
-<BR>
|
||||
+<br>
|
||||
Please enter item to add or remove:
|
||||
<br>
|
||||
-Add Item:
|
||||
-
|
||||
-<SELECT NAME="item">
|
||||
-<OPTION>Beavis & Butt-head Video collection
|
||||
-<OPTION>X-files movie
|
||||
-<OPTION>Twin peaks tapes
|
||||
-<OPTION>NIN CD
|
||||
-<OPTION>JSP Book
|
||||
-<OPTION>Concert tickets
|
||||
-<OPTION>Love life
|
||||
-<OPTION>Switch blade
|
||||
-<OPTION>Rex, Rugs & Rock n' Roll
|
||||
-</SELECT>
|
||||
-
|
||||
+Select Item:
|
||||
+
|
||||
+<select name="itemId">
|
||||
+<%
|
||||
+ for (Item item : Item.values()) {
|
||||
+%>
|
||||
+ <option value="<%= item.ordinal() %>"><%= item.getTitle() %></option>
|
||||
+<%
|
||||
+ }
|
||||
+%>
|
||||
+</select>
|
||||
|
||||
<br> <br>
|
||||
<INPUT TYPE=submit name="submit" value="add">
|
||||
@@ -48,6 +45,6 @@
|
||||
|
||||
</form>
|
||||
|
||||
-</FONT>
|
||||
+</font>
|
||||
</body>
|
||||
</html>
|
||||
717
CVE-2024-54677-8.patch
Normal file
717
CVE-2024-54677-8.patch
Normal file
@ -0,0 +1,717 @@
|
||||
From 84c4af76e7a10fc7f8630ce62e6a46632ea4a90e Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Wed, 4 Dec 2024 18:41:34 +0000
|
||||
Subject: [PATCH] Remove JSP calendar example.
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/84c4af76e7a10fc7f8630ce62e6a46632ea4a90e
|
||||
---
|
||||
build.xml | 9 --
|
||||
webapps/docs/changelog.xml | 3 +
|
||||
.../examples/WEB-INF/classes/cal/Entries.java | 63 --------
|
||||
.../examples/WEB-INF/classes/cal/Entry.java | 52 ------
|
||||
.../WEB-INF/classes/cal/JspCalendar.java | 152 ------------------
|
||||
.../WEB-INF/classes/cal/TableBean.java | 106 ------------
|
||||
webapps/examples/jsp/cal/cal1.jsp | 94 -----------
|
||||
webapps/examples/jsp/cal/cal2.jsp | 45 ------
|
||||
webapps/examples/jsp/cal/calendar.html | 43 -----
|
||||
webapps/examples/jsp/cal/login.html | 47 ------
|
||||
webapps/examples/jsp/index.html | 8 -
|
||||
11 files changed, 3 insertions(+), 619 deletions(-)
|
||||
delete mode 100644 webapps/examples/WEB-INF/classes/cal/Entries.java
|
||||
delete mode 100644 webapps/examples/WEB-INF/classes/cal/Entry.java
|
||||
delete mode 100644 webapps/examples/WEB-INF/classes/cal/JspCalendar.java
|
||||
delete mode 100644 webapps/examples/WEB-INF/classes/cal/TableBean.java
|
||||
delete mode 100644 webapps/examples/jsp/cal/cal1.jsp
|
||||
delete mode 100644 webapps/examples/jsp/cal/cal2.jsp
|
||||
delete mode 100644 webapps/examples/jsp/cal/calendar.html
|
||||
delete mode 100644 webapps/examples/jsp/cal/login.html
|
||||
|
||||
diff --git a/build.xml b/build.xml
|
||||
index 52f5fa11252f..5b3e1738c283 100644
|
||||
--- a/build.xml
|
||||
+++ b/build.xml
|
||||
@@ -1646,15 +1646,6 @@
|
||||
</fileset>
|
||||
</txt2html>
|
||||
|
||||
- <txt2html todir="${tomcat.build}/webapps/examples/jsp/cal">
|
||||
- <fileset dir="webapps/examples/WEB-INF/classes/cal">
|
||||
- <include name="Entries.java"/>
|
||||
- <include name="Entry.java"/>
|
||||
- <include name="JspCalendar.java"/>
|
||||
- <include name="TableBean.java"/>
|
||||
- </fileset>
|
||||
- </txt2html>
|
||||
-
|
||||
<txt2html todir="${tomcat.build}/webapps/examples/jsp/jsptoserv">
|
||||
<fileset dir="webapps/examples/WEB-INF/classes">
|
||||
<include name="ServletToJsp.java"/>
|
||||
diff --git a/webapps/examples/WEB-INF/classes/cal/Entries.java b/webapps/examples/WEB-INF/classes/cal/Entries.java
|
||||
deleted file mode 100644
|
||||
index cac611a03b1f..000000000000
|
||||
--- a/webapps/examples/WEB-INF/classes/cal/Entries.java
|
||||
+++ /dev/null
|
||||
@@ -1,63 +0,0 @@
|
||||
-/*
|
||||
- * 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 cal;
|
||||
-
|
||||
-import java.util.Map;
|
||||
-import java.util.concurrent.ConcurrentHashMap;
|
||||
-
|
||||
-import javax.servlet.http.HttpServletRequest;
|
||||
-
|
||||
-public class Entries {
|
||||
-
|
||||
- private final Map<String, Entry> entries;
|
||||
- private static final String[] time = { "8am", "9am", "10am", "11am",
|
||||
- "12pm", "1pm", "2pm", "3pm", "4pm", "5pm", "6pm", "7pm", "8pm" };
|
||||
- public static final int rows = 12;
|
||||
-
|
||||
- public Entries() {
|
||||
- entries = new ConcurrentHashMap<>(rows);
|
||||
- for (int i = 0; i < rows; i++) {
|
||||
- entries.put(time[i], new Entry(time[i]));
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- public int getRows() {
|
||||
- return rows;
|
||||
- }
|
||||
-
|
||||
- public Entry getEntry(int index) {
|
||||
- return this.entries.get(time[index]);
|
||||
- }
|
||||
-
|
||||
- public int getIndex(String tm) {
|
||||
- for (int i = 0; i < rows; i++) {
|
||||
- if (tm.equals(time[i])) {
|
||||
- return i;
|
||||
- }
|
||||
- }
|
||||
- return -1;
|
||||
- }
|
||||
-
|
||||
- public void processRequest(HttpServletRequest request, String tm) {
|
||||
- int index = getIndex(tm);
|
||||
- if (index >= 0) {
|
||||
- String descr = request.getParameter("description");
|
||||
- entries.get(time[index]).setDescription(descr);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
-}
|
||||
diff --git a/webapps/examples/WEB-INF/classes/cal/Entry.java b/webapps/examples/WEB-INF/classes/cal/Entry.java
|
||||
deleted file mode 100644
|
||||
index ac248bfa3169..000000000000
|
||||
--- a/webapps/examples/WEB-INF/classes/cal/Entry.java
|
||||
+++ /dev/null
|
||||
@@ -1,52 +0,0 @@
|
||||
-/*
|
||||
- * 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 cal;
|
||||
-
|
||||
-public class Entry {
|
||||
-
|
||||
- final String hour;
|
||||
- String description;
|
||||
-
|
||||
- public Entry(String hour) {
|
||||
- this.hour = hour;
|
||||
- this.description = "";
|
||||
-
|
||||
- }
|
||||
-
|
||||
- public String getHour() {
|
||||
- return this.hour;
|
||||
- }
|
||||
-
|
||||
- public String getColor() {
|
||||
- if (description.equals("")) {
|
||||
- return "lightblue";
|
||||
- }
|
||||
- return "red";
|
||||
- }
|
||||
-
|
||||
- public String getDescription() {
|
||||
- if (description.equals("")) {
|
||||
- return "None";
|
||||
- }
|
||||
- return this.description;
|
||||
- }
|
||||
-
|
||||
- public void setDescription(String descr) {
|
||||
- description = descr;
|
||||
- }
|
||||
-
|
||||
-}
|
||||
diff --git a/webapps/examples/WEB-INF/classes/cal/JspCalendar.java b/webapps/examples/WEB-INF/classes/cal/JspCalendar.java
|
||||
deleted file mode 100644
|
||||
index 29541cccb400..000000000000
|
||||
--- a/webapps/examples/WEB-INF/classes/cal/JspCalendar.java
|
||||
+++ /dev/null
|
||||
@@ -1,152 +0,0 @@
|
||||
-/*
|
||||
- * 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 cal;
|
||||
-
|
||||
-import java.util.Calendar;
|
||||
-import java.util.Date;
|
||||
-
|
||||
-public class JspCalendar {
|
||||
- final Calendar calendar;
|
||||
-
|
||||
- public JspCalendar() {
|
||||
- calendar = Calendar.getInstance();
|
||||
- Date trialTime = new Date();
|
||||
- calendar.setTime(trialTime);
|
||||
- }
|
||||
-
|
||||
-
|
||||
- public int getYear() {
|
||||
- return calendar.get(Calendar.YEAR);
|
||||
- }
|
||||
-
|
||||
- public String getMonth() {
|
||||
- int m = getMonthInt();
|
||||
- String[] months = new String [] { "January", "February", "March",
|
||||
- "April", "May", "June",
|
||||
- "July", "August", "September",
|
||||
- "October", "November", "December" };
|
||||
- if (m > 12) {
|
||||
- return "Unknown to Man";
|
||||
- }
|
||||
-
|
||||
- return months[m - 1];
|
||||
-
|
||||
- }
|
||||
-
|
||||
- public String getDay() {
|
||||
- int x = getDayOfWeek();
|
||||
- String[] days = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday",
|
||||
- "Thursday", "Friday", "Saturday"};
|
||||
-
|
||||
- if (x > 7) {
|
||||
- return "Unknown to Man";
|
||||
- }
|
||||
-
|
||||
- return days[x - 1];
|
||||
-
|
||||
- }
|
||||
-
|
||||
- public int getMonthInt() {
|
||||
- return 1 + calendar.get(Calendar.MONTH);
|
||||
- }
|
||||
-
|
||||
- public String getDate() {
|
||||
- return getMonthInt() + "/" + getDayOfMonth() + "/" + getYear();
|
||||
- }
|
||||
-
|
||||
- public String getCurrentDate() {
|
||||
- Date dt = new Date ();
|
||||
- calendar.setTime (dt);
|
||||
- return getMonthInt() + "/" + getDayOfMonth() + "/" + getYear();
|
||||
-
|
||||
- }
|
||||
-
|
||||
- public String getNextDate() {
|
||||
- calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() + 1);
|
||||
- return getDate ();
|
||||
- }
|
||||
-
|
||||
- public String getPrevDate() {
|
||||
- calendar.set (Calendar.DAY_OF_MONTH, getDayOfMonth() - 1);
|
||||
- return getDate ();
|
||||
- }
|
||||
-
|
||||
- public String getTime() {
|
||||
- return getHour() + ":" + getMinute() + ":" + getSecond();
|
||||
- }
|
||||
-
|
||||
- public int getDayOfMonth() {
|
||||
- return calendar.get(Calendar.DAY_OF_MONTH);
|
||||
- }
|
||||
-
|
||||
- public int getDayOfYear() {
|
||||
- return calendar.get(Calendar.DAY_OF_YEAR);
|
||||
- }
|
||||
-
|
||||
- public int getWeekOfYear() {
|
||||
- return calendar.get(Calendar.WEEK_OF_YEAR);
|
||||
- }
|
||||
-
|
||||
- public int getWeekOfMonth() {
|
||||
- return calendar.get(Calendar.WEEK_OF_MONTH);
|
||||
- }
|
||||
-
|
||||
- public int getDayOfWeek() {
|
||||
- return calendar.get(Calendar.DAY_OF_WEEK);
|
||||
- }
|
||||
-
|
||||
- public int getHour() {
|
||||
- return calendar.get(Calendar.HOUR_OF_DAY);
|
||||
- }
|
||||
-
|
||||
- public int getMinute() {
|
||||
- return calendar.get(Calendar.MINUTE);
|
||||
- }
|
||||
-
|
||||
-
|
||||
- public int getSecond() {
|
||||
- return calendar.get(Calendar.SECOND);
|
||||
- }
|
||||
-
|
||||
-
|
||||
- public int getEra() {
|
||||
- return calendar.get(Calendar.ERA);
|
||||
- }
|
||||
-
|
||||
- public String getUSTimeZone() {
|
||||
- String[] zones = new String[] {"Hawaii", "Alaskan", "Pacific",
|
||||
- "Mountain", "Central", "Eastern"};
|
||||
-
|
||||
- return zones[10 + getZoneOffset()];
|
||||
- }
|
||||
-
|
||||
- public int getZoneOffset() {
|
||||
- return calendar.get(Calendar.ZONE_OFFSET)/(60*60*1000);
|
||||
- }
|
||||
-
|
||||
-
|
||||
- public int getDSTOffset() {
|
||||
- return calendar.get(Calendar.DST_OFFSET)/(60*60*1000);
|
||||
- }
|
||||
-
|
||||
-
|
||||
- public int getAMPM() {
|
||||
- return calendar.get(Calendar.AM_PM);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-
|
||||
diff --git a/webapps/examples/WEB-INF/classes/cal/TableBean.java b/webapps/examples/WEB-INF/classes/cal/TableBean.java
|
||||
deleted file mode 100644
|
||||
index 9f1cc4a6cf7d..000000000000
|
||||
--- a/webapps/examples/WEB-INF/classes/cal/TableBean.java
|
||||
+++ /dev/null
|
||||
@@ -1,106 +0,0 @@
|
||||
-/*
|
||||
- * 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 cal;
|
||||
-
|
||||
-import java.util.Map;
|
||||
-import java.util.concurrent.ConcurrentHashMap;
|
||||
-
|
||||
-import javax.servlet.http.HttpServletRequest;
|
||||
-
|
||||
-public class TableBean {
|
||||
-
|
||||
- private final Map<String, Entries> table;
|
||||
- private final JspCalendar JspCal;
|
||||
- private Entries entries;
|
||||
- private String date;
|
||||
- private String name = null;
|
||||
- private String email = null;
|
||||
- private boolean processError = false;
|
||||
-
|
||||
- public TableBean() {
|
||||
- this.table = new ConcurrentHashMap<>(10);
|
||||
- this.JspCal = new JspCalendar();
|
||||
- this.date = JspCal.getCurrentDate();
|
||||
- }
|
||||
-
|
||||
- public void setName(String nm) {
|
||||
- this.name = nm;
|
||||
- }
|
||||
-
|
||||
- public String getName() {
|
||||
- return this.name;
|
||||
- }
|
||||
-
|
||||
- public void setEmail(String mail) {
|
||||
- this.email = mail;
|
||||
- }
|
||||
-
|
||||
- public String getEmail() {
|
||||
- return this.email;
|
||||
- }
|
||||
-
|
||||
- public String getDate() {
|
||||
- return this.date;
|
||||
- }
|
||||
-
|
||||
- public Entries getEntries() {
|
||||
- return this.entries;
|
||||
- }
|
||||
-
|
||||
- public void processRequest(HttpServletRequest request) {
|
||||
-
|
||||
- // Get the name and e-mail.
|
||||
- this.processError = false;
|
||||
- if (name == null || name.equals("")) {
|
||||
- setName(request.getParameter("name"));
|
||||
- }
|
||||
- if (email == null || email.equals("")) {
|
||||
- setEmail(request.getParameter("email"));
|
||||
- }
|
||||
- if (name == null || email == null || name.equals("")
|
||||
- || email.equals("")) {
|
||||
- this.processError = true;
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- // Get the date.
|
||||
- String dateR = request.getParameter("date");
|
||||
- if (dateR == null) {
|
||||
- date = JspCal.getCurrentDate();
|
||||
- } else if (dateR.equalsIgnoreCase("next")) {
|
||||
- date = JspCal.getNextDate();
|
||||
- } else if (dateR.equalsIgnoreCase("prev")) {
|
||||
- date = JspCal.getPrevDate();
|
||||
- }
|
||||
-
|
||||
- entries = table.get(date);
|
||||
- if (entries == null) {
|
||||
- entries = new Entries();
|
||||
- table.put(date, entries);
|
||||
- }
|
||||
-
|
||||
- // If time is provided add the event.
|
||||
- String time = request.getParameter("time");
|
||||
- if (time != null) {
|
||||
- entries.processRequest(request, time);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- public boolean getProcessError() {
|
||||
- return this.processError;
|
||||
- }
|
||||
-}
|
||||
diff --git a/webapps/examples/jsp/cal/cal1.jsp b/webapps/examples/jsp/cal/cal1.jsp
|
||||
deleted file mode 100644
|
||||
index ce29c13f9608..000000000000
|
||||
--- a/webapps/examples/jsp/cal/cal1.jsp
|
||||
+++ /dev/null
|
||||
@@ -1,94 +0,0 @@
|
||||
-<%--
|
||||
- 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.
|
||||
---%>
|
||||
-<%@page contentType="text/html; charset=UTF-8" %>
|
||||
-<HTML>
|
||||
-<HEAD><TITLE>
|
||||
- Calendar: A JSP APPLICATION
|
||||
-</TITLE></HEAD>
|
||||
-
|
||||
-
|
||||
-<BODY BGCOLOR="white">
|
||||
-
|
||||
-<%@ page language="java" import="cal.*" %>
|
||||
-<jsp:useBean id="table" scope="session" class="cal.TableBean" />
|
||||
-
|
||||
-<%
|
||||
- table.processRequest(request);
|
||||
- if (table.getProcessError() == false) {
|
||||
-%>
|
||||
-
|
||||
-<!-- HTML table goes here -->
|
||||
-<CENTER>
|
||||
-<TABLE WIDTH=60% BGCOLOR=yellow CELLPADDING=15>
|
||||
-<TR>
|
||||
-<TD ALIGN=CENTER> <A HREF=cal1.jsp?date=prev> prev </A>
|
||||
-<TD ALIGN=CENTER> Calendar:<%= table.getDate() %></TD>
|
||||
-<TD ALIGN=CENTER> <A HREF=cal1.jsp?date=next> next </A>
|
||||
-</TR>
|
||||
-</TABLE>
|
||||
-
|
||||
-<!-- the main table -->
|
||||
-<TABLE WIDTH=60% BGCOLOR=lightblue BORDER=1 CELLPADDING=10>
|
||||
-<TR>
|
||||
-<TH> Time </TH>
|
||||
-<TH> Appointment </TH>
|
||||
-</TR>
|
||||
-<FORM METHOD=POST ACTION=cal1.jsp>
|
||||
-<%
|
||||
- for(int i=0; i<table.getEntries().getRows(); i++) {
|
||||
- cal.Entry entr = table.getEntries().getEntry(i);
|
||||
-%>
|
||||
- <TR>
|
||||
- <TD>
|
||||
- <A HREF=cal2.jsp?time=<%= entr.getHour() %>>
|
||||
- <%= entr.getHour() %> </A>
|
||||
- </TD>
|
||||
- <TD BGCOLOR=<%= entr.getColor() %>>
|
||||
- <% out.print(util.HTMLFilter.filter(entr.getDescription())); %>
|
||||
- </TD>
|
||||
- </TR>
|
||||
-<%
|
||||
- }
|
||||
-%>
|
||||
-</FORM>
|
||||
-</TABLE>
|
||||
-<BR>
|
||||
-
|
||||
-<!-- footer -->
|
||||
-<TABLE WIDTH=60% BGCOLOR=yellow CELLPADDING=15>
|
||||
-<TR>
|
||||
-<TD ALIGN=CENTER> <% out.print(util.HTMLFilter.filter(table.getName())); %> :
|
||||
- <% out.print(util.HTMLFilter.filter(table.getEmail())); %> </TD>
|
||||
-</TR>
|
||||
-</TABLE>
|
||||
-</CENTER>
|
||||
-
|
||||
-<%
|
||||
- } else {
|
||||
-%>
|
||||
-<font size=5>
|
||||
- You must enter your name and email address correctly.
|
||||
-</font>
|
||||
-<%
|
||||
- }
|
||||
-%>
|
||||
-
|
||||
-
|
||||
-</BODY>
|
||||
-</HTML>
|
||||
-
|
||||
-
|
||||
diff --git a/webapps/examples/jsp/cal/cal2.jsp b/webapps/examples/jsp/cal/cal2.jsp
|
||||
deleted file mode 100644
|
||||
index e7e14d8e0468..000000000000
|
||||
--- a/webapps/examples/jsp/cal/cal2.jsp
|
||||
+++ /dev/null
|
||||
@@ -1,45 +0,0 @@
|
||||
-<%--
|
||||
- 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.
|
||||
---%>
|
||||
-<%@page contentType="text/html; charset=UTF-8" %>
|
||||
-<HTML>
|
||||
-<HEAD><TITLE>
|
||||
- Calendar: A JSP APPLICATION
|
||||
-</TITLE></HEAD>
|
||||
-
|
||||
-
|
||||
-<BODY BGCOLOR="white">
|
||||
-<jsp:useBean id="table" scope="session" class="cal.TableBean" />
|
||||
-
|
||||
-<%
|
||||
- String time = request.getParameter ("time");
|
||||
-%>
|
||||
-
|
||||
-<FONT SIZE=5> Please add the following event:
|
||||
-<BR> <h3> Date <%= table.getDate() %>
|
||||
-<BR> Time <%= util.HTMLFilter.filter(time) %> </h3>
|
||||
-</FONT>
|
||||
-<FORM METHOD=POST ACTION=cal1.jsp>
|
||||
-<BR>
|
||||
-<BR> <INPUT NAME="date" TYPE=HIDDEN VALUE="current">
|
||||
-<BR> <INPUT NAME="time" TYPE=HIDDEN VALUE="<%= util.HTMLFilter.filter(time) %>">
|
||||
-<BR> <h2> Description of the event <INPUT NAME="description" TYPE=TEXT SIZE=20> </h2>
|
||||
-<BR> <INPUT TYPE=SUBMIT VALUE="submit">
|
||||
-</FORM>
|
||||
-
|
||||
-</BODY>
|
||||
-</HTML>
|
||||
-
|
||||
diff --git a/webapps/examples/jsp/cal/calendar.html b/webapps/examples/jsp/cal/calendar.html
|
||||
deleted file mode 100644
|
||||
index a0a3ea184134..000000000000
|
||||
--- a/webapps/examples/jsp/cal/calendar.html
|
||||
+++ /dev/null
|
||||
@@ -1,43 +0,0 @@
|
||||
-<html>
|
||||
-<!--
|
||||
- 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.
|
||||
--->
|
||||
-
|
||||
-<head>
|
||||
-<title>Untitled Document</title>
|
||||
-<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
||||
-</head>
|
||||
-
|
||||
-<body bgcolor="#FFFFFF">
|
||||
-<p><font color="#0000FF"><a href="login.html"><img src="../images/execute.gif" align="right" border="0"></a><a href="../index.html"><img src="../images/return.gif" width="24" height="24" align="right" border="0"></a></font></p>
|
||||
-
|
||||
-<h2> Source Code for Calendar Example. <br>
|
||||
-<h3><a href="cal1.jsp.html">cal1.jsp<font color="#0000FF"></a>
|
||||
- </font> </h3>
|
||||
-<h3><a href="cal2.jsp.html">cal2.jsp<font color="#0000FF"></a>
|
||||
- </font> </h3>
|
||||
-
|
||||
-<br>
|
||||
-<h2> Beans.
|
||||
-<h3><a href="TableBean.java.html">TableBean<font color="#0000FF"></a>
|
||||
- </font> </h3>
|
||||
-<h3><a href="Entries.java.html">Entries<font color="#0000FF"></a>
|
||||
- </font> </h3>
|
||||
-<h3><a href="Entry.java.html">Entry<font color="#0000FF"></a>
|
||||
- </font> </h3>
|
||||
-
|
||||
-</body>
|
||||
-</html>
|
||||
diff --git a/webapps/examples/jsp/cal/login.html b/webapps/examples/jsp/cal/login.html
|
||||
deleted file mode 100644
|
||||
index 8a62eca07b4d..000000000000
|
||||
--- a/webapps/examples/jsp/cal/login.html
|
||||
+++ /dev/null
|
||||
@@ -1,47 +0,0 @@
|
||||
-<html>
|
||||
-<!--
|
||||
- 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.
|
||||
--->
|
||||
-
|
||||
-<head>
|
||||
- <title> Login page for the calendar. </title>
|
||||
-</head>
|
||||
-
|
||||
-<body bgcolor="white">
|
||||
-<center>
|
||||
-
|
||||
- <font size=7 color="red"> Please Enter the following information: </font>
|
||||
-
|
||||
-<br>
|
||||
- <form method=GET action=cal1.jsp>
|
||||
-
|
||||
- <font size=5> Name <input type=text name="name" size=20>
|
||||
- </font>
|
||||
- <br>
|
||||
- <font size=5> Email <input type=text name="email" size=20>
|
||||
- </font>
|
||||
- <br>
|
||||
- <input type=submit name=action value="Submit">
|
||||
-
|
||||
- </form>
|
||||
-<hr>
|
||||
-<font size=3 color="red"> Note: This application does not implement the complete
|
||||
-functionality of a typical calendar application. It demonstrates a way JSP can
|
||||
-be used with HTML tables and forms.</font>
|
||||
-
|
||||
-</center>
|
||||
-</body>
|
||||
-</html>
|
||||
diff --git a/webapps/examples/jsp/index.html b/webapps/examples/jsp/index.html
|
||||
index dc25005b2710..ed2da43b9a12 100644
|
||||
--- a/webapps/examples/jsp/index.html
|
||||
+++ b/webapps/examples/jsp/index.html
|
||||
@@ -249,14 +249,6 @@ <h2>JSP 1.2 Examples</h2>
|
||||
<td style="width: 30%;"><a href="colors/clr.html"><img src="images/code.gif" alt=""></a><a href="colors/clr.html">Source</a></td>
|
||||
</tr>
|
||||
|
||||
-<tr>
|
||||
-<td>Calendar</td>
|
||||
-
|
||||
-<td style="width: 30%;"><a href="cal/login.html"><img src="images/execute.gif" alt=""></a><a href="cal/login.html">Execute</a></td>
|
||||
-
|
||||
-<td style="width: 30%;"><a href="cal/calendar.html"><img src="images/code.gif" alt=""></a><a href="cal/calendar.html">Source</a></td>
|
||||
-</tr>
|
||||
-
|
||||
<tr>
|
||||
<td>Include</td>
|
||||
|
||||
22
CVE-2024-54677-9.patch
Normal file
22
CVE-2024-54677-9.patch
Normal file
@ -0,0 +1,22 @@
|
||||
From 9ffd23fc27f5d1fc95bf97e5cea175c8968f4533 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Fri, 6 Dec 2024 10:44:05 +0000
|
||||
Subject: [PATCH] Revert change made for testing
|
||||
|
||||
---
|
||||
webapps/examples/WEB-INF/classes/RequestHeaderExample.java | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
|
||||
index e32f8c233674..b324e0250d8a 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
|
||||
@@ -73,7 +73,7 @@ protected boolean prefersJSON(String acceptHeader) {
|
||||
|
||||
// text/html, application/html, etc.
|
||||
if (accept.contains("html")) {
|
||||
- return true;
|
||||
+ return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
440
CVE-2024-54677-pre.patch
Normal file
440
CVE-2024-54677-pre.patch
Normal file
@ -0,0 +1,440 @@
|
||||
From 5adf342f83eebd5a5e0a83141c56955a25f3a0c3 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Thomas <markt@apache.org>
|
||||
Date: Mon, 2 Dec 2024 14:19:14 +0000
|
||||
Subject: [PATCH] Code clean-up - formatting. No functional change
|
||||
|
||||
Origin: https://github.com/apache/tomcat/commit/5adf342f83eebd5a5e0a83141c56955a25f3a0c3
|
||||
---
|
||||
.../WEB-INF/classes/CookieExample.java | 32 +++++-------
|
||||
.../WEB-INF/classes/HelloWorldExample.java | 19 +++----
|
||||
.../WEB-INF/classes/RequestHeaderExample.java | 49 ++++++-------------
|
||||
.../WEB-INF/classes/RequestInfoExample.java | 25 ++++------
|
||||
.../WEB-INF/classes/RequestParamExample.java | 22 +++------
|
||||
.../WEB-INF/classes/ServletToJsp.java | 19 ++++---
|
||||
.../WEB-INF/classes/SessionExample.java | 23 +++------
|
||||
7 files changed, 67 insertions(+), 122 deletions(-)
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/CookieExample.java b/webapps/examples/WEB-INF/classes/CookieExample.java
|
||||
index cbf375e861bc..32eb1f806ce8 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/CookieExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/CookieExample.java
|
||||
@@ -40,11 +40,8 @@ public class CookieExample extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
- public void doGet(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
|
||||
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
|
||||
|
||||
String cookieName = request.getParameter("cookiename");
|
||||
String cookieValue = request.getParameter("cookievalue");
|
||||
@@ -75,11 +72,11 @@ public void doGet(HttpServletRequest request,
|
||||
// addition of a PathInfo issue
|
||||
|
||||
out.println("<a href=\"../cookies.html\">");
|
||||
- out.println("<img src=\"../images/code.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
out.println("<a href=\"../index.html\">");
|
||||
- out.println("<img src=\"../images/return.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
|
||||
out.println("<h3>" + title + "</h3>");
|
||||
|
||||
@@ -95,9 +92,8 @@ public void doGet(HttpServletRequest request,
|
||||
String cName = cookie.getName();
|
||||
String cValue = cookie.getValue();
|
||||
out.print("Cookie Name: " + HTMLFilter.filter(cName) + "<br>");
|
||||
- out.println(" Cookie Value: "
|
||||
- + HTMLFilter.filter(CookieFilter.filter(cName, cValue, sessionId))
|
||||
- + "<br><br>");
|
||||
+ out.println(" Cookie Value: " + HTMLFilter.filter(CookieFilter.filter(cName, cValue, sessionId)) +
|
||||
+ "<br><br>");
|
||||
}
|
||||
} else {
|
||||
out.println(rb.getString("cookies.no-cookies"));
|
||||
@@ -106,10 +102,8 @@ public void doGet(HttpServletRequest request,
|
||||
if (aCookie != null) {
|
||||
out.println("<P>");
|
||||
out.println(rb.getString("cookies.set") + "<br>");
|
||||
- out.print(rb.getString("cookies.name") + " "
|
||||
- + HTMLFilter.filter(cookieName) + "<br>");
|
||||
- out.print(rb.getString("cookies.value") + " "
|
||||
- + HTMLFilter.filter(cookieValue));
|
||||
+ out.print(rb.getString("cookies.name") + " " + HTMLFilter.filter(cookieName) + "<br>");
|
||||
+ out.print(rb.getString("cookies.value") + " " + HTMLFilter.filter(cookieValue));
|
||||
}
|
||||
|
||||
out.println("<P>");
|
||||
@@ -128,13 +122,9 @@ public void doGet(HttpServletRequest request,
|
||||
}
|
||||
|
||||
@Override
|
||||
- public void doPost(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
doGet(request, response);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-
|
||||
diff --git a/webapps/examples/WEB-INF/classes/HelloWorldExample.java b/webapps/examples/WEB-INF/classes/HelloWorldExample.java
|
||||
index 15911e23af85..346733aba9ab 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/HelloWorldExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/HelloWorldExample.java
|
||||
@@ -1,3 +1,4 @@
|
||||
+
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
@@ -34,12 +35,8 @@ public class HelloWorldExample extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
- public void doGet(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
- ResourceBundle rb =
|
||||
- ResourceBundle.getBundle("LocalStrings",request.getLocale());
|
||||
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
|
||||
response.setContentType("text/html");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
@@ -64,16 +61,14 @@ public void doGet(HttpServletRequest request,
|
||||
// addition of a PathInfo issue
|
||||
|
||||
out.println("<a href=\"../helloworld.html\">");
|
||||
- out.println("<img src=\"../images/code.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
out.println("<a href=\"../index.html\">");
|
||||
- out.println("<img src=\"../images/return.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
out.println("<h1>" + title + "</h1>");
|
||||
out.println("</body>");
|
||||
out.println("</html>");
|
||||
}
|
||||
}
|
||||
|
||||
-
|
||||
-
|
||||
diff --git a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
|
||||
index df2d2d51f78a..98c0e6a9878b 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/RequestHeaderExample.java
|
||||
@@ -42,10 +42,7 @@ public class RequestHeaderExample extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
- public void doGet(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
if (prefersJSON(request.getHeader("Accept"))) {
|
||||
renderJSON(request, response);
|
||||
} else {
|
||||
@@ -54,16 +51,12 @@ public void doGet(HttpServletRequest request,
|
||||
}
|
||||
|
||||
/**
|
||||
- * Returns true if the client appears to prefer a JSON response,
|
||||
- * false otherwise.
|
||||
- *
|
||||
- * Note that this method is not very pedantic and uses only a very lazy
|
||||
- * algorithm for checking whether JSON is "preferred".
|
||||
+ * Returns true if the client appears to prefer a JSON response, false otherwise. Note that this method is not very
|
||||
+ * pedantic and uses only a very lazy algorithm for checking whether JSON is "preferred".
|
||||
*
|
||||
* @param acceptHeader The value of the HTTP "Accept" header from the client.
|
||||
*
|
||||
- * @return true if the client appears to prefer a JSON response,
|
||||
- * false otherwise.
|
||||
+ * @return true if the client appears to prefer a JSON response, false otherwise.
|
||||
*/
|
||||
protected boolean prefersJSON(String acceptHeader) {
|
||||
if (null == acceptHeader) {
|
||||
@@ -87,11 +80,8 @@ protected boolean prefersJSON(String acceptHeader) {
|
||||
return false;
|
||||
}
|
||||
|
||||
- protected void renderHTML(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException
|
||||
- {
|
||||
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
|
||||
+ protected void renderHTML(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
|
||||
|
||||
response.setContentType("text/html");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
@@ -113,11 +103,11 @@ protected void renderHTML(HttpServletRequest request,
|
||||
// addition of a PathInfo issue
|
||||
|
||||
out.println("<a href=\"../reqheaders.html\">");
|
||||
- out.println("<img src=\"../images/code.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
out.println("<a href=\"../index.html\">");
|
||||
- out.println("<img src=\"../images/return.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
|
||||
out.println("<h3>" + title + "</h3>");
|
||||
out.println("<table border=0>");
|
||||
@@ -143,9 +133,7 @@ protected void renderHTML(HttpServletRequest request,
|
||||
out.println("</table>");
|
||||
}
|
||||
|
||||
- protected void renderJSON(HttpServletRequest request, HttpServletResponse response)
|
||||
- throws IOException
|
||||
- {
|
||||
+ protected void renderJSON(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
response.setContentType("application/json");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
|
||||
@@ -157,14 +145,10 @@ protected void renderJSON(HttpServletRequest request, HttpServletResponse respon
|
||||
String headerName = e.nextElement();
|
||||
String headerValue = request.getHeader(headerName);
|
||||
|
||||
- out.append("{\"")
|
||||
- .append(JSONFilter.escape(headerName))
|
||||
- .append("\":\"")
|
||||
- .append(JSONFilter.escape(headerValue))
|
||||
- .append("\"}")
|
||||
- ;
|
||||
+ out.append("{\"").append(JSONFilter.escape(headerName)).append("\":\"")
|
||||
+ .append(JSONFilter.escape(headerValue)).append("\"}");
|
||||
|
||||
- if(e.hasMoreElements()) {
|
||||
+ if (e.hasMoreElements()) {
|
||||
out.append(',');
|
||||
}
|
||||
}
|
||||
@@ -173,10 +157,7 @@ protected void renderJSON(HttpServletRequest request, HttpServletResponse respon
|
||||
}
|
||||
|
||||
@Override
|
||||
- public void doPost(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
doGet(request, response);
|
||||
}
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/RequestInfoExample.java b/webapps/examples/WEB-INF/classes/RequestInfoExample.java
|
||||
index 9ea4668ad42d..57665bb2c51b 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/RequestInfoExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/RequestInfoExample.java
|
||||
@@ -37,11 +37,8 @@ public class RequestInfoExample extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
- public void doGet(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
|
||||
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
|
||||
|
||||
response.setContentType("text/html");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
@@ -64,11 +61,11 @@ public void doGet(HttpServletRequest request,
|
||||
// addition of a PathInfo issue
|
||||
|
||||
out.println("<a href=\"../reqinfo.html\">");
|
||||
- out.println("<img src=\"../images/code.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
out.println("<a href=\"../index.html\">");
|
||||
- out.println("<img src=\"../images/return.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
|
||||
out.println("<h3>" + title + "</h3>");
|
||||
out.println("<table border=0><tr><td>");
|
||||
@@ -93,9 +90,8 @@ public void doGet(HttpServletRequest request,
|
||||
out.println(HTMLFilter.filter(request.getRemoteAddr()));
|
||||
out.println("</td></tr>");
|
||||
|
||||
- String cipherSuite=
|
||||
- (String)request.getAttribute("javax.servlet.request.cipher_suite");
|
||||
- if(cipherSuite!=null){
|
||||
+ String cipherSuite = (String) request.getAttribute("javax.servlet.request.cipher_suite");
|
||||
+ if (cipherSuite != null) {
|
||||
out.println("<tr><td>");
|
||||
out.println("SSLCipherSuite:");
|
||||
out.println("</td><td>");
|
||||
@@ -107,10 +103,7 @@ public void doGet(HttpServletRequest request,
|
||||
}
|
||||
|
||||
@Override
|
||||
- public void doPost(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
doGet(request, response);
|
||||
}
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/RequestParamExample.java b/webapps/examples/WEB-INF/classes/RequestParamExample.java
|
||||
index cbcc3712b6d4..69482936c344 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/RequestParamExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/RequestParamExample.java
|
||||
@@ -37,11 +37,8 @@ public class RequestParamExample extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
- public void doGet(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
|
||||
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
|
||||
|
||||
response.setContentType("text/html");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
@@ -58,18 +55,18 @@ public void doGet(HttpServletRequest request,
|
||||
|
||||
// img stuff not req'd for source code HTML showing
|
||||
|
||||
- // all links relative
|
||||
+ // all links relative
|
||||
|
||||
// XXX
|
||||
// making these absolute till we work out the
|
||||
// addition of a PathInfo issue
|
||||
|
||||
out.println("<a href=\"../reqparams.html\">");
|
||||
- out.println("<img src=\"../images/code.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
out.println("<a href=\"../index.html\">");
|
||||
- out.println("<img src=\"../images/return.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
|
||||
out.println("<h3>" + title + "</h3>");
|
||||
String firstName = request.getParameter("firstname");
|
||||
@@ -101,10 +98,7 @@ public void doGet(HttpServletRequest request,
|
||||
}
|
||||
|
||||
@Override
|
||||
- public void doPost(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
doGet(request, response);
|
||||
}
|
||||
|
||||
diff --git a/webapps/examples/WEB-INF/classes/ServletToJsp.java b/webapps/examples/WEB-INF/classes/ServletToJsp.java
|
||||
index a4903a9bba74..e83bbf377620 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/ServletToJsp.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/ServletToJsp.java
|
||||
@@ -24,16 +24,15 @@ public class ServletToJsp extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
- public void doGet (HttpServletRequest request,
|
||||
- HttpServletResponse response) {
|
||||
+ public void doGet(HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
- try {
|
||||
- // Set the attribute and Forward to hello.jsp
|
||||
- request.setAttribute ("servletName", "servletToJsp");
|
||||
- getServletConfig().getServletContext().getRequestDispatcher(
|
||||
- "/jsp/jsptoserv/hello.jsp").forward(request, response);
|
||||
- } catch (Exception ex) {
|
||||
- ex.printStackTrace ();
|
||||
- }
|
||||
+ try {
|
||||
+ // Set the attribute and Forward to hello.jsp
|
||||
+ request.setAttribute("servletName", "servletToJsp");
|
||||
+ getServletConfig().getServletContext().getRequestDispatcher("/jsp/jsptoserv/hello.jsp").forward(request,
|
||||
+ response);
|
||||
+ } catch (Exception ex) {
|
||||
+ ex.printStackTrace();
|
||||
+ }
|
||||
}
|
||||
}
|
||||
diff --git a/webapps/examples/WEB-INF/classes/SessionExample.java b/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
index c71ef5f743bb..724b9b437d67 100644
|
||||
--- a/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
+++ b/webapps/examples/WEB-INF/classes/SessionExample.java
|
||||
@@ -40,11 +40,8 @@ public class SessionExample extends HttpServlet {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
- public void doGet(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
- ResourceBundle rb = ResourceBundle.getBundle("LocalStrings",request.getLocale());
|
||||
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
+ ResourceBundle rb = ResourceBundle.getBundle("LocalStrings", request.getLocale());
|
||||
|
||||
response.setContentType("text/html");
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
@@ -68,11 +65,11 @@ public void doGet(HttpServletRequest request,
|
||||
// addition of a PathInfo issue
|
||||
|
||||
out.println("<a href=\"../sessions.html\">");
|
||||
- out.println("<img src=\"../images/code.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/code.gif\" height=24 " + "width=24 align=right border=0 alt=\"view code\"></a>");
|
||||
out.println("<a href=\"../index.html\">");
|
||||
- out.println("<img src=\"../images/return.gif\" height=24 " +
|
||||
- "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
+ out.println(
|
||||
+ "<img src=\"../images/return.gif\" height=24 " + "width=24 align=right border=0 alt=\"return\"></a>");
|
||||
|
||||
out.println("<h3>" + title + "</h3>");
|
||||
|
||||
@@ -96,8 +93,7 @@ public void doGet(HttpServletRequest request,
|
||||
while (names.hasMoreElements()) {
|
||||
String name = names.nextElement();
|
||||
String value = session.getAttribute(name).toString();
|
||||
- out.println(HTMLFilter.filter(name) + " = "
|
||||
- + HTMLFilter.filter(value) + "<br>");
|
||||
+ out.println(HTMLFilter.filter(name) + " = " + HTMLFilter.filter(value) + "<br>");
|
||||
}
|
||||
|
||||
out.println("<P>");
|
||||
@@ -137,10 +133,7 @@ public void doGet(HttpServletRequest request,
|
||||
}
|
||||
|
||||
@Override
|
||||
- public void doPost(HttpServletRequest request,
|
||||
- HttpServletResponse response)
|
||||
- throws IOException, ServletException
|
||||
- {
|
||||
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
|
||||
doGet(request, response);
|
||||
}
|
||||
|
||||
BIN
apache-tomcat-9.0.96-src.tar.gz
Normal file
BIN
apache-tomcat-9.0.96-src.tar.gz
Normal file
Binary file not shown.
88
build-with-jdk-1.8.patch
Normal file
88
build-with-jdk-1.8.patch
Normal file
@ -0,0 +1,88 @@
|
||||
diff --git a/build.xml b/build.xml
|
||||
index 5c09831..9efcd81 100644
|
||||
--- a/build.xml
|
||||
+++ b/build.xml
|
||||
@@ -108,8 +108,8 @@
|
||||
<!-- Keep in sync with webapps/docs/tomcat-docs.xsl -->
|
||||
<property name="compile.release" value="8"/>
|
||||
<property name="min.java.version" value="8"/>
|
||||
- <property name="build.java.version" value="17"/>
|
||||
- <property name="release.java.version" value="17"/>
|
||||
+ <property name="build.java.version" value="1.8"/>
|
||||
+ <property name="release.java.version" value="1.8"/>
|
||||
|
||||
<!-- Check Java Build Version -->
|
||||
<fail message="Java version ${build.java.version} or newer is required (${java.version} is installed)">
|
||||
@@ -1497,8 +1497,6 @@
|
||||
<target name="deploy" depends="package,build-docs,build-tomcat-jdbc,compile-webapp-examples"
|
||||
description="Default. Builds a working Tomcat instance">
|
||||
|
||||
- <copy tofile="${tomcat.build}/bin/commons-daemon.jar" file="${commons-daemon.jar}" />
|
||||
-
|
||||
<!-- Copy scripts -->
|
||||
<copy todir="${tomcat.build}/bin">
|
||||
<fileset dir="bin">
|
||||
diff --git a/java/org/apache/jasper/compiler/JDTCompiler.java b/java/org/apache/jasper/compiler/JDTCompiler.java
|
||||
index 1ed4ef4..8339eff 100644
|
||||
--- a/java/org/apache/jasper/compiler/JDTCompiler.java
|
||||
+++ b/java/org/apache/jasper/compiler/JDTCompiler.java
|
||||
@@ -297,17 +297,17 @@ public class JDTCompiler extends org.apache.jasper.compiler.Compiler {
|
||||
} else if(opt.equals("10")) {
|
||||
settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_10);
|
||||
} else if(opt.equals("11")) {
|
||||
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_11);
|
||||
+ settings.put(CompilerOptions.OPTION_Source, "11");
|
||||
} else if(opt.equals("12")) {
|
||||
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_12);
|
||||
+ settings.put(CompilerOptions.OPTION_Source, "12");
|
||||
} else if(opt.equals("13")) {
|
||||
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_13);
|
||||
+ settings.put(CompilerOptions.OPTION_Source, "13");
|
||||
} else if(opt.equals("14")) {
|
||||
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_14);
|
||||
+ settings.put(CompilerOptions.OPTION_Source, "14");
|
||||
} else if(opt.equals("15")) {
|
||||
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_15);
|
||||
+ settings.put(CompilerOptions.OPTION_Source, "15");
|
||||
} else if(opt.equals("16")) {
|
||||
- settings.put(CompilerOptions.OPTION_Source, CompilerOptions.VERSION_16);
|
||||
+ settings.put(CompilerOptions.OPTION_Source, "16");
|
||||
} else if(opt.equals("17")) {
|
||||
// Constant not available in latest ECJ version that runs on
|
||||
// Java 8.
|
||||
@@ -389,23 +389,23 @@ public class JDTCompiler extends org.apache.jasper.compiler.Compiler {
|
||||
settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_10);
|
||||
settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_10);
|
||||
} else if(opt.equals("11")) {
|
||||
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_11);
|
||||
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_11);
|
||||
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "11");
|
||||
+ settings.put(CompilerOptions.OPTION_Compliance, "11");
|
||||
} else if(opt.equals("12")) {
|
||||
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_12);
|
||||
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_12);
|
||||
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "12");
|
||||
+ settings.put(CompilerOptions.OPTION_Compliance, "12");
|
||||
} else if(opt.equals("13")) {
|
||||
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_13);
|
||||
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_13);
|
||||
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "13");
|
||||
+ settings.put(CompilerOptions.OPTION_Compliance, "13");
|
||||
} else if(opt.equals("14")) {
|
||||
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_14);
|
||||
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_14);
|
||||
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "14");
|
||||
+ settings.put(CompilerOptions.OPTION_Compliance, "14");
|
||||
} else if(opt.equals("15")) {
|
||||
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_15);
|
||||
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_15);
|
||||
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "15");
|
||||
+ settings.put(CompilerOptions.OPTION_Compliance, "15");
|
||||
} else if(opt.equals("16")) {
|
||||
- settings.put(CompilerOptions.OPTION_TargetPlatform, CompilerOptions.VERSION_16);
|
||||
- settings.put(CompilerOptions.OPTION_Compliance, CompilerOptions.VERSION_16);
|
||||
+ settings.put(CompilerOptions.OPTION_TargetPlatform, "16");
|
||||
+ settings.put(CompilerOptions.OPTION_Compliance, "16");
|
||||
} else if(opt.equals("17")) {
|
||||
// Constant not available in latest ECJ version that runs on
|
||||
// Java 8.
|
||||
@ -1,47 +0,0 @@
|
||||
--- tomcat/build.xml.orig 2018-03-15 13:49:03.366863009 -0400
|
||||
+++ tomcat/build.xml 2018-03-15 13:49:29.690870139 -0400
|
||||
@@ -1777,7 +1777,7 @@ Apache Tomcat ${version} native binaries
|
||||
additionalparam="-breakiterator -notimestamp"
|
||||
maxmemory="512m"
|
||||
failonerror="true"
|
||||
- failonwarning="true">
|
||||
+ failonwarning="false">
|
||||
<classpath>
|
||||
<path refid="compile.classpath"/>
|
||||
<path refid="tomcat.webservices.classpath"/>
|
||||
@@ -1798,7 +1798,7 @@ Apache Tomcat ${version} native binaries
|
||||
additionalparam="-breakiterator -notimestamp"
|
||||
maxmemory="512m"
|
||||
failonerror="true"
|
||||
- failonwarning="true">
|
||||
+ failonwarning="false">
|
||||
<classpath>
|
||||
<path refid="compile.classpath"/>
|
||||
<path refid="tomcat.webservices.classpath"/>
|
||||
@@ -1819,7 +1819,7 @@ Apache Tomcat ${version} native binaries
|
||||
additionalparam="-breakiterator -notimestamp"
|
||||
maxmemory="512m"
|
||||
failonerror="true"
|
||||
- failonwarning="true">
|
||||
+ failonwarning="false">
|
||||
<classpath>
|
||||
<path refid="compile.classpath"/>
|
||||
<path refid="tomcat.webservices.classpath"/>
|
||||
@@ -1840,7 +1840,7 @@ Apache Tomcat ${version} native binaries
|
||||
additionalparam="-breakiterator -notimestamp"
|
||||
maxmemory="512m"
|
||||
failonerror="true"
|
||||
- failonwarning="true">
|
||||
+ failonwarning="false">
|
||||
<classpath>
|
||||
<path refid="compile.classpath"/>
|
||||
<path refid="tomcat.webservices.classpath"/>
|
||||
@@ -1860,7 +1860,7 @@ Apache Tomcat ${version} native binaries
|
||||
additionalparam="-breakiterator -notimestamp ${java9.add.modules}"
|
||||
maxmemory="512m"
|
||||
failonerror="true"
|
||||
- failonwarning="true">
|
||||
+ failonwarning="false">
|
||||
<classpath>
|
||||
<path refid="compile.classpath"/>
|
||||
<path refid="tomcat.webservices.classpath"/>
|
||||
@ -1,13 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Export-Package: javax.el;version="2.2.0"
|
||||
Bundle-Vendor: %bundleProvider
|
||||
Bundle-ClassPath: .
|
||||
Bundle-Version: 2.2.0
|
||||
Bundle-Name: %bundleName
|
||||
Bundle-Localization: plugin
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-SymbolicName: javax.el
|
||||
DynamicImport-Package: org.apache.el
|
||||
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
|
||||
2SE-1.3
|
||||
|
||||
@ -1,40 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Export-Package: org.apache.jasper;version="8.0.26",org.apache.jasper.c
|
||||
ompiler;version="8.0.26",org.apache.jasper.compiler.tagplugin;version
|
||||
="8.0.26",org.apache.jasper.resources;version="8.0.26",org.apache.jas
|
||||
per.runtime;version="8.0.26",org.apache.jasper.security;version="7.0.
|
||||
19",org.apache.jasper.servlet;version="8.0.26",org.apache.jasper.tagp
|
||||
lugins.jstl;version="8.0.26",org.apache.jasper.tagplugins.jstl.core;v
|
||||
ersion="8.0.26",org.apache.jasper.util;version="8.0.26",org.apache.ja
|
||||
sper.xmlparser;version="8.0.26"
|
||||
Bundle-Vendor: %bundleProvider
|
||||
Bundle-ClassPath: .
|
||||
Bundle-Version: 8.0.26
|
||||
Bundle-Localization: plugin
|
||||
Bundle-Name: %bundleName
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-SymbolicName: org.apache.jasper
|
||||
Import-Package: javax.servlet;version="[2.4.0, 3.0.0]",javax.servlet.h
|
||||
ttp;version="[2.4.0, 3.0.0]",javax.servlet.jsp;version="[2.0.0, 2.2.0
|
||||
]",javax.servlet.jsp.el;version="[2.0.0, 2.2.0]",javax.servlet.jsp.re
|
||||
sources;version="[2.0.0, 2.2.0]",javax.servlet.jsp.tagext;version="[2
|
||||
.0.0, 2.2.0]",javax.servlet.resources;version="[2.4.0, 3.0.0]",javax.
|
||||
xml.parsers,org.apache.commons.el;version="[1.0.0,2.0.0)",org.apache.
|
||||
commons.logging;version="[1.0.0,2.0.0)",org.apache.tools.ant;resoluti
|
||||
on:=optional,org.apache.tools.ant.taskdefs;resolution:=optional,org.a
|
||||
pache.tools.ant.types;resolution:=optional,org.apache.tools.ant.util;
|
||||
resolution:=optional,org.w3c.dom,org.xml.sax,org.xml.sax.ext,org.xml.
|
||||
sax.helpers,org.apache.tomcat;version="8.0.26",org.apache.juli.loggin
|
||||
g;version="8.0.26",javax.el;version="2.2.0",org.eclipse.jdt.internal.
|
||||
compiler,org.eclipse.jdt.internal.compiler.parser,org.eclipse.jdt.int
|
||||
ernal.compiler.parser.diagnose,org.eclipse.jdt.internal.compiler.flow
|
||||
,org.eclipse.jdt.internal.compiler.util,org.eclipse.jdt.internal.comp
|
||||
iler.impl,org.eclipse.jdt.internal.compiler.lookup,org.eclipse.jdt.in
|
||||
ternal.compiler.codegen,org.eclipse.jdt.internal.compiler.batch,org.e
|
||||
clipse.jdt.internal.compiler.classfmt,org.eclipse.jdt.internal.compil
|
||||
er.ast,org.eclipse.jdt.internal.compiler.problem,org.eclipse.jdt.inte
|
||||
rnal.compiler.env,org.eclipse.jdt.internal.core.util,org.eclipse.jdt.
|
||||
core.compiler
|
||||
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
|
||||
2SE-1.3
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Export-Package: org.apache.el;version="7.0.21"
|
||||
Bundle-Vendor: %bundleProvider
|
||||
Bundle-ClassPath: .
|
||||
Bundle-Version: 8.0.26
|
||||
Bundle-Name: %bundleName
|
||||
Bundle-Localization: plugin
|
||||
Bundle-ManifestVersion: 2
|
||||
Import-Package: javax.el;version="2.2"
|
||||
Bundle-SymbolicName: org.apache.el
|
||||
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
|
||||
2SE-1.3
|
||||
|
||||
7
java-9-start-up-parameters.conf
Normal file
7
java-9-start-up-parameters.conf
Normal file
@ -0,0 +1,7 @@
|
||||
# Add the JAVA 9 specific start-up parameters required by Tomcat
|
||||
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.lang=ALL-UNNAMED"
|
||||
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.io=ALL-UNNAMED"
|
||||
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.util=ALL-UNNAMED"
|
||||
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/java.util.concurrent=ALL-UNNAMED"
|
||||
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED"
|
||||
export JDK_JAVA_OPTIONS
|
||||
@ -1,13 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-Vendor: %bundleProvider
|
||||
Bundle-Localization: plugin
|
||||
Bundle-RequiredExecutionEnvironment: CDC-1.0/Foundation-1.0,J2SE-1.3
|
||||
Bundle-Name: %bundleName
|
||||
Bundle-SymbolicName: javax.servlet.jsp
|
||||
Export-Package: javax.servlet.jsp; version=2.2,javax.servlet.jsp.el; v
|
||||
ersion=2.2,javax.servlet.jsp.resources; version=2.2,javax.servlet.jsp
|
||||
.tagext; version=2.2
|
||||
Bundle-Version: 2.2.0.v200806031607
|
||||
Bundle-ManifestVersion: 2
|
||||
Import-Package: javax.servlet; version=3.0,javax.servlet.http; version
|
||||
=3.0,javax.servlet.resources; version=3.0,javax.el;version="2.2.0"
|
||||
74
remove-bnd-annotation.patch
Normal file
74
remove-bnd-annotation.patch
Normal file
@ -0,0 +1,74 @@
|
||||
diff --git a/build.xml b/build.xml
|
||||
index 1a4b255..ea50aeb 100644
|
||||
--- a/build.xml
|
||||
+++ b/build.xml
|
||||
@@ -3296,7 +3296,7 @@ asf.ldap.username=${release.asfusername}
|
||||
|
||||
<target name="download-compile"
|
||||
description="Download components necessary to compile"
|
||||
- depends="setup-bnd">
|
||||
+ >
|
||||
|
||||
<!-- Download Commons Daemon -->
|
||||
<antcall target="downloadgz-2">
|
||||
diff --git a/java/org/apache/el/ExpressionFactoryImpl.java b/java/org/apache/el/ExpressionFactoryImpl.java
|
||||
index 3a6690a..03a2afe 100644
|
||||
--- a/java/org/apache/el/ExpressionFactoryImpl.java
|
||||
+++ b/java/org/apache/el/ExpressionFactoryImpl.java
|
||||
@@ -34,7 +34,7 @@ import org.apache.el.util.MessageFactory;
|
||||
*
|
||||
* @author Jacob Hookom [jacob@hookom.net]
|
||||
*/
|
||||
-@aQute.bnd.annotation.spi.ServiceProvider(value = ExpressionFactory.class)
|
||||
+//@aQute.bnd.annotation.spi.ServiceProvider(value = ExpressionFactory.class)
|
||||
public class ExpressionFactoryImpl extends ExpressionFactory {
|
||||
|
||||
static {
|
||||
diff --git a/java/org/apache/juli/logging/LogFactory.java b/java/org/apache/juli/logging/LogFactory.java
|
||||
index bfc4238..acf989a 100644
|
||||
--- a/java/org/apache/juli/logging/LogFactory.java
|
||||
+++ b/java/org/apache/juli/logging/LogFactory.java
|
||||
@@ -21,7 +21,7 @@ import java.nio.file.FileSystems;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.logging.LogManager;
|
||||
|
||||
-import aQute.bnd.annotation.spi.ServiceConsumer;
|
||||
+//import aQute.bnd.annotation.spi.ServiceConsumer;
|
||||
|
||||
/**
|
||||
* This is a modified LogFactory that uses a simple {@link ServiceLoader} based
|
||||
@@ -63,7 +63,7 @@ import aQute.bnd.annotation.spi.ServiceConsumer;
|
||||
* @author Costin Manolache
|
||||
* @author Richard A. Sitze
|
||||
*/
|
||||
-@ServiceConsumer(value=Log.class)
|
||||
+//@ServiceConsumer(value=Log.class)
|
||||
public class LogFactory {
|
||||
|
||||
private static final LogFactory singleton = new LogFactory();
|
||||
diff --git a/java/org/apache/tomcat/websocket/WsContainerProvider.java b/java/org/apache/tomcat/websocket/WsContainerProvider.java
|
||||
index 4b0577c..e383290 100644
|
||||
--- a/java/org/apache/tomcat/websocket/WsContainerProvider.java
|
||||
+++ b/java/org/apache/tomcat/websocket/WsContainerProvider.java
|
||||
@@ -19,7 +19,7 @@ package org.apache.tomcat.websocket;
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.WebSocketContainer;
|
||||
|
||||
-@aQute.bnd.annotation.spi.ServiceProvider(value = ContainerProvider.class)
|
||||
+//@aQute.bnd.annotation.spi.ServiceProvider(value = ContainerProvider.class)
|
||||
public class WsContainerProvider extends ContainerProvider {
|
||||
|
||||
@Override
|
||||
diff --git a/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java b/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
|
||||
index 00f492e..fe5c34d 100644
|
||||
--- a/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
|
||||
+++ b/java/org/apache/tomcat/websocket/server/DefaultServerEndpointConfigurator.java
|
||||
@@ -26,7 +26,7 @@ import javax.websocket.HandshakeResponse;
|
||||
import javax.websocket.server.HandshakeRequest;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
-@aQute.bnd.annotation.spi.ServiceProvider(value = ServerEndpointConfig.Configurator.class)
|
||||
+//@aQute.bnd.annotation.spi.ServiceProvider(value = ServerEndpointConfig.Configurator.class)
|
||||
public class DefaultServerEndpointConfigurator extends ServerEndpointConfig.Configurator {
|
||||
|
||||
@Override
|
||||
208
rhbz-1857043.patch
Normal file
208
rhbz-1857043.patch
Normal file
@ -0,0 +1,208 @@
|
||||
diff -up a/build.xml.orig b/build.xml
|
||||
--- a/build.xml.orig 2021-07-07 10:53:55.493742841 +0800
|
||||
+++ b/build.xml 2021-07-07 11:09:43.107968515 +0800
|
||||
@@ -1020,7 +1020,7 @@
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.annotations-api"
|
||||
manifest="${tomcat.manifests}/annotations-api.jar.manifest"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Servlet Implementation JAR File -->
|
||||
<jarIt jarfile="${servlet-api.jar}"
|
||||
@@ -1029,41 +1029,41 @@
|
||||
manifest="${tomcat.manifests}/servlet-api.jar.manifest"
|
||||
notice="${tomcat.manifests}/servlet-api.jar.notice"
|
||||
license="${tomcat.manifests}/servlet-api.jar.license"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- EL Implementation JAR File -->
|
||||
<jarIt jarfile="${el-api.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.el-api"
|
||||
manifest="${tomcat.manifests}/el-api.jar.manifest"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- JSP Implementation JAR File -->
|
||||
<jarIt jarfile="${jsp-api.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.jsp-api"
|
||||
manifest="${tomcat.manifests}/jsp-api.jar.manifest"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- WebSocket API JAR File -->
|
||||
<jarIt jarfile="${websocket-api.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.websocket-api"
|
||||
manifest="${tomcat.manifests}/websocket-api.jar.manifest"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- JASPIC API JAR File -->
|
||||
<jarIt jarfile="${jaspic-api.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.jaspic-api"
|
||||
manifest="${tomcat.manifests}/jaspic-api.jar.manifest"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Tomcat-juli JAR File -->
|
||||
<jarIt jarfile="${tomcat-juli.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-juli"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Bootstrap JAR File -->
|
||||
<jarIt jarfile="${bootstrap.jar}"
|
||||
@@ -1075,68 +1075,68 @@
|
||||
<jarIt jarfile="${tomcat-util.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-util"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Tomcat API JAR File -->
|
||||
<jarIt jarfile="${tomcat-api.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-api"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Tomcat Util Scan JAR File -->
|
||||
<jarIt jarfile="${tomcat-util-scan.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-util-scan"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<jarIt jarfile="${tomcat-jni.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-jni"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Protocol handlers - Coyote -->
|
||||
<jarIt jarfile="${tomcat-coyote.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-coyote"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- OpenSSL FFM - Coyote -->
|
||||
<jarIt jarfile="${tomcat-coyote-ffm.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-coyote-ffm"
|
||||
manifest="${tomcat.manifests}/tomcat-coyote-ffm.jar.manifest"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- WebSocket implementation JAR File -->
|
||||
<jarIt jarfile="${tomcat-websocket.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-websocket"
|
||||
meta-inf="${tomcat.manifests}/tomcat-websocket.jar"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Catalina GroupCom/Tribes JAR File -->
|
||||
<jarIt jarfile="${catalina-tribes.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.catalina-tribes"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Catalina Main JAR File -->
|
||||
<jarIt jarfile="${catalina.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.catalina"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Catalina Cluster/HA JAR File -->
|
||||
<jarIt jarfile="${catalina-ha.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.catalina-ha"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Server-Side Includes (SSI) -->
|
||||
<jarIt jarfile="${catalina-ssi.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.catalina-ssi"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Catalina Ant Tasks JAR File -->
|
||||
<jarIt jarfile="${catalina-ant.jar}"
|
||||
@@ -1140,27 +1140,27 @@
|
||||
<jarIt jarfile="${catalina-storeconfig.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.catalina-storeconfig"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Jasper EL Implementation JAR File -->
|
||||
<jarIt jarfile="${jasper-el.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.jasper-el"
|
||||
meta-inf="${tomcat.manifests}/jasper-el.jar"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Jasper Implementation JAR File -->
|
||||
<jarIt jarfile="${jasper.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.jasper"
|
||||
meta-inf="${tomcat.manifests}/jasper.jar"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- Re-packaged Apache Commons DBCP 2-->
|
||||
<jarIt jarfile="${tomcat-dbcp.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-dbcp"
|
||||
- addOSGi="true" />
|
||||
+ addOSGi="false" />
|
||||
|
||||
<!-- i18n JARs -->
|
||||
<jar jarfile="${tomcat.build}/lib/tomcat-i18n-cs.jar"
|
||||
@@ -1620,7 +1620,7 @@
|
||||
filesId="files.tomcat-embed-core"
|
||||
notice="${tomcat.manifests}/servlet-api.jar.notice"
|
||||
license="${tomcat.manifests}/servlet-api.jar.license"
|
||||
- addOSGi="true"
|
||||
+ addOSGi="false"
|
||||
addGraal="true"
|
||||
graalPrefix="org.apache.tomcat.embed/tomcat-embed-core"
|
||||
graalFiles="res/graal/tomcat-embed-core/native-image"
|
||||
@@ -1628,7 +1628,7 @@
|
||||
<jarIt jarfile="${tomcat-embed-el.jar}"
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-embed-el"
|
||||
- addOSGi="true"
|
||||
+ addOSGi="false"
|
||||
addGraal="true"
|
||||
graalPrefix="org.apache.tomcat.embed/tomcat-embed-el"
|
||||
graalFiles="res/graal/tomcat-embed-el/native-image"
|
||||
@@ -1637,7 +1637,7 @@
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-embed-jasper"
|
||||
meta-inf="${tomcat.manifests}/jasper.jar"
|
||||
- addOSGi="true"
|
||||
+ addOSGi="false"
|
||||
addGraal="true"
|
||||
graalPrefix="org.apache.tomcat.embed/tomcat-embed-jasper"
|
||||
graalFiles="res/graal/tomcat-embed-jasper/native-image"
|
||||
@@ -1646,7 +1646,7 @@
|
||||
filesDir="${tomcat.classes}"
|
||||
filesId="files.tomcat-embed-websocket"
|
||||
meta-inf="${tomcat.manifests}/tomcat-websocket.jar"
|
||||
- addOSGi="true"
|
||||
+ addOSGi="false"
|
||||
addGraal="true"
|
||||
graalPrefix="org.apache.tomcat.embed/tomcat-embed-websocket"
|
||||
graalFiles="res/graal/tomcat-embed-websocket/native-image"
|
||||
@ -1,17 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-RequiredExecutionEnvironment: CDC-1.1/Foundation-1.1,J2SE-1.4
|
||||
Bundle-SymbolicName: javax.servlet
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: %bundleName
|
||||
Bundle-Localization: plugin
|
||||
Bundle-Version: 3.0.0
|
||||
Bundle-Vendor: %bundleProvider
|
||||
Export-Package: javax.servlet;version="3.0",
|
||||
javax.servlet;version="2.6",
|
||||
javax.servlet.http;version="3.0",
|
||||
javax.servlet.http;version="2.6",
|
||||
javax.servlet.annotation;version="2.6",
|
||||
javax.servlet.descriptor;version="3.0",
|
||||
javax.servlet.descriptor;version="2.6",
|
||||
javax.servlet.resources;version="3.0",
|
||||
javax.servlet.resources;version="2.6"
|
||||
25
tomcat-9.0-catalina-policy.patch
Normal file
25
tomcat-9.0-catalina-policy.patch
Normal file
@ -0,0 +1,25 @@
|
||||
--- tomcat/conf/catalina.policy.orig 2022-11-04 16:17:41.227506990 +0800
|
||||
+++ tomcat/conf/catalina.policy 2022-11-04 16:21:51.393351415 +0800
|
||||
@@ -56,6 +56,15 @@ grant codeBase "file:${java.home}/lib/ex
|
||||
// permission java.security.AllPermission;
|
||||
//};
|
||||
|
||||
+// ========== RHEL SPECIFIC CODE PERMISSIONS =======================================
|
||||
+
|
||||
+
|
||||
+// Allowing everything in /usr/share/java allows too many unknowns to be permitted
|
||||
+// Specifying the individual jars that tomcat needs to function with the security manager
|
||||
+// is the safest way forward.
|
||||
+grant codeBase "file:/usr/share/java/ecj/ecj.jar" {
|
||||
+ permission java.security.AllPermission;
|
||||
+};
|
||||
|
||||
// ========== CATALINA CODE PERMISSIONS =======================================
|
||||
|
||||
@@ -261,4 +270,4 @@ grant codeBase "file:${catalina.home}/we
|
||||
//
|
||||
// The permissions granted to a specific JAR
|
||||
// grant codeBase "war:file:${catalina.base}/webapps/examples.war*/WEB-INF/lib/foo.jar" {
|
||||
-// };
|
||||
\ No newline at end of file
|
||||
+// };
|
||||
@ -1,6 +1,6 @@
|
||||
--- tomcat/conf/tomcat-users.xml~ 2008-01-28 17:41:06.000000000 -0500
|
||||
+++ tomcat/conf/tomcat-users.xml 2008-03-07 19:40:07.000000000 -0500
|
||||
@@ -23,4 +23,14 @@
|
||||
@@ -53,4 +53,14 @@
|
||||
<user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
|
||||
<user username="role1" password="<must-be-changed>" roles="role1"/>
|
||||
-->
|
||||
|
||||
@ -43,9 +43,8 @@ JAVA_OPTS="-Djavax.sql.DataSource.Factory=org.apache.commons.dbcp.BasicDataSourc
|
||||
# Run tomcat under the Java Security Manager
|
||||
SECURITY_MANAGER="false"
|
||||
|
||||
# Time to wait in seconds, before killing process
|
||||
# TODO(stingray): does nothing, fix.
|
||||
# SHUTDOWN_WAIT="30"
|
||||
# SHUTDOWN_WAIT has been deprecated. To change the shutdown wait time, set
|
||||
# TimeoutStopSec in tomcat.service.
|
||||
|
||||
# If you wish to further customize your tomcat environment,
|
||||
# put your own definitions here
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
@@@TCLOG@@@/catalina.out {
|
||||
# This is an example config only and is disabled by default
|
||||
# If you wish to use it, you'll need to update /etc/tomcat/logging.properties
|
||||
# to prevent catalina*.log from being rotated by Tomcat
|
||||
@@@TCLOG@@@/catalina*.log {
|
||||
copytruncate
|
||||
weekly
|
||||
rotate 52
|
||||
|
||||
@ -15,8 +15,6 @@ EnvironmentFile=-/etc/sysconfig/tomcat
|
||||
ExecStart=/usr/libexec/tomcat/server start
|
||||
SuccessExitStatus=143
|
||||
User=tomcat
|
||||
Group=tomcat
|
||||
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Export-Package: org.apache.tomcat;version="8.0.26"
|
||||
Bundle-Vendor: %bundleProvider
|
||||
Bundle-ClassPath: .
|
||||
Bundle-Version: 8.0.26
|
||||
Bundle-Name: %bundleName
|
||||
Bundle-Localization: plugin
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-SymbolicName: org.apache.tomcat
|
||||
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
|
||||
2SE-1.3
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
--- tomcat/build.xml.orig 2018-08-07 10:32:04.994403913 -0400
|
||||
+++ tomcat/build.xml 2018-08-07 10:32:30.874319588 -0400
|
||||
@@ -2989,6 +2989,9 @@ Read the Building page on the Apache Tom
|
||||
<path id="bndlib.classpath">
|
||||
<fileset file="${bnd.jar}" />
|
||||
<fileset file="${bndlib.jar}" />
|
||||
+ <fileset file="${bndlibg.jar}" />
|
||||
+ <fileset file="${bndannotation.jar}" />
|
||||
+ <fileset file="${slf4j-api.jar}" />
|
||||
</path>
|
||||
--- tomcat/res/bnd/build-defaults.bnd.orig 2024-05-01 11:07:38.804582327 +0300
|
||||
+++ tomcat/res/bnd/build-defaults.bnd 2024-05-01 11:17:08.857295279 +0300
|
||||
@@ -13,7 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
<taskdef resource="aQute/bnd/ant/taskdef.properties" classpathref="bndlib.classpath" />
|
||||
-Bundle-Version: ${version_cleanup;${version}}
|
||||
+Bundle-Version: ${version}
|
||||
Bundle-License: https://www.apache.org/licenses/LICENSE-2.0.txt
|
||||
|
||||
Specification-Title: Apache Tomcat
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
Manifest-Version: 1.0
|
||||
Export-Package: org.apache.juli;version="8.0.26",org.apache.juli.loggi
|
||||
ng;version="8.0.26"
|
||||
Bundle-Vendor: %bundleProvider
|
||||
Bundle-ClassPath: .
|
||||
Bundle-Version: 8.0.26
|
||||
Bundle-Name: %bundleName
|
||||
Bundle-Localization: plugin
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-SymbolicName: org.apache.juli
|
||||
Bundle-RequiredExecutionEnvironment: J2SE-1.4,CDC-1.0/Foundation-1.0,J
|
||||
2SE-1.3
|
||||
|
||||
@ -13,14 +13,12 @@ After=syslog.target network.target
|
||||
[Service]
|
||||
Type=simple
|
||||
EnvironmentFile=/etc/tomcat/tomcat.conf
|
||||
Environment="NAME=%I"
|
||||
EnvironmentFile=-/etc/sysconfig/tomcat@%I
|
||||
Environment="NAME=%i"
|
||||
EnvironmentFile=-/etc/sysconfig/tomcat@%i
|
||||
ExecStart=/usr/libexec/tomcat/server start
|
||||
ExecStop=/usr/libexec/tomcat/server stop
|
||||
SuccessExitStatus=143
|
||||
User=tomcat
|
||||
Group=tomcat
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
|
||||
499
tomcat.spec
499
tomcat.spec
@ -1,129 +1,87 @@
|
||||
%global jspspec 2.3
|
||||
%global major_version 9
|
||||
%global minor_version 0
|
||||
%global micro_version 10
|
||||
%global micro_version 96
|
||||
%global packdname apache-tomcat-%{version}-src
|
||||
%global servletspec 4.0
|
||||
%global elspec 3.0
|
||||
%global tcuid 91
|
||||
%global tcuid 53
|
||||
|
||||
%{!?_mavendepmapfragdir: %global _mavendepmapfragdir /usr/share/maven-metadata}
|
||||
%{?fc24: %global _mavendepmapfragdir /usr/share/maven-metadata}
|
||||
# FHS 2.3 compliant tree structure - http://www.pathname.com/fhs/2.3/
|
||||
%global basedir %{_var}/lib/%{name}
|
||||
%global appdir %{basedir}/webapps
|
||||
%global homedir %{_datadir}/%{name}
|
||||
%global bindir %{homedir}/bin
|
||||
%global confdir %{_sysconfdir}/%{name}
|
||||
%global libdir %{_javadir}/%{name}
|
||||
%global logdir %{_var}/log/%{name}
|
||||
%global cachedir %{_var}/cache/%{name}
|
||||
%global tempdir %{cachedir}/temp
|
||||
%global workdir %{cachedir}/work
|
||||
%global _systemddir /lib/systemd/system
|
||||
|
||||
Name: tomcat
|
||||
Epoch: 1
|
||||
Version: %{major_version}.%{minor_version}.%{micro_version}
|
||||
Release: 32
|
||||
Summary: Implementation of the Java Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies
|
||||
License: ASL 2.0
|
||||
URL: http://tomcat.apache.org/
|
||||
Release: 4
|
||||
Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API
|
||||
|
||||
Source0: https://github.com/apache/tomcat/archive/%{version}.tar.gz
|
||||
License: Apache-2.0
|
||||
URL: http://tomcat.apache.org/
|
||||
Source0: https://archive.apache.org/dist/tomcat/tomcat-%{major_version}/v%{version}/src/%{packdname}.tar.gz
|
||||
Source1: %{name}-%{major_version}.%{minor_version}.conf
|
||||
Source3: %{name}-%{major_version}.%{minor_version}.sysconfig
|
||||
Source4: %{name}-%{major_version}.%{minor_version}.wrapper
|
||||
Source5: %{name}-%{major_version}.%{minor_version}.logrotate
|
||||
Source6: %{name}-%{major_version}.%{minor_version}-digest.script
|
||||
Source7: %{name}-%{major_version}.%{minor_version}-tool-wrapper.script
|
||||
Source8: servlet-api-OSGi-MANIFEST.MF
|
||||
Source9: jsp-api-OSGi-MANIFEST.MF
|
||||
Source11: %{name}-%{major_version}.%{minor_version}.service
|
||||
Source12: el-api-OSGi-MANIFEST.MF
|
||||
Source13: jasper-el-OSGi-MANIFEST.MF
|
||||
Source14: jasper-OSGi-MANIFEST.MF
|
||||
Source15: tomcat-api-OSGi-MANIFEST.MF
|
||||
Source16: tomcat-juli-OSGi-MANIFEST.MF
|
||||
Source20: %{name}-%{major_version}.%{minor_version}-jsvc.service
|
||||
Source21: tomcat-functions
|
||||
Source30: tomcat-preamble
|
||||
Source31: tomcat-server
|
||||
Source32: tomcat-named.service
|
||||
Source33: java-9-start-up-parameters.conf
|
||||
|
||||
Patch0: %{name}-%{major_version}.%{minor_version}-bootstrap-MANIFEST.MF.patch
|
||||
Patch1: %{name}-%{major_version}.%{minor_version}-tomcat-users-webapp.patch
|
||||
Patch2: %{name}-build.patch
|
||||
Patch3: disableJavadocFailOnWarning.patch
|
||||
Patch6001: CVE-2019-0199-1.patch
|
||||
Patch6002: CVE-2019-0199-2.patch
|
||||
Patch6003: CVE-2019-0199-3.patch
|
||||
Patch6004: CVE-2019-0199-4.patch
|
||||
Patch6005: CVE-2019-0199-5.patch
|
||||
Patch6006: CVE-2019-0199-6.patch
|
||||
Patch6007: CVE-2019-0199-7.patch
|
||||
Patch6008: CVE-2019-0199-8.patch
|
||||
Patch6009: CVE-2019-0199-9.patch
|
||||
Patch6010: CVE-2019-0199-10.patch
|
||||
Patch6011: CVE-2019-0199-11.patch
|
||||
Patch6012: CVE-2018-11784.patch
|
||||
Patch6013: CVE-2019-0221.patch
|
||||
Patch6014: CVE-2019-10072-1.patch
|
||||
Patch6015: CVE-2019-10072-2.patch
|
||||
Patch6016: CVE-2019-17563.patch
|
||||
Patch6017: CVE-2019-12418.patch
|
||||
Patch6018: CVE-2020-1938-1.patch
|
||||
Patch6019: CVE-2020-1938-2.patch
|
||||
Patch6020: CVE-2020-1938-3.patch
|
||||
Patch6021: CVE-2020-1938-4.patch
|
||||
Patch6022: CVE-2020-1938-5.patch
|
||||
Patch6023: CVE-2020-1935.patch
|
||||
Patch6024: CVE-2020-9484.patch
|
||||
Patch6025: CVE-2020-11996.patch
|
||||
Patch6026: CVE-2020-13934.patch
|
||||
Patch6027: CVE-2020-13935.patch
|
||||
Patch6028: CVE-2020-13943-1.patch
|
||||
Patch6029: CVE-2020-13943-2.patch
|
||||
Patch6030: CVE-2020-13943-3.patch
|
||||
Patch6031: CVE-2020-13943-4.patch
|
||||
Patch6032: CVE-2020-17527.patch
|
||||
Patch6033: CVE-2021-24122.patch
|
||||
Patch6035: CVE-2021-25122-pre.patch
|
||||
Patch6036: CVE-2021-25122.patch
|
||||
Patch6037: CVE-2021-25329-pre1.patch
|
||||
Patch6038: CVE-2021-25329-pre2.patch
|
||||
Patch6039: CVE-2021-25329-pre3.patch
|
||||
Patch6040: CVE-2021-25329.patch
|
||||
Patch6041: CVE-2021-33037-1.patch
|
||||
Patch6042: CVE-2021-33037-2.patch
|
||||
Patch6043: CVE-2021-33037-3.patch
|
||||
Patch6044: CVE-2021-30640-pre1.patch
|
||||
Patch6045: CVE-2021-30640-pre2.patch
|
||||
Patch6046: CVE-2021-30640-pre3.patch
|
||||
Patch6047: CVE-2021-30640-pre4.patch
|
||||
Patch6048: CVE-2021-30640-pre5.patch
|
||||
Patch6049: CVE-2021-30640-1.patch
|
||||
Patch6050: CVE-2021-30640-2.patch
|
||||
Patch6051: CVE-2021-30640-3.patch
|
||||
Patch6052: CVE-2021-30640-4.patch
|
||||
Patch6053: CVE-2021-30640-5.patch
|
||||
Patch6054: CVE-2021-30640-6.patch
|
||||
Patch6055: CVE-2021-30640-7.patch
|
||||
Patch6056: CVE-2021-30640-8.patch
|
||||
Patch6057: CVE-2021-41079.patch
|
||||
Patch6058: CVE-2021-42340.patch
|
||||
Patch6069: CVE-2022-23181.patch
|
||||
Patch6070: CVE-2022-42252.patch
|
||||
Patch6071: CVE-2023-28708-pre.patch
|
||||
Patch6072: CVE-2023-28708.patch
|
||||
Patch6073: CVE-2023-41080.patch
|
||||
Patch6074: CVE-2023-45648.patch
|
||||
Patch3: %{name}-%{major_version}.%{minor_version}-catalina-policy.patch
|
||||
Patch4: rhbz-1857043.patch
|
||||
# remove bnd dependency which version is too low on rhel8
|
||||
Patch6: remove-bnd-annotation.patch
|
||||
Patch7: build-with-jdk-1.8.patch
|
||||
Patch8: CVE-2024-52318.patch
|
||||
Patch9: CVE-2024-50379-1.patch
|
||||
Patch10: CVE-2024-50379-2.patch
|
||||
Patch11: CVE-2024-54677-pre.patch
|
||||
Patch12: CVE-2024-54677-1.patch
|
||||
Patch13: CVE-2024-54677-2.patch
|
||||
Patch14: CVE-2024-54677-3.patch
|
||||
Patch15: CVE-2024-54677-4.patch
|
||||
Patch16: CVE-2024-54677-5.patch
|
||||
Patch17: CVE-2024-54677-6.patch
|
||||
Patch18: CVE-2024-54677-7.patch
|
||||
Patch19: CVE-2024-54677-8.patch
|
||||
Patch20: CVE-2024-54677-9.patch
|
||||
|
||||
BuildRequires: ecj >= 1:4.6.1 findutils apache-commons-collections apache-commons-daemon
|
||||
BuildRequires: apache-commons-dbcp apache-commons-pool tomcat-taglibs-standard ant
|
||||
BuildRequires: jpackage-utils >= 0:1.7.0 java-devel >= 1:1.8.0 junit javapackages-local
|
||||
BuildRequires: geronimo-saaj aqute-bndlib aqute-bnd systemd-units wsdl4j geronimo-jaxrpc
|
||||
BuildArch: noarch
|
||||
|
||||
Requires: procps jpackage-utils java-headless >= 1:1.8.0 apache-commons-daemon
|
||||
Requires: tomcat-taglibs-standard >= 0:1.1 ecj libtcnative-1-0 >= 1.2.14
|
||||
Requires: apache-commons-dbcp apache-commons-pool apache-commons-collections
|
||||
BuildRequires: ant
|
||||
BuildRequires: ecj
|
||||
BuildRequires: findutils
|
||||
BuildRequires: javapackages-local
|
||||
BuildRequires: aqute-bnd
|
||||
BuildRequires: aqute-bndlib
|
||||
BuildRequires: systemd
|
||||
|
||||
Requires: (java-headless >= 1:1.8 or java-1.8.0-headless or java-11-headless or java-17-headless or java >= 1:1.8)
|
||||
Requires: javapackages-tools
|
||||
Requires: ecj
|
||||
Requires(pre): shadow-utils
|
||||
Requires(post): chkconfig
|
||||
Requires(preun): chkconfig
|
||||
Requires(postun): chkconfig
|
||||
|
||||
Requires(post): systemd-units
|
||||
Requires(preun): systemd-units
|
||||
Requires(postun): systemd-units
|
||||
Requires(post): systemd
|
||||
Requires(preun): systemd
|
||||
Requires(postun): systemd
|
||||
|
||||
Provides: %{name}-log4j = %{epoch}:%{version}-%{release}
|
||||
Provides: servlet = %{servletspec} servlet6 servlet3 el_api = %{elspec} jsp = %{jspspec}
|
||||
@ -143,13 +101,16 @@ Obsoletes: %{name}-jsp-%{jspspec}-api < %{epoch}:%{version}-%{release}
|
||||
Obsoletes: %{name}-webapps < %{epoch}:%{version}-%{release}
|
||||
Obsoletes: %{name}-admin-webapps < %{epoch}:%{version}-%{release}
|
||||
|
||||
BuildArch: noarch
|
||||
|
||||
%description
|
||||
The Apache Tomcat software is developed in an open and participatory environment
|
||||
and released under the Apache License version 2. The Apache Tomcat project is
|
||||
intended to be a collaboration of the best-of-breed developers from around the
|
||||
world. We invite you to participate in this open development project
|
||||
Tomcat is the servlet container that is used in the official Reference
|
||||
Implementation for the Java Servlet and JavaServer Pages technologies.
|
||||
The Java Servlet and JavaServer Pages specifications are developed by
|
||||
Sun under the Java Community Process.
|
||||
|
||||
Tomcat is developed in an open and participatory environment and
|
||||
released under the Apache Software License version 2.0. Tomcat is intended
|
||||
to be a collaboration of the best-of-breed developers from around the world.
|
||||
|
||||
|
||||
%package jsvc
|
||||
Summary: Apache jsvc wrapper for Apache Tomcat as separate service
|
||||
@ -175,124 +136,105 @@ Obsoletes: %{name}-javadoc < %{epoch}:%{version}-%{release}
|
||||
Man pages and other related documents for %{name}.
|
||||
|
||||
%prep
|
||||
%autosetup -p1 -n %{packdname}
|
||||
# remove pre-built binaries and windows files
|
||||
find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name "*.gz" -o \
|
||||
-name "*.jar" -o -name "*.war" -o -name "*.zip" \) -delete
|
||||
|
||||
%autosetup -p1 -n %{name}-%{version}
|
||||
# Remove webservices naming resources as it's generally unused
|
||||
%{__rm} -rf java/org/apache/naming/factory/webservices
|
||||
|
||||
# Configure maven files
|
||||
%mvn_package ":tomcat-el-api" tomcat-el-api
|
||||
%mvn_alias "org.apache.tomcat:tomcat-el-api" "org.eclipse.jetty.orbit:javax.el"
|
||||
%mvn_file org.apache.tomcat:tomcat-jsp-api tomcat/jsp-api
|
||||
%mvn_package ":tomcat-jsp-api" tomcat-jsp-api
|
||||
%mvn_alias "org.apache.tomcat:tomcat-jsp-api" "org.eclipse.jetty.orbit:javax.servlet.jsp"
|
||||
%mvn_package ":tomcat-servlet-api" tomcat-servlet-api
|
||||
|
||||
ln -s $(build-classpath tomcat-taglibs-standard/taglibs-standard-impl) webapps/examples/WEB-INF/lib/jstl.jar
|
||||
ln -s $(build-classpath tomcat-taglibs-standard/taglibs-standard-compat) webapps/examples/WEB-INF/lib/standard.jar
|
||||
|
||||
%build
|
||||
export OPT_JAR_LIST="xalan-j2-serializer"
|
||||
touch HACK
|
||||
%{ant} -Dbase.path="." \
|
||||
-Dbuild.compiler="modern" \
|
||||
-Dcommons-collections.jar="$(build-classpath apache-commons-collections)" \
|
||||
-Dcommons-daemon.jar="$(build-classpath apache-commons-daemon)" \
|
||||
-Dcommons-daemon.native.src.tgz="HACK" \
|
||||
-Djdt.jar="$(build-classpath ecj/ecj)" \
|
||||
-Dtomcat-native.tar.gz="HACK" \
|
||||
-Dtomcat-native.home="." \
|
||||
-Dcommons-daemon.native.win.mgr.exe="HACK" \
|
||||
-Dnsis.exe="HACK" \
|
||||
-Djaxrpc-lib.jar="$(build-classpath jaxrpc)" \
|
||||
-Dwsdl4j-lib.jar="$(build-classpath wsdl4j)" \
|
||||
-Dsaaj-api.jar="$(build-classpath geronimo-saaj)" \
|
||||
-Dbnd.jar="$(build-classpath aqute-bnd/biz.aQute.bnd)" \
|
||||
-Dbndlib.jar="$(build-classpath aqute-bnd/biz.aQute.bndlib)" \
|
||||
-Dbndlibg.jar="$(build-classpath aqute-bnd/aQute.libg)" \
|
||||
-Dbndannotation.jar="$(build-classpath aqute-bnd/biz.aQute.bnd.annotation)" \
|
||||
-Dslf4j-api.jar="$(build-classpath slf4j/slf4j-api)" \
|
||||
-Dno.build.dbcp=true \
|
||||
-Dversion="%{version}" \
|
||||
-Dversion.build="%{micro_version}" \
|
||||
-Djava.7.home=%{java_home} \
|
||||
-Dexecute.validate=false \
|
||||
deploy dist-prepare dist-source javadoc
|
||||
rm output/build/bin/commons-daemon.jar output/build/lib/ecj.jar
|
||||
pushd output/dist/src/webapps/docs/appdev/sample/src
|
||||
mkdir -p ../web/WEB-INF/classes
|
||||
%{javac} -cp ../../../../../../../../output/build/lib/servlet-api.jar -d ../web/WEB-INF/classes mypackage/Hello.java
|
||||
pushd ../web
|
||||
%{jar} cf ../../../../../../../../output/build/webapps/docs/appdev/sample/sample.war *
|
||||
popd
|
||||
popd
|
||||
# we don't care about the tarballs and we're going to replace
|
||||
# tomcat-dbcp.jar with apache-commons-{collections,dbcp,pool}-tomcat5.jar
|
||||
# so just create a dummy file for later removal
|
||||
touch HACK
|
||||
|
||||
mkdir -p META-INF
|
||||
cp -p %{SOURCE8} META-INF/MANIFEST.MF
|
||||
touch META-INF/MANIFEST.MF
|
||||
zip output/build/lib/servlet-api.jar META-INF/MANIFEST.MF
|
||||
cp -p %{SOURCE9} META-INF/MANIFEST.MF
|
||||
touch META-INF/MANIFEST.MF
|
||||
zip output/build/lib/jsp-api.jar META-INF/MANIFEST.MF
|
||||
cp -p %{SOURCE12} META-INF/MANIFEST.MF
|
||||
touch META-INF/MANIFEST.MF
|
||||
zip output/build/lib/el-api.jar META-INF/MANIFEST.MF
|
||||
cp -p %{SOURCE13} META-INF/MANIFEST.MF
|
||||
touch META-INF/MANIFEST.MF
|
||||
zip output/build/lib/jasper-el.jar META-INF/MANIFEST.MF
|
||||
cp -p %{SOURCE14} META-INF/MANIFEST.MF
|
||||
touch META-INF/MANIFEST.MF
|
||||
zip output/build/lib/jasper.jar META-INF/MANIFEST.MF
|
||||
cp -p %{SOURCE15} META-INF/MANIFEST.MF
|
||||
touch META-INF/MANIFEST.MF
|
||||
zip output/build/lib/tomcat-api.jar META-INF/MANIFEST.MF
|
||||
cp -p %{SOURCE16} META-INF/MANIFEST.MF
|
||||
touch META-INF/MANIFEST.MF
|
||||
zip output/build/bin/tomcat-juli.jar META-INF/MANIFEST.MF
|
||||
# who needs a build.properties file anyway
|
||||
%{ant} -Dbase.path="." \
|
||||
-Dbuild.compiler="modern" \
|
||||
-Dcommons-daemon.jar="HACK" \
|
||||
-Dcommons-daemon.native.src.tgz="HACK" \
|
||||
-Djdt.jar="$(build-classpath ecj/ecj)" \
|
||||
-Dtomcat-native.tar.gz="HACK" \
|
||||
-Dtomcat-native.home="." \
|
||||
-Dcommons-daemon.native.win.mgr.exe="HACK" \
|
||||
-Dnsis.exe="HACK" \
|
||||
-Djaxrpc-lib.jar="HACK" \
|
||||
-Dwsdl4j-lib.jar="HACK" \
|
||||
-Dbnd.jar="HACK" \
|
||||
-Dversion="%{version}" \
|
||||
-Dversion.build="%{micro_version}" \
|
||||
deploy
|
||||
|
||||
# remove some jars that we'll replace with symlinks later
|
||||
%{__rm} output/build/lib/ecj.jar
|
||||
# Remove the example webapps per Apache Tomcat Security Considerations
|
||||
# see https://tomcat.apache.org/tomcat-9.0-doc/security-howto.html
|
||||
%{__rm} -rf output/build/webapps/examples
|
||||
|
||||
%install
|
||||
install -d -m 0755 %{buildroot}%{_bindir}
|
||||
install -d -m 0755 %{buildroot}%{_sbindir}
|
||||
install -d -m 0755 %{buildroot}%{_javadocdir}/%{name}
|
||||
install -d -m 0755 %{buildroot}%{_sysconfdir}/init.d
|
||||
install -d -m 0755 %{buildroot}/lib/systemd/system
|
||||
install -d -m 0755 %{buildroot}%{_systemddir}
|
||||
install -d -m 0755 %{buildroot}%{_sysconfdir}/logrotate.d
|
||||
install -d -m 0755 %{buildroot}%{_sysconfdir}/sysconfig
|
||||
install -d -m 0755 %{buildroot}%{_var}/lib/%{name}/webapps
|
||||
install -d -m 0755 %{buildroot}%{_datadir}/%{name}/bin
|
||||
install -d -m 0775 %{buildroot}%{_sysconfdir}/%{name}
|
||||
install -d -m 0775 %{buildroot}%{_sysconfdir}/%{name}/Catalina/localhost
|
||||
install -d -m 0775 %{buildroot}%{_sysconfdir}/%{name}/conf.d
|
||||
install -d -m 0755 %{buildroot}%{appdir}
|
||||
install -d -m 0755 %{buildroot}%{bindir}
|
||||
install -d -m 0775 %{buildroot}%{confdir}
|
||||
install -d -m 0775 %{buildroot}%{confdir}/Catalina/localhost
|
||||
install -d -m 0775 %{buildroot}%{confdir}/conf.d
|
||||
/bin/echo "Place your custom *.conf files here. Shell expansion is supported." > %{buildroot}%{_sysconfdir}/%{name}/conf.d/README
|
||||
install -d -m 0755 %{buildroot}%{_javadir}/%{name}
|
||||
install -d -m 0775 %{buildroot}%{_var}/log/%{name}
|
||||
/bin/touch %{buildroot}%{_var}/log/%{name}/catalina.out
|
||||
install -d -m 0755 %{buildroot}%{libdir}
|
||||
install -d -m 0775 %{buildroot}%{logdir}
|
||||
/bin/touch %{buildroot}%{logdir}/catalina.out
|
||||
install -d -m 0775 %{buildroot}%{_localstatedir}/lib/tomcats
|
||||
install -d -m 0775 %{buildroot}%{_datadir}/%{name}
|
||||
install -d -m 0775 %{buildroot}%{_var}/cache/%{name}/temp
|
||||
install -d -m 0775 %{buildroot}%{_var}/cache/%{name}/work
|
||||
install -d -m 0775 %{buildroot}%{homedir}
|
||||
install -d -m 0775 %{buildroot}%{tempdir}
|
||||
install -d -m 0775 %{buildroot}%{workdir}
|
||||
install -d -m 0755 %{buildroot}%{_unitdir}
|
||||
install -d -m 0755 %{buildroot}%{_libexecdir}/%{name}
|
||||
|
||||
pushd output/build
|
||||
cp -a bin/*.{jar,xml} %{buildroot}%{_datadir}/%{name}/bin
|
||||
cp -a conf/*.{policy,properties,xml,xsd} %{buildroot}%{_sysconfdir}/%{name}
|
||||
cp -a lib/*.jar %{buildroot}%{_javadir}/%{name}
|
||||
cp -a webapps/* %{buildroot}%{_var}/lib/%{name}/webapps
|
||||
cp -a bin/*.{jar,xml} %{buildroot}%{bindir}
|
||||
cp -a conf/*.{policy,properties,xml,xsd} %{buildroot}%{confdir}
|
||||
cp -a lib/*.jar %{buildroot}%{libdir}
|
||||
cp -a webapps/* %{buildroot}%{appdir}
|
||||
popd
|
||||
|
||||
cp -a output/dist/webapps/docs/api/* %{buildroot}%{_javadocdir}/%{name}
|
||||
|
||||
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \
|
||||
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \
|
||||
sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
|
||||
-e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
|
||||
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE1} \
|
||||
> %{buildroot}%{_sysconfdir}/%{name}/%{name}.conf
|
||||
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \
|
||||
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \
|
||||
> %{buildroot}%{confdir}/%{name}.conf
|
||||
sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
|
||||
-e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
|
||||
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE3} \
|
||||
> %{buildroot}%{_sysconfdir}/sysconfig/%{name}
|
||||
install -m 0644 %{SOURCE4} %{buildroot}%{_sbindir}/%{name}
|
||||
install -m 0644 %{SOURCE11} %{buildroot}%{_unitdir}/%{name}.service
|
||||
install -m 0644 %{SOURCE20} %{buildroot}%{_unitdir}/%{name}-jsvc.service
|
||||
sed -e "s|\@\@\@TCLOG\@\@\@|%{_var}/log/%{name}|g" %{SOURCE5} > %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
|
||||
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \
|
||||
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \
|
||||
install -m 0644 %{SOURCE4} \
|
||||
%{buildroot}%{_sbindir}/%{name}
|
||||
install -m 0644 %{SOURCE11} \
|
||||
%{buildroot}%{_unitdir}/%{name}.service
|
||||
install -m 0644 %{SOURCE20} \
|
||||
%{buildroot}%{_unitdir}/%{name}-jsvc.service
|
||||
sed -e "s|\@\@\@TCLOG\@\@\@|%{logdir}|g" %{SOURCE5} \
|
||||
> %{buildroot}%{_sysconfdir}/logrotate.d/%{name}
|
||||
sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
|
||||
-e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
|
||||
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE6} \
|
||||
> %{buildroot}%{_bindir}/%{name}-digest
|
||||
sed -e "s|\@\@\@TCHOME\@\@\@|%{_datadir}/%{name}|g" \
|
||||
-e "s|\@\@\@TCTEMP\@\@\@|%{_var}/cache/%{name}/temp|g" \
|
||||
sed -e "s|\@\@\@TCHOME\@\@\@|%{homedir}|g" \
|
||||
-e "s|\@\@\@TCTEMP\@\@\@|%{tempdir}|g" \
|
||||
-e "s|\@\@\@LIBDIR\@\@\@|%{_libdir}|g" %{SOURCE7} \
|
||||
> %{buildroot}%{_bindir}/%{name}-tool-wrapper
|
||||
|
||||
@ -300,6 +242,7 @@ install -m 0644 %{SOURCE21} %{buildroot}%{_libexecdir}/%{name}/functions
|
||||
install -m 0755 %{SOURCE30} %{buildroot}%{_libexecdir}/%{name}/preamble
|
||||
install -m 0755 %{SOURCE31} %{buildroot}%{_libexecdir}/%{name}/server
|
||||
install -m 0644 %{SOURCE32} %{buildroot}%{_unitdir}/%{name}@.service
|
||||
install -m 0644 %{SOURCE33} %{buildroot}%{confdir}/conf.d/
|
||||
|
||||
sed -i \
|
||||
"s,el-api.jar,%{name}-el-%{elspec}-api.jar,;
|
||||
@ -317,52 +260,29 @@ pushd %{buildroot}%{_javadir}
|
||||
popd
|
||||
|
||||
pushd output/build
|
||||
%{_bindir}/build-jar-repository lib apache-commons-collections apache-commons-dbcp apache-commons-pool ecj 2>&1
|
||||
%{_bindir}/build-jar-repository -p webapps/examples/WEB-INF/lib \
|
||||
tomcat-taglibs-standard/taglibs-standard-impl.jar tomcat-taglibs-standard/taglibs-standard-compat.jar 2>&1
|
||||
%{_bindir}/build-jar-repository lib ecj 2>&1
|
||||
popd
|
||||
|
||||
pushd %{buildroot}%{_javadir}/%{name}
|
||||
pushd %{buildroot}%{libdir}
|
||||
ln -s ../../java/%{name}-jsp-%{jspspec}-api.jar .
|
||||
ln -s ../../java/%{name}-servlet-%{servletspec}-api.jar .
|
||||
ln -s ../../java/%{name}-el-%{elspec}-api.jar .
|
||||
ln -s $(build-classpath apache-commons-collections) commons-collections.jar
|
||||
ln -s $(build-classpath apache-commons-dbcp) commons-dbcp.jar
|
||||
ln -s $(build-classpath apache-commons-pool) commons-pool.jar
|
||||
ln -s $(build-classpath ecj/ecj) jasper-jdt.jar
|
||||
cp -a %{buildroot}%{_datadir}/%{name}/bin/tomcat-juli.jar ./
|
||||
popd
|
||||
|
||||
pushd %{buildroot}%{_datadir}/%{name}
|
||||
ln -s %{_var}/lib/%{name}/webapps webapps
|
||||
ln -s %{_sysconfdir}/%{name} conf
|
||||
ln -s %{_javadir}/%{name} lib
|
||||
ln -s %{_var}/log/%{name} logs
|
||||
ln -s %{_var}/cache/%{name}/temp temp
|
||||
ln -s %{_var}/cache/%{name}/work work
|
||||
popd
|
||||
|
||||
mkdir -p %{buildroot}%{_var}/lib/%{name}/webapps/sample
|
||||
pushd %{buildroot}%{_var}/lib/%{name}/webapps/sample
|
||||
%{jar} xf %{buildroot}%{_var}/lib/%{name}/webapps/docs/appdev/sample/sample.war
|
||||
popd
|
||||
rm %{buildroot}%{_var}/lib/%{name}/webapps/docs/appdev/sample/sample.war
|
||||
|
||||
mkdir -p %{buildroot}%{_var}/lib/%{name}/webapps/examples/META-INF
|
||||
pushd %{buildroot}%{_var}/lib/%{name}/webapps/examples/META-INF
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?>' > context.xml
|
||||
echo '<Context>' >> context.xml
|
||||
echo ' <Resources allowLinking="true" />' >> context.xml
|
||||
echo '</Context>' >> context.xml
|
||||
popd
|
||||
|
||||
pushd %{buildroot}%{_var}/lib/%{name}/webapps/examples/WEB-INF/lib
|
||||
ln -s -f $(build-classpath tomcat-taglibs-standard/taglibs-standard-impl) jstl.jar
|
||||
ln -s -f $(build-classpath tomcat-taglibs-standard/taglibs-standard-compat) standard.jar
|
||||
# symlink to the FHS locations where we've installed things
|
||||
pushd %{buildroot}%{homedir}
|
||||
ln -s %{appdir} webapps
|
||||
ln -s %{confdir} conf
|
||||
ln -s %{libdir} lib
|
||||
ln -s %{logdir} logs
|
||||
ln -s %{tempdir} temp
|
||||
ln -s %{workdir} work
|
||||
popd
|
||||
|
||||
install -d -m 0755 %{buildroot}%{_mavenpomdir}
|
||||
pushd output/dist/src/res/maven
|
||||
pushd res/maven
|
||||
for pom in *.pom; do
|
||||
sed -i 's/@MAVEN.DEPLOY.VERSION@/%{version}/g' $pom
|
||||
done
|
||||
@ -403,17 +323,26 @@ cp -a tomcat-jaspic-api.pom %{buildroot}%{_mavenpomdir}/JPP.%{name}-jaspic-api.p
|
||||
%add_maven_depmap JPP.%{name}-jaspic-api.pom %{name}/jaspic-api.jar
|
||||
|
||||
%pre
|
||||
%{_sbindir}/groupadd -g %{tcuid} -r tomcat 2>/dev/null || :
|
||||
%{_sbindir}/useradd -c "Apache Tomcat" -u %{tcuid} -g tomcat -s /sbin/nologin -r -d %{_datadir}/%{name} tomcat 2>/dev/null || :
|
||||
# add the tomcat user and group
|
||||
getent group tomcat >/dev/null || %{_sbindir}/groupadd -f -g %{tcuid} -r tomcat
|
||||
if ! getent passwd tomcat >/dev/null ; then
|
||||
if ! getent passwd %{tcuid} >/dev/null ; then
|
||||
%{_sbindir}/useradd -r -u %{tcuid} -g tomcat -d %{homedir} -s /sbin/nologin -c "Apache Tomcat" tomcat
|
||||
# Tomcat uses a reserved ID, so there should never be an else
|
||||
fi
|
||||
fi
|
||||
exit 0
|
||||
|
||||
%post
|
||||
# install but don't activate
|
||||
%systemd_post %{name}.service
|
||||
%{_sbindir}/update-alternatives --install %{_javadir}/servlet.jar servlet %{_javadir}/%{name}-servlet-%{servletspec}-api.jar 30000
|
||||
%{_sbindir}/update-alternatives --install %{_javadir}/elspec.jar elspec %{_javadir}/%{name}-el-%{elspec}-api.jar 20300
|
||||
%{_sbindir}/update-alternatives --install %{_javadir}/jsp.jar jsp %{_javadir}/%{name}-jsp-%{jspspec}-api.jar 20200
|
||||
|
||||
%preun
|
||||
rm -rf %{_var}/cache/%{name}/work/* %{_var}/cache/%{name}/temp/*
|
||||
# clean tempdir and workdir on removal or upgrade
|
||||
%{__rm} -rf %{workdir}/* %{tempdir}/*
|
||||
%systemd_preun %{name}.service
|
||||
|
||||
%postun
|
||||
@ -424,23 +353,8 @@ if [ "$1" = "0" ]; then
|
||||
%{_sbindir}/update-alternatives --remove jsp %{_javadir}/%{name}-jsp-%{jspspec}-api.jar
|
||||
fi
|
||||
|
||||
%triggerun -- tomcat < 0:7.0.22-2
|
||||
/usr/bin/systemd-sysv-convert -- save tomcat > /dev/null 2>&1 || :
|
||||
/sbin/chkconfig --del tomcat > /dev/null 2>&1 || :
|
||||
/bin/systemctl try-restart tomcat.service > /dev/null 2>&1 || :
|
||||
|
||||
%files
|
||||
%doc LICENSE
|
||||
%{_javadir}/%{name}-servlet-%{servletspec}*.jar
|
||||
%dir %{_javadir}/%{name}
|
||||
%{_javadir}/%{name}/*.jar
|
||||
%{_javadir}/*.jar
|
||||
%{_datadir}/%{name}/bin/tomcat-juli.jar
|
||||
%{_mavenpomdir}/JPP*%{name}-*.pom
|
||||
%{_datadir}/maven-metadata/*.xml
|
||||
%{_javadir}/%{name}-el-%{elspec}-api.jar
|
||||
%{_javadir}/%{name}/%{name}-el-%{elspec}-api.jar
|
||||
%{_javadir}/%{name}-jsp-%{jspspec}*.jar
|
||||
%defattr(0664,root,tomcat,0755)
|
||||
%doc {LICENSE,NOTICE,RELEASE*}
|
||||
%attr(0755,root,root) %{_bindir}/%{name}-digest
|
||||
@ -454,46 +368,57 @@ fi
|
||||
%attr(0755,root,root) %{_libexecdir}/%{name}/preamble
|
||||
%attr(0755,root,root) %{_libexecdir}/%{name}/server
|
||||
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/sysconfig/%{name}
|
||||
%attr(0755,root,tomcat) %dir %{_var}/lib/%{name}
|
||||
%attr(0755,root,tomcat) %dir %{_sysconfdir}/%{name}
|
||||
%attr(0755,root,tomcat) %dir %{basedir}
|
||||
%attr(0755,root,tomcat) %dir %{confdir}
|
||||
|
||||
%defattr(0664,tomcat,root,0770)
|
||||
%attr(0770,tomcat,root) %dir %{_var}/log/%{name}
|
||||
%attr(0770,tomcat,root) %dir %{logdir}
|
||||
|
||||
%defattr(0664,root,tomcat,0770)
|
||||
%attr(0770,root,tomcat) %dir %{_var}/cache/%{name}
|
||||
%attr(0770,root,tomcat) %dir %{_var}/cache/%{name}/temp
|
||||
%attr(0770,root,tomcat) %dir %{_var}/cache/%{name}/work
|
||||
%attr(0770,root,tomcat) %dir %{cachedir}
|
||||
%attr(0770,root,tomcat) %dir %{tempdir}
|
||||
%attr(0770,root,tomcat) %dir %{workdir}
|
||||
|
||||
%defattr(0644,root,tomcat,0775)
|
||||
%attr(0775,root,tomcat) %dir %{_var}/lib/%{name}/webapps
|
||||
%attr(0775,root,tomcat) %dir %{_sysconfdir}/%{name}/Catalina
|
||||
%attr(0775,root,tomcat) %dir %{_sysconfdir}/%{name}/Catalina/localhost
|
||||
%attr(0755,root,tomcat) %dir %{_sysconfdir}/%{name}/conf.d
|
||||
%{_sysconfdir}/%{name}/conf.d/README
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/*.policy
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/*.properties
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/context.xml
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/server.xml
|
||||
%attr(0640,root,tomcat) %config(noreplace) %{_sysconfdir}/%{name}/tomcat-users.xml
|
||||
%attr(0664,root,tomcat) %{_sysconfdir}/%{name}/tomcat-users.xsd
|
||||
%attr(0664,root,tomcat) %config(noreplace) %{_sysconfdir}/%{name}/jaspic-providers.xml
|
||||
%attr(0664,root,tomcat) %{_sysconfdir}/%{name}/jaspic-providers.xsd
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/web.xml
|
||||
%dir %{_datadir}/%{name}
|
||||
%{_datadir}/%{name}/bin/bootstrap.jar
|
||||
%{_datadir}/%{name}/bin/catalina-tasks.xml
|
||||
%{_datadir}/%{name}/lib
|
||||
%{_datadir}/%{name}/temp
|
||||
%{_datadir}/%{name}/webapps
|
||||
%{_datadir}/%{name}/work
|
||||
%{_datadir}/%{name}/logs
|
||||
%{_datadir}/%{name}/conf
|
||||
%attr(0775,root,tomcat) %dir %{appdir}
|
||||
%attr(0775,root,tomcat) %dir %{confdir}/Catalina
|
||||
%attr(0775,root,tomcat) %dir %{confdir}/Catalina/localhost
|
||||
%attr(0755,root,tomcat) %dir %{confdir}/conf.d
|
||||
%{confdir}/conf.d/README
|
||||
%{confdir}/conf.d/java-9-start-up-parameters.conf
|
||||
%config(noreplace) %{confdir}/%{name}.conf
|
||||
%config(noreplace) %{confdir}/*.policy
|
||||
%config(noreplace) %{confdir}/*.properties
|
||||
%config(noreplace) %{confdir}/context.xml
|
||||
%config(noreplace) %{confdir}/server.xml
|
||||
%attr(0640,root,tomcat) %config(noreplace) %{confdir}/tomcat-users.xml
|
||||
%attr(0664,root,tomcat) %{confdir}/tomcat-users.xsd
|
||||
%attr(0664,root,tomcat) %config(noreplace) %{confdir}/jaspic-providers.xml
|
||||
%attr(0664,root,tomcat) %{confdir}/jaspic-providers.xsd
|
||||
%config(noreplace) %{confdir}/web.xml
|
||||
|
||||
%dir %{homedir}
|
||||
%{bindir}/catalina-tasks.xml
|
||||
%{homedir}/lib
|
||||
%{homedir}/temp
|
||||
%{homedir}/webapps
|
||||
%{homedir}/work
|
||||
%{homedir}/logs
|
||||
%{homedir}/conf
|
||||
|
||||
%defattr(0664,root,tomcat,0755)
|
||||
%{_var}/lib/%{name}/webapps/host-manager
|
||||
%{_var}/lib/%{name}/webapps/manager
|
||||
%{appdir}/host-manager
|
||||
%{appdir}/manager
|
||||
%defattr(0644,tomcat,tomcat,0755)
|
||||
%{_var}/lib/%{name}/webapps/ROOT
|
||||
%{_var}/lib/%{name}/webapps/examples
|
||||
%{_var}/lib/%{name}/webapps/sample
|
||||
%{appdir}/ROOT
|
||||
|
||||
%dir %{libdir}
|
||||
%{libdir}/*.jar
|
||||
%{_javadir}/*.jar
|
||||
%{bindir}/tomcat-juli.jar
|
||||
%{bindir}/bootstrap.jar
|
||||
%{_mavenpomdir}/JPP*%{name}-*.pom
|
||||
%{_datadir}/maven-metadata/*.xml
|
||||
|
||||
%files jsvc
|
||||
%defattr(755,root,root,0755)
|
||||
@ -502,10 +427,26 @@ fi
|
||||
%attr(0660,tomcat,tomcat) %verify(not size md5 mtime) %{_var}/log/%{name}/catalina.out
|
||||
|
||||
%files help
|
||||
%{_var}/lib/%{name}/webapps/docs
|
||||
%{_javadocdir}/%{name}
|
||||
%{appdir}/docs
|
||||
|
||||
%changelog
|
||||
* Wed Dec 18 2024 wangkai <13474090681@163.com> - 1:9.0.96-4
|
||||
- Fix CVE-2024-50379 CVE-2024-54677
|
||||
|
||||
* Tue Nov 26 2024 wangkai <13474090681@163.com> - 1:9.0.96-3
|
||||
- Add requires ecj for fix server error
|
||||
|
||||
* Tue Nov 19 2024 wangkai <13474090681@163.com> - 1:9.0.96-2
|
||||
- Fix CVE-2024-52318
|
||||
|
||||
* Thu Nov 07 2024 chenyaqiang <chengyaqiang@huawei.com> - 1:9.0.96-1
|
||||
- Update to 9.0.96
|
||||
- Fix CVE-2021-43980 CVE-2022-25762 CVE-2023-44487 CVE-2023-46589
|
||||
CVE-2024-23672 CVE-2024-24549 CVE-2024-34750
|
||||
|
||||
* Tue Jan 23 2024 wangkai <13474090681@163.com> - 1:9.0.10-33
|
||||
- Fix CVE-2024-21733,CVE-2023-24998,CVE-2023-28709,CVE-2023-42795
|
||||
|
||||
* Fri Oct 20 2023 wangkai <13474090681@163.com> - 1:9.0.10-32
|
||||
- Fix CVE-2023-45648
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user