Skip to content

KTOR-8443 Respect parameters in contentType and accept route selectors#5600

Open
fru1tworld wants to merge 4 commits into
ktorio:mainfrom
fru1tworld:fix/8443-routing-content-type-parameters
Open

KTOR-8443 Respect parameters in contentType and accept route selectors#5600
fru1tworld wants to merge 4 commits into
ktorio:mainfrom
fru1tworld:fix/8443-routing-content-type-parameters

Conversation

@fru1tworld
Copy link
Copy Markdown
Contributor

Subsystem
ktor-server

Motivation
https://youtrack.jetbrains.com/issue/KTOR-8443

Solution
Preserve Content-Type parameters when matching contentType and accept route selectors.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

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: d647fe5b-4052-4db8-bd14-76c1aea90dc6

📥 Commits

Reviewing files that changed from the base of the PR and between 14f3862 and 69bb12d.

📒 Files selected for processing (2)
  • ktor-server/ktor-server-core/common/src/io/ktor/server/routing/RouteSelector.kt
  • ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/routing/RoutingProcessingTest.kt
🚧 Files skipped from review as they are similar to previous changes (2)
  • ktor-server/ktor-server-core/common/src/io/ktor/server/routing/RouteSelector.kt
  • ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/routing/RoutingProcessingTest.kt

📝 Walkthrough

Walkthrough

This PR adds a private HeaderValue.toContentType() helper that parses header entries into ContentType excluding the q parameter; ContentTypeHeaderRouteSelector and HttpMultiAcceptRouteSelector now use it. Two tests verify matching behavior with parameters.

Changes

Content Negotiation Parameter Handling

Layer / File(s) Summary
Header Value Extension
ktor-server/ktor-server-core/common/src/io/ktor/server/routing/RouteSelector.kt
New private extension HeaderValue.toContentType() parses header values into ContentType and rebuilds parameters excluding q.
Content-Type Selector
ktor-server/ktor-server-core/common/src/io/ktor/server/routing/RouteSelector.kt
ContentTypeHeaderRouteSelector.evaluate now uses HeaderValue.toContentType() for matching parsed Content-Type entries.
Accept Selector
ktor-server/ktor-server-core/common/src/io/ktor/server/routing/RouteSelector.kt
HttpMultiAcceptRouteSelector.evaluate now uses header.toContentType() for compatibility checks instead of ContentType.parse(header.value).
Parameter Matching Tests
ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/routing/RoutingProcessingTest.kt
Added testContentTypeHeaderWithParameters and testAcceptHeaderWithParameters to verify selectors match MIME types with parameters and ignore quality (q) factors.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • ktorio/ktor#4804: Updates to HttpMultiAcceptRouteSelector evaluation logic for Accept header parsing and matching.
  • ktorio/ktor#5345: Changes to ContentTypeHeaderRouteSelector content-type matching logic.

Suggested reviewers

  • bjhham
🚥 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 PR title clearly and specifically describes the main change: respecting parameters in contentType and accept route selectors, directly matching the core implementation changes.
Description check ✅ Passed The PR description follows the required template with all key sections (Subsystem, Motivation, Solution) present, though the Solution section is concise rather than detailed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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.

Copy link
Copy Markdown
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: 1

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

683-688: ⚡ Quick win

Add one Accept regression with an extension after q.

The new test locks the q case itself, but it still misses the valid form where extra accept-ext params appear after q. Adding something like application/soap+xml; action=foo; q=0.5; trace=1 would pin the intended selector behavior and catch the current delimiter bug immediately.

