Skip to content

Commit c24722c

Browse files
committed
experiment: try sealgurt curl android route
1 parent 2db5a75 commit c24722c

44 files changed

Lines changed: 7512 additions & 50 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/yogurt-dev-release.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Build Yogurt Dev Release
1+
name: Build Yogurt Curl Route Test
22

33
on:
44
push:
@@ -10,7 +10,7 @@ permissions:
1010
contents: write
1111

1212
concurrency:
13-
group: yogurt-dev-release
13+
group: yogurt-curl-route-release
1414
cancel-in-progress: true
1515

1616
jobs:
@@ -85,7 +85,7 @@ jobs:
8585
path: yogurt/build/bin/${{ matrix.target }}/releaseExecutable/**
8686

8787
release-dev:
88-
name: Refresh dev release
88+
name: Refresh curl-route release
8989
needs:
9090
- build-native
9191
runs-on: ubuntu-latest
@@ -111,18 +111,18 @@ jobs:
111111
112112
ls -lah release-assets
113113
114-
- name: Recreate dev release
114+
- name: Recreate curl-route release
115115
env:
116116
GH_REPO: ${{ github.repository }}
117117
GH_TOKEN: ${{ github.token }}
118118
shell: bash
119119
run: |
120120
set -euo pipefail
121121
122-
gh release delete dev --yes --cleanup-tag || true
123-
gh api -X DELETE "repos/${GITHUB_REPOSITORY}/git/refs/tags/dev" || true
122+
gh release delete dev-curl-route --yes --cleanup-tag || true
123+
gh api -X DELETE "repos/${GITHUB_REPOSITORY}/git/refs/tags/dev-curl-route" || true
124124
125-
gh release create dev release-assets/* \
125+
gh release create dev-curl-route release-assets/* \
126126
--target "${GITHUB_SHA}" \
127-
--title "dev" \
128-
--notes "Auto-updated Yogurt dev binaries for ${GITHUB_SHA}"
127+
--title "dev-curl-route" \
128+
--notes "Auto-updated Yogurt curl-route test binaries for ${GITHUB_SHA}"

acidify-core/build.gradle.kts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ kotlin {
6767
implementation(libs.ktor.client.curl)
6868
}
6969
findByName("androidNativeArm64Main")?.dependencies {
70-
implementation(project(":android-https-native"))
71-
implementation(libs.ktor.client.cio)
70+
implementation(project(":ktor-client-curl-android-native"))
7271
}
7372
all {
7473
languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi")
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package org.ntqqrev.acidify.internal.util
22

33
import io.ktor.client.HttpClient
4-
import io.ktor.client.engine.cio.CIO
4+
import io.ktor.client.engine.curl.Curl
55

6-
internal actual fun createPlatformHttpClient(): HttpClient = HttpClient(CIO)
6+
internal actual fun createPlatformHttpClient(): HttpClient = HttpClient(Curl)
Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.ntqqrev.acidify.internal.util
22

3-
import org.ntqqrev.androidhttps.executeTextRequest
4-
53
internal actual fun platformCurlTextRequestOrNull(
64
method: String,
75
url: String,
@@ -10,21 +8,4 @@ internal actual fun platformCurlTextRequestOrNull(
108
contentType: String?,
119
followRedirects: Boolean,
1210
proxy: String?,
13-
): PlatformCurlTextResponse? {
14-
if (!proxy.isNullOrBlank()) {
15-
return null
16-
}
17-
val response = executeTextRequest(
18-
method = method,
19-
url = url,
20-
headers = headers,
21-
body = body,
22-
contentType = contentType,
23-
followRedirects = followRedirects,
24-
)
25-
return PlatformCurlTextResponse(
26-
statusCode = response.statusCode,
27-
headers = response.headers,
28-
body = response.body,
29-
)
30-
}
11+
): PlatformCurlTextResponse? = null
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
@file:OptIn(ExperimentalKotlinGradlePluginApi::class)
2+
3+
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
4+
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
5+
6+
plugins {
7+
kotlin("multiplatform")
8+
}
9+
10+
kotlin {
11+
androidNativeArm64()
12+
13+
sourceSets {
14+
androidNativeMain.dependencies {
15+
implementation(libs.ktor.client.core)
16+
}
17+
}
18+
19+
targets.withType<KotlinNativeTarget> {
20+
val main by compilations.getting
21+
val nativeInterop by main.cinterops.creating {
22+
definitionFile.set(file("src/cinterop/curl.def"))
23+
includeDirs.allHeaders(file("src/cinterop/include"))
24+
extraOpts("-libraryPath", file("src/cinterop/lib"))
25+
}
26+
}
27+
28+
explicitApi()
29+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.client.engine.curl
6+
7+
import io.ktor.client.engine.*
8+
import io.ktor.utils.io.*
9+
import kotlinx.cinterop.*
10+
import libcurl.*
11+
12+
// This function is thread unsafe!
13+
// The man page asks to run it once per program,
14+
// while the program "is still single threaded", explicitly stating that
15+
// it should not called while any other thread is running.
16+
// See the curl_global_init(3) man page for details.
17+
@Suppress("DEPRECATION")
18+
@OptIn(ExperimentalStdlibApi::class)
19+
@EagerInitialization
20+
private val curlGlobalInitReturnCode = curlInitBridge()
21+
22+
@OptIn(ExperimentalForeignApi::class)
23+
internal fun curlInitBridge(): Int = curl_global_init(CURL_GLOBAL_ALL.convert()).convert()
24+
25+
@OptIn(ExperimentalStdlibApi::class)
26+
@Suppress("unused", "DEPRECATION")
27+
@EagerInitialization
28+
private val initHook = Curl
29+
30+
/**
31+
* A Kotlin/Native client engine that can be used on desktop platforms.
32+
*
33+
* To create the client with this engine, pass it to the `HttpClient` constructor:
34+
* ```kotlin
35+
* val client = HttpClient(Curl)
36+
* ```
37+
* To configure the engine, pass settings exposed by [CurlClientEngineConfig] to the `engine` method:
38+
* ```kotlin
39+
* val client = HttpClient(Curl) {
40+
* engine {
41+
* // this: CurlClientEngineConfig
42+
* }
43+
* }
44+
* ```
45+
*
46+
* You can learn more about client engines from [Engines](https://ktor.io/docs/http-client-engines.html).
47+
*
48+
* [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.client.engine.curl.Curl)
49+
*/
50+
@OptIn(InternalAPI::class)
51+
public data object Curl : HttpClientEngineFactory<CurlClientEngineConfig> {
52+
init {
53+
engines.append(this)
54+
}
55+
56+
override fun create(block: CurlClientEngineConfig.() -> Unit): HttpClientEngine {
57+
if (curlGlobalInitReturnCode != 0) {
58+
throw RuntimeException("curl_global_init() returned non-zero verify: $curlGlobalInitReturnCode")
59+
}
60+
61+
return CurlClientEngine(CurlClientEngineConfig().apply(block))
62+
}
63+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2014-2026 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.client.engine.curl
6+
7+
import io.ktor.client.engine.*
8+
import io.ktor.client.engine.curl.internal.*
9+
import io.ktor.client.plugins.*
10+
import io.ktor.client.plugins.sse.*
11+
import io.ktor.client.plugins.websocket.*
12+
import io.ktor.client.request.*
13+
import io.ktor.client.utils.*
14+
import io.ktor.http.*
15+
import io.ktor.http.cio.*
16+
import io.ktor.util.date.*
17+
import io.ktor.utils.io.*
18+
19+
internal class CurlClientEngine(
20+
override val config: CurlClientEngineConfig
21+
) : HttpClientEngineBase("ktor-curl") {
22+
23+
override val supportedCapabilities = setOf(HttpTimeoutCapability, WebSocketCapability, SSECapability)
24+
25+
private val curlProcessor = CurlProcessor(coroutineContext)
26+
27+
@OptIn(InternalAPI::class)
28+
override suspend fun execute(data: HttpRequestData): HttpResponseData {
29+
val callContext = callContext()
30+
31+
val requestTime = GMTDate()
32+
33+
val curlRequest = data.toCurlRequest(config)
34+
val responseData = curlProcessor.executeRequest(curlRequest)
35+
36+
return with(responseData) {
37+
val headerBytes = ByteReadChannel(headersBytes).apply {
38+
// Skip status line
39+
readLineStrict()
40+
}
41+
val rawHeaders = parseHeaders(headerBytes)
42+
val headers = rawHeaders
43+
.toBuilder().apply {
44+
dropCompressionHeaders(data.method, data.attributes)
45+
}.build()
46+
47+
rawHeaders.release()
48+
49+
val status = HttpStatusCode.fromValue(status)
50+
51+
val responseBody: Any = if (data.isUpgradeRequest()) {
52+
val wsConfig = data.attributes[WEBSOCKETS_KEY]
53+
val websocket = responseBody as CurlWebSocketResponseBody
54+
CurlWebSocketSession(
55+
websocket,
56+
callContext,
57+
wsConfig.channelsConfig.outgoing,
58+
curlProcessor,
59+
)
60+
} else {
61+
val httpResponse = responseBody as CurlHttpResponseBody
62+
data.attributes.getOrNull(ResponseAdapterAttributeKey)
63+
?.adapt(data, status, headers, httpResponse.bodyChannel, data.body, callContext)
64+
?: httpResponse.bodyChannel
65+
}
66+
67+
HttpResponseData(
68+
status,
69+
requestTime,
70+
headers,
71+
version.fromCurl(),
72+
responseBody,
73+
callContext
74+
)
75+
}
76+
}
77+
78+
override fun close() {
79+
super.close()
80+
curlProcessor.close()
81+
}
82+
}
83+
84+
@Deprecated("This exception will be removed in a future release in favor of a better error handling.")
85+
public class CurlIllegalStateException(cause: String) : IllegalStateException(cause)
86+
87+
@Deprecated("This exception will be removed in a future release in favor of a better error handling.")
88+
public class CurlRuntimeException(cause: String) : RuntimeException(cause)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright 2014-2021 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.client.engine.curl
6+
7+
import io.ktor.client.engine.*
8+
9+
/**
10+
* A configuration for the [Curl] client engine.
11+
*
12+
* [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.client.engine.curl.CurlClientEngineConfig)
13+
*/
14+
public class CurlClientEngineConfig : HttpClientEngineConfig() {
15+
/**
16+
* Forces proxy tunneling by setting `CURLOPT_HTTPPROXYTUNNEL`.
17+
*/
18+
internal var forceProxyTunneling: Boolean = false
19+
20+
/**
21+
* Sets path to Certificate Authority (CA) bundle using `CURLOPT_CAINFO`.
22+
*
23+
* [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.client.engine.curl.CurlClientEngineConfig.caInfo)
24+
*/
25+
public var caInfo: String? = null
26+
27+
/**
28+
* Sets directory that holds Certificate Authority (CA) certificates using `CURLOPT_CAPATH`.
29+
*
30+
* [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.client.engine.curl.CurlClientEngineConfig.caPath)
31+
*/
32+
public var caPath: String? = null
33+
34+
/**
35+
* Enables TLS host and certificate verification by setting the
36+
* `CURLOPT_SSL_VERIFYPEER` and `CURLOPT_SSL_VERIFYHOST` options.
37+
* Similar to `-k/--insecure` curl option.
38+
*
39+
* Setting this property to `false` is recommended only for testing purposes.
40+
*
41+
* [Report a problem](https://ktor.io/feedback/?fqname=io.ktor.client.engine.curl.CurlClientEngineConfig.sslVerify)
42+
*/
43+
public var sslVerify: Boolean = true
44+
}

0 commit comments

Comments
 (0)