This is an automated email from the ASF dual-hosted git repository. lihan pushed a commit to branch 8.5.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/8.5.x by this push: new be7af10c4d Back-porting JsonErrorReportValve be7af10c4d is described below commit be7af10c4d5f2aad54a5d59add49a8d2ad09db2c Author: lihan <li...@apache.org> AuthorDate: Fri Sep 2 16:41:28 2022 +0800 Back-porting JsonErrorReportValve --- .../apache/catalina/valves/ErrorReportValve.java | 35 +++++---- .../catalina/valves/JsonErrorReportValve.java | 89 ++++++++++++++++++++++ webapps/docs/changelog.xml | 5 ++ webapps/docs/config/valve.xml | 33 ++++++++ 4 files changed, 144 insertions(+), 18 deletions(-) diff --git a/java/org/apache/catalina/valves/ErrorReportValve.java b/java/org/apache/catalina/valves/ErrorReportValve.java index ae02d62499..d59799b066 100644 --- a/java/org/apache/catalina/valves/ErrorReportValve.java +++ b/java/org/apache/catalina/valves/ErrorReportValve.java @@ -141,6 +141,23 @@ public class ErrorReportValve extends ValveBase { response.setSuspended(false); try { + int statusCode = response.getStatus(); + + // Do nothing on a 1xx, 2xx and 3xx status + // Do nothing if anything has been written already + // Do nothing if the response hasn't been explicitly marked as in error + // and that error has not been reported. + if (statusCode < 400 || response.getContentWritten() > 0 || !response.setErrorReported()) { + return; + } + + // If an error has occurred that prevents further I/O, don't waste time + // producing an error report that will never be read + AtomicBoolean result = new AtomicBoolean(false); + response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result); + if (!result.get()) { + return; + } report(request, response, throwable); } catch (Throwable tt) { ExceptionUtils.handleThrowable(tt); @@ -160,25 +177,7 @@ public class ErrorReportValve extends ValveBase { * a root cause exception */ protected void report(Request request, Response response, Throwable throwable) { - int statusCode = response.getStatus(); - - // Do nothing on a 1xx, 2xx and 3xx status - // Do nothing if anything has been written already - // Do nothing if the response hasn't been explicitly marked as in error - // and that error has not been reported. - if (statusCode < 400 || response.getContentWritten() > 0 || !response.setErrorReported()) { - return; - } - - // If an error has occurred that prevents further I/O, don't waste time - // producing an error report that will never be read - AtomicBoolean result = new AtomicBoolean(false); - response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result); - if (!result.get()) { - return; - } - ErrorPage errorPage = null; if (throwable != null) { errorPage = errorPageSupport.find(throwable); diff --git a/java/org/apache/catalina/valves/JsonErrorReportValve.java b/java/org/apache/catalina/valves/JsonErrorReportValve.java new file mode 100644 index 0000000000..19afec6d84 --- /dev/null +++ b/java/org/apache/catalina/valves/JsonErrorReportValve.java @@ -0,0 +1,89 @@ +/* + * 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.valves; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.tomcat.util.ExceptionUtils; +import org.apache.tomcat.util.res.StringManager; + +/** + * <p>Implementation of a Valve that outputs error jsons.</p> + * + * <p>This Valve should be attached at the Host level, although it will work + * if attached to a Context.</p> + * + */ +public class JsonErrorReportValve extends ErrorReportValve { + + public JsonErrorReportValve() { + super(); + } + + @Override + protected void report(Request request, Response response, Throwable throwable) { + int statusCode = response.getStatus(); + StringManager smClient = StringManager.getManager(Constants.Package, request.getLocales()); + response.setLocale(smClient.getLocale()); + String type = null; + 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(); + } + String description = null; + description = smClient.getString("http." + statusCode + ".desc"); + if (description == null) { + if (message == null || message.isEmpty()) { + return; + } else { + description = smClient.getString("errorReportValve.noDescription"); + } + } + String jsonReport = "{\n" + + " \"type\": \"" + type + "\",\n" + + " \"message\": \"" + message + "\",\n" + + " \"description\": \"" + description + "\"\n" + + "}"; + try { + try { + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + } catch (Throwable t) { + ExceptionUtils.handleThrowable(t); + if (container.getLogger().isDebugEnabled()) { + container.getLogger().debug("status.setContentType", t); + } + } + Writer writer = response.getReporter(); + if (writer != null) { + writer.write(jsonReport); + response.finishResponse(); + return; + } + } catch (IOException | IllegalStateException e) { + // Ignore + } + } +} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index ae183af042..10132e276d 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -142,6 +142,11 @@ executor to protect against failure of the logging thread. Based on pull request <pr>545</pr> by Piotr P. Karwasz. (markt) </fix> + <add> + Add <code>JsonErrorReportValve</code> that extends the + <code>ErrorReportValve</code> that returns response as JSON instead of + HTML. Back-porting kfujino's implementation. (lihan) + </add> </changelog> </subsection> <subsection name="Coyote"> diff --git a/webapps/docs/config/valve.xml b/webapps/docs/config/valve.xml index 6b6f20a259..cc54106592 100644 --- a/webapps/docs/config/valve.xml +++ b/webapps/docs/config/valve.xml @@ -2174,6 +2174,39 @@ </section> +<section name="Json Error Report Valve"> + + <subsection name="Introduction"> + + <p>The <strong>Json Error Report Valve</strong> is a simple error handler + for HTTP status codes that will return Json error messages.</p> + + <p>By specifying this class in <code>errorReportValveClass</code> attribute + in <code>HOST</code>, it will be used instead of + <code>ErrorReportValve</code> and will return JSON response instead of HTML. + </p> + + </subsection> + + <subsection name="Attributes"> + + <p>The <strong>Json Error Report Valve</strong> supports the following + configuration attributes:</p> + + <attributes> + + <attribute name="className" required="true"> + <p>Java class name of the implementation to use. This MUST be set to + <strong>org.apache.catalina.valves.JsonErrorReportValve</strong>.</p> + </attribute> + + </attributes> + + </subsection> + +</section> + + <section name="Crawler Session Manager Valve"> <subsection name="Introduction"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org