You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ds...@apache.org on 2026/06/09 12:52:59 UTC
(tomcat) branch 10.1.x updated (3b43ffc226 -> 41976af724)
This is an automated email from the ASF dual-hosted git repository.
dsoumis pushed a change to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
from 3b43ffc226 Fix issues with concurrent read/write of standard HashMap
new e08caccd72 Add showReport attribute in JsonErrorReportValve.
new cd2b532ed7 Add showReport attribute in ProxyErrorReportValve.
new 41976af724 Add changelog entry for showReport attribute
The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../catalina/valves/JsonErrorReportValve.java | 124 +++++++++++----------
.../catalina/valves/ProxyErrorReportValve.java | 23 ++--
.../catalina/valves/TestJsonErrorReportValve.java | 74 ++++++++++++
.../catalina/valves/TestProxyErrorReportValve.java | 87 +++++++++++++++
webapps/docs/changelog.xml | 7 ++
webapps/docs/config/valve.xml | 21 ++++
6 files changed, 268 insertions(+), 68 deletions(-)
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org
(tomcat) 01/03: Add showReport attribute in JsonErrorReportValve.
Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dsoumis pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit e08caccd72c2025e7aa0ad64b3f74483d279fc6c
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Tue Jun 9 14:08:10 2026 +0300
Add showReport attribute in JsonErrorReportValve.
---
.../catalina/valves/JsonErrorReportValve.java | 124 +++++++++++----------
.../catalina/valves/TestJsonErrorReportValve.java | 74 ++++++++++++
webapps/docs/config/valve.xml | 9 ++
3 files changed, 149 insertions(+), 58 deletions(-)
diff --git a/java/org/apache/catalina/valves/JsonErrorReportValve.java b/java/org/apache/catalina/valves/JsonErrorReportValve.java
index a5b0a58747..cf6622d907 100644
--- a/java/org/apache/catalina/valves/JsonErrorReportValve.java
+++ b/java/org/apache/catalina/valves/JsonErrorReportValve.java
@@ -61,73 +61,81 @@ public class JsonErrorReportValve extends ErrorReportValve {
return;
}
- StringManager smClient = StringManager.getManager(Constants.Package, request.getLocales());
- response.setLocale(smClient.getLocale());
- String type;
- if (throwable != null) {
- type = smClient.getString("errorReportValve.exceptionReport");
- } else {
- type = smClient.getString("errorReportValve.statusReport");
- }
- String message = response.getMessage();
- if (message == null && throwable != null) {
- message = throwable.getMessage();
- }
- if (message == null) {
- message = "";
- }
- String description = smClient.getString("http." + statusCode + ".desc");
- if (description == null) {
- if (message.isEmpty()) {
- return;
+ StringBuilder sb = new StringBuilder();
+ sb.append("{\n");
+ sb.append(" \"status\": ").append(statusCode);
+
+ if (isShowReport()) {
+ StringManager smClient = StringManager.getManager(Constants.Package, request.getLocales());
+ response.setLocale(smClient.getLocale());
+ String type;
+ if (throwable != null) {
+ type = smClient.getString("errorReportValve.exceptionReport");
} else {
- description = smClient.getString("errorReportValve.noDescription");
+ type = smClient.getString("errorReportValve.statusReport");
}
- }
- StringBuilder sb = new StringBuilder();
- sb.append("{\n \"type\": \"").append(JSONFilter.escape(type)).append("\",\n");
- sb.append(" \"status\": ").append(statusCode).append(",\n");
- sb.append(" \"message\": \"").append(JSONFilter.escape(message)).append("\",\n");
- sb.append(" \"description\": \"").append(JSONFilter.escape(description));
-
- if (throwable != null) {
- sb.append("\",\n");
-
- // Stack trace
- sb.append(" \"throwable\": [");
- int loops = 0;
- boolean first = true;
- do {
- if (!first) {
- sb.append(',');
+ String message = response.getMessage();
+ if (message == null && throwable != null) {
+ message = throwable.getMessage();
+ }
+ if (message == null) {
+ message = "";
+ }
+ String description = smClient.getString("http." + statusCode + ".desc");
+ if (description == null) {
+ if (message.isEmpty()) {
+ return;
} else {
- first = false;
+ description = smClient.getString("errorReportValve.noDescription");
}
- sb.append('\"').append(JSONFilter.escape(throwable.toString())).append('\"');
-
- StackTraceElement[] elements = throwable.getStackTrace();
- int pos = elements.length;
- for (int i = elements.length - 1; i >= 0; i--) {
- if ((elements[i].getClassName().startsWith("org.apache.catalina.core.ApplicationFilterChain")) &&
- (elements[i].getMethodName().equals("internalDoFilter"))) {
- pos = i;
- break;
+ }
+ sb.append(",\n");
+ sb.append(" \"type\": \"").append(JSONFilter.escape(type)).append("\",\n");
+ sb.append(" \"message\": \"").append(JSONFilter.escape(message)).append("\",\n");
+ sb.append(" \"description\": \"").append(JSONFilter.escape(description));
+
+ if (throwable != null) {
+ sb.append("\",\n");
+
+ // Stack trace
+ sb.append(" \"throwable\": [");
+ int loops = 0;
+ boolean first = true;
+ do {
+ if (!first) {
+ sb.append(',');
+ } else {
+ first = false;
}
- }
- for (int i = 0; i < pos; i++) {
- if (!(elements[i].getClassName().startsWith("org.apache.catalina.core."))) {
- sb.append(',').append('\"').append(' ').append(JSONFilter.escape(elements[i].toString()))
- .append('\"');
+ sb.append('\"').append(JSONFilter.escape(throwable.toString())).append('\"');
+
+ StackTraceElement[] elements = throwable.getStackTrace();
+ int pos = elements.length;
+ for (int i = elements.length - 1; i >= 0; i--) {
+ if (elements[i].getClassName()
+ .startsWith("org.apache.catalina.core.ApplicationFilterChain") &&
+ elements[i].getMethodName().equals("internalDoFilter")) {
+ pos = i;
+ break;
+ }
+ }
+ for (int i = 0; i < pos; i++) {
+ if (!elements[i].getClassName().startsWith("org.apache.catalina.core.")) {
+ sb.append(',').append('\"').append(' ')
+ .append(JSONFilter.escape(elements[i].toString())).append('\"');
+ }
}
- }
- throwable = throwable.getCause();
- loops++;
- } while (throwable != null && (loops < 10));
- sb.append("]\n}");
+ throwable = throwable.getCause();
+ loops++;
+ } while (throwable != null && loops < 10);
+ sb.append("]\n}");
+ } else {
+ sb.append("\"\n}");
+ }
} else {
- sb.append("\"\n}");
+ sb.append("\n}");
}
try {
diff --git a/test/org/apache/catalina/valves/TestJsonErrorReportValve.java b/test/org/apache/catalina/valves/TestJsonErrorReportValve.java
index 4770a93802..5115c0c5ba 100644
--- a/test/org/apache/catalina/valves/TestJsonErrorReportValve.java
+++ b/test/org/apache/catalina/valves/TestJsonErrorReportValve.java
@@ -34,6 +34,7 @@ import org.junit.Assert;
import org.junit.Test;
import org.apache.catalina.Context;
+import org.apache.catalina.Valve;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.startup.TomcatBaseTest;
@@ -338,6 +339,79 @@ public class TestJsonErrorReportValve extends TomcatBaseTest {
Assert.assertEquals(unicodeMessage, json.get("message"));
}
+ @Test
+ public void testJsonErrorShowReportFalse() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ StandardHost host = (StandardHost) tomcat.getHost();
+ host.setErrorReportValveClass(JSON_VALVE);
+
+ Context ctx = getProgrammaticRootContext();
+
+ Tomcat.addServlet(ctx, "sendError", new SendErrorServlet(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server broke"));
+ ctx.addServletMappingDecoded("/", "sendError");
+
+ tomcat.start();
+
+ for (Valve valve : host.getPipeline().getValves()) {
+ if (valve instanceof JsonErrorReportValve) {
+ ((JsonErrorReportValve) valve).setShowReport(false);
+ break;
+ }
+ }
+
+ ByteChunk res = new ByteChunk();
+ int rc = getUrl("http://localhost:" + getPort(), res, null);
+
+ Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
+
+ String body = res.toString();
+ JSONParser parser = new JSONParser(body);
+ LinkedHashMap<String, Object> json = parser.parseObject();
+
+ Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ((Number) json.get("status")).intValue());
+ Assert.assertNull(json.get("type"));
+ Assert.assertNull(json.get("message"));
+ Assert.assertNull(json.get("description"));
+ }
+
+
+ @Test
+ public void testJsonErrorWithThrowableShowReportFalse() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ StandardHost host = (StandardHost) tomcat.getHost();
+ host.setErrorReportValveClass(JSON_VALVE);
+
+ Context ctx = getProgrammaticRootContext();
+
+ Tomcat.addServlet(ctx, "exception", new ExceptionServlet("Something went wrong"));
+ ctx.addServletMappingDecoded("/", "exception");
+
+ tomcat.start();
+
+ for (Valve valve : host.getPipeline().getValves()) {
+ if (valve instanceof JsonErrorReportValve) {
+ ((JsonErrorReportValve) valve).setShowReport(false);
+ break;
+ }
+ }
+
+ ByteChunk res = new ByteChunk();
+ int rc = getUrl("http://localhost:" + getPort(), res, null);
+
+ Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
+
+ String body = res.toString();
+ JSONParser parser = new JSONParser(body);
+ LinkedHashMap<String, Object> json = parser.parseObject();
+
+ Assert.assertEquals(500, ((Number) json.get("status")).intValue());
+ Assert.assertNull(json.get("throwable"));
+ Assert.assertNull(json.get("message"));
+ Assert.assertNull(json.get("description"));
+ }
+
+
@Test
public void testNoJsonForUnknownStatusWithoutMessage() throws Exception {
Tomcat tomcat = getTomcatInstance();
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index b73a554d06..659f2b4c4d 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -2449,6 +2449,15 @@
<strong>org.apache.catalina.valves.JsonErrorReportValve</strong>.</p>
</attribute>
+ <attribute name="showReport" required="false">
+ <p>Flag to determine if the error report (custom error message,
+ description and/or stack trace) is included in the JSON response
+ when an error occurs. If set to <code>false</code>, then the JSON
+ response will only contain the HTTP status code.
+ Default value: <code>true</code>
+ </p>
+ </attribute>
+
</attributes>
</subsection>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org
(tomcat) 02/03: Add showReport attribute in ProxyErrorReportValve.
Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dsoumis pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit cd2b532ed778d8b541d0f5379755ca0429984537
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Tue Jun 9 14:08:39 2026 +0300
Add showReport attribute in ProxyErrorReportValve.
---
.../catalina/valves/ProxyErrorReportValve.java | 23 +++---
.../catalina/valves/TestProxyErrorReportValve.java | 87 ++++++++++++++++++++++
webapps/docs/config/valve.xml | 12 +++
3 files changed, 112 insertions(+), 10 deletions(-)
diff --git a/java/org/apache/catalina/valves/ProxyErrorReportValve.java b/java/org/apache/catalina/valves/ProxyErrorReportValve.java
index 80883f51a7..a813f51c9b 100644
--- a/java/org/apache/catalina/valves/ProxyErrorReportValve.java
+++ b/java/org/apache/catalina/valves/ProxyErrorReportValve.java
@@ -193,19 +193,22 @@ public class ProxyErrorReportValve extends ErrorReportValve {
reason = smClient.getString("errorReportValve.unknownReason");
description = smClient.getString("errorReportValve.noDescription");
}
- stringBuilder.append("&statusDescription=");
- stringBuilder.append(URLEncoder.encode(description, StandardCharsets.UTF_8));
stringBuilder.append("&statusReason=");
stringBuilder.append(URLEncoder.encode(reason, StandardCharsets.UTF_8));
- String message = response.getMessage();
- if (message != null) {
- stringBuilder.append("&message=");
- stringBuilder.append(URLEncoder.encode(message, StandardCharsets.UTF_8));
- }
- if (throwable != null) {
- stringBuilder.append("&throwable=");
- stringBuilder.append(URLEncoder.encode(throwable.toString(), StandardCharsets.UTF_8));
+ if (isShowReport()) {
+ stringBuilder.append("&statusDescription=");
+ stringBuilder.append(URLEncoder.encode(description, StandardCharsets.UTF_8));
+
+ String message = response.getMessage();
+ if (message != null) {
+ stringBuilder.append("&message=");
+ stringBuilder.append(URLEncoder.encode(message, StandardCharsets.UTF_8));
+ }
+ if (throwable != null) {
+ stringBuilder.append("&throwable=");
+ stringBuilder.append(URLEncoder.encode(throwable.toString(), StandardCharsets.UTF_8));
+ }
}
urlString = stringBuilder.toString();
diff --git a/test/org/apache/catalina/valves/TestProxyErrorReportValve.java b/test/org/apache/catalina/valves/TestProxyErrorReportValve.java
index 36242452a2..458c9109c7 100644
--- a/test/org/apache/catalina/valves/TestProxyErrorReportValve.java
+++ b/test/org/apache/catalina/valves/TestProxyErrorReportValve.java
@@ -17,6 +17,9 @@
package org.apache.catalina.valves;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
@@ -248,6 +251,90 @@ public class TestProxyErrorReportValve extends TomcatBaseTest {
}
+ @Test
+ public void testRedirectShowReportFalse() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ StandardHost host = (StandardHost) tomcat.getHost();
+ host.setErrorReportValveClass(PROXY_VALVE);
+
+ Context ctx = getProgrammaticRootContext();
+
+ Tomcat.addServlet(ctx, "sendError", new SendErrorServlet(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server broke"));
+ ctx.addServletMappingDecoded("/", "sendError");
+
+ Tomcat.addServlet(ctx, "errorPage", new ErrorPageServlet());
+ ctx.addServletMappingDecoded("/error-page", "errorPage");
+
+ tomcat.start();
+
+ ProxyErrorReportValve proxyErrorReportValve = null;
+ for (Valve valve : host.getPipeline().getValves()) {
+ if (valve instanceof ProxyErrorReportValve) {
+ proxyErrorReportValve = (ProxyErrorReportValve) valve;
+ break;
+ }
+ }
+ Assert.assertNotNull(proxyErrorReportValve);
+ proxyErrorReportValve.setUseRedirect(true);
+ proxyErrorReportValve.setShowReport(false);
+ proxyErrorReportValve.setProperty("errorCode." + HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
+ "http://localhost:" + getPort() + "/error-page");
+
+ ByteChunk res = new ByteChunk();
+ Map<String, List<String>> resHead = new HashMap<>();
+ int rc = methodUrl("http://localhost:" + getPort(), res,
+ DEFAULT_CLIENT_TIMEOUT_MS, null, resHead, "GET", false);
+
+ Assert.assertEquals(HttpServletResponse.SC_FOUND, rc);
+
+ List<String> locationHeader = resHead.get("Location");
+ Assert.assertNotNull(locationHeader);
+ String location = locationHeader.get(0);
+
+ Assert.assertTrue(location.contains("statusCode="));
+ Assert.assertTrue(location.contains("statusReason="));
+ Assert.assertFalse(location.contains("message="));
+ Assert.assertFalse(location.contains("throwable="));
+ Assert.assertFalse(location.contains("statusDescription="));
+ }
+
+
+ @Test
+ public void testNoErrorPageShowReportFalse() throws Exception {
+ Tomcat tomcat = getTomcatInstance();
+ StandardHost host = (StandardHost) tomcat.getHost();
+ host.setErrorReportValveClass(PROXY_VALVE);
+
+ Context ctx = getProgrammaticRootContext();
+
+ Tomcat.addServlet(ctx, "sendError", new SendErrorServlet(
+ HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server broke"));
+ ctx.addServletMappingDecoded("/", "sendError");
+
+ tomcat.start();
+
+ ProxyErrorReportValve proxyErrorReportValve = null;
+ for (Valve valve : host.getPipeline().getValves()) {
+ if (valve instanceof ProxyErrorReportValve) {
+ proxyErrorReportValve = (ProxyErrorReportValve) valve;
+ break;
+ }
+ }
+ Assert.assertNotNull(proxyErrorReportValve);
+ proxyErrorReportValve.setShowReport(false);
+
+ ByteChunk res = new ByteChunk();
+ int rc = getUrl("http://localhost:" + getPort(), res, null);
+
+ Assert.assertEquals(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, rc);
+
+ String body = res.toString();
+ Assert.assertNotNull(body);
+ Assert.assertFalse(body.contains("Server broke"));
+ }
+
+
private static final class SendErrorServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml
index 659f2b4c4d..0ece09e1f3 100644
--- a/webapps/docs/config/valve.xml
+++ b/webapps/docs/config/valve.xml
@@ -2500,6 +2500,18 @@
value is <code>false</code>.</p>
</attribute>
+ <attribute name="showReport" required="false">
+ <p>Flag to determine if the error report details (description, custom
+ error message and/or stack trace) are included in the query
+ parameters of the redirect or proxy URL when an error occurs. If
+ set to <code>false</code>, then only the status code and reason
+ phrase are included. When falling back to the default error report
+ valve (no error page configured), this flag also controls whether
+ the HTML error report includes these details.
+ Default value: <code>true</code>
+ </p>
+ </attribute>
+
<attribute name="useRedirect" required="false">
<p>If <code>true</code>, the valve will redirect the client to the URL
that displays the error and the redirect will include full details of
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org
(tomcat) 03/03: Add changelog entry for showReport attribute
Posted by ds...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
dsoumis pushed a commit to branch 10.1.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 41976af7242e15db3c6714fa8fe08886d4cee604
Author: Dimitris Soumis <ji...@gmail.com>
AuthorDate: Tue Jun 9 14:52:39 2026 +0300
Add changelog entry for showReport attribute
---
webapps/docs/changelog.xml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 88ddde0cdf..15580cafcd 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -253,6 +253,13 @@
<code>ProxyErrorReportValve</code> from <code>true</code> to
<code>false</code>. (markt)
</fix>
+ <add>
+ Add support for the <code>showReport</code> attribute in
+ <code>JsonErrorReportValve</code> and
+ <code>ProxyErrorReportValve</code>. When set to <code>false</code>,
+ detailed error information (message, description, stack trace) is
+ suppressed from error responses. (dsoumis)
+ </add>
</changelog>
</subsection>
<subsection name="Coyote">
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org