Repository: quarkusio/quarkus
Issue: NullPointerException during http post request when quarkus-csrf-reactive extension is added to a project

### Describe the bug

Adding the quarkus-csrf-reactive extension to a project leads to a NPE during a post request when CsrfRequestResponseReactiveFilter is checking the media type.

```
2023-08-09 13:47:44,725 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (vert.x-eventloop-thread-1) HTTP Request to /hello failed, error id: c34caf8f-fc59-48c7-9cdd-cb2b1041ef1c-13: java.lang.NullPointerException: Cannot invoke "jakarta.ws.rs.core.MediaType.getType()" because "contentType" is null
	at io.quarkus.csrf.reactive.runtime.CsrfRequestResponseReactiveFilter.isMatchingMediaType(CsrfRequestResponseReactiveFilter.java:152)
	at io.quarkus.csrf.reactive.runtime.CsrfRequestResponseReactiveFilter.filter(CsrfRequestResponseReactiveFilter.java:100)
	at io.quarkus.csrf.reactive.runtime.CsrfRequestResponseReactiveFilter$GeneratedServerRequestFilter$filter.filter(Unknown Source)
	at io.quarkus.csrf.reactive.runtime.CsrfRequestResponseReactiveFilter$GeneratedServerRequestFilter$filter_ClientProxy.filter(Unknown Source)
	at org.jboss.resteasy.reactive.server.handlers.ResourceRequestFilterHandler.handle(ResourceRequestFilterHandler.java:48)
	at io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext.invokeHandler(QuarkusResteasyReactiveRequestContext.java:131)
	at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:145)
	at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:48)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:23)
	at org.jboss.resteasy.reactive.server.vertx.ResteasyReactiveVertxHandler.handle(ResteasyReactiveVertxHandler.java:10)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$1.handle(HttpServerCommonHandlers.java:58)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$1.handle(HttpServerCommonHandlers.java:36)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveRecorder$13.handle(ResteasyReactiveRecorder.java:382)
	at io.quarkus.resteasy.reactive.server.runtime.ResteasyReactiveRecorder$13.handle(ResteasyReactiveRecorder.java:375)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup.handleHotReplacementRequest(VertxHttpHotReplacementSetup.java:135)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:434)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$5.handle(VertxHttpRecorder.java:430)
	at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
	at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
	at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:141)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:68)
	at io.vertx.ext.web.impl.RouterImpl.handle(RouterImpl.java:37)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$2.handle(HttpServerCommonHandlers.java:82)
	at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$2.handle(HttpServerCommonHandlers.java:65)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:177)
	at io.quarkus.vertx.http.runtime.VertxHttpRecorder$1.handle(VertxHttpRecorder.java:153)
	at io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup$5.handle(VertxHttpHotReplacementSetup.java:194)
	at io.quarkus.vertx.http.runtime.devmode.VertxHttpHotReplacementSetup$5.handle(VertxHttpHotReplacementSetup.java:185)
	at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141)
	at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:54)
	at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174)
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167)
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569)
	at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
	at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)
```

### Expected behavior

Adding the extension doesn't lead to a NPEs during HTTP POST Requests.

### How to Reproduce?

Reproducer: https://github.com/tamaro-skaljic/quarkus-csrf-np-bug/

Steps to reproduce behaviour:
1. Start Quarkus in Dev Mode
2. Visit http://localhost:8080/q/dev-ui/io.quarkus.quarkus-smallrye-openapi/swagger-ui
3. Do a post request to /hello through it

### Additional information

If further configuration is required so that the extension does not produce errors, the build should fail.

Changed files:
extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java

diff --git a/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java b/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java
index 8fcba71e714..4c5eaa15177 100644
--- a/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java
+++ b/extensions/csrf-reactive/runtime/src/main/java/io/quarkus/csrf/reactive/runtime/CsrfRequestResponseReactiveFilter.java
@@ -97,15 +97,16 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi
         } else if (config.verifyToken) {
             // unsafe HTTP method, token is required
 
-            if (!isMatchingMediaType(requestContext.getMediaType(), MediaType.APPLICATION_FORM_URLENCODED_TYPE)
-                    && !isMatchingMediaType(requestContext.getMediaType(), MediaType.MULTIPART_FORM_DATA_TYPE)) {
+            MediaType mediaType = requestContext.getMediaType();
+            if (!isMatchingMediaType(mediaType, MediaType.APPLICATION_FORM_URLENCODED_TYPE)
+                    && !isMatchingMediaType(mediaType, MediaType.MULTIPART_FORM_DATA_TYPE)) {
                 if (config.requireFormUrlEncoded) {
-                    LOG.debugf("Request has the wrong media type: %s", requestContext.getMediaType().toString());
+                    LOG.debugf("Request has the wrong media type: %s", mediaType);
                     requestContext.abortWith(badClientRequest());
                     return;
                 } else {
-                    LOG.debugf("Request has the  media type: %s, skipping the token verification",
-                            requestContext.getMediaType().toString());
+                    LOG.debugf("Request has the media type: %s, skipping the token verification",
+                            mediaType);
                     return;
                 }
             }
@@ -148,7 +149,16 @@ public void filter(ResteasyReactiveContainerRequestContext requestContext, Routi
         }
     }
 
+    /**
+     * Compares if {@link MediaType} matches the expected type.
+     * <p>
+     * Note: isCompatible is taking wildcards, which is why we individually compare types and subtypes,
+     * so if someone sends a <code>Content-Type: *</code> it will be marked as compatible which is a problem
+     */
     private static boolean isMatchingMediaType(MediaType contentType, MediaType expectedType) {
+        if (contentType == null) {
+            return (expectedType == null);
+        }
         return contentType.getType().equals(expectedType.getType())
                 && contentType.getSubtype().equals(expectedType.getSubtype());
     }