diff --git a/docs/root/api/starting_envoy.rst b/docs/root/api/starting_envoy.rst index 70947fddd9..fb35f9b4f9 100644 --- a/docs/root/api/starting_envoy.rst +++ b/docs/root/api/starting_envoy.rst @@ -428,6 +428,37 @@ This can help negate the effect of head-of-line (HOL) blocking for slow connecti // Swift builder.h2ExtendKeepaliveTimeout(true) + +~~~~~~~~~~~~~~~~~~~~ +``addKeyValueStore`` +~~~~~~~~~~~~~~~~~~~~ + +Implementations of a public KeyValueStore interface may be added in their respective languages and +made available to the library. General usage is supported, but typical future usage will be in +support of HTTP and endpoint property caching. + +**Example**:: + + // Kotlin + builder.addKeyValueStore("io.envoyproxy.envoymobile.MyKeyValueStore", MyKeyValueStoreImpl()) + + // Swift + // Coming soon. + + +The library also contains a simple Android-specific KeyValueStore implementation based on Android's +SharedPreferences. + +**Example**:: + + // Android + val preferences = context.getSharedPreferences("io.envoyproxy.envoymobile.MyPreferences", Context.MODE_PRIVATE) + builder.addKeyValueStore("io.envoyproxy.envoymobile.MyKeyValueStore", SharedPreferencesStore(preferences)) + + // iOS + // Coming soon. + + ---------------------- Advanced configuration ---------------------- diff --git a/docs/root/intro/version_history.rst b/docs/root/intro/version_history.rst index fd1247bb5d..8a9f8da400 100644 --- a/docs/root/intro/version_history.rst +++ b/docs/root/intro/version_history.rst @@ -30,6 +30,7 @@ Features: - android: enable the filtering of unroutable families by default. (:issues: `#2267 <2267>`) - instrumentation: add timers and warnings to platform-provided callbacks (:issue: `#2300 <2300>`) - iOS: add support for integrating Envoy Mobile via the Swift Package Manager +- android: create simple persistent SharedPreferencesStore (:issue: `#2319 <2319>`) - iOS: A documentation archive is now included in the GitHub release artifact (:issue: `#2335 <2335>`) 0.4.6 (April 26, 2022) diff --git a/library/kotlin/io/envoyproxy/envoymobile/BUILD b/library/kotlin/io/envoyproxy/envoymobile/BUILD index faf074edd6..bda143dd19 100644 --- a/library/kotlin/io/envoyproxy/envoymobile/BUILD +++ b/library/kotlin/io/envoyproxy/envoymobile/BUILD @@ -22,6 +22,7 @@ kt_android_library( name = "envoy_lib", srcs = [ "AndroidEngineBuilder.kt", + "android/SharedPreferencesStore.kt", ], custom_package = "io.envoyproxy.envoymobile", manifest = "EnvoyManifest.xml", diff --git a/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt b/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt index ce67373e8f..6863592411 100644 --- a/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt +++ b/library/kotlin/io/envoyproxy/envoymobile/EngineBuilder.kt @@ -455,7 +455,7 @@ open class EngineBuilder( * @return this builder. */ fun addKeyValueStore(name: String, keyValueStore: KeyValueStore): EngineBuilder { - this.keyValueStores.put(name, EnvoyKeyValueStoreAdapter(keyValueStore)) + this.keyValueStores.put(name, keyValueStore) return this } diff --git a/library/kotlin/io/envoyproxy/envoymobile/KeyValueStore.kt b/library/kotlin/io/envoyproxy/envoymobile/KeyValueStore.kt index 50bc19d991..f3e97cf2d4 100644 --- a/library/kotlin/io/envoyproxy/envoymobile/KeyValueStore.kt +++ b/library/kotlin/io/envoyproxy/envoymobile/KeyValueStore.kt @@ -3,30 +3,7 @@ package io.envoyproxy.envoymobile import io.envoyproxy.envoymobile.engine.types.EnvoyKeyValueStore /** - * `KeyValueStore` is bridged through to `EnvoyKeyValueStore` to communicate with the engine. + * `KeyValueStore` is an interface that may be implemented to provide access to an arbitrary + * key-value store implementation that may be made accessible to native Envoy Mobile code. */ -class KeyValueStore constructor ( - val read: ((key: String) -> String?), - val remove: ((key: String) -> Unit), - val save: ((key: String, value: String) -> Unit) -) - -/** - * Class responsible for bridging between the platform-level `KeyValueStore` and the - * engine's `EnvoyKeyValueStore`. - */ -internal class EnvoyKeyValueStoreAdapter( - private val callbacks: KeyValueStore -) : EnvoyKeyValueStore { - override fun read(key: String): String? { - return callbacks.read(key) - } - - override fun remove(key: String) { - callbacks.remove(key) - } - - override fun save(key: String, value: String) { - callbacks.save(key, value) - } -} +interface KeyValueStore : EnvoyKeyValueStore diff --git a/library/kotlin/io/envoyproxy/envoymobile/android/SharedPreferencesStore.kt b/library/kotlin/io/envoyproxy/envoymobile/android/SharedPreferencesStore.kt new file mode 100644 index 0000000000..783f12f1de --- /dev/null +++ b/library/kotlin/io/envoyproxy/envoymobile/android/SharedPreferencesStore.kt @@ -0,0 +1,27 @@ +package io.envoyproxy.envoymobile.android + +import android.content.SharedPreferences + +import io.envoyproxy.envoymobile.KeyValueStore + +/** + * Simple implementation of a `KeyValueStore` leveraging `SharedPreferences` for persistence. + */ +class SharedPreferencesStore(sharedPreferences: SharedPreferences) : KeyValueStore { + private val preferences = sharedPreferences + private val editor = sharedPreferences.edit() + + override fun read(key: String): String? { + return preferences.getString(key, null) + } + + override fun remove(key: String) { + editor.remove(key) + editor.apply() + } + + override fun save(key: String, value: String) { + editor.putString(key, value) + editor.apply() + } +} diff --git a/test/kotlin/apps/experimental/MainActivity.kt b/test/kotlin/apps/experimental/MainActivity.kt index 5eb19b5fd9..db5897d21f 100644 --- a/test/kotlin/apps/experimental/MainActivity.kt +++ b/test/kotlin/apps/experimental/MainActivity.kt @@ -1,6 +1,7 @@ package io.envoyproxy.envoymobile.helloenvoykotlin import android.app.Activity +import android.content.Context import android.os.Bundle import android.os.Handler import android.os.HandlerThread @@ -8,6 +9,7 @@ import android.util.Log import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import io.envoyproxy.envoymobile.android.SharedPreferencesStore import io.envoyproxy.envoymobile.AndroidEngineBuilder import io.envoyproxy.envoymobile.Element import io.envoyproxy.envoymobile.Engine @@ -26,6 +28,7 @@ private const val REQUEST_HANDLER_THREAD_NAME = "hello_envoy_kt" private const val REQUEST_AUTHORITY = "api.lyft.com" private const val REQUEST_PATH = "/ping" private const val REQUEST_SCHEME = "https" +private const val PERSISTENCE_KEY = "EnvoyMobilePersistenceKey" private val FILTERED_HEADERS = setOf( "server", "filter-demo", @@ -45,6 +48,8 @@ class MainActivity : Activity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + val preferences = getSharedPreferences(PERSISTENCE_KEY, Context.MODE_PRIVATE) + engine = AndroidEngineBuilder(application) .addLogLevel(LogLevel.DEBUG) .addPlatformFilter(::DemoFilter) @@ -54,6 +59,7 @@ class MainActivity : Activity() { .enableInterfaceBinding(true) .addNativeFilter("envoy.filters.http.buffer", "{\"@type\":\"type.googleapis.com/envoy.extensions.filters.http.buffer.v3.Buffer\",\"max_request_bytes\":5242880}") .addStringAccessor("demo-accessor", { "PlatformString" }) + .addKeyValueStore("demo-kv-store", SharedPreferencesStore(preferences)) .setOnEngineRunning { Log.d("MainActivity", "Envoy async internal setup completed") } .setEventTracker({ for (entry in it.entries) { diff --git a/test/kotlin/integration/KeyValueStoreTest.kt b/test/kotlin/integration/KeyValueStoreTest.kt index a4ffee9fbb..e6d6ba12d6 100644 --- a/test/kotlin/integration/KeyValueStoreTest.kt +++ b/test/kotlin/integration/KeyValueStoreTest.kt @@ -68,11 +68,11 @@ class KeyValueStoreTest { val readExpectation = CountDownLatch(3) val saveExpectation = CountDownLatch(1) - val testKeyValueStore = KeyValueStore( - read = { _ -> readExpectation.countDown(); null }, - remove = { _ -> {}}, - save = { _, _ -> saveExpectation.countDown() } - ) + val testKeyValueStore = object : KeyValueStore { + override fun read(key: String): String? { readExpectation.countDown(); return null } + override fun remove(key: String) {} + override fun save(key: String, value: String) { saveExpectation.countDown() } + } val engine = EngineBuilder(Custom(config)) .addKeyValueStore("envoy.key_value.platform_test", testKeyValueStore)