diff --git a/.github/workflows/publish_android.yml b/.github/workflows/publish_android.yml
index 26730ba3..b09f1515 100644
--- a/.github/workflows/publish_android.yml
+++ b/.github/workflows/publish_android.yml
@@ -23,6 +23,8 @@ jobs:
:flocon:publishToMavenCentral \
:flocon-no-op:publishToMavenCentral \
:okhttp-interceptor:publishToMavenCentral \
+ :grpc-interceptor-base:publishToMavenCentral \
+ :grpc-interceptor-lite:publishToMavenCentral \
:grpc-interceptor:publishToMavenCentral \
:ktor-interceptor:publishToMavenCentral \
--no-configuration-cache
diff --git a/FloconAndroid/.idea/gradle.xml b/FloconAndroid/.idea/gradle.xml
index 4b005bf0..3449fbee 100644
--- a/FloconAndroid/.idea/gradle.xml
+++ b/FloconAndroid/.idea/gradle.xml
@@ -15,6 +15,8 @@
+
+
diff --git a/FloconAndroid/app/build.gradle.kts b/FloconAndroid/app/build.gradle.kts
index facfe7c3..5269d6b2 100644
--- a/FloconAndroid/app/build.gradle.kts
+++ b/FloconAndroid/app/build.gradle.kts
@@ -66,19 +66,20 @@ android {
}
}
-val useMaven = false
+val useMaven = true
dependencies {
if(useMaven) {
- val floconVersion = "1.0.5"
+ val floconVersion = "1.1.2"
implementation("io.github.openflocon:flocon:$floconVersion")
//implementation("io.github.openflocon:flocon-no-op:$floconVersion")
- implementation("io.github.openflocon:flocon-grpc-interceptor:$floconVersion")
+ implementation("io.github.openflocon:flocon-grpc-interceptor-lite:$floconVersion")
implementation("io.github.openflocon:flocon-okhttp-interceptor:$floconVersion")
+ implementation("io.github.openflocon:flocon-ktor-interceptor:$floconVersion")
} else {
debugImplementation(project(":flocon"))
releaseImplementation(project(":flocon-no-op"))
implementation(project(":okhttp-interceptor"))
- implementation(project(":grpc-interceptor"))
+ implementation(project(":grpc-interceptor-lite"))
implementation(project(":ktor-interceptor"))
}
@@ -106,7 +107,7 @@ dependencies {
// region grpc
implementation(libs.grpc.android)
- implementation(libs.grpc.kotin.stub)
+ implementation(libs.grpc.kotlin.stub)
implementation(libs.grpc.protobuf.lite)
implementation(libs.grpc.okhttp)
implementation(libs.protobuf.kotlin.lite)
diff --git a/FloconAndroid/app/src/main/java/io/github/openflocon/flocon/myapplication/grpc/IntiializeGrpc.kt b/FloconAndroid/app/src/main/java/io/github/openflocon/flocon/myapplication/grpc/InitializeGrpc.kt
similarity index 84%
rename from FloconAndroid/app/src/main/java/io/github/openflocon/flocon/myapplication/grpc/IntiializeGrpc.kt
rename to FloconAndroid/app/src/main/java/io/github/openflocon/flocon/myapplication/grpc/InitializeGrpc.kt
index 3ccf37c2..3015343c 100644
--- a/FloconAndroid/app/src/main/java/io/github/openflocon/flocon/myapplication/grpc/IntiializeGrpc.kt
+++ b/FloconAndroid/app/src/main/java/io/github/openflocon/flocon/myapplication/grpc/InitializeGrpc.kt
@@ -1,12 +1,11 @@
package io.github.openflocon.flocon.myapplication.grpc
-import io.github.openflocon.flocon.grpc.FloconGrpcInterceptor
+import io.github.openflocon.flocon.grpc.lite.FloconGrpcInterceptor
import io.grpc.CallOptions
import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder
import io.grpc.examples.helloworld.GreeterGrpcKt
import io.grpc.examples.helloworld.helloRequest
-import kotlin.getValue
object GrpcController {
val channel: ManagedChannel by lazy {
@@ -19,7 +18,7 @@ object GrpcController {
.build()
}
- val geeterClient by lazy {
+ val greeterClient by lazy {
GreeterGrpcKt.GreeterCoroutineStub(
channel = channel,
callOptions = CallOptions.DEFAULT,
@@ -31,7 +30,7 @@ object GrpcController {
val request = helloRequest {
name = "florent"
}
- val response = geeterClient.sayHello(request)
+ val response = greeterClient.sayHello(request)
return response.message
} catch (t: Throwable) {
t.printStackTrace()
diff --git a/FloconAndroid/gradle/libs.versions.toml b/FloconAndroid/gradle/libs.versions.toml
index 9d65f322..95e8cabd 100644
--- a/FloconAndroid/gradle/libs.versions.toml
+++ b/FloconAndroid/gradle/libs.versions.toml
@@ -51,7 +51,8 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3"
# for grpc
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
grpc-android = { group = "io.grpc", name = "grpc-android", version.ref = "grpc" }
-grpc-kotin-stub = { group = "io.grpc", name = "grpc-kotlin-stub", version.ref = "grpcKotlin" }
+grpc-okhttp = { group = "io.grpc", name = "grpc-okhttp", version.ref = "grpc" }
+grpc-kotlin-stub = { group = "io.grpc", name = "grpc-kotlin-stub", version.ref = "grpcKotlin" }
grpc-protobuf-lite = { group = "io.grpc", name = "grpc-protobuf-lite", version.ref = "grpc" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" }
kotlinx-coroutines-bom = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-bom", version.ref = "kotlinxCoroutinesBom" }
@@ -65,7 +66,7 @@ okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp" }
org-jetbrains-kotlinx-kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android" }
org-jetbrains-kotlinx-kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core" }
protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf"}
-grpc-okhttp = { group = "io.grpc", name = "grpc-okhttp", version.ref = "grpc" }
+protobuf-util = { group = "com.google.protobuf", name = "protobuf-java-util", version.ref = "protobuf" }
squareup-okhttp = { module = "com.squareup.okhttp3:okhttp" }
[plugins]
diff --git a/FloconAndroid/grpc-interceptor-base/.gitignore b/FloconAndroid/grpc-interceptor-base/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-base/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor-base/build.gradle.kts b/FloconAndroid/grpc-interceptor-base/build.gradle.kts
new file mode 100644
index 00000000..6541500c
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-base/build.gradle.kts
@@ -0,0 +1,88 @@
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+ id("com.vanniktech.maven.publish") version "0.34.0"
+}
+
+android {
+ namespace = "io.github.openflocon.flocon.grpc.base"
+ compileSdk = 36
+
+ defaultConfig {
+ minSdk = 24
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+}
+
+dependencies {
+ implementation(project(":flocon-base"))
+
+ implementation(platform(libs.kotlinx.coroutines.bom))
+ implementation(libs.kotlinx.coroutines.core)
+
+ implementation(libs.grpc.android)
+}
+
+
+mavenPublishing {
+ publishToMavenCentral(automaticRelease = true)
+
+ if (project.hasProperty("signing.required") && project.property("signing.required") == "false") {
+ // Skip signing
+ } else {
+ signAllPublications()
+ }
+
+ coordinates(
+ groupId = project.property("floconGroupId") as String,
+ artifactId = "flocon-grpc-interceptor-base",
+ version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String
+ )
+
+
+ pom {
+ name = "Flocon Grpc Interceptor"
+ description = project.property("floconDescription") as String
+ inceptionYear = "2025"
+ url = "https://github.com/openflocon/Flocon"
+ licenses {
+ license {
+ name = "The Apache License, Version 2.0"
+ url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ }
+ }
+ developers {
+ developer {
+ id = "openflocon"
+ name = "Open Flocon"
+ url = "https://github.com/openflocon"
+ }
+ }
+ scm {
+ url = "https://github.com/openflocon/Flocon"
+ connection = "scm:git:git://github.com/openflocon/Flocon.git"
+ developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git"
+ }
+ }
+}
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor-base/consumer-rules.pro b/FloconAndroid/grpc-interceptor-base/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/FloconAndroid/grpc-interceptor-base/proguard-rules.pro b/FloconAndroid/grpc-interceptor-base/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-base/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor-base/src/main/AndroidManifest.xml b/FloconAndroid/grpc-interceptor-base/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..74b7379f
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-base/src/main/AndroidManifest.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/BadQuality.kt b/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/BadQuality.kt
similarity index 100%
rename from FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/BadQuality.kt
rename to FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/BadQuality.kt
diff --git a/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcBaseFormatter.kt b/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcBaseFormatter.kt
new file mode 100644
index 00000000..c5ebcbd2
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcBaseFormatter.kt
@@ -0,0 +1,6 @@
+package io.github.openflocon.flocon.grpc
+
+interface FloconGrpcBaseFormatter {
+
+ fun format(message: T): String
+}
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcBaseInterceptor.kt b/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcBaseInterceptor.kt
new file mode 100644
index 00000000..c4fea098
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcBaseInterceptor.kt
@@ -0,0 +1,168 @@
+package io.github.openflocon.flocon.grpc
+
+import io.github.openflocon.flocon.FloconApp
+import io.github.openflocon.flocon.FloconLogger
+import io.github.openflocon.flocon.grpc.model.RequestHolder
+import io.github.openflocon.flocon.grpc.model.toHeaders
+import io.github.openflocon.flocon.plugins.network.FloconNetworkPlugin
+import io.github.openflocon.flocon.plugins.network.model.FloconNetworkRequest
+import io.github.openflocon.flocon.plugins.network.model.FloconNetworkResponse
+import io.grpc.CallOptions
+import io.grpc.Channel
+import io.grpc.ClientCall
+import io.grpc.ClientInterceptor
+import io.grpc.ForwardingClientCall
+import io.grpc.ForwardingClientCallListener
+import io.grpc.Metadata
+import io.grpc.MethodDescriptor
+import io.grpc.Status
+import kotlinx.coroutines.runBlocking
+import java.util.UUID
+
+abstract class FloconGrpcBaseInterceptor : ClientInterceptor {
+
+ private val floconGrpcPlugin = FloconGrpcPlugin()
+
+ abstract val floconGrpcFormatter: FloconGrpcBaseFormatter
+
+ override fun interceptCall(
+ method: MethodDescriptor,
+ callOptions: CallOptions,
+ next: Channel,
+ ): ClientCall {
+ val networkPlugin = FloconApp.instance?.client?.networkPlugin
+ if (networkPlugin == null) {
+ // do not intercept if no network plugin, just call
+ return next.newCall(method, callOptions)
+ }
+
+ val callId = UUID.randomUUID().toString()
+ return LoggingForwardingClientCall(
+ floconGrpcPlugin = floconGrpcPlugin,
+ floconNetworkPlugin = networkPlugin,
+ callId = callId,
+ method = method,
+ next = next,
+ formatter = floconGrpcFormatter,
+ callOptions = callOptions,
+ )
+ }
+}
+
+private class LoggingForwardingClientCall(
+ private val floconNetworkPlugin: FloconNetworkPlugin,
+ private val floconGrpcPlugin: FloconGrpcPlugin,
+ private val callId: String,
+ private val method: MethodDescriptor,
+ private val next: Channel,
+ private val formatter: FloconGrpcBaseFormatter,
+ callOptions: CallOptions,
+) : ForwardingClientCall.SimpleForwardingClientCall(
+ next.newCall(
+ method,
+ callOptions,
+ ),
+) {
+
+ val requestHolder = RequestHolder()
+
+ private var headers: Metadata? = null
+
+ override fun start(responseListener: Listener, headers: Metadata) {
+ this.headers = headers
+ super.start(
+ LoggingClientCallListener(
+ floconGrpcPlugin = floconGrpcPlugin,
+ callId = callId,
+ requestHolder = requestHolder,
+ responseListener = responseListener,
+ formatter = formatter,
+ ),
+ headers,
+ )
+ }
+
+ override fun sendMessage(message: ReqT) {
+ val request = FloconNetworkRequest(
+ url = next.authority(),
+ method = method.fullMethodName,
+ body = formatter.format(message),
+ startTime = System.currentTimeMillis(),
+ headers = headers?.toHeaders().orEmpty(),
+ size = 0, // TODO
+ isMocked = false, // cannot mock grpc
+ )
+ requestHolder.request.complete(request)
+ floconGrpcPlugin.reportRequest(
+ callId = callId,
+ request = request
+ )
+ floconNetworkPlugin.badQualityConfig?.let {
+ executeBadQuality(it)
+ }
+ super.sendMessage(message)
+ }
+}
+
+private class LoggingClientCallListener(
+ private val floconGrpcPlugin: FloconGrpcPlugin,
+ private val callId: String,
+ responseListener: ClientCall.Listener,
+ private val requestHolder: RequestHolder,
+ private val formatter: FloconGrpcBaseFormatter,
+) : ForwardingClientCallListener.SimpleForwardingClientCallListener(
+ responseListener,
+) {
+
+ private var headers: Metadata? = null
+ private var message: RespT? = null
+
+ override fun onClose(status: Status, trailers: Metadata) {
+ try {
+ runBlocking { requestHolder.request.await() }.let { request ->
+ status.cause?.let { cause ->
+ floconGrpcPlugin.reportResponse(
+ callId = callId,
+ request = request,
+ response = FloconNetworkResponse(
+ body = null,
+ headers = emptyMap(),
+ httpCode = null,
+ contentType = "grpc",
+ size = null,
+ grpcStatus = null,
+ error = cause.message ?: cause.javaClass.simpleName,
+ ),
+ )
+ } ?: run {
+ floconGrpcPlugin.reportResponse(
+ callId = callId,
+ request = request,
+ response = FloconNetworkResponse(
+ body = formatter.format(message),
+ headers = (this.headers ?: trailers).toHeaders(),
+ httpCode = null,
+ contentType = "grpc",
+ size = 0L,
+ grpcStatus = status.code.toString(),
+ error = null,
+ ),
+ )
+ }
+ }
+ } catch (t: Throwable) {
+ FloconLogger.logError("cannot find request for callId $callId", t)
+ }
+ super.onClose(status, trailers)
+ }
+
+ override fun onMessage(message: RespT) {
+ super.onMessage(message)
+ this.message = message
+ }
+
+ override fun onHeaders(headers: Metadata?) {
+ super.onHeaders(headers)
+ this.headers = headers
+ }
+}
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcPlugin.kt b/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcPlugin.kt
similarity index 100%
rename from FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcPlugin.kt
rename to FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcPlugin.kt
diff --git a/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/model/GrpcHeader.kt b/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/model/GrpcHeader.kt
similarity index 100%
rename from FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/model/GrpcHeader.kt
rename to FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/model/GrpcHeader.kt
diff --git a/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/model/RequestHolder.kt b/FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/model/RequestHolder.kt
similarity index 100%
rename from FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/model/RequestHolder.kt
rename to FloconAndroid/grpc-interceptor-base/src/main/java/io/github/openflocon/flocon/grpc/model/RequestHolder.kt
diff --git a/FloconAndroid/grpc-interceptor-lite/.gitignore b/FloconAndroid/grpc-interceptor-lite/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-lite/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor-lite/build.gradle.kts b/FloconAndroid/grpc-interceptor-lite/build.gradle.kts
new file mode 100644
index 00000000..7e06b316
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-lite/build.gradle.kts
@@ -0,0 +1,86 @@
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+ id("com.vanniktech.maven.publish") version "0.34.0"
+}
+
+android {
+ namespace = "io.github.openflocon.flocon.grpc.lite"
+ compileSdk = 36
+
+ defaultConfig {
+ minSdk = 24
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles("consumer-rules.pro")
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+}
+
+dependencies {
+ api(project(":grpc-interceptor-base"))
+
+ implementation(libs.grpc.android)
+ implementation(libs.gson)
+}
+
+
+mavenPublishing {
+ publishToMavenCentral(automaticRelease = true)
+
+ if (project.hasProperty("signing.required") && project.property("signing.required") == "false") {
+ // Skip signing
+ } else {
+ signAllPublications()
+ }
+
+ coordinates(
+ groupId = project.property("floconGroupId") as String,
+ artifactId = "flocon-grpc-interceptor-lite",
+ version = System.getenv("PROJECT_VERSION_NAME") ?: project.property("floconVersion") as String
+ )
+
+
+ pom {
+ name = "Flocon Grpc Interceptor Lite"
+ description = project.property("floconDescription") as String
+ inceptionYear = "2025"
+ url = "https://github.com/openflocon/Flocon"
+ licenses {
+ license {
+ name = "The Apache License, Version 2.0"
+ url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt"
+ }
+ }
+ developers {
+ developer {
+ id = "openflocon"
+ name = "Open Flocon"
+ url = "https://github.com/openflocon"
+ }
+ }
+ scm {
+ url = "https://github.com/openflocon/Flocon"
+ connection = "scm:git:git://github.com/openflocon/Flocon.git"
+ developerConnection = "scm:git:ssh://git@github.com/openflocon/Flocon.git"
+ }
+ }
+}
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor-lite/consumer-rules.pro b/FloconAndroid/grpc-interceptor-lite/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/FloconAndroid/grpc-interceptor-lite/proguard-rules.pro b/FloconAndroid/grpc-interceptor-lite/proguard-rules.pro
new file mode 100644
index 00000000..481bb434
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-lite/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor-lite/src/main/AndroidManifest.xml b/FloconAndroid/grpc-interceptor-lite/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..a5918e68
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-lite/src/main/AndroidManifest.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor-lite/src/main/java/io/github/openflocon/flocon/grpc/lite/FloconGrpcFormatter.kt b/FloconAndroid/grpc-interceptor-lite/src/main/java/io/github/openflocon/flocon/grpc/lite/FloconGrpcFormatter.kt
new file mode 100644
index 00000000..098352c0
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-lite/src/main/java/io/github/openflocon/flocon/grpc/lite/FloconGrpcFormatter.kt
@@ -0,0 +1,51 @@
+package io.github.openflocon.flocon.grpc.lite
+
+import com.google.gson.ExclusionStrategy
+import com.google.gson.FieldAttributes
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import io.github.openflocon.flocon.grpc.FloconGrpcBaseFormatter
+
+class FloconGrpcFormatter : FloconGrpcBaseFormatter {
+
+ private val excluded = setOf(
+ "unknownFields",
+ "memoizedHashCode",
+ "bitField",
+ "memoizedSerializedSize",
+ "bytes",
+ )
+
+ private val defaultFieldExcluder: (name: String) -> Boolean = { name ->
+ var isExcluded = false
+ excluded.forEach { toExclude ->
+ if (name.startsWith(prefix = toExclude)) {
+ isExcluded = true
+ }
+ }
+ isExcluded
+ }
+
+ private val gson = buildGsonInstance(defaultFieldExcluder)
+
+ override fun format(message: T): String =
+ gson.toJson(message)
+
+ private fun buildGsonInstance(
+ excluder: (name: String) -> Boolean,
+ ): Gson {
+ return GsonBuilder().setPrettyPrinting()
+ .setFieldNamingStrategy {
+ it.name.removeSuffix("_")
+ }
+ .setExclusionStrategies(object : ExclusionStrategy {
+ override fun shouldSkipField(f: FieldAttributes): Boolean {
+ return excluder(f.name)
+ }
+
+ override fun shouldSkipClass(clazz: Class<*>): Boolean {
+ return false
+ }
+ }).create()
+ }
+}
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor-lite/src/main/java/io/github/openflocon/flocon/grpc/lite/FloconGrpcInterceptor.kt b/FloconAndroid/grpc-interceptor-lite/src/main/java/io/github/openflocon/flocon/grpc/lite/FloconGrpcInterceptor.kt
new file mode 100644
index 00000000..ac5a2158
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor-lite/src/main/java/io/github/openflocon/flocon/grpc/lite/FloconGrpcInterceptor.kt
@@ -0,0 +1,9 @@
+package io.github.openflocon.flocon.grpc.lite
+
+import io.github.openflocon.flocon.grpc.FloconGrpcBaseFormatter
+import io.github.openflocon.flocon.grpc.FloconGrpcBaseInterceptor
+
+class FloconGrpcInterceptor : FloconGrpcBaseInterceptor() {
+
+ override val floconGrpcFormatter: FloconGrpcBaseFormatter = FloconGrpcFormatter()
+}
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor/build.gradle.kts b/FloconAndroid/grpc-interceptor/build.gradle.kts
index 6e670020..27f12de3 100644
--- a/FloconAndroid/grpc-interceptor/build.gradle.kts
+++ b/FloconAndroid/grpc-interceptor/build.gradle.kts
@@ -35,13 +35,10 @@ android {
}
dependencies {
- implementation(project(":flocon-base"))
+ api(project(":grpc-interceptor-base"))
- implementation(platform(libs.kotlinx.coroutines.bom))
- implementation(libs.kotlinx.coroutines.core)
-
- implementation(libs.gson)
implementation(libs.grpc.android)
+ implementation(libs.protobuf.util)
}
diff --git a/FloconAndroid/grpc-interceptor/src/main/AndroidManifest.xml b/FloconAndroid/grpc-interceptor/src/main/AndroidManifest.xml
index 74b7379f..a5918e68 100644
--- a/FloconAndroid/grpc-interceptor/src/main/AndroidManifest.xml
+++ b/FloconAndroid/grpc-interceptor/src/main/AndroidManifest.xml
@@ -1,3 +1,4 @@
+
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcFormatter.kt b/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcFormatter.kt
new file mode 100644
index 00000000..e87c7394
--- /dev/null
+++ b/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcFormatter.kt
@@ -0,0 +1,12 @@
+package io.github.openflocon.flocon.grpc
+
+import com.google.protobuf.MessageOrBuilder
+import com.google.protobuf.util.JsonFormat
+
+class FloconGrpcFormatter : FloconGrpcBaseFormatter {
+
+ private val printer = JsonFormat.printer().alwaysPrintFieldsWithNoPresence()
+
+ override fun format(message: T): String =
+ (message as? MessageOrBuilder)?.let { printer.print(it) } ?: ""
+}
\ No newline at end of file
diff --git a/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcInterceptor.kt b/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcInterceptor.kt
index 69f1bd5d..5db69302 100644
--- a/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcInterceptor.kt
+++ b/FloconAndroid/grpc-interceptor/src/main/java/io/github/openflocon/flocon/grpc/FloconGrpcInterceptor.kt
@@ -1,212 +1,6 @@
package io.github.openflocon.flocon.grpc
-import com.google.gson.ExclusionStrategy
-import com.google.gson.FieldAttributes
-import com.google.gson.Gson
-import com.google.gson.GsonBuilder
-import io.github.openflocon.flocon.FloconApp
-import io.github.openflocon.flocon.FloconLogger
-import io.github.openflocon.flocon.grpc.model.RequestHolder
-import io.github.openflocon.flocon.grpc.model.toHeaders
-import io.github.openflocon.flocon.plugins.network.FloconNetworkPlugin
-import io.github.openflocon.flocon.plugins.network.model.FloconNetworkRequest
-import io.github.openflocon.flocon.plugins.network.model.FloconNetworkResponse
-import io.grpc.CallOptions
-import io.grpc.Channel
-import io.grpc.ClientCall
-import io.grpc.ClientInterceptor
-import io.grpc.ForwardingClientCall
-import io.grpc.ForwardingClientCallListener
-import io.grpc.Metadata
-import io.grpc.MethodDescriptor
-import io.grpc.Status
-import kotlinx.coroutines.runBlocking
-import java.util.UUID
+class FloconGrpcInterceptor : FloconGrpcBaseInterceptor() {
-private val excluded = setOf(
- "unknownFields",
- "memoizedHashCode",
- "bitField",
- "memoizedSerializedSize",
- "bytes",
-)
-
-private val defaultFieldExcluder: (name: String) -> Boolean = { name ->
- var isExcluded = false
- excluded.forEach { toExclude ->
- if (name.startsWith(prefix = toExclude)) {
- isExcluded = true
- }
- }
- isExcluded
-}
-
-class FloconGrpcInterceptor(
- private val shouldExcludeField: (name: String) -> Boolean = defaultFieldExcluder,
-) : ClientInterceptor {
-
- private val floconGrpcPlugin = FloconGrpcPlugin()
-
- private val gson = buildGsonInstance(shouldExcludeField)
-
- override fun interceptCall(
- method: MethodDescriptor,
- callOptions: CallOptions,
- next: Channel,
- ): ClientCall {
- val networkPlugin = FloconApp.instance?.client?.networkPlugin
- if (networkPlugin == null) {
- // do not intercept if no network plugin, just call
- return next.newCall(method, callOptions)
- }
-
- val callId = UUID.randomUUID().toString()
- return LoggingForwardingClientCall(
- floconGrpcPlugin = floconGrpcPlugin,
- floconNetworkPlugin = networkPlugin,
- callId = callId,
- method = method,
- next = next,
- callOptions = callOptions,
- gson = gson,
- )
- }
-}
-
-private class LoggingForwardingClientCall(
- private val floconNetworkPlugin: FloconNetworkPlugin,
- private val floconGrpcPlugin: FloconGrpcPlugin,
- private val callId: String,
- private val method: MethodDescriptor,
- private val next: Channel,
- callOptions: CallOptions,
- private val gson: Gson,
-) : ForwardingClientCall.SimpleForwardingClientCall(
- next.newCall(
- method,
- callOptions,
- ),
-) {
-
- val requestHolder = RequestHolder()
-
- private var headers: Metadata? = null
-
- override fun start(responseListener: Listener, headers: Metadata) {
- this.headers = headers
- super.start(
- LoggingClientCallListener(
- floconGrpcPlugin = floconGrpcPlugin,
- callId = callId,
- requestHolder = requestHolder,
- responseListener = responseListener,
- gson = gson
- ),
- headers,
- )
- }
-
- override fun sendMessage(message: ReqT) {
- val request = FloconNetworkRequest(
- url = next.authority(),
- method = method.fullMethodName,
- body = message?.toJson(gson = gson) ?: "",
- startTime = System.currentTimeMillis(),
- headers = headers?.toHeaders().orEmpty(),
- size = 0, // TODO
- isMocked = false, // cannot mock grpc
- )
- requestHolder.request.complete(request)
- floconGrpcPlugin.reportRequest(
- callId = callId,
- request = request
- )
- floconNetworkPlugin.badQualityConfig?.let {
- executeBadQuality(it)
- }
- super.sendMessage(message)
- }
-}
-
-private class LoggingClientCallListener(
- private val floconGrpcPlugin: FloconGrpcPlugin,
- private val callId: String,
- responseListener: ClientCall.Listener,
- private val gson: Gson,
- private val requestHolder: RequestHolder,
-) : ForwardingClientCallListener.SimpleForwardingClientCallListener(
- responseListener,
-) {
-
- private var headers: Metadata? = null
- private var message: RespT? = null
-
- override fun onClose(status: Status, trailers: Metadata) {
- try {
- runBlocking { requestHolder.request.await() }.let { request ->
- status.cause?.let { cause ->
- floconGrpcPlugin.reportResponse(
- callId = callId,
- request = request,
- response = FloconNetworkResponse(
- body = null,
- headers = emptyMap(),
- httpCode = null,
- contentType = "grpc",
- size = null,
- grpcStatus = null,
- error = cause.message ?: cause.javaClass.simpleName,
- ),
- )
- } ?: run {
- floconGrpcPlugin.reportResponse(
- callId = callId,
- request = request,
- response = FloconNetworkResponse(
- body = message?.toJson(gson),
- headers = (this.headers ?: trailers).toHeaders(),
- httpCode = null,
- contentType = "grpc",
- size = 0L,
- grpcStatus = status.code.toString(),
- error = null,
- ),
- )
- }
- }
- } catch (t: Throwable) {
- FloconLogger.logError("cannot find request for callId $callId", t)
- }
- super.onClose(status, trailers)
- }
-
- override fun onMessage(message: RespT) {
- super.onMessage(message)
- this.message = message
- }
-
- override fun onHeaders(headers: Metadata?) {
- super.onHeaders(headers)
- this.headers = headers
- }
-}
-
-private fun buildGsonInstance(
- excluder: (name: String) -> Boolean,
-): Gson {
- return GsonBuilder().setPrettyPrinting()
- .setFieldNamingStrategy {
- it.name.removeSuffix("_")
- }
- .setExclusionStrategies(object : ExclusionStrategy {
- override fun shouldSkipField(f: FieldAttributes): Boolean {
- return excluder(f.name)
- }
-
- override fun shouldSkipClass(clazz: Class<*>): Boolean {
- return false
- }
- }).create()
-}
-
-private fun Any.toJson(gson: Gson): String = gson.toJson(this)
+ override val floconGrpcFormatter: FloconGrpcBaseFormatter = FloconGrpcFormatter()
+}
\ No newline at end of file
diff --git a/FloconAndroid/publishLocal.sh b/FloconAndroid/publishLocal.sh
index 73b8ae87..72316b94 100755
--- a/FloconAndroid/publishLocal.sh
+++ b/FloconAndroid/publishLocal.sh
@@ -2,7 +2,9 @@
:flocon-base:assembleRelease \
:flocon:assembleRelease \
:flocon-no-op:assembleRelease \
+ :grpc-interceptor-base:assembleRelease \
:grpc-interceptor:assembleRelease \
+ :grpc-interceptor-lite:assembleRelease \
:okhttp-interceptor:assembleRelease \
:ktor-interceptor:assembleRelease
@@ -10,7 +12,9 @@
:flocon-base:publishToMavenLocal \
:flocon:publishToMavenLocal \
:flocon-no-op:publishToMavenLocal \
+ :grpc-interceptor-base:publishToMavenLocal \
:grpc-interceptor:publishToMavenLocal \
+ :grpc-interceptor-lite:publishToMavenLocal \
:okhttp-interceptor:publishToMavenLocal \
:ktor-interceptor:publishToMavenLocal \
-Psigning.required=false
\ No newline at end of file
diff --git a/FloconAndroid/settings.gradle.kts b/FloconAndroid/settings.gradle.kts
index ee4b0b2b..a5dc8b5e 100644
--- a/FloconAndroid/settings.gradle.kts
+++ b/FloconAndroid/settings.gradle.kts
@@ -21,4 +21,6 @@ include(":flocon")
include(":flocon-no-op")
include(":okhttp-interceptor")
include(":grpc-interceptor")
+include(":grpc-interceptor-base")
+include(":grpc-interceptor-lite")
include(":ktor-interceptor")
diff --git a/README.md b/README.md
index 9fef379e..f4008c27 100644
--- a/README.md
+++ b/README.md
@@ -373,10 +373,32 @@ Similar to network inteceptions, Flocon works with grpc
it works with `io.grpc:grpc-android` : https://github.com/grpc/grpc-java
-⚠️ please ensure your version is at lease `1.70.0` ⚠️
+> [!WARNING]
+> please ensure your version is at lease `1.70.0`
[](https://search.maven.org/artifact/io.github.openflocon/flocon-grpc-interceptor)
+> [!IMPORTANT]
+> While dealing with protobuf on Android projects, it's best to use its lighter artifact (protobuf-javalite or protobuf-kotlin-lite).
+> It might be that your project needs the larger protobuf version (protobuf-java or protobuf-kotlin).
+> Flocon offers two interceptor artifacts that leverage a different JSON formatter. It declutters the JSON printing by removing unwanted fields with a dedicated formatter depending on the protobuf library.
+> Make sure you choose the correct artifact.
+
+```
+ // If you're using protobuf-javalite or protobuf-kotlin-lite
+implementation("com.google.protobuf:protobuf-kotlin-lite:$PROTOBUF_VERSION")
+
+implementation("io.github.openflocon:grpc-interceptor-lite:LAST_VERSION")
+```
+or
+```
+// If you're using protobuf-java or protobuf-kotlin
+implementation("com.google.protobuf:protobuf-java:$PROTOBUF_VERSION")
+
+implementation("io.github.openflocon:grpc-interceptor:LAST_VERSION")
+```
+
+
```kotlin
ManagedChannelBuilder
...