From 44d05d75d696ca10ce251e4e370511e38f20ae75 Mon Sep 17 00:00:00 2001
From: Mark Thomas <markt@apache.org>
Date: Thu, 5 Oct 2023 20:52:46 +0100
Subject: [PATCH] Improve handling of failures during recycle() methods

Origin: https://github.com/apache/tomcat/commit/44d05d75d696ca10ce251e4e370511e38f20ae75

---
 .../catalina/connector/LocalStrings.properties   |  1 +
 java/org/apache/catalina/connector/Request.java  |  7 ++++---
 .../catalina/core/ApplicationHttpRequest.java    | 16 ++++++++++++----
 .../apache/catalina/core/LocalStrings.properties |  1 +
 .../catalina/core/LocalStrings_es.properties     |  2 ++
 .../catalina/core/LocalStrings_fr.properties     |  1 +
 .../catalina/core/LocalStrings_ja.properties     |  1 +
 .../org/apache/tomcat/util/buf/B2CConverter.java | 11 ++++++++++-
 .../org/apache/tomcat/util/buf/C2BConverter.java | 15 ++++++++++++++-
 .../tomcat/util/buf/LocalStrings.properties      |  3 +++
 10 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/java/org/apache/catalina/connector/LocalStrings.properties b/java/org/apache/catalina/connector/LocalStrings.properties
index 86c6487..596805b 100644
--- a/java/org/apache/catalina/connector/LocalStrings.properties
+++ b/java/org/apache/catalina/connector/LocalStrings.properties
@@ -47,6 +47,7 @@ coyoteRequest.setAttribute.namenull=Cannot call setAttribute with a null name
 coyoteRequest.attributeEvent=Exception thrown by attributes event listener
 coyoteRequest.parseParameters=Exception thrown whilst processing POSTed parameters
 coyoteRequest.postTooLarge=Parameters were not parsed because the size of the posted data was too big. Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs.
+coyoteRequest.deletePartFailed=Failed to deleted temporary file used for part [{0}]
 coyoteRequest.chunkedPostTooLarge=Parameters were not parsed because the size of the posted data was too big. Because this request was a chunked request, it could not be processed further. Use the maxPostSize attribute of the connector to resolve this if the application should accept large POSTs.
 coyoteRequest.alreadyAuthenticated=This request has already been authenticated
 coyoteRequest.authenticate.ise=Cannot call authenticate() after the response has been committed
