1
17 package org.apache.catalina.valves;
18
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.io.Writer;
25 import java.util.Scanner;
26 import java.util.concurrent.atomic.AtomicBoolean;
27
28 import javax.servlet.RequestDispatcher;
29 import javax.servlet.ServletException;
30 import javax.servlet.http.HttpServletResponse;
31
32 import org.apache.catalina.connector.Request;
33 import org.apache.catalina.connector.Response;
34 import org.apache.catalina.util.ErrorPageSupport;
35 import org.apache.catalina.util.IOTools;
36 import org.apache.catalina.util.ServerInfo;
37 import org.apache.catalina.util.TomcatCSS;
38 import org.apache.coyote.ActionCode;
39 import org.apache.tomcat.util.ExceptionUtils;
40 import org.apache.tomcat.util.descriptor.web.ErrorPage;
41 import org.apache.tomcat.util.res.StringManager;
42 import org.apache.tomcat.util.security.Escape;
43
44
58 public class ErrorReportValve extends ValveBase {
59
60 private boolean showReport = true;
61
62 private boolean showServerInfo = true;
63
64 private final ErrorPageSupport errorPageSupport = new ErrorPageSupport();
65
66
67
68
69 public ErrorReportValve() {
70 super(true);
71 }
72
73
74
75
76
88 @Override
89 public void invoke(Request request, Response response) throws IOException, ServletException {
90
91
92 getNext().invoke(request, response);
93
94 if (response.isCommitted()) {
95 if (response.setErrorReported()) {
96
97
98
99 try {
100 response.flushBuffer();
101 } catch (Throwable t) {
102 ExceptionUtils.handleThrowable(t);
103 }
104
105
106 response.getCoyoteResponse().action(ActionCode.CLOSE_NOW,
107 request.getAttribute(RequestDispatcher.ERROR_EXCEPTION));
108 }
109 return;
110 }
111
112 Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
113
114
115
116 if (request.isAsync() && !request.isAsyncCompleting()) {
117 return;
118 }
119
120 if (throwable != null && !response.isError()) {
121
122
123
124
125
126 response.reset();
127 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
128 }
129
130
131
132
133 response.setSuspended(false);
134
135 try {
136 report(request, response, throwable);
137 } catch (Throwable tt) {
138 ExceptionUtils.handleThrowable(tt);
139 }
140 }
141
142
143
144
145
146
154 protected void report(Request request, Response response, Throwable throwable) {
155
156 int statusCode = response.getStatus();
157
158
159
160
161
162 if (statusCode < 400 || response.getContentWritten() > 0 || !response.setErrorReported()) {
163 return;
164 }
165
166
167
168 AtomicBoolean result = new AtomicBoolean(false);
169 response.getCoyoteResponse().action(ActionCode.IS_IO_ALLOWED, result);
170 if (!result.get()) {
171 return;
172 }
173
174 ErrorPage errorPage = null;
175 if (throwable != null) {
176 errorPage = errorPageSupport.find(throwable);
177 }
178 if (errorPage == null) {
179 errorPage = errorPageSupport.find(statusCode);
180 }
181 if (errorPage == null) {
182
183 errorPage = errorPageSupport.find(0);
184 }
185
186
187 if (errorPage != null) {
188 if (sendErrorPage(errorPage.getLocation(), response)) {
189
190
191 return;
192 }
193 }
194
195 String message = Escape.htmlElementContent(response.getMessage());
196 if (message == null) {
197 if (throwable != null) {
198 String exceptionMessage = throwable.getMessage();
199 if (exceptionMessage != null && exceptionMessage.length() > 0) {
200 message = Escape.htmlElementContent((new Scanner(exceptionMessage)).nextLine());
201 }
202 }
203 if (message == null) {
204 message = "";
205 }
206 }
207
208
209
210 String reason = null;
211 String description = null;
212 StringManager smClient = StringManager.getManager(
213 Constants.Package, request.getLocales());
214 response.setLocale(smClient.getLocale());
215 try {
216 reason = smClient.getString("http." + statusCode + ".reason");
217 description = smClient.getString("http." + statusCode + ".desc");
218 } catch (Throwable t) {
219 ExceptionUtils.handleThrowable(t);
220 }
221 if (reason == null || description == null) {
222 if (message.isEmpty()) {
223 return;
224 } else {
225 reason = smClient.getString("errorReportValve.unknownReason");
226 description = smClient.getString("errorReportValve.noDescription");
227 }
228 }
229
230 StringBuilder sb = new StringBuilder();
231
232 sb.append("<!doctype html><html lang=\"");
233 sb.append(smClient.getLocale().getLanguage()).append("\">");
234 sb.append("<head>");
235 sb.append("<title>");
236 sb.append(smClient.getString("errorReportValve.statusHeader",
237 String.valueOf(statusCode), reason));
238 sb.append("</title>");
239 sb.append("<style type=\"text/css\">");
240 sb.append(TomcatCSS.TOMCAT_CSS);
241 sb.append("</style>");
242 sb.append("</head><body>");
243 sb.append("<h1>");
244 sb.append(smClient.getString("errorReportValve.statusHeader",
245 String.valueOf(statusCode), reason)).append("</h1>");
246 if (isShowReport()) {
247 sb.append("<hr class=\"line\" />");
248 sb.append("<p><b>");
249 sb.append(smClient.getString("errorReportValve.type"));
250 sb.append("</b> ");
251 if (throwable != null) {
252 sb.append(smClient.getString("errorReportValve.exceptionReport"));
253 } else {
254 sb.append(smClient.getString("errorReportValve.statusReport"));
255 }
256 sb.append("</p>");
257 if (!message.isEmpty()) {
258 sb.append("<p><b>");
259 sb.append(smClient.getString("errorReportValve.message"));
260 sb.append("</b> ");
261 sb.append(message).append("</p>");
262 }
263 sb.append("<p><b>");
264 sb.append(smClient.getString("errorReportValve.description"));
265 sb.append("</b> ");
266 sb.append(description);
267 sb.append("</p>");
268 if (throwable != null) {
269 String stackTrace = getPartialServletStackTrace(throwable);
270 sb.append("<p><b>");
271 sb.append(smClient.getString("errorReportValve.exception"));
272 sb.append("</b></p><pre>");
273 sb.append(Escape.htmlElementContent(stackTrace));
274 sb.append("</pre>");
275
276 int loops = 0;
277 Throwable rootCause = throwable.getCause();
278 while (rootCause != null && (loops < 10)) {
279 stackTrace = getPartialServletStackTrace(rootCause);
280 sb.append("<p><b>");
281 sb.append(smClient.getString("errorReportValve.rootCause"));
282 sb.append("</b></p><pre>");
283 sb.append(Escape.htmlElementContent(stackTrace));
284 sb.append("</pre>");
285
286 rootCause = rootCause.getCause();
287 loops++;
288 }
289
290 sb.append("<p><b>");
291 sb.append(smClient.getString("errorReportValve.note"));
292 sb.append("</b> ");
293 sb.append(smClient.getString("errorReportValve.rootCauseInLogs"));
294 sb.append("</p>");
295
296 }
297 sb.append("<hr class=\"line\" />");
298 }
299 if (isShowServerInfo()) {
300 sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>");
301 }
302 sb.append("</body></html>");
303
304 try {
305 try {
306 response.setContentType("text/html");
307 response.setCharacterEncoding("utf-8");
308 } catch (Throwable t) {
309 ExceptionUtils.handleThrowable(t);
310 if (container.getLogger().isDebugEnabled()) {
311 container.getLogger().debug("status.setContentType", t);
312 }
313 }
314 Writer writer = response.getReporter();
315 if (writer != null) {
316
317
318 writer.write(sb.toString());
319 response.finishResponse();
320 }
321 } catch (IOException e) {
322
323 } catch (IllegalStateException e) {
324
325 }
326
327 }
328
329
330
336 protected String getPartialServletStackTrace(Throwable t) {
337 StringBuilder trace = new StringBuilder();
338 trace.append(t.toString()).append(System.lineSeparator());
339 StackTraceElement[] elements = t.getStackTrace();
340 int pos = elements.length;
341 for (int i = elements.length - 1; i >= 0; i--) {
342 if ((elements[i].getClassName().startsWith
343 ("org.apache.catalina.core.ApplicationFilterChain"))
344 && (elements[i].getMethodName().equals("internalDoFilter"))) {
345 pos = i;
346 break;
347 }
348 }
349 for (int i = 0; i < pos; i++) {
350 if (!(elements[i].getClassName().startsWith
351 ("org.apache.catalina.core."))) {
352 trace.append('\t').append(elements[i].toString()).append(System.lineSeparator());
353 }
354 }
355 return trace.toString();
356 }
357
358
359 private boolean sendErrorPage(String location, Response response) {
360 File file = new File(location);
361 if (!file.isAbsolute()) {
362 file = new File(getContainer().getCatalinaBase(), location);
363 }
364 if (!file.isFile() || !file.canRead()) {
365 getContainer().getLogger().warn(
366 sm.getString("errorReportValve.errorPageNotFound", location));
367 return false;
368 }
369
370
371
372 response.setContentType("text/html");
373 response.setCharacterEncoding("UTF-8");
374
375 try (OutputStream os = response.getOutputStream();
376 InputStream is = new FileInputStream(file);){
377 IOTools.flow(is, os);
378 } catch (IOException e) {
379 getContainer().getLogger().warn(
380 sm.getString("errorReportValve.errorPageIOException", location), e);
381 return false;
382 }
383
384 return true;
385 }
386
387
388
393 public void setShowReport(boolean showReport) {
394 this.showReport = showReport;
395 }
396
397 public boolean isShowReport() {
398 return showReport;
399 }
400
401
406 public void setShowServerInfo(boolean showServerInfo) {
407 this.showServerInfo = showServerInfo;
408 }
409
410 public boolean isShowServerInfo() {
411 return showServerInfo;
412 }
413
414
415 public boolean setProperty(String name, String value) {
416 if (name.startsWith("errorCode.")) {
417 int code = Integer.parseInt(name.substring(10));
418 ErrorPage ep = new ErrorPage();
419 ep.setErrorCode(code);
420 ep.setLocation(value);
421 errorPageSupport.add(ep);
422 return true;
423 } else if (name.startsWith("exceptionType.")) {
424 String className = name.substring(14);
425 ErrorPage ep = new ErrorPage();
426 ep.setExceptionType(className);
427 ep.setLocation(value);
428 errorPageSupport.add(ep);
429 return true;
430 }
431 return false;
432 }
433
434 public String getProperty(String name) {
435 String result;
436 if (name.startsWith("errorCode.")) {
437 int code = Integer.parseInt(name.substring(10));
438 ErrorPage ep = errorPageSupport.find(code);
439 if (ep == null) {
440 result = null;
441 } else {
442 result = ep.getLocation();
443 }
444 } else if (name.startsWith("exceptionType.")) {
445 String className = name.substring(14);
446 ErrorPage ep = errorPageSupport.find(className);
447 if (ep == null) {
448 result = null;
449 } else {
450 result = ep.getLocation();
451 }
452 } else {
453 result = null;
454 }
455 return result;
456 }
457 }
458