From 01ebb11708dd505c35abe6e6a6db7714ba3ddeac Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Tue, 13 Sep 2022 12:05:02 +0530 Subject: [PATCH 01/25] Update logic for finding optimal keepalive (#41) * Reset state when optimal keepalive is failed * Update keepalive handling logic on failure * Reset state on network change without connected check * Fix unit tests --- .../keepalive/AdaptiveKeepAliveStateHandler.kt | 8 ++++---- .../gojek/keepalive/OptimalKeepAliveCalculator.kt | 9 ++++----- .../keepalive/OptimalKeepAliveCalculatorTest.kt | 15 ++------------- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/adaptive-keep-alive/src/main/java/com/gojek/keepalive/AdaptiveKeepAliveStateHandler.kt b/adaptive-keep-alive/src/main/java/com/gojek/keepalive/AdaptiveKeepAliveStateHandler.kt index 20f5467c..60377261 100644 --- a/adaptive-keep-alive/src/main/java/com/gojek/keepalive/AdaptiveKeepAliveStateHandler.kt +++ b/adaptive-keep-alive/src/main/java/com/gojek/keepalive/AdaptiveKeepAliveStateHandler.kt @@ -98,7 +98,7 @@ internal class AdaptiveKeepAliveStateHandler( ) } else { val currentUpperBound = keepAlive.keepAliveMinutes - 1 - if (state.lastSuccessfulKA == currentUpperBound) { + if (state.lastSuccessfulKA >= currentUpperBound) { state = state.copy( currentUpperBound = currentUpperBound, isOptimalKeepAlive = true, @@ -191,8 +191,7 @@ internal class AdaptiveKeepAliveStateHandler( keepAlive.keepAliveMinutes == state.currentKA } - @VisibleForTesting - internal fun resetState() { + fun resetState() { state = state.copy( lastSuccessfulKA = state.lowerBound - state.step, isOptimalKeepAlive = false, @@ -201,7 +200,8 @@ internal class AdaptiveKeepAliveStateHandler( currentKA = -1, currentKAFailureCount = 0, probeCount = 0, - convergenceTime = 0 + convergenceTime = 0, + optimalKAFailureCount = 0 ) } diff --git a/adaptive-keep-alive/src/main/java/com/gojek/keepalive/OptimalKeepAliveCalculator.kt b/adaptive-keep-alive/src/main/java/com/gojek/keepalive/OptimalKeepAliveCalculator.kt index f61b6022..8f879658 100644 --- a/adaptive-keep-alive/src/main/java/com/gojek/keepalive/OptimalKeepAliveCalculator.kt +++ b/adaptive-keep-alive/src/main/java/com/gojek/keepalive/OptimalKeepAliveCalculator.kt @@ -20,11 +20,9 @@ internal class OptimalKeepAliveCalculator( object : NetworkStateListener { override fun onStateChanged(activeNetworkState: NetworkState) { synchronized(this) { - if (activeNetworkState.isConnected) { - val networkType = networkUtils.getNetworkType(activeNetworkState.netInfo) - val networkName = networkUtils.getNetworkName(activeNetworkState.netInfo) - onNetworkStateChanged(networkType, networkName) - } + val networkType = networkUtils.getNetworkType(activeNetworkState.netInfo) + val networkName = networkUtils.getNetworkName(activeNetworkState.netInfo) + onNetworkStateChanged(networkType, networkName) } } } @@ -108,6 +106,7 @@ internal class OptimalKeepAliveCalculator( stateHandler.updateOptimalKeepAliveFailureState() if (stateHandler.isOptimalKeepAliveFailureLimitExceeded()) { stateHandler.removeStateFromPersistence() + stateHandler.resetState() } } } diff --git a/adaptive-keep-alive/src/test/java/com/gojek/keepalive/OptimalKeepAliveCalculatorTest.kt b/adaptive-keep-alive/src/test/java/com/gojek/keepalive/OptimalKeepAliveCalculatorTest.kt index f5ca76d0..9b729073 100644 --- a/adaptive-keep-alive/src/test/java/com/gojek/keepalive/OptimalKeepAliveCalculatorTest.kt +++ b/adaptive-keep-alive/src/test/java/com/gojek/keepalive/OptimalKeepAliveCalculatorTest.kt @@ -39,10 +39,9 @@ class OptimalKeepAliveCalculatorTest { } @Test - fun `test onStateChanged should notify state handler when network is connected`() { + fun `test onStateChanged should notify state handler`() { val networkState = mock() val netInfo = mock() - whenever(networkState.isConnected).thenReturn(true) whenever(networkState.netInfo).thenReturn(netInfo) val networkType = 1 val networkName = "test-network" @@ -52,23 +51,12 @@ class OptimalKeepAliveCalculatorTest { optimalKeepAliveCalculator.networkStateListener.onStateChanged(networkState) verify(stateHandler).onNetworkChanged(networkType, networkName) - verify(networkState).isConnected verify(networkState, times(2)).netInfo verify(networkUtils).getNetworkType(netInfo) verify(networkUtils).getNetworkName(netInfo) verifyNoMoreInteractions(networkState) } - @Test - fun `test onStateChanged should not notify state handler when network is not connected`() { - val networkState = mock() - whenever(networkState.isConnected).thenReturn(false) - - optimalKeepAliveCalculator.networkStateListener.onStateChanged(networkState) - - verify(networkState).isConnected - } - @Test fun `test getUnderTrialKeepAlive when optimal keep alive is already found`() { val optimalKeepAlive = mock() @@ -271,6 +259,7 @@ class OptimalKeepAliveCalculatorTest { verify(stateHandler).updateOptimalKeepAliveFailureState() verify(stateHandler).isOptimalKeepAliveFailureLimitExceeded() verify(stateHandler).removeStateFromPersistence() + verify(stateHandler).resetState() } @Test From 13b166b9d97a8523dacbc59674533b824425f6a0 Mon Sep 17 00:00:00 2001 From: Anubhav Gupta Date: Wed, 28 Sep 2022 11:27:32 +0530 Subject: [PATCH 02/25] Improved TLS handling and add support for ALPN (#43) * Improved tls handling and add support for alpn * removed socket factory from connection config * applied spotless * added expeirmental config for new ssl flow * dump public api * corrected sample app * fixed test failures * removed redundant code and required checks * fixed review comments * converted paho to java module * updated docs * add back required checks * fixed broker docs link Co-authored-by: Anubhav Gupta --- adaptive-keep-alive/build.gradle.kts | 1 + .../com/gojek/courier/app/ui/MainActivity.kt | 23 +- build.gradle.kts | 6 +- buildSrc/src/main/kotlin/deps.kt | 1 + courier-auth-http/build.gradle.kts | 1 + docs/docs/ConnectionSetup.md | 28 +- docs/docs/ExperimentConfigs.md | 4 +- docs/docs/MqttConfiguration.md | 2 - docs/docs/NonStandardOptions.md | 20 +- mqtt-client/api/mqtt-client.api | 89 ++- mqtt-client/build.gradle.kts | 1 + .../mqtt/client/config/ExperimentConfigs.kt | 3 +- .../mqtt/client/config/MqttConfiguration.kt | 2 - .../client/config/v3/MqttV3Configuration.kt | 3 - .../mqtt/client/v3/impl/AndroidMqttClient.kt | 20 +- .../gojek/mqtt/connection/MqttConnection.kt | 43 +- .../connection/config/v3/ConnectionConfig.kt | 5 +- .../java/com/gojek/mqtt/model/KeepAlive.kt | 6 +- .../gojek/mqtt/model/MqttConnectOptions.kt | 224 +++++- .../v3/impl/MqttExceptionHandlerImplTest.kt | 36 +- network-tracker/build.gradle.kts | 1 + paho/build.gradle.kts | 12 +- paho/src/main/AndroidManifest.xml | 3 + .../paho/client/mqttv3/ConnectionSpec.kt | 375 ++++++++++ .../client/mqttv3/IExperimentsConfig.java | 2 + .../paho/client/mqttv3/MqttAsyncClient.java | 212 +++--- .../client/mqttv3/MqttConnectOptions.java | 44 ++ .../eclipse/paho/client/mqttv3/Protocol.kt | 5 + .../client/mqttv3/SuppressSignatureCheck.kt | 12 + .../org/eclipse/paho/client/mqttv3/Util.kt | 68 ++ .../mqttv3/internal/SSLNetworkModule.java | 9 +- .../mqttv3/internal/SSLNetworkModuleV2.java | 122 ++++ .../internal/platform/Android10Platform.kt | 89 +++ .../internal/platform/AndroidPlatform.kt | 155 ++++ .../internal/platform/BouncyCastlePlatform.kt | 104 +++ .../internal/platform/ConscryptPlatform.kt | 140 ++++ .../platform/Jdk8WithJettyBootPlatform.kt | 166 +++++ .../mqttv3/internal/platform/Jdk9Platform.kt | 105 +++ .../internal/platform/OpenJSSEPlatform.kt | 103 +++ .../mqttv3/internal/platform/Platform.kt | 284 ++++++++ .../android/Android10SocketAdapter.kt | 83 +++ .../android/AndroidCertificateChainCleaner.kt | 71 ++ .../platform/android/AndroidSocketAdapter.kt | 131 ++++ .../android/BouncyCastleSocketAdapter.kt | 68 ++ .../android/ConscryptSocketAdapter.kt | 65 ++ .../platform/android/DeferredSocketAdapter.kt | 64 ++ .../platform/android/SocketAdapter.kt | 36 + .../android/StandardAndroidSocketAdapter.kt | 74 ++ .../tls/BasicCertificateChainCleaner.kt | 139 ++++ .../internal/tls/BasicTrustRootIndex.kt | 55 ++ .../internal/tls/CertificateChainCleaner.kt | 49 ++ .../client/mqttv3/internal/tls/CipherSuite.kt | 665 ++++++++++++++++++ .../client/mqttv3/internal/tls/TlsVersion.kt | 52 ++ .../mqttv3/internal/tls/TrustRootIndex.kt | 23 + .../ExtendedByteArrayOutputStream.java | 12 + .../WebSocketSecureNetworkModuleV2.java | 132 ++++ .../org.mockito.plugins.MockMaker | 1 + pingsender/timer-pingsender/build.gradle.kts | 1 + .../build.gradle.kts | 2 +- .../workmanager-pingsender/build.gradle.kts | 2 +- 60 files changed, 4020 insertions(+), 234 deletions(-) create mode 100644 paho/src/main/AndroidManifest.xml create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/ConnectionSpec.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/Protocol.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/SuppressSignatureCheck.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/Util.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModuleV2.java create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Android10Platform.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/AndroidPlatform.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/BouncyCastlePlatform.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/ConscryptPlatform.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Jdk8WithJettyBootPlatform.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Jdk9Platform.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/OpenJSSEPlatform.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Platform.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/Android10SocketAdapter.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidCertificateChainCleaner.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/BouncyCastleSocketAdapter.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/ConscryptSocketAdapter.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/DeferredSocketAdapter.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/SocketAdapter.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/StandardAndroidSocketAdapter.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/BasicCertificateChainCleaner.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/BasicTrustRootIndex.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/CertificateChainCleaner.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/CipherSuite.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/TlsVersion.kt create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/TrustRootIndex.kt create mode 100755 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModuleV2.java create mode 100644 paho/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/adaptive-keep-alive/build.gradle.kts b/adaptive-keep-alive/build.gradle.kts index 7039018a..d55df42a 100644 --- a/adaptive-keep-alive/build.gradle.kts +++ b/adaptive-keep-alive/build.gradle.kts @@ -46,6 +46,7 @@ dependencies { implementation(project(":mqtt-pingsender")) implementation(project(":network-tracker")) + testImplementation(deps.android.test.mockitoCore) testImplementation(deps.android.test.kotlinTestJunit) } diff --git a/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt b/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt index a5c89f6e..83a821c5 100644 --- a/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt +++ b/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt @@ -92,23 +92,20 @@ class MainActivity : AppCompatActivity() { } private fun connectMqtt(clientId: String, username: String, password: String, ip: String, port: Int) { - val connectOptions = MqttConnectOptions( - serverUris = listOf(ServerUri(ip, port, if (port == 443) "ssl" else "tcp")), - clientId = clientId, - username = username, - keepAlive = KeepAlive( - timeSeconds = 30 - ), - isCleanSession = false, - password = password - ) + val connectOptions = MqttConnectOptions.Builder() + .serverUris(listOf(ServerUri(ip, port, if (port == 443) "ssl" else "tcp"))) + .clientId(clientId) + .userName(username) + .password(password) + .cleanSession(false) + .keepAlive(KeepAlive(timeSeconds = 30)) + .build() mqttClient.connect(connectOptions) } private fun initialiseCourier() { val mqttConfig = MqttV3Configuration( - socketFactory = null, logger = getLogger(), eventHandler = eventHandler, authenticator = object : Authenticator { @@ -116,7 +113,9 @@ class MainActivity : AppCompatActivity() { connectOptions: MqttConnectOptions, forceRefresh: Boolean ): MqttConnectOptions { - return connectOptions.copy(password = password.text.toString()) + return connectOptions.newBuilder() + .password(password.text.toString()) + .build() } }, mqttInterceptorList = listOf(MqttChuckInterceptor(this, MqttChuckConfig(retentionPeriod = Period.ONE_HOUR))), diff --git a/build.gradle.kts b/build.gradle.kts index a3c80aa8..9119f06d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,12 +41,12 @@ allprojects { subprojects { tasks.withType().configureEach { kotlinOptions { - jvmTarget = "1.8" + jvmTarget = "11" } } tasks.withType().configureEach { - sourceCompatibility = JavaVersion.VERSION_1_8.toString() - targetCompatibility = JavaVersion.VERSION_1_8.toString() + sourceCompatibility = JavaVersion.VERSION_11.toString() + targetCompatibility = JavaVersion.VERSION_11.toString() } } diff --git a/buildSrc/src/main/kotlin/deps.kt b/buildSrc/src/main/kotlin/deps.kt index 388f820e..fa26f2b9 100644 --- a/buildSrc/src/main/kotlin/deps.kt +++ b/buildSrc/src/main/kotlin/deps.kt @@ -59,6 +59,7 @@ object deps { const val runner = "androidx.test:runner:1.2.0" const val roboelectric = "org.robolectric:robolectric:4.2" const val mockito = "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0" + const val mockitoCore = "org.mockito:mockito-core:4.4.0" const val junitExt = "androidx.test.ext:junit:1.1.1" const val kotlinTestJunit = "org.jetbrains.kotlin:kotlin-test-junit:${versions.kotlin}" } diff --git a/courier-auth-http/build.gradle.kts b/courier-auth-http/build.gradle.kts index 974206fd..739c986d 100644 --- a/courier-auth-http/build.gradle.kts +++ b/courier-auth-http/build.gradle.kts @@ -30,6 +30,7 @@ dependencies { implementation(deps.square.retrofit) + testImplementation(deps.android.test.mockitoCore) testImplementation(deps.android.test.kotlinTestJunit) } diff --git a/docs/docs/ConnectionSetup.md b/docs/docs/ConnectionSetup.md index 04bedde3..4c64e8ef 100644 --- a/docs/docs/ConnectionSetup.md +++ b/docs/docs/ConnectionSetup.md @@ -14,16 +14,16 @@ val mqttClient = MqttClientFactory.create( ### Connect using MqttClient ~~~ kotlin -val connectOptions = MqttConnectOptions( - serverUris = listOf(ServerUri(SERVER_URI, SERVER_PORT)), - clientId = clientId, - username = username, - keepAlive = KeepAlive( - timeSeconds = keepAliveSeconds - ), - isCleanSession = cleanSessionFlag, - password = password -) +val alpnProtocol = "mqtt" +val connectOptions = MqttConnectOptions.Builder() + .serverUris(listof(ServerUri(SERVER_URI, SERVER_PORT))) + .clientId(clientId) + .userName(username) + .password(password) + .keepAlive(KeepAlive(timeSeconds = keepAliveSeconds)) + .cleanSession(cleanSessionFlag) + .alpnProtocols(listOf(Protocol(alpnProtocol))) + .build() mqttClient.connect(connectOptions) ~~~ @@ -56,6 +56,14 @@ mqttClient.disconnect() - **User properties** : Custom user properties appended to the CONNECT packet. +- **Socket Factory** : Sets the socket factory used to create connections. If unset, the **SocketFactory.getDefault** socket factory will be used. Set [shouldUseNewSSLFlow](ExperimentConfigs) to true to enable this. + +- **SSL Socket Factory**: Sets the socket factory and trust manager used to secure MQTT connections. If unset, the system defaults will be used. Set [shouldUseNewSSLFlow](ExperimentConfigs) to true to enable this. + +- **Connection Spec**: Specifies configuration for the socket connection that MQTT traffic travels through. This includes the TLS version and cipher suites to use when negotiating a secure connection. Set [shouldUseNewSSLFlow](ExperimentConfigs) to true to enable this. + +- **ALPN Protocols**: Configure the alpn protocols used by this client to communicate with MQTT broker. Set [shouldUseNewSSLFlow](ExperimentConfigs) to true to enable this. + [1]: https://github.com/gojek/courier-android/blob/main/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttClient.kt [2]: https://github.com/gojek/courier-android/blob/main/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt [3]: https://github.com/gojek/courier-android/blob/main/mqtt-client/src/main/java/com/gojek/mqtt/model/ServerUri.kt diff --git a/docs/docs/ExperimentConfigs.md b/docs/docs/ExperimentConfigs.md index 9fd5c0c8..a4f4f833 100644 --- a/docs/docs/ExperimentConfigs.md +++ b/docs/docs/ExperimentConfigs.md @@ -14,4 +14,6 @@ These are the experimentation configs used in Courier library. These are volatil - **incomingMessagesTTLSecs** : When there is no listener attached for an incoming message, messages are persisted for this interval. -- **incomingMessagesCleanupIntervalSecs** : Interval at which cleanup for incoming messages persistence is performed. \ No newline at end of file +- **incomingMessagesCleanupIntervalSecs** : Interval at which cleanup for incoming messages persistence is performed. + +- **shouldUseNewSSLFlow** : Set this true for enabling alpn protocol, custom ssl socket factory. \ No newline at end of file diff --git a/docs/docs/MqttConfiguration.md b/docs/docs/MqttConfiguration.md index a93f3a56..55a438e4 100644 --- a/docs/docs/MqttConfiguration.md +++ b/docs/docs/MqttConfiguration.md @@ -22,8 +22,6 @@ As we have seen earlier, [MqttClient][1] requires an instance of [MqttV3Configur - **Experimentation Configs** : These are the experiment configs used inside Courier library which are explained in detail [here](ExperimentConfigs). -- **Socket Factory** : An instance of SocketFactory can be passed to control the socket creation for the underlying MQTT connection. - - **WakeLock Timeout** : When positive value of this timeout is passed, a wakelock is acquired while creating the MQTT connection. By default, it is 0. [1]: https://github.com/gojek/courier-android/blob/main/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttClient.kt diff --git a/docs/docs/NonStandardOptions.md b/docs/docs/NonStandardOptions.md index 241e7445..641f6197 100644 --- a/docs/docs/NonStandardOptions.md +++ b/docs/docs/NonStandardOptions.md @@ -5,15 +5,17 @@ This option allows you to send user-properties in CONNECT packet for MQTT v3.1.1. ~~~ kotlin -val connectOptions = MqttConnectOptions( - serverUris = listOf(ServerUri(SERVER_URI, SERVER_PORT)), - clientId = clientId, - ... - userPropertiesMap = mapOf( - "key1" to "value1", - "key2" to "value2" - ) -) +val connectOptions = MqttConnectOptions.Builder() + .serverUris(listOf(ServerUri(SERVER_URI, SERVER_PORT))) + .clientId(clientId) + ... + .userProperties( + userProperties = mapOf( + "key1" to "value1", + "key2" to "value2" + ) + ) + .build() mqttClient.connect(connectOptions) ~~~ diff --git a/mqtt-client/api/mqtt-client.api b/mqtt-client/api/mqtt-client.api index f0b0e04f..fcbcf07a 100644 --- a/mqtt-client/api/mqtt-client.api +++ b/mqtt-client/api/mqtt-client.api @@ -26,8 +26,8 @@ public abstract interface class com/gojek/mqtt/client/MqttInterceptor { public final class com/gojek/mqtt/client/config/ExperimentConfigs { public fun ()V - public fun (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJ)V - public synthetic fun (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZ)V + public synthetic fun (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lcom/gojek/mqtt/client/config/SubscriptionStore; public final fun component2 ()Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig; public final fun component3 ()I @@ -35,8 +35,9 @@ public final class com/gojek/mqtt/client/config/ExperimentConfigs { public final fun component5 ()I public final fun component6 ()J public final fun component7 ()J - public final fun copy (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJ)Lcom/gojek/mqtt/client/config/ExperimentConfigs; - public static synthetic fun copy$default (Lcom/gojek/mqtt/client/config/ExperimentConfigs;Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJILjava/lang/Object;)Lcom/gojek/mqtt/client/config/ExperimentConfigs; + public final fun component8 ()Z + public final fun copy (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZ)Lcom/gojek/mqtt/client/config/ExperimentConfigs; + public static synthetic fun copy$default (Lcom/gojek/mqtt/client/config/ExperimentConfigs;Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZILjava/lang/Object;)Lcom/gojek/mqtt/client/config/ExperimentConfigs; public fun equals (Ljava/lang/Object;)Z public final fun getActivityCheckIntervalSeconds ()I public final fun getAdaptiveKeepAliveConfig ()Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig; @@ -44,13 +45,14 @@ public final class com/gojek/mqtt/client/config/ExperimentConfigs { public final fun getIncomingMessagesCleanupIntervalSecs ()J public final fun getIncomingMessagesTTLSecs ()J public final fun getPolicyResetTimeSeconds ()I + public final fun getShouldUseNewSSLFlow ()Z public final fun getSubscriptionStore ()Lcom/gojek/mqtt/client/config/SubscriptionStore; public fun hashCode ()I public fun toString ()Ljava/lang/String; } public abstract class com/gojek/mqtt/client/config/MqttConfiguration { - public fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILjavax/net/SocketFactory;Lcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)V + public fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)V public fun getAuthFailureHandler ()Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler; public fun getAuthenticator ()Lcom/gojek/mqtt/auth/Authenticator; public fun getConnectRetryTimePolicy ()Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy; @@ -61,7 +63,6 @@ public abstract class com/gojek/mqtt/client/config/MqttConfiguration { public fun getMqttInterceptorList ()Ljava/util/List; public fun getPersistenceOptions ()Lcom/gojek/mqtt/client/config/PersistenceOptions; public fun getPingSender ()Lcom/gojek/mqtt/pingsender/MqttPingSender; - public fun getSocketFactory ()Ljavax/net/SocketFactory; public fun getSubscriptionRetryPolicy ()Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy; public fun getUnsubscriptionRetryPolicy ()Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy; public fun getWakeLockTimeout ()I @@ -94,24 +95,23 @@ public final class com/gojek/mqtt/client/config/SubscriptionStore : java/lang/En } public final class com/gojek/mqtt/client/config/v3/MqttV3Configuration : com/gojek/mqtt/client/config/MqttConfiguration { - public fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILjavax/net/SocketFactory;Lcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)V - public synthetic fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILjavax/net/SocketFactory;Lcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)V + public synthetic fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy; - public final fun component10 ()Lcom/gojek/mqtt/event/EventHandler; - public final fun component11 ()Lcom/gojek/mqtt/pingsender/MqttPingSender; - public final fun component12 ()Ljava/util/List; - public final fun component13 ()Lcom/gojek/mqtt/client/config/PersistenceOptions; - public final fun component14 ()Lcom/gojek/mqtt/client/config/ExperimentConfigs; + public final fun component10 ()Lcom/gojek/mqtt/pingsender/MqttPingSender; + public final fun component11 ()Ljava/util/List; + public final fun component12 ()Lcom/gojek/mqtt/client/config/PersistenceOptions; + public final fun component13 ()Lcom/gojek/mqtt/client/config/ExperimentConfigs; public final fun component2 ()Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy; public final fun component3 ()Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy; public final fun component4 ()Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy; public final fun component5 ()I - public final fun component6 ()Ljavax/net/SocketFactory; - public final fun component7 ()Lcom/gojek/courier/logging/ILogger; - public final fun component8 ()Lcom/gojek/mqtt/auth/Authenticator; - public final fun component9 ()Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler; - public final fun copy (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILjavax/net/SocketFactory;Lcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration; - public static synthetic fun copy$default (Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration;Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILjavax/net/SocketFactory;Lcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;ILjava/lang/Object;)Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration; + public final fun component6 ()Lcom/gojek/courier/logging/ILogger; + public final fun component7 ()Lcom/gojek/mqtt/auth/Authenticator; + public final fun component8 ()Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler; + public final fun component9 ()Lcom/gojek/mqtt/event/EventHandler; + public final fun copy (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration; + public static synthetic fun copy$default (Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration;Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;ILjava/lang/Object;)Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration; public fun equals (Ljava/lang/Object;)Z public fun getAuthFailureHandler ()Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler; public fun getAuthenticator ()Lcom/gojek/mqtt/auth/Authenticator; @@ -123,7 +123,6 @@ public final class com/gojek/mqtt/client/config/v3/MqttV3Configuration : com/goj public fun getMqttInterceptorList ()Ljava/util/List; public fun getPersistenceOptions ()Lcom/gojek/mqtt/client/config/PersistenceOptions; public fun getPingSender ()Lcom/gojek/mqtt/pingsender/MqttPingSender; - public fun getSocketFactory ()Ljavax/net/SocketFactory; public fun getSubscriptionRetryPolicy ()Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy; public fun getUnsubscriptionRetryPolicy ()Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy; public fun getWakeLockTimeout ()I @@ -1012,6 +1011,7 @@ public final class com/gojek/mqtt/model/AdaptiveKeepAliveConfig { } public final class com/gojek/mqtt/model/KeepAlive { + public static final field Companion Lcom/gojek/mqtt/model/KeepAlive$Companion; public fun (IZ)V public synthetic fun (IZILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()I @@ -1023,32 +1023,49 @@ public final class com/gojek/mqtt/model/KeepAlive { public fun toString ()Ljava/lang/String; } +public final class com/gojek/mqtt/model/KeepAlive$Companion { + public final fun getNO_KEEP_ALIVE ()Lcom/gojek/mqtt/model/KeepAlive; +} + public final class com/gojek/mqtt/model/MqttConnectOptions { - public fun (Ljava/util/List;Lcom/gojek/mqtt/model/KeepAlive;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILcom/gojek/mqtt/model/MqttVersion;Ljava/util/Map;)V - public synthetic fun (Ljava/util/List;Lcom/gojek/mqtt/model/KeepAlive;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILcom/gojek/mqtt/model/MqttVersion;Ljava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/List; - public final fun component2 ()Lcom/gojek/mqtt/model/KeepAlive; - public final fun component3 ()Ljava/lang/String; - public final fun component4 ()Ljava/lang/String; - public final fun component5 ()Ljava/lang/String; - public final fun component6 ()Z - public final fun component7 ()I - public final fun component8 ()Lcom/gojek/mqtt/model/MqttVersion; - public final fun component9 ()Ljava/util/Map; - public final fun copy (Ljava/util/List;Lcom/gojek/mqtt/model/KeepAlive;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILcom/gojek/mqtt/model/MqttVersion;Ljava/util/Map;)Lcom/gojek/mqtt/model/MqttConnectOptions; - public static synthetic fun copy$default (Lcom/gojek/mqtt/model/MqttConnectOptions;Ljava/util/List;Lcom/gojek/mqtt/model/KeepAlive;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZILcom/gojek/mqtt/model/MqttVersion;Ljava/util/Map;ILjava/lang/Object;)Lcom/gojek/mqtt/model/MqttConnectOptions; - public fun equals (Ljava/lang/Object;)Z + public static final field Companion Lcom/gojek/mqtt/model/MqttConnectOptions$Companion; + public synthetic fun (Lcom/gojek/mqtt/model/MqttConnectOptions$Builder;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getClientId ()Ljava/lang/String; + public final fun getConnectionSpec ()Lorg/eclipse/paho/client/mqttv3/ConnectionSpec; public final fun getKeepAlive ()Lcom/gojek/mqtt/model/KeepAlive; public final fun getPassword ()Ljava/lang/String; + public final fun getProtocols ()Ljava/util/List; public final fun getReadTimeoutSecs ()I public final fun getServerUris ()Ljava/util/List; + public final fun getSocketFactory ()Ljavax/net/SocketFactory; + public final fun getSslSocketFactory ()Ljavax/net/ssl/SSLSocketFactory; public final fun getUserPropertiesMap ()Ljava/util/Map; public final fun getUsername ()Ljava/lang/String; public final fun getVersion ()Lcom/gojek/mqtt/model/MqttVersion; - public fun hashCode ()I + public final fun getX509TrustManager ()Ljavax/net/ssl/X509TrustManager; public final fun isCleanSession ()Z - public fun toString ()Ljava/lang/String; + public final fun newBuilder ()Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; +} + +public final class com/gojek/mqtt/model/MqttConnectOptions$Builder { + public fun ()V + public final fun alpnProtocols (Ljava/util/List;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun build ()Lcom/gojek/mqtt/model/MqttConnectOptions; + public final fun cleanSession (Z)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun clientId (Ljava/lang/String;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun connectionSpec (Lorg/eclipse/paho/client/mqttv3/ConnectionSpec;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun keepAlive (Lcom/gojek/mqtt/model/KeepAlive;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun mqttVersion (Lcom/gojek/mqtt/model/MqttVersion;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun password (Ljava/lang/String;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun readTimeoutSecs (I)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun serverUris (Ljava/util/List;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun socketFactory (Ljavax/net/SocketFactory;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun sslSocketFactory (Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun userName (Ljava/lang/String;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun userProperties (Ljava/util/Map;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; +} + +public final class com/gojek/mqtt/model/MqttConnectOptions$Companion { } public final class com/gojek/mqtt/model/MqttVersion : java/lang/Enum { diff --git a/mqtt-client/build.gradle.kts b/mqtt-client/build.gradle.kts index 2fd26cab..626500e4 100644 --- a/mqtt-client/build.gradle.kts +++ b/mqtt-client/build.gradle.kts @@ -53,6 +53,7 @@ dependencies { testImplementation(deps.android.test.junit) testImplementation(deps.android.test.mockito) + testImplementation(deps.android.test.mockitoCore) androidTestImplementation(deps.android.test.junitExt) } diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/ExperimentConfigs.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/ExperimentConfigs.kt index ed49af0c..2f1d863d 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/ExperimentConfigs.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/ExperimentConfigs.kt @@ -13,7 +13,8 @@ data class ExperimentConfigs( val inactivityTimeoutSeconds: Int = DEFAULT_INACTIVITY_TIMEOUT_SECS, val policyResetTimeSeconds: Int = DEFAULT_POLICY_RESET_TIME_SECS, val incomingMessagesTTLSecs: Long = 360, - val incomingMessagesCleanupIntervalSecs: Long = 60 + val incomingMessagesCleanupIntervalSecs: Long = 60, + val shouldUseNewSSLFlow: Boolean = false ) enum class SubscriptionStore { diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/MqttConfiguration.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/MqttConfiguration.kt index abfbc30b..0a6c3f6b 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/MqttConfiguration.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/MqttConfiguration.kt @@ -9,7 +9,6 @@ import com.gojek.mqtt.pingsender.MqttPingSender import com.gojek.mqtt.policies.connectretrytime.IConnectRetryTimePolicy import com.gojek.mqtt.policies.connecttimeout.IConnectTimeoutPolicy import com.gojek.mqtt.policies.subscriptionretry.ISubscriptionRetryPolicy -import javax.net.SocketFactory abstract class MqttConfiguration( open val connectRetryTimePolicy: IConnectRetryTimePolicy, @@ -17,7 +16,6 @@ abstract class MqttConfiguration( open val subscriptionRetryPolicy: ISubscriptionRetryPolicy, open val unsubscriptionRetryPolicy: ISubscriptionRetryPolicy, open val wakeLockTimeout: Int, - open val socketFactory: SocketFactory?, open val logger: ILogger, open val authenticator: Authenticator, open val authFailureHandler: AuthFailureHandler?, diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/v3/MqttV3Configuration.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/v3/MqttV3Configuration.kt index d7457a2e..39dd9c2a 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/v3/MqttV3Configuration.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/v3/MqttV3Configuration.kt @@ -22,7 +22,6 @@ import com.gojek.mqtt.policies.connecttimeout.IConnectTimeoutPolicy import com.gojek.mqtt.policies.subscriptionretry.ISubscriptionRetryPolicy import com.gojek.mqtt.policies.subscriptionretry.SubscriptionRetryConfig import com.gojek.mqtt.policies.subscriptionretry.SubscriptionRetryPolicy -import javax.net.SocketFactory data class MqttV3Configuration( override val connectRetryTimePolicy: IConnectRetryTimePolicy = @@ -34,7 +33,6 @@ data class MqttV3Configuration( override val unsubscriptionRetryPolicy: ISubscriptionRetryPolicy = SubscriptionRetryPolicy(SubscriptionRetryConfig()), override val wakeLockTimeout: Int = DEFAULT_WAKELOCK_TIMEOUT, - override val socketFactory: SocketFactory? = null, override val logger: ILogger = NoOpLogger(), override val authenticator: Authenticator, override val authFailureHandler: AuthFailureHandler? = null, @@ -49,7 +47,6 @@ data class MqttV3Configuration( subscriptionRetryPolicy = subscriptionRetryPolicy, unsubscriptionRetryPolicy = unsubscriptionRetryPolicy, wakeLockTimeout = wakeLockTimeout, - socketFactory = socketFactory, logger = logger, authenticator = authenticator, authFailureHandler = authFailureHandler, diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt index 6963419a..757403f8 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt @@ -171,13 +171,13 @@ internal class AndroidMqttClient( maxInflightMessages = MAX_INFLIGHT_MESSAGES_ALLOWED, logger = mqttConfiguration.logger, connectionEventHandler = mqttClientEventAdapter.adapt(), - socketFactory = mqttConfiguration.socketFactory, mqttInterceptorList = mqttConfiguration.mqttInterceptorList.map { mapToPahoInterceptor(it) }, persistenceOptions = mqttConfiguration.persistenceOptions, inactivityTimeoutSeconds = experimentConfigs.inactivityTimeoutSeconds, - policyResetTimeSeconds = experimentConfigs.policyResetTimeSeconds + policyResetTimeSeconds = experimentConfigs.policyResetTimeSeconds, + shouldUseNewSSLFlow = experimentConfigs.shouldUseNewSSLFlow ) mqttConnection = MqttConnection( @@ -506,15 +506,15 @@ internal class AndroidMqttClient( forceRefresh = false this.hostFallbackPolicy = HostFallbackPolicy(connectOptions.serverUris) val mqttConnectOptions = if (isAdaptiveKAConnection) { - connectOptions.copy( - keepAlive = keepAliveProvider.getKeepAlive(connectOptions), - clientId = connectOptions.clientId + ":adaptive", - isCleanSession = true - ) + connectOptions.newBuilder() + .keepAlive(keepAliveProvider.getKeepAlive(connectOptions)) + .clientId(connectOptions.clientId + ":adaptive") + .cleanSession(true) + .build() } else { - connectOptions.copy( - keepAlive = keepAliveProvider.getKeepAlive(connectOptions) - ) + connectOptions.newBuilder() + .keepAlive(keepAliveProvider.getKeepAlive(connectOptions)) + .build() } if (isAdaptiveKAConnection.not()) { diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/connection/MqttConnection.kt b/mqtt-client/src/main/java/com/gojek/mqtt/connection/MqttConnection.kt index 69c4b03b..804bb047 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/connection/MqttConnection.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/connection/MqttConnection.kt @@ -162,27 +162,26 @@ internal class MqttConnection( if (options == null) { options = MqttConnectOptions() } - // Setting some connection options which we need to reset on every connect - if (isSSL()) { - options!!.socketFactory = connectionConfig.socketFactory - } else { - options!!.socketFactory = null - } - options!!.userName = connectOptions.username - options!!.password = connectOptions.password.toCharArray() - options!!.isCleanSession = connectOptions.isCleanSession - options!!.keepAliveInterval = connectOptions.keepAlive.timeSeconds - options!!.keepAliveIntervalServer = connectOptions.keepAlive.timeSeconds - options!!.readTimeout = if (connectOptions.readTimeoutSecs == -1) { - connectOptions.keepAlive.timeSeconds + 60 - } else { - connectOptions.readTimeoutSecs + options!!.apply { + userName = connectOptions.username + password = connectOptions.password.toCharArray() + isCleanSession = connectOptions.isCleanSession + keepAliveInterval = connectOptions.keepAlive.timeSeconds + keepAliveIntervalServer = connectOptions.keepAlive.timeSeconds + readTimeout = connectOptions.readTimeoutSecs + connectionTimeout = connectTimeoutPolicy.getConnectTimeOut() + handshakeTimeout = connectTimeoutPolicy.getHandshakeTimeOut() + protocolName = mqttConnectOptions.version.protocolName + protocolLevel = mqttConnectOptions.version.protocolLevel + userPropertyList = getUserPropertyList(connectOptions.userPropertiesMap) + socketFactory = mqttConnectOptions.socketFactory + sslSocketFactory = mqttConnectOptions.sslSocketFactory + x509TrustManager = mqttConnectOptions.x509TrustManager + connectionSpec = mqttConnectOptions.connectionSpec + alpnProtocolList = mqttConnectOptions.protocols } - options!!.connectionTimeout = connectTimeoutPolicy.getConnectTimeOut() - options!!.handshakeTimeout = connectTimeoutPolicy.getHandshakeTimeOut() - options!!.protocolName = mqttConnectOptions.version.protocolName - options!!.protocolLevel = mqttConnectOptions.version.protocolLevel - options!!.userPropertyList = getUserPropertyList(connectOptions.userPropertiesMap) + // Setting some connection options which we need to reset on every connect + logger.d(TAG, "MQTT connecting on : " + mqtt!!.serverURI) updatePolicyParams = true connectStartTime = clock.nanoTime() @@ -652,6 +651,10 @@ internal class MqttConnection( override fun inactivityTimeoutSecs(): Int { return connectionConfig.inactivityTimeoutSeconds } + + override fun useNewSSLFlow(): Boolean { + return connectionConfig.shouldUseNewSSLFlow + } } } diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/connection/config/v3/ConnectionConfig.kt b/mqtt-client/src/main/java/com/gojek/mqtt/connection/config/v3/ConnectionConfig.kt index 70fbe574..8329b27c 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/connection/config/v3/ConnectionConfig.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/connection/config/v3/ConnectionConfig.kt @@ -8,7 +8,6 @@ import com.gojek.mqtt.constants.QUIESCE_TIME_MILLIS import com.gojek.mqtt.policies.connectretrytime.IConnectRetryTimePolicy import com.gojek.mqtt.policies.connecttimeout.IConnectTimeoutPolicy import com.gojek.mqtt.policies.subscriptionretry.ISubscriptionRetryPolicy -import javax.net.SocketFactory import org.eclipse.paho.client.mqttv3.MqttInterceptor internal data class ConnectionConfig( @@ -19,12 +18,12 @@ internal data class ConnectionConfig( val wakeLockTimeout: Int, val maxInflightMessages: Int, val logger: ILogger, - val socketFactory: SocketFactory?, val connectionEventHandler: ConnectionEventHandler, val quiesceTimeout: Int = QUIESCE_TIME_MILLIS, val disconnectTimeout: Int = DISCONNECT_TIMEOUT_MILLIS, val mqttInterceptorList: List, val persistenceOptions: PersistenceOptions, val inactivityTimeoutSeconds: Int, - val policyResetTimeSeconds: Int + val policyResetTimeSeconds: Int, + val shouldUseNewSSLFlow: Boolean ) diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/model/KeepAlive.kt b/mqtt-client/src/main/java/com/gojek/mqtt/model/KeepAlive.kt index 6ea17cdf..0e59d48e 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/model/KeepAlive.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/model/KeepAlive.kt @@ -3,4 +3,8 @@ package com.gojek.mqtt.model data class KeepAlive( val timeSeconds: Int, internal val isOptimal: Boolean = false -) +) { + companion object { + val NO_KEEP_ALIVE = KeepAlive(timeSeconds = 60, isOptimal = false) + } +} diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt b/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt index 5aa83e0e..69592f2f 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt @@ -1,18 +1,220 @@ package com.gojek.mqtt.model +import com.gojek.mqtt.model.KeepAlive.Companion.NO_KEEP_ALIVE import com.gojek.mqtt.model.MqttVersion.VERSION_3_1_1 +import javax.net.SocketFactory +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager +import org.eclipse.paho.client.mqttv3.ConnectionSpec +import org.eclipse.paho.client.mqttv3.Protocol +import org.eclipse.paho.client.mqttv3.internal.platform.Platform -data class MqttConnectOptions( - val serverUris: List, - val keepAlive: KeepAlive, - val clientId: String, - val username: String, - val password: String, - val isCleanSession: Boolean, - val readTimeoutSecs: Int = -1, - val version: MqttVersion = VERSION_3_1_1, - val userPropertiesMap: Map = emptyMap() -) +class MqttConnectOptions private constructor( + builder: Builder +) { + val serverUris: List = builder.serverUris + + val keepAlive: KeepAlive = builder.keepAlive + + val clientId: String = builder.clientId + + val username: String = builder.username + + val password: String = builder.password + + val isCleanSession: Boolean = builder.isCleanSession + + val readTimeoutSecs: Int + + val version: MqttVersion = builder.version + + val userPropertiesMap: Map = builder.userPropertiesMap + + val socketFactory: SocketFactory = builder.socketFactory + + val sslSocketFactory: SSLSocketFactory? + + val x509TrustManager: X509TrustManager? + + val connectionSpec: ConnectionSpec = + builder.connectionSpec + + val protocols: List = builder.protocols + + init { + if (connectionSpec.isTls.not()) { + this.sslSocketFactory = null + this.x509TrustManager = null + } else if (builder.sslSocketFactoryOrNull != null) { + this.sslSocketFactory = builder.sslSocketFactoryOrNull + this.x509TrustManager = builder.x509TrustManagerOrNull!! + } else { + this.x509TrustManager = Platform.get().platformTrustManager() + this.sslSocketFactory = Platform.get().newSslSocketFactory(x509TrustManager!!) + } + + this.readTimeoutSecs = if (builder.readTimeoutSecs < builder.keepAlive.timeSeconds) { + builder.keepAlive.timeSeconds + 60 + } else { + builder.readTimeoutSecs + } + } + + fun newBuilder(): Builder = Builder(this) + + class Builder() { + internal var serverUris: List = emptyList() + internal var keepAlive: KeepAlive = NO_KEEP_ALIVE + internal var clientId: String = "" + internal var username: String = "" + internal var password: String = "" + internal var isCleanSession: Boolean = false + internal var readTimeoutSecs: Int = DEFAULT_READ_TIMEOUT + internal var version: MqttVersion = VERSION_3_1_1 + internal var userPropertiesMap: Map = emptyMap() + internal var socketFactory: SocketFactory = SocketFactory.getDefault() + internal var sslSocketFactoryOrNull: SSLSocketFactory? = null + internal var x509TrustManagerOrNull: X509TrustManager? = null + internal var connectionSpec: ConnectionSpec = DEFAULT_CONNECTION_SPECS + internal var protocols: List = emptyList() + + internal constructor(mqttConnectOptions: MqttConnectOptions) : this() { + this.serverUris = mqttConnectOptions.serverUris + this.keepAlive = mqttConnectOptions.keepAlive + this.clientId = mqttConnectOptions.clientId + this.username = mqttConnectOptions.username + this.password = mqttConnectOptions.password + this.isCleanSession = mqttConnectOptions.isCleanSession + this.readTimeoutSecs = mqttConnectOptions.readTimeoutSecs + this.version = mqttConnectOptions.version + this.userPropertiesMap = mqttConnectOptions.userPropertiesMap + this.socketFactory = mqttConnectOptions.socketFactory + this.sslSocketFactoryOrNull = mqttConnectOptions.sslSocketFactory + this.x509TrustManagerOrNull = mqttConnectOptions.x509TrustManager + this.connectionSpec = mqttConnectOptions.connectionSpec + this.protocols = mqttConnectOptions.protocols + } + + fun serverUris(serverUris: List) = apply { + require(serverUris.isNotEmpty()) { "serverUris cannot be empty" } + this.serverUris = serverUris + } + + fun keepAlive(keepAlive: KeepAlive) = apply { + require(keepAlive.timeSeconds > 0) { "keepAlive timeSeconds must be >0" } + this.keepAlive = keepAlive + } + + fun clientId(clientId: String) = apply { + require(clientId.isNotEmpty()) { "clientId cannot be empty" } + this.clientId = clientId + } + + fun userName(username: String) = apply { + require(username.isNotEmpty()) { "username cannot be empty" } + this.username = username + } + + fun password(password: String) = apply { + this.password = password + } + + fun cleanSession(cleanSession: Boolean) = apply { + this.isCleanSession = cleanSession + } + + fun readTimeoutSecs(readTimeoutSecs: Int) = apply { + require(readTimeoutSecs > 0) { "read timeout should be > 0" } + this.readTimeoutSecs = readTimeoutSecs + } + + fun mqttVersion(mqttVersion: MqttVersion) = apply { + this.version = mqttVersion + } + + fun userProperties(userProperties: Map) = apply { + this.userPropertiesMap = userProperties.toMap() + } + + /** + * Sets the socket factory used to create connections. + * + * If unset, the [system-wide default][SocketFactory.getDefault] socket factory will be used. + */ + fun socketFactory(socketFactory: SocketFactory) = apply { + require(socketFactory !is SSLSocketFactory) { "socketFactory instanceof SSLSocketFactory" } + + this.socketFactory = socketFactory + } + + /** + * Sets the socket factory and trust manager used to secure MQTT/Websocket connections. If unset, the + * system defaults will be used. + * + * Most applications should not call this method, and instead use the system defaults. Those + * classes include special optimizations that can be lost if the implementations are decorated. + * + * If necessary, you can create and configure the defaults yourself with the following code: + * + * ```java + * TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( + * TrustManagerFactory.getDefaultAlgorithm()); + * trustManagerFactory.init((KeyStore) null); + * TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + * if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { + * throw new IllegalStateException("Unexpected default trust managers:" + * + Arrays.toString(trustManagers)); + * } + * X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; + * + * SSLContext sslContext = SSLContext.getInstance("TLS"); + * sslContext.init(null, new TrustManager[] { trustManager }, null); + * SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); + * + * MQTTConnectOptions client = MQTTConnectOptions.Builder() + * .sslSocketFactory(sslSocketFactory, trustManager) + * .build(); + * ``` + * + * ## TrustManagers on Android are Weird! + * + * Trust managers targeting Android must also define a method that has this signature: + * + * ```java + * @SuppressWarnings("unused") + * public List checkServerTrusted( + * X509Certificate[] chain, String authType, String host) throws CertificateException { + * } + * ``` + * See [android.net.http.X509TrustManagerExtensions] for more information. + */ + fun sslSocketFactory( + sslSocketFactory: SSLSocketFactory, + trustManager: X509TrustManager + ) = apply { + this.sslSocketFactoryOrNull = sslSocketFactory + this.x509TrustManagerOrNull = trustManager + } + + fun connectionSpec(connectionSpec: ConnectionSpec) = apply { + this.connectionSpec = connectionSpec + } + + fun alpnProtocols(protocols: List) = apply { + require(protocols.isNotEmpty()) { "alpn protocol list cannot be empty" } + this.protocols = protocols + } + + fun build(): MqttConnectOptions = MqttConnectOptions(this) + } + + companion object { + + internal const val DEFAULT_READ_TIMEOUT = -1 + + internal val DEFAULT_CONNECTION_SPECS = ConnectionSpec.MODERN_TLS + } +} enum class MqttVersion(internal val protocolName: String, internal val protocolLevel: Int) { VERSION_3_1("MQIsdp", 3), VERSION_3_1_1("MQTT", 4) diff --git a/mqtt-client/src/test/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImplTest.kt b/mqtt-client/src/test/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImplTest.kt index c69e942d..abe8c35f 100644 --- a/mqtt-client/src/test/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImplTest.kt +++ b/mqtt-client/src/test/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImplTest.kt @@ -6,7 +6,6 @@ import com.gojek.mqtt.policies.connectretrytime.IConnectRetryTimePolicy import com.gojek.mqtt.scheduler.IRunnableScheduler import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify -import com.nhaarman.mockitokotlin2.verifyZeroInteractions import com.nhaarman.mockitokotlin2.whenever import java.net.SocketException import java.net.SocketTimeoutException @@ -40,6 +39,7 @@ import org.eclipse.paho.client.mqttv3.MqttException.REASON_CODE_TOKEN_INUSE import org.eclipse.paho.client.mqttv3.MqttException.REASON_CODE_UNEXPECTED_ERROR import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.verifyNoInteractions import org.mockito.junit.MockitoJUnitRunner @RunWith(MockitoJUnitRunner::class) @@ -177,7 +177,7 @@ class MqttExceptionHandlerImplTest { fun `test exception with reason code 32101 with reconnect=false`() { val exception = MqttException(REASON_CODE_CLIENT_ALREADY_DISCONNECTED.toInt()) mqttExceptionHandlerImpl.handleException(exception, false) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test @@ -191,7 +191,7 @@ class MqttExceptionHandlerImplTest { fun `test exception with reason code 32102 with reconnect=false`() { val exception = MqttException(REASON_CODE_CLIENT_DISCONNECTING.toInt()) mqttExceptionHandlerImpl.handleException(exception, false) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test @@ -205,7 +205,7 @@ class MqttExceptionHandlerImplTest { fun `test exception with reason code 32104 with reconnect=false`() { val exception = MqttException(REASON_CODE_CLIENT_NOT_CONNECTED.toInt()) mqttExceptionHandlerImpl.handleException(exception, false) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test @@ -219,14 +219,14 @@ class MqttExceptionHandlerImplTest { fun `test exception with reason code 32000 with reconnect=false`() { val exception = MqttException(REASON_CODE_CLIENT_TIMEOUT.toInt()) mqttExceptionHandlerImpl.handleException(exception, false) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32110`() { val exception = MqttException(REASON_CODE_CONNECT_IN_PROGRESS.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test @@ -249,83 +249,83 @@ class MqttExceptionHandlerImplTest { fun `test exception with reason code 32109 with reconnect=false`() { val exception = MqttException(REASON_CODE_CONNECTION_LOST.toInt()) mqttExceptionHandlerImpl.handleException(exception, false) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32202`() { val exception = MqttException(REASON_CODE_MAX_INFLIGHT.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32111`() { val exception = MqttException(REASON_CODE_CLIENT_CLOSED.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32100`() { val exception = MqttException(REASON_CODE_CLIENT_CONNECTED.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32107`() { val exception = MqttException(REASON_CODE_CLIENT_DISCONNECT_PROHIBITED.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 2`() { val exception = MqttException(REASON_CODE_INVALID_CLIENT_ID.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32108`() { val exception = MqttException(REASON_CODE_INVALID_MESSAGE.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 1`() { val exception = MqttException(REASON_CODE_INVALID_PROTOCOL_VERSION.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32001`() { val exception = MqttException(REASON_CODE_NO_MESSAGE_IDS_AVAILABLE.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32105`() { val exception = MqttException(REASON_CODE_SOCKET_FACTORY_MISMATCH.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32106`() { val exception = MqttException(REASON_CODE_SSL_CONFIG_ERROR.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } @Test fun `test exception with reason code 32201`() { val exception = MqttException(REASON_CODE_TOKEN_INUSE.toInt()) mqttExceptionHandlerImpl.handleException(exception, true) - verifyZeroInteractions(runnableScheduler, connectRetryTimePolicy) + verifyNoInteractions(runnableScheduler, connectRetryTimePolicy) } } diff --git a/network-tracker/build.gradle.kts b/network-tracker/build.gradle.kts index 592d4260..770282ce 100644 --- a/network-tracker/build.gradle.kts +++ b/network-tracker/build.gradle.kts @@ -28,6 +28,7 @@ dependencies { api(project(":courier-core")) implementation(deps.android.androidx.coreKtx) + testImplementation(deps.android.test.mockitoCore) testImplementation(deps.android.test.kotlinTestJunit) } diff --git a/paho/build.gradle.kts b/paho/build.gradle.kts index 237b0b34..6ef57762 100644 --- a/paho/build.gradle.kts +++ b/paho/build.gradle.kts @@ -16,12 +16,20 @@ plugins { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } dependencies { implementation(deps.kotlin.stdlib.core) + implementation("com.squareup.okio:okio:3.2.0") + + compileOnly("org.robolectric:android-all:13-robolectric-9030017") + compileOnly("org.bouncycastle:bcprov-jdk15to18:1.71") + compileOnly("org.bouncycastle:bctls-jdk15to18:1.71") + compileOnly("org.conscrypt:conscrypt-openjdk-uber:2.5.2") + compileOnly("org.openjsse:openjsse:1.1.10") + testImplementation(deps.android.test.kotlinTestJunit) } diff --git a/paho/src/main/AndroidManifest.xml b/paho/src/main/AndroidManifest.xml new file mode 100644 index 00000000..17e0e3ec --- /dev/null +++ b/paho/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/ConnectionSpec.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/ConnectionSpec.kt new file mode 100644 index 00000000..7e48192f --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/ConnectionSpec.kt @@ -0,0 +1,375 @@ +/* + * Copyright (C) 2014 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3 + +import java.util.Arrays +import java.util.Objects +import javax.net.ssl.SSLSocket +import org.eclipse.paho.client.mqttv3.ConnectionSpec.Builder +import org.eclipse.paho.client.mqttv3.internal.tls.CipherSuite +import org.eclipse.paho.client.mqttv3.internal.tls.TlsVersion + +/** + * Specifies configuration for the socket connection that HTTP traffic travels through. For `https:` + * URLs, this includes the TLS version and cipher suites to use when negotiating a secure + * connection. + * + * The TLS versions configured in a connection spec are only be used if they are also enabled in the + * SSL socket. For example, if an SSL socket does not have TLS 1.3 enabled, it will not be used even + * if it is present on the connection spec. The same policy also applies to cipher suites. + * + * Use [Builder.allEnabledTlsVersions] and [Builder.allEnabledCipherSuites] to defer all feature + * selection to the underlying SSL socket. + * + * The configuration of each spec changes with each OkHttp release. This is annoying: upgrading + * your OkHttp library can break connectivity to certain web servers! But it’s a necessary annoyance + * because the TLS ecosystem is dynamic and staying up to date is necessary to stay secure. See + * [OkHttp's TLS Configuration History][tls_history] to track these changes. + * + * [tls_history]: https://square.github.io/okhttp/tls_configuration_history/ + */ +class ConnectionSpec internal constructor( + @get:JvmName("isTls") val isTls: Boolean, + @get:JvmName("supportsTlsExtensions") val supportsTlsExtensions: Boolean, + internal val cipherSuitesAsString: Array?, + private val tlsVersionsAsString: Array? +) { + + /** + * Returns the cipher suites to use for a connection. Returns null if all of the SSL socket's + * enabled cipher suites should be used. + */ + @get:JvmName("cipherSuites") + val cipherSuites: List? + get() { + return cipherSuitesAsString?.map { CipherSuite.forJavaName(it) }?.toList() + } + + @JvmName("-deprecated_cipherSuites") + @Deprecated( + message = "moved to val", + replaceWith = ReplaceWith(expression = "cipherSuites"), + level = DeprecationLevel.ERROR + ) + fun cipherSuites(): List? = cipherSuites + + /** + * Returns the TLS versions to use when negotiating a connection. Returns null if all of the SSL + * socket's enabled TLS versions should be used. + */ + @get:JvmName("tlsVersions") + val tlsVersions: List? + get() { + return tlsVersionsAsString?.map { TlsVersion.forJavaName(it) }?.toList() + } + + @JvmName("-deprecated_tlsVersions") + @Deprecated( + message = "moved to val", + replaceWith = ReplaceWith(expression = "tlsVersions"), + level = DeprecationLevel.ERROR + ) + fun tlsVersions(): List? = tlsVersions + + @JvmName("-deprecated_supportsTlsExtensions") + @Deprecated( + message = "moved to val", + replaceWith = ReplaceWith(expression = "supportsTlsExtensions"), + level = DeprecationLevel.ERROR + ) + fun supportsTlsExtensions(): Boolean = supportsTlsExtensions + + /** Applies this spec to [sslSocket]. */ + fun apply(sslSocket: SSLSocket, isFallback: Boolean) { + val specToApply = supportedSpec(sslSocket, isFallback) + + if (specToApply.tlsVersions != null) { + sslSocket.enabledProtocols = specToApply.tlsVersionsAsString + } + + if (specToApply.cipherSuites != null) { + sslSocket.enabledCipherSuites = specToApply.cipherSuitesAsString + } + } + + /** + * Returns a copy of this that omits cipher suites and TLS versions not enabled by [sslSocket]. + */ + private fun supportedSpec(sslSocket: SSLSocket, isFallback: Boolean): ConnectionSpec { + val socketEnabledCipherSuites = sslSocket.enabledCipherSuites + var cipherSuitesIntersection: Array = + effectiveCipherSuites(socketEnabledCipherSuites) + + val tlsVersionsIntersection = if (tlsVersionsAsString != null) { + sslSocket.enabledProtocols.intersect(tlsVersionsAsString, naturalOrder()) + } else { + sslSocket.enabledProtocols + } + + // In accordance with https://tools.ietf.org/html/draft-ietf-tls-downgrade-scsv-00 the SCSV + // cipher is added to signal that a protocol fallback has taken place. + val supportedCipherSuites = sslSocket.supportedCipherSuites + val indexOfFallbackScsv = supportedCipherSuites.indexOf( + "TLS_FALLBACK_SCSV", + CipherSuite.ORDER_BY_NAME + ) + if (isFallback && indexOfFallbackScsv != -1) { + cipherSuitesIntersection = cipherSuitesIntersection.concat( + supportedCipherSuites[indexOfFallbackScsv] + ) + } + + return Builder(this) + .cipherSuites(*cipherSuitesIntersection) + .tlsVersions(*tlsVersionsIntersection) + .build() + } + + /** + * Returns `true` if the socket, as currently configured, supports this connection spec. In + * order for a socket to be compatible the enabled cipher suites and protocols must intersect. + * + * For cipher suites, at least one of the [required cipher suites][cipherSuites] must match the + * socket's enabled cipher suites. If there are no required cipher suites the socket must have at + * least one cipher suite enabled. + * + * For protocols, at least one of the [required protocols][tlsVersions] must match the socket's + * enabled protocols. + */ + fun isCompatible(socket: SSLSocket): Boolean { + if (!isTls) { + return false + } + + if (tlsVersionsAsString != null && + !tlsVersionsAsString.hasIntersection(socket.enabledProtocols, naturalOrder()) + ) { + return false + } + + if (cipherSuitesAsString != null && !cipherSuitesAsString.hasIntersection( + socket.enabledCipherSuites, + CipherSuite.ORDER_BY_NAME + ) + ) { + return false + } + + return true + } + + override fun equals(other: Any?): Boolean { + if (other !is ConnectionSpec) return false + if (other === this) return true + + if (this.isTls != other.isTls) return false + + if (isTls) { + if (!Arrays.equals(this.cipherSuitesAsString, other.cipherSuitesAsString)) return false + if (!Arrays.equals(this.tlsVersionsAsString, other.tlsVersionsAsString)) return false + if (this.supportsTlsExtensions != other.supportsTlsExtensions) return false + } + + return true + } + + override fun hashCode(): Int { + var result = 17 + if (isTls) { + result = 31 * result + (cipherSuitesAsString?.contentHashCode() ?: 0) + result = 31 * result + (tlsVersionsAsString?.contentHashCode() ?: 0) + result = 31 * result + if (supportsTlsExtensions) 0 else 1 + } + return result + } + + override fun toString(): String { + if (!isTls) return "ConnectionSpec()" + + return ( + "ConnectionSpec(" + + "cipherSuites=${Objects.toString(cipherSuites, "[all enabled]")}, " + + "tlsVersions=${Objects.toString(tlsVersions, "[all enabled]")}, " + + "supportsTlsExtensions=$supportsTlsExtensions)" + ) + } + + class Builder { + internal var tls: Boolean = false + internal var cipherSuites: Array? = null + internal var tlsVersions: Array? = null + internal var supportsTlsExtensions: Boolean = false + + internal constructor(tls: Boolean) { + this.tls = tls + } + + constructor(connectionSpec: ConnectionSpec) { + this.tls = connectionSpec.isTls + this.cipherSuites = connectionSpec.cipherSuitesAsString + this.tlsVersions = connectionSpec.tlsVersionsAsString + this.supportsTlsExtensions = connectionSpec.supportsTlsExtensions + } + + fun allEnabledCipherSuites() = apply { + require(tls) { "no cipher suites for cleartext connections" } + this.cipherSuites = null + } + + fun cipherSuites(vararg cipherSuites: CipherSuite): Builder = apply { + require(tls) { "no cipher suites for cleartext connections" } + val strings = cipherSuites.map { it.javaName }.toTypedArray() + return cipherSuites(*strings) + } + + fun cipherSuites(vararg cipherSuites: String) = apply { + require(tls) { "no cipher suites for cleartext connections" } + require(cipherSuites.isNotEmpty()) { "At least one cipher suite is required" } + + this.cipherSuites = cipherSuites.clone() as Array // Defensive copy. + } + + fun allEnabledTlsVersions() = apply { + require(tls) { "no TLS versions for cleartext connections" } + this.tlsVersions = null + } + + fun tlsVersions(vararg tlsVersions: TlsVersion): Builder = apply { + require(tls) { "no TLS versions for cleartext connections" } + + val strings = tlsVersions.map { it.javaName }.toTypedArray() + return tlsVersions(*strings) + } + + fun tlsVersions(vararg tlsVersions: String) = apply { + require(tls) { "no TLS versions for cleartext connections" } + require(tlsVersions.isNotEmpty()) { "At least one TLS version is required" } + + this.tlsVersions = tlsVersions.clone() as Array // Defensive copy. + } + + @Deprecated( + "since OkHttp 3.13 all TLS-connections are expected to support TLS extensions.\n" + + "In a future release setting this to true will be unnecessary and setting it to false\n" + + "will have no effect." + ) + fun supportsTlsExtensions(supportsTlsExtensions: Boolean) = apply { + require(tls) { "no TLS extensions for cleartext connections" } + this.supportsTlsExtensions = supportsTlsExtensions + } + + fun build(): ConnectionSpec = ConnectionSpec( + tls, + supportsTlsExtensions, + cipherSuites, + tlsVersions + ) + } + + @Suppress("DEPRECATION") + companion object { + // Most secure but generally supported list. + private val RESTRICTED_CIPHER_SUITES = arrayOf( + // TLSv1.3. + CipherSuite.TLS_AES_128_GCM_SHA256, + CipherSuite.TLS_AES_256_GCM_SHA384, + CipherSuite.TLS_CHACHA20_POLY1305_SHA256, + + // TLSv1.0, TLSv1.1, TLSv1.2. + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + ) + + // This is nearly equal to the cipher suites supported in Chrome 72, current as of 2019-02-24. + // See https://tinyurl.com/okhttp-cipher-suites for availability. + private val APPROVED_CIPHER_SUITES = arrayOf( + // TLSv1.3. + CipherSuite.TLS_AES_128_GCM_SHA256, + CipherSuite.TLS_AES_256_GCM_SHA384, + CipherSuite.TLS_CHACHA20_POLY1305_SHA256, + + // TLSv1.0, TLSv1.1, TLSv1.2. + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + + // Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll + // continue to include them until better suites are commonly available. + CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384, + CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA, + CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA + ) + + /** A secure TLS connection that requires a recent client platform and a recent server. */ + @JvmField + val RESTRICTED_TLS = Builder(true) + .cipherSuites(*RESTRICTED_CIPHER_SUITES) + .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2) + .supportsTlsExtensions(true) + .build() + + /** + * A modern TLS configuration that works on most client platforms and can connect to most servers. + * This is OkHttp's default configuration. + */ + @JvmField + val MODERN_TLS = Builder(true) + .cipherSuites(*APPROVED_CIPHER_SUITES) + .tlsVersions(TlsVersion.TLS_1_3, TlsVersion.TLS_1_2) + .supportsTlsExtensions(true) + .build() + + /** + * A backwards-compatible fallback configuration that works on obsolete client platforms and can + * connect to obsolete servers. When possible, prefer to upgrade your client platform or server + * rather than using this configuration. + */ + @JvmField + val COMPATIBLE_TLS = Builder(true) + .cipherSuites(*APPROVED_CIPHER_SUITES) + .tlsVersions( + TlsVersion.TLS_1_3, + TlsVersion.TLS_1_2, + TlsVersion.TLS_1_1, + TlsVersion.TLS_1_0 + ) + .supportsTlsExtensions(true) + .build() + + /** Unencrypted, unauthenticated connections for `http:` URLs. */ + @JvmField + val CLEARTEXT = Builder(false).build() + } + + private fun ConnectionSpec.effectiveCipherSuites(socketEnabledCipherSuites: Array): Array { + return if (cipherSuitesAsString != null) { + socketEnabledCipherSuites.intersect(cipherSuitesAsString, CipherSuite.ORDER_BY_NAME) + } else { + socketEnabledCipherSuites + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/IExperimentsConfig.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/IExperimentsConfig.java index f64516bc..955afe43 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/IExperimentsConfig.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/IExperimentsConfig.java @@ -2,4 +2,6 @@ public interface IExperimentsConfig { int inactivityTimeoutSecs(); + + Boolean useNewSSLFlow(); } diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java index 9675c2d6..45a8bf38 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java @@ -23,10 +23,12 @@ import org.eclipse.paho.client.mqttv3.internal.LocalNetworkModule; import org.eclipse.paho.client.mqttv3.internal.NetworkModule; import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModule; +import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModuleV2; import org.eclipse.paho.client.mqttv3.internal.TCPNetworkModule; import org.eclipse.paho.client.mqttv3.internal.security.SSLSocketFactoryFactory; import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketNetworkModule; import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketSecureNetworkModule; +import org.eclipse.paho.client.mqttv3.internal.websocket.WebSocketSecureNetworkModuleV2; import org.eclipse.paho.client.mqttv3.internal.wire.MqttDisconnect; import org.eclipse.paho.client.mqttv3.internal.wire.MqttPingReq; import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; @@ -35,17 +37,16 @@ import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; import java.util.Hashtable; import java.util.List; import java.util.Properties; -import javax.net.SocketFactory; -import javax.net.ssl.SSLSocketFactory; - /** * Lightweight client for talking to an MQTT server using non-blocking methods that allow an operation to run in the background. - * + * *

* This class implements the non-blocking {@link IMqttAsyncClient} client interface allowing applications to initiate MQTT actions and then carry on working while the MQTT action * completes on a background thread. This implementation is compatible with all Java SE runtimes from 1.4.2 and up. @@ -72,7 +73,7 @@ *

* The message store interface is pluggable. Different stores can be used by implementing the {@link MqttClientPersistence} interface and passing it to the clients constructor. *

- * + * * @see IMqttAsyncClient */ public class MqttAsyncClient implements IMqttAsyncClient @@ -101,6 +102,8 @@ public class MqttAsyncClient implements IMqttAsyncClient private IPahoEvents pahoEvents; + private IExperimentsConfig experimentsConfig; + final static String className = MqttAsyncClient.class.getName(); final private String TAG = "MqttAsyncClient"; @@ -110,13 +113,13 @@ public class MqttAsyncClient implements IMqttAsyncClient *

* The address of a server can be specified on the constructor. Alternatively a list containing one or more servers can be specified using the * {@link MqttConnectOptions#setServerURIs(String[]) setServerURIs} method on MqttConnectOptions. - * + * *

* The serverURI parameter is typically used with the the clientId parameter to form a key. The key is used to store and reference messages while they * are being delivered. Hence the serverURI specified on the constructor must still be specified even if a list of servers is specified on an MqttConnectOptions object. The * serverURI on the constructor must remain the same across restarts of the client for delivery of messages to be maintained from a given client to a given server or set of * servers. - * + * *

* The address of the server to connect to is specified as a URI. Two types of connection are supported tcp:// for a TCP connection and ssl:// for a * TCP connection secured by SSL/TLS. For example: @@ -126,7 +129,7 @@ public class MqttAsyncClient implements IMqttAsyncClient * * If the port is not specified, it will default to 1883 for tcp://" URIs, and 8883 for ssl:// URIs. *

- * + * *

* A client identifier clientId must be specified and be less that 65535 characters. It must be unique across all clients connecting to the same server. The * clientId is used by the server to store data related to the client, hence it is important that the clientId remain the same when connecting to a server if durable @@ -145,15 +148,15 @@ public class MqttAsyncClient implements IMqttAsyncClient *

  • SSL Properties - applications can supply SSL settings as a simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.
  • *
  • Use JVM settings - There are a number of standard Java system properties that can be used to configure key and trust stores.
  • * - * + * *

    * In Java ME, the platform settings are used for SSL connections. *

    - * + * *

    * An instance of the default persistence mechanism {@link MqttDefaultFilePersistence} is used by the client. To specify a different persistence mechanism or to turn off * persistence, use the {@link #MqttAsyncClient(String, String, MqttClientPersistence)} constructor. - * + * * @param serverURI * the address of the server to connect to, specified as a URI. Can be overridden using {@link MqttConnectOptions#setServerURIs(String[])} * @param clientId @@ -194,13 +197,13 @@ public MqttAsyncClient( *

    * The address of a server can be specified on the constructor. Alternatively a list containing one or more servers can be specified using the * {@link MqttConnectOptions#setServerURIs(String[]) setServerURIs} method on MqttConnectOptions. - * + * *

    * The serverURI parameter is typically used with the the clientId parameter to form a key. The key is used to store and reference messages while they * are being delivered. Hence the serverURI specified on the constructor must still be specified even if a list of servers is specified on an MqttConnectOptions object. The * serverURI on the constructor must remain the same across restarts of the client for delivery of messages to be maintained from a given client to a given server or set of * servers. - * + * *

    * The address of the server to connect to is specified as a URI. Two types of connection are supported tcp:// for a TCP connection and ssl:// for a * TCP connection secured by SSL/TLS. For example: @@ -210,7 +213,7 @@ public MqttAsyncClient( * * If the port is not specified, it will default to 1883 for tcp://" URIs, and 8883 for ssl:// URIs. *

    - * + * *

    * A client identifier clientId must be specified and be less that 65535 characters. It must be unique across all clients connecting to the same server. The * clientId is used by the server to store data related to the client, hence it is important that the clientId remain the same when connecting to a server if durable @@ -229,7 +232,7 @@ public MqttAsyncClient( *

  • SSL Properties - applications can supply SSL settings as a simple Java Properties using {@link MqttConnectOptions#setSSLProperties(Properties)}.
  • *
  • Use JVM settings - There are a number of standard Java system properties that can be used to configure key and trust stores.
  • * - * + * *

    * In Java ME, the platform settings are used for SSL connections. *

    @@ -244,7 +247,7 @@ public MqttAsyncClient( * An implementation of file-based persistence is provided in class {@link MqttDefaultFilePersistence} which will work in all Java SE based systems. If no persistence is * needed, the persistence parameter can be explicitly set to null. *

    - * + * * @param serverURI * the address of the server to connect to, specified as a URI. Can be overridden using {@link MqttConnectOptions#setServerURIs(String[])} * @param clientId @@ -298,6 +301,7 @@ public MqttAsyncClient( this.mqttVersion = mqttVersion; this.persistence = persistence; + this.experimentsConfig = experimentsConfig; if (this.persistence == null) { this.persistence = new MemoryPersistence(); @@ -331,7 +335,7 @@ protected static boolean Character_isHighSurrogate(char ch) /** * Factory method to create an array of network modules, one for each of the supplied URIs - * + * * @param address * the URI for the server. * @return a network module appropriate to the specified address. @@ -410,29 +414,22 @@ else if (factory instanceof SSLSocketFactory) ((TCPNetworkModule) netModule).setReadTimeout(options.getReadTimeout()); break; case MqttConnectOptions.URI_TYPE_SSL: + if(experimentsConfig.useNewSSLFlow()) { + netModule = getSSLNetworkModuleV2(address, options); + break; + } shortAddress = address.substring(6); host = getHostName(shortAddress); port = getPort(shortAddress, 8883); SSLSocketFactoryFactory factoryFactory = null; - if (factory == null) - { - // try { - factoryFactory = new SSLSocketFactoryFactory(); - Properties sslClientProps = options.getSSLProperties(); - if (null != sslClientProps) - factoryFactory.initialize(sslClientProps, null); - factory = factoryFactory.createSocketFactory(null); - // } - // catch (MqttDirectException ex) { - // throw ExceptionHelper.createMqttException(ex.getCause()); - // } - } - else if ((factory instanceof SSLSocketFactory) == false) - { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); + + factoryFactory = new SSLSocketFactoryFactory(); + Properties sslClientProps = options.getSSLProperties(); + if (null != sslClientProps) { + factoryFactory.initialize(sslClientProps, null); } + factory = factoryFactory.createSocketFactory(null); - // Create the network module... netModule = new SSLNetworkModule((SSLSocketFactory) factory, host, port, clientId, logger, pahoEvents); ((SSLNetworkModule) netModule).setConnectTimeout(options.getConnectionTimeout()); ((SSLNetworkModule) netModule).setSSLhandshakeTimeout(options.getHandshakeTimeout()); @@ -462,23 +459,22 @@ else if (factory instanceof SSLSocketFactory) { ((WebSocketNetworkModule)netModule).setReadTimeout(options.getReadTimeout()); break; case MqttConnectOptions.URI_TYPE_WSS: + if(experimentsConfig.useNewSSLFlow()) { + netModule = getWSSNetworkModuleV2(address, options); + break; + } shortAddress = address.substring(6); host = getHostName(shortAddress); port = getPort(shortAddress, 443); SSLSocketFactoryFactory wSSFactoryFactory = null; - if (factory == null) { - wSSFactoryFactory = new SSLSocketFactoryFactory(); - Properties sslClientProps = options.getSSLProperties(); - if (null != sslClientProps) - wSSFactoryFactory.initialize(sslClientProps, null); - factory = wSSFactoryFactory.createSocketFactory(null); + wSSFactoryFactory = new SSLSocketFactoryFactory(); + sslClientProps = options.getSSLProperties(); + if (null != sslClientProps) { + wSSFactoryFactory.initialize(sslClientProps, null); } - else if ((factory instanceof SSLSocketFactory) == false) { - throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_SOCKET_FACTORY_MISMATCH); - } + factory = wSSFactoryFactory.createSocketFactory(null); - // Create the network module... netModule = new WebSocketSecureNetworkModule((SSLSocketFactory) factory, address, host, port, clientId, logger, pahoEvents); ((WebSocketSecureNetworkModule)netModule).setConnectTimeout(options.getConnectionTimeout()); ((WebSocketSecureNetworkModule)netModule).setSSLhandshakeTimeout(options.getHandshakeTimeout()); @@ -501,6 +497,56 @@ else if ((factory instanceof SSLSocketFactory) == false) { return netModule; } + private NetworkModule getSSLNetworkModuleV2(String address, MqttConnectOptions options) + throws MqttException { + String shortAddress = address.substring(6); + String host = getHostName(shortAddress); + int port = getPort(shortAddress, 443); + + // Create the network module... + NetworkModule netModule = new SSLNetworkModuleV2( + options.getSocketFactory(), + options.getSslSocketFactory(), + options.getX509TrustManager(), + options.getConnectionSpec(), + options.getAlpnProtocolList(), + host, + port, + clientId, + logger, + pahoEvents + ); + ((SSLNetworkModuleV2) netModule).setConnectTimeout(options.getConnectionTimeout()); + ((SSLNetworkModuleV2) netModule).setSSLhandshakeTimeout(options.getHandshakeTimeout()); + ((SSLNetworkModuleV2) netModule).setReadTimeout(options.getReadTimeout()); + return netModule; + } + + private NetworkModule getWSSNetworkModuleV2(String address, MqttConnectOptions options) + throws MqttException { + String shortAddress = address.substring(6); + String host = getHostName(shortAddress); + int port = getPort(shortAddress, 443); + + NetworkModule netModule = new WebSocketSecureNetworkModuleV2( + options.getSocketFactory(), + options.getSslSocketFactory(), + options.getX509TrustManager(), + options.getConnectionSpec(), + options.getAlpnProtocolList(), + address, + host, + port, + clientId, + logger, + pahoEvents + ); + ((WebSocketSecureNetworkModuleV2) netModule).setConnectTimeout(options.getConnectionTimeout()); + ((WebSocketSecureNetworkModuleV2) netModule).setSSLhandshakeTimeout(options.getHandshakeTimeout()); + ((WebSocketSecureNetworkModuleV2) netModule).setReadTimeout(options.getReadTimeout()); + return netModule; + } + private int getPort(String uri, int defaultPort) { int port; @@ -529,7 +575,7 @@ private String getHostName(String uri) /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) */ public IMqttToken connect(Object userContext, IMqttActionListener callback) throws MqttException, MqttSecurityException @@ -539,7 +585,7 @@ public IMqttToken connect(Object userContext, IMqttActionListener callback) thro /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect() */ public IMqttToken connect() throws MqttException, MqttSecurityException @@ -549,7 +595,7 @@ public IMqttToken connect() throws MqttException, MqttSecurityException /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.client.mqttv3.MqttConnectOptions) */ public IMqttToken connect(MqttConnectOptions options) throws MqttException, MqttSecurityException @@ -559,7 +605,7 @@ public IMqttToken connect(MqttConnectOptions options) throws MqttException, Mqtt /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#connect(org.eclipse.paho.client.mqttv3.MqttConnectOptions, java.lang.Object, * org.eclipse.paho.client.mqttv3.IMqttActionListener) */ @@ -601,7 +647,7 @@ public IMqttToken connect(MqttConnectOptions options, Object userContext, IMqttA /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) */ public IMqttToken disconnect(Object userContext, IMqttActionListener callback) throws MqttException @@ -611,7 +657,7 @@ public IMqttToken disconnect(Object userContext, IMqttActionListener callback) t /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect() */ public IMqttToken disconnect() throws MqttException @@ -621,7 +667,7 @@ public IMqttToken disconnect() throws MqttException /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long) */ public IMqttToken disconnect(long quiesceTimeout) throws MqttException @@ -631,7 +677,7 @@ public IMqttToken disconnect(long quiesceTimeout) throws MqttException /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnect(long, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) */ public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActionListener callback) throws MqttException @@ -660,7 +706,7 @@ public IMqttToken disconnect(long quiesceTimeout, Object userContext, IMqttActio /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly() */ public void disconnectForcibly() throws MqttException @@ -670,7 +716,7 @@ public void disconnectForcibly() throws MqttException /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long) */ public void disconnectForcibly(long disconnectTimeout) throws MqttException @@ -680,7 +726,7 @@ public void disconnectForcibly(long disconnectTimeout) throws MqttException /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#disconnectForcibly(long, long) */ public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) throws MqttException @@ -690,24 +736,24 @@ public void disconnectForcibly(long quiesceTimeout, long disconnectTimeout) thro /* * (non-Javadoc) - * + * * @see IMqttAsyncClient#isConnected() */ public boolean isConnected() { return comms.isConnected(); } - + public boolean isConnecting() { return comms.isConnecting(); } - + public boolean isDisconnecting() { return comms.isDisconnecting(); } - + public boolean isDisconnected() { return comms.isDisconnected(); @@ -715,7 +761,7 @@ public boolean isDisconnected() /* * (non-Javadoc) - * + * * @see IMqttAsyncClient#getClientId() */ public String getClientId() @@ -735,7 +781,7 @@ public void setClientId(String clientId) /* * (non-Javadoc) - * + * * @see IMqttAsyncClient#getServerURI() */ public String getServerURI() @@ -769,7 +815,7 @@ public void setServerURI(String serverURI) throws MqttException *

    * When you build an application, the design of the topic tree should take into account the following principles of topic name syntax and semantics: *

    - * + * *
      *
    • A topic must be at least one character long.
    • *
    • Topic names are case sensitive. For example, ACCOUNTS and Accounts are two different topics.
    • @@ -778,17 +824,17 @@ public void setServerURI(String serverURI) throws MqttException *
    • A leading "/" creates a distinct topic. For example, /finance is different from finance. /finance matches "+/+" and "/+", but not "+".
    • *
    • Do not include the null character (Unicode \x0000) in any topic.
    • *
    - * + * *

    * The following principles apply to the construction and content of a topic tree: *

    - * + * *
      *
    • The length is limited to 64k but within that there are no limits to the number of levels in a topic tree.
    • *
    • There can be any number of root nodes; that is, there can be any number of topic trees.
    • *
    *

    - * + * * @param topic * the topic to use, for example "finance/stock/ibm". * @return an MqttTopic object, which can be used to publish messages to the topic. @@ -811,7 +857,7 @@ protected MqttTopic getTopic(String topic) /* * (non-Javadoc) Check and send a ping if needed.

    By default, client sends PingReq to server to keep the connection to server. For some platforms which cannot use this * mechanism, such as Android, developer needs to handle the ping request manually with this method.

    - * + * * @throws MqttException for other errors encountered while publishing the message. */ public IMqttToken checkPing(Object userContext, IMqttActionListener callback) throws MqttException @@ -831,10 +877,10 @@ public void checkActivity() { comms.checkActivity(); } - + /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) */ public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMqttActionListener callback) throws MqttException @@ -844,7 +890,7 @@ public IMqttToken subscribe(String topicFilter, int qos, Object userContext, IMq /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String, int) */ public IMqttToken subscribe(String topicFilter, int qos) throws MqttException @@ -854,7 +900,7 @@ public IMqttToken subscribe(String topicFilter, int qos) throws MqttException /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[]) */ public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttException @@ -864,7 +910,7 @@ public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttExcepti /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) */ public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) throws MqttException @@ -906,7 +952,7 @@ public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) */ public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActionListener callback) throws MqttException @@ -916,7 +962,7 @@ public IMqttToken unsubscribe(String topicFilter, Object userContext, IMqttActio /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String) */ public IMqttToken unsubscribe(String topicFilter) throws MqttException @@ -926,7 +972,7 @@ public IMqttToken unsubscribe(String topicFilter) throws MqttException /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String[]) */ public IMqttToken unsubscribe(String[] topicFilters) throws MqttException @@ -936,7 +982,7 @@ public IMqttToken unsubscribe(String[] topicFilters) throws MqttException /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#unsubscribe(java.lang.String[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) */ public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttActionListener callback) throws MqttException @@ -975,7 +1021,7 @@ public IMqttToken unsubscribe(String[] topicFilters, Object userContext, IMqttAc /* * (non-Javadoc) - * + * * @see IMqttAsyncClient#setCallback(MqttCallback) */ public void setCallback(MqttCallback callback) @@ -989,7 +1035,7 @@ public void setCallback(MqttCallback callback) * When cleanSession is set to false, an application must ensure it uses the same client identifier when it reconnects to the server to resume state and maintain assured * message delivery. *

    - * + * * @return a generated client identifier * @see MqttConnectOptions#setCleanSession(boolean) */ @@ -1001,7 +1047,7 @@ public static String generateClientId() /* * (non-Javadoc) - * + * * @see IMqttAsyncClient#getPendingDeliveryTokens() */ public IMqttDeliveryToken[] getPendingDeliveryTokens() @@ -1011,7 +1057,7 @@ public IMqttDeliveryToken[] getPendingDeliveryTokens() /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, byte[], int, boolean, java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) */ public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean retained, Object userContext, IMqttActionListener callback) throws MqttException, @@ -1025,7 +1071,7 @@ public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, byte[], int, boolean) */ public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean retained) throws MqttException, MqttPersistenceException @@ -1035,7 +1081,7 @@ public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage) */ public IMqttDeliveryToken publish(String topic, MqttMessage message) throws MqttException, MqttPersistenceException @@ -1045,7 +1091,7 @@ public IMqttDeliveryToken publish(String topic, MqttMessage message) throws Mqtt /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#publish(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage, java.lang.Object, * org.eclipse.paho.client.mqttv3.IMqttActionListener) */ @@ -1073,7 +1119,7 @@ public IMqttDeliveryToken publish(String topic, MqttMessage message, Object user /* * (non-Javadoc) - * + * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#close() */ public void close() throws MqttException diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java index d938c965..767a77a3 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java @@ -15,6 +15,7 @@ */ package org.eclipse.paho.client.mqttv3; +import org.eclipse.paho.client.mqttv3.internal.tls.CertificateChainCleaner; import org.eclipse.paho.client.mqttv3.internal.wire.UserProperty; import java.net.URI; @@ -23,6 +24,8 @@ import java.util.Properties; import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; /** * Holds the set of options that control how the client connects to a server. @@ -78,6 +81,14 @@ public class MqttConnectOptions private SocketFactory socketFactory; + private SSLSocketFactory sslSocketFactory; + + private X509TrustManager x509TrustManager; + + private ConnectionSpec connectionSpec; + + private List alpnProtocolList; + private Properties sslClientProps = null; private boolean cleanSession = CLEAN_SESSION_DEFAULT; @@ -371,6 +382,39 @@ public void setSocketFactory(SocketFactory socketFactory) this.socketFactory = socketFactory; } + public SSLSocketFactory getSslSocketFactory() { + return sslSocketFactory; + } + + public void setSslSocketFactory(SSLSocketFactory sslSocketFactory) { + this.sslSocketFactory = sslSocketFactory; + } + + public X509TrustManager getX509TrustManager() { + return x509TrustManager; + } + + public void setX509TrustManager(X509TrustManager x509TrustManager) { + this.x509TrustManager = x509TrustManager; + } + + public ConnectionSpec getConnectionSpec() { + return connectionSpec; + } + + public void setConnectionSpec( + ConnectionSpec connectionSpec) { + this.connectionSpec = connectionSpec; + } + + public List getAlpnProtocolList() { + return alpnProtocolList; + } + + public void setAlpnProtocolList(List alpnProtocolList) { + this.alpnProtocolList = alpnProtocolList; + } + /** * Returns the topic to be used for last will and testament (LWT). * diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/Protocol.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/Protocol.kt new file mode 100644 index 00000000..d0cdc17c --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/Protocol.kt @@ -0,0 +1,5 @@ +package org.eclipse.paho.client.mqttv3 + +data class Protocol( + val name: String +) diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/SuppressSignatureCheck.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/SuppressSignatureCheck.kt new file mode 100644 index 00000000..1434b1f4 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/SuppressSignatureCheck.kt @@ -0,0 +1,12 @@ +package org.eclipse.paho.client.mqttv3 + +import java.lang.annotation.Documented +import kotlin.annotation.AnnotationRetention.BINARY +import kotlin.annotation.AnnotationTarget.CLASS +import kotlin.annotation.AnnotationTarget.CONSTRUCTOR +import kotlin.annotation.AnnotationTarget.FUNCTION + +@Retention(BINARY) +@Documented +@Target(CONSTRUCTOR, CLASS, FUNCTION) +annotation class SuppressSignatureCheck diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/Util.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/Util.kt new file mode 100644 index 00000000..a7c50a51 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/Util.kt @@ -0,0 +1,68 @@ +package org.eclipse.paho.client.mqttv3 + +internal fun readFieldOrNull(instance: Any, fieldType: Class, fieldName: String): T? { + var c: Class<*> = instance.javaClass + while (c != Any::class.java) { + try { + val field = c.getDeclaredField(fieldName) + field.isAccessible = true + val value = field.get(instance) + return if (!fieldType.isInstance(value)) null else fieldType.cast(value) + } catch (_: NoSuchFieldException) { + } + + c = c.superclass + } + + // Didn't find the field we wanted. As a last gasp attempt, + // try to find the value on a delegate. + if (fieldName != "delegate") { + val delegate = readFieldOrNull(instance, Any::class.java, "delegate") + if (delegate != null) return readFieldOrNull(delegate, fieldType, fieldName) + } + + return null +} + +internal fun Array.hasIntersection( + other: Array?, + comparator: Comparator +): Boolean { + if (isEmpty() || other == null || other.isEmpty()) { + return false + } + for (a in this) { + for (b in other) { + if (comparator.compare(a, b) == 0) { + return true + } + } + } + return false +} + +internal fun Array.intersect( + other: Array, + comparator: Comparator +): Array { + val result = mutableListOf() + for (a in this) { + for (b in other) { + if (comparator.compare(a, b) == 0) { + result.add(a) + break + } + } + } + return result.toTypedArray() +} + +internal fun Array.indexOf(value: String, comparator: Comparator): Int = + indexOfFirst { comparator.compare(it, value) == 0 } + +@Suppress("UNCHECKED_CAST") +internal fun Array.concat(value: String): Array { + val result = copyOf(size + 1) + result[result.lastIndex] = value + return result as Array +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java index e1184896..0a169a41 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModule.java @@ -3,11 +3,11 @@ * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. + * and Eclipse Distribution License v1.0 which accompany this distribution. * - * The Eclipse Public License is available at + * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at + * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: @@ -63,7 +63,6 @@ public void setEnabledCiphers(String[] enabledCiphers) this.enabledCiphers = enabledCiphers; if ((socket != null) && (enabledCiphers != null)) { - ((SSLSocket) socket).setEnabledCipherSuites(enabledCiphers); } } @@ -103,7 +102,7 @@ public void start() throws IOException, MqttException throw ex; } } - + public void stop() throws IOException { // In case of SSLSocket we should not try to shutdownOutput and shutdownInput it would result diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModuleV2.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModuleV2.java new file mode 100644 index 00000000..1e99e08f --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/SSLNetworkModuleV2.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2009, 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Dave Locke - initial API and implementation and/or initial documentation + */ +package org.eclipse.paho.client.mqttv3.internal; + +import org.eclipse.paho.client.mqttv3.ConnectionSpec; +import org.eclipse.paho.client.mqttv3.ILogger; +import org.eclipse.paho.client.mqttv3.IPahoEvents; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.Protocol; +import org.eclipse.paho.client.mqttv3.internal.platform.Platform; +import org.eclipse.paho.client.mqttv3.internal.tls.CertificateChainCleaner; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * A network module for connecting over SSL. + */ +public class SSLNetworkModuleV2 extends TCPNetworkModule { + private SocketFactory socketFactory; + private SSLSocketFactory sslSocketFactory; + private X509TrustManager x509TrustManager; + private ConnectionSpec connectionSpec; + private List alpnProtocolList; + + private int handshakeTimeoutSecs; + + final static String className = SSLNetworkModuleV2.class.getName(); + + private final String TAG = "SSLNETWORKMODULE"; + + /** + * Constructs a new SSLNetworkModule using the specified host and port. The supplied + * SSLSocketFactory is used to supply the network socket. + */ + + public SSLNetworkModuleV2( + SocketFactory socketFactory, + SSLSocketFactory sslSocketFactory, + X509TrustManager x509TrustManager, + ConnectionSpec connectionSpec, + List alpnProtocolList, + String host, + int port, + String resourceContext, + ILogger logger, + IPahoEvents pahoEvents + ) { + super(socketFactory, host, port, resourceContext, logger, pahoEvents); + this.socketFactory = socketFactory; + this.sslSocketFactory = sslSocketFactory; + this.x509TrustManager = x509TrustManager; + this.connectionSpec = connectionSpec; + this.alpnProtocolList = alpnProtocolList; + } + + public void setSSLhandshakeTimeout(int timeout) { + this.handshakeTimeoutSecs = timeout; + } + + public void start() throws IOException, MqttException { + super.start(); + long socketStartTime = System.nanoTime(); + try { + pahoEvents.onSSLSocketAttempt(port, host, socket.getSoTimeout()); + + socket = sslSocketFactory.createSocket(socket, host, port, true); + + connectionSpec.apply((SSLSocket) socket, false); + if (connectionSpec.supportsTlsExtensions()) { + Platform.get().configureTlsExtensions((SSLSocket) socket, host, alpnProtocolList); + } + long socketEndTime = System.nanoTime(); + pahoEvents.onSSLSocketSuccess(port, host, socket.getSoTimeout(), + TimeUnit.NANOSECONDS.toMillis(socketEndTime - socketStartTime)); + + int soTimeout = socket.getSoTimeout(); + // RTC 765: Set a timeout to avoid the SSL handshake being blocked indefinitely + socket.setSoTimeout(this.handshakeTimeoutSecs * 1000); + long handshakeStartTime = System.nanoTime(); + ((SSLSocket) socket).startHandshake(); + long handshakeEndTime = System.nanoTime(); + + pahoEvents.onSSLHandshakeSuccess(port, host, socket.getSoTimeout(), + TimeUnit.NANOSECONDS.toMillis(handshakeEndTime - handshakeStartTime)); + + // reset timeout to default value + socket.setSoTimeout(soTimeout); + } catch (IOException ex) { + long socketEndTime = System.nanoTime(); + pahoEvents.onSSLSocketFailure(port, host, socket.getSoTimeout(), ex, + TimeUnit.NANOSECONDS.toMillis(socketEndTime - socketStartTime)); + throw ex; + } + } + + public void stop() throws IOException { + // In case of SSLSocket we should not try to shutdownOutput and shutdownInput it would result + // in java.lang.UnsupportedOperationException. only SSLSocket.close() is enough to close + // an SSLSocket. + socket.close(); + } +} \ No newline at end of file diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Android10Platform.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Android10Platform.kt new file mode 100644 index 00000000..14b636e6 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Android10Platform.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform + +import android.annotation.SuppressLint +import android.os.Build +import android.security.NetworkSecurityPolicy +import android.util.CloseGuard +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager +import org.eclipse.paho.client.mqttv3.Protocol +import org.eclipse.paho.client.mqttv3.SuppressSignatureCheck +import org.eclipse.paho.client.mqttv3.internal.platform.android.Android10SocketAdapter +import org.eclipse.paho.client.mqttv3.internal.platform.android.AndroidCertificateChainCleaner +import org.eclipse.paho.client.mqttv3.internal.platform.android.AndroidSocketAdapter +import org.eclipse.paho.client.mqttv3.internal.platform.android.BouncyCastleSocketAdapter +import org.eclipse.paho.client.mqttv3.internal.platform.android.ConscryptSocketAdapter +import org.eclipse.paho.client.mqttv3.internal.platform.android.DeferredSocketAdapter +import org.eclipse.paho.client.mqttv3.internal.tls.CertificateChainCleaner + +/** Android 29+. */ +@SuppressSignatureCheck +class Android10Platform : Platform() { + private val socketAdapters = listOfNotNull( + Android10SocketAdapter.buildIfSupported(), + DeferredSocketAdapter(AndroidSocketAdapter.playProviderFactory), + // Delay and Defer any initialisation of Conscrypt and BouncyCastle + DeferredSocketAdapter(ConscryptSocketAdapter.factory), + DeferredSocketAdapter(BouncyCastleSocketAdapter.factory) + ).filter { it.isSupported() } + + override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? = + socketAdapters.find { it.matchesSocketFactory(sslSocketFactory) } + ?.trustManager(sslSocketFactory) + + override fun configureTlsExtensions(sslSocket: SSLSocket, hostname: String?, protocols: List) { + // No TLS extensions if the socket class is custom. + socketAdapters.find { it.matchesSocket(sslSocket) } + ?.configureTlsExtensions(sslSocket, hostname, protocols) + } + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? = + // No TLS extensions if the socket class is custom. + socketAdapters.find { it.matchesSocket(sslSocket) }?.getSelectedProtocol(sslSocket) + + override fun getStackTraceForCloseable(closer: String): Any? { + return if (Build.VERSION.SDK_INT >= 30) { + CloseGuard().apply { open(closer) } + } else { + super.getStackTraceForCloseable(closer) + } + } + + override fun logCloseableLeak(message: String, stackTrace: Any?) { + if (Build.VERSION.SDK_INT >= 30) { + (stackTrace as CloseGuard).warnIfOpen() + } else { + // Unable to report via CloseGuard. As a last-ditch effort, send it to the logger. + super.logCloseableLeak(message, stackTrace) + } + } + + @SuppressLint("NewApi") + override fun isCleartextTrafficPermitted(hostname: String): Boolean = + NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted(hostname) + + override fun buildCertificateChainCleaner(trustManager: X509TrustManager): CertificateChainCleaner = + AndroidCertificateChainCleaner.buildIfSupported(trustManager) ?: super.buildCertificateChainCleaner(trustManager) + + companion object { + val isSupported: Boolean = isAndroid && Build.VERSION.SDK_INT >= 29 + + fun buildIfSupported(): Platform? = if (isSupported) Android10Platform() else null + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/AndroidPlatform.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/AndroidPlatform.kt new file mode 100644 index 00000000..c0ef8bd1 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/AndroidPlatform.kt @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform + +import android.os.Build +import android.security.NetworkSecurityPolicy +import java.io.IOException +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.net.InetSocketAddress +import java.net.Socket +import java.security.cert.TrustAnchor +import java.security.cert.X509Certificate +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager +import org.eclipse.paho.client.mqttv3.Protocol +import org.eclipse.paho.client.mqttv3.SuppressSignatureCheck +import org.eclipse.paho.client.mqttv3.internal.platform.android.AndroidCertificateChainCleaner +import org.eclipse.paho.client.mqttv3.internal.platform.android.AndroidSocketAdapter +import org.eclipse.paho.client.mqttv3.internal.platform.android.BouncyCastleSocketAdapter +import org.eclipse.paho.client.mqttv3.internal.platform.android.ConscryptSocketAdapter +import org.eclipse.paho.client.mqttv3.internal.platform.android.DeferredSocketAdapter +import org.eclipse.paho.client.mqttv3.internal.platform.android.StandardAndroidSocketAdapter +import org.eclipse.paho.client.mqttv3.internal.tls.CertificateChainCleaner +import org.eclipse.paho.client.mqttv3.internal.tls.TrustRootIndex + +/** Android 5+. */ +@SuppressSignatureCheck +class AndroidPlatform : Platform() { + private val socketAdapters = listOfNotNull( + StandardAndroidSocketAdapter.buildIfSupported(), + DeferredSocketAdapter(AndroidSocketAdapter.playProviderFactory), + // Delay and Defer any initialisation of Conscrypt and BouncyCastle + DeferredSocketAdapter(ConscryptSocketAdapter.factory), + DeferredSocketAdapter(BouncyCastleSocketAdapter.factory) + ).filter { it.isSupported() } + + @Throws(IOException::class) + override fun connectSocket( + socket: Socket, + address: InetSocketAddress, + connectTimeout: Int + ) { + try { + socket.connect(address, connectTimeout) + } catch (e: ClassCastException) { + // On android 8.0, socket.connect throws a ClassCastException due to a bug + // see https://issuetracker.google.com/issues/63649622 + if (Build.VERSION.SDK_INT == 26) { + throw IOException("Exception in connect", e) + } else { + throw e + } + } + } + + override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? = + socketAdapters.find { it.matchesSocketFactory(sslSocketFactory) } + ?.trustManager(sslSocketFactory) + + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List<@JvmSuppressWildcards Protocol> + ) { + // No TLS extensions if the socket class is custom. + socketAdapters.find { it.matchesSocket(sslSocket) } + ?.configureTlsExtensions(sslSocket, hostname, protocols) + } + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? = + // No TLS extensions if the socket class is custom. + socketAdapters.find { it.matchesSocket(sslSocket) }?.getSelectedProtocol(sslSocket) + + override fun isCleartextTrafficPermitted(hostname: String): Boolean = when { + Build.VERSION.SDK_INT >= 24 -> NetworkSecurityPolicy.getInstance() + .isCleartextTrafficPermitted(hostname) + Build.VERSION.SDK_INT >= 23 -> NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted + else -> true + } + + override fun buildCertificateChainCleaner(trustManager: X509TrustManager): CertificateChainCleaner = + AndroidCertificateChainCleaner.buildIfSupported(trustManager) + ?: super.buildCertificateChainCleaner(trustManager) + + override fun buildTrustRootIndex(trustManager: X509TrustManager): TrustRootIndex = try { + // From org.conscrypt.TrustManagerImpl, we want the method with this signature: + // private TrustAnchor findTrustAnchorByIssuerAndSignature(X509Certificate lastCert); + val method = trustManager.javaClass.getDeclaredMethod( + "findTrustAnchorByIssuerAndSignature", + X509Certificate::class.java + ) + method.isAccessible = true + CustomTrustRootIndex(trustManager, method) + } catch (e: NoSuchMethodException) { + super.buildTrustRootIndex(trustManager) + } + + /** + * A trust manager for Android applications that customize the trust manager. + * + * This class exploits knowledge of Android implementation details. This class is potentially + * much faster to initialize than [BasicTrustRootIndex] because it doesn't need to load and + * index trusted CA certificates. + */ + internal data class CustomTrustRootIndex( + private val trustManager: X509TrustManager, + private val findByIssuerAndSignatureMethod: Method + ) : TrustRootIndex { + override fun findByIssuerAndSignature(cert: X509Certificate): X509Certificate? { + return try { + val trustAnchor = findByIssuerAndSignatureMethod.invoke( + trustManager, + cert + ) as TrustAnchor + trustAnchor.trustedCert + } catch (e: IllegalAccessException) { + throw AssertionError("unable to get issues and signature", e) + } catch (_: InvocationTargetException) { + null + } + } + } + + companion object { + val isSupported: Boolean = when { + !isAndroid -> false + Build.VERSION.SDK_INT >= 30 -> false // graylisted methods are banned + else -> { + // Fail Fast + check( + Build.VERSION.SDK_INT >= 21 + ) { "Expected Android API level 21+ but was ${Build.VERSION.SDK_INT}" } + + true + } + } + + fun buildIfSupported(): Platform? = if (isSupported) AndroidPlatform() else null + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/BouncyCastlePlatform.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/BouncyCastlePlatform.kt new file mode 100644 index 00000000..81cf7108 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/BouncyCastlePlatform.kt @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform + +import java.security.KeyStore +import java.security.Provider +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.TrustManagerFactory +import javax.net.ssl.X509TrustManager +import org.bouncycastle.jsse.BCSSLSocket +import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider +import org.eclipse.paho.client.mqttv3.Protocol + +/** + * Platform using BouncyCastle if installed as the first Security Provider. + * + * Requires org.bouncycastle:bctls-jdk15on on the classpath. + */ +class BouncyCastlePlatform private constructor() : Platform() { + private val provider: Provider = BouncyCastleJsseProvider() + + override fun newSSLContext(): SSLContext = + SSLContext.getInstance("TLS", provider) + + override fun platformTrustManager(): X509TrustManager { + val factory = TrustManagerFactory.getInstance( + "PKIX", + BouncyCastleJsseProvider.PROVIDER_NAME + ) + factory.init(null as KeyStore?) + val trustManagers = factory.trustManagers!! + check(trustManagers.size == 1 && trustManagers[0] is X509TrustManager) { + "Unexpected default trust managers: ${trustManagers.contentToString()}" + } + return trustManagers[0] as X509TrustManager + } + + override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager = + throw UnsupportedOperationException( + "clientBuilder.sslSocketFactory(SSLSocketFactory) not supported with BouncyCastle" + ) + + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List<@JvmSuppressWildcards Protocol> + ) { + if (sslSocket is BCSSLSocket) { + val sslParameters = sslSocket.parameters + + // Enable ALPN. + val names = alpnProtocolNames(protocols) + sslParameters.applicationProtocols = names.toTypedArray() + + sslSocket.parameters = sslParameters + } else { + super.configureTlsExtensions(sslSocket, hostname, protocols) + } + } + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? = + if (sslSocket is BCSSLSocket) { + when (val protocol = (sslSocket as BCSSLSocket).applicationProtocol) { + // Handles both un-configured and none selected. + null, "" -> null + else -> protocol + } + } else { + super.getSelectedProtocol(sslSocket) + } + + companion object { + val isSupported: Boolean = try { + // Trigger an early exception over a fatal error, prefer a RuntimeException over Error. + Class.forName( + "org.bouncycastle.jsse.provider.BouncyCastleJsseProvider", + false, + javaClass.classLoader + ) + + true + } catch (_: ClassNotFoundException) { + false + } + + fun buildIfSupported(): BouncyCastlePlatform? = + if (isSupported) BouncyCastlePlatform() else null + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/ConscryptPlatform.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/ConscryptPlatform.kt new file mode 100644 index 00000000..2d2ff6ab --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/ConscryptPlatform.kt @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2014 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform + +import java.security.KeyStore +import java.security.Provider +import java.security.cert.X509Certificate +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLSession +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.TrustManager +import javax.net.ssl.TrustManagerFactory +import javax.net.ssl.X509TrustManager +import org.conscrypt.Conscrypt +import org.conscrypt.ConscryptHostnameVerifier +import org.eclipse.paho.client.mqttv3.Protocol + +/** + * Platform using Conscrypt (conscrypt.org) if installed as the first Security Provider. + * + * Requires org.conscrypt:conscrypt-openjdk-uber >= 2.1.0 on the classpath. + */ +class ConscryptPlatform private constructor() : Platform() { + private val provider: Provider = Conscrypt.newProvider() + + // See release notes https://groups.google.com/forum/#!forum/conscrypt + // for version differences + override fun newSSLContext(): SSLContext = + // supports TLSv1.3 by default (version api is >= 1.4.0) + SSLContext.getInstance("TLS", provider) + + override fun platformTrustManager(): X509TrustManager { + val trustManagers = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply { + init(null as KeyStore?) + }.trustManagers!! + check(trustManagers.size == 1 && trustManagers[0] is X509TrustManager) { + "Unexpected default trust managers: ${trustManagers.contentToString()}" + } + val x509TrustManager = trustManagers[0] as X509TrustManager + // Disabled because OkHttp will run anyway + Conscrypt.setHostnameVerifier(x509TrustManager, DisabledHostnameVerifier) + return x509TrustManager + } + + internal object DisabledHostnameVerifier : ConscryptHostnameVerifier { + fun verify( + hostname: String?, + session: SSLSession? + ): Boolean { + return true + } + + override fun verify( + certs: Array?, + hostname: String?, + session: SSLSession? + ): Boolean { + return true + } + } + + override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? = null + + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List<@JvmSuppressWildcards Protocol> + ) { + if (Conscrypt.isConscrypt(sslSocket)) { + // Enable session tickets. + Conscrypt.setUseSessionTickets(sslSocket, true) + + // Enable ALPN. + val names = alpnProtocolNames(protocols) + Conscrypt.setApplicationProtocols(sslSocket, names.toTypedArray()) + } else { + super.configureTlsExtensions(sslSocket, hostname, protocols) + } + } + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? = + if (Conscrypt.isConscrypt(sslSocket)) { + Conscrypt.getApplicationProtocol(sslSocket) + } else { + super.getSelectedProtocol(sslSocket) + } + + override fun newSslSocketFactory(trustManager: X509TrustManager): SSLSocketFactory { + return newSSLContext().apply { + init(null, arrayOf(trustManager), null) + }.socketFactory + } + + companion object { + val isSupported: Boolean = try { + // Trigger an early exception over a fatal error, prefer a RuntimeException over Error. + Class.forName("org.conscrypt.Conscrypt\$Version", false, javaClass.classLoader) + + when { + // Bump this version if we ever have a binary incompatibility + Conscrypt.isAvailable() && atLeastVersion(2, 1, 0) -> true + else -> false + } + } catch (e: NoClassDefFoundError) { + false + } catch (e: ClassNotFoundException) { + false + } + + fun buildIfSupported(): ConscryptPlatform? = if (isSupported) ConscryptPlatform() else null + + fun atLeastVersion(major: Int, minor: Int = 0, patch: Int = 0): Boolean { + val conscryptVersion = Conscrypt.version() ?: return false + + if (conscryptVersion.major() != major) { + return conscryptVersion.major() > major + } + + if (conscryptVersion.minor() != minor) { + return conscryptVersion.minor() > minor + } + + return conscryptVersion.patch() >= patch + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Jdk8WithJettyBootPlatform.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Jdk8WithJettyBootPlatform.kt new file mode 100644 index 00000000..62f0cc2a --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Jdk8WithJettyBootPlatform.kt @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform + +import java.lang.reflect.InvocationHandler +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.lang.reflect.Proxy +import javax.net.ssl.SSLSocket +import org.eclipse.paho.client.mqttv3.Protocol + +/** OpenJDK 8 with `org.mortbay.jetty.alpn:alpn-boot` in the boot class path. */ +class Jdk8WithJettyBootPlatform( + private val putMethod: Method, + private val getMethod: Method, + private val removeMethod: Method, + private val clientProviderClass: Class<*>, + private val serverProviderClass: Class<*> +) : Platform() { + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List + ) { + val names = alpnProtocolNames(protocols) + + try { + val alpnProvider = Proxy.newProxyInstance( + Platform::class.java.classLoader, + arrayOf(clientProviderClass, serverProviderClass), + AlpnProvider(names) + ) + putMethod.invoke(null, sslSocket, alpnProvider) + } catch (e: InvocationTargetException) { + throw AssertionError("failed to set ALPN", e) + } catch (e: IllegalAccessException) { + throw AssertionError("failed to set ALPN", e) + } + } + + override fun afterHandshake(sslSocket: SSLSocket) { + try { + removeMethod.invoke(null, sslSocket) + } catch (e: IllegalAccessException) { + throw AssertionError("failed to remove ALPN", e) + } catch (e: InvocationTargetException) { + throw AssertionError("failed to remove ALPN", e) + } + } + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? { + try { + val provider = + Proxy.getInvocationHandler(getMethod.invoke(null, sslSocket)) as AlpnProvider + if (!provider.unsupported && provider.selected == null) { + log("ALPN callback dropped: HTTP/2 is disabled. " + "Is alpn-boot on the boot class path?") + return null + } + return if (provider.unsupported) null else provider.selected + } catch (e: InvocationTargetException) { + throw AssertionError("failed to get ALPN selected protocol", e) + } catch (e: IllegalAccessException) { + throw AssertionError("failed to get ALPN selected protocol", e) + } + } + + /** + * Handle the methods of ALPN's ClientProvider and ServerProvider without a compile-time + * dependency on those interfaces. + */ + private class AlpnProvider( + /** This peer's supported protocols. */ + private val protocols: List + ) : InvocationHandler { + /** Set when remote peer notifies ALPN is unsupported. */ + var unsupported: Boolean = false + + /** The protocol the server selected. */ + var selected: String? = null + + @Throws(Throwable::class) + override fun invoke(proxy: Any, method: Method, args: Array?): Any? { + val callArgs = args ?: arrayOf() + val methodName = method.name + val returnType = method.returnType + if (methodName == "supports" && Boolean::class.javaPrimitiveType == returnType) { + return true // ALPN is supported. + } else if (methodName == "unsupported" && Void.TYPE == returnType) { + this.unsupported = true // Peer doesn't support ALPN. + return null + } else if (methodName == "protocols" && callArgs.isEmpty()) { + return protocols // Client advertises these protocols. + } else if ((methodName == "selectProtocol" || methodName == "select") && + String::class.java == returnType && callArgs.size == 1 && callArgs[0] is List<*> + ) { + val peerProtocols = callArgs[0] as List<*> + // Pick the first known protocol the peer advertises. + for (i in 0..peerProtocols.size) { + val protocol = peerProtocols[i] as String + if (protocol in protocols) { + selected = protocol + return selected + } + } + selected = protocols[0] // On no intersection, try peer's first protocol. + return selected + } else if ((methodName == "protocolSelected" || methodName == "selected") && callArgs.size == 1) { + this.selected = callArgs[0] as String // Server selected this protocol. + return null + } else { + return method.invoke(this, *callArgs) + } + } + } + + companion object { + fun buildIfSupported(): Platform? { + val jvmVersion = System.getProperty("java.specification.version", "unknown") + try { + // 1.8, 9, 10, 11, 12 etc + val version = jvmVersion.toInt() + if (version >= 9) return null + } catch (_: NumberFormatException) { + // expected on >= JDK 9 + } + + // Find Jetty's ALPN extension for OpenJDK. + try { + val alpnClassName = "org.eclipse.jetty.alpn.ALPN" + val alpnClass = Class.forName(alpnClassName, true, null) + val providerClass = Class.forName("$alpnClassName\$Provider", true, null) + val clientProviderClass = + Class.forName("$alpnClassName\$ClientProvider", true, null) + val serverProviderClass = + Class.forName("$alpnClassName\$ServerProvider", true, null) + val putMethod = alpnClass.getMethod("put", SSLSocket::class.java, providerClass) + val getMethod = alpnClass.getMethod("get", SSLSocket::class.java) + val removeMethod = alpnClass.getMethod("remove", SSLSocket::class.java) + return Jdk8WithJettyBootPlatform( + putMethod, + getMethod, + removeMethod, + clientProviderClass, + serverProviderClass + ) + } catch (_: ClassNotFoundException) { + } catch (_: NoSuchMethodException) { + } + + return null + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Jdk9Platform.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Jdk9Platform.kt new file mode 100644 index 00000000..c0285867 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Jdk9Platform.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform + +import java.security.NoSuchAlgorithmException +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager +import org.eclipse.paho.client.mqttv3.Protocol +import org.eclipse.paho.client.mqttv3.SuppressSignatureCheck + +/** OpenJDK 9+. */ +open class Jdk9Platform : Platform() { + @SuppressSignatureCheck + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List<@JvmSuppressWildcards Protocol> + ) { + val sslParameters = sslSocket.sslParameters + + val names = alpnProtocolNames(protocols) + + sslParameters.applicationProtocols = names.toTypedArray() + + sslSocket.sslParameters = sslParameters + } + + @SuppressSignatureCheck + override fun getSelectedProtocol(sslSocket: SSLSocket): String? { + return try { + // SSLSocket.getApplicationProtocol returns "" if application protocols values will not + // be used. Observed if you didn't specify SSLParameters.setApplicationProtocols + when (val protocol = sslSocket.applicationProtocol) { + null, "" -> null + else -> protocol + } + } catch (e: UnsupportedOperationException) { + // https://docs.oracle.com/javase/9/docs/api/javax/net/ssl/SSLSocket.html#getApplicationProtocol-- + null + } + } + + override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? { + // Not supported due to access checks on JDK 9+: + // java.lang.reflect.InaccessibleObjectException: Unable to make member of class + // sun.security.ssl.SSLSocketFactoryImpl accessible: module java.base does not export + // sun.security.ssl to unnamed module @xxx + throw UnsupportedOperationException( + "clientBuilder.sslSocketFactory(SSLSocketFactory) not supported on JDK 8 (>= 252) or JDK 9+" + ) + } + + override fun newSSLContext(): SSLContext { + return when { + majorVersion != null && majorVersion >= 9 -> + SSLContext.getInstance("TLS") + else -> + try { + // Based on SSLSocket.getApplicationProtocol check we should + // have TLSv1.3 if we request it. + // See https://www.oracle.com/java/technologies/javase/8u261-relnotes.html + SSLContext.getInstance("TLSv1.3") + } catch (nsae: NoSuchAlgorithmException) { + SSLContext.getInstance("TLS") + } + } + } + + companion object { + val isAvailable: Boolean + + val majorVersion = System.getProperty("java.specification.version")?.toIntOrNull() + + init { + isAvailable = if (majorVersion != null) { + majorVersion >= 9 + } else { + try { + // also present on JDK8 after build 252. + SSLSocket::class.java.getMethod("getApplicationProtocol") + true + } catch (nsme: NoSuchMethodException) { + false + } + } + } + + fun buildIfSupported(): Jdk9Platform? = if (isAvailable) Jdk9Platform() else null + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/OpenJSSEPlatform.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/OpenJSSEPlatform.kt new file mode 100644 index 00000000..718ca797 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/OpenJSSEPlatform.kt @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform + +import java.security.KeyStore +import java.security.Provider +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.TrustManagerFactory +import javax.net.ssl.X509TrustManager +import org.eclipse.paho.client.mqttv3.Protocol + +/** + * Platform using OpenJSSE (https://github.com/openjsse/openjsse) if installed as the first + * Security Provider. + * + * Requires org.openjsse:openjsse >= 1.1.0 on the classpath. + */ +class OpenJSSEPlatform private constructor() : Platform() { + private val provider: Provider = org.openjsse.net.ssl.OpenJSSE() + + // Selects TLSv1.3 so we are specific about our intended version ranges (not just 1.3) + // and because it's a common pattern for VMs to have differences between supported and + // defaulted versions for TLS based on what is requested. + override fun newSSLContext(): SSLContext = + SSLContext.getInstance("TLSv1.3", provider) + + override fun platformTrustManager(): X509TrustManager { + val factory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm(), + provider + ) + factory.init(null as KeyStore?) + val trustManagers = factory.trustManagers!! + check(trustManagers.size == 1 && trustManagers[0] is X509TrustManager) { + "Unexpected default trust managers: ${trustManagers.contentToString()}" + } + return trustManagers[0] as X509TrustManager + } + + override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager = + throw UnsupportedOperationException( + "clientBuilder.sslSocketFactory(SSLSocketFactory) not supported with OpenJSSE" + ) + + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List<@JvmSuppressWildcards Protocol> + ) { + if (sslSocket is org.openjsse.javax.net.ssl.SSLSocket) { + val sslParameters = sslSocket.sslParameters + + if (sslParameters is org.openjsse.javax.net.ssl.SSLParameters) { + // Enable ALPN. + val names = alpnProtocolNames(protocols) + sslParameters.applicationProtocols = names.toTypedArray() + + sslSocket.sslParameters = sslParameters + } + } else { + super.configureTlsExtensions(sslSocket, hostname, protocols) + } + } + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? = + if (sslSocket is org.openjsse.javax.net.ssl.SSLSocket) { + when (val protocol = sslSocket.applicationProtocol) { + // Handles both un-configured and none selected. + null, "" -> null + else -> protocol + } + } else { + super.getSelectedProtocol(sslSocket) + } + + companion object { + val isSupported: Boolean = try { + // Trigger an early exception over a fatal error, prefer a RuntimeException over Error. + Class.forName("org.openjsse.net.ssl.OpenJSSE", false, javaClass.classLoader) + + true + } catch (_: ClassNotFoundException) { + false + } + + fun buildIfSupported(): OpenJSSEPlatform? = if (isSupported) OpenJSSEPlatform() else null + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Platform.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Platform.kt new file mode 100644 index 00000000..59064f06 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/Platform.kt @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2012 Square, Inc. + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform + +import java.io.IOException +import java.net.InetSocketAddress +import java.net.Socket +import java.security.GeneralSecurityException +import java.security.KeyStore +import java.security.Security +import java.util.logging.Level +import java.util.logging.Logger +import javax.net.ssl.SSLContext +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.TrustManager +import javax.net.ssl.TrustManagerFactory +import javax.net.ssl.X509TrustManager +import okio.Buffer +import org.eclipse.paho.client.mqttv3.Protocol +import org.eclipse.paho.client.mqttv3.internal.tls.BasicCertificateChainCleaner +import org.eclipse.paho.client.mqttv3.internal.tls.BasicTrustRootIndex +import org.eclipse.paho.client.mqttv3.internal.tls.CertificateChainCleaner +import org.eclipse.paho.client.mqttv3.internal.tls.TrustRootIndex +import org.eclipse.paho.client.mqttv3.readFieldOrNull + +/** + * Access to platform-specific features. + * + * ### Session Tickets + * + * Supported on Android 2.3+. + * Supported on JDK 8+ via Conscrypt. + * + * ### ALPN (Application Layer Protocol Negotiation) + * + * Supported on Android 5.0+. + * + * Supported on OpenJDK 8 via the JettyALPN-boot library or Conscrypt. + * + * Supported on OpenJDK 9+ via SSLParameters and SSLSocket features. + * + * ### Trust Manager Extraction + * + * Supported on Android 2.3+ and OpenJDK 7+. There are no public APIs to recover the trust + * manager that was used to create an [SSLSocketFactory]. + * + * Not supported by choice on JDK9+ due to access checks. + * + * ### Android Cleartext Permit Detection + * + * Supported on Android 6.0+ via `NetworkSecurityPolicy`. + */ +open class Platform { + + /** Prefix used on custom headers. */ + fun getPrefix() = "OkHttp" + + open fun newSSLContext(): SSLContext = SSLContext.getInstance("TLS") + + open fun platformTrustManager(): X509TrustManager { + val factory = TrustManagerFactory.getInstance( + TrustManagerFactory.getDefaultAlgorithm() + ) + factory.init(null as KeyStore?) + val trustManagers = factory.trustManagers!! + check(trustManagers.size == 1 && trustManagers[0] is X509TrustManager) { + "Unexpected default trust managers: ${trustManagers.contentToString()}" + } + return trustManagers[0] as X509TrustManager + } + + open fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? { + return try { + // Attempt to get the trust manager from an OpenJDK socket factory. We attempt this on all + // platforms in order to support Robolectric, which mixes classes from both Android and the + // Oracle JDK. Note that we don't support HTTP/2 or other nice features on Robolectric. + val sslContextClass = Class.forName("sun.security.ssl.SSLContextImpl") + val context = + readFieldOrNull(sslSocketFactory, sslContextClass, "context") ?: return null + readFieldOrNull(context, X509TrustManager::class.java, "trustManager") + } catch (e: ClassNotFoundException) { + null + } catch (e: RuntimeException) { + // Throws InaccessibleObjectException (added in JDK9) on JDK 17 due to + // JEP 403 Strongly Encapsulate JDK Internals. + if (e.javaClass.name != "java.lang.reflect.InaccessibleObjectException") { + throw e + } + + null + } + } + + /** + * Configure TLS extensions on `sslSocket` for `route`. + */ + open fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List<@JvmSuppressWildcards Protocol> + ) { + } + + /** Called after the TLS handshake to release resources allocated by [configureTlsExtensions]. */ + open fun afterHandshake(sslSocket: SSLSocket) { + } + + /** Returns the negotiated protocol, or null if no protocol was negotiated. */ + open fun getSelectedProtocol(sslSocket: SSLSocket): String? = null + + @Throws(IOException::class) + open fun connectSocket(socket: Socket, address: InetSocketAddress, connectTimeout: Int) { + socket.connect(address, connectTimeout) + } + + open fun log(message: String, level: Int = INFO, t: Throwable? = null) { + val logLevel = if (level == WARN) Level.WARNING else Level.INFO + logger.log(logLevel, message, t) + } + + open fun isCleartextTrafficPermitted(hostname: String): Boolean = true + + /** + * Returns an object that holds a stack trace created at the moment this method is executed. This + * should be used specifically for [java.io.Closeable] objects and in conjunction with + * [logCloseableLeak]. + */ + open fun getStackTraceForCloseable(closer: String): Any? { + return when { + logger.isLoggable(Level.FINE) -> Throwable(closer) // These are expensive to allocate. + else -> null + } + } + + open fun logCloseableLeak(message: String, stackTrace: Any?) { + var logMessage = message + if (stackTrace == null) { + logMessage += " To see where this was allocated, set the OkHttpClient logger level to " + + "FINE: Logger.getLogger(OkHttpClient.class.getName()).setLevel(Level.FINE);" + } + log(logMessage, WARN, stackTrace as Throwable?) + } + + open fun buildCertificateChainCleaner(trustManager: X509TrustManager): CertificateChainCleaner = + BasicCertificateChainCleaner(buildTrustRootIndex(trustManager)) + + open fun buildTrustRootIndex(trustManager: X509TrustManager): TrustRootIndex = + BasicTrustRootIndex(*trustManager.acceptedIssuers) + + open fun newSslSocketFactory(trustManager: X509TrustManager): SSLSocketFactory { + try { + return newSSLContext().apply { + init(null, arrayOf(trustManager), null) + }.socketFactory + } catch (e: GeneralSecurityException) { + throw AssertionError("No System TLS: $e", e) // The system has no TLS. Just give up. + } + } + + override fun toString(): String = javaClass.simpleName + + companion object { + @Volatile private var platform = findPlatform() + + const val INFO = 4 + const val WARN = 5 + + private val logger = Logger.getLogger(Platform::class.java.name) + + @JvmStatic + fun get(): Platform = platform + + fun resetForTests(platform: Platform = findPlatform()) { + Companion.platform = platform + } + + fun alpnProtocolNames(protocols: List) = + protocols.map { it.name } + + // This explicit check avoids activating in Android Studio with Android specific classes + // available when running plugins inside the IDE. + val isAndroid: Boolean + get() = "Dalvik" == System.getProperty("java.vm.name") + + private val isConscryptPreferred: Boolean + get() { + val preferredProvider = Security.getProviders()[0].name + return "Conscrypt" == preferredProvider + } + + private val isOpenJSSEPreferred: Boolean + get() { + val preferredProvider = Security.getProviders()[0].name + return "OpenJSSE" == preferredProvider + } + + private val isBouncyCastlePreferred: Boolean + get() { + val preferredProvider = Security.getProviders()[0].name + return "BC" == preferredProvider + } + + /** Attempt to match the host runtime to a capable Platform implementation. */ + private fun findPlatform(): Platform = if (isAndroid) { + findAndroidPlatform() + } else { + findJvmPlatform() + } + + private fun findAndroidPlatform(): Platform { + return Android10Platform.buildIfSupported() ?: AndroidPlatform.buildIfSupported()!! + } + + private fun findJvmPlatform(): Platform { + if (isConscryptPreferred) { + val conscrypt = ConscryptPlatform.buildIfSupported() + + if (conscrypt != null) { + return conscrypt + } + } + + if (isBouncyCastlePreferred) { + val bc = BouncyCastlePlatform.buildIfSupported() + + if (bc != null) { + return bc + } + } + + if (isOpenJSSEPreferred) { + val openJSSE = OpenJSSEPlatform.buildIfSupported() + + if (openJSSE != null) { + return openJSSE + } + } + + // An Oracle JDK 9 like OpenJDK, or JDK 8 251+. + val jdk9 = Jdk9Platform.buildIfSupported() + + if (jdk9 != null) { + return jdk9 + } + + // An Oracle JDK 8 like OpenJDK, pre 251. + val jdkWithJettyBoot = Jdk8WithJettyBootPlatform.buildIfSupported() + + if (jdkWithJettyBoot != null) { + return jdkWithJettyBoot + } + + return Platform() + } + + /** + * Returns the concatenation of 8-bit, length prefixed protocol names. + * http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-04#page-4 + */ + fun concatLengthPrefixed(protocols: List): ByteArray { + val result = Buffer() + for (protocol in alpnProtocolNames(protocols)) { + result.writeByte(protocol.length) + result.writeUtf8(protocol) + } + return result.readByteArray() + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/Android10SocketAdapter.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/Android10SocketAdapter.kt new file mode 100644 index 00000000..7a34b6f0 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/Android10SocketAdapter.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform.android + +import android.annotation.SuppressLint +import android.net.ssl.SSLSockets +import android.os.Build +import java.io.IOException +import javax.net.ssl.SSLSocket +import org.eclipse.paho.client.mqttv3.Protocol +import org.eclipse.paho.client.mqttv3.SuppressSignatureCheck +import org.eclipse.paho.client.mqttv3.internal.platform.Platform +import org.eclipse.paho.client.mqttv3.internal.platform.Platform.Companion.isAndroid + +/** + * Simple non-reflection SocketAdapter for Android Q+. + * + * These API assumptions make it unsuitable for use on earlier Android versions. + */ +@SuppressLint("NewApi") +@SuppressSignatureCheck +class Android10SocketAdapter : SocketAdapter { + override fun matchesSocket(sslSocket: SSLSocket): Boolean = + SSLSockets.isSupportedSocket(sslSocket) + + override fun isSupported(): Boolean = Companion.isSupported() + + @SuppressLint("NewApi") + override fun getSelectedProtocol(sslSocket: SSLSocket): String? { + return try { + // SSLSocket.getApplicationProtocol returns "" if application protocols values will not + // be used. Observed if you didn't specify SSLParameters.setApplicationProtocols + when (val protocol = sslSocket.applicationProtocol) { + null, "" -> null + else -> protocol + } + } catch (e: UnsupportedOperationException) { + // https://docs.oracle.com/javase/9/docs/api/javax/net/ssl/SSLSocket.html#getApplicationProtocol-- + null + } + } + + @SuppressLint("NewApi") + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List + ) { + try { + val sslParameters = sslSocket.sslParameters + + // Enable ALPN. + sslParameters.applicationProtocols = + Platform.alpnProtocolNames(protocols).toTypedArray() + + sslSocket.sslParameters = sslParameters + } catch (iae: IllegalArgumentException) { + // probably java.lang.IllegalArgumentException: Invalid input to toASCII from IDN.toASCII + throw IOException("Android internal error", iae) + } + } + + @SuppressSignatureCheck + companion object { + fun buildIfSupported(): SocketAdapter? = + if (isSupported()) Android10SocketAdapter() else null + + fun isSupported() = isAndroid && Build.VERSION.SDK_INT >= 29 + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidCertificateChainCleaner.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidCertificateChainCleaner.kt new file mode 100644 index 00000000..24b6444f --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidCertificateChainCleaner.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform.android + +import android.net.http.X509TrustManagerExtensions +import java.security.cert.Certificate +import java.security.cert.CertificateException +import java.security.cert.X509Certificate +import javax.net.ssl.SSLPeerUnverifiedException +import javax.net.ssl.X509TrustManager +import org.eclipse.paho.client.mqttv3.SuppressSignatureCheck +import org.eclipse.paho.client.mqttv3.internal.tls.CertificateChainCleaner + +/** + * Android implementation of CertificateChainCleaner using direct Android API calls. + * Not used if X509TrustManager doesn't implement [X509TrustManager.checkServerTrusted] with + * an additional host param. + */ +internal class AndroidCertificateChainCleaner( + private val trustManager: X509TrustManager, + private val x509TrustManagerExtensions: X509TrustManagerExtensions +) : CertificateChainCleaner() { + @Suppress("UNCHECKED_CAST") + @Throws(SSLPeerUnverifiedException::class) + @SuppressSignatureCheck + override + fun clean(chain: List, hostname: String): List { + val certificates = (chain as List).toTypedArray() + try { + return x509TrustManagerExtensions.checkServerTrusted(certificates, "RSA", hostname) + } catch (ce: CertificateException) { + throw SSLPeerUnverifiedException(ce.message).apply { initCause(ce) } + } + } + + override fun equals(other: Any?): Boolean = + other is AndroidCertificateChainCleaner && + other.trustManager === this.trustManager + + override fun hashCode(): Int = System.identityHashCode(trustManager) + + companion object { + @SuppressSignatureCheck + fun buildIfSupported(trustManager: X509TrustManager): AndroidCertificateChainCleaner? { + val extensions = try { + X509TrustManagerExtensions(trustManager) + } catch (iae: IllegalArgumentException) { + // X509TrustManagerExtensions checks for checkServerTrusted(X509Certificate[], String, String) + null + } + + return when { + extensions != null -> AndroidCertificateChainCleaner(trustManager, extensions) + else -> null + } + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt new file mode 100644 index 00000000..f51ef808 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform.android + +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import javax.net.ssl.SSLSocket +import org.eclipse.paho.client.mqttv3.Protocol +import org.eclipse.paho.client.mqttv3.internal.platform.AndroidPlatform +import org.eclipse.paho.client.mqttv3.internal.platform.Platform +import org.eclipse.paho.client.mqttv3.internal.platform.android.DeferredSocketAdapter.Factory + +/** + * Modern reflection based SocketAdapter for Conscrypt class SSLSockets. + * + * This is used directly for providers where class name is known e.g. the Google Play Provider + * but we can't compile directly against it, or in fact reliably know if it is registered and + * on classpath. + */ +open class AndroidSocketAdapter(private val sslSocketClass: Class) : SocketAdapter { + private val setUseSessionTickets: Method = + sslSocketClass.getDeclaredMethod("setUseSessionTickets", Boolean::class.javaPrimitiveType) + private val setHostname = sslSocketClass.getMethod("setHostname", String::class.java) + private val getAlpnSelectedProtocol = sslSocketClass.getMethod("getAlpnSelectedProtocol") + private val setAlpnProtocols = + sslSocketClass.getMethod("setAlpnProtocols", ByteArray::class.java) + + override fun isSupported(): Boolean = AndroidPlatform.isSupported + + override fun matchesSocket(sslSocket: SSLSocket): Boolean = sslSocketClass.isInstance(sslSocket) + + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List + ) { + // No TLS extensions if the socket class is custom. + if (matchesSocket(sslSocket)) { + try { + // Enable session tickets. + setUseSessionTickets.invoke(sslSocket, true) + + if (hostname != null) { + // This is SSLParameters.setServerNames() in API 24+. + setHostname.invoke(sslSocket, hostname) + } + + // Enable ALPN. + setAlpnProtocols.invoke( + sslSocket, + Platform.concatLengthPrefixed(protocols) + ) + } catch (e: IllegalAccessException) { + throw AssertionError(e) + } catch (e: InvocationTargetException) { + throw AssertionError(e) + } + } + } + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? { + // No TLS extensions if the socket class is custom. + if (!matchesSocket(sslSocket)) { + return null + } + + return try { + val alpnResult = getAlpnSelectedProtocol.invoke(sslSocket) as ByteArray? + alpnResult?.toString(Charsets.UTF_8) + } catch (e: IllegalAccessException) { + throw AssertionError(e) + } catch (e: InvocationTargetException) { + // https://github.com/square/okhttp/issues/5587 + val cause = e.cause + when { + cause is NullPointerException && cause.message == "ssl == null" -> null + else -> throw AssertionError(e) + } + } + } + + companion object { + val playProviderFactory: Factory = + factory("com.google.android.gms.org.conscrypt") + + /** + * Builds a SocketAdapter from an observed implementation class, by grabbing the Class + * reference to perform reflection on at runtime. + * + * @param actualSSLSocketClass the runtime class of Conscrypt class socket. + */ + private fun build(actualSSLSocketClass: Class): AndroidSocketAdapter { + var possibleClass: Class? = actualSSLSocketClass + while (possibleClass != null && possibleClass.simpleName != "OpenSSLSocketImpl") { + possibleClass = possibleClass.superclass + + if (possibleClass == null) { + throw AssertionError( + "No OpenSSLSocketImpl superclass of socket of type $actualSSLSocketClass" + ) + } + } + + return AndroidSocketAdapter(possibleClass!!) + } + + fun factory(packageName: String): Factory { + return object : Factory { + override fun matchesSocket(sslSocket: SSLSocket): Boolean = + sslSocket.javaClass.name.startsWith("$packageName.") + + override fun create(sslSocket: SSLSocket): SocketAdapter { + return build(sslSocket.javaClass) + } + } + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/BouncyCastleSocketAdapter.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/BouncyCastleSocketAdapter.kt new file mode 100644 index 00000000..530f9ff8 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/BouncyCastleSocketAdapter.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform.android + +import javax.net.ssl.SSLSocket +import org.bouncycastle.jsse.BCSSLSocket +import org.eclipse.paho.client.mqttv3.Protocol +import org.eclipse.paho.client.mqttv3.internal.platform.BouncyCastlePlatform +import org.eclipse.paho.client.mqttv3.internal.platform.Platform +import org.eclipse.paho.client.mqttv3.internal.platform.android.DeferredSocketAdapter.Factory + +/** + * Simple non-reflection SocketAdapter for BouncyCastle. + */ +class BouncyCastleSocketAdapter : SocketAdapter { + override fun matchesSocket(sslSocket: SSLSocket): Boolean = sslSocket is BCSSLSocket + + override fun isSupported(): Boolean = BouncyCastlePlatform.isSupported + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? { + val s = sslSocket as BCSSLSocket + + return when (val protocol = s.applicationProtocol) { + null, "" -> null + else -> protocol + } + } + + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List + ) { + // No TLS extensions if the socket class is custom. + if (matchesSocket(sslSocket)) { + val bcSocket = sslSocket as BCSSLSocket + + val sslParameters = bcSocket.parameters + + // Enable ALPN. + sslParameters.applicationProtocols = Platform.alpnProtocolNames(protocols).toTypedArray() + + bcSocket.parameters = sslParameters + } + } + + companion object { + val factory = object : Factory { + override fun matchesSocket(sslSocket: SSLSocket): Boolean { + return BouncyCastlePlatform.isSupported && sslSocket is BCSSLSocket + } + override fun create(sslSocket: SSLSocket): SocketAdapter = BouncyCastleSocketAdapter() + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/ConscryptSocketAdapter.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/ConscryptSocketAdapter.kt new file mode 100644 index 00000000..47462765 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/ConscryptSocketAdapter.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform.android + +import javax.net.ssl.SSLSocket +import org.conscrypt.Conscrypt +import org.eclipse.paho.client.mqttv3.Protocol +import org.eclipse.paho.client.mqttv3.internal.platform.ConscryptPlatform +import org.eclipse.paho.client.mqttv3.internal.platform.Platform +import org.eclipse.paho.client.mqttv3.internal.platform.android.DeferredSocketAdapter.Factory + +/** + * Simple non-reflection SocketAdapter for Conscrypt when included as an application dependency + * directly. + */ +class ConscryptSocketAdapter : SocketAdapter { + override fun matchesSocket(sslSocket: SSLSocket): Boolean = Conscrypt.isConscrypt(sslSocket) + + override fun isSupported(): Boolean = ConscryptPlatform.isSupported + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? = + when { + matchesSocket(sslSocket) -> Conscrypt.getApplicationProtocol(sslSocket) + else -> null // No TLS extensions if the socket class is custom. + } + + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List + ) { + // No TLS extensions if the socket class is custom. + if (matchesSocket(sslSocket)) { + // Enable session tickets. + Conscrypt.setUseSessionTickets(sslSocket, true) + + // Enable ALPN. + val names = Platform.alpnProtocolNames(protocols) + Conscrypt.setApplicationProtocols(sslSocket, names.toTypedArray()) + } + } + + companion object { + val factory = object : Factory { + override fun matchesSocket(sslSocket: SSLSocket): Boolean { + return ConscryptPlatform.isSupported && Conscrypt.isConscrypt(sslSocket) + } + + override fun create(sslSocket: SSLSocket): SocketAdapter = ConscryptSocketAdapter() + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/DeferredSocketAdapter.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/DeferredSocketAdapter.kt new file mode 100644 index 00000000..fb1987d2 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/DeferredSocketAdapter.kt @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform.android + +import javax.net.ssl.SSLSocket +import org.eclipse.paho.client.mqttv3.Protocol + +/** + * Deferred implementation of SocketAdapter that works by observing the socket + * and initializing on first use. + * + * We use this because eager classpath checks cause confusion and excessive logging in Android, + * and we can't rely on classnames after proguard, so are probably best served by falling through + * to a situation of trying our least likely noisiest options. + */ +class DeferredSocketAdapter(private val socketAdapterFactory: Factory) : SocketAdapter { + private var delegate: SocketAdapter? = null + + override fun isSupported(): Boolean { + return true + } + + override fun matchesSocket(sslSocket: SSLSocket): Boolean = + socketAdapterFactory.matchesSocket(sslSocket) + + override fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List + ) { + getDelegate(sslSocket)?.configureTlsExtensions(sslSocket, hostname, protocols) + } + + override fun getSelectedProtocol(sslSocket: SSLSocket): String? { + return getDelegate(sslSocket)?.getSelectedProtocol(sslSocket) + } + + @Synchronized + private fun getDelegate(sslSocket: SSLSocket): SocketAdapter? { + if (this.delegate == null && socketAdapterFactory.matchesSocket(sslSocket)) { + this.delegate = socketAdapterFactory.create(sslSocket) + } + + return delegate + } + + interface Factory { + fun matchesSocket(sslSocket: SSLSocket): Boolean + fun create(sslSocket: SSLSocket): SocketAdapter + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/SocketAdapter.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/SocketAdapter.kt new file mode 100644 index 00000000..3c2e2cbd --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/SocketAdapter.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform.android + +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager +import org.eclipse.paho.client.mqttv3.Protocol + +interface SocketAdapter { + fun isSupported(): Boolean + fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? = null + fun matchesSocket(sslSocket: SSLSocket): Boolean + fun matchesSocketFactory(sslSocketFactory: SSLSocketFactory): Boolean = false + + fun configureTlsExtensions( + sslSocket: SSLSocket, + hostname: String?, + protocols: List + ) + + fun getSelectedProtocol(sslSocket: SSLSocket): String? +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/StandardAndroidSocketAdapter.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/StandardAndroidSocketAdapter.kt new file mode 100644 index 00000000..43425639 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/StandardAndroidSocketAdapter.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.platform.android + +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager +import org.eclipse.paho.client.mqttv3.internal.platform.Platform +import org.eclipse.paho.client.mqttv3.readFieldOrNull + +/** + * Base Android reflection based SocketAdapter for the built in Android SSLSocket. + * + * It's assumed to always be present with known class names on Android devices, so we build + * optimistically via [buildIfSupported]. But it also doesn't assume a compile time API. + */ +class StandardAndroidSocketAdapter( + sslSocketClass: Class, + private val sslSocketFactoryClass: Class, + private val paramClass: Class<*> +) : AndroidSocketAdapter(sslSocketClass) { + + override fun matchesSocketFactory(sslSocketFactory: SSLSocketFactory): Boolean = + sslSocketFactoryClass.isInstance(sslSocketFactory) + + override fun trustManager(sslSocketFactory: SSLSocketFactory): X509TrustManager? { + val context: Any? = + readFieldOrNull( + sslSocketFactory, + paramClass, + "sslParameters" + ) + val x509TrustManager = readFieldOrNull( + context!!, + X509TrustManager::class.java, + "x509TrustManager" + ) + return x509TrustManager ?: readFieldOrNull( + context, + X509TrustManager::class.java, + "trustManager" + ) + } + + companion object { + @Suppress("UNCHECKED_CAST") + fun buildIfSupported(packageName: String = "com.android.org.conscrypt"): SocketAdapter? { + return try { + val sslSocketClass = Class.forName("$packageName.OpenSSLSocketImpl") as Class + val sslSocketFactoryClass = + Class.forName("$packageName.OpenSSLSocketFactoryImpl") as Class + val paramsClass = Class.forName("$packageName.SSLParametersImpl") + + StandardAndroidSocketAdapter(sslSocketClass, sslSocketFactoryClass, paramsClass) + } catch (e: Exception) { + Platform.get().log(level = Platform.WARN, message = "unable to load android socket classes", t = e) + null + } + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/BasicCertificateChainCleaner.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/BasicCertificateChainCleaner.kt new file mode 100644 index 00000000..95691c1f --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/BasicCertificateChainCleaner.kt @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.tls + +import java.security.GeneralSecurityException +import java.security.cert.Certificate +import java.security.cert.X509Certificate +import java.util.ArrayDeque +import java.util.Deque +import javax.net.ssl.SSLPeerUnverifiedException + +/** + * A certificate chain cleaner that uses a set of trusted root certificates to build the trusted + * chain. This class duplicates the clean chain building performed during the TLS handshake. We + * prefer other mechanisms where they exist, such as with + * [okhttp3.internal.platform.AndroidPlatform.AndroidCertificateChainCleaner]. + * + * This class includes code from [Conscrypt's][Conscrypt] [TrustManagerImpl] and + * [TrustedCertificateIndex]. + * + * [Conscrypt]: https://conscrypt.org/ + */ +class BasicCertificateChainCleaner( + private val trustRootIndex: TrustRootIndex +) : CertificateChainCleaner() { + + /** + * Returns a cleaned chain for [chain]. + * + * This method throws if the complete chain to a trusted CA certificate cannot be constructed. + * This is unexpected unless the trust root index in this class has a different trust manager than + * what was used to establish [chain]. + */ + @Throws(SSLPeerUnverifiedException::class) + override fun clean(chain: List, hostname: String): List { + val queue: Deque = ArrayDeque(chain) + val result = mutableListOf() + result.add(queue.removeFirst()) + var foundTrustedCertificate = false + + followIssuerChain@ + for (c in 0 until MAX_SIGNERS) { + val toVerify = result[result.size - 1] as X509Certificate + + // If this cert has been signed by a trusted cert, use that. Add the trusted certificate to + // the end of the chain unless it's already present. (That would happen if the first + // certificate in the chain is itself a self-signed and trusted CA certificate.) + val trustedCert = trustRootIndex.findByIssuerAndSignature(toVerify) + if (trustedCert != null) { + if (result.size > 1 || toVerify != trustedCert) { + result.add(trustedCert) + } + if (verifySignature(trustedCert, trustedCert, result.size - 2)) { + return result // The self-signed cert is a root CA. We're done. + } + foundTrustedCertificate = true + continue + } + + // Search for the certificate in the chain that signed this certificate. This is typically + // the next element in the chain, but it could be any element. + val i = queue.iterator() + while (i.hasNext()) { + val signingCert = i.next() as X509Certificate + if (verifySignature(toVerify, signingCert, result.size - 1)) { + i.remove() + result.add(signingCert) + continue@followIssuerChain + } + } + + // We've reached the end of the chain. If any cert in the chain is trusted, we're done. + if (foundTrustedCertificate) { + return result + } + + // The last link isn't trusted. Fail. + throw SSLPeerUnverifiedException( + "Failed to find a trusted cert that signed $toVerify" + ) + } + + throw SSLPeerUnverifiedException("Certificate chain too long: $result") + } + + /** + * Returns true if [toVerify] was signed by [signingCert]'s public key. + * + * @param minIntermediates the minimum number of intermediate certificates in [signingCert]. This + * is -1 if signing cert is a lone self-signed certificate. + */ + private fun verifySignature( + toVerify: X509Certificate, + signingCert: X509Certificate, + minIntermediates: Int + ): Boolean { + if (toVerify.issuerDN != signingCert.subjectDN) { + return false + } + if (signingCert.basicConstraints < minIntermediates) { + return false // The signer can't have this many intermediates beneath it. + } + return try { + toVerify.verify(signingCert.publicKey) + true + } catch (verifyFailed: GeneralSecurityException) { + false + } + } + + override fun hashCode(): Int { + return trustRootIndex.hashCode() + } + + override fun equals(other: Any?): Boolean { + return if (other === this) { + true + } else { + other is BasicCertificateChainCleaner && other.trustRootIndex == trustRootIndex + } + } + + companion object { + private const val MAX_SIGNERS = 9 + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/BasicTrustRootIndex.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/BasicTrustRootIndex.kt new file mode 100644 index 00000000..138ae185 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/BasicTrustRootIndex.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.tls + +import java.security.cert.X509Certificate +import javax.security.auth.x500.X500Principal + +/** A simple index that of trusted root certificates that have been loaded into memory. */ +class BasicTrustRootIndex(vararg caCerts: X509Certificate) : TrustRootIndex { + private val subjectToCaCerts: Map> + + init { + val map = mutableMapOf>() + for (caCert in caCerts) { + map.getOrPut(caCert.subjectX500Principal) { mutableSetOf() }.add(caCert) + } + this.subjectToCaCerts = map + } + + override fun findByIssuerAndSignature(cert: X509Certificate): X509Certificate? { + val issuer = cert.issuerX500Principal + val subjectCaCerts = subjectToCaCerts[issuer] ?: return null + + return subjectCaCerts.firstOrNull { + try { + cert.verify(it.publicKey) + return@firstOrNull true + } catch (_: Exception) { + return@firstOrNull false + } + } + } + + override fun equals(other: Any?): Boolean { + return other === this || + (other is BasicTrustRootIndex && other.subjectToCaCerts == subjectToCaCerts) + } + + override fun hashCode(): Int { + return subjectToCaCerts.hashCode() + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/CertificateChainCleaner.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/CertificateChainCleaner.kt new file mode 100644 index 00000000..68a8491e --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/CertificateChainCleaner.kt @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.tls + +import java.security.cert.Certificate +import java.security.cert.X509Certificate +import javax.net.ssl.SSLPeerUnverifiedException +import javax.net.ssl.X509TrustManager +import org.eclipse.paho.client.mqttv3.internal.platform.Platform + +/** + * Computes the effective certificate chain from the raw array returned by Java's built in TLS APIs. + * Cleaning a chain returns a list of certificates where the first element is `chain[0]`, each + * certificate is signed by the certificate that follows, and the last certificate is a trusted CA + * certificate. + * + * Use of the chain cleaner is necessary to omit unexpected certificates that aren't relevant to + * the TLS handshake and to extract the trusted CA certificate for the benefit of certificate + * pinning. + */ +abstract class CertificateChainCleaner { + + @Throws(SSLPeerUnverifiedException::class) + abstract fun clean(chain: List, hostname: String): List + + companion object { + fun get(trustManager: X509TrustManager): CertificateChainCleaner { + return Platform.get().buildCertificateChainCleaner(trustManager) + } + + fun get(vararg caCerts: X509Certificate): CertificateChainCleaner { + return BasicCertificateChainCleaner(BasicTrustRootIndex(*caCerts)) + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/CipherSuite.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/CipherSuite.kt new file mode 100644 index 00000000..03d319f5 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/CipherSuite.kt @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2014 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.tls + +/** + * [TLS cipher suites][iana_tls_parameters]. + * + * **Not all cipher suites are supported on all platforms.** As newer cipher suites are created (for + * stronger privacy, better performance, etc.) they will be adopted by the platform and then exposed + * here. Cipher suites that are not available on either Android (through API level 24) or Java + * (through JDK 9) are omitted for brevity. + * + * See [Android SSLEngine][sslengine] which lists the cipher suites supported by Android. + * + * See [JDK Providers][oracle_providers] which lists the cipher suites supported by Oracle. + * + * See [NativeCrypto.java][conscrypt_providers] which lists the cipher suites supported by + * Conscrypt. + * + * [iana_tls_parameters]: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml + * [sslengine]: https://developer.android.com/reference/javax/net/ssl/SSLEngine.html + * [oracle_providers]: https://docs.oracle.com/javase/10/security/oracle-providers.htm + * [conscrypt_providers]: https://github.com/google/conscrypt/blob/master/common/src/main/java/org/conscrypt/NativeCrypto.java + */ +class CipherSuite private constructor( + /** + * Returns the Java name of this cipher suite. For some older cipher suites the Java name has the + * prefix `SSL_`, causing the Java name to be different from the instance name which is always + * prefixed `TLS_`. For example, `TLS_RSA_EXPORT_WITH_RC4_40_MD5.javaName()` is + * `"SSL_RSA_EXPORT_WITH_RC4_40_MD5"`. + */ + @get:JvmName("javaName") val javaName: String +) { + @JvmName("-deprecated_javaName") + @Deprecated( + message = "moved to val", + replaceWith = ReplaceWith(expression = "javaName"), + level = DeprecationLevel.ERROR + ) + fun javaName(): String = javaName + + override fun toString(): String = javaName + + companion object { + /** + * Compares cipher suites names like "TLS_RSA_WITH_NULL_MD5" and "SSL_RSA_WITH_NULL_MD5", + * ignoring the "TLS_" or "SSL_" prefix which is not consistent across platforms. In particular + * some IBM JVMs use the "SSL_" prefix everywhere whereas Oracle JVMs mix "TLS_" and "SSL_". + */ + internal val ORDER_BY_NAME = object : Comparator { + override fun compare(a: String, b: String): Int { + var i = 4 + val limit = minOf(a.length, b.length) + while (i < limit) { + val charA = a[i] + val charB = b[i] + if (charA != charB) return if (charA < charB) -1 else 1 + i++ + } + val lengthA = a.length + val lengthB = b.length + if (lengthA != lengthB) return if (lengthA < lengthB) -1 else 1 + return 0 + } + } + + /** + * Holds interned instances. This needs to be above the init() calls below so that it's + * initialized by the time those parts of `()` run. Guarded by CipherSuite.class. + */ + private val INSTANCES = mutableMapOf() + + // Last updated 2016-07-03 using cipher suites from Android 24 and Java 9. + + // @JvmField val TLS_NULL_WITH_NULL_NULL = init("TLS_NULL_WITH_NULL_NULL", 0x0000) + @JvmField val TLS_RSA_WITH_NULL_MD5 = init("SSL_RSA_WITH_NULL_MD5", 0x0001) + + @JvmField val TLS_RSA_WITH_NULL_SHA = init("SSL_RSA_WITH_NULL_SHA", 0x0002) + + @JvmField val TLS_RSA_EXPORT_WITH_RC4_40_MD5 = + init("SSL_RSA_EXPORT_WITH_RC4_40_MD5", 0x0003) + + @JvmField val TLS_RSA_WITH_RC4_128_MD5 = init("SSL_RSA_WITH_RC4_128_MD5", 0x0004) + + @JvmField val TLS_RSA_WITH_RC4_128_SHA = init("SSL_RSA_WITH_RC4_128_SHA", 0x0005) + + // @JvmField val TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = init("SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5", 0x0006) + // @JvmField val TLS_RSA_WITH_IDEA_CBC_SHA = init("TLS_RSA_WITH_IDEA_CBC_SHA", 0x0007) + @JvmField val TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = + init("SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0008) + + @JvmField val TLS_RSA_WITH_DES_CBC_SHA = init("SSL_RSA_WITH_DES_CBC_SHA", 0x0009) + + @JvmField val TLS_RSA_WITH_3DES_EDE_CBC_SHA = init("SSL_RSA_WITH_3DES_EDE_CBC_SHA", 0x000a) + + // @JvmField val TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = init("SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x000b) + // @JvmField val TLS_DH_DSS_WITH_DES_CBC_SHA = init("TLS_DH_DSS_WITH_DES_CBC_SHA", 0x000c) + // @JvmField val TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = init("TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", 0x000d) + // @JvmField val TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = init("SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x000e) + // @JvmField val TLS_DH_RSA_WITH_DES_CBC_SHA = init("TLS_DH_RSA_WITH_DES_CBC_SHA", 0x000f) + // @JvmField val TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = init("TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", 0x0010) + @JvmField val TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = + init("SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", 0x0011) + + @JvmField val TLS_DHE_DSS_WITH_DES_CBC_SHA = init("SSL_DHE_DSS_WITH_DES_CBC_SHA", 0x0012) + + @JvmField val TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = + init("SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", 0x0013) + + @JvmField val TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = + init("SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", 0x0014) + + @JvmField val TLS_DHE_RSA_WITH_DES_CBC_SHA = init("SSL_DHE_RSA_WITH_DES_CBC_SHA", 0x0015) + + @JvmField val TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = + init("SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", 0x0016) + + @JvmField val TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = + init("SSL_DH_anon_EXPORT_WITH_RC4_40_MD5", 0x0017) + + @JvmField val TLS_DH_anon_WITH_RC4_128_MD5 = init("SSL_DH_anon_WITH_RC4_128_MD5", 0x0018) + + @JvmField val TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = + init("SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA", 0x0019) + + @JvmField val TLS_DH_anon_WITH_DES_CBC_SHA = init("SSL_DH_anon_WITH_DES_CBC_SHA", 0x001a) + + @JvmField val TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = + init("SSL_DH_anon_WITH_3DES_EDE_CBC_SHA", 0x001b) + + @JvmField val TLS_KRB5_WITH_DES_CBC_SHA = init("TLS_KRB5_WITH_DES_CBC_SHA", 0x001e) + + @JvmField val TLS_KRB5_WITH_3DES_EDE_CBC_SHA = + init("TLS_KRB5_WITH_3DES_EDE_CBC_SHA", 0x001f) + + @JvmField val TLS_KRB5_WITH_RC4_128_SHA = init("TLS_KRB5_WITH_RC4_128_SHA", 0x0020) + + // @JvmField val TLS_KRB5_WITH_IDEA_CBC_SHA = init("TLS_KRB5_WITH_IDEA_CBC_SHA", 0x0021) + @JvmField val TLS_KRB5_WITH_DES_CBC_MD5 = init("TLS_KRB5_WITH_DES_CBC_MD5", 0x0022) + + @JvmField val TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = + init("TLS_KRB5_WITH_3DES_EDE_CBC_MD5", 0x0023) + + @JvmField val TLS_KRB5_WITH_RC4_128_MD5 = init("TLS_KRB5_WITH_RC4_128_MD5", 0x0024) + + // @JvmField val TLS_KRB5_WITH_IDEA_CBC_MD5 = init("TLS_KRB5_WITH_IDEA_CBC_MD5", 0x0025) + @JvmField val TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = + init("TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", 0x0026) + + // @JvmField val TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = init("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", 0x0027) + @JvmField val TLS_KRB5_EXPORT_WITH_RC4_40_SHA = + init("TLS_KRB5_EXPORT_WITH_RC4_40_SHA", 0x0028) + + @JvmField val TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = + init("TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", 0x0029) + + // @JvmField val TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = init("TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", 0x002a) + @JvmField val TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = + init("TLS_KRB5_EXPORT_WITH_RC4_40_MD5", 0x002b) + + // @JvmField val TLS_PSK_WITH_NULL_SHA = init("TLS_PSK_WITH_NULL_SHA", 0x002c) + // @JvmField val TLS_DHE_PSK_WITH_NULL_SHA = init("TLS_DHE_PSK_WITH_NULL_SHA", 0x002d) + // @JvmField val TLS_RSA_PSK_WITH_NULL_SHA = init("TLS_RSA_PSK_WITH_NULL_SHA", 0x002e) + @JvmField val TLS_RSA_WITH_AES_128_CBC_SHA = init("TLS_RSA_WITH_AES_128_CBC_SHA", 0x002f) + + // @JvmField val TLS_DH_DSS_WITH_AES_128_CBC_SHA = init("TLS_DH_DSS_WITH_AES_128_CBC_SHA", 0x0030) + // @JvmField val TLS_DH_RSA_WITH_AES_128_CBC_SHA = init("TLS_DH_RSA_WITH_AES_128_CBC_SHA", 0x0031) + @JvmField val TLS_DHE_DSS_WITH_AES_128_CBC_SHA = + init("TLS_DHE_DSS_WITH_AES_128_CBC_SHA", 0x0032) + + @JvmField val TLS_DHE_RSA_WITH_AES_128_CBC_SHA = + init("TLS_DHE_RSA_WITH_AES_128_CBC_SHA", 0x0033) + + @JvmField val TLS_DH_anon_WITH_AES_128_CBC_SHA = + init("TLS_DH_anon_WITH_AES_128_CBC_SHA", 0x0034) + + @JvmField val TLS_RSA_WITH_AES_256_CBC_SHA = init("TLS_RSA_WITH_AES_256_CBC_SHA", 0x0035) + + // @JvmField val TLS_DH_DSS_WITH_AES_256_CBC_SHA = init("TLS_DH_DSS_WITH_AES_256_CBC_SHA", 0x0036) + // @JvmField val TLS_DH_RSA_WITH_AES_256_CBC_SHA = init("TLS_DH_RSA_WITH_AES_256_CBC_SHA", 0x0037) + @JvmField val TLS_DHE_DSS_WITH_AES_256_CBC_SHA = + init("TLS_DHE_DSS_WITH_AES_256_CBC_SHA", 0x0038) + + @JvmField val TLS_DHE_RSA_WITH_AES_256_CBC_SHA = + init("TLS_DHE_RSA_WITH_AES_256_CBC_SHA", 0x0039) + + @JvmField val TLS_DH_anon_WITH_AES_256_CBC_SHA = + init("TLS_DH_anon_WITH_AES_256_CBC_SHA", 0x003a) + + @JvmField val TLS_RSA_WITH_NULL_SHA256 = init("TLS_RSA_WITH_NULL_SHA256", 0x003b) + + @JvmField val TLS_RSA_WITH_AES_128_CBC_SHA256 = + init("TLS_RSA_WITH_AES_128_CBC_SHA256", 0x003c) + + @JvmField val TLS_RSA_WITH_AES_256_CBC_SHA256 = + init("TLS_RSA_WITH_AES_256_CBC_SHA256", 0x003d) + + // @JvmField val TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = init("TLS_DH_DSS_WITH_AES_128_CBC_SHA256", 0x003e) + // @JvmField val TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = init("TLS_DH_RSA_WITH_AES_128_CBC_SHA256", 0x003f) + @JvmField val TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = + init("TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", 0x0040) + + @JvmField val TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = + init("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0041) + + // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = init("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", 0x0042) + // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = init("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0043) + @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = + init("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", 0x0044) + + @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = + init("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", 0x0045) + + // @JvmField val TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = init("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA", 0x0046) + @JvmField val TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = + init("TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", 0x0067) + + // @JvmField val TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = init("TLS_DH_DSS_WITH_AES_256_CBC_SHA256", 0x0068) + // @JvmField val TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = init("TLS_DH_RSA_WITH_AES_256_CBC_SHA256", 0x0069) + @JvmField val TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = + init("TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", 0x006a) + + @JvmField val TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = + init("TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", 0x006b) + + @JvmField val TLS_DH_anon_WITH_AES_128_CBC_SHA256 = + init("TLS_DH_anon_WITH_AES_128_CBC_SHA256", 0x006c) + + @JvmField val TLS_DH_anon_WITH_AES_256_CBC_SHA256 = + init("TLS_DH_anon_WITH_AES_256_CBC_SHA256", 0x006d) + + @JvmField val TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = + init("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0084) + + // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = init("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", 0x0085) + // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = init("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0086) + @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = + init("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", 0x0087) + + @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = + init("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", 0x0088) + + // @JvmField val TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = init("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", 0x0089) + @JvmField val TLS_PSK_WITH_RC4_128_SHA = init("TLS_PSK_WITH_RC4_128_SHA", 0x008a) + + @JvmField val TLS_PSK_WITH_3DES_EDE_CBC_SHA = init("TLS_PSK_WITH_3DES_EDE_CBC_SHA", 0x008b) + + @JvmField val TLS_PSK_WITH_AES_128_CBC_SHA = init("TLS_PSK_WITH_AES_128_CBC_SHA", 0x008c) + + @JvmField val TLS_PSK_WITH_AES_256_CBC_SHA = init("TLS_PSK_WITH_AES_256_CBC_SHA", 0x008d) + + // @JvmField val TLS_DHE_PSK_WITH_RC4_128_SHA = init("TLS_DHE_PSK_WITH_RC4_128_SHA", 0x008e) + // @JvmField val TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = init("TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", 0x008f) + // @JvmField val TLS_DHE_PSK_WITH_AES_128_CBC_SHA = init("TLS_DHE_PSK_WITH_AES_128_CBC_SHA", 0x0090) + // @JvmField val TLS_DHE_PSK_WITH_AES_256_CBC_SHA = init("TLS_DHE_PSK_WITH_AES_256_CBC_SHA", 0x0091) + // @JvmField val TLS_RSA_PSK_WITH_RC4_128_SHA = init("TLS_RSA_PSK_WITH_RC4_128_SHA", 0x0092) + // @JvmField val TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = init("TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", 0x0093) + // @JvmField val TLS_RSA_PSK_WITH_AES_128_CBC_SHA = init("TLS_RSA_PSK_WITH_AES_128_CBC_SHA", 0x0094) + // @JvmField val TLS_RSA_PSK_WITH_AES_256_CBC_SHA = init("TLS_RSA_PSK_WITH_AES_256_CBC_SHA", 0x0095) + @JvmField val TLS_RSA_WITH_SEED_CBC_SHA = init("TLS_RSA_WITH_SEED_CBC_SHA", 0x0096) + + // @JvmField val TLS_DH_DSS_WITH_SEED_CBC_SHA = init("TLS_DH_DSS_WITH_SEED_CBC_SHA", 0x0097) + // @JvmField val TLS_DH_RSA_WITH_SEED_CBC_SHA = init("TLS_DH_RSA_WITH_SEED_CBC_SHA", 0x0098) + // @JvmField val TLS_DHE_DSS_WITH_SEED_CBC_SHA = init("TLS_DHE_DSS_WITH_SEED_CBC_SHA", 0x0099) + // @JvmField val TLS_DHE_RSA_WITH_SEED_CBC_SHA = init("TLS_DHE_RSA_WITH_SEED_CBC_SHA", 0x009a) + // @JvmField val TLS_DH_anon_WITH_SEED_CBC_SHA = init("TLS_DH_anon_WITH_SEED_CBC_SHA", 0x009b) + @JvmField val TLS_RSA_WITH_AES_128_GCM_SHA256 = + init("TLS_RSA_WITH_AES_128_GCM_SHA256", 0x009c) + + @JvmField val TLS_RSA_WITH_AES_256_GCM_SHA384 = + init("TLS_RSA_WITH_AES_256_GCM_SHA384", 0x009d) + + @JvmField val TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = + init("TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", 0x009e) + + @JvmField val TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = + init("TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", 0x009f) + + // @JvmField val TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = init("TLS_DH_RSA_WITH_AES_128_GCM_SHA256", 0x00a0) + // @JvmField val TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = init("TLS_DH_RSA_WITH_AES_256_GCM_SHA384", 0x00a1) + @JvmField val TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = + init("TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", 0x00a2) + + @JvmField val TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = + init("TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", 0x00a3) + + // @JvmField val TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = init("TLS_DH_DSS_WITH_AES_128_GCM_SHA256", 0x00a4) + // @JvmField val TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = init("TLS_DH_DSS_WITH_AES_256_GCM_SHA384", 0x00a5) + @JvmField val TLS_DH_anon_WITH_AES_128_GCM_SHA256 = + init("TLS_DH_anon_WITH_AES_128_GCM_SHA256", 0x00a6) + + @JvmField val TLS_DH_anon_WITH_AES_256_GCM_SHA384 = + init("TLS_DH_anon_WITH_AES_256_GCM_SHA384", 0x00a7) + + // @JvmField val TLS_PSK_WITH_AES_128_GCM_SHA256 = init("TLS_PSK_WITH_AES_128_GCM_SHA256", 0x00a8) + // @JvmField val TLS_PSK_WITH_AES_256_GCM_SHA384 = init("TLS_PSK_WITH_AES_256_GCM_SHA384", 0x00a9) + // @JvmField val TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = init("TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", 0x00aa) + // @JvmField val TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = init("TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", 0x00ab) + // @JvmField val TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = init("TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", 0x00ac) + // @JvmField val TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = init("TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", 0x00ad) + // @JvmField val TLS_PSK_WITH_AES_128_CBC_SHA256 = init("TLS_PSK_WITH_AES_128_CBC_SHA256", 0x00ae) + // @JvmField val TLS_PSK_WITH_AES_256_CBC_SHA384 = init("TLS_PSK_WITH_AES_256_CBC_SHA384", 0x00af) + // @JvmField val TLS_PSK_WITH_NULL_SHA256 = init("TLS_PSK_WITH_NULL_SHA256", 0x00b0) + // @JvmField val TLS_PSK_WITH_NULL_SHA384 = init("TLS_PSK_WITH_NULL_SHA384", 0x00b1) + // @JvmField val TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = init("TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", 0x00b2) + // @JvmField val TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = init("TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", 0x00b3) + // @JvmField val TLS_DHE_PSK_WITH_NULL_SHA256 = init("TLS_DHE_PSK_WITH_NULL_SHA256", 0x00b4) + // @JvmField val TLS_DHE_PSK_WITH_NULL_SHA384 = init("TLS_DHE_PSK_WITH_NULL_SHA384", 0x00b5) + // @JvmField val TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = init("TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", 0x00b6) + // @JvmField val TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = init("TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", 0x00b7) + // @JvmField val TLS_RSA_PSK_WITH_NULL_SHA256 = init("TLS_RSA_PSK_WITH_NULL_SHA256", 0x00b8) + // @JvmField val TLS_RSA_PSK_WITH_NULL_SHA384 = init("TLS_RSA_PSK_WITH_NULL_SHA384", 0x00b9) + // @JvmField val TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00ba) + // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", 0x00bb) + // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00bc) + // @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", 0x00bd) + // @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0x00be) + // @JvmField val TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", 0x00bf) + // @JvmField val TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = init("TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c0) + // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = init("TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", 0x00c1) + // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = init("TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c2) + // @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = init("TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", 0x00c3) + // @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = init("TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", 0x00c4) + // @JvmField val TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = init("TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256", 0x00c5) + @JvmField val TLS_EMPTY_RENEGOTIATION_INFO_SCSV = + init("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", 0x00ff) + + @JvmField val TLS_FALLBACK_SCSV = init("TLS_FALLBACK_SCSV", 0x5600) + + @JvmField val TLS_ECDH_ECDSA_WITH_NULL_SHA = init("TLS_ECDH_ECDSA_WITH_NULL_SHA", 0xc001) + + @JvmField val TLS_ECDH_ECDSA_WITH_RC4_128_SHA = + init("TLS_ECDH_ECDSA_WITH_RC4_128_SHA", 0xc002) + + @JvmField val TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = + init("TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", 0xc003) + + @JvmField val TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = + init("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", 0xc004) + + @JvmField val TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = + init("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", 0xc005) + + @JvmField val TLS_ECDHE_ECDSA_WITH_NULL_SHA = init("TLS_ECDHE_ECDSA_WITH_NULL_SHA", 0xc006) + + @JvmField val TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = + init("TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", 0xc007) + + @JvmField val TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = + init("TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", 0xc008) + + @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = + init("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", 0xc009) + + @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = + init("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", 0xc00a) + + @JvmField val TLS_ECDH_RSA_WITH_NULL_SHA = init("TLS_ECDH_RSA_WITH_NULL_SHA", 0xc00b) + + @JvmField val TLS_ECDH_RSA_WITH_RC4_128_SHA = init("TLS_ECDH_RSA_WITH_RC4_128_SHA", 0xc00c) + + @JvmField val TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = + init("TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", 0xc00d) + + @JvmField val TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = + init("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", 0xc00e) + + @JvmField val TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = + init("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", 0xc00f) + + @JvmField val TLS_ECDHE_RSA_WITH_NULL_SHA = init("TLS_ECDHE_RSA_WITH_NULL_SHA", 0xc010) + + @JvmField val TLS_ECDHE_RSA_WITH_RC4_128_SHA = + init("TLS_ECDHE_RSA_WITH_RC4_128_SHA", 0xc011) + + @JvmField val TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = + init("TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", 0xc012) + + @JvmField val TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = + init("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", 0xc013) + + @JvmField val TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = + init("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", 0xc014) + + @JvmField val TLS_ECDH_anon_WITH_NULL_SHA = init("TLS_ECDH_anon_WITH_NULL_SHA", 0xc015) + + @JvmField val TLS_ECDH_anon_WITH_RC4_128_SHA = + init("TLS_ECDH_anon_WITH_RC4_128_SHA", 0xc016) + + @JvmField val TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = + init("TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", 0xc017) + + @JvmField val TLS_ECDH_anon_WITH_AES_128_CBC_SHA = + init("TLS_ECDH_anon_WITH_AES_128_CBC_SHA", 0xc018) + + @JvmField val TLS_ECDH_anon_WITH_AES_256_CBC_SHA = + init("TLS_ECDH_anon_WITH_AES_256_CBC_SHA", 0xc019) + + // @JvmField val TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = init("TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", 0xc01a) + // @JvmField val TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = init("TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", 0xc01b) + // @JvmField val TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = init("TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", 0xc01c) + // @JvmField val TLS_SRP_SHA_WITH_AES_128_CBC_SHA = init("TLS_SRP_SHA_WITH_AES_128_CBC_SHA", 0xc01d) + // @JvmField val TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = init("TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", 0xc01e) + // @JvmField val TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = init("TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", 0xc01f) + // @JvmField val TLS_SRP_SHA_WITH_AES_256_CBC_SHA = init("TLS_SRP_SHA_WITH_AES_256_CBC_SHA", 0xc020) + // @JvmField val TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = init("TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", 0xc021) + // @JvmField val TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = init("TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", 0xc022) + @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = + init("TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", 0xc023) + + @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = + init("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", 0xc024) + + @JvmField val TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = + init("TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", 0xc025) + + @JvmField val TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = + init("TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", 0xc026) + + @JvmField val TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = + init("TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", 0xc027) + + @JvmField val TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = + init("TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", 0xc028) + + @JvmField val TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = + init("TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", 0xc029) + + @JvmField val TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = + init("TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", 0xc02a) + + @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = + init("TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", 0xc02b) + + @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = + init("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", 0xc02c) + + @JvmField val TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = + init("TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", 0xc02d) + + @JvmField val TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = + init("TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", 0xc02e) + + @JvmField val TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = + init("TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 0xc02f) + + @JvmField val TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = + init("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", 0xc030) + + @JvmField val TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = + init("TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", 0xc031) + + @JvmField val TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = + init("TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", 0xc032) + + // @JvmField val TLS_ECDHE_PSK_WITH_RC4_128_SHA = init("TLS_ECDHE_PSK_WITH_RC4_128_SHA", 0xc033) + // @JvmField val TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = init("TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", 0xc034) + @JvmField val TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = + init("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", 0xc035) + + @JvmField val TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = + init("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", 0xc036) + + // @JvmField val TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = init("TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", 0xc037) + // @JvmField val TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = init("TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", 0xc038) + // @JvmField val TLS_ECDHE_PSK_WITH_NULL_SHA = init("TLS_ECDHE_PSK_WITH_NULL_SHA", 0xc039) + // @JvmField val TLS_ECDHE_PSK_WITH_NULL_SHA256 = init("TLS_ECDHE_PSK_WITH_NULL_SHA256", 0xc03a) + // @JvmField val TLS_ECDHE_PSK_WITH_NULL_SHA384 = init("TLS_ECDHE_PSK_WITH_NULL_SHA384", 0xc03b) + // @JvmField val TLS_RSA_WITH_ARIA_128_CBC_SHA256 = init("TLS_RSA_WITH_ARIA_128_CBC_SHA256", 0xc03c) + // @JvmField val TLS_RSA_WITH_ARIA_256_CBC_SHA384 = init("TLS_RSA_WITH_ARIA_256_CBC_SHA384", 0xc03d) + // @JvmField val TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 = init("TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256", 0xc03e) + // @JvmField val TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 = init("TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384", 0xc03f) + // @JvmField val TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 = init("TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256", 0xc040) + // @JvmField val TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 = init("TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384", 0xc041) + // @JvmField val TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 = init("TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256", 0xc042) + // @JvmField val TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 = init("TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384", 0xc043) + // @JvmField val TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 = init("TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256", 0xc044) + // @JvmField val TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 = init("TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384", 0xc045) + // @JvmField val TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 = init("TLS_DH_anon_WITH_ARIA_128_CBC_SHA256", 0xc046) + // @JvmField val TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 = init("TLS_DH_anon_WITH_ARIA_256_CBC_SHA384", 0xc047) + // @JvmField val TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 = init("TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", 0xc048) + // @JvmField val TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 = init("TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", 0xc049) + // @JvmField val TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 = init("TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256", 0xc04a) + // @JvmField val TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 = init("TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384", 0xc04b) + // @JvmField val TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 = init("TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", 0xc04c) + // @JvmField val TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 = init("TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", 0xc04d) + // @JvmField val TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 = init("TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256", 0xc04e) + // @JvmField val TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 = init("TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384", 0xc04f) + // @JvmField val TLS_RSA_WITH_ARIA_128_GCM_SHA256 = init("TLS_RSA_WITH_ARIA_128_GCM_SHA256", 0xc050) + // @JvmField val TLS_RSA_WITH_ARIA_256_GCM_SHA384 = init("TLS_RSA_WITH_ARIA_256_GCM_SHA384", 0xc051) + // @JvmField val TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 = init("TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256", 0xc052) + // @JvmField val TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 = init("TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384", 0xc053) + // @JvmField val TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 = init("TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256", 0xc054) + // @JvmField val TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 = init("TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384", 0xc055) + // @JvmField val TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 = init("TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256", 0xc056) + // @JvmField val TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 = init("TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384", 0xc057) + // @JvmField val TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 = init("TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256", 0xc058) + // @JvmField val TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 = init("TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384", 0xc059) + // @JvmField val TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 = init("TLS_DH_anon_WITH_ARIA_128_GCM_SHA256", 0xc05a) + // @JvmField val TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 = init("TLS_DH_anon_WITH_ARIA_256_GCM_SHA384", 0xc05b) + // @JvmField val TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 = init("TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256", 0xc05c) + // @JvmField val TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 = init("TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", 0xc05d) + // @JvmField val TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 = init("TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256", 0xc05e) + // @JvmField val TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 = init("TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384", 0xc05f) + // @JvmField val TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 = init("TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", 0xc060) + // @JvmField val TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 = init("TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", 0xc061) + // @JvmField val TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 = init("TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256", 0xc062) + // @JvmField val TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 = init("TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384", 0xc063) + // @JvmField val TLS_PSK_WITH_ARIA_128_CBC_SHA256 = init("TLS_PSK_WITH_ARIA_128_CBC_SHA256", 0xc064) + // @JvmField val TLS_PSK_WITH_ARIA_256_CBC_SHA384 = init("TLS_PSK_WITH_ARIA_256_CBC_SHA384", 0xc065) + // @JvmField val TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 = init("TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256", 0xc066) + // @JvmField val TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 = init("TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384", 0xc067) + // @JvmField val TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 = init("TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256", 0xc068) + // @JvmField val TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 = init("TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384", 0xc069) + // @JvmField val TLS_PSK_WITH_ARIA_128_GCM_SHA256 = init("TLS_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06a) + // @JvmField val TLS_PSK_WITH_ARIA_256_GCM_SHA384 = init("TLS_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06b) + // @JvmField val TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 = init("TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06c) + // @JvmField val TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 = init("TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06d) + // @JvmField val TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 = init("TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256", 0xc06e) + // @JvmField val TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 = init("TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384", 0xc06f) + // @JvmField val TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 = init("TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256", 0xc070) + // @JvmField val TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 = init("TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384", 0xc071) + // @JvmField val TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc072) + // @JvmField val TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = init("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc073) + // @JvmField val TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc074) + // @JvmField val TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = init("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc075) + // @JvmField val TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc076) + // @JvmField val TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 = init("TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc077) + // @JvmField val TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256", 0xc078) + // @JvmField val TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 = init("TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384", 0xc079) + // @JvmField val TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07a) + // @JvmField val TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07b) + // @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07c) + // @JvmField val TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07d) + // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc07e) + // @JvmField val TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc07f) + // @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256", 0xc080) + // @JvmField val TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384", 0xc081) + // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256", 0xc082) + // @JvmField val TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384", 0xc083) + // @JvmField val TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256", 0xc084) + // @JvmField val TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384", 0xc085) + // @JvmField val TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc086) + // @JvmField val TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc087) + // @JvmField val TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc088) + // @JvmField val TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc089) + // @JvmField val TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc08a) + // @JvmField val TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc08b) + // @JvmField val TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", 0xc08c) + // @JvmField val TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384", 0xc08d) + // @JvmField val TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc08e) + // @JvmField val TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc08f) + // @JvmField val TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc090) + // @JvmField val TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc091) + // @JvmField val TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 = init("TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256", 0xc092) + // @JvmField val TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 = init("TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384", 0xc093) + // @JvmField val TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc094) + // @JvmField val TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 = init("TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc095) + // @JvmField val TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc096) + // @JvmField val TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = init("TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc097) + // @JvmField val TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc098) + // @JvmField val TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 = init("TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc099) + // @JvmField val TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = init("TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256", 0xc09a) + // @JvmField val TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = init("TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384", 0xc09b) + // @JvmField val TLS_RSA_WITH_AES_128_CCM = init("TLS_RSA_WITH_AES_128_CCM", 0xc09c) + // @JvmField val TLS_RSA_WITH_AES_256_CCM = init("TLS_RSA_WITH_AES_256_CCM", 0xc09d) + // @JvmField val TLS_DHE_RSA_WITH_AES_128_CCM = init("TLS_DHE_RSA_WITH_AES_128_CCM", 0xc09e) + // @JvmField val TLS_DHE_RSA_WITH_AES_256_CCM = init("TLS_DHE_RSA_WITH_AES_256_CCM", 0xc09f) + // @JvmField val TLS_RSA_WITH_AES_128_CCM_8 = init("TLS_RSA_WITH_AES_128_CCM_8", 0xc0a0) + // @JvmField val TLS_RSA_WITH_AES_256_CCM_8 = init("TLS_RSA_WITH_AES_256_CCM_8", 0xc0a1) + // @JvmField val TLS_DHE_RSA_WITH_AES_128_CCM_8 = init("TLS_DHE_RSA_WITH_AES_128_CCM_8", 0xc0a2) + // @JvmField val TLS_DHE_RSA_WITH_AES_256_CCM_8 = init("TLS_DHE_RSA_WITH_AES_256_CCM_8", 0xc0a3) + // @JvmField val TLS_PSK_WITH_AES_128_CCM = init("TLS_PSK_WITH_AES_128_CCM", 0xc0a4) + // @JvmField val TLS_PSK_WITH_AES_256_CCM = init("TLS_PSK_WITH_AES_256_CCM", 0xc0a5) + // @JvmField val TLS_DHE_PSK_WITH_AES_128_CCM = init("TLS_DHE_PSK_WITH_AES_128_CCM", 0xc0a6) + // @JvmField val TLS_DHE_PSK_WITH_AES_256_CCM = init("TLS_DHE_PSK_WITH_AES_256_CCM", 0xc0a7) + // @JvmField val TLS_PSK_WITH_AES_128_CCM_8 = init("TLS_PSK_WITH_AES_128_CCM_8", 0xc0a8) + // @JvmField val TLS_PSK_WITH_AES_256_CCM_8 = init("TLS_PSK_WITH_AES_256_CCM_8", 0xc0a9) + // @JvmField val TLS_PSK_DHE_WITH_AES_128_CCM_8 = init("TLS_PSK_DHE_WITH_AES_128_CCM_8", 0xc0aa) + // @JvmField val TLS_PSK_DHE_WITH_AES_256_CCM_8 = init("TLS_PSK_DHE_WITH_AES_256_CCM_8", 0xc0ab) + // @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_CCM = init("TLS_ECDHE_ECDSA_WITH_AES_128_CCM", 0xc0ac) + // @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_CCM = init("TLS_ECDHE_ECDSA_WITH_AES_256_CCM", 0xc0ad) + // @JvmField val TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = init("TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8", 0xc0ae) + // @JvmField val TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = init("TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8", 0xc0af) + @JvmField val TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = + init("TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 0xcca8) + + @JvmField val TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = + init("TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", 0xcca9) + + @JvmField val TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = + init("TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", 0xccaa) + + // @JvmField val TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 = init("TLS_PSK_WITH_CHACHA20_POLY1305_SHA256", 0xccab) + @JvmField val TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = + init("TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", 0xccac) + + // @JvmField val TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = init("TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", 0xccad) + // @JvmField val TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 = init("TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256", 0xccae) + + // TLS 1.3 https://tools.ietf.org/html/rfc8446 + @JvmField val TLS_AES_128_GCM_SHA256 = init("TLS_AES_128_GCM_SHA256", 0x1301) + + @JvmField val TLS_AES_256_GCM_SHA384 = init("TLS_AES_256_GCM_SHA384", 0x1302) + + @JvmField val TLS_CHACHA20_POLY1305_SHA256 = init("TLS_CHACHA20_POLY1305_SHA256", 0x1303) + + @JvmField val TLS_AES_128_CCM_SHA256 = init("TLS_AES_128_CCM_SHA256", 0x1304) + + @JvmField val TLS_AES_128_CCM_8_SHA256 = init("TLS_AES_128_CCM_8_SHA256", 0x1305) + + /** + * @param javaName the name used by Java APIs for this cipher suite. Different than the IANA + * name for older cipher suites because the prefix is `SSL_` instead of `TLS_`. + */ + @JvmStatic + @Synchronized + fun forJavaName(javaName: String): CipherSuite { + var result: CipherSuite? = INSTANCES[javaName] + if (result == null) { + result = INSTANCES[secondaryName(javaName)] + + if (result == null) { + result = CipherSuite(javaName) + } + + // Add the new cipher suite, or a confirmed alias. + INSTANCES[javaName] = result + } + return result + } + + private fun secondaryName(javaName: String): String { + return when { + javaName.startsWith("TLS_") -> "SSL_" + javaName.substring(4) + javaName.startsWith("SSL_") -> "TLS_" + javaName.substring(4) + else -> javaName + } + } + + /** + * @param javaName the name used by Java APIs for this cipher suite. Different than the IANA + * name for older cipher suites because the prefix is `SSL_` instead of `TLS_`. + * @param value the integer identifier for this cipher suite. (Documentation only.) + */ + private fun init(javaName: String, value: Int): CipherSuite { + val suite = CipherSuite(javaName) + INSTANCES[javaName] = suite + return suite + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/TlsVersion.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/TlsVersion.kt new file mode 100644 index 00000000..1b250865 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/TlsVersion.kt @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.tls + +/** + * Versions of TLS that can be offered when negotiating a secure socket. See + * [javax.net.ssl.SSLSocket.setEnabledProtocols]. + */ +enum class TlsVersion( + @get:JvmName("javaName") val javaName: String +) { + TLS_1_3("TLSv1.3"), // 2016. + TLS_1_2("TLSv1.2"), // 2008. + TLS_1_1("TLSv1.1"), // 2006. + TLS_1_0("TLSv1"), // 1999. + SSL_3_0("SSLv3"); // 1996. + + @JvmName("-deprecated_javaName") + @Deprecated( + message = "moved to val", + replaceWith = ReplaceWith(expression = "javaName"), + level = DeprecationLevel.ERROR + ) + fun javaName(): String = javaName + + companion object { + @JvmStatic + fun forJavaName(javaName: String): TlsVersion { + return when (javaName) { + "TLSv1.3" -> TLS_1_3 + "TLSv1.2" -> TLS_1_2 + "TLSv1.1" -> TLS_1_1 + "TLSv1" -> TLS_1_0 + "SSLv3" -> SSL_3_0 + else -> throw IllegalArgumentException("Unexpected TLS version: $javaName") + } + } + } +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/TrustRootIndex.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/TrustRootIndex.kt new file mode 100644 index 00000000..4f3662be --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/tls/TrustRootIndex.kt @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.eclipse.paho.client.mqttv3.internal.tls + +import java.security.cert.X509Certificate + +fun interface TrustRootIndex { + /** Returns the trusted CA certificate that signed [cert]. */ + fun findByIssuerAndSignature(cert: X509Certificate): X509Certificate? +} diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java index d8fabe06..fbeaa31c 100755 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/ExtendedByteArrayOutputStream.java @@ -9,15 +9,24 @@ class ExtendedByteArrayOutputStream extends ByteArrayOutputStream { final WebSocketNetworkModule webSocketNetworkModule; final WebSocketSecureNetworkModule webSocketSecureNetworkModule; + final WebSocketSecureNetworkModuleV2 webSocketSecureNetworkModuleV2; ExtendedByteArrayOutputStream(WebSocketNetworkModule module) { this.webSocketNetworkModule = module; this.webSocketSecureNetworkModule = null; + this.webSocketSecureNetworkModuleV2 = null; } ExtendedByteArrayOutputStream(WebSocketSecureNetworkModule module) { this.webSocketNetworkModule = null; this.webSocketSecureNetworkModule = module; + this.webSocketSecureNetworkModuleV2 = null; + } + + ExtendedByteArrayOutputStream(WebSocketSecureNetworkModuleV2 module) { + this.webSocketNetworkModule = null; + this.webSocketSecureNetworkModule = null; + this.webSocketSecureNetworkModuleV2 = module; } public void flush() throws IOException { @@ -41,6 +50,9 @@ OutputStream getSocketOutputStream() throws IOException { if(webSocketSecureNetworkModule != null){ return webSocketSecureNetworkModule.getSocketOutputStream(); } + if(webSocketSecureNetworkModuleV2 != null) { + return webSocketSecureNetworkModuleV2.getSocketOutputStream(); + } return null; } diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModuleV2.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModuleV2.java new file mode 100755 index 00000000..98e70410 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/websocket/WebSocketSecureNetworkModuleV2.java @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2009, 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * James Sutton - Bug 459142 - WebSocket support for the Java client. + */ +package org.eclipse.paho.client.mqttv3.internal.websocket; + +import org.eclipse.paho.client.mqttv3.ConnectionSpec; +import org.eclipse.paho.client.mqttv3.ILogger; +import org.eclipse.paho.client.mqttv3.IPahoEvents; +import org.eclipse.paho.client.mqttv3.MqttException; +import org.eclipse.paho.client.mqttv3.Protocol; +import org.eclipse.paho.client.mqttv3.internal.SSLNetworkModuleV2; +import org.eclipse.paho.client.mqttv3.internal.tls.CertificateChainCleaner; + +import javax.net.SocketFactory; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.X509TrustManager; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.nio.ByteBuffer; +import java.util.List; + +public class WebSocketSecureNetworkModuleV2 extends SSLNetworkModuleV2 { + + private static final String CLASS_NAME = WebSocketSecureNetworkModuleV2.class.getName(); + + private PipedInputStream pipedInputStream; + private WebSocketReceiver webSocketReceiver; + private String uri; + private String host; + private int port; + private ILogger logger; + ByteBuffer recievedPayload; + + /** + * Overrides the flush method. + * This allows us to encode the MQTT payload into a WebSocket + * Frame before passing it through to the real socket. + */ + private ByteArrayOutputStream outputStream = new ExtendedByteArrayOutputStream(this); + + public WebSocketSecureNetworkModuleV2( + SocketFactory socketFactory, + SSLSocketFactory sslSocketFactory, + X509TrustManager x509TrustManager, + ConnectionSpec connectionSpec, + List alpnProtocolList, + String uri, + String host, + int port, + String clientId, + ILogger logger, + IPahoEvents pahoEvents + ) { + super( + socketFactory, + sslSocketFactory, + x509TrustManager, + connectionSpec, + alpnProtocolList, + host, + port, + clientId, + logger, + pahoEvents + ); + this.uri = uri; + this.host = host; + this.port = port; + this.logger = logger; + this.pipedInputStream = new PipedInputStream(); + } + + public void start() throws IOException, MqttException { + super.start(); + WebSocketHandshake handshake = new WebSocketHandshake(super.getInputStream(), + super.getOutputStream(), uri, host, port); + handshake.execute(); + this.webSocketReceiver = new WebSocketReceiver(getSocketInputStream(), pipedInputStream); + webSocketReceiver.start("WssSocketReceiver"); + + } + + OutputStream getSocketOutputStream() throws IOException { + return super.getOutputStream(); + } + + InputStream getSocketInputStream() throws IOException { + return super.getInputStream(); + } + + public InputStream getInputStream() throws IOException { + return pipedInputStream; + } + + public OutputStream getOutputStream() throws IOException { + return outputStream; + } + + public void stop() throws IOException { + // Creating Close Frame + WebSocketFrame frame = new WebSocketFrame((byte) 0x08, true, "1000".getBytes()); + byte[] rawFrame = frame.encodeFrame(); + getSocketOutputStream().write(rawFrame); + getSocketOutputStream().flush(); + + if (webSocketReceiver != null) { + webSocketReceiver.stop(); + } + super.stop(); + } + + public String getServerURI() { + return "wss://" + host + ":" + port; + } + + +} diff --git a/paho/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/paho/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 00000000..ca6ee9ce --- /dev/null +++ b/paho/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file diff --git a/pingsender/timer-pingsender/build.gradle.kts b/pingsender/timer-pingsender/build.gradle.kts index f7231086..1da60e96 100644 --- a/pingsender/timer-pingsender/build.gradle.kts +++ b/pingsender/timer-pingsender/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { implementation(project(":courier-core-android")) implementation(deps.android.androidx.annotation) + testImplementation(deps.android.test.mockitoCore) testImplementation(deps.android.test.kotlinTestJunit) } diff --git a/pingsender/workmanager-2.6.0-pingsender/build.gradle.kts b/pingsender/workmanager-2.6.0-pingsender/build.gradle.kts index 8fd8b586..d1ae1f36 100644 --- a/pingsender/workmanager-2.6.0-pingsender/build.gradle.kts +++ b/pingsender/workmanager-2.6.0-pingsender/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { api(project(":mqtt-pingsender")) implementation(project(":courier-core-android")) implementation(deps.workManager.runtime_2_6_0) - + testImplementation(deps.android.test.mockitoCore) testImplementation(deps.android.test.kotlinTestJunit) } diff --git a/pingsender/workmanager-pingsender/build.gradle.kts b/pingsender/workmanager-pingsender/build.gradle.kts index f9097570..07352792 100644 --- a/pingsender/workmanager-pingsender/build.gradle.kts +++ b/pingsender/workmanager-pingsender/build.gradle.kts @@ -35,7 +35,7 @@ dependencies { api(project(":mqtt-pingsender")) implementation(project(":courier-core-android")) implementation(deps.workManager.runtime) - + testImplementation(deps.android.test.mockitoCore) testImplementation(deps.android.test.kotlinTestJunit) } From 5bee5771a3ed06bace9723c417c1a5425d213bae Mon Sep 17 00:00:00 2001 From: Anubhav Gupta Date: Wed, 28 Sep 2022 15:45:56 +0530 Subject: [PATCH 03/25] removed java 11 depedency (#45) Co-authored-by: Anubhav Gupta --- build.gradle.kts | 6 +++--- paho/build.gradle.kts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9119f06d..a3c80aa8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,12 +41,12 @@ allprojects { subprojects { tasks.withType().configureEach { kotlinOptions { - jvmTarget = "11" + jvmTarget = "1.8" } } tasks.withType().configureEach { - sourceCompatibility = JavaVersion.VERSION_11.toString() - targetCompatibility = JavaVersion.VERSION_11.toString() + sourceCompatibility = JavaVersion.VERSION_1_8.toString() + targetCompatibility = JavaVersion.VERSION_1_8.toString() } } diff --git a/paho/build.gradle.kts b/paho/build.gradle.kts index 6ef57762..cf078729 100644 --- a/paho/build.gradle.kts +++ b/paho/build.gradle.kts @@ -16,8 +16,8 @@ plugins { } java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 } dependencies { From 49c1ebbecd48cc793ee5d170a25175ba19f3c215 Mon Sep 17 00:00:00 2001 From: Juan Sebastian sanchez Date: Fri, 7 Oct 2022 04:27:24 -0500 Subject: [PATCH 04/25] Update com.google.code.gson dependecy (#47) In order to fix vulnerability reported on https://security.snyk.io/vuln/SNYK-JAVA-COMGOOGLECODEGSON-1730327 --- dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.gradle b/dependencies.gradle index 080678c4..11eecb84 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -6,6 +6,6 @@ ext { kotlinCoroutines = 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2' kotlinCoroutinesRxInterop = 'org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.3.2' - gson = 'com.google.code.gson:gson:2.8.5' + gson = 'com.google.code.gson:gson:2.8.9' okio = 'com.squareup.okio:okio:1.13.0' } \ No newline at end of file From fba09e5523cd2fa5e081cc3b5eef54d5fccb5bfd Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Fri, 28 Oct 2022 11:47:08 +0530 Subject: [PATCH 05/25] Remove empty username check (#49) --- .../src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt | 1 - .../java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt b/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt index 69592f2f..d90d7c12 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt @@ -111,7 +111,6 @@ class MqttConnectOptions private constructor( } fun userName(username: String) = apply { - require(username.isNotEmpty()) { "username cannot be empty" } this.username = username } diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java index 767a77a3..1d2e6044 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttConnectOptions.java @@ -162,7 +162,7 @@ public void setUserName(String userName) { if ((userName != null) && (userName.trim().equals(""))) { - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Username is empty"); } this.userName = userName; } From 09892e0dcf6165ca0b213b543e8c1aee1f250c5b Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Sun, 30 Oct 2022 12:32:50 +0530 Subject: [PATCH 06/25] Add topic & content-type in message adapter (#48) * Add topic & content-type in message adapter --- courier-core/api/courier-core.api | 5 ++-- .../java/com/gojek/courier/MessageAdapter.kt | 7 ++++-- .../messageadapter/gson/GsonMessageAdapter.kt | 6 +++-- .../moshi/MoshiMessageAdapter.kt | 6 +++-- .../protobuf/ProtobufMessageAdapter.kt | 6 +++-- .../gojek/courier/coordinator/Coordinator.kt | 24 ++++++++++++------- .../builtin/ByteArrayMessageAdapter.kt | 6 +++-- .../builtin/TextMessageAdapter.kt | 6 +++-- 8 files changed, 44 insertions(+), 22 deletions(-) diff --git a/courier-core/api/courier-core.api b/courier-core/api/courier-core.api index 055044f4..e4ca55c0 100644 --- a/courier-core/api/courier-core.api +++ b/courier-core/api/courier-core.api @@ -8,8 +8,9 @@ public final class com/gojek/courier/Message$Bytes : com/gojek/courier/Message { } public abstract interface class com/gojek/courier/MessageAdapter { - public abstract fun fromMessage (Lcom/gojek/courier/Message;)Ljava/lang/Object; - public abstract fun toMessage (Ljava/lang/Object;)Lcom/gojek/courier/Message; + public abstract fun contentType ()Ljava/lang/String; + public abstract fun fromMessage (Ljava/lang/String;Lcom/gojek/courier/Message;)Ljava/lang/Object; + public abstract fun toMessage (Ljava/lang/String;Ljava/lang/Object;)Lcom/gojek/courier/Message; } public abstract interface class com/gojek/courier/MessageAdapter$Factory { diff --git a/courier-core/src/main/java/com/gojek/courier/MessageAdapter.kt b/courier-core/src/main/java/com/gojek/courier/MessageAdapter.kt index 665d4d8a..3e4085a7 100644 --- a/courier-core/src/main/java/com/gojek/courier/MessageAdapter.kt +++ b/courier-core/src/main/java/com/gojek/courier/MessageAdapter.kt @@ -5,10 +5,13 @@ import java.lang.reflect.Type interface MessageAdapter { /** Returns an object of type `T` that represents a [Message]. */ - fun fromMessage(message: Message): T + fun fromMessage(topic: String, message: Message): T /** Returns a [Message] that represents [data]. */ - fun toMessage(data: T): Message + fun toMessage(topic: String, data: T): Message + + /** Returns the content type supported by this adapter. */ + fun contentType(): String /** Creates [MessageAdapter] instances based on a type and target usage. */ interface Factory { diff --git a/courier-message-adapter-gson/src/main/java/com/gojek/courier/messageadapter/gson/GsonMessageAdapter.kt b/courier-message-adapter-gson/src/main/java/com/gojek/courier/messageadapter/gson/GsonMessageAdapter.kt index 0239c442..03e27a74 100644 --- a/courier-message-adapter-gson/src/main/java/com/gojek/courier/messageadapter/gson/GsonMessageAdapter.kt +++ b/courier-message-adapter-gson/src/main/java/com/gojek/courier/messageadapter/gson/GsonMessageAdapter.kt @@ -19,7 +19,7 @@ private class GsonMessageAdapter constructor( private val typeAdapter: TypeAdapter ) : MessageAdapter { - override fun fromMessage(message: Message): T { + override fun fromMessage(topic: String, message: Message): T { val stringValue = when (message) { is Message.Bytes -> String(message.value) } @@ -27,7 +27,7 @@ private class GsonMessageAdapter constructor( return typeAdapter.read(jsonReader)!! } - override fun toMessage(data: T): Message { + override fun toMessage(topic: String, data: T): Message { val buffer = Buffer() val writer = OutputStreamWriter(buffer.outputStream(), UTF_8) val jsonWriter = gson.newJsonWriter(writer) @@ -36,6 +36,8 @@ private class GsonMessageAdapter constructor( val stringValue = buffer.readByteString().utf8() return Message.Bytes(stringValue.toByteArray()) } + + override fun contentType() = "application/json" } class GsonMessageAdapterFactory( diff --git a/courier-message-adapter-moshi/src/main/java/com/gojek/courier/messageadapter/moshi/MoshiMessageAdapter.kt b/courier-message-adapter-moshi/src/main/java/com/gojek/courier/messageadapter/moshi/MoshiMessageAdapter.kt index c6cd0945..1ba6152a 100644 --- a/courier-message-adapter-moshi/src/main/java/com/gojek/courier/messageadapter/moshi/MoshiMessageAdapter.kt +++ b/courier-message-adapter-moshi/src/main/java/com/gojek/courier/messageadapter/moshi/MoshiMessageAdapter.kt @@ -16,7 +16,7 @@ private class MoshiMessageAdapter constructor( private val jsonAdapter: JsonAdapter ) : MessageAdapter { - override fun fromMessage(message: Message): T { + override fun fromMessage(topic: String, message: Message): T { val stringValue = when (message) { is Message.Bytes -> { val byteString = ByteString.of(message.value, 0, message.value.size) @@ -32,11 +32,13 @@ private class MoshiMessageAdapter constructor( return jsonAdapter.fromJson(stringValue)!! } - override fun toMessage(data: T): Message { + override fun toMessage(topic: String, data: T): Message { val stringValue = jsonAdapter.toJson(data) return Message.Bytes(stringValue.toByteArray()) } + override fun contentType() = "application/json" + private companion object { private val UTF8_BOM = ByteString.decodeHex("EFBBBF") } diff --git a/courier-message-adapter-protobuf/src/main/java/com/gojek/courier/messageadapter/protobuf/ProtobufMessageAdapter.kt b/courier-message-adapter-protobuf/src/main/java/com/gojek/courier/messageadapter/protobuf/ProtobufMessageAdapter.kt index 3e583b19..3bf1044d 100644 --- a/courier-message-adapter-protobuf/src/main/java/com/gojek/courier/messageadapter/protobuf/ProtobufMessageAdapter.kt +++ b/courier-message-adapter-protobuf/src/main/java/com/gojek/courier/messageadapter/protobuf/ProtobufMessageAdapter.kt @@ -17,7 +17,7 @@ private class ProtobufMessageAdapter constructor( private val registry: ExtensionRegistryLite? ) : MessageAdapter { - override fun fromMessage(message: Message): T { + override fun fromMessage(topic: String, message: Message): T { val bytesValue = when (message) { is Message.Bytes -> message.value } @@ -31,7 +31,9 @@ private class ProtobufMessageAdapter constructor( } } - override fun toMessage(data: T): Message = Message.Bytes(data.toByteArray()) + override fun toMessage(topic: String, data: T): Message = Message.Bytes(data.toByteArray()) + + override fun contentType() = "application/x-protobuf" } class ProtobufMessageAdapterFactory( diff --git a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt index c3a906d1..a4f7253a 100644 --- a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt +++ b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt @@ -23,9 +23,9 @@ internal class Coordinator( @Synchronized override fun send(stubMethod: StubMethod.Send, args: Array): Any { val data = stubMethod.argumentProcessor.getDataArgument(args) - val message = stubMethod.messageAdapter.toMessage(data) - stubMethod.argumentProcessor.inject(args) val topic = stubMethod.argumentProcessor.getTopic() + val message = stubMethod.messageAdapter.toMessage(topic, data) + stubMethod.argumentProcessor.inject(args) return client.send(message, topic, stubMethod.qos) } @@ -50,9 +50,13 @@ internal class Coordinator( ) val stream = flowable - .map { it.message } .observeOn(Schedulers.computation()) - .flatMap { message -> message.adapt(stubMethod.messageAdapter)?.let { Flowable.just(it) } ?: Flowable.empty() } + .flatMap { mqttMessage -> + mqttMessage.message.adapt( + mqttMessage.topic, + stubMethod.messageAdapter + )?.let { Flowable.just(it) } ?: Flowable.empty() + } .toStream() return stubMethod.streamAdapter.adapt(stream) } @@ -87,9 +91,13 @@ internal class Coordinator( ) val stream = flowable - .map { it.message } .observeOn(Schedulers.computation()) - .flatMap { message -> message.adapt(stubMethod.messageAdapter)?.let { Flowable.just(it) } ?: Flowable.empty() } + .flatMap { mqttMessage -> + mqttMessage.message.adapt( + mqttMessage.topic, + stubMethod.messageAdapter + )?.let { Flowable.just(it) } ?: Flowable.empty() + } .toStream() return stubMethod.streamAdapter.adapt(stream) } @@ -113,9 +121,9 @@ internal class Coordinator( } } - private fun Message.adapt(messageAdapter: MessageAdapter): T? { + private fun Message.adapt(topic: String, messageAdapter: MessageAdapter): T? { return try { - val message = messageAdapter.fromMessage(this) + val message = messageAdapter.fromMessage(topic, this) logger.d("Coordinator", "Message after parsing: $message") message } catch (th: Throwable) { diff --git a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/ByteArrayMessageAdapter.kt b/courier/src/main/java/com/gojek/courier/messageadapter/builtin/ByteArrayMessageAdapter.kt index c41ef7af..10536091 100644 --- a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/ByteArrayMessageAdapter.kt +++ b/courier/src/main/java/com/gojek/courier/messageadapter/builtin/ByteArrayMessageAdapter.kt @@ -5,9 +5,11 @@ import com.gojek.courier.MessageAdapter internal class ByteArrayMessageAdapter : MessageAdapter { - override fun fromMessage(message: Message): ByteArray = when (message) { + override fun fromMessage(topic: String, message: Message): ByteArray = when (message) { is Message.Bytes -> message.value } - override fun toMessage(data: ByteArray): Message = Message.Bytes(data) + override fun toMessage(topic: String, data: ByteArray): Message = Message.Bytes(data) + + override fun contentType() = "application/octet-stream" } diff --git a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/TextMessageAdapter.kt b/courier/src/main/java/com/gojek/courier/messageadapter/builtin/TextMessageAdapter.kt index 9cb44d47..5748f173 100644 --- a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/TextMessageAdapter.kt +++ b/courier/src/main/java/com/gojek/courier/messageadapter/builtin/TextMessageAdapter.kt @@ -5,9 +5,11 @@ import com.gojek.courier.MessageAdapter internal class TextMessageAdapter : MessageAdapter { - override fun fromMessage(message: Message): String = when (message) { + override fun fromMessage(topic: String, message: Message): String = when (message) { is Message.Bytes -> String(message.value) } - override fun toMessage(data: String): Message = Message.Bytes(data.toByteArray()) + override fun toMessage(topic: String, data: String): Message = Message.Bytes(data.toByteArray()) + + override fun contentType() = "text/plain" } From a227b3b3df6187289ffab0ac842c69a01d52bac1 Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Mon, 31 Oct 2022 10:52:20 +0530 Subject: [PATCH 07/25] Update documentation (#50) --- docs/docs/MessageStreamAdapters.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/docs/MessageStreamAdapters.md b/docs/docs/MessageStreamAdapters.md index 3857e07a..0e9015c0 100644 --- a/docs/docs/MessageStreamAdapters.md +++ b/docs/docs/MessageStreamAdapters.md @@ -26,13 +26,17 @@ class MyCustomMessageAdapterFactory : MessageAdapter.Factory { private class MyCustomMessageAdapter constructor() : MessageAdapter { - override fun fromMessage(message: Message): T { + override fun fromMessage(topic: String, message: Message): T { // convert message to custom type } - override fun toMessage(data: T): Message { + override fun toMessage(topic: String, data: T): Message { // convert custom type to message } + + override fun contentType(): String { + // content-type supported by this adapter. + } } ~~~ From f186dbda22b5829e43b6ccc38ee5e6d35ad62159 Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Mon, 14 Nov 2022 12:17:08 +0530 Subject: [PATCH 08/25] Remove built-in adapters and add text message adapter (#51) --- .github/workflows/publish.yml | 1 + build.gradle.kts | 1 + courier-message-adapter-text/.gitignore | 1 + .../api/courier-message-adapter-text.api | 5 ++++ courier-message-adapter-text/build.gradle.kts | 28 +++++++++++++++++++ .../messageadapter/text/TextMessageAdapter.kt | 23 +++++++++++++++ .../messageadapter/text/ExampleUnitTest.kt | 16 +++++++++++ .../main/java/com/gojek/courier/Courier.kt | 6 ++-- .../builtin/BuiltInMessageAdapterFactory.kt | 14 ---------- .../builtin/ByteArrayMessageAdapter.kt | 15 ---------- .../builtin/TextMessageAdapter.kt | 15 ---------- .../builtin/BuiltInStreamAdapterFactory.kt | 14 ---------- .../builtin/IdentityStreamAdapter.kt | 9 ------ scripts/publishMavenLocal.sh | 2 +- settings.gradle | 1 + 15 files changed, 79 insertions(+), 72 deletions(-) create mode 100644 courier-message-adapter-text/.gitignore create mode 100644 courier-message-adapter-text/api/courier-message-adapter-text.api create mode 100644 courier-message-adapter-text/build.gradle.kts create mode 100644 courier-message-adapter-text/src/main/java/com/gojek/courier/messageadapter/text/TextMessageAdapter.kt create mode 100644 courier-message-adapter-text/src/test/java/com/gojek/courier/messageadapter/text/ExampleUnitTest.kt delete mode 100644 courier/src/main/java/com/gojek/courier/messageadapter/builtin/BuiltInMessageAdapterFactory.kt delete mode 100644 courier/src/main/java/com/gojek/courier/messageadapter/builtin/ByteArrayMessageAdapter.kt delete mode 100644 courier/src/main/java/com/gojek/courier/messageadapter/builtin/TextMessageAdapter.kt delete mode 100644 courier/src/main/java/com/gojek/courier/streamadapter/builtin/BuiltInStreamAdapterFactory.kt delete mode 100644 courier/src/main/java/com/gojek/courier/streamadapter/builtin/IdentityStreamAdapter.kt diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7c7e766e..2e813338 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,6 +34,7 @@ jobs: :timer-pingsender:publishReleasePublicationToSonatypeRepository :adaptive-keep-alive:publishReleasePublicationToSonatypeRepository :network-tracker:publishReleasePublicationToSonatypeRepository + :courier-message-adapter-text:publishReleasePublicationToSonatypeRepository :courier-message-adapter-gson:publishReleasePublicationToSonatypeRepository :courier-message-adapter-moshi:publishReleasePublicationToSonatypeRepository :courier-message-adapter-protobuf:publishReleasePublicationToSonatypeRepository diff --git a/build.gradle.kts b/build.gradle.kts index a3c80aa8..89c3dc31 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,6 +56,7 @@ val clean by tasks.creating(Delete::class) { delete("${rootDir}/courier/build") delete("${rootDir}/courier-core/build") delete("${rootDir}/courier-core-android/build") + delete("${rootDir}/courier-message-adapter-text/build") delete("${rootDir}/courier-message-adapter-gson/build") delete("${rootDir}/courier-message-adapter-moshi/build") delete("${rootDir}/courier-message-adapter-protobuf/build") diff --git a/courier-message-adapter-text/.gitignore b/courier-message-adapter-text/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/courier-message-adapter-text/.gitignore @@ -0,0 +1 @@ +/build diff --git a/courier-message-adapter-text/api/courier-message-adapter-text.api b/courier-message-adapter-text/api/courier-message-adapter-text.api new file mode 100644 index 00000000..c902388d --- /dev/null +++ b/courier-message-adapter-text/api/courier-message-adapter-text.api @@ -0,0 +1,5 @@ +public final class com/gojek/courier/messageadapter/text/TextMessageAdapterFactory : com/gojek/courier/MessageAdapter$Factory { + public fun ()V + public fun create (Ljava/lang/reflect/Type;[Ljava/lang/annotation/Annotation;)Lcom/gojek/courier/MessageAdapter; +} + diff --git a/courier-message-adapter-text/build.gradle.kts b/courier-message-adapter-text/build.gradle.kts new file mode 100644 index 00000000..16bc8532 --- /dev/null +++ b/courier-message-adapter-text/build.gradle.kts @@ -0,0 +1,28 @@ +import plugin.KotlinLibraryConfigurationPlugin + +apply() +apply("$rootDir/gradle/script-ext.gradle") + +val version = ext.get("gitVersionName") + +ext { + set("PUBLISH_GROUP_ID", "com.gojek.courier") + set("PUBLISH_ARTIFACT_ID", "courier-message-adapter-text") + set("PUBLISH_VERSION", ext.get("gitVersionName")) + set("minimumCoverage", "0.0") +} + +plugins { + id("java-library") + kotlin("jvm") + id(ScriptPlugins.apiValidator) version versions.apiValidator +} + +dependencies { + api(project(":courier-core")) + implementation(deps.kotlin.stdlib.core) + implementation(deps.square.okio) + testImplementation(deps.android.test.kotlinTestJunit) +} + +apply(from = "${rootProject.projectDir}/gradle/publish-module.gradle") diff --git a/courier-message-adapter-text/src/main/java/com/gojek/courier/messageadapter/text/TextMessageAdapter.kt b/courier-message-adapter-text/src/main/java/com/gojek/courier/messageadapter/text/TextMessageAdapter.kt new file mode 100644 index 00000000..eb20768a --- /dev/null +++ b/courier-message-adapter-text/src/main/java/com/gojek/courier/messageadapter/text/TextMessageAdapter.kt @@ -0,0 +1,23 @@ +package com.gojek.courier.messageadapter.text + +import com.gojek.courier.Message +import com.gojek.courier.MessageAdapter +import com.gojek.courier.utils.getRawType +import java.lang.reflect.Type + +class TextMessageAdapterFactory : MessageAdapter.Factory { + override fun create(type: Type, annotations: Array): MessageAdapter<*> = when (type.getRawType()) { + String::class.java -> TextMessageAdapter() + else -> throw IllegalArgumentException("Type is not supported by this MessageAdapterFactory: $type") + } +} + +internal class TextMessageAdapter : MessageAdapter { + override fun fromMessage(topic: String, message: Message): String = when (message) { + is Message.Bytes -> String(message.value) + } + + override fun toMessage(topic: String, data: String): Message = Message.Bytes(data.toByteArray()) + + override fun contentType() = "text/plain" +} diff --git a/courier-message-adapter-text/src/test/java/com/gojek/courier/messageadapter/text/ExampleUnitTest.kt b/courier-message-adapter-text/src/test/java/com/gojek/courier/messageadapter/text/ExampleUnitTest.kt new file mode 100644 index 00000000..0fd4d3b7 --- /dev/null +++ b/courier-message-adapter-text/src/test/java/com/gojek/courier/messageadapter/text/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package com.gojek.courier.messageadapter.gson + +import org.junit.Assert.assertEquals +import org.junit.Test + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/courier/src/main/java/com/gojek/courier/Courier.kt b/courier/src/main/java/com/gojek/courier/Courier.kt index bf11b215..5af24039 100644 --- a/courier/src/main/java/com/gojek/courier/Courier.kt +++ b/courier/src/main/java/com/gojek/courier/Courier.kt @@ -3,8 +3,6 @@ package com.gojek.courier import com.gojek.courier.coordinator.Coordinator import com.gojek.courier.logging.ILogger import com.gojek.courier.logging.NoOpLogger -import com.gojek.courier.messageadapter.builtin.BuiltInMessageAdapterFactory -import com.gojek.courier.streamadapter.builtin.BuiltInStreamAdapterFactory import com.gojek.courier.stub.ProxyFactory import com.gojek.courier.stub.StubInterface import com.gojek.courier.stub.StubMethod @@ -52,10 +50,10 @@ class Courier(configuration: Configuration) { ) private fun Configuration.createStreamAdapterResolver(): StreamAdapterResolver { - return StreamAdapterResolver(listOf(BuiltInStreamAdapterFactory()) + streamAdapterFactories) + return StreamAdapterResolver(streamAdapterFactories) } private fun Configuration.createMessageAdapterResolver(): MessageAdapterResolver { - return MessageAdapterResolver(listOf(BuiltInMessageAdapterFactory()) + messageAdapterFactories) + return MessageAdapterResolver(messageAdapterFactories) } } diff --git a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/BuiltInMessageAdapterFactory.kt b/courier/src/main/java/com/gojek/courier/messageadapter/builtin/BuiltInMessageAdapterFactory.kt deleted file mode 100644 index 674abf30..00000000 --- a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/BuiltInMessageAdapterFactory.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.gojek.courier.messageadapter.builtin - -import com.gojek.courier.MessageAdapter -import com.gojek.courier.utils.getRawType -import java.lang.reflect.Type - -internal class BuiltInMessageAdapterFactory : MessageAdapter.Factory { - - override fun create(type: Type, annotations: Array): MessageAdapter<*> = when (type.getRawType()) { - String::class.java -> TextMessageAdapter() - ByteArray::class.java -> ByteArrayMessageAdapter() - else -> throw IllegalArgumentException("Type is not supported by this MessageAdapterFactory: $type") - } -} diff --git a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/ByteArrayMessageAdapter.kt b/courier/src/main/java/com/gojek/courier/messageadapter/builtin/ByteArrayMessageAdapter.kt deleted file mode 100644 index 10536091..00000000 --- a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/ByteArrayMessageAdapter.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.gojek.courier.messageadapter.builtin - -import com.gojek.courier.Message -import com.gojek.courier.MessageAdapter - -internal class ByteArrayMessageAdapter : MessageAdapter { - - override fun fromMessage(topic: String, message: Message): ByteArray = when (message) { - is Message.Bytes -> message.value - } - - override fun toMessage(topic: String, data: ByteArray): Message = Message.Bytes(data) - - override fun contentType() = "application/octet-stream" -} diff --git a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/TextMessageAdapter.kt b/courier/src/main/java/com/gojek/courier/messageadapter/builtin/TextMessageAdapter.kt deleted file mode 100644 index 5748f173..00000000 --- a/courier/src/main/java/com/gojek/courier/messageadapter/builtin/TextMessageAdapter.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.gojek.courier.messageadapter.builtin - -import com.gojek.courier.Message -import com.gojek.courier.MessageAdapter - -internal class TextMessageAdapter : MessageAdapter { - - override fun fromMessage(topic: String, message: Message): String = when (message) { - is Message.Bytes -> String(message.value) - } - - override fun toMessage(topic: String, data: String): Message = Message.Bytes(data.toByteArray()) - - override fun contentType() = "text/plain" -} diff --git a/courier/src/main/java/com/gojek/courier/streamadapter/builtin/BuiltInStreamAdapterFactory.kt b/courier/src/main/java/com/gojek/courier/streamadapter/builtin/BuiltInStreamAdapterFactory.kt deleted file mode 100644 index 503bf8d9..00000000 --- a/courier/src/main/java/com/gojek/courier/streamadapter/builtin/BuiltInStreamAdapterFactory.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.gojek.courier.streamadapter.builtin - -import com.gojek.courier.Stream -import com.gojek.courier.StreamAdapter -import com.gojek.courier.utils.getRawType -import java.lang.reflect.Type - -internal class BuiltInStreamAdapterFactory : StreamAdapter.Factory { - - override fun create(type: Type): StreamAdapter = when (type.getRawType()) { - Stream::class.java -> IdentityStreamAdapter() - else -> throw IllegalArgumentException("$type is not supported.") - } -} diff --git a/courier/src/main/java/com/gojek/courier/streamadapter/builtin/IdentityStreamAdapter.kt b/courier/src/main/java/com/gojek/courier/streamadapter/builtin/IdentityStreamAdapter.kt deleted file mode 100644 index bb2551ee..00000000 --- a/courier/src/main/java/com/gojek/courier/streamadapter/builtin/IdentityStreamAdapter.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.gojek.courier.streamadapter.builtin - -import com.gojek.courier.Stream -import com.gojek.courier.StreamAdapter - -internal class IdentityStreamAdapter : StreamAdapter> { - - override fun adapt(stream: Stream): Stream = stream -} diff --git a/scripts/publishMavenLocal.sh b/scripts/publishMavenLocal.sh index 661d826b..0803a611 100755 --- a/scripts/publishMavenLocal.sh +++ b/scripts/publishMavenLocal.sh @@ -4,7 +4,7 @@ echo "Publishing Libraries to Maven Local..." ./gradlew :paho:assemble :courier-core:assemble :courier-core-android:assemble --parallel --daemon && ./gradlew :paho:publishToMavenLocal -PIS_LOCAL=true :courier-core:publishToMavenLocal -PIS_LOCAL=true :courier-core-android:publishToMavenLocal -PIS_LOCAL=true --parallel --daemon ./gradlew :mqtt-pingsender:assemble && ./gradlew :mqtt-pingsender:publishToMavenLocal -PIS_LOCAL=true ./gradlew :workmanager-pingsender:assemble :workmanager-2.6.0-pingsender:assemble :alarm-pingsender:assemble :timer-pingsender:assemble --parallel --daemon && ./gradlew :workmanager-pingsender:publishToMavenLocal -PIS_LOCAL=true :workmanager-2.6.0-pingsender:publishToMavenLocal -PIS_LOCAL=true :alarm-pingsender:publishToMavenLocal -PIS_LOCAL=true :timer-pingsender:publishToMavenLocal -PIS_LOCAL=true --parallel --daemon -./gradlew :network-tracker:assemble :adaptive-keep-alive:assemble :courier-message-adapter-gson:assemble :courier-message-adapter-protobuf:assemble :courier-message-adapter-moshi:assemble :courier-stream-adapter-rxjava:assemble :courier-stream-adapter-rxjava2:assemble :courier-stream-adapter-coroutines:assemble :courier:assemble :app-state-manager:assemble --parallel --daemon && ./gradlew :network-tracker:publishToMavenLocal -PIS_LOCAL=true :adaptive-keep-alive:publishToMavenLocal -PIS_LOCAL=true :courier-message-adapter-gson:publishToMavenLocal -PIS_LOCAL=true :courier-message-adapter-moshi:publishToMavenLocal -PIS_LOCAL=true :courier-message-adapter-protobuf:publishToMavenLocal -PIS_LOCAL=true :courier-stream-adapter-rxjava:publishToMavenLocal -PIS_LOCAL=true :courier-stream-adapter-rxjava2:publishToMavenLocal -PIS_LOCAL=true :courier-stream-adapter-coroutines:publishToMavenLocal -PIS_LOCAL=true :courier:publishToMavenLocal -PIS_LOCAL=true :app-state-manager:publishToMavenLocal -PIS_LOCAL=true --parallel --daemon +./gradlew :network-tracker:assemble :adaptive-keep-alive:assemble :courier-message-adapter-gson:assemble :courier-message-adapter-text:assemble :courier-message-adapter-protobuf:assemble :courier-message-adapter-moshi:assemble :courier-stream-adapter-rxjava:assemble :courier-stream-adapter-rxjava2:assemble :courier-stream-adapter-coroutines:assemble :courier:assemble :app-state-manager:assemble --parallel --daemon && ./gradlew :network-tracker:publishToMavenLocal -PIS_LOCAL=true :adaptive-keep-alive:publishToMavenLocal -PIS_LOCAL=true :courier-message-adapter-gson:publishToMavenLocal -PIS_LOCAL=true :courier-message-adapter-text:publishToMavenLocal -PIS_LOCAL=true :courier-message-adapter-moshi:publishToMavenLocal -PIS_LOCAL=true :courier-message-adapter-protobuf:publishToMavenLocal -PIS_LOCAL=true :courier-stream-adapter-rxjava:publishToMavenLocal -PIS_LOCAL=true :courier-stream-adapter-rxjava2:publishToMavenLocal -PIS_LOCAL=true :courier-stream-adapter-coroutines:publishToMavenLocal -PIS_LOCAL=true :courier:publishToMavenLocal -PIS_LOCAL=true :app-state-manager:publishToMavenLocal -PIS_LOCAL=true --parallel --daemon ./gradlew :mqtt-client:assemble --parallel --daemon && ./gradlew :mqtt-client:publishToMavenLocal -PIS_LOCAL=true --parallel --daemon ./gradlew :courier:assemble :courier-auth-http:assemble --parallel --daemon && ./gradlew :courier:publishToMavenLocal -PIS_LOCAL=true :courier-auth-http:publishToMavenLocal -PIS_LOCAL=true --parallel --daemon ./gradlew :chuck-mqtt:assemble :chuck-mqtt-no-ops:assembleRelease --parallel --daemon && ./gradlew :chuck-mqtt:publishToMavenLocal -PIS_LOCAL=true :chuck-mqtt-no-ops:publishToMavenLocal -PIS_LOCAL=true --parallel --daemon diff --git a/settings.gradle b/settings.gradle index bfc495c8..535bc28f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,6 +5,7 @@ include ':mqtt-client' include ':courier' include ':courier-core' include ':courier-core-android' +include ':courier-message-adapter-text' include ':courier-message-adapter-gson' include ':courier-message-adapter-moshi' include ':courier-message-adapter-protobuf' From df1403fde9aba961c7427be23577466e1afd564a Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Fri, 18 Nov 2022 09:15:14 +0530 Subject: [PATCH 09/25] Fix argument injection in topic name (#52) --- .../src/main/java/com/gojek/courier/coordinator/Coordinator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt index a4f7253a..3d54e6d8 100644 --- a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt +++ b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt @@ -23,9 +23,9 @@ internal class Coordinator( @Synchronized override fun send(stubMethod: StubMethod.Send, args: Array): Any { val data = stubMethod.argumentProcessor.getDataArgument(args) + stubMethod.argumentProcessor.inject(args) val topic = stubMethod.argumentProcessor.getTopic() val message = stubMethod.messageAdapter.toMessage(topic, data) - stubMethod.argumentProcessor.inject(args) return client.send(message, topic, stubMethod.qos) } From ef5f5df43a449a7afc21da6d0988afd0cf051b15 Mon Sep 17 00:00:00 2001 From: Anubhav Gupta Date: Fri, 2 Dec 2022 23:22:24 +0530 Subject: [PATCH 10/25] only set alpn protocol if protocol list is not empty (#53) --- .../internal/platform/android/AndroidSocketAdapter.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt index f51ef808..ce02a4f5 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt @@ -59,10 +59,12 @@ open class AndroidSocketAdapter(private val sslSocketClass: Class) } // Enable ALPN. - setAlpnProtocols.invoke( - sslSocket, - Platform.concatLengthPrefixed(protocols) - ) + if (protocols.isNotEmpty()) { + setAlpnProtocols.invoke( + sslSocket, + Platform.concatLengthPrefixed(protocols) + ) + } } catch (e: IllegalAccessException) { throw AssertionError(e) } catch (e: InvocationTargetException) { From 97216660e8d225b0007668fc3a8df8d0e14c1b02 Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Fri, 3 Mar 2023 17:14:56 +0530 Subject: [PATCH 11/25] Add api for adding/removing event handler dynamically (#56) * Add api for adding/removing event handler dynamically * Add API to get event stream via Courier * Add API to get connection state via Courier --- .../com/gojek/courier/app/ui/MainActivity.kt | 2 +- courier/api/courier.api | 2 + .../main/java/com/gojek/courier/Courier.kt | 10 ++++ .../gojek/courier/coordinator/Coordinator.kt | 52 +++++++++++++++++++ .../com/gojek/courier/stub/StubInterface.kt | 5 ++ mqtt-client/api/mqtt-client.api | 23 ++++---- .../java/com/gojek/mqtt/client/MqttClient.kt | 3 ++ .../gojek/mqtt/client/MqttCourierClient.kt | 9 ++++ .../mqtt/client/config/MqttConfiguration.kt | 2 - .../client/config/v3/MqttV3Configuration.kt | 4 -- ...entsInterceptor.kt => MqttEventHandler.kt} | 13 ++++- .../factory/IAndroidMqttClientFactory.kt | 13 +++-- .../client/internal/MqttClientInternal.kt | 13 ++++- .../mqtt/client/v3/impl/AndroidMqttClient.kt | 34 ++++++------ 14 files changed, 139 insertions(+), 46 deletions(-) rename mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/{MqttEventsInterceptor.kt => MqttEventHandler.kt} (62%) diff --git a/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt b/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt index 83a821c5..40c344a4 100644 --- a/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt +++ b/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt @@ -107,7 +107,6 @@ class MainActivity : AppCompatActivity() { private fun initialiseCourier() { val mqttConfig = MqttV3Configuration( logger = getLogger(), - eventHandler = eventHandler, authenticator = object : Authenticator { override fun authenticate( connectOptions: MqttConnectOptions, @@ -136,6 +135,7 @@ class MainActivity : AppCompatActivity() { pingSender = WorkPingSenderFactory.createMqttPingSender(applicationContext, WorkManagerPingSenderConfig()) ) mqttClient = MqttClientFactory.create(this, mqttConfig) + mqttClient.addEventHandler(eventHandler) val configuration = Courier.Configuration( client = mqttClient, diff --git a/courier/api/courier.api b/courier/api/courier.api index acc81f5d..d7d5ed12 100644 --- a/courier/api/courier.api +++ b/courier/api/courier.api @@ -1,6 +1,8 @@ public final class com/gojek/courier/Courier { public fun (Lcom/gojek/courier/Courier$Configuration;)V public final fun create (Ljava/lang/Class;)Ljava/lang/Object; + public final fun getConnectionState ()Lcom/gojek/mqtt/client/model/ConnectionState; + public final fun getEventStream ()Lcom/gojek/courier/Stream; } public final class com/gojek/courier/Courier$Configuration { diff --git a/courier/src/main/java/com/gojek/courier/Courier.kt b/courier/src/main/java/com/gojek/courier/Courier.kt index 5af24039..5b74fa36 100644 --- a/courier/src/main/java/com/gojek/courier/Courier.kt +++ b/courier/src/main/java/com/gojek/courier/Courier.kt @@ -10,6 +10,8 @@ import com.gojek.courier.utils.MessageAdapterResolver import com.gojek.courier.utils.RuntimePlatform import com.gojek.courier.utils.StreamAdapterResolver import com.gojek.mqtt.client.MqttClient +import com.gojek.mqtt.client.model.ConnectionState +import com.gojek.mqtt.event.MqttEvent class Courier(configuration: Configuration) { private val stubInterfaceFactory: StubInterface.Factory @@ -42,6 +44,14 @@ class Courier(configuration: Configuration) { */ inline fun create(): T = create(T::class.java) + fun getEventStream(): Stream { + return coordinator.getEventStream() + } + + fun getConnectionState(): ConnectionState { + return coordinator.getConnectionState() + } + data class Configuration( val client: MqttClient, val streamAdapterFactories: List = emptyList(), diff --git a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt index 3d54e6d8..0b298076 100644 --- a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt +++ b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt @@ -3,17 +3,24 @@ package com.gojek.courier.coordinator import com.gojek.courier.Message import com.gojek.courier.MessageAdapter import com.gojek.courier.QoS +import com.gojek.courier.Stream +import com.gojek.courier.Stream.Disposable +import com.gojek.courier.Stream.Observer import com.gojek.courier.logging.ILogger import com.gojek.courier.stub.StubInterface import com.gojek.courier.stub.StubMethod import com.gojek.courier.utils.toStream import com.gojek.mqtt.client.MqttClient import com.gojek.mqtt.client.listener.MessageListener +import com.gojek.mqtt.client.model.ConnectionState import com.gojek.mqtt.client.model.MqttMessage +import com.gojek.mqtt.event.EventHandler +import com.gojek.mqtt.event.MqttEvent import io.reactivex.BackpressureStrategy import io.reactivex.Flowable import io.reactivex.FlowableOnSubscribe import io.reactivex.schedulers.Schedulers +import org.reactivestreams.Subscriber internal class Coordinator( private val client: MqttClient, @@ -121,6 +128,51 @@ internal class Coordinator( } } + override fun getEventStream(): Stream { + return object : Stream { + override fun start(observer: Observer): Disposable { + val eventHandler = object : EventHandler { + override fun onEvent(mqttEvent: MqttEvent) { + try { + observer.onNext(mqttEvent) + } catch (throwable: Throwable) { + observer.onError(throwable) + } + } + } + client.addEventHandler(eventHandler) + var isDisposed = false + return object : Disposable { + override fun dispose() { + client.removeEventHandler(eventHandler) + isDisposed = true + } + + override fun isDisposed(): Boolean { + return isDisposed + } + } + } + + override fun subscribe(s: Subscriber) { + val eventHandler = object : EventHandler { + override fun onEvent(mqttEvent: MqttEvent) { + try { + s.onNext(mqttEvent) + } catch (throwable: Throwable) { + s.onError(throwable) + } + } + } + client.addEventHandler(eventHandler) + } + } + } + + override fun getConnectionState(): ConnectionState { + return client.getCurrentState() + } + private fun Message.adapt(topic: String, messageAdapter: MessageAdapter): T? { return try { val message = messageAdapter.fromMessage(topic, this) diff --git a/courier/src/main/java/com/gojek/courier/stub/StubInterface.kt b/courier/src/main/java/com/gojek/courier/stub/StubInterface.kt index d4ca1c9d..a5a85261 100644 --- a/courier/src/main/java/com/gojek/courier/stub/StubInterface.kt +++ b/courier/src/main/java/com/gojek/courier/stub/StubInterface.kt @@ -1,6 +1,9 @@ package com.gojek.courier.stub +import com.gojek.courier.Stream import com.gojek.courier.utils.RuntimePlatform +import com.gojek.mqtt.client.model.ConnectionState +import com.gojek.mqtt.event.MqttEvent import java.lang.reflect.Method internal class StubInterface( @@ -38,6 +41,8 @@ internal class StubInterface( fun subscribeWithStream(stubMethod: StubMethod.SubscribeWithStream, args: Array): Any fun unsubscribe(stubMethod: StubMethod.Unsubscribe, args: Array): Any fun subscribeAll(stubMethod: StubMethod.SubscribeAll, args: Array): Any + fun getEventStream(): Stream + fun getConnectionState(): ConnectionState } internal class Factory( diff --git a/mqtt-client/api/mqtt-client.api b/mqtt-client/api/mqtt-client.api index fcbcf07a..acf9fa4c 100644 --- a/mqtt-client/api/mqtt-client.api +++ b/mqtt-client/api/mqtt-client.api @@ -3,12 +3,14 @@ public abstract interface class com/gojek/mqtt/auth/Authenticator { } public abstract interface class com/gojek/mqtt/client/MqttClient { + public abstract fun addEventHandler (Lcom/gojek/mqtt/event/EventHandler;)V public abstract fun addGlobalMessageListener (Lcom/gojek/mqtt/client/listener/MessageListener;)V public abstract fun addMessageListener (Ljava/lang/String;Lcom/gojek/mqtt/client/listener/MessageListener;)V public abstract fun connect (Lcom/gojek/mqtt/model/MqttConnectOptions;)V public abstract fun disconnect (Z)V public abstract fun getCurrentState ()Lcom/gojek/mqtt/client/model/ConnectionState; public abstract fun reconnect ()V + public abstract fun removeEventHandler (Lcom/gojek/mqtt/event/EventHandler;)V public abstract fun removeMessageListener (Ljava/lang/String;Lcom/gojek/mqtt/client/listener/MessageListener;)V public abstract fun send (Lcom/gojek/courier/Message;Ljava/lang/String;Lcom/gojek/courier/QoS;)Z public abstract fun subscribe (Lkotlin/Pair;[Lkotlin/Pair;)V @@ -52,12 +54,11 @@ public final class com/gojek/mqtt/client/config/ExperimentConfigs { } public abstract class com/gojek/mqtt/client/config/MqttConfiguration { - public fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)V + public fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)V public fun getAuthFailureHandler ()Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler; public fun getAuthenticator ()Lcom/gojek/mqtt/auth/Authenticator; public fun getConnectRetryTimePolicy ()Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy; public fun getConnectTimeoutPolicy ()Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy; - public fun getEventHandler ()Lcom/gojek/mqtt/event/EventHandler; public fun getExperimentConfigs ()Lcom/gojek/mqtt/client/config/ExperimentConfigs; public fun getLogger ()Lcom/gojek/courier/logging/ILogger; public fun getMqttInterceptorList ()Ljava/util/List; @@ -95,13 +96,12 @@ public final class com/gojek/mqtt/client/config/SubscriptionStore : java/lang/En } public final class com/gojek/mqtt/client/config/v3/MqttV3Configuration : com/gojek/mqtt/client/config/MqttConfiguration { - public fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)V - public synthetic fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)V + public synthetic fun (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy; - public final fun component10 ()Lcom/gojek/mqtt/pingsender/MqttPingSender; - public final fun component11 ()Ljava/util/List; - public final fun component12 ()Lcom/gojek/mqtt/client/config/PersistenceOptions; - public final fun component13 ()Lcom/gojek/mqtt/client/config/ExperimentConfigs; + public final fun component10 ()Ljava/util/List; + public final fun component11 ()Lcom/gojek/mqtt/client/config/PersistenceOptions; + public final fun component12 ()Lcom/gojek/mqtt/client/config/ExperimentConfigs; public final fun component2 ()Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy; public final fun component3 ()Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy; public final fun component4 ()Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy; @@ -109,15 +109,14 @@ public final class com/gojek/mqtt/client/config/v3/MqttV3Configuration : com/goj public final fun component6 ()Lcom/gojek/courier/logging/ILogger; public final fun component7 ()Lcom/gojek/mqtt/auth/Authenticator; public final fun component8 ()Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler; - public final fun component9 ()Lcom/gojek/mqtt/event/EventHandler; - public final fun copy (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration; - public static synthetic fun copy$default (Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration;Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/event/EventHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;ILjava/lang/Object;)Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration; + public final fun component9 ()Lcom/gojek/mqtt/pingsender/MqttPingSender; + public final fun copy (Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;)Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration; + public static synthetic fun copy$default (Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration;Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy;Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;Lcom/gojek/mqtt/policies/subscriptionretry/ISubscriptionRetryPolicy;ILcom/gojek/courier/logging/ILogger;Lcom/gojek/mqtt/auth/Authenticator;Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler;Lcom/gojek/mqtt/pingsender/MqttPingSender;Ljava/util/List;Lcom/gojek/mqtt/client/config/PersistenceOptions;Lcom/gojek/mqtt/client/config/ExperimentConfigs;ILjava/lang/Object;)Lcom/gojek/mqtt/client/config/v3/MqttV3Configuration; public fun equals (Ljava/lang/Object;)Z public fun getAuthFailureHandler ()Lcom/gojek/mqtt/exception/handler/v3/AuthFailureHandler; public fun getAuthenticator ()Lcom/gojek/mqtt/auth/Authenticator; public fun getConnectRetryTimePolicy ()Lcom/gojek/mqtt/policies/connectretrytime/IConnectRetryTimePolicy; public fun getConnectTimeoutPolicy ()Lcom/gojek/mqtt/policies/connecttimeout/IConnectTimeoutPolicy; - public fun getEventHandler ()Lcom/gojek/mqtt/event/EventHandler; public fun getExperimentConfigs ()Lcom/gojek/mqtt/client/config/ExperimentConfigs; public fun getLogger ()Lcom/gojek/courier/logging/ILogger; public fun getMqttInterceptorList ()Ljava/util/List; diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttClient.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttClient.kt index 5a47a919..2aadb27f 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttClient.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttClient.kt @@ -4,6 +4,7 @@ import com.gojek.courier.Message import com.gojek.courier.QoS import com.gojek.mqtt.client.listener.MessageListener import com.gojek.mqtt.client.model.ConnectionState +import com.gojek.mqtt.event.EventHandler import com.gojek.mqtt.model.MqttConnectOptions interface MqttClient { @@ -17,4 +18,6 @@ interface MqttClient { fun addMessageListener(topic: String, listener: MessageListener) fun removeMessageListener(topic: String, listener: MessageListener) fun addGlobalMessageListener(listener: MessageListener) + fun addEventHandler(eventHandler: EventHandler) + fun removeEventHandler(eventHandler: EventHandler) } diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttCourierClient.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttCourierClient.kt index c75df2b4..a0643119 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttCourierClient.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/MqttCourierClient.kt @@ -5,6 +5,7 @@ import com.gojek.courier.QoS import com.gojek.mqtt.client.internal.MqttClientInternal import com.gojek.mqtt.client.listener.MessageListener import com.gojek.mqtt.client.model.ConnectionState +import com.gojek.mqtt.event.EventHandler import com.gojek.mqtt.model.MqttConnectOptions import com.gojek.mqtt.model.MqttPacket @@ -50,4 +51,12 @@ internal class MqttCourierClient( override fun addGlobalMessageListener(listener: MessageListener) { mqttClient.addGlobalMessageListener(listener) } + + override fun addEventHandler(eventHandler: EventHandler) { + mqttClient.addEventHandler(eventHandler) + } + + override fun removeEventHandler(eventHandler: EventHandler) { + mqttClient.removeEventHandler(eventHandler) + } } diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/MqttConfiguration.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/MqttConfiguration.kt index 0a6c3f6b..26e83a36 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/MqttConfiguration.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/MqttConfiguration.kt @@ -3,7 +3,6 @@ package com.gojek.mqtt.client.config import com.gojek.courier.logging.ILogger import com.gojek.mqtt.auth.Authenticator import com.gojek.mqtt.client.MqttInterceptor -import com.gojek.mqtt.event.EventHandler import com.gojek.mqtt.exception.handler.v3.AuthFailureHandler import com.gojek.mqtt.pingsender.MqttPingSender import com.gojek.mqtt.policies.connectretrytime.IConnectRetryTimePolicy @@ -19,7 +18,6 @@ abstract class MqttConfiguration( open val logger: ILogger, open val authenticator: Authenticator, open val authFailureHandler: AuthFailureHandler?, - open val eventHandler: EventHandler, open val pingSender: MqttPingSender, open val mqttInterceptorList: List, open val persistenceOptions: PersistenceOptions, diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/v3/MqttV3Configuration.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/v3/MqttV3Configuration.kt index 39dd9c2a..475d9914 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/v3/MqttV3Configuration.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/v3/MqttV3Configuration.kt @@ -9,8 +9,6 @@ import com.gojek.mqtt.client.config.MqttConfiguration import com.gojek.mqtt.client.config.PersistenceOptions import com.gojek.mqtt.client.config.PersistenceOptions.PahoPersistenceOptions import com.gojek.mqtt.constants.DEFAULT_WAKELOCK_TIMEOUT -import com.gojek.mqtt.event.EventHandler -import com.gojek.mqtt.event.NoOpEventHandler import com.gojek.mqtt.exception.handler.v3.AuthFailureHandler import com.gojek.mqtt.pingsender.MqttPingSender import com.gojek.mqtt.policies.connectretrytime.ConnectRetryTimeConfig @@ -36,7 +34,6 @@ data class MqttV3Configuration( override val logger: ILogger = NoOpLogger(), override val authenticator: Authenticator, override val authFailureHandler: AuthFailureHandler? = null, - override val eventHandler: EventHandler = NoOpEventHandler(), override val pingSender: MqttPingSender, override val mqttInterceptorList: List = emptyList(), override val persistenceOptions: PersistenceOptions = PahoPersistenceOptions(), @@ -50,7 +47,6 @@ data class MqttV3Configuration( logger = logger, authenticator = authenticator, authFailureHandler = authFailureHandler, - eventHandler = eventHandler, pingSender = pingSender, mqttInterceptorList = mqttInterceptorList, persistenceOptions = persistenceOptions, diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventsInterceptor.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventHandler.kt similarity index 62% rename from mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventsInterceptor.kt rename to mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventHandler.kt index 42bb9455..fdb70f31 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventsInterceptor.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventHandler.kt @@ -4,9 +4,10 @@ import com.gojek.mqtt.event.EventHandler import com.gojek.mqtt.event.MqttEvent import java.util.concurrent.CopyOnWriteArrayList -internal class MqttEventsInterceptor(private val eventHandler: EventHandler) : EventHandler { +internal class MqttEventHandler : EventHandler { private val interceptorList = CopyOnWriteArrayList() + private val eventHandlers = CopyOnWriteArrayList() init { interceptorList.add(ConnectionInfoInterceptor()) @@ -17,7 +18,15 @@ internal class MqttEventsInterceptor(private val eventHandler: EventHandler) : E interceptorList.forEach { event = it.intercept(event) } - eventHandler.onEvent(event) + eventHandlers.forEach { it.onEvent(mqttEvent) } + } + + fun addEventHandler(handler: EventHandler) { + eventHandlers.add(handler) + } + + fun removeEventHandler(handler: EventHandler) { + eventHandlers.remove(handler) } fun addInterceptor(interceptor: EventInterceptor) { diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/factory/IAndroidMqttClientFactory.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/factory/IAndroidMqttClientFactory.kt index ba61a07f..bc54b154 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/factory/IAndroidMqttClientFactory.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/factory/IAndroidMqttClientFactory.kt @@ -48,13 +48,12 @@ internal class AndroidMqttClientFactory : IAndroidMqttClientFactory { pingSender.setPingEventHandler(pingEventHandler) return AndroidMqttClient( context = context, - mqttConfiguration = mqttConfiguration.copy( - eventHandler = eventHandler - ), + mqttConfiguration = mqttConfiguration, networkStateTracker = networkStateTracker, mqttPingSender = pingSender, keepAliveProvider = keepAliveProvider, - keepAliveFailureHandler = keepAliveFailureHandler + keepAliveFailureHandler = keepAliveFailureHandler, + eventHandler = eventHandler ) } @@ -71,14 +70,14 @@ internal class AndroidMqttClientFactory : IAndroidMqttClientFactory { return AndroidMqttClient( context = context, mqttConfiguration = mqttConfiguration.copy( - logger = NoOpLogger(), - eventHandler = NoOpEventHandler() + logger = NoOpLogger() ), networkStateTracker = networkStateTracker, mqttPingSender = pingSender, isAdaptiveKAConnection = true, keepAliveProvider = keepAliveProvider, - keepAliveFailureHandler = keepAliveFailureHandler + keepAliveFailureHandler = keepAliveFailureHandler, + eventHandler = NoOpEventHandler() ) } } diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/internal/MqttClientInternal.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/internal/MqttClientInternal.kt index 8d5dbec0..ea2259b2 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/internal/MqttClientInternal.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/internal/MqttClientInternal.kt @@ -9,12 +9,13 @@ import com.gojek.keepalive.OptimalKeepAliveObserver import com.gojek.keepalive.OptimalKeepAliveProvider import com.gojek.keepalive.config.AdaptiveKeepAliveConfig as AdaptiveKAConfig import com.gojek.mqtt.client.config.v3.MqttV3Configuration -import com.gojek.mqtt.client.event.interceptor.MqttEventsInterceptor +import com.gojek.mqtt.client.event.interceptor.MqttEventHandler import com.gojek.mqtt.client.factory.getAndroidMqttClientFactory import com.gojek.mqtt.client.listener.MessageListener import com.gojek.mqtt.client.model.ConnectionState import com.gojek.mqtt.client.v3.IAndroidMqttClient import com.gojek.mqtt.event.AdaptivePingEventHandler +import com.gojek.mqtt.event.EventHandler import com.gojek.mqtt.event.MqttEvent.OptimalKeepAliveFoundEvent import com.gojek.mqtt.event.PingEventHandler import com.gojek.mqtt.model.AdaptiveKeepAliveConfig @@ -40,7 +41,7 @@ internal class MqttClientInternal( private var keepAliveProvider: KeepAliveProvider = NonAdaptiveKeepAliveProvider() private var keepAliveFailureHandler: KeepAliveFailureHandler = NoOpKeepAliveFailureHandler() - private val eventHandler = MqttEventsInterceptor(mqttConfiguration.eventHandler) + private val eventHandler = MqttEventHandler() private val optimalKeepAliveObserver = object : OptimalKeepAliveObserver { override fun onOptimalKeepAliveFound( @@ -166,4 +167,12 @@ internal class MqttClientInternal( ) } } + + fun addEventHandler(eventHandler: EventHandler) { + this.eventHandler.addEventHandler(eventHandler) + } + + fun removeEventHandler(eventHandler: EventHandler) { + this.eventHandler.removeEventHandler(eventHandler) + } } diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt index 757403f8..65209a5f 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt @@ -43,6 +43,7 @@ import com.gojek.mqtt.connection.config.v3.ConnectionConfig import com.gojek.mqtt.constants.MAX_INFLIGHT_MESSAGES_ALLOWED import com.gojek.mqtt.constants.MESSAGE import com.gojek.mqtt.constants.MSG_APP_PUBLISH +import com.gojek.mqtt.event.EventHandler import com.gojek.mqtt.event.MqttEvent.AuthenticatorErrorEvent import com.gojek.mqtt.event.MqttEvent.MqttConnectDiscardedEvent import com.gojek.mqtt.event.MqttEvent.MqttConnectFailureEvent @@ -88,6 +89,7 @@ internal class AndroidMqttClient( private val mqttPingSender: MqttPingSender, private val isAdaptiveKAConnection: Boolean = false, private val keepAliveProvider: KeepAliveProvider, + private val eventHandler: EventHandler, keepAliveFailureHandler: KeepAliveFailureHandler ) : IAndroidMqttClient, IClientSchedulerBridge { @@ -144,7 +146,7 @@ internal class AndroidMqttClient( mqttThreadHandler, this, logger, - mqttConfiguration.eventHandler, + eventHandler, experimentConfigs.activityCheckIntervalSeconds ) mqttUtils = MqttUtils() @@ -158,7 +160,7 @@ internal class AndroidMqttClient( networkStateTracker = networkStateTracker ) mqttClientEventAdapter = MqttClientEventAdapter( - eventHandler = mqttConfiguration.eventHandler, + eventHandler = eventHandler, networkHandler = networkHandler ) val connectionConfig = @@ -198,7 +200,7 @@ internal class AndroidMqttClient( mqttUtils, mqttPersistence, logger, - mqttConfiguration.eventHandler, + eventHandler, experimentConfigs.incomingMessagesTTLSecs, experimentConfigs.incomingMessagesCleanupIntervalSecs, clock @@ -217,7 +219,7 @@ internal class AndroidMqttClient( // This can be invoked on any thread override fun reconnect() { - mqttConfiguration.eventHandler.onEvent(MqttReconnectEvent()) + eventHandler.onEvent(MqttReconnectEvent()) runnableScheduler.disconnectMqtt(true) } @@ -245,14 +247,14 @@ internal class AndroidMqttClient( "with qos ${mqttPacket.qos}" ) with(mqttPacket) { - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttMessageSendEvent(topic, qos, message.size) ) } mqttConnection.publish(mqttPacket, mqttPacket.qos, mqttPacket.topic) } catch (e: MqttPersistenceException) { with(mqttPacket) { - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttMessageSendFailureEvent( topic = topic, qos = qos, @@ -263,7 +265,7 @@ internal class AndroidMqttClient( } } catch (e: MqttException) { with(mqttPacket) { - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttMessageSendFailureEvent( topic = topic, qos = qos, @@ -276,7 +278,7 @@ internal class AndroidMqttClient( } catch (e: java.lang.Exception) { // this might happen if mqtt object becomes null while disconnect, so just ignore with(mqttPacket) { - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttMessageSendFailureEvent( topic = topic, qos = qos, @@ -337,7 +339,7 @@ internal class AndroidMqttClient( logger.d(TAG, "Sending onConnectAttempt event") if (!isInitialised) { logger.d(TAG, "Mqtt Client not initialised") - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttConnectDiscardedEvent( "Mqtt Client not initialised", networkHandler.getActiveNetworkInfo() @@ -368,7 +370,7 @@ internal class AndroidMqttClient( } catch (e: AuthApiException) /* this exception can be thrown by authenticator */ { logger.e(TAG, "Auth exception : ${e.message}") forceRefresh = true - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( AuthenticatorErrorEvent( exception = e.toCourierException(), nextRetryTimeSecs = e.nextRetrySeconds, @@ -384,7 +386,7 @@ internal class AndroidMqttClient( } } catch (e: Exception) /* this exception cannot be thrown on connect */ { logger.e(TAG, "Connect exception : ${e.message}") - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttConnectFailureEvent( exception = e.toCourierException(), activeNetInfo = networkHandler.getActiveNetworkInfo(), @@ -399,7 +401,7 @@ internal class AndroidMqttClient( // This runs on Mqtt thread override fun disconnectMqtt(clearState: Boolean) { - mqttConfiguration.eventHandler.onEvent(MqttDisconnectEvent()) + eventHandler.onEvent(MqttDisconnectEvent()) mqttConnection.disconnect() if (clearState) { mqttConnection.shutDown() @@ -537,7 +539,7 @@ internal class AndroidMqttClient( IMessageReceiveListener { override fun messageArrived(topic: String, byteArray: ByteArray): Boolean { try { - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttMessageReceiveEvent(topic, byteArray.size) ) val bytes = mqttUtils.uncompressByteArray(byteArray)!! @@ -555,14 +557,14 @@ internal class AndroidMqttClient( globalListener?.onMessageReceived(mqttPacket.toMqttMessage()) triggerHandleMessage() } catch (e: IllegalStateException) { - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttMessageReceiveErrorEvent(topic, byteArray.size, e.toCourierException()) ) logger.e(TAG, "Exception when msg arrived : ", e) runnableScheduler.disconnectMqtt(true) return false } catch (e: Throwable) { - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttMessageReceiveErrorEvent(topic, byteArray.size, e.toCourierException()) ) logger.e(TAG, "Exception when msg arrived : ", e) @@ -575,7 +577,7 @@ internal class AndroidMqttClient( IMessageSendListener { override fun onSuccess(packet: MqttSendPacket) { with(packet) { - mqttConfiguration.eventHandler.onEvent( + eventHandler.onEvent( MqttMessageSendSuccessEvent( topic, qos, From 48db728e320884649f6990cab1e18d9a9a93ff17 Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Fri, 10 Mar 2023 12:00:07 +0530 Subject: [PATCH 12/25] Add new builder functionality in Courier (#57) * Add new builder functionality in Courier --- courier/api/courier.api | 8 ++++++ .../main/java/com/gojek/courier/Courier.kt | 27 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/courier/api/courier.api b/courier/api/courier.api index d7d5ed12..825a2dca 100644 --- a/courier/api/courier.api +++ b/courier/api/courier.api @@ -3,6 +3,14 @@ public final class com/gojek/courier/Courier { public final fun create (Ljava/lang/Class;)Ljava/lang/Object; public final fun getConnectionState ()Lcom/gojek/mqtt/client/model/ConnectionState; public final fun getEventStream ()Lcom/gojek/courier/Stream; + public final fun newBuilder ()Lcom/gojek/courier/Courier$Builder; +} + +public final class com/gojek/courier/Courier$Builder { + public fun (Lcom/gojek/courier/Courier$Configuration;)V + public final fun addMessageAdapterFactories (Ljava/util/List;)Lcom/gojek/courier/Courier$Builder; + public final fun addStreamAdapterFactories (Ljava/util/List;)Lcom/gojek/courier/Courier$Builder; + public final fun build ()Lcom/gojek/courier/Courier; } public final class com/gojek/courier/Courier$Configuration { diff --git a/courier/src/main/java/com/gojek/courier/Courier.kt b/courier/src/main/java/com/gojek/courier/Courier.kt index 5b74fa36..4e39cd71 100644 --- a/courier/src/main/java/com/gojek/courier/Courier.kt +++ b/courier/src/main/java/com/gojek/courier/Courier.kt @@ -13,7 +13,7 @@ import com.gojek.mqtt.client.MqttClient import com.gojek.mqtt.client.model.ConnectionState import com.gojek.mqtt.event.MqttEvent -class Courier(configuration: Configuration) { +class Courier(private val configuration: Configuration) { private val stubInterfaceFactory: StubInterface.Factory private val proxyFactory: ProxyFactory private val coordinator: Coordinator @@ -52,6 +52,10 @@ class Courier(configuration: Configuration) { return coordinator.getConnectionState() } + fun newBuilder(): Builder { + return Builder(configuration) + } + data class Configuration( val client: MqttClient, val streamAdapterFactories: List = emptyList(), @@ -66,4 +70,25 @@ class Courier(configuration: Configuration) { private fun Configuration.createMessageAdapterResolver(): MessageAdapterResolver { return MessageAdapterResolver(messageAdapterFactories) } + + class Builder(private var configuration: Configuration) { + + fun addMessageAdapterFactories(messageAdapterFactories: List): Builder { + configuration = configuration.copy( + messageAdapterFactories = messageAdapterFactories + configuration.messageAdapterFactories + ) + return this + } + + fun addStreamAdapterFactories(streamAdapterFactories: List): Builder { + configuration = configuration.copy( + streamAdapterFactories = streamAdapterFactories + configuration.streamAdapterFactories + ) + return this + } + + fun build(): Courier { + return Courier(configuration) + } + } } From d98980c17f88110415813f88a3c8921e98eabbf2 Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Thu, 23 Mar 2023 11:04:56 +0530 Subject: [PATCH 13/25] Fix event stream buffer overflow issue (#60) --- .../java/com/gojek/courier/coordinator/Coordinator.kt | 11 ++++++++++- .../mqtt/client/event/interceptor/MqttEventHandler.kt | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt index 0b298076..c93a085f 100644 --- a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt +++ b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt @@ -21,6 +21,7 @@ import io.reactivex.Flowable import io.reactivex.FlowableOnSubscribe import io.reactivex.schedulers.Schedulers import org.reactivestreams.Subscriber +import org.reactivestreams.Subscription internal class Coordinator( private val client: MqttClient, @@ -164,7 +165,15 @@ internal class Coordinator( } } } - client.addEventHandler(eventHandler) + s.onSubscribe(object : Subscription { + override fun request(n: Long) { + client.addEventHandler(eventHandler) + } + + override fun cancel() { + client.removeEventHandler(eventHandler) + } + }) } } } diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventHandler.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventHandler.kt index fdb70f31..028c5cd0 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventHandler.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/event/interceptor/MqttEventHandler.kt @@ -22,7 +22,7 @@ internal class MqttEventHandler : EventHandler { } fun addEventHandler(handler: EventHandler) { - eventHandlers.add(handler) + eventHandlers.addIfAbsent(handler) } fun removeEventHandler(handler: EventHandler) { @@ -30,6 +30,6 @@ internal class MqttEventHandler : EventHandler { } fun addInterceptor(interceptor: EventInterceptor) { - interceptorList.add(interceptor) + interceptorList.addIfAbsent(interceptor) } } From d53d2639be31dfb4fef6e20a377935521325ab98 Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Wed, 29 Mar 2023 13:11:17 +0530 Subject: [PATCH 14/25] Fix ordering of message & stream adapter factories (#61) --- courier/src/main/java/com/gojek/courier/Courier.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/courier/src/main/java/com/gojek/courier/Courier.kt b/courier/src/main/java/com/gojek/courier/Courier.kt index 4e39cd71..40a978b1 100644 --- a/courier/src/main/java/com/gojek/courier/Courier.kt +++ b/courier/src/main/java/com/gojek/courier/Courier.kt @@ -75,14 +75,14 @@ class Courier(private val configuration: Configuration) { fun addMessageAdapterFactories(messageAdapterFactories: List): Builder { configuration = configuration.copy( - messageAdapterFactories = messageAdapterFactories + configuration.messageAdapterFactories + messageAdapterFactories = configuration.messageAdapterFactories + messageAdapterFactories ) return this } fun addStreamAdapterFactories(streamAdapterFactories: List): Builder { configuration = configuration.copy( - streamAdapterFactories = streamAdapterFactories + configuration.streamAdapterFactories + streamAdapterFactories = configuration.streamAdapterFactories + streamAdapterFactories ) return this } From 42a90aa6b4d389bc8a0bde255de1818f22f190e0 Mon Sep 17 00:00:00 2001 From: ChandoraAnkit Date: Thu, 30 Mar 2023 22:13:46 +0530 Subject: [PATCH 15/25] Fix formatting issue --- .../presentation/mapper/MqttTransactionUiModelMapper.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt b/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt index 292de1c2..2ee31cf9 100644 --- a/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt +++ b/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt @@ -24,7 +24,8 @@ import `in`.mohalla.paho.client.mqttv3.internal.wire.MqttUnsubAck import `in`.mohalla.paho.client.mqttv3.internal.wire.MqttUnsubscribe import `in`.mohalla.paho.client.mqttv3.internal.wire.MqttWireMessage -internal class MqttTransactionUiModelMapper : Mapper { +internal class MqttTransactionUiModelMapper : + Mapper { override fun map(input: MqttTransactionDomainModel): MqttTransactionUiModel { return with(input) { MqttTransactionUiModel( @@ -161,7 +162,8 @@ internal class MqttTransactionUiModelMapper : Mapper { formatBody(String(mqttWireMessage.message.payload, UTF_8)) - } else -> "" + } + else -> "" } } From e66ea5ec0f512aa072c4fa86d4dcd6fe1aaa1b13 Mon Sep 17 00:00:00 2001 From: ChandoraAnkit Date: Thu, 30 Mar 2023 22:21:24 +0530 Subject: [PATCH 16/25] Fix formatting issue --- .../presentation/mapper/MqttTransactionUiModelMapper.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt b/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt index 2ee31cf9..090258f1 100644 --- a/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt +++ b/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt @@ -5,8 +5,6 @@ import com.gojek.chuckmqtt.internal.domain.model.MqttTransactionDomainModel import com.gojek.chuckmqtt.internal.presentation.model.MqttTransactionUiModel import com.gojek.chuckmqtt.internal.utils.formatBody import com.gojek.chuckmqtt.internal.utils.formatByteCount -import java.text.DateFormat -import kotlin.text.Charsets.UTF_8 import `in`.mohalla.paho.client.mqttv3.MqttMessage import `in`.mohalla.paho.client.mqttv3.internal.wire.MqttConnack import `in`.mohalla.paho.client.mqttv3.internal.wire.MqttConnect @@ -23,6 +21,8 @@ import `in`.mohalla.paho.client.mqttv3.internal.wire.MqttSubscribe import `in`.mohalla.paho.client.mqttv3.internal.wire.MqttUnsubAck import `in`.mohalla.paho.client.mqttv3.internal.wire.MqttUnsubscribe import `in`.mohalla.paho.client.mqttv3.internal.wire.MqttWireMessage +import java.text.DateFormat +import kotlin.text.Charsets.UTF_8 internal class MqttTransactionUiModelMapper : Mapper { From 4e6309dee959a1c2f238ec94d84ab4cd872ace4d Mon Sep 17 00:00:00 2001 From: ChandoraAnkit Date: Thu, 30 Mar 2023 22:33:37 +0530 Subject: [PATCH 17/25] Fix formatting issue --- .../gojek/mqtt/client/v3/impl/AndroidMqttClient.kt | 4 ++-- .../handler/v3/impl/MqttExceptionHandlerImpl.kt | 2 +- .../gojek/mqtt/persistence/impl/PahoPersistence.kt | 4 ++-- .../handler/v3/impl/MqttExceptionHandlerImplTest.kt | 12 ++++++------ 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt index ff22d04c..afac607e 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt @@ -76,11 +76,11 @@ import com.gojek.mqtt.utils.MqttUtils import com.gojek.mqtt.utils.NetworkUtils import com.gojek.mqtt.wakelock.WakeLockProvider import com.gojek.networktracker.NetworkStateTracker -import java.nio.charset.StandardCharsets -import java.util.concurrent.TimeUnit import `in`.mohalla.paho.client.mqttv3.MqttException import `in`.mohalla.paho.client.mqttv3.MqttException.REASON_CODE_UNEXPECTED_ERROR import `in`.mohalla.paho.client.mqttv3.MqttPersistenceException +import java.nio.charset.StandardCharsets +import java.util.concurrent.TimeUnit internal class AndroidMqttClient( private val context: Context, diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImpl.kt b/mqtt-client/src/main/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImpl.kt index 305ae0a7..988f51bd 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImpl.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImpl.kt @@ -5,13 +5,13 @@ import com.gojek.mqtt.constants.SERVER_UNAVAILABLE_MAX_CONNECT_TIME import com.gojek.mqtt.exception.handler.v3.MqttExceptionHandler import com.gojek.mqtt.policies.connectretrytime.IConnectRetryTimePolicy import com.gojek.mqtt.scheduler.IRunnableScheduler +import `in`.mohalla.paho.client.mqttv3.MqttException import java.net.SocketException import java.net.SocketTimeoutException import java.net.UnknownHostException import java.nio.channels.UnresolvedAddressException import java.util.Random import javax.net.ssl.SSLHandshakeException -import `in`.mohalla.paho.client.mqttv3.MqttException internal class MqttExceptionHandlerImpl( private val runnableScheduler: IRunnableScheduler, diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/persistence/impl/PahoPersistence.kt b/mqtt-client/src/main/java/com/gojek/mqtt/persistence/impl/PahoPersistence.kt index 27a23c15..e4981649 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/persistence/impl/PahoPersistence.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/persistence/impl/PahoPersistence.kt @@ -8,11 +8,11 @@ import com.gojek.mqtt.persistence.dao.PahoMessagesDao import com.gojek.mqtt.persistence.db.MqttDatabase import com.gojek.mqtt.persistence.model.MqttPahoPacket import com.gojek.mqtt.persistence.model.MqttReceivePacket -import java.util.Collections -import java.util.Enumeration import `in`.mohalla.paho.client.mqttv3.MqttClientPersistence import `in`.mohalla.paho.client.mqttv3.MqttPersistable import `in`.mohalla.paho.client.mqttv3.internal.MqttPersistentData +import java.util.Collections +import java.util.Enumeration internal class PahoPersistence(private val context: Context) : MqttClientPersistence, IMqttReceivePersistence { diff --git a/mqtt-client/src/test/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImplTest.kt b/mqtt-client/src/test/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImplTest.kt index e0599f5c..b6af64e7 100644 --- a/mqtt-client/src/test/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImplTest.kt +++ b/mqtt-client/src/test/java/com/gojek/mqtt/exception/handler/v3/impl/MqttExceptionHandlerImplTest.kt @@ -7,12 +7,6 @@ import com.gojek.mqtt.scheduler.IRunnableScheduler import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import java.net.SocketException -import java.net.SocketTimeoutException -import java.net.UnknownHostException -import java.nio.channels.UnresolvedAddressException -import java.util.Random -import javax.net.ssl.SSLHandshakeException import `in`.mohalla.paho.client.mqttv3.MqttException import `in`.mohalla.paho.client.mqttv3.MqttException.REASON_CODE_BROKER_UNAVAILABLE import `in`.mohalla.paho.client.mqttv3.MqttException.REASON_CODE_CLIENT_ALREADY_DISCONNECTED @@ -37,6 +31,12 @@ import `in`.mohalla.paho.client.mqttv3.MqttException.REASON_CODE_SOCKET_FACTORY_ import `in`.mohalla.paho.client.mqttv3.MqttException.REASON_CODE_SSL_CONFIG_ERROR import `in`.mohalla.paho.client.mqttv3.MqttException.REASON_CODE_TOKEN_INUSE import `in`.mohalla.paho.client.mqttv3.MqttException.REASON_CODE_UNEXPECTED_ERROR +import java.net.SocketException +import java.net.SocketTimeoutException +import java.net.UnknownHostException +import java.nio.channels.UnresolvedAddressException +import java.util.Random +import javax.net.ssl.SSLHandshakeException import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.verifyNoInteractions From 561c5a6b27608e37c230b9bff360bff1bf213ccf Mon Sep 17 00:00:00 2001 From: ChandoraAnkit Date: Thu, 30 Mar 2023 22:43:53 +0530 Subject: [PATCH 18/25] Fix formatting issue --- .../gojek/mqtt/model/MqttConnectOptions.kt | 6 +++--- .../hostfallback/HostFallbackPolicyTest.kt | 2 +- .../paho/client/mqttv3/ConnectionSpec.kt | 6 +++--- .../internal/platform/Android10Platform.kt | 6 +++--- .../internal/platform/AndroidPlatform.kt | 20 +++++++++---------- .../internal/platform/BouncyCastlePlatform.kt | 2 +- .../internal/platform/ConscryptPlatform.kt | 2 +- .../platform/Jdk8WithJettyBootPlatform.kt | 2 +- .../mqttv3/internal/platform/Jdk9Platform.kt | 4 ++-- .../internal/platform/OpenJSSEPlatform.kt | 2 +- .../mqttv3/internal/platform/Platform.kt | 12 +++++------ .../android/Android10SocketAdapter.kt | 4 ++-- .../android/AndroidCertificateChainCleaner.kt | 4 ++-- .../platform/android/AndroidSocketAdapter.kt | 6 +++--- .../android/BouncyCastleSocketAdapter.kt | 4 ++-- .../android/ConscryptSocketAdapter.kt | 4 ++-- .../platform/android/DeferredSocketAdapter.kt | 2 +- .../platform/android/SocketAdapter.kt | 2 +- .../android/StandardAndroidSocketAdapter.kt | 4 ++-- .../internal/tls/CertificateChainCleaner.kt | 2 +- .../gojek/timer/pingsender/TimerPingSender.kt | 4 ++-- .../timer/pingsender/TimerPingSenderTest.kt | 8 ++++---- .../WorkManagerPingSenderAdaptiveTest.kt | 8 ++++---- .../pingsender/WorkManagerPingSenderTest.kt | 8 ++++---- .../WorkManagerPingSenderAdaptiveTest.kt | 8 ++++---- .../pingsender/WorkManagerPingSenderTest.kt | 8 ++++---- 26 files changed, 70 insertions(+), 70 deletions(-) diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt b/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt index 043e84ab..2a453e24 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/model/MqttConnectOptions.kt @@ -2,12 +2,12 @@ package com.gojek.mqtt.model import com.gojek.mqtt.model.KeepAlive.Companion.NO_KEEP_ALIVE import com.gojek.mqtt.model.MqttVersion.VERSION_3_1_1 -import javax.net.SocketFactory -import javax.net.ssl.SSLSocketFactory -import javax.net.ssl.X509TrustManager import `in`.mohalla.paho.client.mqttv3.ConnectionSpec import `in`.mohalla.paho.client.mqttv3.Protocol import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform +import javax.net.SocketFactory +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager class MqttConnectOptions private constructor( builder: Builder diff --git a/mqtt-client/src/test/java/com/gojek/mqtt/policies/hostfallback/HostFallbackPolicyTest.kt b/mqtt-client/src/test/java/com/gojek/mqtt/policies/hostfallback/HostFallbackPolicyTest.kt index cd2ed3ee..82bf366d 100644 --- a/mqtt-client/src/test/java/com/gojek/mqtt/policies/hostfallback/HostFallbackPolicyTest.kt +++ b/mqtt-client/src/test/java/com/gojek/mqtt/policies/hostfallback/HostFallbackPolicyTest.kt @@ -1,8 +1,8 @@ package com.gojek.mqtt.policies.hostfallback import com.gojek.mqtt.model.ServerUri -import java.lang.IllegalArgumentException import `in`.mohalla.paho.client.mqttv3.MqttException +import java.lang.IllegalArgumentException import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/ConnectionSpec.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/ConnectionSpec.kt index 63e4f2da..36ddae50 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/ConnectionSpec.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/ConnectionSpec.kt @@ -15,12 +15,12 @@ */ package `in`.mohalla.paho.client.mqttv3 -import java.util.Arrays -import java.util.Objects -import javax.net.ssl.SSLSocket import `in`.mohalla.paho.client.mqttv3.ConnectionSpec.Builder import `in`.mohalla.paho.client.mqttv3.internal.tls.CipherSuite import `in`.mohalla.paho.client.mqttv3.internal.tls.TlsVersion +import java.util.Arrays +import java.util.Objects +import javax.net.ssl.SSLSocket /** * Specifies configuration for the socket connection that HTTP traffic travels through. For `https:` diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Android10Platform.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Android10Platform.kt index 9fad3e89..b48928eb 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Android10Platform.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Android10Platform.kt @@ -19,9 +19,6 @@ import android.annotation.SuppressLint import android.os.Build import android.security.NetworkSecurityPolicy import android.util.CloseGuard -import javax.net.ssl.SSLSocket -import javax.net.ssl.SSLSocketFactory -import javax.net.ssl.X509TrustManager import `in`.mohalla.paho.client.mqttv3.Protocol import `in`.mohalla.paho.client.mqttv3.SuppressSignatureCheck import `in`.mohalla.paho.client.mqttv3.internal.platform.android.Android10SocketAdapter @@ -31,6 +28,9 @@ import `in`.mohalla.paho.client.mqttv3.internal.platform.android.BouncyCastleSoc import `in`.mohalla.paho.client.mqttv3.internal.platform.android.ConscryptSocketAdapter import `in`.mohalla.paho.client.mqttv3.internal.platform.android.DeferredSocketAdapter import `in`.mohalla.paho.client.mqttv3.internal.tls.CertificateChainCleaner +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager /** Android 29+. */ @SuppressSignatureCheck diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/AndroidPlatform.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/AndroidPlatform.kt index 08b71a47..86512470 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/AndroidPlatform.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/AndroidPlatform.kt @@ -17,16 +17,6 @@ package `in`.mohalla.paho.client.mqttv3.internal.platform import android.os.Build import android.security.NetworkSecurityPolicy -import java.io.IOException -import java.lang.reflect.InvocationTargetException -import java.lang.reflect.Method -import java.net.InetSocketAddress -import java.net.Socket -import java.security.cert.TrustAnchor -import java.security.cert.X509Certificate -import javax.net.ssl.SSLSocket -import javax.net.ssl.SSLSocketFactory -import javax.net.ssl.X509TrustManager import `in`.mohalla.paho.client.mqttv3.Protocol import `in`.mohalla.paho.client.mqttv3.SuppressSignatureCheck import `in`.mohalla.paho.client.mqttv3.internal.platform.android.AndroidCertificateChainCleaner @@ -37,6 +27,16 @@ import `in`.mohalla.paho.client.mqttv3.internal.platform.android.DeferredSocketA import `in`.mohalla.paho.client.mqttv3.internal.platform.android.StandardAndroidSocketAdapter import `in`.mohalla.paho.client.mqttv3.internal.tls.CertificateChainCleaner import `in`.mohalla.paho.client.mqttv3.internal.tls.TrustRootIndex +import java.io.IOException +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.net.InetSocketAddress +import java.net.Socket +import java.security.cert.TrustAnchor +import java.security.cert.X509Certificate +import javax.net.ssl.SSLSocket +import javax.net.ssl.SSLSocketFactory +import javax.net.ssl.X509TrustManager /** Android 5+. */ @SuppressSignatureCheck diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/BouncyCastlePlatform.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/BouncyCastlePlatform.kt index f5b6d137..f4c8e541 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/BouncyCastlePlatform.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/BouncyCastlePlatform.kt @@ -15,6 +15,7 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform +import `in`.mohalla.paho.client.mqttv3.Protocol import java.security.KeyStore import java.security.Provider import javax.net.ssl.SSLContext @@ -24,7 +25,6 @@ import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager import org.bouncycastle.jsse.BCSSLSocket import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider -import `in`.mohalla.paho.client.mqttv3.Protocol /** * Platform using BouncyCastle if installed as the first Security Provider. diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/ConscryptPlatform.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/ConscryptPlatform.kt index eb792e12..92580b16 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/ConscryptPlatform.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/ConscryptPlatform.kt @@ -15,6 +15,7 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform +import `in`.mohalla.paho.client.mqttv3.Protocol import java.security.KeyStore import java.security.Provider import java.security.cert.X509Certificate @@ -27,7 +28,6 @@ import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager import org.conscrypt.Conscrypt import org.conscrypt.ConscryptHostnameVerifier -import `in`.mohalla.paho.client.mqttv3.Protocol /** * Platform using Conscrypt (conscrypt.org) if installed as the first Security Provider. diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Jdk8WithJettyBootPlatform.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Jdk8WithJettyBootPlatform.kt index 9bbf192f..1df58949 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Jdk8WithJettyBootPlatform.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Jdk8WithJettyBootPlatform.kt @@ -15,12 +15,12 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform +import `in`.mohalla.paho.client.mqttv3.Protocol import java.lang.reflect.InvocationHandler import java.lang.reflect.InvocationTargetException import java.lang.reflect.Method import java.lang.reflect.Proxy import javax.net.ssl.SSLSocket -import `in`.mohalla.paho.client.mqttv3.Protocol /** OpenJDK 8 with `org.mortbay.jetty.alpn:alpn-boot` in the boot class path. */ class Jdk8WithJettyBootPlatform( diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Jdk9Platform.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Jdk9Platform.kt index 421726f4..2f1ba467 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Jdk9Platform.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Jdk9Platform.kt @@ -15,13 +15,13 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform +import `in`.mohalla.paho.client.mqttv3.Protocol +import `in`.mohalla.paho.client.mqttv3.SuppressSignatureCheck import java.security.NoSuchAlgorithmException import javax.net.ssl.SSLContext import javax.net.ssl.SSLSocket import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager -import `in`.mohalla.paho.client.mqttv3.Protocol -import `in`.mohalla.paho.client.mqttv3.SuppressSignatureCheck /** OpenJDK 9+. */ open class Jdk9Platform : Platform() { diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/OpenJSSEPlatform.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/OpenJSSEPlatform.kt index ee795a65..ebf1f59c 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/OpenJSSEPlatform.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/OpenJSSEPlatform.kt @@ -15,6 +15,7 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform +import `in`.mohalla.paho.client.mqttv3.Protocol import java.security.KeyStore import java.security.Provider import javax.net.ssl.SSLContext @@ -22,7 +23,6 @@ import javax.net.ssl.SSLSocket import javax.net.ssl.SSLSocketFactory import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager -import `in`.mohalla.paho.client.mqttv3.Protocol /** * Platform using OpenJSSE (https://github.com/openjsse/openjsse) if installed as the first diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Platform.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Platform.kt index 99af275f..ed285d81 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Platform.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/Platform.kt @@ -16,6 +16,12 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform +import `in`.mohalla.paho.client.mqttv3.Protocol +import `in`.mohalla.paho.client.mqttv3.internal.tls.BasicCertificateChainCleaner +import `in`.mohalla.paho.client.mqttv3.internal.tls.BasicTrustRootIndex +import `in`.mohalla.paho.client.mqttv3.internal.tls.CertificateChainCleaner +import `in`.mohalla.paho.client.mqttv3.internal.tls.TrustRootIndex +import `in`.mohalla.paho.client.mqttv3.readFieldOrNull import java.io.IOException import java.net.InetSocketAddress import java.net.Socket @@ -31,12 +37,6 @@ import javax.net.ssl.TrustManager import javax.net.ssl.TrustManagerFactory import javax.net.ssl.X509TrustManager import okio.Buffer -import `in`.mohalla.paho.client.mqttv3.Protocol -import `in`.mohalla.paho.client.mqttv3.internal.tls.BasicCertificateChainCleaner -import `in`.mohalla.paho.client.mqttv3.internal.tls.BasicTrustRootIndex -import `in`.mohalla.paho.client.mqttv3.internal.tls.CertificateChainCleaner -import `in`.mohalla.paho.client.mqttv3.internal.tls.TrustRootIndex -import `in`.mohalla.paho.client.mqttv3.readFieldOrNull /** * Access to platform-specific features. diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/Android10SocketAdapter.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/Android10SocketAdapter.kt index 2be18362..22b1c3e8 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/Android10SocketAdapter.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/Android10SocketAdapter.kt @@ -18,12 +18,12 @@ package `in`.mohalla.paho.client.mqttv3.internal.platform.android import android.annotation.SuppressLint import android.net.ssl.SSLSockets import android.os.Build -import java.io.IOException -import javax.net.ssl.SSLSocket import `in`.mohalla.paho.client.mqttv3.Protocol import `in`.mohalla.paho.client.mqttv3.SuppressSignatureCheck import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform.Companion.isAndroid +import java.io.IOException +import javax.net.ssl.SSLSocket /** * Simple non-reflection SocketAdapter for Android Q+. diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/AndroidCertificateChainCleaner.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/AndroidCertificateChainCleaner.kt index 14f94a7d..f7f1759f 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/AndroidCertificateChainCleaner.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/AndroidCertificateChainCleaner.kt @@ -16,13 +16,13 @@ package `in`.mohalla.paho.client.mqttv3.internal.platform.android import android.net.http.X509TrustManagerExtensions +import `in`.mohalla.paho.client.mqttv3.SuppressSignatureCheck +import `in`.mohalla.paho.client.mqttv3.internal.tls.CertificateChainCleaner import java.security.cert.Certificate import java.security.cert.CertificateException import java.security.cert.X509Certificate import javax.net.ssl.SSLPeerUnverifiedException import javax.net.ssl.X509TrustManager -import `in`.mohalla.paho.client.mqttv3.SuppressSignatureCheck -import `in`.mohalla.paho.client.mqttv3.internal.tls.CertificateChainCleaner /** * Android implementation of CertificateChainCleaner using direct Android API calls. diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt index 2b7c4bda..20881154 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/AndroidSocketAdapter.kt @@ -15,13 +15,13 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform.android -import java.lang.reflect.InvocationTargetException -import java.lang.reflect.Method -import javax.net.ssl.SSLSocket import `in`.mohalla.paho.client.mqttv3.Protocol import `in`.mohalla.paho.client.mqttv3.internal.platform.AndroidPlatform import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform import `in`.mohalla.paho.client.mqttv3.internal.platform.android.DeferredSocketAdapter.Factory +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import javax.net.ssl.SSLSocket /** * Modern reflection based SocketAdapter for Conscrypt class SSLSockets. diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/BouncyCastleSocketAdapter.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/BouncyCastleSocketAdapter.kt index 0ea18009..98c57083 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/BouncyCastleSocketAdapter.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/BouncyCastleSocketAdapter.kt @@ -15,12 +15,12 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform.android -import javax.net.ssl.SSLSocket -import org.bouncycastle.jsse.BCSSLSocket import `in`.mohalla.paho.client.mqttv3.Protocol import `in`.mohalla.paho.client.mqttv3.internal.platform.BouncyCastlePlatform import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform import `in`.mohalla.paho.client.mqttv3.internal.platform.android.DeferredSocketAdapter.Factory +import javax.net.ssl.SSLSocket +import org.bouncycastle.jsse.BCSSLSocket /** * Simple non-reflection SocketAdapter for BouncyCastle. diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/ConscryptSocketAdapter.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/ConscryptSocketAdapter.kt index 80cac921..31beee52 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/ConscryptSocketAdapter.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/ConscryptSocketAdapter.kt @@ -15,12 +15,12 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform.android -import javax.net.ssl.SSLSocket -import org.conscrypt.Conscrypt import `in`.mohalla.paho.client.mqttv3.Protocol import `in`.mohalla.paho.client.mqttv3.internal.platform.ConscryptPlatform import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform import `in`.mohalla.paho.client.mqttv3.internal.platform.android.DeferredSocketAdapter.Factory +import javax.net.ssl.SSLSocket +import org.conscrypt.Conscrypt /** * Simple non-reflection SocketAdapter for Conscrypt when included as an application dependency diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/DeferredSocketAdapter.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/DeferredSocketAdapter.kt index b9518b88..79d493c1 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/DeferredSocketAdapter.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/DeferredSocketAdapter.kt @@ -15,8 +15,8 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform.android -import javax.net.ssl.SSLSocket import `in`.mohalla.paho.client.mqttv3.Protocol +import javax.net.ssl.SSLSocket /** * Deferred implementation of SocketAdapter that works by observing the socket diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/SocketAdapter.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/SocketAdapter.kt index cd97fa12..6ac47f9c 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/SocketAdapter.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/SocketAdapter.kt @@ -15,10 +15,10 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform.android +import `in`.mohalla.paho.client.mqttv3.Protocol import javax.net.ssl.SSLSocket import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager -import `in`.mohalla.paho.client.mqttv3.Protocol interface SocketAdapter { fun isSupported(): Boolean diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/StandardAndroidSocketAdapter.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/StandardAndroidSocketAdapter.kt index 1b54af05..8f26eadd 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/StandardAndroidSocketAdapter.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/platform/android/StandardAndroidSocketAdapter.kt @@ -15,11 +15,11 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.platform.android +import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform +import `in`.mohalla.paho.client.mqttv3.readFieldOrNull import javax.net.ssl.SSLSocket import javax.net.ssl.SSLSocketFactory import javax.net.ssl.X509TrustManager -import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform -import `in`.mohalla.paho.client.mqttv3.readFieldOrNull /** * Base Android reflection based SocketAdapter for the built in Android SSLSocket. diff --git a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/tls/CertificateChainCleaner.kt b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/tls/CertificateChainCleaner.kt index f70b3825..db7f9430 100644 --- a/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/tls/CertificateChainCleaner.kt +++ b/paho/src/main/java/in/mohalla/paho/client/mqttv3/internal/tls/CertificateChainCleaner.kt @@ -16,11 +16,11 @@ */ package `in`.mohalla.paho.client.mqttv3.internal.tls +import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform import java.security.cert.Certificate import java.security.cert.X509Certificate import javax.net.ssl.SSLPeerUnverifiedException import javax.net.ssl.X509TrustManager -import `in`.mohalla.paho.client.mqttv3.internal.platform.Platform /** * Computes the effective certificate chain from the raw array returned by Java's built in TLS APIs. diff --git a/pingsender/timer-pingsender/src/main/java/com/gojek/timer/pingsender/TimerPingSender.kt b/pingsender/timer-pingsender/src/main/java/com/gojek/timer/pingsender/TimerPingSender.kt index 4a1d804d..04e14fa9 100644 --- a/pingsender/timer-pingsender/src/main/java/com/gojek/timer/pingsender/TimerPingSender.kt +++ b/pingsender/timer-pingsender/src/main/java/com/gojek/timer/pingsender/TimerPingSender.kt @@ -7,12 +7,12 @@ import com.gojek.courier.utils.Clock import com.gojek.mqtt.pingsender.IPingSenderEvents import com.gojek.mqtt.pingsender.MqttPingSender import com.gojek.mqtt.pingsender.NoOpPingSenderEvents -import java.util.Timer -import java.util.TimerTask import `in`.mohalla.paho.client.mqttv3.ILogger import `in`.mohalla.paho.client.mqttv3.IMqttActionListener import `in`.mohalla.paho.client.mqttv3.IMqttToken import `in`.mohalla.paho.client.mqttv3.internal.ClientComms +import java.util.Timer +import java.util.TimerTask /** * Default ping sender implementation diff --git a/pingsender/timer-pingsender/src/test/java/com/gojek/timer/pingsender/TimerPingSenderTest.kt b/pingsender/timer-pingsender/src/test/java/com/gojek/timer/pingsender/TimerPingSenderTest.kt index 1e5e7313..e297698b 100644 --- a/pingsender/timer-pingsender/src/test/java/com/gojek/timer/pingsender/TimerPingSenderTest.kt +++ b/pingsender/timer-pingsender/src/test/java/com/gojek/timer/pingsender/TimerPingSenderTest.kt @@ -8,14 +8,14 @@ import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import java.util.Timer -import java.util.concurrent.TimeUnit -import kotlin.test.assertEquals import `in`.mohalla.paho.client.mqttv3.ILogger import `in`.mohalla.paho.client.mqttv3.IMqttActionListener import `in`.mohalla.paho.client.mqttv3.IMqttAsyncClient import `in`.mohalla.paho.client.mqttv3.MqttToken -import `in`.mohalla.phao.client.mqttv3.internal.ClientComms +import `in`.mohalla.paho.client.mqttv3.internal.ClientComms +import java.util.Timer +import java.util.concurrent.TimeUnit +import kotlin.test.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith diff --git a/pingsender/workmanager-2.6.0-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderAdaptiveTest.kt b/pingsender/workmanager-2.6.0-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderAdaptiveTest.kt index 5d3cd384..b2bc0ea1 100644 --- a/pingsender/workmanager-2.6.0-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderAdaptiveTest.kt +++ b/pingsender/workmanager-2.6.0-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderAdaptiveTest.kt @@ -9,14 +9,14 @@ import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import java.util.concurrent.TimeUnit -import kotlin.test.assertFalse -import kotlin.test.assertTrue import `in`.mohalla.paho.client.mqttv3.ILogger import `in`.mohalla.paho.client.mqttv3.IMqttActionListener import `in`.mohalla.paho.client.mqttv3.IMqttAsyncClient import `in`.mohalla.paho.client.mqttv3.MqttToken -import `in`.mohalla.phao.client.mqttv3.internal.ClientComms +import `in`.mohalla.paho.client.mqttv3.internal.ClientComms +import java.util.concurrent.TimeUnit +import kotlin.test.assertFalse +import kotlin.test.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith diff --git a/pingsender/workmanager-2.6.0-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderTest.kt b/pingsender/workmanager-2.6.0-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderTest.kt index a14f462e..f6b24bae 100644 --- a/pingsender/workmanager-2.6.0-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderTest.kt +++ b/pingsender/workmanager-2.6.0-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderTest.kt @@ -6,14 +6,14 @@ import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import java.util.concurrent.TimeUnit -import kotlin.test.assertFalse -import kotlin.test.assertTrue import `in`.mohalla.paho.client.mqttv3.ILogger import `in`.mohalla.paho.client.mqttv3.IMqttActionListener import `in`.mohalla.paho.client.mqttv3.IMqttAsyncClient import `in`.mohalla.paho.client.mqttv3.MqttToken -import `in`.mohalla.phao.client.mqttv3.internal.ClientComms +import `in`.mohalla.paho.client.mqttv3.internal.ClientComms +import java.util.concurrent.TimeUnit +import kotlin.test.assertFalse +import kotlin.test.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith diff --git a/pingsender/workmanager-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderAdaptiveTest.kt b/pingsender/workmanager-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderAdaptiveTest.kt index 5d3cd384..b2bc0ea1 100644 --- a/pingsender/workmanager-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderAdaptiveTest.kt +++ b/pingsender/workmanager-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderAdaptiveTest.kt @@ -9,14 +9,14 @@ import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import java.util.concurrent.TimeUnit -import kotlin.test.assertFalse -import kotlin.test.assertTrue import `in`.mohalla.paho.client.mqttv3.ILogger import `in`.mohalla.paho.client.mqttv3.IMqttActionListener import `in`.mohalla.paho.client.mqttv3.IMqttAsyncClient import `in`.mohalla.paho.client.mqttv3.MqttToken -import `in`.mohalla.phao.client.mqttv3.internal.ClientComms +import `in`.mohalla.paho.client.mqttv3.internal.ClientComms +import java.util.concurrent.TimeUnit +import kotlin.test.assertFalse +import kotlin.test.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith diff --git a/pingsender/workmanager-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderTest.kt b/pingsender/workmanager-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderTest.kt index a14f462e..f6b24bae 100644 --- a/pingsender/workmanager-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderTest.kt +++ b/pingsender/workmanager-pingsender/src/test/java/com/gojek/workmanager/pingsender/WorkManagerPingSenderTest.kt @@ -6,14 +6,14 @@ import com.nhaarman.mockitokotlin2.argumentCaptor import com.nhaarman.mockitokotlin2.mock import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import java.util.concurrent.TimeUnit -import kotlin.test.assertFalse -import kotlin.test.assertTrue import `in`.mohalla.paho.client.mqttv3.ILogger import `in`.mohalla.paho.client.mqttv3.IMqttActionListener import `in`.mohalla.paho.client.mqttv3.IMqttAsyncClient import `in`.mohalla.paho.client.mqttv3.MqttToken -import `in`.mohalla.phao.client.mqttv3.internal.ClientComms +import `in`.mohalla.paho.client.mqttv3.internal.ClientComms +import java.util.concurrent.TimeUnit +import kotlin.test.assertFalse +import kotlin.test.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith From ec7b45bf6f6c076ed09ab51dd5bd2b92d523a11b Mon Sep 17 00:00:00 2001 From: ChandoraAnkit Date: Fri, 31 Mar 2023 11:39:47 +0530 Subject: [PATCH 19/25] Fix APIs contract issues --- mqtt-client/api/mqtt-client.api | 152 ++++++++++-------- .../mqtt-pingsender/api/mqtt-pingsender.api | 2 +- 2 files changed, 85 insertions(+), 69 deletions(-) diff --git a/mqtt-client/api/mqtt-client.api b/mqtt-client/api/mqtt-client.api index acf9fa4c..b4972b29 100644 --- a/mqtt-client/api/mqtt-client.api +++ b/mqtt-client/api/mqtt-client.api @@ -418,13 +418,14 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttConnectionLostEvent : com/ } public final class com/gojek/mqtt/event/MqttEvent$MqttDisconnectCompleteEvent : com/gojek/mqtt/event/MqttEvent { - public fun ()V - public fun (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectCompleteEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectCompleteEvent;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectCompleteEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectCompleteEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectCompleteEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectCompleteEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public fun hashCode ()I public fun setConnectionInfo (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V @@ -432,13 +433,14 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttDisconnectCompleteEvent : } public final class com/gojek/mqtt/event/MqttEvent$MqttDisconnectEvent : com/gojek/mqtt/event/MqttEvent { - public fun ()V - public fun (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectEvent;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public fun hashCode ()I public fun setConnectionInfo (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V @@ -446,13 +448,14 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttDisconnectEvent : com/goje } public final class com/gojek/mqtt/event/MqttEvent$MqttDisconnectStartEvent : com/gojek/mqtt/event/MqttEvent { - public fun ()V - public fun (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectStartEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectStartEvent;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectStartEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectStartEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectStartEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttDisconnectStartEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public fun hashCode ()I public fun setConnectionInfo (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V @@ -673,13 +676,14 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttPingSuccessEvent : com/goj } public final class com/gojek/mqtt/event/MqttEvent$MqttReconnectEvent : com/gojek/mqtt/event/MqttEvent { - public fun ()V - public fun (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttReconnectEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttReconnectEvent;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttReconnectEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttReconnectEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttReconnectEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttReconnectEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public fun hashCode ()I public fun setConnectionInfo (Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V @@ -687,13 +691,15 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttReconnectEvent : com/gojek } public final class com/gojek/mqtt/event/MqttEvent$MqttSubscribeAttemptEvent : com/gojek/mqtt/event/MqttEvent { - public fun (Ljava/util/Map;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Ljava/util/Map;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/Map; - public final fun component2 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Ljava/util/Map;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeAttemptEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeAttemptEvent;Ljava/util/Map;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeAttemptEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Ljava/util/Map; + public final fun component3 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeAttemptEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeAttemptEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeAttemptEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public final fun getTopics ()Ljava/util/Map; public fun hashCode ()I @@ -702,15 +708,17 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttSubscribeAttemptEvent : co } public final class com/gojek/mqtt/event/MqttEvent$MqttSubscribeFailureEvent : com/gojek/mqtt/event/MqttEvent { - public fun (Ljava/util/Map;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Ljava/util/Map;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/Map; - public final fun component2 ()Lcom/gojek/mqtt/exception/CourierException; - public final fun component3 ()J - public final fun component4 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Ljava/util/Map;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeFailureEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeFailureEvent;Ljava/util/Map;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeFailureEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Ljava/util/Map; + public final fun component3 ()Lcom/gojek/mqtt/exception/CourierException; + public final fun component4 ()J + public final fun component5 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeFailureEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeFailureEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeFailureEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public final fun getException ()Lcom/gojek/mqtt/exception/CourierException; public final fun getTimeTakenMillis ()J @@ -721,14 +729,16 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttSubscribeFailureEvent : co } public final class com/gojek/mqtt/event/MqttEvent$MqttSubscribeSuccessEvent : com/gojek/mqtt/event/MqttEvent { - public fun (Ljava/util/Map;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Ljava/util/Map;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/Map; - public final fun component2 ()J - public final fun component3 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Ljava/util/Map;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeSuccessEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeSuccessEvent;Ljava/util/Map;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeSuccessEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Ljava/util/Map; + public final fun component3 ()J + public final fun component4 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeSuccessEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeSuccessEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Map;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttSubscribeSuccessEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public final fun getTimeTakenMillis ()J public final fun getTopics ()Ljava/util/Map; @@ -738,13 +748,15 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttSubscribeSuccessEvent : co } public final class com/gojek/mqtt/event/MqttEvent$MqttUnsubscribeAttemptEvent : com/gojek/mqtt/event/MqttEvent { - public fun (Ljava/util/Set;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Ljava/util/Set;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/Set; - public final fun component2 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Ljava/util/Set;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeAttemptEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeAttemptEvent;Ljava/util/Set;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeAttemptEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Ljava/util/Set; + public final fun component3 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeAttemptEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeAttemptEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeAttemptEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public final fun getTopics ()Ljava/util/Set; public fun hashCode ()I @@ -753,15 +765,17 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttUnsubscribeAttemptEvent : } public final class com/gojek/mqtt/event/MqttEvent$MqttUnsubscribeFailureEvent : com/gojek/mqtt/event/MqttEvent { - public fun (Ljava/util/Set;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Ljava/util/Set;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/Set; - public final fun component2 ()Lcom/gojek/mqtt/exception/CourierException; - public final fun component3 ()J - public final fun component4 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Ljava/util/Set;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeFailureEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeFailureEvent;Ljava/util/Set;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeFailureEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Ljava/util/Set; + public final fun component3 ()Lcom/gojek/mqtt/exception/CourierException; + public final fun component4 ()J + public final fun component5 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeFailureEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeFailureEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;Lcom/gojek/mqtt/exception/CourierException;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeFailureEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public final fun getException ()Lcom/gojek/mqtt/exception/CourierException; public final fun getTimeTakenMillis ()J @@ -772,14 +786,16 @@ public final class com/gojek/mqtt/event/MqttEvent$MqttUnsubscribeFailureEvent : } public final class com/gojek/mqtt/event/MqttEvent$MqttUnsubscribeSuccessEvent : com/gojek/mqtt/event/MqttEvent { - public fun (Ljava/util/Set;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V - public synthetic fun (Ljava/util/Set;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/Set; - public final fun component2 ()J - public final fun component3 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; - public final fun copy (Ljava/util/Set;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeSuccessEvent; - public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeSuccessEvent;Ljava/util/Set;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeSuccessEvent; + public fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)V + public synthetic fun (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/gojek/mqtt/network/ActiveNetInfo; + public final fun component2 ()Ljava/util/Set; + public final fun component3 ()J + public final fun component4 ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; + public final fun copy (Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeSuccessEvent; + public static synthetic fun copy$default (Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeSuccessEvent;Lcom/gojek/mqtt/network/ActiveNetInfo;Ljava/util/Set;JLcom/gojek/mqtt/client/connectioninfo/ConnectionInfo;ILjava/lang/Object;)Lcom/gojek/mqtt/event/MqttEvent$MqttUnsubscribeSuccessEvent; public fun equals (Ljava/lang/Object;)Z + public final fun getActiveNetInfo ()Lcom/gojek/mqtt/network/ActiveNetInfo; public fun getConnectionInfo ()Lcom/gojek/mqtt/client/connectioninfo/ConnectionInfo; public final fun getTimeTakenMillis ()J public final fun getTopics ()Ljava/util/Set; @@ -1030,7 +1046,7 @@ public final class com/gojek/mqtt/model/MqttConnectOptions { public static final field Companion Lcom/gojek/mqtt/model/MqttConnectOptions$Companion; public synthetic fun (Lcom/gojek/mqtt/model/MqttConnectOptions$Builder;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getClientId ()Ljava/lang/String; - public final fun getConnectionSpec ()Lorg/eclipse/paho/client/mqttv3/ConnectionSpec; + public final fun getConnectionSpec ()Lin/mohalla/paho/client/mqttv3/ConnectionSpec; public final fun getKeepAlive ()Lcom/gojek/mqtt/model/KeepAlive; public final fun getPassword ()Ljava/lang/String; public final fun getProtocols ()Ljava/util/List; @@ -1052,7 +1068,7 @@ public final class com/gojek/mqtt/model/MqttConnectOptions$Builder { public final fun build ()Lcom/gojek/mqtt/model/MqttConnectOptions; public final fun cleanSession (Z)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; public final fun clientId (Ljava/lang/String;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; - public final fun connectionSpec (Lorg/eclipse/paho/client/mqttv3/ConnectionSpec;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; + public final fun connectionSpec (Lin/mohalla/paho/client/mqttv3/ConnectionSpec;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; public final fun keepAlive (Lcom/gojek/mqtt/model/KeepAlive;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; public final fun mqttVersion (Lcom/gojek/mqtt/model/MqttVersion;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; public final fun password (Ljava/lang/String;)Lcom/gojek/mqtt/model/MqttConnectOptions$Builder; diff --git a/pingsender/mqtt-pingsender/api/mqtt-pingsender.api b/pingsender/mqtt-pingsender/api/mqtt-pingsender.api index 7debc9dc..ded64793 100644 --- a/pingsender/mqtt-pingsender/api/mqtt-pingsender.api +++ b/pingsender/mqtt-pingsender/api/mqtt-pingsender.api @@ -5,7 +5,7 @@ public final class com/gojek/mqtt/pingsender/KeepAliveKt { } public abstract interface class com/gojek/mqtt/pingsender/MqttPingSender { - public abstract fun init (Lorg/eclipse/paho/client/mqttv3/internal/ClientComms;Lorg/eclipse/paho/client/mqttv3/ILogger;)V + public abstract fun init (Lin/mohalla/paho/client/mqttv3/internal/ClientComms;Lin/mohalla/paho/client/mqttv3/ILogger;)V public abstract fun schedule (J)V public abstract fun start ()V public abstract fun stop ()V From 692bb33f00bb16b63710806dad20cf7214230471 Mon Sep 17 00:00:00 2001 From: ChandoraAnkit Date: Fri, 31 Mar 2023 14:47:25 +0530 Subject: [PATCH 20/25] Update gitVersionName to 0.2.0 --- gradle/script-ext.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/script-ext.gradle b/gradle/script-ext.gradle index fb332678..76cbe66f 100644 --- a/gradle/script-ext.gradle +++ b/gradle/script-ext.gradle @@ -1,5 +1,5 @@ ext { gitVersionCode = ""//grgit.tag.list().size() - gitVersionName = "0.1.0"//grgit.describe(tags: true, always: true) + gitVersionName = "0.2.0"//grgit.describe(tags: true, always: true) println("version : $gitVersionName") } From 89b433dbcbc70f850231beb17605d9e6061dadce Mon Sep 17 00:00:00 2001 From: Anubhav Gupta Date: Tue, 11 Apr 2023 14:51:22 +0530 Subject: [PATCH 21/25] add new functionality for qos1 without persistence (#62) * add new functionality for qos1 without persistence * fixed review comments and handled case for removal of msgids for qos1 without persistence messages * fixed review comment * updated docs * change from entry to subscription flag model * removed unused import * review comments * fixed spotless --- .../mapper/MqttTransactionUiModelMapper.kt | 2 +- courier-core/api/courier-core.api | 3 ++ .../src/main/java/com/gojek/courier/QoS.kt | 22 +++++++++-- docs/docs/QoS.md | 15 +++++++- .../gojek/mqtt/client/model/MqttSendPacket.kt | 7 +++- .../mqtt/client/v3/impl/AndroidMqttClient.kt | 5 ++- .../gojek/mqtt/connection/IMqttConnection.kt | 2 +- .../gojek/mqtt/connection/MqttConnection.kt | 37 +++++++++++++++---- .../paho/client/mqttv3/IMqttAsyncClient.java | 8 ++++ .../paho/client/mqttv3/MqttAsyncClient.java | 32 ++++++++++++++-- .../paho/client/mqttv3/MqttMessage.java | 12 ++++++ .../client/mqttv3/internal/ClientState.java | 35 +++++++++++++----- .../client/mqttv3/internal/CommsSender.java | 10 ++++- .../mqttv3/internal/wire/MqttPublish.java | 7 ++-- .../mqttv3/internal/wire/MqttSubscribe.java | 26 +++++++++++-- .../mqttv3/internal/wire/SubscribeFlags.java | 21 +++++++++++ 16 files changed, 205 insertions(+), 39 deletions(-) create mode 100644 paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/SubscribeFlags.java diff --git a/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt b/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt index c23ed6f9..eefe30de 100644 --- a/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt +++ b/chuck-mqtt/src/main/java/com/gojek/chuckmqtt/internal/presentation/mapper/MqttTransactionUiModelMapper.kt @@ -202,7 +202,7 @@ internal class MqttTransactionUiModelMapper : Mapper { sb.append("PUBLISH \n") sb.append("Qos : ${mqttWireMessage.message.qos} \n") - if (mqttWireMessage.message.qos > 0) { + if (mqttWireMessage.message.qos > 0 || mqttWireMessage.message.type > 2) { sb.append("MsgId : ${mqttWireMessage.messageId} \n") } sb.append("Retained : ${mqttWireMessage.message.isRetained} \n") diff --git a/courier-core/api/courier-core.api b/courier-core/api/courier-core.api index e4ca55c0..edccd6e2 100644 --- a/courier-core/api/courier-core.api +++ b/courier-core/api/courier-core.api @@ -19,8 +19,11 @@ public abstract interface class com/gojek/courier/MessageAdapter$Factory { public final class com/gojek/courier/QoS : java/lang/Enum { public static final field ONE Lcom/gojek/courier/QoS; + public static final field ONE_WITHOUT_PERSISTENCE_AND_NO_RETRY Lcom/gojek/courier/QoS; + public static final field ONE_WITHOUT_PERSISTENCE_AND_RETRY Lcom/gojek/courier/QoS; public static final field TWO Lcom/gojek/courier/QoS; public static final field ZERO Lcom/gojek/courier/QoS; + public final fun getType ()I public final fun getValue ()I public static fun valueOf (Ljava/lang/String;)Lcom/gojek/courier/QoS; public static fun values ()[Lcom/gojek/courier/QoS; diff --git a/courier-core/src/main/java/com/gojek/courier/QoS.kt b/courier-core/src/main/java/com/gojek/courier/QoS.kt index 5fcf5167..5bda5549 100644 --- a/courier-core/src/main/java/com/gojek/courier/QoS.kt +++ b/courier-core/src/main/java/com/gojek/courier/QoS.kt @@ -1,7 +1,21 @@ package com.gojek.courier -enum class QoS(val value: Int) { - ZERO(0), - ONE(1), - TWO(2) +enum class QoS(val value: Int, val type: Int) { + // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718100 + ZERO(0, 0), + + // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718100 + ONE(1, 1), + + // http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718100 + TWO(2, 2), + + /** Like QoS1, Message delivery is acknowledged with PUBACK, but unlike QoS1 messages are + neither persisted nor retried at send after one attempt. + The message arrives at the receiver either once or not at all **/ + ONE_WITHOUT_PERSISTENCE_AND_NO_RETRY(0, 3), + + /** Like QoS1, Message delivery is acknowledged with PUBACK, but unlike QoS1 messages are + not persisted. The messages are retried within active connection if delivery is not acknowledged.**/ + ONE_WITHOUT_PERSISTENCE_AND_RETRY(0, 4) } diff --git a/docs/docs/QoS.md b/docs/docs/QoS.md index f9f78784..60aacb7b 100644 --- a/docs/docs/QoS.md +++ b/docs/docs/QoS.md @@ -11,4 +11,17 @@ When you talk about QoS in MQTT, you need to consider the two sides of message d - Message delivery form the publishing client to the broker. - Message delivery from the broker to the subscribing client. -You can read more about the detail of QoS in MQTT from [HiveMQ site](https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels/). \ No newline at end of file +You can read more about the detail of QoS in MQTT from [HiveMQ site](https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels/). + +:warning: ** +These are non standard QoS options. You need to have compatible broker to use these QoS options + +We added two more Qos options + +- QoS1 with no persistence and no retry: Like QoS1, Message delivery is acknowledged with PUBACK, but unlike QoS1 messages are + neither persisted nor retried at send after one attempt. The message arrives at the receiver either once or not at all +- QoS1 with no persistence and with retry: Like QoS1, Message delivery is acknowledged with PUBACK, but unlike QoS1 messages are + not persisted. The messages are retried within active connection if delivery is not acknowledged. + + Note: Both these Qos options have same behaviour (without retry) during publish and +different behaviour for subscribe \ No newline at end of file diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/model/MqttSendPacket.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/model/MqttSendPacket.kt index 3382b130..50c96220 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/model/MqttSendPacket.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/model/MqttSendPacket.kt @@ -8,14 +8,16 @@ internal data class MqttSendPacket( var messageId: Long, var timestamp: Long, var qos: Int, - var topic: String + var topic: String, + var type: Int ) : Parcelable { constructor(parcel: Parcel) : this( parcel.createByteArray()!!, parcel.readLong(), parcel.readLong(), parcel.readInt(), - parcel.readString()!! + parcel.readString()!!, + parcel.readInt() ) override fun writeToParcel(parcel: Parcel, flags: Int) { @@ -24,6 +26,7 @@ internal data class MqttSendPacket( parcel.writeLong(timestamp) parcel.writeInt(qos) parcel.writeString(topic) + parcel.writeInt(type) } override fun describeContents(): Int { diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt index 65209a5f..92f498b4 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt @@ -251,7 +251,7 @@ internal class AndroidMqttClient( MqttMessageSendEvent(topic, qos, message.size) ) } - mqttConnection.publish(mqttPacket, mqttPacket.qos, mqttPacket.topic) + mqttConnection.publish(mqttPacket) } catch (e: MqttPersistenceException) { with(mqttPacket) { eventHandler.onEvent( @@ -297,7 +297,8 @@ internal class AndroidMqttClient( 0, System.currentTimeMillis(), mqttPacket.qos.value, - mqttPacket.topic + mqttPacket.topic, + mqttPacket.qos.type ) val msg = Message.obtain() diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/connection/IMqttConnection.kt b/mqtt-client/src/main/java/com/gojek/mqtt/connection/IMqttConnection.kt index 6cd20814..790f3baf 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/connection/IMqttConnection.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/connection/IMqttConnection.kt @@ -17,7 +17,7 @@ internal interface IMqttConnection { fun subscribe(topicMap: Map) fun unsubscribe(topics: Set) - fun publish(mqttPacket: MqttSendPacket, qos: Int, topic: String) + fun publish(mqttPacket: MqttSendPacket) fun disconnect() diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/connection/MqttConnection.kt b/mqtt-client/src/main/java/com/gojek/mqtt/connection/MqttConnection.kt index 804bb047..97761d6c 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/connection/MqttConnection.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/connection/MqttConnection.kt @@ -3,6 +3,8 @@ package com.gojek.mqtt.connection import android.content.Context import android.os.SystemClock import com.gojek.courier.QoS +import com.gojek.courier.QoS.ONE_WITHOUT_PERSISTENCE_AND_NO_RETRY +import com.gojek.courier.QoS.ONE_WITHOUT_PERSISTENCE_AND_RETRY import com.gojek.courier.extensions.fromNanosToMillis import com.gojek.courier.logging.ILogger import com.gojek.courier.utils.Clock @@ -44,6 +46,7 @@ import org.eclipse.paho.client.mqttv3.MqttException.REASON_CODE_UNEXPECTED_ERROR import org.eclipse.paho.client.mqttv3.MqttMessage import org.eclipse.paho.client.mqttv3.MqttSecurityException import org.eclipse.paho.client.mqttv3.internal.wire.MqttSuback +import org.eclipse.paho.client.mqttv3.internal.wire.SubscribeFlags import org.eclipse.paho.client.mqttv3.internal.wire.UserProperty internal class MqttConnection( @@ -224,16 +227,15 @@ internal class MqttConnection( } override fun publish( - mqttPacket: MqttSendPacket, - qos: Int, - topic: String + mqttPacket: MqttSendPacket ) { logger.d(TAG, "Current inflight msg count : " + mqtt!!.inflightMessages) - mqtt!!.publish( - topic, + mqtt!!.publishWithNewType( + mqttPacket.topic, mqttPacket.message, - qos, + mqttPacket.qos, + mqttPacket.type, false, mqttPacket, object : IMqttActionListenerNew { @@ -465,16 +467,35 @@ internal class MqttConnection( if (topicMap.isNotEmpty()) { val topicArray: Array = topicMap.keys.toTypedArray() val qosArray = IntArray(topicMap.size) + val subscribeFlagList = ArrayList(topicMap.size) for ((index, qos) in topicMap.values.withIndex()) { - qosArray[index] = qos.value + if (qos == ONE_WITHOUT_PERSISTENCE_AND_NO_RETRY || qos == ONE_WITHOUT_PERSISTENCE_AND_RETRY) { + qosArray[index] = 1 + } else { + qosArray[index] = qos.value + } + } + for ((index, qos) in topicMap.values.withIndex()) { + when (qos) { + ONE_WITHOUT_PERSISTENCE_AND_NO_RETRY -> { + subscribeFlagList.add(index, SubscribeFlags(false, false)) + } + ONE_WITHOUT_PERSISTENCE_AND_RETRY -> { + subscribeFlagList.add(index, SubscribeFlags(false, true)) + } + else -> { + subscribeFlagList.add(index, SubscribeFlags(true, true)) + } + } } val subscribeStartTime = clock.nanoTime() try { logger.d(TAG, "Subscribing to topics: ${topicMap.keys}") connectionConfig.connectionEventHandler.onMqttSubscribeAttempt(topicMap) - mqtt!!.subscribe( + mqtt!!.subscribeWithPersistableRetryableFlags( topicArray, qosArray, + subscribeFlagList, MqttContext(subscribeStartTime), getSubscribeListener(topicMap) ) diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java index 79cf4e0f..698ea5fb 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/IMqttAsyncClient.java @@ -1,5 +1,10 @@ package org.eclipse.paho.client.mqttv3; +import org.eclipse.paho.client.mqttv3.internal.wire.SubscribeFlags; + +import java.util.List; +import java.util.Map; + /** * Enables an application to communicate with an MQTT server using non-blocking methods. *

    @@ -616,6 +621,9 @@ public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean */ public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) throws MqttException; + public IMqttToken subscribeWithPersistableRetryableFlags(String[] topicFilters, int[] qos, + List subscribeFlagsList, Object userContext, IMqttActionListener callback) throws MqttException; + /** * Requests the server unsubscribe the client from a topic. * diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java index 45a8bf38..03cade6a 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttAsyncClient.java @@ -34,13 +34,18 @@ import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; import org.eclipse.paho.client.mqttv3.internal.wire.MqttSubscribe; import org.eclipse.paho.client.mqttv3.internal.wire.MqttUnsubscribe; +import org.eclipse.paho.client.mqttv3.internal.wire.SubscribeFlags; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import org.eclipse.paho.client.mqttv3.persist.MqttDefaultFilePersistence; import javax.net.SocketFactory; import javax.net.ssl.SSLSocketFactory; +import java.util.AbstractMap; +import java.util.AbstractMap.SimpleEntry; +import java.util.ArrayList; import java.util.Hashtable; import java.util.List; +import java.util.Map; import java.util.Properties; @@ -913,8 +918,22 @@ public IMqttToken subscribe(String[] topicFilters, int[] qos) throws MqttExcepti * * @see org.eclipse.paho.client.mqttv3.IMqttAsyncClient#subscribe(java.lang.String[], int[], java.lang.Object, org.eclipse.paho.client.mqttv3.IMqttActionListener) */ - public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) throws MqttException - { + + public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext, IMqttActionListener callback) throws MqttException { + List subscribeFlagsList = new ArrayList<>(); + for(int i = 0; i < qos.length ; i++) { + subscribeFlagsList.add(new SubscribeFlags(true, true)); + } + return subscribeWithPersistableRetryableFlags(topicFilters, qos, subscribeFlagsList, userContext, callback); + } + + public IMqttToken subscribeWithPersistableRetryableFlags( + String[] topicFilters, + int[] qos, + List subscribeFlagsList, + Object userContext, + IMqttActionListener callback + ) throws MqttException { final String methodName = "subscribe"; if (topicFilters.length != qos.length) @@ -942,7 +961,7 @@ public IMqttToken subscribe(String[] topicFilters, int[] qos, Object userContext token.setUserContext(userContext); token.internalTok.setTopics(topicFilters); - MqttSubscribe register = new MqttSubscribe(topicFilters, qos); + MqttSubscribe register = new MqttSubscribe(topicFilters, qos, subscribeFlagsList); comms.sendNoWait(register, token); // @TRACE 109=< @@ -1062,9 +1081,16 @@ public IMqttDeliveryToken[] getPendingDeliveryTokens() */ public IMqttDeliveryToken publish(String topic, byte[] payload, int qos, boolean retained, Object userContext, IMqttActionListener callback) throws MqttException, MqttPersistenceException + { + return this.publishWithNewType(topic, payload, qos, qos, retained, userContext, callback); + } + + public IMqttDeliveryToken publishWithNewType(String topic, byte[] payload, int qos, int type, boolean retained, Object userContext, IMqttActionListener callback) throws MqttException, + MqttPersistenceException { MqttMessage message = new MqttMessage(payload); message.setQos(qos); + message.setType(type); message.setRetained(retained); return this.publish(topic, message, userContext, callback); } diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttMessage.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttMessage.java index a6c6f4bd..d70bd889 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttMessage.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/MqttMessage.java @@ -28,6 +28,8 @@ public class MqttMessage private int qos = 1; + private int type = -1; + private boolean retained = false; private boolean dup = false; @@ -148,6 +150,10 @@ public int getQos() return qos; } + public int getType() + { + return type; + } /** * Sets the quality of service for this message. *

      @@ -180,6 +186,12 @@ public void setQos(int qos) this.qos = qos; } + public void setType(int type) + { + checkMutable(); + this.type = type; + } + /** * Returns a string representation of this message's payload. Makes an attempt to return the payload as a string. As the MQTT client has no control over the content of the * payload it may fail. diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java index 2e50f942..c38cc94a 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/ClientState.java @@ -15,7 +15,6 @@ */ package org.eclipse.paho.client.mqttv3.internal; - import org.eclipse.paho.client.mqttv3.ICommsCallback; import org.eclipse.paho.client.mqttv3.IExperimentsConfig; import org.eclipse.paho.client.mqttv3.ILogger; @@ -46,6 +45,7 @@ import java.io.EOFException; import java.util.Enumeration; import java.util.Hashtable; +import java.util.Map; import java.util.Properties; import java.util.Vector; @@ -93,6 +93,8 @@ public class ClientState private Hashtable inUseMsgIds; // Used to store a set of in-use message IDs + private Hashtable inUseMsdIdsQos1WithoutPersistence; + volatile private Vector pendingMessages; volatile private Vector pendingFlows; @@ -169,6 +171,7 @@ protected ClientState( this.maxInflight = maxInflightMsgs; inUseMsgIds = new Hashtable(); + inUseMsdIdsQos1WithoutPersistence = new Hashtable(); pendingMessages = new Vector(this.maxInflight); pendingFlows = new Vector(); outboundQoS2 = new Hashtable(); @@ -234,6 +237,7 @@ protected void clearState() throws MqttException persistence.clear(); clientComms.clear(); inUseMsgIds.clear(); + inUseMsdIdsQos1WithoutPersistence.clear(); pendingMessages.clear(); pendingFlows.clear(); outboundQoS2.clear(); @@ -516,7 +520,8 @@ public void send(MqttWireMessage message, MqttToken token) throws MqttException final String methodName = "send"; if (message.isMessageIdRequired() && (message.getMessageId() == 0)) { - message.setMessageId(getNextMessageId()); + boolean isQos1NonPersistenceMessage = (message instanceof MqttPublish) && (((MqttPublish) message).getMessage().getType() > 2); + message.setMessageId(getNextMessageId(isQos1NonPersistenceMessage)); } if (token != null) { @@ -555,6 +560,7 @@ public void send(MqttWireMessage message, MqttToken token) throws MqttException persistence.put(getSendPersistenceKey(message), (MqttPublish) message); break; } + tokenStore.saveToken(token, message); pendingMessages.addElement(message); queueLock.notifyAll(); @@ -939,7 +945,7 @@ protected void notifySent(MqttWireMessage message) token.internalTok.notifySent(); if (message instanceof MqttPublish) { - if (((MqttPublish) message).getMessage().getQos() == 0) + if (((MqttPublish) message).getMessage().getQos() == 0 && ((MqttPublish) message).getMessage().getType() == 0) { // once a QoS 0 message is sent we can clean up its records straight away as // we won't be hearing about it again @@ -1021,7 +1027,7 @@ protected boolean checkQuiesceLock() /** * Called by the CommsReceiver when an ack has arrived. * - * @param message + * @param ack * @throws MqttException */ protected void notifyReceivedAck(MqttAck ack) throws MqttException @@ -1169,7 +1175,7 @@ else if (message instanceof MqttPubRel) * Called when waiters and callbacks have processed the message. For messages where delivery is complete the message can be removed from persistence and counters adjusted * accordingly. Also tidy up by removing token from store... * - * @param message + * @param token * @throws MqttException */ protected void notifyComplete(MqttToken token) throws MqttException @@ -1323,11 +1329,15 @@ public void disconnected(MqttException reason) try { - if (cleanSession) - { + if (cleanSession) { clearState(); } + for (Integer key : inUseMsdIdsQos1WithoutPersistence.keySet()) { + inUseMsgIds.remove(key); + } + inUseMsdIdsQos1WithoutPersistence.clear(); + pendingMessages.clear(); pendingFlows.clear(); synchronized (pingOutstanding) @@ -1348,17 +1358,19 @@ public void disconnected(MqttException reason) * @param msgId * A message ID that can be freed up for re-use. */ - private synchronized void releaseMessageId(int msgId) + public synchronized void releaseMessageId(int msgId) { inUseMsgIds.remove(Integer.valueOf(msgId)); + inUseMsdIdsQos1WithoutPersistence.remove(Integer.valueOf(msgId)); } /** * Get the next MQTT message ID that is not already in use, and marks it as now being in use. * * @return the next MQTT message ID to use + * @param isQos1NonPersistenceMessage */ - private synchronized int getNextMessageId() throws MqttException + private synchronized int getNextMessageId(boolean isQos1NonPersistenceMessage) throws MqttException { int startingMessageId = nextMsgId; // Allow two complete passes of the message ID range. This gives @@ -1383,6 +1395,7 @@ private synchronized int getNextMessageId() throws MqttException while (inUseMsgIds.containsKey(Integer.valueOf(nextMsgId))); Integer id = Integer.valueOf(nextMsgId); inUseMsgIds.put(id, id); + if(isQos1NonPersistenceMessage) inUseMsdIdsQos1WithoutPersistence.put(id, id); return nextMsgId; } @@ -1469,6 +1482,7 @@ protected void deliveryComplete(MqttPublish message) throws MqttPersistenceExcep protected void close() { inUseMsgIds.clear(); + inUseMsdIdsQos1WithoutPersistence.clear(); pendingMessages.clear(); pendingFlows.clear(); outboundQoS2.clear(); @@ -1476,6 +1490,7 @@ protected void close() inboundQoS2.clear(); tokenStore.clear(); inUseMsgIds = null; + inUseMsdIdsQos1WithoutPersistence = null; pendingMessages = null; pendingFlows = null; outboundQoS2 = null; @@ -1552,7 +1567,7 @@ public void persistBufferedMessage(MqttWireMessage message) throws MqttException // Because the client will have disconnected, we will want to re-open persistence try { - message.setMessageId(getNextMessageId()); + message.setMessageId(getNextMessageId(false)); key = getSendBufferedPersistenceKey(message); try { persistence.put(key, (MqttPublish) message); diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java index 0c11f6f5..3d765483 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/CommsSender.java @@ -281,8 +281,16 @@ private void handleRunException(MqttWireMessage message, Exception ex) { mex = (MqttException) ex; } - + if((message instanceof MqttPublish) && ( + (((MqttPublish) message)).getMessage().getQos() == 0 + || (((MqttPublish) message)).getMessage().getType() > 2 + ) + ) { + clientState.releaseMessageId(message.getMessageId()); + } + clientState.releaseMessageId(message.getType()); running = false; + clientComms.shutdownConnection(null, mex); } diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java index 26de5774..c2ef225d 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttPublish.java @@ -114,7 +114,7 @@ public String toString() StringBuffer sb = new StringBuffer(); sb.append(super.toString()); sb.append(" qos:" + message.getQos()); - if (message.getQos() > 0) + if (message.getQos() > 0 || message.getType() > 2) { sb.append(" msgId:" + msgId); } @@ -130,7 +130,8 @@ public String toString() protected byte getMessageInfo() { - byte info = (byte) (message.getQos() << 1); + int qos = message.getType() > 2 ? 1 : message.getQos(); + byte info = (byte) (qos << 1); if (message.isRetained()) { info |= 0x01; @@ -196,7 +197,7 @@ protected byte[] getVariableHeader() throws MqttException ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); encodeUTF8(dos, topicName); - if (message.getQos() > 0) + if (message.getQos() > 0 || message.getType() > 2) { dos.writeShort(msgId); } diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java index 04a818cf..cf111809 100644 --- a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/MqttSubscribe.java @@ -20,6 +20,9 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; @@ -33,6 +36,8 @@ public class MqttSubscribe extends MqttWireMessage private int[] qos; + List subscribeFlagsList; + private int count; /** @@ -51,13 +56,19 @@ public MqttSubscribe(byte info, byte[] data) throws IOException count = 0; names = new String[10]; qos = new int[10]; + subscribeFlagsList = new ArrayList<>(10); boolean end = false; while (!end) { try { names[count] = decodeUTF8(dis); - qos[count++] = dis.readByte(); + + byte qosAndFlagByte = dis.readByte(); + qos[count++] = qosAndFlagByte & 0x3; + boolean isPersistable = (qosAndFlagByte & 0x04) == 0; + boolean isRetryable = (qosAndFlagByte & 0x08) == 0; + subscribeFlagsList.add(new SubscribeFlags(isPersistable, isRetryable)); } catch (Exception e) { @@ -75,11 +86,12 @@ public MqttSubscribe(byte info, byte[] data) throws IOException * @param qos * - the max QoS that each each topic will be subscribed at */ - public MqttSubscribe(String[] names, int[] qos) + public MqttSubscribe(String[] names, int[] qos, List subscribeFlagsList) { super(MqttWireMessage.MESSAGE_TYPE_SUBSCRIBE); this.names = names; this.qos = qos; + this.subscribeFlagsList = subscribeFlagsList; this.count = names.length; if (names.length != qos.length) @@ -153,7 +165,15 @@ public byte[] getPayload() throws MqttException for (int i = 0; i < names.length; i++) { encodeUTF8(dos, names[i]); - dos.writeByte(qos[i]); + byte nextByte = 0; + nextByte = (byte) (nextByte | qos[i]); + if (!subscribeFlagsList.get(i).isPersistableFlagEnabled()) { + nextByte |= 0x04; + } + if (!subscribeFlagsList.get(i).isRetryableFlagEnabled()) { + nextByte |= 0x08; + } + dos.writeByte(nextByte); } return baos.toByteArray(); } diff --git a/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/SubscribeFlags.java b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/SubscribeFlags.java new file mode 100644 index 00000000..50406bc8 --- /dev/null +++ b/paho/src/main/java/org/eclipse/paho/client/mqttv3/internal/wire/SubscribeFlags.java @@ -0,0 +1,21 @@ +package org.eclipse.paho.client.mqttv3.internal.wire; + +public class SubscribeFlags { + + private final boolean isPersistable; + + private final boolean isRetryable; + + public SubscribeFlags(boolean isPersistable, boolean isRetryable){ + this.isPersistable = isPersistable; + this.isRetryable = isRetryable; + } + + public boolean isPersistableFlagEnabled() { + return isPersistable; + } + + public boolean isRetryableFlagEnabled() { + return isRetryable; + } +} From 8d10dafb0292e19e45489e991b2d211979731249 Mon Sep 17 00:00:00 2001 From: Deepanshu Date: Wed, 12 Apr 2023 14:25:24 +0530 Subject: [PATCH 22/25] Add config for max inflight messages limit (#63) --- .../main/java/com/gojek/courier/app/ui/MainActivity.kt | 1 + mqtt-client/api/mqtt-client.api | 10 ++++++---- .../com/gojek/mqtt/client/config/ExperimentConfigs.kt | 4 +++- .../com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt | 3 +-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt b/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt index 40c344a4..2244ce47 100644 --- a/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt +++ b/app/src/main/java/com/gojek/courier/app/ui/MainActivity.kt @@ -131,6 +131,7 @@ class MainActivity : AppCompatActivity() { activityCheckIntervalSeconds = 30, incomingMessagesTTLSecs = 60, incomingMessagesCleanupIntervalSecs = 10, + maxInflightMessagesLimit = 1000, ), pingSender = WorkPingSenderFactory.createMqttPingSender(applicationContext, WorkManagerPingSenderConfig()) ) diff --git a/mqtt-client/api/mqtt-client.api b/mqtt-client/api/mqtt-client.api index acf9fa4c..ca006f6f 100644 --- a/mqtt-client/api/mqtt-client.api +++ b/mqtt-client/api/mqtt-client.api @@ -28,8 +28,8 @@ public abstract interface class com/gojek/mqtt/client/MqttInterceptor { public final class com/gojek/mqtt/client/config/ExperimentConfigs { public fun ()V - public fun (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZ)V - public synthetic fun (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZI)V + public synthetic fun (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZIILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lcom/gojek/mqtt/client/config/SubscriptionStore; public final fun component2 ()Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig; public final fun component3 ()I @@ -38,14 +38,16 @@ public final class com/gojek/mqtt/client/config/ExperimentConfigs { public final fun component6 ()J public final fun component7 ()J public final fun component8 ()Z - public final fun copy (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZ)Lcom/gojek/mqtt/client/config/ExperimentConfigs; - public static synthetic fun copy$default (Lcom/gojek/mqtt/client/config/ExperimentConfigs;Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZILjava/lang/Object;)Lcom/gojek/mqtt/client/config/ExperimentConfigs; + public final fun component9 ()I + public final fun copy (Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZI)Lcom/gojek/mqtt/client/config/ExperimentConfigs; + public static synthetic fun copy$default (Lcom/gojek/mqtt/client/config/ExperimentConfigs;Lcom/gojek/mqtt/client/config/SubscriptionStore;Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig;IIIJJZIILjava/lang/Object;)Lcom/gojek/mqtt/client/config/ExperimentConfigs; public fun equals (Ljava/lang/Object;)Z public final fun getActivityCheckIntervalSeconds ()I public final fun getAdaptiveKeepAliveConfig ()Lcom/gojek/mqtt/model/AdaptiveKeepAliveConfig; public final fun getInactivityTimeoutSeconds ()I public final fun getIncomingMessagesCleanupIntervalSecs ()J public final fun getIncomingMessagesTTLSecs ()J + public final fun getMaxInflightMessagesLimit ()I public final fun getPolicyResetTimeSeconds ()I public final fun getShouldUseNewSSLFlow ()Z public final fun getSubscriptionStore ()Lcom/gojek/mqtt/client/config/SubscriptionStore; diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/ExperimentConfigs.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/ExperimentConfigs.kt index 2f1d863d..d0421ca0 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/config/ExperimentConfigs.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/config/ExperimentConfigs.kt @@ -4,6 +4,7 @@ import com.gojek.mqtt.client.config.SubscriptionStore.PERSISTABLE import com.gojek.mqtt.constants.DEFAULT_ACTIVITY_CHECK_INTERVAL_SECS import com.gojek.mqtt.constants.DEFAULT_INACTIVITY_TIMEOUT_SECS import com.gojek.mqtt.constants.DEFAULT_POLICY_RESET_TIME_SECS +import com.gojek.mqtt.constants.MAX_INFLIGHT_MESSAGES_ALLOWED import com.gojek.mqtt.model.AdaptiveKeepAliveConfig data class ExperimentConfigs( @@ -14,7 +15,8 @@ data class ExperimentConfigs( val policyResetTimeSeconds: Int = DEFAULT_POLICY_RESET_TIME_SECS, val incomingMessagesTTLSecs: Long = 360, val incomingMessagesCleanupIntervalSecs: Long = 60, - val shouldUseNewSSLFlow: Boolean = false + val shouldUseNewSSLFlow: Boolean = false, + val maxInflightMessagesLimit: Int = MAX_INFLIGHT_MESSAGES_ALLOWED ) enum class SubscriptionStore { diff --git a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt index 92f498b4..e69da2da 100644 --- a/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt +++ b/mqtt-client/src/main/java/com/gojek/mqtt/client/v3/impl/AndroidMqttClient.kt @@ -40,7 +40,6 @@ import com.gojek.mqtt.client.v3.IAndroidMqttClient import com.gojek.mqtt.connection.IMqttConnection import com.gojek.mqtt.connection.MqttConnection import com.gojek.mqtt.connection.config.v3.ConnectionConfig -import com.gojek.mqtt.constants.MAX_INFLIGHT_MESSAGES_ALLOWED import com.gojek.mqtt.constants.MESSAGE import com.gojek.mqtt.constants.MSG_APP_PUBLISH import com.gojek.mqtt.event.EventHandler @@ -170,7 +169,7 @@ internal class AndroidMqttClient( subscriptionRetryPolicy = mqttConfiguration.subscriptionRetryPolicy, unsubscriptionRetryPolicy = mqttConfiguration.unsubscriptionRetryPolicy, wakeLockTimeout = mqttConfiguration.wakeLockTimeout, - maxInflightMessages = MAX_INFLIGHT_MESSAGES_ALLOWED, + maxInflightMessages = experimentConfigs.maxInflightMessagesLimit, logger = mqttConfiguration.logger, connectionEventHandler = mqttClientEventAdapter.adapt(), mqttInterceptorList = mqttConfiguration.mqttInterceptorList.map { From 9b08b653ea2e7077f642840b2dc5b24418fb5f49 Mon Sep 17 00:00:00 2001 From: deepanshu42 Date: Tue, 18 Apr 2023 10:47:07 +0530 Subject: [PATCH 23/25] Add debug logs in Coordinator --- .../gojek/courier/coordinator/Coordinator.kt | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt index c93a085f..c221db2a 100644 --- a/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt +++ b/courier/src/main/java/com/gojek/courier/coordinator/Coordinator.kt @@ -30,15 +30,20 @@ internal class Coordinator( @Synchronized override fun send(stubMethod: StubMethod.Send, args: Array): Any { + logger.d("Coordinator", "Send method invoked") val data = stubMethod.argumentProcessor.getDataArgument(args) stubMethod.argumentProcessor.inject(args) val topic = stubMethod.argumentProcessor.getTopic() val message = stubMethod.messageAdapter.toMessage(topic, data) - return client.send(message, topic, stubMethod.qos) + val qos = stubMethod.qos + val sent = client.send(message, topic, qos) + logger.d("Coordinator", "Sending message on topic: $topic, qos: $qos, message: $data") + return sent } @Synchronized override fun receive(stubMethod: StubMethod.Receive, args: Array): Any { + logger.d("Coordinator", "Receive method invoked") stubMethod.argumentProcessor.inject(args) val topic = stubMethod.argumentProcessor.getTopic() @@ -70,18 +75,25 @@ internal class Coordinator( } override fun subscribe(stubMethod: StubMethod.Subscribe, args: Array): Any { + logger.d("Coordinator", "Subscribe method invoked") stubMethod.argumentProcessor.inject(args) val topic = stubMethod.argumentProcessor.getTopic() - return client.subscribe(topic to stubMethod.qos) + val qos = stubMethod.qos + val status = client.subscribe(topic to qos) + logger.d("Coordinator", "Subscribing topic: $topic with qos: $qos") + return status } override fun subscribeWithStream( stubMethod: StubMethod.SubscribeWithStream, args: Array ): Any { + logger.d("Coordinator", "Subscribe method invoked with a returning stream") stubMethod.argumentProcessor.inject(args) val topic = stubMethod.argumentProcessor.getTopic() - client.subscribe(topic to stubMethod.qos) + val qos = stubMethod.qos + client.subscribe(topic to qos) + logger.d("Coordinator", "Subscribing topic: $topic with qos: $qos") val flowable = Flowable.create( FlowableOnSubscribe { emitter -> @@ -111,22 +123,28 @@ internal class Coordinator( } override fun unsubscribe(stubMethod: StubMethod.Unsubscribe, args: Array): Any { + logger.d("Coordinator", "Unsubscribe method invoked") stubMethod.argumentProcessor.inject(args) val topics = stubMethod.argumentProcessor.getTopics() - return if (topics.size == 1) { + val status = if (topics.size == 1) { client.unsubscribe(topics[0]) } else { client.unsubscribe(topics[0], *topics.sliceArray(IntRange(1, topics.size - 1))) } + logger.d("Coordinator", "Unsubscribing topics: $topics") + return status } override fun subscribeAll(stubMethod: StubMethod.SubscribeAll, args: Array): Any { + logger.d("Coordinator", "Subscribe method invoked for multiple topics") val topicList = (args[0] as Map).toList() - return if (topicList.size == 1) { + val status = if (topicList.size == 1) { client.subscribe(topicList[0]) } else { client.subscribe(topicList[0], *topicList.toTypedArray().sliceArray(IntRange(1, topicList.size - 1))) } + logger.d("Coordinator", "Subscribing topics: $topicList") + return status } override fun getEventStream(): Stream { From bb309e01ab0d9c189c45de1b80610fea6624cf9c Mon Sep 17 00:00:00 2001 From: deepanshu42 Date: Wed, 19 Apr 2023 11:08:50 +0530 Subject: [PATCH 24/25] Add empty service interface check --- courier/src/main/java/com/gojek/courier/stub/StubInterface.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/courier/src/main/java/com/gojek/courier/stub/StubInterface.kt b/courier/src/main/java/com/gojek/courier/stub/StubInterface.kt index a5a85261..35918cb6 100644 --- a/courier/src/main/java/com/gojek/courier/stub/StubInterface.kt +++ b/courier/src/main/java/com/gojek/courier/stub/StubInterface.kt @@ -60,6 +60,7 @@ internal class StubInterface( private fun Class<*>.findStubMethods(): Map { // Remove all default methods val methods = declaredMethods.filterNot { runtimePlatform.isDefaultMethod(it) } + require(methods.isNotEmpty()) { "Service interface should have atleast one abstract method" } val stubMethods = methods.mapNotNull { stubMethodFactory.create(it) } return methods.zip(stubMethods).toMap() } From a418e75569384d85be46e5d72313e64a8d9c7ef0 Mon Sep 17 00:00:00 2001 From: deepanshu42 Date: Wed, 19 Apr 2023 11:11:36 +0530 Subject: [PATCH 25/25] Update proguard rules --- proguard/proguard-rules.pro | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/proguard/proguard-rules.pro b/proguard/proguard-rules.pro index 6e5c4b94..441b7447 100644 --- a/proguard/proguard-rules.pro +++ b/proguard/proguard-rules.pro @@ -137,4 +137,14 @@ public static int d(...); public static int e(...); } -##---------------End: proguard configuration for android.Log---------- \ No newline at end of file +##---------------End: proguard configuration for android.Log---------- + +##---------------Begin: proguard configuration for courier annotations---------- +-keepclasseswithmembers class * { + @com.gojek.courier.annotation.* ; +} +-if interface * { @com.gojek.courier.annotation.* ; } +-keepclassmembers,allowshrinking,allowobfuscation interface * { + @com.gojek.courier.annotation.* ; +} +##---------------End: proguard configuration for courier annotations---------- \ No newline at end of file