Merge "Add throwable cause of REST API error to request"
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestUtil.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestUtil.java
index c47ca30..04aa44a 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestUtil.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RequestUtil.java
@@ -18,6 +18,18 @@
/** Utilities for manipulating HTTP request objects. */
public class RequestUtil {
+ /** HTTP request attribute for storing the Throwable that caused an error condition. */
+ private static final String ATTRIBUTE_ERROR_TRACE =
+ RequestUtil.class.getName() + "/ErrorTraceThrowable";
+
+ public static void setErrorTraceAttribute(HttpServletRequest req, Throwable t) {
+ req.setAttribute(ATTRIBUTE_ERROR_TRACE, t);
+ }
+
+ public static Throwable getErrorTraceAttribute(HttpServletRequest req) {
+ return (Throwable) req.getAttribute(ATTRIBUTE_ERROR_TRACE);
+ }
+
/**
* @return the same value as {@link HttpServletRequest#getPathInfo()}, but
* without decoding URL-encoded characters.
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java
index 3418354..59f96b9 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/RunAsFilter.java
@@ -14,6 +14,7 @@
package com.google.gerrit.httpd;
+import static com.google.gerrit.httpd.restapi.RestApiServlet.replyError;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
@@ -77,17 +78,19 @@
String runas = req.getHeader(RUN_AS);
if (runas != null) {
if (!enabled) {
- RestApiServlet.replyError(req, res,
+ replyError(req, res,
SC_FORBIDDEN,
- RUN_AS + " disabled by auth.enableRunAs = false");
+ RUN_AS + " disabled by auth.enableRunAs = false",
+ null);
return;
}
CurrentUser self = session.get().getCurrentUser();
if (!self.getCapabilities().canRunAs()) {
- RestApiServlet.replyError(req, res,
+ replyError(req, res,
SC_FORBIDDEN,
- "not permitted to use " + RUN_AS);
+ "not permitted to use " + RUN_AS,
+ null);
return;
}
@@ -96,15 +99,17 @@
target = accountResolver.find(runas);
} catch (OrmException e) {
log.warn("cannot resolve account for " + RUN_AS, e);
- RestApiServlet.replyError(req, res,
+ replyError(req, res,
SC_INTERNAL_SERVER_ERROR,
- "cannot resolve " + RUN_AS);
+ "cannot resolve " + RUN_AS,
+ e);
return;
}
if (target == null) {
- RestApiServlet.replyError(req, res,
+ replyError(req, res,
SC_FORBIDDEN,
- "no account matches " + RUN_AS);
+ "no account matches " + RUN_AS,
+ null);
return;
}
session.get().setUserAccountId(target.getId());
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java
index 9183d5c..46843fc 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/ParameterParser.java
@@ -68,7 +68,7 @@
clp.parseOptionMap(in);
} catch (CmdLineException e) {
if (!clp.wasHelpRequestedByOption()) {
- replyError(req, res, SC_BAD_REQUEST, e.getMessage());
+ replyError(req, res, SC_BAD_REQUEST, e.getMessage(), e);
return false;
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 979990a..4fb6e24 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -345,27 +345,27 @@
}
}
} catch (AuthException e) {
- replyError(req, res, status = SC_FORBIDDEN, e.getMessage(), e.caching());
+ replyError(req, res, status = SC_FORBIDDEN, e.getMessage(), e.caching(), e);
} catch (BadRequestException e) {
- replyError(req, res, status = SC_BAD_REQUEST, e.getMessage(), e.caching());
+ replyError(req, res, status = SC_BAD_REQUEST, e.getMessage(), e.caching(), e);
} catch (MethodNotAllowedException e) {
- replyError(req, res, status = SC_METHOD_NOT_ALLOWED, "Method not allowed", e.caching());
+ replyError(req, res, status = SC_METHOD_NOT_ALLOWED, "Method not allowed", e.caching(), e);
} catch (ResourceConflictException e) {
- replyError(req, res, status = SC_CONFLICT, e.getMessage(), e.caching());
+ replyError(req, res, status = SC_CONFLICT, e.getMessage(), e.caching(), e);
} catch (PreconditionFailedException e) {
replyError(req, res, status = SC_PRECONDITION_FAILED,
- MoreObjects.firstNonNull(e.getMessage(), "Precondition failed"), e.caching());
+ MoreObjects.firstNonNull(e.getMessage(), "Precondition failed"), e.caching(), e);
} catch (ResourceNotFoundException e) {
- replyError(req, res, status = SC_NOT_FOUND, "Not found", e.caching());
+ replyError(req, res, status = SC_NOT_FOUND, "Not found", e.caching(), e);
} catch (UnprocessableEntityException e) {
replyError(req, res, status = 422,
- MoreObjects.firstNonNull(e.getMessage(), "Unprocessable Entity"), e.caching());
+ MoreObjects.firstNonNull(e.getMessage(), "Unprocessable Entity"), e.caching(), e);
} catch (AmbiguousViewException e) {
- replyError(req, res, status = SC_NOT_FOUND, e.getMessage());
+ replyError(req, res, status = SC_NOT_FOUND, e.getMessage(), e);
} catch (MalformedJsonException e) {
- replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request");
+ replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request", e);
} catch (JsonParseException e) {
- replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request");
+ replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request", e);
} catch (Exception e) {
status = SC_INTERNAL_SERVER_ERROR;
handleException(e, req, res);
@@ -928,21 +928,24 @@
if (!res.isCommitted()) {
res.reset();
- replyError(req, res, SC_INTERNAL_SERVER_ERROR, "Internal server error");
+ replyError(req, res, SC_INTERNAL_SERVER_ERROR, "Internal server error", err);
}
}
- public static void replyError(HttpServletRequest req,
- HttpServletResponse res, int statusCode, String msg) throws IOException {
- replyError(req, res, statusCode, msg, CacheControl.NONE);
+ public static void replyError(HttpServletRequest req, HttpServletResponse res,
+ int statusCode, String msg, @Nullable Throwable err) throws IOException {
+ replyError(req, res, statusCode, msg, CacheControl.NONE, err);
}
public static void replyError(HttpServletRequest req,
HttpServletResponse res, int statusCode, String msg,
- CacheControl c) throws IOException {
+ CacheControl c, @Nullable Throwable err) throws IOException {
res.setStatus(statusCode);
configureCaching(req, res, null, c);
replyText(req, res, msg);
+ if (err != null) {
+ RequestUtil.setErrorTraceAttribute(req, err);
+ }
}
static void replyText(@Nullable HttpServletRequest req,