@@ -61,6 +61,43 @@ public HttpRequestHandler(Configuration<String> configuration) {
6161
6262 @ Override
6363 protected void channelRead0 (ChannelHandlerContext ctx , FullHttpRequest original ) {
64+ // Handle CORS preflight (OPTIONS) requests up-front: these have no body.
65+ if (original .method () == HttpMethod .OPTIONS ) {
66+ // CORS preflight handling with configurability
67+ String origin = original .headers ().get (HttpHeaderNames .ORIGIN );
68+ String acrMethod = original .headers ().get (HttpHeaderNames .ACCESS_CONTROL_REQUEST_METHOD );
69+ String acrHeaders = original .headers ().get (HttpHeaderNames .ACCESS_CONTROL_REQUEST_HEADERS );
70+
71+ // Allow origins: prefer explicit setting, otherwise echo Origin or wildcard
72+ String allowOrigin = configuration .getOrDefault ("cors.allowed.origins" , origin != null ? origin : "*" );
73+ FullHttpResponse response = new DefaultFullHttpResponse (HTTP_1_1 , OK );
74+ response .headers ().set (HttpHeaderNames .ACCESS_CONTROL_ALLOW_ORIGIN , allowOrigin );
75+ // Make responses vary by Origin when echoing it
76+ if (origin != null ) {
77+ response .headers ().set (HttpHeaderNames .VARY , "Origin" );
78+ }
79+
80+ // Allow methods: prefer configured list, otherwise echo requested or use sensible defaults
81+ String allowMethods = configuration .getOrDefault ("cors.allowed.methods" , acrMethod != null ? acrMethod : "GET,POST,PUT,DELETE,OPTIONS" );
82+ response .headers ().set (HttpHeaderNames .ACCESS_CONTROL_ALLOW_METHODS , allowMethods );
83+
84+ // Allow headers: prefer configured list, otherwise echo requested or common headers
85+ String allowHeaders = configuration .getOrDefault ("cors.allowed.headers" , acrHeaders != null ? acrHeaders : "Content-Type,Authorization" );
86+ response .headers ().set (HttpHeaderNames .ACCESS_CONTROL_ALLOW_HEADERS , allowHeaders );
87+
88+ // Allow credentials if explicitly enabled in settings
89+ if ("true" .equalsIgnoreCase (configuration .get ("cors.allow.credentials" ))) {
90+ response .headers ().set (HttpHeaderNames .ACCESS_CONTROL_ALLOW_CREDENTIALS , "true" );
91+ }
92+
93+ // Cache the preflight response for a configurable duration (seconds)
94+ String maxAge = configuration .getOrDefault ("cors.preflight.maxage" , "3600" );
95+ response .headers ().set (HttpHeaderNames .ACCESS_CONTROL_MAX_AGE , maxAge );
96+
97+ response .headers ().set (HttpHeaderNames .CONTENT_LENGTH , 0 );
98+ ctx .writeAndFlush (response ).addListener (ChannelFutureListener .CLOSE );
99+ return ;
100+ }
64101 // Decide whether to close the connection or not.
65102 boolean keepAlive = HttpUtil .isKeepAlive (original );
66103 boolean ssl = Boolean .parseBoolean (configuration .getOrDefault ("ssl.enabled" , "false" ));
0 commit comments