Allow error responses from REST API to be cached
Change-Id: I45986c287f244a1a4b53ea623258255e29dbacb5 Signed-off-by: Edwin Kempin <edwin.kempin@sap.com>
This commit is contained in:
@@ -17,6 +17,7 @@ package com.google.gerrit.extensions.restapi;
|
|||||||
/** Root exception type for JSON API failures. */
|
/** Root exception type for JSON API failures. */
|
||||||
public abstract class RestApiException extends Exception {
|
public abstract class RestApiException extends Exception {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
private CacheControl caching = CacheControl.NONE;
|
||||||
|
|
||||||
public RestApiException() {
|
public RestApiException() {
|
||||||
}
|
}
|
||||||
@@ -28,4 +29,14 @@ public abstract class RestApiException extends Exception {
|
|||||||
public RestApiException(String msg, Throwable cause) {
|
public RestApiException(String msg, Throwable cause) {
|
||||||
super(msg, cause);
|
super(msg, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CacheControl caching() {
|
||||||
|
return caching;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends RestApiException> T caching(CacheControl c) {
|
||||||
|
caching = c;
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -77,7 +77,7 @@ class RunAsFilter implements Filter {
|
|||||||
String runas = req.getHeader(RUN_AS);
|
String runas = req.getHeader(RUN_AS);
|
||||||
if (runas != null) {
|
if (runas != null) {
|
||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
RestApiServlet.replyError(res,
|
RestApiServlet.replyError(req, res,
|
||||||
SC_FORBIDDEN,
|
SC_FORBIDDEN,
|
||||||
RUN_AS + " disabled by auth.enableRunAs = false");
|
RUN_AS + " disabled by auth.enableRunAs = false");
|
||||||
return;
|
return;
|
||||||
@@ -85,7 +85,7 @@ class RunAsFilter implements Filter {
|
|||||||
|
|
||||||
CurrentUser self = session.get().getCurrentUser();
|
CurrentUser self = session.get().getCurrentUser();
|
||||||
if (!self.getCapabilities().canRunAs()) {
|
if (!self.getCapabilities().canRunAs()) {
|
||||||
RestApiServlet.replyError(res,
|
RestApiServlet.replyError(req, res,
|
||||||
SC_FORBIDDEN,
|
SC_FORBIDDEN,
|
||||||
"not permitted to use " + RUN_AS);
|
"not permitted to use " + RUN_AS);
|
||||||
return;
|
return;
|
||||||
@@ -96,13 +96,13 @@ class RunAsFilter implements Filter {
|
|||||||
target = accountResolver.find(runas);
|
target = accountResolver.find(runas);
|
||||||
} catch (OrmException e) {
|
} catch (OrmException e) {
|
||||||
log.warn("cannot resolve account for " + RUN_AS, e);
|
log.warn("cannot resolve account for " + RUN_AS, e);
|
||||||
RestApiServlet.replyError(res,
|
RestApiServlet.replyError(req, res,
|
||||||
SC_INTERNAL_SERVER_ERROR,
|
SC_INTERNAL_SERVER_ERROR,
|
||||||
"cannot resolve " + RUN_AS);
|
"cannot resolve " + RUN_AS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
RestApiServlet.replyError(res,
|
RestApiServlet.replyError(req, res,
|
||||||
SC_FORBIDDEN,
|
SC_FORBIDDEN,
|
||||||
"no account matches " + RUN_AS);
|
"no account matches " + RUN_AS);
|
||||||
return;
|
return;
|
||||||
|
@@ -68,7 +68,7 @@ class ParameterParser {
|
|||||||
clp.parseOptionMap(in);
|
clp.parseOptionMap(in);
|
||||||
} catch (CmdLineException e) {
|
} catch (CmdLineException e) {
|
||||||
if (!clp.wasHelpRequestedByOption()) {
|
if (!clp.wasHelpRequestedByOption()) {
|
||||||
replyError(res, SC_BAD_REQUEST, e.getMessage());
|
replyError(req, res, SC_BAD_REQUEST, e.getMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -321,27 +321,27 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (AuthException e) {
|
} catch (AuthException e) {
|
||||||
replyError(res, status = SC_FORBIDDEN, e.getMessage());
|
replyError(req, res, status = SC_FORBIDDEN, e.getMessage(), e.caching());
|
||||||
} catch (BadRequestException e) {
|
} catch (BadRequestException e) {
|
||||||
replyError(res, status = SC_BAD_REQUEST, e.getMessage());
|
replyError(req, res, status = SC_BAD_REQUEST, e.getMessage(), e.caching());
|
||||||
} catch (MethodNotAllowedException e) {
|
} catch (MethodNotAllowedException e) {
|
||||||
replyError(res, status = SC_METHOD_NOT_ALLOWED, "Method not allowed");
|
replyError(req, res, status = SC_METHOD_NOT_ALLOWED, "Method not allowed", e.caching());
|
||||||
} catch (ResourceConflictException e) {
|
} catch (ResourceConflictException e) {
|
||||||
replyError(res, status = SC_CONFLICT, e.getMessage());
|
replyError(req, res, status = SC_CONFLICT, e.getMessage(), e.caching());
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (PreconditionFailedException e) {
|
||||||
replyError(res, status = SC_PRECONDITION_FAILED,
|
replyError(req, res, status = SC_PRECONDITION_FAILED,
|
||||||
Objects.firstNonNull(e.getMessage(), "Precondition failed"));
|
Objects.firstNonNull(e.getMessage(), "Precondition failed"), e.caching());
|
||||||
} catch (ResourceNotFoundException e) {
|
} catch (ResourceNotFoundException e) {
|
||||||
replyError(res, status = SC_NOT_FOUND, "Not found");
|
replyError(req, res, status = SC_NOT_FOUND, "Not found", e.caching());
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
replyError(res, status = 422,
|
replyError(req, res, status = 422,
|
||||||
Objects.firstNonNull(e.getMessage(), "Unprocessable Entity"));
|
Objects.firstNonNull(e.getMessage(), "Unprocessable Entity"), e.caching());
|
||||||
} catch (AmbiguousViewException e) {
|
} catch (AmbiguousViewException e) {
|
||||||
replyError(res, status = SC_NOT_FOUND, e.getMessage());
|
replyError(req, res, status = SC_NOT_FOUND, e.getMessage());
|
||||||
} catch (MalformedJsonException e) {
|
} catch (MalformedJsonException e) {
|
||||||
replyError(res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request");
|
replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request");
|
||||||
} catch (JsonParseException e) {
|
} catch (JsonParseException e) {
|
||||||
replyError(res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request");
|
replyError(req, res, status = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
status = SC_INTERNAL_SERVER_ERROR;
|
status = SC_INTERNAL_SERVER_ERROR;
|
||||||
handleException(e, req, res);
|
handleException(e, req, res);
|
||||||
@@ -837,14 +837,20 @@ public class RestApiServlet extends HttpServlet {
|
|||||||
|
|
||||||
if (!res.isCommitted()) {
|
if (!res.isCommitted()) {
|
||||||
res.reset();
|
res.reset();
|
||||||
replyError(res, SC_INTERNAL_SERVER_ERROR, "Internal server error");
|
replyError(req, res, SC_INTERNAL_SERVER_ERROR, "Internal server error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void replyError(HttpServletResponse res, int statusCode,
|
public static void replyError(HttpServletRequest req,
|
||||||
String msg) throws IOException {
|
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,
|
||||||
|
CacheControl c) throws IOException {
|
||||||
res.setStatus(statusCode);
|
res.setStatus(statusCode);
|
||||||
CacheHeaders.setNotCacheable(res);
|
configureCaching(req, res, c);
|
||||||
replyText(null, res, msg);
|
replyText(null, res, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user