On Wed, Oct 14, 2026 at 3:09 PM <[email protected]> wrote:
>
> This is an automated email from the ASF dual-hosted git repository.
>
> rmaucher pushed a commit to branch main
> in repository https://gitbox.apache.org/repos/asf/tomcat.git
>
> commit 5f6da06c35a9bdfb296e107d3cd689bc2d16b560
> Author: opencode <[email protected]>
> AuthorDate: Wed Oct 14 14:13:53 2026 +0200
>
>     Default filters to async unsupported unless set to true
>
>     FilterDef.asyncSupportedBoolean defaulted to true and any value
>     other than "false"/"no" was parsed as true. The Servlet specification
>     defaults filters to not supporting async processing.

So this looks like a "massive" initial error when this was written,
clearly the default is that async-supported is false. Maybe it was
snuck in to avoid disruption (??).
The initial code was only in ApplicationFilterChain, which did a
"false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported()),
while it should instead check if it has an explicit "true" value.

Since I don't like trouble, I won't backport it.

Rémy

>     The web.xml path masked the incorrect default because ContextConfig
>     normalises a missing async-supported element to "false", but filters
>     registered dynamically through ServletContext.addFilter() that never
>     called Dynamic.setAsyncSupported(true) were treated as async supported,
>     so ApplicationFilterChain applied them to ASYNC and ERROR dispatches
>     contrary to the specification.
>
>     The boolean field could also disagree with the stored string: values
>     such as "yes" or "1" returned true from getAsyncSupportedBoolean() but
>     false from the getAsyncSupported().equals("true") checks used elsewhere,
>     so the same filter was inconsistently treated as async supported and
>     async unsupported.
>
>     asyncSupportedBoolean now defaults to false and the setter derives it
>     with "true".equalsIgnoreCase(), consistent with ServletDef and the
>     Boolean parsing used by the other descriptor setters. The string value
>     is stored unchanged so a storeconfig round-trip preserves the original
>     descriptor value.
>
>     This is a user-visible behaviour change: dynamically registered filters
>     now require an explicit setAsyncSupported(true) to receive async and
>     error dispatches. Annotation-defined filters and the shipped filter
>     definitions are unaffected. Adds tests for the default and the full
>     setter matrix, plus a changelog entry.
> ---
>  .../tomcat/util/descriptor/web/FilterDef.java      |  8 +++--
>  .../tomcat/util/descriptor/web/TestFilterDef.java  | 40 
> +++++++++++++++++++++-
>  webapps/docs/changelog.xml                         | 10 ++++++
>  3 files changed, 54 insertions(+), 4 deletions(-)
>
> diff --git a/java/org/apache/tomcat/util/descriptor/web/FilterDef.java 
> b/java/org/apache/tomcat/util/descriptor/web/FilterDef.java
> index 01e6af030a..4e7f028215 100644
> --- a/java/org/apache/tomcat/util/descriptor/web/FilterDef.java
> +++ b/java/org/apache/tomcat/util/descriptor/web/FilterDef.java
> @@ -247,19 +247,21 @@ public class FilterDef implements Serializable {
>      }
>
>      /**
> -     * Sets the async-supported setting for this filter.
> +     * Sets the async-supported setting for this filter. Only the value
> +     * <code>"true"</code> enables async support; anything else, including
> +     * <code>null</code>, leaves the specification default of no async 
> support.
>       *
>       * @param asyncSupported The async-supported value
>       */
>      public void setAsyncSupported(String asyncSupported) {
>          this.asyncSupported = asyncSupported;
> -        asyncSupportedBoolean = !("false".equalsIgnoreCase(asyncSupported));
> +        asyncSupportedBoolean = "true".equalsIgnoreCase(asyncSupported);
>      }
>
>      /**
>       * The async-supported boolean setting for this filter.
>       */
> -    private boolean asyncSupportedBoolean = true;
> +    private boolean asyncSupportedBoolean = false;
>
>      /**
>       * Returns whether async processing is supported for this filter.
> diff --git a/test/org/apache/tomcat/util/descriptor/web/TestFilterDef.java 
> b/test/org/apache/tomcat/util/descriptor/web/TestFilterDef.java
> index 0f4b6cdecf..810e8de837 100644
> --- a/test/org/apache/tomcat/util/descriptor/web/TestFilterDef.java
> +++ b/test/org/apache/tomcat/util/descriptor/web/TestFilterDef.java
> @@ -42,4 +42,42 @@ public class TestFilterDef {
>                  "test", filterDef.getFilterName());
>      }
>
> -}
> \ No newline at end of file
> +    @Test
> +    public void testAsyncSupportedDefaultsToFalse() {
> +        // The specification default is that a filter does not support async
> +        // processing and every other async-supported default in the 
> container
> +        // is false. This matters in particular for filters registered
> +        // dynamically via ServletContext.addFilter() which do not pass 
> through
> +        // the ContextConfig normalisation of the descriptor value.
> +        FilterDef filterDef = new FilterDef();
> +        Assert.assertNull(filterDef.getAsyncSupported());
> +        Assert.assertFalse(filterDef.getAsyncSupportedBoolean());
> +    }
> +
> +    @Test
> +    public void testSetAsyncSupported() {
> +        FilterDef filterDef = new FilterDef();
> +
> +        filterDef.setAsyncSupported("true");
> +        Assert.assertTrue(filterDef.getAsyncSupportedBoolean());
> +
> +        filterDef.setAsyncSupported("TRUE");
> +        Assert.assertTrue(filterDef.getAsyncSupportedBoolean());
> +
> +        filterDef.setAsyncSupported("false");
> +        Assert.assertFalse(filterDef.getAsyncSupportedBoolean());
> +
> +        // Only "true" enables async support. Anything else, including null
> +        // and invalid values such as "yes", is not supported which matches
> +        // the handling by ServletDef and by Boolean. The string value is
> +        // stored as-is since WebXml.mergeFilter() and the ContextConfig
> +        // normalisation of the descriptor value operate on it.
> +        filterDef.setAsyncSupported("yes");
> +        Assert.assertEquals("yes", filterDef.getAsyncSupported());
> +        Assert.assertFalse(filterDef.getAsyncSupportedBoolean());
> +
> +        filterDef.setAsyncSupported(null);
> +        Assert.assertFalse(filterDef.getAsyncSupportedBoolean());
> +    }
> +
> +}
> diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
> index d402a2858f..664c31e0a1 100644
> --- a/webapps/docs/changelog.xml
> +++ b/webapps/docs/changelog.xml
> @@ -121,6 +121,16 @@
>    </subsection>
>    <subsection name="Catalina">
>      <changelog>
> +      <fix>
> +        Treat a filter as not supporting asynchronous processing unless
> +        async supported is explicitly set to <code>"true"</code>.
> +        Previously, a filter registered via
> +        <code>ServletContext.addFilter()</code> that did not call
> +        <code>setAsyncSupported(true)</code> was incorrectly treated as
> +        async supported, and descriptor values such as <code>"yes"</code>
> +        were treated as <code>true</code> for some uses and
> +        <code>false</code> for others. (remm)
> +      </fix>
>        <fix>
>          Lower the log level to debug when OpenSSL initialization fails in
>          <code>OpenSSLLifecycleListener</code> to avoid stack traces
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: [email protected]
> For additional commands, e-mail: [email protected]
>

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to