diff --git a/java/org/apache/catalina/connector/Request.java b/java/org/apache/catalina/connector/Request.java
index 889d5e7..de53769 100644
--- a/java/org/apache/catalina/connector/Request.java
+++ b/java/org/apache/catalina/connector/Request.java
@@ -465,8 +465,9 @@ public class Request implements HttpServletRequest {
             for (Part part: parts) {
                 try {
                     part.delete();
-                } catch (IOException ignored) {
-                    // ApplicationPart.delete() never throws an IOEx
+		} catch (Throwable t) {
+                    ExceptionUtils.handleThrowable(t);
+                    log.warn(sm.getString("coyoteRequest.deletePartFailed", part.getName()), t);
                 }
             }
             parts = null;
@@ -518,8 +519,8 @@ public class Request implements HttpServletRequest {
         asyncSupported = null;
         if (asyncContext!=null) {
             asyncContext.recycle();
+	    asyncContext = null;
         }
-        asyncContext = null;
     }


diff --git a/java/org/apache/catalina/core/ApplicationHttpRequest.java b/java/org/apache/catalina/core/ApplicationHttpRequest.java
index fc3a1d6..0b5b4f5 100644
--- a/java/org/apache/catalina/core/ApplicationHttpRequest.java
+++ b/java/org/apache/catalina/core/ApplicationHttpRequest.java
@@ -29,6 +29,8 @@ import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Arrays;
+import java.util.HashMap;

 import javax.servlet.DispatcherType;
 import javax.servlet.RequestDispatcher;
@@ -48,9 +50,12 @@ import org.apache.catalina.Session;
 import org.apache.catalina.connector.RequestFacade;
 import org.apache.catalina.util.ParameterMap;
 import org.apache.catalina.util.RequestUtil;
+import org.apache.catalina.util.URLEncoder;
+import org.apache.tomcat.util.ExceptionUtils;
 import org.apache.tomcat.util.buf.B2CConverter;
 import org.apache.tomcat.util.buf.MessageBytes;
 import org.apache.tomcat.util.http.Parameters;
+import org.apache.tomcat.util.res.StringManager;


 /**
@@ -70,9 +75,7 @@ import org.apache.tomcat.util.http.Parameters;
  */
 class ApplicationHttpRequest extends HttpServletRequestWrapper {

-
-    // ------------------------------------------------------- Static Variables
-
+    private static final StringManager sm = StringManager.getManager(ApplicationHttpRequest.class);

     /**
      * The set of attribute names that are special for request dispatchers.
@@ -626,7 +629,12 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper {
      */
     public void recycle() {
         if (session != null) {
-            session.endAccess();
+            try {
+                session.endAccess();
+            } catch (Throwable t) {
+                ExceptionUtils.handleThrowable(t);
+                context.getLogger().warn(sm.getString("applicationHttpRequest.sessionEndAccessFail"), t);
+            }
         }
     }

diff --git a/java/org/apache/catalina/core/LocalStrings.properties b/java/org/apache/catalina/core/LocalStrings.properties
index c5b55b1..0e17e3c 100644
--- a/java/org/apache/catalina/core/LocalStrings.properties
+++ b/java/org/apache/catalina/core/LocalStrings.properties
@@ -55,6 +55,7 @@ applicationFilterConfig.release=Failed to destroy the filter named [{0}] of type
 applicationFilterRegistration.nullInitParam=Unable to set initialisation parameter for filter due to null name and/or value. Name [{0}], Value [{1}]
 applicationFilterRegistration.nullInitParams=Unable to set initialisation parameters for filter due to null name and/or value. Name [{0}], Value [{1}]

+applicationHttpRequest.sessionEndAccessFail=Exception triggered ending access to session while recycling request
 applicationPushBuilder.methodInvalid=The HTTP method for a push request must be both cacheable and safe but [{0}] is not
 applicationPushBuilder.methodNotToken=HTTP methods must be tokens but [{0}] contains a non-token character
 applicationPushBuilder.noCoyoteRequest=Unable to find the underlying Coyote request object (which is required to create a push request) from the request of type [{0}]
diff --git a/java/org/apache/catalina/core/LocalStrings_es.properties b/java/org/apache/catalina/core/LocalStrings_es.properties
index f138d17..e6a9ab2 100644
--- a/java/org/apache/catalina/core/LocalStrings_es.properties
+++ b/java/org/apache/catalina/core/LocalStrings_es.properties
@@ -43,6 +43,8 @@ applicationFilterConfig.jmxUnregister = Se ha completado el desregistro JMX para
 applicationFilterConfig.jmxUnregisterFail = Ha fallado el desregistro JMX para el filtro del tipo [{0}] y nombre [{1}]
 applicationFilterRegistration.nullInitParam = No puedo poner el par\u00E1metro de inicializaci\u00F3n para el filtro debido a un nombre nulo y/o valor. Nombre [{0}], Valor [{1}]
 applicationFilterRegistration.nullInitParams = No puedo poner los par\u00E1metros de inicializaci\u00F3n para el filtro debido a un nombre nulo y/o valor. Nombre [{0}], Valor [{1}]
+applicationHttpRequest.sessionEndAccessFail=Excepción disparada acabando acceso a sesión mientras se reciclaba el requerimiento
+
 applicationServletRegistration.setServletSecurity.iae = Se ha especificado restricci\u00F3n Null para el servlet [{0}] desplegado en el contexto con el nombre [{1}]
 applicationServletRegistration.setServletSecurity.ise = No se pueden a\u00F1adir restricciones de seguridad al servlet [{0}] desplegado en el contexto con el nombre [{1}] ya que el contexto ya ha sido inicializado.
 aprListener.aprInit = La biblioteca nativa de Apache Tomcat basada en ARP que permite un rendimiento \u00F3ptimo en entornos de desarrollo no ha sido hallada en java.library.path: [{0}]
diff --git a/java/org/apache/catalina/core/LocalStrings_fr.properties b/java/org/apache/catalina/core/LocalStrings_fr.properties
index dfc1cf7..91ead47 100644
--- a/java/org/apache/catalina/core/LocalStrings_fr.properties
+++ b/java/org/apache/catalina/core/LocalStrings_fr.properties
@@ -59,6 +59,7 @@ standardContext.startFailed=Erreur de d\u00e9marrage du contexte [{0}] suite aux
 standardContext.startingContext=Exception lors du d\u00e9marrage du contexte [{0}]
 standardContext.stoppingContext=Exception \u00e0 l''arr\u00eat du Context [{0}]
 standardContext.resourcesStart=Erreur lors du d\u00e9marrage des ressources statiques
+applicationHttpRequest.sessionEndAccessFail=Exception lancée durant l'arrêt de l'accès à la session durant le recyclage de la requête
 standardContext.urlPattern.patternWarning=ATTENTION: Le mod\u00e8le (pattern) URL [{0}] doit commencer par un ''/'' dans l''API Servlet 2.4
 standardEngine.noHost=Aucune h\u00f4te (host) ne correspond au nom de serveur [{0}]
 standardEngine.notHost=Le fils d''un moteur (child of an Engine) doit \u00eatre un h\u00f4te
diff --git a/java/org/apache/catalina/core/LocalStrings_ja.properties b/java/org/apache/catalina/core/LocalStrings_ja.properties
index d34d598..ae85dd4 100644
--- a/java/org/apache/catalina/core/LocalStrings_ja.properties
+++ b/java/org/apache/catalina/core/LocalStrings_ja.properties
@@ -66,6 +66,7 @@ standardEngine.notParent=\u30a8\u30f3\u30b8\u30f3\u306f\u89aa\u306e\u30b3\u30f3\
 standardHost.clientAbort=\u30ea\u30e2\u30fc\u30c8\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u304c\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u4e2d\u6b62\u3057\u307e\u3057\u305f, IOException: [{0}]
 standardHost.invalidErrorReportValveClass=\u6307\u5b9a\u3055\u308c\u305f\u30a8\u30e9\u30fc\u30ea\u30dd\u30fc\u30c8\u30d0\u30eb\u30d6\u30af\u30e9\u30b9\u3092\u30ed\u30fc\u30c9\u3067\u304d\u307e\u305b\u3093: [{0}]
 standardHost.noContext=\u3053\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u51e6\u7406\u3059\u308b\u305f\u3081\u306b\u8a2d\u5b9a\u3055\u308c\u305f\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u304c\u3042\u308a\u307e\u305b\u3093
+applicationHttpRequest.sessionEndAccessFail=リクエストの再利用中に行ったセッションへのアクセス終了処理で例外が送出されました。
 standardHost.notContext=\u30db\u30b9\u30c8\u306e\u5b50\u4f9b\u306f\u30b3\u30f3\u30c6\u30ad\u30b9\u30c8\u3067\u306a\u3051\u308c\u3070\u3044\u3051\u307e\u305b\u3093
 standardHost.nullName=\u30db\u30b9\u30c8\u540d\u304c\u5fc5\u8981\u3067\u3059
 standardService.start.name=\u30b5\u30fc\u30d3\u30b9 [{0}] \u3092\u8d77\u52d5\u3057\u307e\u3059
diff --git a/java/org/apache/tomcat/util/buf/B2CConverter.java b/java/org/apache/tomcat/util/buf/B2CConverter.java
index f046ad7..1e3e1f4 100644
--- a/java/org/apache/tomcat/util/buf/B2CConverter.java
+++ b/java/org/apache/tomcat/util/buf/B2CConverter.java
@@ -27,6 +27,9 @@ import java.nio.charset.CodingErrorAction;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Locale;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
 import java.util.Map;

 import org.apache.tomcat.util.res.StringManager;
@@ -35,6 +38,7 @@ import org.apache.tomcat.util.res.StringManager;
  * NIO based character decoder.
  */
 public class B2CConverter {
+    private static final Log log = LogFactory.getLog(B2CConverter.class);

     private static final StringManager sm = StringManager.getManager(B2CConverter.class);

@@ -120,7 +124,12 @@ public class B2CConverter {
      * Reset the decoder state.
      */
     public void recycle() {
-        decoder.reset();
+        try {
+            decoder.reset();
+        } catch (Throwable t) {
+            ExceptionUtils.handleThrowable(t);
+            log.warn(sm.getString("b2cConverter.decoderResetFail", decoder.charset()), t);
+        }
         leftovers.position(0);
     }

diff --git a/java/org/apache/tomcat/util/buf/C2BConverter.java b/java/org/apache/tomcat/util/buf/C2BConverter.java
index e5062de..f3b4dd7 100644
--- a/java/org/apache/tomcat/util/buf/C2BConverter.java
+++ b/java/org/apache/tomcat/util/buf/C2BConverter.java
@@ -24,11 +24,19 @@ import java.nio.charset.CharsetEncoder;
 import java.nio.charset.CoderResult;
 import java.nio.charset.CodingErrorAction;

+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.ExceptionUtils;
+import org.apache.tomcat.util.res.StringManager;
+
 /**
  * NIO based character encoder.
  */
 public final class C2BConverter {

+    private static final Log log = LogFactory.getLog(C2BConverter.class);
+    private static final StringManager sm = StringManager.getManager(C2BConverter.class);
+
     private final CharsetEncoder encoder;
     private ByteBuffer bb = null;
     private CharBuffer cb = null;
@@ -50,7 +58,12 @@ public final class C2BConverter {
      * Reset the encoder state.
      */
     public void recycle() {
-        encoder.reset();
+        try {
+            encoder.reset();
+        } catch (Throwable t) {
+            ExceptionUtils.handleThrowable(t);
+            log.warn(sm.getString("c2bConverter.decoderResetFail", encoder.charset()), t);
+        }
         leftovers.position(0);
     }

diff --git a/java/org/apache/tomcat/util/buf/LocalStrings.properties b/java/org/apache/tomcat/util/buf/LocalStrings.properties
index c8a8d3b..574f6c2 100644
--- a/java/org/apache/tomcat/util/buf/LocalStrings.properties
+++ b/java/org/apache/tomcat/util/buf/LocalStrings.properties
@@ -13,9 +13,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.

+b2cConverter.decoderResetFail=Failed to reset instance of decoder for character set [{0}]
 b2cConverter.unknownEncoding=The character encoding [{0}] is not supported
 c2bConverter.recycleFailed=Failed to recycle the C2B Converter. Creating new BufferedWriter, WriteConvertor and IntermediateOutputStream.

+c2bConverter.encoderResetFail=Failed to reset instance of encoder for character set [{0}]
+
 hexUtils.fromHex.oddDigits=The input must consist of an even number of hex digits
 hexUtils.fromHex.nonHex=The input must consist only of hex digits

--
2.33.0
