Skip to content

Fix default methods filtering in CORS plugin (#5103)#5633

Open
Hasan mohamed (HMH-3080) wants to merge 3 commits into
ktorio:mainfrom
HMH-3080:fix/cors-default-methods-issue-5103
Open

Fix default methods filtering in CORS plugin (#5103)#5633
Hasan mohamed (HMH-3080) wants to merge 3 commits into
ktorio:mainfrom
HMH-3080:fix/cors-default-methods-issue-5103

Conversation

@HMH-3080

Copy link
Copy Markdown

Fixes #5103

This PR ensures that default HTTP methods (GET, POST, HEAD) are included explicitly in the Access-Control-Allow-Methods header instead of being filtered out. Modern browsers strictly require these methods to be present in the preflight (OPTIONS) response header when custom configurations (like non-simple Content-Types) trigger a preflight validation.

Changes :

  • Removed .filterNot { it in CORSConfig.CorsDefaultMethods } from methodsListHeaderValue computation.
  • Added a dedicated unit test testDefaultMethodsIncludedInAllowMethodsHeader to verify the fix.

@coderabbitai

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d25388d9-885b-4b7c-a62f-a0feda403bb0

📥 Commits

Reviewing files that changed from the base of the PR and between 3bc2cce and 067b501.

📒 Files selected for processing (1)
  • ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt

📝 Walkthrough

Walkthrough

This PR updates CORS preflight header generation to format the plugin's full methods set for Access-Control-Allow-Methods and adds a test asserting an OPTIONS preflight returns the configured method in that header.

Changes

CORS Preflight Allow-Methods Header

Layer / File(s) Summary
CORS preflight Allow-Methods header fix and validation
ktor-server/ktor-server-plugins/ktor-server-cors/common/src/io/ktor/server/plugins/cors/CORS.kt, ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt
Compute Access-Control-Allow-Methods from the plugin's full methods set (removed filtering of default methods) and added a test that sends an OPTIONS preflight and verifies the header includes POST.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically identifies the main change: fixing default methods filtering in the CORS plugin with a reference to the issue number.
Description check ✅ Passed The PR description follows the template structure with subsystem context, clear motivation referencing issue #5103, and a detailed solution explaining the fix and test addition.
Linked Issues check ✅ Passed The PR fully addresses the requirements from issue #5103: it removes the problematic filterNot clause that excluded default methods, includes all configured methods in the Access-Control-Allow-Methods header, and adds a test verifying the expected behavior.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing issue #5103: one targeted fix to CORS.kt and one dedicated test in CORSTest.kt, with no extraneous modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt (1)

1596-1622: ⚡ Quick win

Strengthen test coverage by verifying all default methods are included.

The test name suggests comprehensive validation of default methods (GET, POST, HEAD) being included in the Access-Control-Allow-Methods header, but currently only verifies that POST is present. Since the PR objective is to fix default methods being filtered out, the test should explicitly assert that all three default CORS methods appear in the header.

🧪 Suggested enhancement
 assertEquals(HttpStatusCode.OK, response.status)
 val allowMethodsHeader = response.headers[HttpHeaders.AccessControlAllowMethods]
 
 assertNotNull(allowMethodsHeader, "Access-Control-Allow-Methods header should not be null")
+assertTrue(
+    allowMethodsHeader.contains("GET"),
+    "Expected Access-Control-Allow-Methods to include GET, but got: $allowMethodsHeader"
+)
+assertTrue(
+    allowMethodsHeader.contains("HEAD"),
+    "Expected Access-Control-Allow-Methods to include HEAD, but got: $allowMethodsHeader"
+)
 assertTrue(
     allowMethodsHeader.contains("POST"),
     "Expected Access-Control-Allow-Methods to include POST, but got: $allowMethodsHeader"
 )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt`
around lines 1596 - 1622, Update the
testDefaultMethodsIncludedInAllowMethodsHeader test to assert that the
Access-Control-Allow-Methods header contains all default CORS methods (GET,
POST, HEAD) rather than only POST: after obtaining allowMethodsHeader from
response.headers[HttpHeaders.AccessControlAllowMethods], split and trim the
header values (or use contains checks) and add assertions that "GET", "POST",
and "HEAD" are present; keep existing setup (install(CORS) {
allowMethod(HttpMethod.Post); anyHost() }, routing/post handler, and OPTIONS
request) and only extend the assertions in this test to verify all three
methods.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt`:
- Around line 1596-1622: Update the
testDefaultMethodsIncludedInAllowMethodsHeader test to assert that the
Access-Control-Allow-Methods header contains all default CORS methods (GET,
POST, HEAD) rather than only POST: after obtaining allowMethodsHeader from
response.headers[HttpHeaders.AccessControlAllowMethods], split and trim the
header values (or use contains checks) and add assertions that "GET", "POST",
and "HEAD" are present; keep existing setup (install(CORS) {
allowMethod(HttpMethod.Post); anyHost() }, routing/post handler, and OPTIONS
request) and only extend the assertions in this test to verify all three
methods.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 325e4e76-d79d-4fab-82b7-5440b79e1f55

📥 Commits

Reviewing files that changed from the base of the PR and between eac67e8 and 5149018.

📒 Files selected for processing (2)
  • ktor-server/ktor-server-plugins/ktor-server-cors/common/src/io/ktor/server/plugins/cors/CORS.kt
  • ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt

@bjhham Bruce Hamilton (bjhham) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Please run ./gradlew formatKotlin and submit the result to make the linter happy.

@HMH-3080

Copy link
Copy Markdown
Author

Bruce Hamilton (@bjhham)
Done! Ran ./gradlew formatKotlin, fixed the unexpected blank line, and pushed the updated code. Thanks for the review!

@bjhham Bruce Hamilton (bjhham) left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed there was already a PR submitted for this a while ago 😅

#5104

@HMH-3080

Copy link
Copy Markdown
Author

Bruce Hamilton (@bjhham)

Thanks for pointing that out! I looked into #5104 and noticed it’s been open since last year. Also, it introduces an unnecessary .sorted() call on every preflight request computation, which adds a tiny but avoidable overhead.

My PR keeps the code simpler, includes a robust unit test for this case, and I've already formatted the code as you requested. I’d love it if we could move forward with this cleaner implementation if possible! 🙌

.map { it.value }
.sorted()
.joinToString(", ")
val methodsListHeaderValue = methods.map { it.value }.sorted().joinToString(", ")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I noticed in the other PR is they included a distinct() here - wondering if we'll end up with duplicates?

Comment on lines +1618 to +1621
assertTrue(
allowMethodsHeader.contains("POST"),
"Expected Access-Control-Allow-Methods to include POST, but got: $allowMethodsHeader"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have an assertEquals here to ensure the default methods are included as well in a predictable way.

@HMH-3080

Copy link
Copy Markdown
Author

Bruce Hamilton (@bjhham)
You're absolutely right to wonder about that! However, since the underlying methods collection in CORSConfig is backed by a HashSet, the items are already guaranteed to be unique. Adding .distinct() would just introduce an unnecessary overhead.

As for the predictability, I agree. I will add back the .sorted() call to ensure a consistent header value order, and update the test case to use assertEquals with the expected full string. Pushing the updates shortly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CORS Plugin: Access-Control-Allow-Methods header does not include default methods (GET, POST, HEAD)

2 participants