Repository: quarkusio/quarkus
Issue: CORS Allowed-Headers shouldn't be case sensitive

**Describe the bug**
CORS config `quarkus.http.cors.headers` shouldn't be case sensitive, since [HTTP headers are case-insensitive](https://tools.ietf.org/html/rfc2616#section-4.2).

**Expected behavior**
Setting  `quarkus.http.cors.headers=Content-Type` on `application.properties` should cause [pre-flight `OPTIONS` http requests](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) with `Access-Control-Request-Headers: content-type` to return a response with header `Access-Control-Allow-Headers: content-type`.

**Actual behavior**
Lower case allow-headers `content-type` is not returned when client asks for camel-case `Content-Type`.

**To Reproduce**
Steps to reproduce the behavior:
1. [Configure the Quarkus CORS filter](https://quarkus.io/guides/undertow-reference#cors-filter) with `quarkus.http.cors.headers=Content-Typet`
2. Make a pre-flight `OPTIONS` request with the header `Access-Control-Request-Headers: content-type`
3. Notice the missing `Access-Control-Allow-Headers: content-type` in the response headers.

**Configuration**
```properties
#CORS
quarkus.http.cors=true
quarkus.http.cors.headers=Content-Type
```
**Environment (please complete the following information):**
 - Quarkus version or git rev: 0.21.1

**Additional context**
HTTP headers are case-insensitive, from [RFC 2616 -   Hypertext Transfer Protocol -- HTTP/1.1, section 4.2 - Message Headers](https://tools.ietf.org/html/rfc2616#section-4.2),.



Changed files:
extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/cors/CORSFilter.java

diff --git a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/cors/CORSFilter.java b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/cors/CORSFilter.java
index 05810ec5170..74460bcda01 100644
--- a/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/cors/CORSFilter.java
+++ b/extensions/vertx-web/runtime/src/main/java/io/quarkus/vertx/web/runtime/cors/CORSFilter.java
@@ -1,6 +1,9 @@
 package io.quarkus.vertx.web.runtime.cors;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import io.vertx.core.Handler;
@@ -12,6 +15,8 @@
 
 public class CORSFilter implements Handler<RoutingContext> {
 
+    private static final Pattern COMMA_SEPARATED_SPLIT_REGEX = Pattern.compile("\\\\s*,\\\\s*");
+
     // This is set in the recorder at runtime.
     // Must be static because the filter is created(deployed) at build time and runtime config is still not available
     final CORSConfig corsConfig;
@@ -20,34 +25,46 @@ public CORSFilter(CORSConfig corsConfig) {
         this.corsConfig = corsConfig;
     }
 
-    private void processRequestedHeaders(HttpServerResponse response, String requestedHeaders) {
+    private void processRequestedHeaders(HttpServerResponse response, String allowHeadersValue) {
         if (corsConfig.headers.isEmpty()) {
-            response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestedHeaders);
+            response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, allowHeadersValue);
         } else {
-            final String validRequestHeaders = corsConfig.headers
-                    .stream()
-                    .filter(requestedHeaders::contains)
-                    .collect(Collectors.joining(","));
+            List<String> requestedHeaders = new ArrayList<>();
+            for (String requestedHeader : COMMA_SEPARATED_SPLIT_REGEX.split(allowHeadersValue)) {
+                requestedHeaders.add(requestedHeader.toLowerCase());
+            }
+
+            List<String> validRequestedHeaders = new ArrayList<>();
+            for (String configHeader : corsConfig.headers) {
+                if (requestedHeaders.contains(configHeader.toLowerCase())) {
+                    validRequestedHeaders.add(configHeader);
+                }
+            }
 
-            if (!validRequestHeaders.isEmpty()) {
-                response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, validRequestHeaders);
+            if (!validRequestedHeaders.isEmpty()) {
+                response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, String.join(",", validRequestedHeaders));
             }
         }
     }
 
-    private void processMethods(HttpServerResponse response, String requestedMethods) {
+    private void processMethods(HttpServerResponse response, String allowMethodsValue) {
         if (corsConfig.methods.isEmpty()) {
-            response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestedMethods);
+            response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, allowMethodsValue);
         } else {
+            List<String> requestedMethods = new ArrayList<>();
+            for (String requestedMethod : COMMA_SEPARATED_SPLIT_REGEX.split(allowMethodsValue)) {
+                requestedMethods.add(requestedMethod.toLowerCase());
+            }
 
-            final String validRequestedMethods = corsConfig.methods
-                    .stream()
-                    .filter(method -> requestedMethods.contains(method.name()))
-                    .map(HttpMethod::name)
-                    .collect(Collectors.joining(","));
+            List<String> validRequestedMethods = new ArrayList<>();
+            for (HttpMethod configMethod : corsConfig.methods) {
+                if (requestedMethods.contains(configMethod.name().toLowerCase())) {
+                    validRequestedMethods.add(configMethod.name());
+                }
+            }
 
             if (!validRequestedMethods.isEmpty()) {
-                response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, validRequestedMethods);
+                response.headers().set(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, String.join(",", validRequestedMethods));
             }
         }
     }