Skip to content

Conversation

@mltheuser
Copy link

Subsystem
ktor-http, ktor-client

Motivation
Currently, FormDataContent and formUrlEncode forcibly encode spaces as +. This prevents integration with external APIs that strictly follow RFC 3986 (expecting %20) or use exact string matching for signature verification. We need a way to configure this behavior without forcing users to rewrite the encoding logic from scratch.

Solution
I introduced a spaceToPlus boolean parameter to FormDataContent and the various formUrlEncode extension functions.

  • Default: spaceToPlus = true. I chose to keep the current behavior as the default to ensure full backward compatibility for existing applications.
  • Alternative: Users can now pass spaceToPlus = false to generate %20 instead of +, matching the WHATWG spec recommendation.

Note to reviewers: I stuck with true for safety, but I am open to changing the default to false if you prefer stricter adherence to the spec over backward compatibility.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 6, 2025

Walkthrough

A new spaceToPlus boolean parameter (defaulting to true) is introduced across form URL encoding APIs to control space-to-plus encoding behavior in form data. The parameter propagates through Parameters.formUrlEncode() and related encoding functions down to lower-level utilities, with corresponding updates to the FormDataContent constructor and comprehensive public API surface changes.

Changes

Cohort / File(s) Summary
Core URL Encoding API Implementation
ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt
Introduces spaceToPlus: Boolean = true parameter across public functions Parameters.formUrlEncode(), Parameters.formUrlEncodeTo(), List<Pair<String, String?>>.formUrlEncode(), and List<Pair<String, String?>>.formUrlEncodeTo(), with propagation to internal utilities. Updated documentation.
Form Data Content Constructor
ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/FormDataContent.kt
Adds spaceToPlus: Boolean = true parameter to public constructor and updates internal content generation to forward parameter to formData.formUrlEncode(spaceToPlus).
Test Coverage
ktor-http/common/test/io/ktor/tests/http/CodecTest.kt
Adds new unit tests testFormUrlEncodeWithSpaceToPlus and testListFormUrlEncodeWithSpaceToPlus validating space-to-plus encoding toggle behavior with default and explicit parameter values.
JVM Public API Surface
ktor-http/api/ktor-http.api
Updates JVM signatures for formUrlEncode and formUrlEncodeTo methods to include trailing boolean parameter (Z) and adds synthetic default variants. Includes synthetic default variant for parseUrlEncodedParameters.
Native/Multiplatform API Surface
ktor-http/api/ktor-http.klib.api
Expands public API for Parameters and List encoding functions with Boolean parameter; adds numerous URLBuilder overloads and URL-related utilities; updates form encoding method signatures and adds synthetic defaults. Marks as major update.
Client Core JVM API Surface
ktor-client/ktor-client-core/api/ktor-client-core.api
Updates FormDataContent constructor signature to accept boolean parameter and adds synthetic constructor variant with default-parameter markers. Adds public getFormData() method.
Client Core Multiplatform API Surface
ktor-client/ktor-client-core/api/ktor-client-core.klib.api
Expands FormDataContent constructor with boolean parameter and default value; exposes new public properties contentType and formData, plus bytes() method.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Signature consistency: Verify spaceToPlus parameter is consistently threaded through all overloads and call sites across HttpUrlEncoded.kt
  • Default parameter propagation: Ensure default values correctly cascade through nested function calls in formUrlEncodeTo implementations
  • Test coverage completeness: Verify test cases cover both map and list-based encoding paths with true/false variants
  • API surface alignment: Cross-check JVM API (ktor-http.api) and native API (ktor-http.klib.api) surfaces for consistency; validate synthetic default variants match Kotlin conventions
  • FormDataContent integration: Confirm constructor changes and internal formUrlEncode() call updates are compatible with existing code paths and client request handling

Suggested labels

👍 ship!

Suggested reviewers

  • bjhham
  • marychatte

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding a configurable spaceToPlus parameter to form parameter encoding, which is the core focus of this PR.
Description check ✅ Passed The PR description follows the template with Subsystem, Motivation, and Solution sections. It clearly explains the problem (forced space-to-plus encoding conflicts with RFC 3986), the solution (new spaceToPlus parameter with true default), and design rationale.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b12f47 and 88c0d56.

