package io.vertx.ext.web.handler.impl;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.MIMEHeader;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.ErrorHandler;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
public class ErrorHandlerImpl implements ErrorHandler {
private final Logger log = LoggerFactory.getLogger(ErrorHandlerImpl.class);
private final boolean displayExceptionDetails;
private final String errorTemplate;
public ErrorHandlerImpl(Vertx vertx, String errorTemplateName, boolean displayExceptionDetails) {
Objects.requireNonNull(errorTemplateName);
this.errorTemplate = vertx.fileSystem()
.readFileBlocking(errorTemplateName)
.toString(StandardCharsets.UTF_8);
this.displayExceptionDetails = displayExceptionDetails;
}
@Override
public void handle(RoutingContext context) {
HttpServerResponse response = context.response();
Throwable failure = context.failure();
if (response.headWritten()) {
log.error("Unexpected error on route", failure);
try {
response.close();
} catch (RuntimeException e) {
}
return;
}
int errorCode = context.statusCode();
if (errorCode == -1) {
errorCode = 500;
}
response.setStatusCode(errorCode);
String errorMessage = response.getStatusMessage();
if (displayExceptionDetails) {
String exceptionMessage = failure == null ? null : failure.getMessage();
if (exceptionMessage != null) {
exceptionMessage = exceptionMessage.replaceAll("[\\r\\n]", " ");
response.setStatusMessage(exceptionMessage);
errorMessage = exceptionMessage;
}
}
answerWithError(context, errorCode, errorMessage);
}
private void answerWithError(RoutingContext context, int errorCode, String errorMessage) {
if (!sendErrorResponseMIME(context, errorCode, errorMessage) && !sendErrorAcceptMIME(context, errorCode, errorMessage)) {
sendError(context, "text/plain", errorCode, errorMessage);
}
}
private boolean sendErrorResponseMIME(RoutingContext context, int errorCode, String errorMessage) {
String mime = context.response().headers().get(HttpHeaders.CONTENT_TYPE);
if (mime == null) {
mime = context.getAcceptableContentType();
}
return mime != null && sendError(context, mime, errorCode, errorMessage);
}
private boolean sendErrorAcceptMIME(RoutingContext context, int errorCode, String errorMessage) {
List<MIMEHeader> acceptableMimes = context.parsedHeaders().accept();
for (MIMEHeader accept : acceptableMimes) {
if (sendError(context, accept.value(), errorCode, errorMessage)) {
return true;
}
}
return false;
}
private boolean sendError(RoutingContext context, String mime, int errorCode, String errorMessage) {
final String title = "An unexpected error occurred";
HttpServerResponse response = context.response();
if (mime.startsWith("text/html")) {
StringBuilder stack = new StringBuilder();
if (context.failure() != null && displayExceptionDetails) {
for (StackTraceElement elem : context.failure().getStackTrace()) {
stack.append("<li>").append(elem).append("</li>");
}
}
response.putHeader(HttpHeaders.CONTENT_TYPE, "text/html");
response.end(
errorTemplate
.replace("{title}", title)
.replace("{errorCode}", Integer.toString(errorCode))
.replace("{errorMessage}", errorMessage)
.replace("{stackTrace}", stack.toString())
);
return true;
}
if (mime.startsWith("application/json")) {
JsonObject jsonError = new JsonObject();
jsonError.put("error", new JsonObject().put("code", errorCode).put("message", errorMessage));
if (context.failure() != null && displayExceptionDetails) {
JsonArray stack = new JsonArray();
for (StackTraceElement elem : context.failure().getStackTrace()) {
stack.add(elem.toString());
}
jsonError.put("stack", stack);
}
response.putHeader(HttpHeaders.CONTENT_TYPE, "application/json");
response.end(jsonError.encode());
return true;
}
if (mime.startsWith("text/plain")) {
response.putHeader(HttpHeaders.CONTENT_TYPE, "text/plain");
StringBuilder sb = new StringBuilder();
sb.append("Error ");
sb.append(errorCode);
sb.append(": ");
sb.append(errorMessage);
if (context.failure() != null && displayExceptionDetails) {
for (StackTraceElement elem : context.failure().getStackTrace()) {
sb.append("\tat ").append(elem).append("\n");
}
}
response.end(sb.toString());
return true;
}
return false;
}
}