From 35fe53617058bba833e738c0ecd622c4a4bd4965 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 9 Jun 2021 13:09:25 +0200 Subject: [PATCH] Fix options request return internal server error (#1688) --- gradle/changelog/options_request.yaml | 2 + .../main/java/sonia/scm/api/ErrorDtos.java | 45 ++++++ .../sonia/scm/api/FailureExceptionMapper.java | 84 ++++++++++++ .../api/WebApplicationExceptionMapper.java | 8 +- .../scm/api/FailureExceptionMapperTest.java | 128 ++++++++++++++++++ 5 files changed, 260 insertions(+), 7 deletions(-) create mode 100644 gradle/changelog/options_request.yaml create mode 100644 scm-webapp/src/main/java/sonia/scm/api/ErrorDtos.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/FailureExceptionMapper.java create mode 100644 scm-webapp/src/test/java/sonia/scm/api/FailureExceptionMapperTest.java diff --git a/gradle/changelog/options_request.yaml b/gradle/changelog/options_request.yaml new file mode 100644 index 0000000000..0baf2bad26 --- /dev/null +++ b/gradle/changelog/options_request.yaml @@ -0,0 +1,2 @@ +- type: fixed + description: Options requests returning internal server errors ([#1685](https://github.com/scm-manager/scm-manager/issues/1685), [#1688](https://github.com/scm-manager/scm-manager/pull/1688)) diff --git a/scm-webapp/src/main/java/sonia/scm/api/ErrorDtos.java b/scm-webapp/src/main/java/sonia/scm/api/ErrorDtos.java new file mode 100644 index 0000000000..85833a8061 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/ErrorDtos.java @@ -0,0 +1,45 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api; + +import org.slf4j.MDC; +import sonia.scm.api.v2.resources.ErrorDto; + +import java.util.Collections; + +class ErrorDtos { + + private ErrorDtos() { + } + + static ErrorDto from(String code, Exception exception) { + ErrorDto errorDto = new ErrorDto(); + errorDto.setMessage(exception.getMessage()); + errorDto.setContext(Collections.emptyList()); + errorDto.setErrorCode(code); + errorDto.setTransactionId(MDC.get("transaction_id")); + return errorDto; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/FailureExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/FailureExceptionMapper.java new file mode 100644 index 0000000000..2cd5f8e73a --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/FailureExceptionMapper.java @@ -0,0 +1,84 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.api; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableSet; +import org.jboss.resteasy.spi.Failure; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import sonia.scm.api.v2.resources.ErrorDto; +import sonia.scm.web.VndMediaType; + +import javax.inject.Inject; +import javax.inject.Provider; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import java.util.Set; + +@javax.ws.rs.ext.Provider +public class FailureExceptionMapper implements ExceptionMapper { + + private static final Logger LOG = LoggerFactory.getLogger(FailureExceptionMapper.class); + + private static final Set METHODS_WITHOUT_BODY = ImmutableSet.of("HEAD", "OPTIONS"); + + @VisibleForTesting + static final String ERROR_CODE = "2BSZikGOB1"; + + private final Provider requestProvider; + + @Inject + public FailureExceptionMapper(Provider requestProvider) { + this.requestProvider = requestProvider; + } + + @Override + public Response toResponse(Failure exception) { + if (exception.isLoggable()) { + LOG.warn("handle uncatched failure", exception); + } + Response.ResponseBuilder builder = builder(exception); + if (shouldAppendErrorDto()) { + ErrorDto errorDto = ErrorDtos.from(ERROR_CODE, exception); + builder.entity(errorDto).type(VndMediaType.ERROR_TYPE); + } + return builder.build(); + } + + private Response.ResponseBuilder builder(Failure exception) { + Response response = exception.getResponse(); + if (response != null) { + return Response.fromResponse(response); + } + return Response.status(exception.getErrorCode()); + } + + private boolean shouldAppendErrorDto() { + String method = requestProvider.get().getMethod(); + return !METHODS_WITHOUT_BODY.contains(method); + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/WebApplicationExceptionMapper.java b/scm-webapp/src/main/java/sonia/scm/api/WebApplicationExceptionMapper.java index 41ec6787ef..3c091fdb52 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/WebApplicationExceptionMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/WebApplicationExceptionMapper.java @@ -26,7 +26,6 @@ package sonia.scm.api; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.MDC; import sonia.scm.api.v2.resources.ErrorDto; import sonia.scm.web.VndMediaType; @@ -34,7 +33,6 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; -import java.util.Collections; @Provider public class WebApplicationExceptionMapper implements ExceptionMapper { @@ -47,12 +45,8 @@ public class WebApplicationExceptionMapper implements ExceptionMapper