Ktor2Curl
2.0.1indexedTransforms Ktor requests into cURL logs, enabling easy debugging and logging of HTTP requests. Offers customization for excluding or masking headers in generated logs. Inspired by Ok2Curl.
Transforms Ktor requests into cURL logs, enabling easy debugging and logging of HTTP requests. Offers customization for excluding or masking headers in generated logs. Inspired by Ok2Curl.
Transform Ktor HTTP requests into runnable curl commands for debugging and logging. Pure Kotlin
library supporting KMP (Android, iOS, JVM) and standard Android projects.
Inspired by Ok2Curl for OkHttp.
Add to your build.gradle.kts:
val commonMain by getting {
dependencies {
implementation("io.github.kabirnayeem99:ktor2curl:2.0.1")
}
}
Supports all KMP targets: Android, iOS (arm64/x64/simulator), JVM, JS, WASM.
build.gradle.kts)dependencies {
implementation("io.github.kabirnayeem99:ktor2curl:2.0.1")
}
build.gradle)dependencies {
implementation 'io.github.kabirnayeem99:ktor2curl:2.0.1'
}
After adding the dependency, run:
./gradlew build
If you hit Kotlin version mismatch, ensure your project uses Kotlin 2.0+ ( see Troubleshooting).
val client = HttpClient(CIO) {
install(KtorToCurl) {
converter = object : CurlLogger {
override fun log(curl: String) {
println(curl) // or use Android Log.d(), your logging framework, etc.
}
}
}
}
// Every request via this client now logs a curl command
client.post("https://api.example.com/users") {
headers {
append(HttpHeaders.Authorization, "Bearer YOUR_TOKEN")
append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
}
setBody()
}
Output:
curl -X POST \
https://data.techforpalestine.org/api/v2/users \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
--data '{"name":"Alice"}'
Hide API keys, tokens, and custom auth headers from logs:
val client = HttpClient(CIO) {
install(KtorToCurl) {
converter = object : CurlLogger {
override fun log(curl: String) {
Log.d("API", curl)
}
}
maskedHeaders = setOf(
HttpHeaders.Authorization,
"X-API-Key",
"X-Session-Token"
)
}
}
Output:
curl -X POST \
https://data.techforpalestine.org/api/v2/users \
-H "Authorization: [omitted]" \
-H "Content-Type: application/json" \
--data '{"name":"Alice"}'
Remove User-Agent, Content-Length, or other headers from logs:
val client = HttpClient(CIO) {
install(KtorToCurl) {
converter = object : CurlLogger {
override fun log(curl: String) {
Log.d("API", curl)
}
}
excludedHeaders = setOf(
HttpHeaders.UserAgent,
HttpHeaders.ContentLength
)
maskedHeaders = setOf(HttpHeaders.Authorization)
}
}
client.post("https://data.techforpalestine.org/api/v2/killed-in-gaza.min.json") {
headers {
append(HttpHeaders.Authorization, "Basic SXNyYWVsIGtpbGxzIGNoaWxkcmVuLg")
append(HttpHeaders.ContentType, ContentType.Application.Json.toString())
}
setBody("""{"en_name":"Mazen Ahmed Mohammed Al-Kahlout","name":"مازن أحمد محمد الكحلوت","age":52,"dob":"1972-02-05","sex":"m","id":"985194547","source":"u"}""")
}
Output:
curl -X POST \
https://data.techforpalestine.org/api/v2/killed-in-gaza.min.json \
-H "Authorization: [omitted]" \
-H "Content-Type: application/json" \
--data '{"en_name":"Mazen Ahmed Mohammed Al-Kahlout","name":"مازن أحمد محمد الكحلوت","age":52,"dob":"1972-02-05","sex":"m","id":"985194547","source":"u"}'
Send curl commands to a remote service, database, or aggregation tool:
class RemoteCurlLogger : CurlLogger {
override fun log(curl: String) {
// Send to your backend, analytics, or debug dashboard
analyticsService.trackRequest(curl)
}
}
val client = HttpClient(CIO) {
install(KtorToCurl) {
converter = RemoteCurlLogger()
}
}
File uploads and form fields render correctly:
client.post("https://api.example.com/upload") {
setBody(
MultiPartFormDataContent(
formData {
append(
"file", InputProvider { "file content".byteInputStream() },
Headers.build { append(HttpHeaders.ContentDisposition, "filename=data.txt") })
append("field", "value")
}
))
}
Output:
curl -X POST \
https://data.techforpalestine.org/api/v2/upload \
-F "file=@data.txt" \
-F "field=value"
Cause: Kotlin version mismatch or incorrect repository.
Fix:
kotlin = "2.3.0" or later in plugins blockrepositories {
mavenCentral()
google()
}
Cause: Logger implementation not called.
Fix:
converter is set in plugin configval client = HttpClient {
install(KtorToCurl) { ... } // Must be before other requests
}
Cause: Header not in maskedHeaders set.
Fix:
maskedHeaders = setOf(
"Authorization", // ✓ Correct
"X-Custom-Token",
HttpHeaders.Authorization // ✓ Also correct (from Ktor)
)
excludedHeadersCause: Header name mismatch (case/spelling).
Fix:
HttpHeaders.HEADER_NAME constants from Ktor when possibleCause: Kotlin < 2.3.0 incompatible with Ktor 3.5 on Native.
Fix:
plugins {
kotlin("multiplatform") version "2.3.0"
}
Contributions welcome. Before submitting:
git clone https://github.com/YOUR_USERNAME/Ktor2Curl.git
Ktor2Curl
Run the full test suite to verify no regressions:
./gradlew :ktor2curl:jvmTest :ktor2curl:testDebugUnitTest :ktor2curl:iosSimulatorArm64Test
Or just JVM for quick feedback:
./gradlew :ktor2curl:jvmTest --console=plain
MIT - See LICENSE file for details.
| Property | Type | Default | Purpose |
|---|
converter | CurlLogger | Required | Implementation to handle curl output (log it, send it, store it) |
maskedHeaders | Set<String> | emptySet() | Headers to replace with [omitted] (auth, tokens, keys) |
excludedHeaders | Set<String> | emptySet() | Headers to skip entirely in output |
git clone --recursive https://github.com/YOUR_USERNAME/ktor2curl.git)git checkout -b feature/add-cookie-masking
ktor2curl/src/commonTest/ (see existing test structure)./gradlew :ktor2curl:jvmTest --console=plain
./gradlew ktlintFormat
git remote add upstream https://github.com/kabirnayeem99/Ktor2Curl.git
git fetch upstream
git checkout -b feature/your-idea
Surfaced from shared tags and platforms — no rankings paid for.