🤖 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/routing/RoutingProcessingTest.kt`
around lines 683 - 688, Add a regression test variant to cover the Accept header
form that has accept-ext parameters after the q parameter; specifically, in the
RoutingProcessingTest (the existing client.get block using
header(HttpHeaders.Accept, "application/soap+xml; action=foo; q=0.5")), add
another request that uses a header like "application/soap+xml; action=foo;
q=0.5; trace=1" (or similar) and assert the same outcome
(assertEquals("matched", it.bodyAsText())); this ensures the selector logic that
parses Accept parameters handles extra accept-ext entries following q.
🤖 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.

Inline comments:
In
`@ktor-server/ktor-server-core/common/src/io/ktor/server/routing/RouteSelector.kt`:
- Around line 831-834: The current HeaderValue.toContentType() reconstructs
media parameters after a "q" token and treats "q" case-sensitively, which breaks
Accept handling; change the helper to accept a stopAtQuality: Boolean (default
false) and when stopAtQuality is true skip the "q" parameter (case-insensitive)
and stop processing any subsequent params (i.e., ignore params after the first
param whose name equals "q" ignoring case) while still folding earlier params
into ContentType.parse(value); keep existing behavior (stopAtQuality=false) for
Content-Type usage and call the new stopAtQuality=true when parsing Accept
values so accept-exts are not reinterpreted as media parameters (refer to
HeaderValue.toContentType(), params, and ContentType.parse in your changes).

---

Nitpick comments:
In
`@ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/routing/RoutingProcessingTest.kt`:
- Around line 683-688: Add a regression test variant to cover the Accept header
form that has accept-ext parameters after the q parameter; specifically, in the
RoutingProcessingTest (the existing client.get block using
header(HttpHeaders.Accept, "application/soap+xml; action=foo; q=0.5")), add
another request that uses a header like "application/soap+xml; action=foo;
q=0.5; trace=1" (or similar) and assert the same outcome
(assertEquals("matched", it.bodyAsText())); this ensures the selector logic that
parses Accept parameters handles extra accept-ext entries following q.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 99686339-b25e-4ce3-bf61-05f9d1304a53

📥 Commits

Reviewing files that changed from the base of the PR and between ccb08e9 and c0114fa.

📒 Files selected for processing (2)
  • ktor-server/ktor-server-core/common/src/io/ktor/server/routing/RouteSelector.kt
  • ktor-server/ktor-server-tests/common/test/io/ktor/tests/server/routing/RoutingProcessingTest.kt

Comment on lines +831 to +834
private fun HeaderValue.toContentType(): ContentType =
params.asSequence()
.filter { it.name != "q" }
.fold(ContentType.parse(value)) { acc, p -> acc.withParameter(p.name, p.value) }
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop reconstructing Accept media parameters after q.

This helper is now shared by both Content-Type and Accept, but Accept treats q as the boundary between media parameters and accept-exts. Filtering out only "q" means a valid header like application/soap+xml; action=foo; q=0.5; trace=1 is reconstructed with trace=1 as a media parameter, which can make accept(...) reject a request it should match. The comparison should also treat q case-insensitively. A small split between Content-Type and Accept handling would avoid regressing one while fixing the other.

💡 Possible direction
-private fun HeaderValue.toContentType(): ContentType =
-    params.asSequence()
-        .filter { it.name != "q" }
+private fun HeaderValue.toContentType(stopAtQuality: Boolean): ContentType =
+    params.asSequence()
+        .let { sequence ->
+            if (stopAtQuality) sequence.takeWhile { !it.name.equals("q", ignoreCase = true) } else sequence
+        }
         .fold(ContentType.parse(value)) { acc, p -> acc.withParameter(p.name, p.value) }

Then use stopAtQuality = false for Content-Type and true for Accept.

🤖 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-core/common/src/io/ktor/server/routing/RouteSelector.kt`
around lines 831 - 834, The current HeaderValue.toContentType() reconstructs
media parameters after a "q" token and treats "q" case-sensitively, which breaks
Accept handling; change the helper to accept a stopAtQuality: Boolean (default
false) and when stopAtQuality is true skip the "q" parameter (case-insensitive)
and stop processing any subsequent params (i.e., ignore params after the first
param whose name equals "q" ignoring case) while still folding earlier params
into ContentType.parse(value); keep existing behavior (stopAtQuality=false) for
Content-Type usage and call the new stopAtQuality=true when parsing Accept
values so accept-exts are not reinterpreted as media parameters (refer to
HeaderValue.toContentType(), params, and ContentType.parse in your changes).

Copy link
Copy Markdown
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.

Thanks for the fix! One little optimization

Comment thread ktor-server/ktor-server-core/common/src/io/ktor/server/routing/RouteSelector.kt Outdated
…/RouteSelector.kt

Co-authored-by: Bruce Hamilton <150327496+bjhham@users.noreply.github.com>
@bjhham bjhham enabled auto-merge (squash) May 12, 2026 11:23
Comment thread ktor-server/ktor-server-core/common/src/io/ktor/server/routing/RouteSelector.kt Outdated
…/RouteSelector.kt

Co-authored-by: Bruce Hamilton <150327496+bjhham@users.noreply.github.com>
auto-merge was automatically disabled May 12, 2026 11:37

Head branch was pushed to by a user without write access

@fru1tworld
Copy link
Copy Markdown
Contributor Author

Thanks For Feedback !

Resolve conflict in ContentTypeHeaderRouteSelector: combine plural
contentTypes API from main with parameter-aware matching from PR
(header.match(route) direction).
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.

2 participants