Skip to content

Conversation

@osipxd
Copy link
Member

@osipxd osipxd commented Nov 12, 2025

Subsystem
ktor-client-core

Motivation
KTOR-9037 Multipart/form-data: Make formData's block inline

Solution
Add the inline modifier to the function

@osipxd osipxd self-assigned this Nov 12, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

Walkthrough

Added a public no-arg constructor and a build() method to FormBuilder, introduced multiple append() overloads and an inline formData(builder) builder API, updated FormDataContent constructor/signatures, and applied @PublishedApi annotations to FormBuilder members for inline use.

Changes

Cohort / File(s) Summary
Public API files
ktor-client/ktor-client-core/api/ktor-client-core.api, ktor-client/ktor-client-core/api/ktor-client-core.klib.api
Added public FormBuilder() constructor and build() method returning List<FormPart<*>>. Added multiple append(...) overloads (InputProvider, Iterable, Array, Boolean, ByteArray, Number, String, Source) and appendInput(...). Added formData(Function1<FormBuilder, Unit>) overload. Updated FormDataContent constructor to accept io.ktor.http.Parameters and exposed contentLength, contentType, formData, and bytes().
Source implementation
ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt
Made formData inline; added @PublishedApi to FormBuilder declaration, its internal constructor and build() method; replaced broad kotlinx.io import with explicit imports; added Kotlin contracts imports; minor header copyright update.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Check correct placement and intent of @PublishedApi on the constructor and build() for inline safety.
  • Verify inline formData uses contracts/InvocationKind correctly and does not expose internals beyond intended scope.
  • Validate signatures and default Headers parameter consistency across all new append overloads and appendInput.
  • Confirm FormDataContent API surface changes (constructor and accessors) align with consumers and bytecode compatibility.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% 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 summarizes the main change: making the formData function inline as indicated by the issue KTOR-9037.
Description check ✅ Passed The description follows the required template with all three sections (Subsystem, Motivation, Solution) properly completed and linked to the relevant issue.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch osipxd/make-formData-inline

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b93efe and c9fcb5c.

📒 Files selected for processing (3)
  • ktor-client/ktor-client-core/api/ktor-client-core.api (2 hunks)
  • ktor-client/ktor-client-core/api/ktor-client-core.klib.api (3 hunks)
  • ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • ktor-client/ktor-client-core/api/ktor-client-core.api
  • ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt
🧰 Additional context used
🧠 Learnings (2)
📚 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.klib.api
📚 Learning: 2025-06-23T12:49:56.883Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T12:49:56.883Z
Learning: Binary compatibility is enforced using the binary-compatibility-validator plugin.

Applied to files:

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

891-907: FormBuilder constructor and build() are a safe additive ABI surface

Exposing a no‑arg constructor and build(): List<FormPart<*>> on FormBuilder is purely additive and lines up with the builder‑style DSL and the new inline formData overload. It gives external callers an escape hatch to construct and reuse builders manually but doesn’t introduce binary‑compat risks on its own.


1520-1531: Inline formData(FormBuilder.() -> Unit) matches the DSL goal and should be binary‑safe

Making the existing formData(Function1<FormBuilder, Unit>) overload inline is exactly what’s needed to inline the user block and avoid extra allocations, and it fits with exposing FormBuilder’s constructor and build() for use inside the inline body (likely via @PublishedApi, which is the pattern Kotlin recommends for public inline functions using internal helpers).(kotlinlang.org)

Kotlin’s compatibility docs call out removing inline from a non‑private function as binary‑incompatible, but do not treat adding inline as a binary break; adding the modifier keeps the callable symbol while only changing how new call sites are compiled.(kotlinlang.org) Given that, this change should be safe for Kotlin/JVM/JS, and Kotlin/Native binary compatibility isn’t a stated target in the earlier discussion.

If you want extra assurance for Native, I’d still let CI’s binary‑compatibility‑validator and any existing K/N consumers confirm that updating to this version of formData doesn’t surface unexpected linkage issues.


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.

@osipxd osipxd force-pushed the osipxd/make-formData-inline branch from c07f996 to 2b93efe Compare November 13, 2025 09:57
@osipxd osipxd marked this pull request as ready for review November 13, 2025 09:57
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-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt (1)

89-90: Document the builder contract on the inline overload.

We invoke block exactly once here. Adding the same ExperimentalContracts opt-in + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } used elsewhere tightens the DSL guarantees and keeps the new inline overload aligned with the rest of the API surface.

- public inline fun formData(block: FormBuilder.() -> Unit): List<PartData> =
-    formData(*FormBuilder().apply(block).build().toTypedArray())
+@OptIn(ExperimentalContracts::class)
+public inline fun formData(block: FormBuilder.() -> Unit): List<PartData> {
+    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
+    return formData(*FormBuilder().apply(block).build().toTypedArray())
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b293fc and 2b93efe.

📒 Files selected for processing (3)
  • ktor-client/ktor-client-core/api/ktor-client-core.api (2 hunks)
  • ktor-client/ktor-client-core/api/ktor-client-core.klib.api (3 hunks)
  • ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt (3 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 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/common/src/io/ktor/client/request/forms/formDsl.kt
  • ktor-client/ktor-client-core/api/ktor-client-core.klib.api
📚 Learning: 2025-06-23T12:49:56.883Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T12:49:56.883Z
Learning: Binary compatibility is enforced using the binary-compatibility-validator plugin.

Applied to files:

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

891-907: The review comment is incorrect; FormBuilder is already correctly annotated.

The source code in ktor-client/ktor-client-core/common/src/io/ktor/client/request/forms/formDsl.kt already implements what the review suggests:

  • Constructor: @PublishedApi internal constructor()
  • build() method: @PublishedApi internal fun build()

The klib.api dump correctly exposes these members because @PublishedApi internal means "internal but part of the public API for inliners." This is the intended design and causes no external compatibility issues since only the inline formData() function uses FormBuilder() directly.

Likely an incorrect or invalid review comment.


1531-1531: Kotlin/Native ABI incompatibility confirmed; verify release status before proceeding.

Web search confirms your concern: changing a function from non-inline to inline is a binary-breaking change for Kotlin/Native klib consumers, as previously compiled call sites expect the original callable symbol, and making the function inline removes/changes that emitted symbol.

However, referencing Ktor team precedent: breaking changes to unreleased code are acceptable per prior PRs (#4916, #4855, #4860). If ktor-client-core hasn't been released yet, this change may be acceptable with a corresponding version bump. Confirm release status; if released, consider preserving a non-inline forwarding symbol to maintain binary compatibility.

@osipxd osipxd requested a review from e5l November 18, 2025 16:17
@osipxd osipxd force-pushed the osipxd/make-formData-inline branch from 2b93efe to c9fcb5c Compare November 18, 2025 16:17
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.

3 participants