use of com.google.gerrit.server.logging.TraceContext in project gerrit by GerritCodeReview.
the class MergeSuperSet method completeChangeSet.
/**
* Gets the ChangeSet of this {@code change} based on visiblity of the {@code user}. if
* change.submitWholeTopic is true, we return the topic closure as well as the dependent changes
* of the topic closure. Otherwise, we return just the dependent changes.
*
* @param change the change for which we get the dependent changes / topic closure.
* @param user the current user for visibility purposes.
* @param includingTopicClosure when true, return as if change.submitWholeTopic = true, so we
* return the topic closure.
* @return {@link ChangeSet} object that represents the dependent changes and/or topic closure of
* the requested change.
*/
public ChangeSet completeChangeSet(Change change, CurrentUser user, boolean includingTopicClosure) throws IOException, PermissionBackendException {
try {
if (orm == null) {
orm = repoManagerProvider.get();
closeOrm = true;
}
ChangeData cd = changeDataFactory.create(change.getProject(), change.getId());
boolean visible = false;
if (cd != null) {
if (projectCache.get(cd.project()).map(ProjectState::statePermitsRead).orElse(false)) {
try {
permissionBackend.user(user).change(cd).check(ChangePermission.READ);
visible = true;
} catch (AuthException e) {
// Do nothing.
}
}
}
ChangeSet changeSet = new ChangeSet(cd, visible);
if (wholeTopicEnabled(cfg) || includingTopicClosure) {
return completeChangeSetIncludingTopics(changeSet, user);
}
try (TraceContext traceContext = PluginContext.newTrace(mergeSuperSetComputation)) {
return mergeSuperSetComputation.get().completeWithoutTopic(orm, changeSet, user);
}
} finally {
if (closeOrm && orm != null) {
orm.close();
orm = null;
}
}
}
use of com.google.gerrit.server.logging.TraceContext in project gerrit by GerritCodeReview.
the class Upload method runImpl.
@Override
protected void runImpl() throws IOException, Failure {
PermissionBackend.ForProject perm = permissionBackend.user(user).project(projectState.getNameKey());
try {
perm.check(ProjectPermission.RUN_UPLOAD_PACK);
} catch (AuthException e) {
throw new Failure(1, "fatal: upload-pack not permitted on this server", e);
} catch (PermissionBackendException e) {
throw new Failure(1, "fatal: unable to check permissions ", e);
}
Repository permissionAwareRepo = PermissionAwareRepositoryManager.wrap(repo, perm);
UploadPack up = new UploadPack(permissionAwareRepo);
up.setPackConfig(config.getPackConfig());
up.setTimeout(config.getTimeout());
up.setPostUploadHook(PostUploadHookChain.newChain(Lists.newArrayList(postUploadHooks)));
if (projectState.isAllUsers()) {
up.setAdvertiseRefsHook(usersSelfAdvertiseRefsHook);
}
if (extraParameters != null) {
up.setExtraParameters(ImmutableList.copyOf(extraParameters));
}
List<PreUploadHook> allPreUploadHooks = Lists.newArrayList(preUploadHooks);
allPreUploadHooks.add(uploadValidatorsFactory.create(project, permissionAwareRepo, session.getRemoteAddressAsString()));
up.setPreUploadHook(PreUploadHookChain.newChain(allPreUploadHooks));
for (UploadPackInitializer initializer : uploadPackInitializers) {
initializer.init(projectState.getNameKey(), up);
}
try (TraceContext traceContext = TraceContext.open();
TracingHook tracingHook = new TracingHook()) {
RequestInfo requestInfo = RequestInfo.builder(RequestInfo.RequestType.GIT_UPLOAD, user, traceContext).project(projectState.getNameKey()).build();
requestListeners.runEach(l -> l.onRequest(requestInfo));
up.setProtocolV2Hook(tracingHook);
up.upload(in, out, err);
session.setPeerAgent(up.getPeerUserAgent());
stats = up.getStatistics();
} catch (UploadValidationException e) {
// internal server error to the client.
if (!e.isOutput()) {
up.sendMessage(e.getMessage());
}
}
}
use of com.google.gerrit.server.logging.TraceContext in project gerrit by GerritCodeReview.
the class RestApiServlet method service.
@Override
protected final void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
final long startNanos = System.nanoTime();
long auditStartTs = TimeUtil.nowMs();
res.setHeader("Content-Disposition", "attachment");
res.setHeader("X-Content-Type-Options", "nosniff");
int statusCode = SC_OK;
long responseBytes = -1;
Optional<Exception> cause = Optional.empty();
Response<?> response = null;
QueryParams qp = null;
Object inputRequestBody = null;
RestResource rsrc = TopLevelResource.INSTANCE;
ViewData viewData = null;
try (TraceContext traceContext = enableTracing(req, res)) {
String requestUri = requestUri(req);
try (PerThreadCache ignored = PerThreadCache.create()) {
List<IdString> path = splitPath(req);
RequestInfo requestInfo = createRequestInfo(traceContext, requestUri, path);
globals.requestListeners.runEach(l -> l.onRequest(requestInfo));
// TraceIT#performanceLoggingForRestCall()).
try (RequestStateContext requestStateContext = RequestStateContext.open().addRequestStateProvider(globals.deadlineCheckerFactory.create(requestInfo, req.getHeader(X_GERRIT_DEADLINE)));
PerformanceLogContext performanceLogContext = new PerformanceLogContext(globals.config, globals.performanceLoggers)) {
traceRequestData(req);
if (isCorsPreflight(req)) {
doCorsPreflight(req, res);
return;
}
qp = ParameterParser.getQueryParams(req);
checkCors(req, res, qp.hasXdOverride());
if (qp.hasXdOverride()) {
req = applyXdOverrides(req, qp);
}
checkUserSession(req);
RestCollection<RestResource, RestResource> rc = members.get();
globals.permissionBackend.currentUser().checkAny(GlobalPermission.fromAnnotation(rc.getClass()));
viewData = new ViewData(null, null);
if (path.isEmpty()) {
globals.quotaChecker.enforce(req);
if (rc instanceof NeedsParams) {
((NeedsParams) rc).setParams(qp.params());
}
if (isRead(req)) {
viewData = new ViewData(null, rc.list());
} else if (isPost(req)) {
RestView<RestResource> restCollectionView = rc.views().get(PluginName.GERRIT, "POST_ON_COLLECTION./");
if (restCollectionView != null) {
viewData = new ViewData(null, restCollectionView);
} else {
throw methodNotAllowed(req);
}
} else {
// DELETE on root collections is not supported
throw methodNotAllowed(req);
}
} else {
IdString id = path.remove(0);
try {
rsrc = parseResourceWithRetry(req, traceContext, viewData.pluginName, rc, rsrc, id);
globals.quotaChecker.enforce(rsrc, req);
if (path.isEmpty()) {
checkPreconditions(req);
}
} catch (ResourceNotFoundException e) {
if (!path.isEmpty()) {
throw e;
}
globals.quotaChecker.enforce(req);
if (isPost(req) || isPut(req)) {
RestView<RestResource> createView = rc.views().get(PluginName.GERRIT, "CREATE./");
if (createView != null) {
viewData = new ViewData(null, createView);
path.add(id);
} else {
throw e;
}
} else if (isDelete(req)) {
RestView<RestResource> deleteView = rc.views().get(PluginName.GERRIT, "DELETE_MISSING./");
if (deleteView != null) {
viewData = new ViewData(null, deleteView);
path.add(id);
} else {
throw e;
}
} else {
throw e;
}
}
if (viewData.view == null) {
viewData = view(rc, req.getMethod(), path);
}
}
checkRequiresCapability(viewData);
while (viewData.view instanceof RestCollection<?, ?>) {
@SuppressWarnings("unchecked") RestCollection<RestResource, RestResource> c = (RestCollection<RestResource, RestResource>) viewData.view;
if (path.isEmpty()) {
if (isRead(req)) {
viewData = new ViewData(null, c.list());
} else if (isPost(req)) {
// TODO: Here and on other collection methods: There is a bug that binds child views
// with pluginName="gerrit" instead of the real plugin name. This has never worked
// correctly and should be fixed where the binding gets created (DynamicMapProvider)
// and here.
RestView<RestResource> restCollectionView = c.views().get(PluginName.GERRIT, "POST_ON_COLLECTION./");
if (restCollectionView != null) {
viewData = new ViewData(null, restCollectionView);
} else {
throw methodNotAllowed(req);
}
} else if (isDelete(req)) {
RestView<RestResource> restCollectionView = c.views().get(PluginName.GERRIT, "DELETE_ON_COLLECTION./");
if (restCollectionView != null) {
viewData = new ViewData(null, restCollectionView);
} else {
throw methodNotAllowed(req);
}
} else {
throw methodNotAllowed(req);
}
break;
}
IdString id = path.remove(0);
try {
rsrc = parseResourceWithRetry(req, traceContext, viewData.pluginName, c, rsrc, id);
checkPreconditions(req);
viewData = new ViewData(null, null);
} catch (ResourceNotFoundException e) {
if (!path.isEmpty()) {
throw e;
}
if (isPost(req) || isPut(req)) {
RestView<RestResource> createView = c.views().get(PluginName.GERRIT, "CREATE./");
if (createView != null) {
viewData = new ViewData(viewData.pluginName, createView);
path.add(id);
} else {
throw e;
}
} else if (isDelete(req)) {
RestView<RestResource> deleteView = c.views().get(PluginName.GERRIT, "DELETE_MISSING./");
if (deleteView != null) {
viewData = new ViewData(viewData.pluginName, deleteView);
path.add(id);
} else {
throw e;
}
} else {
throw e;
}
}
if (viewData.view == null) {
viewData = view(c, req.getMethod(), path);
}
checkRequiresCapability(viewData);
}
if (notModified(req, traceContext, viewData, rsrc)) {
logger.atFinest().log("REST call succeeded: %d", SC_NOT_MODIFIED);
res.sendError(SC_NOT_MODIFIED);
return;
}
try (DynamicOptions pluginOptions = new DynamicOptions(globals.injector, globals.dynamicBeans)) {
if (!globals.paramParser.get().parse(viewData.view, pluginOptions, qp.params(), req, res)) {
return;
}
if (viewData.view instanceof RestReadView<?> && isRead(req)) {
response = invokeRestReadViewWithRetry(req, traceContext, viewData, (RestReadView<RestResource>) viewData.view, rsrc);
} else if (viewData.view instanceof RestModifyView<?, ?>) {
@SuppressWarnings("unchecked") RestModifyView<RestResource, Object> m = (RestModifyView<RestResource, Object>) viewData.view;
Type type = inputType(m);
inputRequestBody = parseRequest(req, type);
response = invokeRestModifyViewWithRetry(req, traceContext, viewData, m, rsrc, inputRequestBody);
if (inputRequestBody instanceof RawInput) {
try (InputStream is = req.getInputStream()) {
ServletUtils.consumeRequestBody(is);
}
}
} else if (viewData.view instanceof RestCollectionCreateView<?, ?, ?>) {
@SuppressWarnings("unchecked") RestCollectionCreateView<RestResource, RestResource, Object> m = (RestCollectionCreateView<RestResource, RestResource, Object>) viewData.view;
Type type = inputType(m);
inputRequestBody = parseRequest(req, type);
response = invokeRestCollectionCreateViewWithRetry(req, traceContext, viewData, m, rsrc, path.get(0), inputRequestBody);
if (inputRequestBody instanceof RawInput) {
try (InputStream is = req.getInputStream()) {
ServletUtils.consumeRequestBody(is);
}
}
} else if (viewData.view instanceof RestCollectionDeleteMissingView<?, ?, ?>) {
@SuppressWarnings("unchecked") RestCollectionDeleteMissingView<RestResource, RestResource, Object> m = (RestCollectionDeleteMissingView<RestResource, RestResource, Object>) viewData.view;
Type type = inputType(m);
inputRequestBody = parseRequest(req, type);
response = invokeRestCollectionDeleteMissingViewWithRetry(req, traceContext, viewData, m, rsrc, path.get(0), inputRequestBody);
if (inputRequestBody instanceof RawInput) {
try (InputStream is = req.getInputStream()) {
ServletUtils.consumeRequestBody(is);
}
}
} else if (viewData.view instanceof RestCollectionModifyView<?, ?, ?>) {
@SuppressWarnings("unchecked") RestCollectionModifyView<RestResource, RestResource, Object> m = (RestCollectionModifyView<RestResource, RestResource, Object>) viewData.view;
Type type = inputType(m);
inputRequestBody = parseRequest(req, type);
response = invokeRestCollectionModifyViewWithRetry(req, traceContext, viewData, m, rsrc, inputRequestBody);
if (inputRequestBody instanceof RawInput) {
try (InputStream is = req.getInputStream()) {
ServletUtils.consumeRequestBody(is);
}
}
} else {
throw new ResourceNotFoundException();
}
String isUpdatedRefEnabled = req.getHeader(X_GERRIT_UPDATED_REF_ENABLED);
if (!Strings.isNullOrEmpty(isUpdatedRefEnabled) && Boolean.valueOf(isUpdatedRefEnabled)) {
setXGerritUpdatedRefResponseHeaders(req, res);
}
if (response instanceof Response.Redirect) {
CacheHeaders.setNotCacheable(res);
String location = ((Response.Redirect) response).location();
res.sendRedirect(location);
logger.atFinest().log("REST call redirected to: %s", location);
return;
} else if (response instanceof Response.Accepted) {
CacheHeaders.setNotCacheable(res);
res.setStatus(response.statusCode());
res.setHeader(HttpHeaders.LOCATION, ((Response.Accepted) response).location());
logger.atFinest().log("REST call succeeded: %d", response.statusCode());
return;
}
statusCode = response.statusCode();
configureCaching(req, res, traceContext, rsrc, viewData, response.caching());
res.setStatus(statusCode);
logger.atFinest().log("REST call succeeded: %d", statusCode);
}
if (response != Response.none()) {
Object value = Response.unwrap(response);
if (value instanceof BinaryResult) {
responseBytes = replyBinaryResult(req, res, (BinaryResult) value);
} else {
responseBytes = replyJson(req, res, false, qp.config(), value);
}
}
}
} catch (MalformedJsonException | JsonParseException e) {
cause = Optional.of(e);
logger.atFine().withCause(e).log("REST call failed on JSON parsing");
responseBytes = replyError(req, res, statusCode = SC_BAD_REQUEST, "Invalid " + JSON_TYPE + " in request", e);
} catch (BadRequestException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_BAD_REQUEST, messageOr(e, "Bad Request"), e.caching(), e);
} catch (AuthException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_FORBIDDEN, messageOr(e, "Forbidden"), e.caching(), e);
} catch (AmbiguousViewException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_NOT_FOUND, messageOr(e, "Ambiguous"), e);
} catch (ResourceNotFoundException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_NOT_FOUND, messageOr(e, "Not Found"), e.caching(), e);
} catch (MethodNotAllowedException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_METHOD_NOT_ALLOWED, messageOr(e, "Method Not Allowed"), e.caching(), e);
} catch (ResourceConflictException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_CONFLICT, messageOr(e, "Conflict"), e.caching(), e);
} catch (PreconditionFailedException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_PRECONDITION_FAILED, messageOr(e, "Precondition Failed"), e.caching(), e);
} catch (UnprocessableEntityException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_UNPROCESSABLE_ENTITY, messageOr(e, "Unprocessable Entity"), e.caching(), e);
} catch (NotImplementedException e) {
cause = Optional.of(e);
logger.atSevere().withCause(e).log("Error in %s %s", req.getMethod(), uriForLogging(req));
responseBytes = replyError(req, res, statusCode = SC_NOT_IMPLEMENTED, messageOr(e, "Not Implemented"), e);
} catch (QuotaException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_TOO_MANY_REQUESTS, messageOr(e, "Quota limit reached"), e.caching(), e);
} catch (InvalidDeadlineException e) {
cause = Optional.of(e);
responseBytes = replyError(req, res, statusCode = SC_BAD_REQUEST, messageOr(e, "Bad Request"), e);
} catch (Exception e) {
cause = Optional.of(e);
Optional<RequestCancelledException> requestCancelledException = RequestCancelledException.getFromCausalChain(e);
if (requestCancelledException.isPresent()) {
RequestStateProvider.Reason cancellationReason = requestCancelledException.get().getCancellationReason();
globals.cancellationMetrics.countCancelledRequest(RequestInfo.RequestType.REST, requestUri, cancellationReason);
statusCode = getCancellationStatusCode(cancellationReason);
responseBytes = replyError(req, res, statusCode, getCancellationMessage(requestCancelledException.get()), e);
} else {
statusCode = SC_INTERNAL_SERVER_ERROR;
Optional<ExceptionHook.Status> status = getStatus(e);
statusCode = status.map(ExceptionHook.Status::statusCode).orElse(SC_INTERNAL_SERVER_ERROR);
if (res.isCommitted()) {
responseBytes = 0;
if (statusCode == SC_INTERNAL_SERVER_ERROR) {
logger.atSevere().withCause(e).log("Error in %s %s, response already committed", req.getMethod(), uriForLogging(req));
} else {
logger.atWarning().log("Response for %s %s already committed, wanted to set status %d", req.getMethod(), uriForLogging(req), statusCode);
}
} else {
res.reset();
TraceContext.getTraceId().ifPresent(traceId -> res.addHeader(X_GERRIT_TRACE, traceId));
if (status.isPresent()) {
responseBytes = reply(req, res, e, status.get(), getUserMessages(e));
} else {
responseBytes = replyInternalServerError(req, res, e, getUserMessages(e));
}
}
}
} finally {
String metric = getViewName(viewData);
String formattedCause = cause.map(globals.retryHelper::formatCause).orElse("_none");
globals.metrics.count.increment(metric);
if (statusCode >= SC_BAD_REQUEST) {
globals.metrics.errorCount.increment(metric, statusCode, formattedCause);
}
if (responseBytes != -1) {
globals.metrics.responseBytes.record(metric, responseBytes);
}
globals.metrics.serverLatency.record(metric, System.nanoTime() - startNanos, TimeUnit.NANOSECONDS);
globals.auditService.dispatch(new ExtendedHttpAuditEvent(globals.webSession.get().getSessionId(), globals.currentUser.get(), req, auditStartTs, qp != null ? qp.params() : ImmutableListMultimap.of(), inputRequestBody, statusCode, response, rsrc, viewData == null ? null : viewData.view));
}
}
}
use of com.google.gerrit.server.logging.TraceContext in project gerrit by GerritCodeReview.
the class RestApiServlet method enableTracing.
private TraceContext enableTracing(HttpServletRequest req, HttpServletResponse res) {
// There are 2 ways to enable tracing for REST calls:
// 1. by using the 'trace' or 'trace=<trace-id>' request parameter
// 2. by setting the 'X-Gerrit-Trace:' or 'X-Gerrit-Trace:<trace-id>' header
String traceValueFromHeader = req.getHeader(X_GERRIT_TRACE);
String traceValueFromRequestParam = req.getParameter(ParameterParser.TRACE_PARAMETER);
boolean doTrace = traceValueFromHeader != null || traceValueFromRequestParam != null;
// Check whether no trace ID, one trace ID or 2 different trace IDs have been specified.
String traceId1;
String traceId2;
if (!Strings.isNullOrEmpty(traceValueFromHeader)) {
traceId1 = traceValueFromHeader;
if (!Strings.isNullOrEmpty(traceValueFromRequestParam) && !traceValueFromHeader.equals(traceValueFromRequestParam)) {
traceId2 = traceValueFromRequestParam;
} else {
traceId2 = null;
}
} else {
traceId1 = Strings.emptyToNull(traceValueFromRequestParam);
traceId2 = null;
}
// Use the first trace ID to start tracing. If this trace ID is null, a trace ID will be
// generated.
TraceContext traceContext = TraceContext.newTrace(doTrace, traceId1, (tagName, traceId) -> res.setHeader(X_GERRIT_TRACE, traceId));
// If a second trace ID was specified, add a tag for it as well.
if (traceId2 != null) {
traceContext.addTag(RequestId.Type.TRACE_ID, traceId2);
res.addHeader(X_GERRIT_TRACE, traceId2);
}
return traceContext;
}
use of com.google.gerrit.server.logging.TraceContext in project gerrit by GerritCodeReview.
the class CheckAccess method apply.
public Response<AccessCheckInfo> apply(ProjectResource rsrc, AccessCheckInput input) throws PermissionBackendException, RestApiException, IOException, ConfigInvalidException {
permissionBackend.user(rsrc.getUser()).check(GlobalPermission.VIEW_ACCESS);
rsrc.getProjectState().checkStatePermitsRead();
if (input == null) {
throw new BadRequestException("input is required");
}
if (Strings.isNullOrEmpty(input.account)) {
throw new BadRequestException("input requires 'account'");
}
try (TraceContext traceContext = TraceContext.open()) {
traceContext.enableAclLogging();
Account.Id match = accountResolver.resolve(input.account).asUnique().account().id();
try {
permissionBackend.absentUser(match).project(rsrc.getNameKey()).check(ProjectPermission.ACCESS);
} catch (AuthException e) {
return Response.ok(createInfo(HttpServletResponse.SC_FORBIDDEN, String.format("user %s cannot see project %s", match, rsrc.getName())));
}
RefPermission refPerm;
if (!Strings.isNullOrEmpty(input.permission)) {
if (Strings.isNullOrEmpty(input.ref)) {
throw new BadRequestException("must set 'ref' when specifying 'permission'");
}
Optional<RefPermission> rp = DefaultPermissionMappings.refPermission(input.permission);
if (!rp.isPresent()) {
throw new BadRequestException(String.format("'%s' is not recognized as ref permission", input.permission));
}
refPerm = rp.get();
} else {
refPerm = RefPermission.READ;
}
String message = null;
if (!Strings.isNullOrEmpty(input.ref)) {
try {
permissionBackend.absentUser(match).ref(BranchNameKey.create(rsrc.getNameKey(), input.ref)).check(refPerm);
} catch (AuthException e) {
return Response.ok(createInfo(HttpServletResponse.SC_FORBIDDEN, String.format("user %s lacks permission %s for %s in project %s", match, input.permission, input.ref, rsrc.getName())));
}
} else {
// as access denied looks the same as no branches to the user.
try (Repository repo = gitRepositoryManager.openRepository(rsrc.getNameKey())) {
if (repo.getRefDatabase().getRefsByPrefix(REFS_HEADS).isEmpty()) {
message = "access is OK, but repository has no branches under refs/heads/";
}
}
}
return Response.ok(createInfo(HttpServletResponse.SC_OK, message));
}
}
Aggregations