Skip to content

Commit 62b0b3d

Browse files
committed
Handle CORS preflight (OPTIONS) requests up-front
1 parent 1b37e5f commit 62b0b3d

1 file changed

Lines changed: 37 additions & 0 deletions

File tree

src/main/java/org/tinystruct/handler/HttpRequestHandler.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)