diff --git a/changelog/@unreleased/pr-2373.v2.yml b/changelog/@unreleased/pr-2373.v2.yml new file mode 100644 index 000000000..5253e3f28 --- /dev/null +++ b/changelog/@unreleased/pr-2373.v2.yml @@ -0,0 +1,5 @@ +type: improvement +improvement: + description: conjure-undertow propagates QosException metadata + links: + - https://github.com/palantir/conjure-java/pull/2373 diff --git a/conjure-java-undertow-runtime/build.gradle b/conjure-java-undertow-runtime/build.gradle index ef791a7ff..9916402bf 100644 --- a/conjure-java-undertow-runtime/build.gradle +++ b/conjure-java-undertow-runtime/build.gradle @@ -41,7 +41,11 @@ dependencies { implementation 'org.slf4j:slf4j-api' testImplementation 'com.fasterxml.jackson.core:jackson-annotations' - testImplementation 'com.palantir.conjure.java.api:test-utils' + testImplementation 'com.palantir.conjure.java.api:test-utils', { + // TODO(ckozak): upgrading junit dependencies is blocked by the ancient dropwizard + // test libraries used in this project + exclude group: 'org.mockito' + } testImplementation 'com.palantir.safe-logging:preconditions-assertj' testImplementation 'org.assertj:assertj-core' testImplementation 'org.junit.jupiter:junit-jupiter' diff --git a/conjure-java-undertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureExceptions.java b/conjure-java-undertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureExceptions.java index 241899748..a495efe9e 100644 --- a/conjure-java-undertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureExceptions.java +++ b/conjure-java-undertow-runtime/src/main/java/com/palantir/conjure/java/undertow/runtime/ConjureExceptions.java @@ -19,6 +19,8 @@ import com.google.common.util.concurrent.RateLimiter; import com.palantir.conjure.java.api.errors.ErrorType; import com.palantir.conjure.java.api.errors.QosException; +import com.palantir.conjure.java.api.errors.QosReasons; +import com.palantir.conjure.java.api.errors.QosReasons.QosResponseEncodingAdapter; import com.palantir.conjure.java.api.errors.RemoteException; import com.palantir.conjure.java.api.errors.SerializableError; import com.palantir.conjure.java.api.errors.ServiceException; @@ -31,6 +33,7 @@ import io.undertow.io.UndertowOutputStream; import io.undertow.server.HttpServerExchange; import io.undertow.util.Headers; +import io.undertow.util.HttpString; import java.io.IOException; import java.io.OutputStream; import java.time.temporal.ChronoUnit; @@ -93,6 +96,7 @@ private static void serviceException(HttpServerExchange exchange, ServiceExcepti private static void qosException(HttpServerExchange exchange, QosException qosException) { qosException.accept(QOS_EXCEPTION_HEADERS).accept(exchange); + QosReasons.encodeToResponse(qosException.getReason(), exchange, UndertowQosResponseEncodingAdapter.INSTANCE); if (log.isDebugEnabled()) { log.debug("Quality-of-Service error handling request", qosException); @@ -287,4 +291,13 @@ public Consumer visit(QosException.Unavailable _exception) { return _exchange -> {}; } }; + + private enum UndertowQosResponseEncodingAdapter implements QosResponseEncodingAdapter { + INSTANCE; + + @Override + public void setHeader(HttpServerExchange exchange, String headerName, String headerValue) { + exchange.getResponseHeaders().put(HttpString.tryFromString(headerName), headerValue); + } + } } diff --git a/conjure-java-undertow-runtime/src/test/java/com/palantir/conjure/java/undertow/runtime/ConjureExceptionHandlerTest.java b/conjure-java-undertow-runtime/src/test/java/com/palantir/conjure/java/undertow/runtime/ConjureExceptionHandlerTest.java index d6cfb001d..97c8e6000 100644 --- a/conjure-java-undertow-runtime/src/test/java/com/palantir/conjure/java/undertow/runtime/ConjureExceptionHandlerTest.java +++ b/conjure-java-undertow-runtime/src/test/java/com/palantir/conjure/java/undertow/runtime/ConjureExceptionHandlerTest.java @@ -23,6 +23,9 @@ import com.palantir.conjure.java.api.errors.ErrorType; import com.palantir.conjure.java.api.errors.ErrorType.Code; import com.palantir.conjure.java.api.errors.QosException; +import com.palantir.conjure.java.api.errors.QosReason; +import com.palantir.conjure.java.api.errors.QosReason.DueTo; +import com.palantir.conjure.java.api.errors.QosReason.RetryHint; import com.palantir.conjure.java.api.errors.RemoteException; import com.palantir.conjure.java.api.errors.SerializableError; import com.palantir.conjure.java.api.errors.ServiceException; @@ -181,6 +184,21 @@ public void handlesQosExceptionUnavailable() throws IOException { assertThat(connection.getErrorStream()).isNull(); } + @Test + public void handlesQosExceptionUnavailableWithMetadata() throws IOException { + exception = QosException.unavailable(QosReason.builder() + .reason("reason") + .retryHint(RetryHint.DO_NOT_RETRY) + .dueTo(DueTo.CUSTOM) + .build()); + HttpURLConnection connection = execute(); + + assertThat(connection.getResponseCode()).isEqualTo(503); + assertThat(connection.getHeaderFields()).containsEntry("Qos-Retry-Hint", ImmutableList.of("do-not-retry")); + assertThat(connection.getHeaderFields()).containsEntry("Qos-Due-To", ImmutableList.of("custom")); + assertThat(connection.getErrorStream()).isNull(); + } + @Test public void handlesIllegalArgumentException() throws IOException { exception = new IllegalArgumentException("Foo"); diff --git a/versions.lock b/versions.lock index dce63926a..0b54794ca 100644 --- a/versions.lock +++ b/versions.lock @@ -2,7 +2,7 @@ com.atlassian.commonmark:commonmark:0.12.1 (1 constraints: 36052a3b) com.fasterxml.jackson.core:jackson-annotations:2.18.0 (24 constraints: 5e95f703) com.fasterxml.jackson.core:jackson-core:2.18.0 (22 constraints: 19c138db) -com.fasterxml.jackson.core:jackson-databind:2.18.0 (35 constraints: eb9a40b0) +com.fasterxml.jackson.core:jackson-databind:2.18.0 (35 constraints: ed9acab7) com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.18.0 (4 constraints: e865624f) com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.18.0 (1 constraints: 811ca2a4) com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.0 (3 constraints: 3b25da0f) @@ -13,17 +13,17 @@ com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.0 (2 constraints: 32 com.github.ben-manes.caffeine:caffeine:3.1.8 (10 constraints: 63b008f3) com.google.auto:auto-common:1.2.2 (2 constraints: 1d175731) com.google.code.findbugs:jsr305:3.0.2 (35 constraints: b94940f6) -com.google.errorprone:error_prone_annotations:2.7.1 (27 constraints: aec23fbb) +com.google.errorprone:error_prone_annotations:2.7.1 (27 constraints: afc2edbf) com.google.guava:failureaccess:1.0.2 (1 constraints: 150ae2b4) -com.google.guava:guava:33.2.0-jre (36 constraints: 5f9b9378) +com.google.guava:guava:33.3.1-jre (36 constraints: 659b358d) com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava (1 constraints: bd17c918) com.google.j2objc:j2objc-annotations:3.0.0 (1 constraints: 150aeab4) com.palantir.common:streams:2.3.0 (1 constraints: 0705fe35) com.palantir.conjure:conjure-api-objects:4.36.0 (3 constraints: 9f27d521) com.palantir.conjure:conjure-generator-common:4.36.0 (2 constraints: 05146783) -com.palantir.conjure.java.api:errors:2.53.0 (6 constraints: dc7150a5) -com.palantir.conjure.java.api:service-config:2.53.0 (6 constraints: 3166aed4) -com.palantir.conjure.java.api:ssl-config:2.53.0 (5 constraints: af4fdb29) +com.palantir.conjure.java.api:errors:2.54.0 (6 constraints: df71bda8) +com.palantir.conjure.java.api:service-config:2.54.0 (6 constraints: 3366ded6) +com.palantir.conjure.java.api:ssl-config:2.54.0 (5 constraints: b24f2a2c) com.palantir.conjure.java.runtime:client-config:8.9.0 (6 constraints: 9e6a9c2e) com.palantir.conjure.java.runtime:conjure-java-jackson-optimizations:8.9.0 (1 constraints: 571c4b88) com.palantir.conjure.java.runtime:conjure-java-jackson-serialization:8.9.0 (4 constraints: 6b46b1dd) @@ -53,7 +53,7 @@ com.palantir.tracing:tracing-api:6.18.0 (6 constraints: 9955cf58) com.palantir.tracing:tracing-undertow:6.18.0 (1 constraints: 41055f3b) com.palantir.tritium:tritium-api:0.88.0 (2 constraints: 411f7abe) com.palantir.tritium:tritium-core:0.88.0 (1 constraints: 481060a2) -com.palantir.tritium:tritium-ids:0.88.0 (1 constraints: d30fb896) +com.palantir.tritium:tritium-ids:0.95.0 (1 constraints: d10fb396) com.palantir.tritium:tritium-metrics:0.88.0 (5 constraints: 94587234) com.palantir.tritium:tritium-registry:0.88.0 (10 constraints: 8dcb7e9e) com.squareup:javapoet:1.13.0 (2 constraints: 2b113eee) @@ -68,7 +68,7 @@ org.apache.commons:commons-lang3:3.14.0 (4 constraints: 483074c5) org.apache.httpcomponents.client5:httpclient5:5.3.1 (1 constraints: cd13996e) org.apache.httpcomponents.core5:httpcore5:5.2.4 (3 constraints: 6639870e) org.apache.httpcomponents.core5:httpcore5-h2:5.2.4 (1 constraints: 3f130d3c) -org.checkerframework:checker-qual:3.42.0 (4 constraints: 58379941) +org.checkerframework:checker-qual:3.43.0 (4 constraints: 5937f041) org.derive4j:derive4j-annotation:1.1.1 (1 constraints: 0505f435) org.eclipse.collections:eclipse-collections:11.1.0 (1 constraints: 35052f3b) org.eclipse.collections:eclipse-collections-api:11.1.0 (2 constraints: 12187676) @@ -107,7 +107,7 @@ com.helger:profiler:1.1.1 (1 constraints: e21053b8) com.netflix.feign:feign-core:8.18.0 (3 constraints: de3f76e0) com.netflix.feign:feign-jackson:8.18.0 (1 constraints: c718909e) com.palantir.conjure:conjure-core:4.36.0 (1 constraints: 3f05553b) -com.palantir.conjure.java.api:test-utils:2.53.0 (1 constraints: 3b05453b) +com.palantir.conjure.java.api:test-utils:2.54.0 (1 constraints: 3d054b3b) com.palantir.conjure.java.runtime:conjure-java-annotations:8.9.0 (1 constraints: 9718cf85) com.palantir.conjure.java.runtime:conjure-java-jaxrs-client:8.9.0 (1 constraints: 11052836) com.palantir.conjure.java.runtime:conjure-java-jersey-server:8.9.0 (1 constraints: 11052836) @@ -145,12 +145,12 @@ javax.servlet:javax.servlet-api:4.0.1 (8 constraints: e381a7be) javax.validation:validation-api:2.0.1.Final (13 constraints: e5c5c6d1) javax.xml.bind:jaxb-api:2.3.1 (3 constraints: 0242851b) junit:junit:4.13.2 (2 constraints: de1ce71a) -net.bytebuddy:byte-buddy:1.14.15 (2 constraints: f316be66) -net.bytebuddy:byte-buddy-agent:1.14.15 (1 constraints: 760bb5e9) +net.bytebuddy:byte-buddy:1.14.18 (2 constraints: f716b767) +net.bytebuddy:byte-buddy-agent:1.14.12 (1 constraints: 730bb2e9) net.sourceforge.argparse4j:argparse4j:0.8.1 (2 constraints: da1b0490) org.apache.commons:commons-text:1.10.0 (1 constraints: 3b114ace) org.apiguardian:apiguardian-api:1.1.2 (5 constraints: 105480ac) -org.assertj:assertj-core:3.25.3 (3 constraints: ee2a9aa1) +org.assertj:assertj-core:3.26.3 (3 constraints: ef2ad6a1) org.eclipse.jetty:jetty-continuation:9.4.49.v20220914 (1 constraints: e21047f8) org.eclipse.jetty:jetty-http:9.4.49.v20220914 (5 constraints: 9f57507b) org.eclipse.jetty:jetty-io:9.4.49.v20220914 (7 constraints: 4d741ea9) @@ -187,8 +187,8 @@ org.junit.jupiter:junit-jupiter-params:5.10.2 (2 constraints: 6e13725b) org.junit.platform:junit-platform-commons:1.10.2 (2 constraints: 2f218183) org.junit.platform:junit-platform-engine:1.10.2 (1 constraints: d410e6c4) org.jvnet:animal-sniffer-annotation:1.0 (1 constraints: f20b95eb) -org.mockito:mockito-core:5.12.0 (3 constraints: 7625be53) -org.mockito:mockito-junit-jupiter:5.12.0 (1 constraints: 3905443b) +org.mockito:mockito-core:5.11.0 (2 constraints: 2a143786) +org.mockito:mockito-junit-jupiter:5.11.0 (1 constraints: 3905443b) org.objenesis:objenesis:3.3 (1 constraints: b20a14bd) org.opentest4j:opentest4j:1.3.0 (2 constraints: cf209249) org.ow2.asm:asm:9.1 (1 constraints: 040aa7a4) diff --git a/versions.props b/versions.props index 709422e87..3fe3aac90 100644 --- a/versions.props +++ b/versions.props @@ -6,7 +6,7 @@ com.google.code.findbugs:jsr305 = 3.0.2 com.google.guava:guava = 33.1.0-jre com.google.testing.compile:compile-testing = 0.21.0 com.palantir.common:streams = 2.3.0 -com.palantir.conjure.java.api:* = 2.52.0 +com.palantir.conjure.java.api:* = 2.54.0 com.palantir.conjure.java.runtime:* = 8.7.0 com.palantir.conjure.verification:* = 0.19.0 com.palantir.conjure:* = 4.36.0