httpHeaders, InputStream entityStream)
+ throws IOException, WebApplicationException {
+ // unfortunately we don't do much here to avoid the file leak
+ // however this should never be called in a real world scenario
+ return FileBodyHandler.doRead(httpHeaders, entityStream, Files.createTempFile(PREFIX, SUFFIX).toFile()).toPath();
+ }
+
+ static Path createFile(ServerRequestContext context) throws IOException {
+ RuntimeConfiguration.Body runtimeBodyConfiguration = ResteasyReactiveRecorder.getCurrentDeployment()
+ .getRuntimeConfiguration().body();
+ boolean deleteUploadedFilesOnEnd = runtimeBodyConfiguration.deleteUploadedFilesOnEnd();
+ String uploadsDirectoryStr = runtimeBodyConfiguration.uploadsDirectory();
+ Path uploadDirectory = Paths.get(uploadsDirectoryStr);
+ try {
+ Files.createDirectories(uploadDirectory);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+
+ Path file = Files.createTempFile(uploadDirectory, PREFIX, SUFFIX);
+ if (deleteUploadedFilesOnEnd) {
+ context.registerCompletionCallback(new CompletionCallback() {
+ @Override
+ public void onComplete(Throwable throwable) {
+ ResteasyReactiveRecorder.EXECUTOR_SUPPLIER.get().execute(new Runnable() {
+ @Override
+ public void run() {
+ if (Files.exists(file)) {
+ try {
+ Files.delete(file);
+ } catch (NoSuchFileException e) { // ignore
+ } catch (IOException e) {
+ log.error("Cannot remove uploaded file " + file, e);
+ }
+ }
+ }
+ });
+ }
+ });
+ }
+ return file;
+ }
+}
diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/proxy/TrustedForwarderProxyTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/proxy/TrustedForwarderProxyTest.java
index b7e344d8d73c1..d267b617b99d2 100644
--- a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/proxy/TrustedForwarderProxyTest.java
+++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/proxy/TrustedForwarderProxyTest.java
@@ -30,4 +30,20 @@ public void testHeadersAreUsed() {
.then()
.body(Matchers.equalTo("http|somehost2|backend2:5555|/path|http://somehost2/path"));
}
+
+ /**
+ * As described on https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Forwarded,
+ * the syntax should be case-insensitive.
+ *
+ * Kong, for example, uses `Proto` instead of `proto` and `For` instead of `for`.
+ */
+ @Test
+ public void testHeadersAreUsedWhenUsingCasedCharacters() {
+ RestAssured.given()
+ .header("Forwarded", "Proto=http;For=backend2:5555;Host=somehost2")
+ .get("/path")
+ .then()
+ .body(Matchers.equalTo("http|somehost2|backend2:5555|/path|http://somehost2/path"));
+ }
}
diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ForwardedParser.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ForwardedParser.java
index 1b11431a81c7f..b768c4c6cddfc 100644
--- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ForwardedParser.java
+++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/ForwardedParser.java
@@ -42,9 +42,9 @@ class ForwardedParser {
private static final AsciiString X_FORWARDED_PORT = AsciiString.cached("X-Forwarded-Port");
private static final AsciiString X_FORWARDED_FOR = AsciiString.cached("X-Forwarded-For");
- private static final Pattern FORWARDED_HOST_PATTERN = Pattern.compile("host=\"?([^;,\"]+)\"?");
- private static final Pattern FORWARDED_PROTO_PATTERN = Pattern.compile("proto=\"?([^;,\"]+)\"?");
- private static final Pattern FORWARDED_FOR_PATTERN = Pattern.compile("for=\"?([^;,\"]+)\"?");
+ private static final Pattern FORWARDED_HOST_PATTERN = Pattern.compile("host=\"?([^;,\"]+)\"?", Pattern.CASE_INSENSITIVE);
+ private static final Pattern FORWARDED_PROTO_PATTERN = Pattern.compile("proto=\"?([^;,\"]+)\"?", Pattern.CASE_INSENSITIVE);
+ private static final Pattern FORWARDED_FOR_PATTERN = Pattern.compile("for=\"?([^;,\"]+)\"?", Pattern.CASE_INSENSITIVE);
private final static int PORT_MIN_VALID_VALUE = 0;
private final static int PORT_MAX_VALID_VALUE = 65535;
diff --git a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java
index f665300bc4444..5a86f367a6992 100644
--- a/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java
+++ b/independent-projects/arc/processor/src/main/java/io/quarkus/arc/processor/SubclassGenerator.java
@@ -343,7 +343,7 @@ public String apply(List keys) {
applicationClassPredicate.test(bean.getBeanClass()),
funBytecode.getMethodParam(1),
funBytecode.getMethodParam(0));
- funBytecode.returnValue(ret);
+ funBytecode.returnValue(ret != null ? ret : funBytecode.loadNull());
constructor.invokeInterfaceMethod(MethodDescriptors.LIST_ADD, methodsList, fun.getInstance());
}
constructor.writeInstanceField(field.getFieldDescriptor(), constructor.getThis(), methodsList);
@@ -464,9 +464,9 @@ public String apply(List keys) {
MethodDescriptor virtualMethodDescriptor = MethodDescriptor.ofMethod(declaringClass,
originalMethodDescriptor.getName(),
decoratorMethodDescriptor.getReturnType(), decoratorMethodDescriptor.getParameterTypes());
- funcBytecode
- .returnValue(funcBytecode.invokeVirtualMethod(virtualMethodDescriptor, funDecoratorInstance,
- superParamHandles));
+ ResultHandle superResult = funcBytecode.invokeVirtualMethod(virtualMethodDescriptor, funDecoratorInstance,
+ superParamHandles);
+ funcBytecode.returnValue(superResult != null ? superResult : funcBytecode.loadNull());
} else {
ResultHandle superResult = funcBytecode.invokeVirtualMethod(forwardDescriptor, targetHandle,
superParamHandles);
diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/decorators/VoidMethodDecoratorTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/decorators/VoidMethodDecoratorTest.java
new file mode 100644
index 0000000000000..f39ed5510e120
--- /dev/null
+++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/decorators/VoidMethodDecoratorTest.java
@@ -0,0 +1,69 @@
+package io.quarkus.arc.test.decorators;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jakarta.annotation.Priority;
+import jakarta.decorator.Decorator;
+import jakarta.decorator.Delegate;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.context.Dependent;
+import jakarta.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.arc.test.ArcTestContainer;
+
+public class VoidMethodDecoratorTest {
+ @RegisterExtension
+ public ArcTestContainer container = new ArcTestContainer(Performer.class, MainPerformer.class,
+ PerformerDecorator.class);
+
+ @Test
+ public void testDecoration() {
+ MainPerformer performer = Arc.container().instance(MainPerformer.class).get();
+
+ assertFalse(MainPerformer.DONE.get());
+ assertFalse(PerformerDecorator.DONE.get());
+
+ performer.doSomething();
+
+ assertTrue(MainPerformer.DONE.get());
+ assertTrue(PerformerDecorator.DONE.get());
+ }
+
+ interface Performer {
+ void doSomething();
+ }
+
+ @ApplicationScoped
+ static class MainPerformer implements Performer {
+ static final AtomicBoolean DONE = new AtomicBoolean();
+
+ @Override
+ public void doSomething() {
+ DONE.set(true);
+ }
+ }
+
+ @Dependent
+ @Priority(1)
+ @Decorator
+ static class PerformerDecorator implements Performer {
+ static final AtomicBoolean DONE = new AtomicBoolean();
+
+ @Inject
+ @Delegate
+ Performer delegate;
+
+ @Override
+ public void doSomething() {
+ DONE.set(true);
+ delegate.doSomething();
+ }
+ }
+}
diff --git a/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/decorators/interceptor/VoidMethodInterceptorAndDecoratorTest.java b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/decorators/interceptor/VoidMethodInterceptorAndDecoratorTest.java
new file mode 100644
index 0000000000000..b17de441bd387
--- /dev/null
+++ b/independent-projects/arc/tests/src/test/java/io/quarkus/arc/test/decorators/interceptor/VoidMethodInterceptorAndDecoratorTest.java
@@ -0,0 +1,101 @@
+package io.quarkus.arc.test.decorators.interceptor;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import jakarta.annotation.Priority;
+import jakarta.decorator.Decorator;
+import jakarta.decorator.Delegate;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.context.Dependent;
+import jakarta.inject.Inject;
+import jakarta.interceptor.AroundInvoke;
+import jakarta.interceptor.Interceptor;
+import jakarta.interceptor.InterceptorBinding;
+import jakarta.interceptor.InvocationContext;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.arc.test.ArcTestContainer;
+
+public class VoidMethodInterceptorAndDecoratorTest {
+ @RegisterExtension
+ public ArcTestContainer container = new ArcTestContainer(Performer.class, MainPerformer.class,
+ PerformerDecorator.class, MyInterceptorBinding.class, MyInterceptor.class);
+
+ @Test
+ public void testDecoration() {
+ MainPerformer performer = Arc.container().instance(MainPerformer.class).get();
+
+ assertFalse(MainPerformer.DONE.get());
+ assertFalse(PerformerDecorator.DONE.get());
+ assertFalse(MyInterceptor.INTERCEPTED.get());
+
+ performer.doSomething();
+
+ assertTrue(MainPerformer.DONE.get());
+ assertTrue(PerformerDecorator.DONE.get());
+ assertTrue(MyInterceptor.INTERCEPTED.get());
+ }
+
+ interface Performer {
+ void doSomething();
+ }
+
+ @ApplicationScoped
+ @MyInterceptorBinding
+ static class MainPerformer implements Performer {
+ static final AtomicBoolean DONE = new AtomicBoolean();
+
+ @Override
+ public void doSomething() {
+ DONE.set(true);
+ }
+ }
+
+ @Dependent
+ @Priority(1)
+ @Decorator
+ static class PerformerDecorator implements Performer {
+ static final AtomicBoolean DONE = new AtomicBoolean();
+
+ @Inject
+ @Delegate
+ Performer delegate;
+
+ @Override
+ public void doSomething() {
+ DONE.set(true);
+ delegate.doSomething();
+ }
+ }
+
+ @Target({ ElementType.TYPE, ElementType.METHOD })
+ @Retention(RetentionPolicy.RUNTIME)
+ @Documented
+ @InterceptorBinding
+ public @interface MyInterceptorBinding {
+ }
+
+ @MyInterceptorBinding
+ @Priority(1)
+ @Interceptor
+ static class MyInterceptor {
+ static final AtomicBoolean INTERCEPTED = new AtomicBoolean();
+
+ @AroundInvoke
+ Object log(InvocationContext ctx) throws Exception {
+ INTERCEPTED.set(true);
+ return ctx.proceed();
+ }
+ }
+}
diff --git a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java
index 476a8f662b887..46345007ce745 100644
--- a/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java
+++ b/independent-projects/resteasy-reactive/client/runtime/src/main/java/org/jboss/resteasy/reactive/client/impl/ClientImpl.java
@@ -217,6 +217,7 @@ public void close() {
if (closeVertx) {
vertx.close();
}
+ log.debug("Client is closed");
}
void abortIfClosed() {
diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/FileBodyHandler.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/FileBodyHandler.java
index 82d2045179024..1f502c66555a2 100644
--- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/FileBodyHandler.java
+++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/providers/serialisers/FileBodyHandler.java
@@ -21,8 +21,8 @@
import org.jboss.resteasy.reactive.common.headers.HeaderUtil;
public class FileBodyHandler implements MessageBodyReader, MessageBodyWriter {
- protected static final String PREFIX = "pfx";
- protected static final String SUFFIX = "sfx";
+ public static final String PREFIX = "pfx";
+ public static final String SUFFIX = "sfx";
@Override
public boolean isReadable(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
@@ -33,16 +33,20 @@ public boolean isReadable(Class> type, Type genericType, Annotation[] annotati
public File readFrom(Class type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap httpHeaders, InputStream entityStream) throws IOException {
- File downloadedFile = Files.createTempFile(PREFIX, SUFFIX).toFile();
+ return doRead(httpHeaders, entityStream, Files.createTempFile(PREFIX, SUFFIX).toFile());
+ }
+
+ public static File doRead(MultivaluedMap httpHeaders, InputStream entityStream,
+ File file) throws IOException {
if (HeaderUtil.isContentLengthZero(httpHeaders)) {
- return downloadedFile;
+ return file;
}
- try (OutputStream output = new BufferedOutputStream(new FileOutputStream(downloadedFile))) {
+ try (OutputStream output = new BufferedOutputStream(new FileOutputStream(file))) {
entityStream.transferTo(output);
}
- return downloadedFile;
+ return file;
}
public boolean isWriteable(Class> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java
index 2248a8f7763ac..4fd59f33828db 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/core/ResteasyReactiveRequestContext.java
@@ -19,6 +19,7 @@
import jakarta.ws.rs.core.Cookie;
import jakarta.ws.rs.core.GenericEntity;
+import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.PathSegment;
import jakarta.ws.rs.core.Request;
@@ -148,6 +149,11 @@ public ResteasyReactiveRequestContext(Deployment deployment,
@Override
public abstract ServerHttpResponse serverResponse();
+ @Override
+ public HttpHeaders getRequestHeaders() {
+ return getHttpHeaders();
+ }
+
public Deployment getDeployment() {
return deployment;
}
diff --git a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerRequestContext.java b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerRequestContext.java
index 50fba8210e0da..4825de1472c11 100644
--- a/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerRequestContext.java
+++ b/independent-projects/resteasy-reactive/server/runtime/src/main/java/org/jboss/resteasy/reactive/server/spi/ServerRequestContext.java
@@ -3,6 +3,7 @@
import java.io.InputStream;
import java.io.OutputStream;
+import jakarta.ws.rs.core.HttpHeaders;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
@@ -22,5 +23,7 @@ public interface ServerRequestContext extends ResteasyReactiveCallbackContext {
ResteasyReactiveResourceInfo getResteasyReactiveResourceInfo();
+ HttpHeaders getRequestHeaders();
+
void abortWith(Response response);
}
diff --git a/independent-projects/tools/pom.xml b/independent-projects/tools/pom.xml
index f762a765eb81c..520d298317d64 100644
--- a/independent-projects/tools/pom.xml
+++ b/independent-projects/tools/pom.xml
@@ -56,7 +56,7 @@
2.15.2
4.0.1
5.9.3
- 1.24.0
+ 1.26.0
3.5.1.Final
5.3.1
3.2.1