📒 Files selected for processing (4)
  • ktor-client/ktor-client-core/api/ktor-client-core.api (1 hunks)
  • ktor-client/ktor-client-core/api/ktor-client-core.klib.api (1 hunks)
  • ktor-http/api/ktor-http.api (1 hunks)
  • ktor-http/api/ktor-http.klib.api (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • ktor-client/ktor-client-core/api/ktor-client-core.api
  • ktor-client/ktor-client-core/api/ktor-client-core.klib.api
  • ktor-http/api/ktor-http.api
🧰 Additional context used
🧠 Learnings (8)
📚 Learning: 2025-05-14T18:05:02.321Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4855
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.klib.api:334-336
Timestamp: 2025-05-14T18:05:02.321Z
Learning: Breaking changes in constructor parameter order are acceptable for the ktor-server-di module when the code hasn't been released yet, as confirmed by the development team.

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-11-25T09:38:19.393Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : API changes must be intentional and well-documented

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-06-09T07:08:35.085Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4916
File: ktor-server/ktor-server-core/api/ktor-server-core.api:151-151
Timestamp: 2025-06-09T07:08:35.085Z
Learning: Breaking changes are acceptable in Ktor codebase when the code hasn't been released yet, as confirmed by bjhham from the development team.

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-11-25T09:38:19.393Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : Binary compatibility is enforced - all public API changes must be tracked in the `/api/` directories

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-11-25T09:38:19.393Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : Document all public APIs including parameters, return types, and exceptions

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-05-16T13:11:28.416Z
Learnt from: osipxd
Repo: ktorio/ktor PR: 4860
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.api:24-26
Timestamp: 2025-05-16T13:11:28.416Z
Learning: Binary incompatible changes (such as constructor signature changes) are acceptable in the ktor-server-di module when the version is not yet released, as confirmed by the development team.

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-09-30T07:52:14.769Z
Learnt from: yschimke
Repo: ktorio/ktor PR: 4013
File: ktor-client/ktor-client-android/jvm/src/io/ktor/client/engine/android/Android14URLConnectionFactory.kt:24-26
Timestamp: 2025-09-30T07:52:14.769Z
Learning: In the Ktor Android HTTP client engine (ktor-client-android), prefer using `URI.create(urlString).toURL()` over the `URL(urlString)` constructor when opening connections with Android's HttpEngine, as it avoids deprecated APIs and the different exception behavior (IllegalArgumentException vs MalformedURLException) is acceptable.

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-05-30T06:45:52.309Z
Learnt from: rururux
Repo: ktorio/ktor PR: 4896
File: ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt:1-12
Timestamp: 2025-05-30T06:45:52.309Z
Learning: The headersOf() function from io.ktor.http package is available through wildcard imports like `import io.ktor.http.*`, so no explicit import statement is needed when using wildcard imports from that package.

Applied to files:

  • ktor-http/api/ktor-http.klib.api
🔇 Additional comments (2)
ktor-http/api/ktor-http.klib.api (2)

1759-1760: LGTM! Consistent API extension for List-based form encoding.

The Boolean parameter (presumably spaceToPlus) is consistently added to the List-based overloads, maintaining API symmetry with the Parameters-based functions. The default value ensures backward compatibility for existing callers.

Based on learnings, these API changes are properly tracked in the ABI dump file as required for binary compatibility enforcement in the Ktor codebase.


1738-1739: LGTM! Backward-compatible API extension for Parameters form encoding.

The addition of a trailing Boolean parameter spaceToPlus with a default value of true maintains backward compatibility while enabling the new space-encoding behavior. The changes are consistently applied to all four form encoding functions: Parameters.formUrlEncode, Parameters.formUrlEncodeTo, List<Pair<String, String?>>.formUrlEncode, and List<Pair<String, String?>>.formUrlEncodeTo.


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-http/common/src/io/ktor/http/HttpUrlEncoded.kt (1)

45-47: Optional: add @param out to KDocs for completeness

Since you’re touching these KDocs, consider documenting the existing out parameter alongside spaceToPlus for consistency with the “document all public APIs” guideline.

Also applies to: 75-77

ktor-http/common/test/io/ktor/tests/http/CodecTest.kt (1)

156-185: Optional: consider aligning new test names with the project’s recommended pattern

The new tests keep the existing testXxx naming style from this file. If/when you touch this test suite again, it might be worth aligning with the DescribeWhatIsBeingTested pattern from the guidelines (e.g., FormUrlEncode_respects_spaceToPlus_flag). Not urgent given the current file’s conventions.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e081285 and 2541ece.

📒 Files selected for processing (3)
  • ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/FormDataContent.kt (1 hunks)
  • ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt (2 hunks)
  • ktor-http/common/test/io/ktor/tests/http/CodecTest.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.kt: Follow Kotlin official style guide (https://kotlinlang.org/docs/coding-conventions.html)
Use star imports for io.ktor.* packages
Document all public APIs including parameters, return types, and exceptions
Mark internal APIs with @InternalAPI annotation
Run ./gradlew lintKotlin and fix all linting issues before giving control back to the user
Use ./gradlew formatKotlin to automatically fix formatting issues
Run ./gradlew updateLegacyAbi after making ABI changes to update ABI signature files
Binary compatibility is enforced - all public API changes must be tracked in the /api/ directories
Validate ABI with ./gradlew checkLegacyAbi and update with ./gradlew updateLegacyAbi
API changes must be intentional and well-documented
Error handling follows Kotlin conventions with specific Ktor exceptions

Files:

  • ktor-http/common/test/io/ktor/tests/http/CodecTest.kt
  • ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/FormDataContent.kt
  • ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt
**/test/**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

Follow test naming pattern: DescribeWhatIsBeingTested

Files:

  • ktor-http/common/test/io/ktor/tests/http/CodecTest.kt
🧠 Learnings (2)
📓 Common learnings
Learnt from: bjhham
Repo: ktorio/ktor PR: 4855
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.klib.api:334-336
Timestamp: 2025-05-14T18:05:02.321Z
Learning: Breaking changes in constructor parameter order are acceptable for the ktor-server-di module when the code hasn't been released yet, as confirmed by the development team.
📚 Learning: 2025-05-30T06:45:52.309Z
Learnt from: rururux
Repo: ktorio/ktor PR: 4896
File: ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt:1-12
Timestamp: 2025-05-30T06:45:52.309Z
Learning: In Ktor test files, particularly in the ktor-client/ktor-client-core/jvm/test/ directory, test files follow the convention of not including explicit package declarations. This is consistent across test files like CachingCacheStorageTest.kt and should be maintained for consistency.

Applied to files:

  • ktor-http/common/test/io/ktor/tests/http/CodecTest.kt
🔇 Additional comments (5)
ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt (2)

31-40: Space encoding flag is wired correctly through all form encoders

The new spaceToPlus flag is consistently propagated through list- and Parameters-based formUrlEncode* functions and into encodeURLParameter(spaceToPlus = spaceToPlus). Defaulting to true preserves existing + behavior while allowing %20 when needed. From a behavior perspective this looks solid and backwards-compatible for callers who recompile.

Also applies to: 48-58, 67-70, 78-80, 82-84, 86-90


38-40: Ensure ABI changes are properly tracked via ./gradlew updateLegacyAbi

All four formUrlEncode / formUrlEncodeTo signatures now take spaceToPlus: Boolean = true. Run ./gradlew updateLegacyAbi to document this intentional API change in the API signature files. Note that breaking changes in unreleased code are acceptable per Ktor development practices—the key requirement is that API changes must be tracked in the /api/ directories.

ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/FormDataContent.kt (2)

25-33: FormDataContent wiring to spaceToPlus looks correct

The new spaceToPlus parameter feeds directly into formData.formUrlEncode(spaceToPlus) and the KDoc documents it clearly; the default preserves existing behavior while allowing %20 when required.


28-33: Constructor change needs verification against ABI tracking

Verify whether this constructor change has been tracked in the /api/ktor-client-core.api descriptor files. If the change is unreleased and properly documented in the API files, breaking changes may be acceptable per Ktor's development guidelines. If the change is not yet tracked in API descriptors, run ./gradlew updateLegacyAbi to update them. Only add a secondary constructor if empirical ABI checks via ./gradlew checkLegacyAbi fail.

ktor-http/common/test/io/ktor/tests/http/CodecTest.kt (1)

156-185: Good coverage for spaceToPlus behavior on both map and list encoders

The two new tests correctly exercise:

  • Default plus behavior and explicit spaceToPlus = true/false for map-based entries.formUrlEncodeTo.
  • Default plus behavior and explicit %20 behavior for list-based formUrlEncode.

This should prevent regressions around the new flag.

@mltheuser
Copy link
Author

@coderabbitai generate docstrings

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 6, 2025

✅ Actions performed

Initiated docstring generation; will generate only if new commits exist.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 6, 2025

Note

Docstrings generation - SUCCESS
Generated docstrings for this pull request at #5239

coderabbitai bot added a commit that referenced this pull request Dec 6, 2025
Docstrings generation was requested by @mltheuser.

* #5238 (comment)

The following files were modified:

* `ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt`
@mltheuser
Copy link
Author

I am confused, I would have expected that the generate docstrings command adds a commit to this PR instead it made a separate one? Also all of those test failure I am unable to recreate locally. Will look into this more some other time.

@bjhham
Copy link
Contributor

bjhham commented Dec 17, 2025

The failed tests might've been a temporary issue with the CI. Hopefully they'll pass this time.

You'll need to run updateLegacyAbi for the new dump as well, however.

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: 1

🧹 Nitpick comments (1)
ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt (1)

70-80: Consider documentation consistency.

This function retains the "Report a problem" link (line 73), while the other modified public functions in this file don't have it. For consistency, consider either adding the link to all public functions or removing it from this one.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2541ece and 95d04c9.

📒 Files selected for processing (1)
  • ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.kt: Follow Kotlin official style guide (https://kotlinlang.org/docs/coding-conventions.html)
Use star imports for io.ktor.* packages
Document all public APIs including parameters, return types, and exceptions
Mark internal APIs with @InternalAPI annotation
Run ./gradlew lintKotlin and fix all linting issues before giving control back to the user
Use ./gradlew formatKotlin to automatically fix formatting issues
Run ./gradlew updateLegacyAbi after making ABI changes to update ABI signature files
Binary compatibility is enforced - all public API changes must be tracked in the /api/ directories
Validate ABI with ./gradlew checkLegacyAbi and update with ./gradlew updateLegacyAbi
API changes must be intentional and well-documented
Error handling follows Kotlin conventions with specific Ktor exceptions

Files:

  • ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt
🔇 Additional comments (4)
ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt (4)

40-58: Implementation looks solid.

The spaceToPlus parameter is correctly propagated to both key and value encoding using named parameter syntax, and null values are handled appropriately.


60-68: LGTM!

The parameter chaining is correct, and the documentation clearly describes the behavior.


82-84: LGTM!

The internal function correctly propagates the parameter through the call chain.


86-90: LGTM!

The flattening logic correctly handles empty value lists and properly delegates with the spaceToPlus parameter.

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 (1)
ktor-http/api/ktor-http.klib.api (1)

1738-1760: API changes correctly tracked with comprehensive documentation.

The ABI dump properly reflects the addition of the spaceToPlus Boolean parameter to all four form URL encoding functions. All corresponding source implementations in ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt include KDoc documentation covering parameter purpose, default value, and encoding behavior.

The documentation could be optionally enhanced by clarifying use cases for setting spaceToPlus = false, such as RFC 3986 compliance or signature verification scenarios, to help developers choose the appropriate setting.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95d04c9 and a179fa8.

📒 Files selected for processing (2)
  • ktor-http/api/ktor-http.api (1 hunks)
  • ktor-http/api/ktor-http.klib.api (2 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-11-25T09:38:19.393Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : API changes must be intentional and well-documented

Applied to files:

  • ktor-http/api/ktor-http.api
  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-11-25T09:38:19.393Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : Document all public APIs including parameters, return types, and exceptions

Applied to files:

  • ktor-http/api/ktor-http.api
  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-09-30T07:52:14.769Z
Learnt from: yschimke
Repo: ktorio/ktor PR: 4013
File: ktor-client/ktor-client-android/jvm/src/io/ktor/client/engine/android/Android14URLConnectionFactory.kt:24-26
Timestamp: 2025-09-30T07:52:14.769Z
Learning: In the Ktor Android HTTP client engine (ktor-client-android), prefer using `URI.create(urlString).toURL()` over the `URL(urlString)` constructor when opening connections with Android's HttpEngine, as it avoids deprecated APIs and the different exception behavior (IllegalArgumentException vs MalformedURLException) is acceptable.

Applied to files:

  • ktor-http/api/ktor-http.api
  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-05-14T18:05:02.321Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4855
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.klib.api:334-336
Timestamp: 2025-05-14T18:05:02.321Z
Learning: Breaking changes in constructor parameter order are acceptable for the ktor-server-di module when the code hasn't been released yet, as confirmed by the development team.

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-11-25T09:38:19.393Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : Binary compatibility is enforced - all public API changes must be tracked in the `/api/` directories

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-05-30T06:45:52.309Z
Learnt from: rururux
Repo: ktorio/ktor PR: 4896
File: ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt:1-12
Timestamp: 2025-05-30T06:45:52.309Z
Learning: The headersOf() function from io.ktor.http package is available through wildcard imports like `import io.ktor.http.*`, so no explicit import statement is needed when using wildcard imports from that package.

Applied to files:

  • ktor-http/api/ktor-http.klib.api
🔇 Additional comments (1)
ktor-http/api/ktor-http.api (1)

771-778: API changes look correct and consistent.

The addition of the Boolean parameter to form URL encoding methods is properly reflected in the API descriptor with:

  • Both formUrlEncode and formUrlEncodeTo method families updated
  • Both Parameters and List overloads covered
  • Synthetic $default methods correctly generated for JVM default parameter support

This maintains binary compatibility through default parameters.

Please verify the following to ensure completeness:

#!/bin/bash
# Verify that the default value is spaceToPlus = true for backward compatibility
echo "=== Checking default parameter value in implementation ==="
rg -n "fun formUrlEncode.*spaceToPlus.*=" --type=kt -A2

echo -e "\n=== Checking KDoc for the new parameter ==="
rg -n "@param spaceToPlus" --type=kt -B2 -A2

Based on learnings, API changes must be well-documented with parameter descriptions.

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: 2

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a179fa8 and 1b12f47.

📒 Files selected for processing (5)
  • ktor-client/ktor-client-core/api/ktor-client-core.api (1 hunks)
  • ktor-client/ktor-client-core/api/ktor-client-core.klib.api (1 hunks)
  • ktor-http/api/ktor-http.api (1 hunks)
  • ktor-http/api/ktor-http.klib.api (2 hunks)
  • ktor-server/ktor-server-core/api/ktor-server-core.api (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2025-11-25T09:38:19.393Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : API changes must be intentional and well-documented

Applied to files:

  • ktor-server/ktor-server-core/api/ktor-server-core.api
  • ktor-http/api/ktor-http.api
  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-09-30T07:52:14.769Z
Learnt from: yschimke
Repo: ktorio/ktor PR: 4013
File: ktor-client/ktor-client-android/jvm/src/io/ktor/client/engine/android/Android14URLConnectionFactory.kt:24-26
Timestamp: 2025-09-30T07:52:14.769Z
Learning: In the Ktor Android HTTP client engine (ktor-client-android), prefer using `URI.create(urlString).toURL()` over the `URL(urlString)` constructor when opening connections with Android's HttpEngine, as it avoids deprecated APIs and the different exception behavior (IllegalArgumentException vs MalformedURLException) is acceptable.

Applied to files:

  • ktor-http/api/ktor-http.api
  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-05-14T18:05:02.321Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4855
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.klib.api:334-336
Timestamp: 2025-05-14T18:05:02.321Z
Learning: Breaking changes in constructor parameter order are acceptable for the ktor-server-di module when the code hasn't been released yet, as confirmed by the development team.

Applied to files:

  • ktor-client/ktor-client-core/api/ktor-client-core.api
  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-11-25T09:38:19.393Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : Document all public APIs including parameters, return types, and exceptions

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-11-25T09:38:19.393Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : Binary compatibility is enforced - all public API changes must be tracked in the `/api/` directories

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-06-09T07:08:35.085Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4916
File: ktor-server/ktor-server-core/api/ktor-server-core.api:151-151
Timestamp: 2025-06-09T07:08:35.085Z
Learning: Breaking changes are acceptable in Ktor codebase when the code hasn't been released yet, as confirmed by bjhham from the development team.

Applied to files:

  • ktor-http/api/ktor-http.klib.api
📚 Learning: 2025-05-30T06:45:52.309Z
Learnt from: rururux
Repo: ktorio/ktor PR: 4896
File: ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt:1-12
Timestamp: 2025-05-30T06:45:52.309Z
Learning: The headersOf() function from io.ktor.http package is available through wildcard imports like `import io.ktor.http.*`, so no explicit import statement is needed when using wildcard imports from that package.

Applied to files:

  • ktor-http/api/ktor-http.klib.api
🔇 Additional comments (4)
ktor-http/api/ktor-http.klib.api (2)

1738-1739: spaceToPlus flag on Parameters-based formUrlEncode looks consistent; just confirm ABI expectations

The added Boolean parameter (with a default) on the Parameters.formUrlEncode / formUrlEncodeTo extensions matches the PR intent and preserves previous behavior when omitted. It does, however, change the function descriptors compared to the earlier single-parameter versions, which is an ABI-breaking change for callers compiled against the old signatures (especially in native/JS targets). Please confirm this break is acceptable for the branch you’re targeting, or consider adding overloads that keep the old signatures delegating to the new ones.


1759-1760: List<Pair<...>> overloads mirror Parameters APIs; same ABI caveat applies

The list-based formUrlEncode / formUrlEncodeTo extensions are updated symmetrically with the same Boolean default parameter, which is good for API consistency and behavior. As with the Parameters overloads, this changes the exported signatures; please double-check that this intended ABI break is acceptable, or add delegating overloads that retain the legacy one-parameter forms if you need binary compatibility.

ktor-client/ktor-client-core/api/ktor-client-core.klib.api (1)

909-920: Binary incompatibility introduced; clarify if acceptable for this development cycle

The constructor signature change from (Parameters) to (Parameters, Boolean = true) breaks binary compatibility. Even though the Boolean parameter has a default value, the old single-argument constructor is no longer available at the ABI level—existing binaries compiled against previous versions will fail to link.

Per the learnings from this project, breaking changes are acceptable for unreleased versions (similar to prior decisions on ktor-server-di). If this version is still in development, this change may be intentional and acceptable. However, verify this with the team or use the dual-constructor pattern suggested in the original review if binary compatibility with prior releases must be maintained:

public class FormDataContent(
    public val formData: Parameters,
    spaceToPlus: Boolean,
) : OutgoingContent.ByteArrayContent() {
    // ...
    
    constructor(formData: Parameters) : this(formData, spaceToPlus = true)
}

Note: The default parameter on the primary constructor maintains source-level compatibility for new code, which is good practice regardless.

ktor-http/api/ktor-http.api (1)

771-781: API surface changes are consistent and preserve backward compatibility.

The addition of the boolean spaceToPlus parameter to formUrlEncode and formUrlEncodeTo (for both Parameters and List<Pair> overloads) is handled correctly:

  • Synthetic default methods (formUrlEncode$default, formUrlEncodeTo$default) enable Kotlin's default parameter semantics at the JVM boundary, ensuring existing callers continue to work without modification.
  • All four encoding function variants are updated consistently.

Based on learnings, API changes must be intentional and well-documented. Please ensure:

  1. The implementation correctly defaults spaceToPlus = true (as stated in PR objectives).
  2. Public documentation (KDoc) clearly explains the behavior difference between spaceToPlus = true (spaces encoded as +) and spaceToPlus = false (spaces encoded as %20).
  3. Migration guidance is provided if the default may change in future versions.

Run the following script to verify the implementation includes proper documentation and default values:

#!/bin/bash
# Verify implementation of formUrlEncode functions includes proper defaults and documentation

# Check for the spaceToPlus parameter with default value in implementation
rg -A5 -B2 'fun formUrlEncode.*spaceToPlus.*=.*true' ktor-http/common/src/io/ktor/http/

# Check for KDoc documentation on spaceToPlus parameter
rg -B10 'fun formUrlEncode' ktor-http/common/src/io/ktor/http/HttpUrlEncoded.kt | grep -E '(@param|spaceToPlus|%20|\+)'

Comment on lines +1467 to +1468
public fun <init> (Lio/ktor/http/Parameters;Z)V
public synthetic fun <init> (Lio/ktor/http/Parameters;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 21, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

FormDataContent constructor signature is binary-incompatible; document as major version breaking change

Changing the primary constructor to (Parameters, Boolean) removes the previous (Parameters) entry point from the ABI. Existing binaries compiled against the old signature will fail to link. If preserving binary compatibility within the current major version is desired, add a secondary constructor delegating to the new one. However, if this change targets the next major version (4.0), the breaking change is acceptable per semantic versioning—but should be clearly documented in release notes as ABI-incompatible.

🤖 Prompt for AI Agents
In ktor-client/ktor-client-core/api/ktor-client-core.api around lines 1467-1468,
the FormDataContent primary constructor was changed to (Parameters, Boolean)
which removes the previous (Parameters) ABI entry and breaks binary
compatibility; to fix, restore ABI compatibility by adding a secondary
constructor with the original single-Parameters signature that delegates to the
new two-argument constructor (providing the appropriate default Boolean value to
preserve prior behavior) and update the API surface accordingly, or if this is
an intended major-version break, document the ABI-incompatible constructor
change clearly in the 4.0 release notes instead of adding the compatibility
constructor.

Copy link
Author

Choose a reason for hiding this comment

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

Should I add back a version with one argument for binary compatibility or is breaking it acceptable? Let's leave that up to the reviewers.

Copy link
Contributor

Choose a reason for hiding this comment

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

@mltheuser, that's a sensible approach! Based on the project's history, breaking changes in Ktor are generally acceptable in these scenarios:

  1. When the code hasn't been released yet – if this feature is part of an unreleased version, binary incompatibility is typically fine
  2. In major version releases – if this is targeting a major version bump (e.g., 4.0), breaking the ABI is acceptable per semantic versioning

Since the decision depends on the release timeline and versioning strategy, letting the reviewers make the call is the right move. They'll know whether this change:

  • Is still unreleased (in which case the break is fine)
  • Should preserve binary compatibility by adding a secondary constructor with the old signature
  • Is acceptable as a documented breaking change for the next major version

The synthetic default constructor that's already generated (<init>(Lio/ktor/http/Parameters;ZILkotlin/jvm/internal/DefaultConstructorMarker;)) helps with source compatibility, but doesn't restore binary compatibility for the original single-parameter constructor.


🧠 Learnings used
Learnt from: bjhham
Repo: ktorio/ktor PR: 4855
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.klib.api:334-336
Timestamp: 2025-05-14T18:05:02.321Z
Learning: Breaking changes in constructor parameter order are acceptable for the ktor-server-di module when the code hasn't been released yet, as confirmed by the development team.

Learnt from: osipxd
Repo: ktorio/ktor PR: 4860
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.api:24-26
Timestamp: 2025-05-16T13:11:28.416Z
Learning: Binary incompatible changes (such as constructor signature changes) are acceptable in the ktor-server-di module when the version is not yet released, as confirmed by the development team.

Learnt from: bjhham
Repo: ktorio/ktor PR: 4916
File: ktor-server/ktor-server-core/api/ktor-server-core.api:151-151
Timestamp: 2025-06-09T07:08:35.085Z
Learning: Breaking changes are acceptable in Ktor codebase when the code hasn't been released yet, as confirmed by bjhham from the development team.

Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-25T09:38:19.393Z
Learning: Applies to **/*.kt : API changes must be intentional and well-documented

Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T12:49:56.883Z
Learning: Breaking changes to public APIs are only allowed in major version releases.

@mltheuser
Copy link
Author

Thanks @bjhham for bringing this back to my attention and guiding me in the right direction!

I suspect this is ready for review now. There are 2 remaining test failures, but TeamCity marks them as flaky, so they seem unrelated to these changes.

Regarding binary compatibility, I will leave the final call to the reviewers. If you prefer strict compatibility, I am happy to add back single argument versions to support it.

@mltheuser
Copy link
Author

mltheuser commented Dec 21, 2025

Oh and could someone close the PR by coderabbit: #5239 (I can't do it since it is not in my name)
I copied it's docString changes over to this branch already.

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