Skip to content

Conversation

@morteza-69
Copy link

Fixes #5103

Problem
In Ktor 3.3.0, the CORS plugin does not correctly include the default HTTP methods (GET, POST, HEAD) in the Access-Control-Allow-Methods response header.

Current implementation in CORSConfig builds the header value like this:

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

Since CorsDefaultMethods = { GET, POST, HEAD }, these methods are filtered out and not included in the final header.
This results in browsers seeing an incomplete Access-Control-Allow-Methods (often only OPTIONS), which breaks CORS preflight validation.

Solution
Replaced filtering logic with a distinct collection:

val methodsListHeaderValue = methods.distinct() .map { it.value } .sorted() .joinToString(", ")
This ensures that the default methods (GET, POST, HEAD) are included along with any additional configured methods.

Tests
Added a new test testPreflightIncludesDefaultMethods to verify that:

GET, POST, and HEAD are always present in Access-Control-Allow-Methods

Custom allowed methods (e.g., PUT) are also included

Before the fix, this test fails. After the fix, it passes ✅

Environment

Ktor version: 3.3.0

Module: ktor-server-cors

JVM: 17

OS: Ubuntu 22.04 / Windows 11

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 23, 2025

Walkthrough

Updated CORS methods header construction to include all configured HTTP methods (including defaults) in a sorted, comma-separated list. Added a test to verify that preflight responses include default methods when an explicit method is allowed.

Changes

Cohort / File(s) Summary
CORS header logic update
ktor-server/ktor-server-plugins/ktor-server-cors/common/src/io/ktor/server/plugins/cors/CORS.kt
Changed methodsListHeaderValue to use methods.distinct().map { it.value }.sorted().joinToString(", "), ensuring default methods are included in Access-Control-Allow-Methods.
Tests
ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt
Added test testPreflightIncludesDefaultMethods asserting preflight includes GET, POST, HEAD, and PUT when PUT is explicitly allowed.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested reviewers

  • osipxd
  • bjhham
  • e5l

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly and concisely describes the primary change: fixing the CORS plugin so the Access-Control-Allow-Methods header includes default methods (GET, POST, HEAD). It directly matches the code change and the added test in the changeset and is specific enough for reviewers to understand the intent at a glance.
Linked Issues Check ✅ Passed The code change replaces the filter that removed CorsDefaultMethods with a distinct-based collection and the new test verifies default methods are present in preflight responses, which directly satisfies the linked issue #5103 objectives to include GET, POST, and HEAD alongside configured methods in Access-Control-Allow-Methods. The change is limited to header construction logic and a unit test, addressing the coding requirements specified by the issue.
Out of Scope Changes Check ✅ Passed The modifications are limited to the CORS plugin implementation and its tests (CORS.kt and CORSTest), there are no signature/API changes or edits to unrelated modules, so no out-of-scope changes are evident in the provided summary.
Description Check ✅ Passed The PR description documents the problem, references the linked issue, explains the code change and includes test and environment details, so it largely follows the template's Motivation and Solution requirements; however the explicit "Subsystem" heading from the template is not present even though the module is mentioned elsewhere. Overall the description is sufficient to understand why the change was made and how it was implemented.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

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

1596-1622: LGTM + make tokenization resilient to formatting

Test correctly covers the regression. Minor: split by comma and trim to avoid coupling to a specific spacing in the header.

-        val allowMethods = response.headers[HttpHeaders.AccessControlAllowMethods]?.split(", ")?.toSet()
+        val allowMethods = response.headers[HttpHeaders.AccessControlAllowMethods]
+            ?.split(',')
+            ?.map { it.trim() }
+            ?.toSet()
ktor-server/ktor-server-plugins/ktor-server-cors/common/src/io/ktor/server/plugins/cors/CORS.kt (1)

57-60: Good fix; drop redundant distinct()

Including default methods in the header is correct. methods is already a Set, so distinct() is redundant.

-    val methodsListHeaderValue = methods.distinct()
-        .map { it.value }
+    val methodsListHeaderValue = methods
+        .map { it.value }
         .sorted()
         .joinToString(", ")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af24a5a and a3af910.

📒 Files selected for processing (2)
  • ktor-server/ktor-server-plugins/ktor-server-cors/common/src/io/ktor/server/plugins/cors/CORS.kt (1 hunks)
  • ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{kt,kts}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{kt,kts}: Follow Kotlin official style guide for all Kotlin source and build scripts
Use star imports for io.ktor.* packages
Max line length is 120 characters
Indent with 4 spaces in Kotlin code
Include a copyright header in new Kotlin files

Files:

  • ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt
  • ktor-server/ktor-server-plugins/ktor-server-cors/common/src/io/ktor/server/plugins/cors/CORS.kt
**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.kt: Document all public Kotlin APIs, including parameters, return types, and exceptions
Annotate internal APIs with @internalapi
Follow Kotlin error-handling conventions and use specific Ktor exceptions

Files:

  • ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/plugins/CORSTest.kt
  • ktor-server/ktor-server-plugins/ktor-server-cors/common/src/io/ktor/server/plugins/cors/CORS.kt

Copy link
Contributor

@bjhham bjhham left a comment

Choose a reason for hiding this comment

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

Yep, this sounds like how it ought to work:

By default, the CORS plugin allows the GET, POST and HEAD HTTP methods. To add additional methods, use the allowMethod function.

Thanks for the fix.

Could you please run ./gradlew formatKotlin to get past the linter check?

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