From 4ee2192afeff6d352c23769aed05be7fc7f684e2 Mon Sep 17 00:00:00 2001 From: "marcin.cebo" Date: Tue, 21 Oct 2025 16:04:36 +0200 Subject: [PATCH 1/5] Added tests to verify if there is no channel name duplications in subscribe and heartbeat URLs --- .../integration/PresenceIntegrationTests.kt | 2 +- .../integration/SubscribeIntegrationTests.kt | 147 ++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/PresenceIntegrationTests.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/PresenceIntegrationTests.kt index b3a9bdd38..99a20a4b4 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/PresenceIntegrationTests.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/PresenceIntegrationTests.kt @@ -324,7 +324,7 @@ class PresenceIntegrationTests : BaseIntegrationTest() { } @Test - fun testHereNowWithStartFrom() { + fun testHereNowWithOffset() { val offsetValue = 2 val totalClientsCount = 5 val expectedChannel = randomChannel() diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt index a76ee1b64..44d24a557 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt @@ -1253,6 +1253,153 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { assertEquals(2, subscriptionSet.subscriptions.size) } + @Test + fun `shouldDeduplicateChannelSubscriptionsWhenSubscribingToSameChannelMultipleTimes`() { + // given + val numberOfSubscribe = 4 + // punbub.subscribe does subscribe to already subscribed channel so only two subscribe calls should occur. Handshake and actual subscribe. + val countDownLatch = CountDownLatch(2) + var interceptedUrl: HttpUrl? = null + val testChannel = randomChannel() + + clientConfig = { + httpLoggingInterceptor = + HttpLoggingInterceptor { message -> + // Intercept subscribe GET request + if (message.startsWith("--> GET https://") && message.contains("/v2/subscribe/")) { + val url = message.substringAfter("--> GET ").substringBefore(" HTTP") + interceptedUrl = url.toHttpUrlOrNull() + countDownLatch.countDown() + } + }.apply { level = HttpLoggingInterceptor.Level.BASIC } + } + + try { + repeat(numberOfSubscribe) { iteration -> + pubnub.subscribe(channels = listOf(testChannel)) + Thread.sleep(2000) + println("Subscribe call ${iteration + 1}/$numberOfSubscribe completed") + } + + // Wait for the subscribe request to be made + assertTrue(countDownLatch.await(12000, TimeUnit.MILLISECONDS)) + + // then: verify channel appears only once in subscribed channels + val subscribedChannels = pubnub.getSubscribedChannels() + + assertEquals(1, subscribedChannels.size) + assertTrue(subscribedChannels.contains(testChannel)) + + // then: verify the actual HTTP request only includes the channel once + assertNotNull("Expected to intercept subscribe URL", interceptedUrl) + + val channelsParam = + interceptedUrl!!.queryParameter("channel") ?: interceptedUrl!!.encodedPath.substringAfter("/subscribe/") + .substringAfter("/").substringBefore("/") + + val channelList = channelsParam.split(",").filter { it.isNotEmpty() } + + assertEquals(1, channelList.count { it == testChannel }) + } finally { + pubnub.forceDestroy() + } + } + + @Test + fun `heartbeatShouldDeduplicateChannelNameInUrlWhenSubscribingToSameChannelMultipleTimes`() { + // given + val numberOfSubscribe = 4 + val countDownLatch = CountDownLatch(2) // we want to verify second heartbeat URL + var interceptedUrl: HttpUrl? = null + val testChannel = randomChannel() + + clientConfig = { + heartbeatInterval = 5 + httpLoggingInterceptor = + HttpLoggingInterceptor { message -> + // Intercept subscribe GET request + if (message.startsWith("--> GET https://") && message.contains("/v2/presence/") && message.contains("/heartbeat")) { + val url = message.substringAfter("--> GET ").substringBefore(" HTTP") + interceptedUrl = url.toHttpUrlOrNull() + countDownLatch.countDown() + } + }.apply { level = HttpLoggingInterceptor.Level.BASIC } + } + + try { + repeat(numberOfSubscribe) { iteration -> + pubnub.subscribe(channels = listOf(testChannel)) + Thread.sleep(150) + println("Subscribe call ${iteration + 1}/$numberOfSubscribe completed") + } + + // Wait for the heartbeat request to be made + assertTrue(countDownLatch.await(6000, TimeUnit.MILLISECONDS)) + + // then: verify the actual HTTP request only includes the channel once + assertNotNull("Expected to intercept heartbeat URL", interceptedUrl) + + // Extract channel from heartbeat URL: /v2/presence/sub-key/{sub-key}/channel/{channels}/heartbeat + val channelsParam = interceptedUrl!!.encodedPath + .substringAfter("/channel/") + .substringBefore("/heartbeat") + + val channelList = channelsParam.split(",").filter { it.isNotEmpty() } + + assertEquals(1, channelList.count { it == testChannel }) + + } finally { + pubnub.forceDestroy() + } + } + + @Test + fun `shouldDeduplicateChannelSubscriptionsWhenSubscribingToListOfTheSameChannels`() { + // given + val countDownLatch = CountDownLatch(2) // Only two subscribe calls should occur. Handshake and actual subscribe. + var interceptedUrl: HttpUrl? = null + val testChannel = randomChannel() + + clientConfig = { + httpLoggingInterceptor = + HttpLoggingInterceptor { message -> + // Intercept subscribe GET request + if (message.startsWith("--> GET https://") && message.contains("/v2/subscribe/")) { + val url = message.substringAfter("--> GET ").substringBefore(" HTTP") + interceptedUrl = url.toHttpUrlOrNull() + countDownLatch.countDown() + } + }.apply { level = HttpLoggingInterceptor.Level.BASIC } + } + + try { + pubnub.subscribe(channels = listOf(testChannel, testChannel, testChannel)) + Thread.sleep(2000) + + // Wait for the subscribe request to be made + assertTrue(countDownLatch.await(12000, TimeUnit.MILLISECONDS)) + + // then: verify channel appears only once in subscribed channels + val subscribedChannels = pubnub.getSubscribedChannels() + + assertEquals(1, subscribedChannels.size) + assertTrue(subscribedChannels.contains(testChannel)) + + // then: verify the actual HTTP request only includes the channel once + assertNotNull("Expected to intercept subscribe URL", interceptedUrl) + + val channelsParam = + interceptedUrl!!.queryParameter("channel") ?: interceptedUrl!!.encodedPath.substringAfter("/subscribe/") + .substringAfter("/").substringBefore("/") + + val channelList = channelsParam.split(",").filter { it.isNotEmpty() } + + assertEquals(1, channelList.count { it == testChannel }) + } finally { + pubnub.forceDestroy() + } + } + private fun publishToChannels(channelsList: List) { channelsList.forEach { channelName -> pubnub.publish(channelName, "-=message to $channelName").sync() From 9d2348dc2dcd530b771e49d56d26a2f497ef044d Mon Sep 17 00:00:00 2001 From: "marcin.cebo" Date: Wed, 22 Oct 2025 12:03:23 +0200 Subject: [PATCH 2/5] Remove deprecation warning for httpLoggingInterceptor config property. --- .../java/com/pubnub/api/java/v2/PNConfiguration.kt | 12 ++---------- .../pubnub/internal/java/v2/PNConfigurationImpl.kt | 4 ---- .../kotlin/com/pubnub/api/v2/PNConfiguration.kt | 12 ++---------- .../api/integration/SubscribeIntegrationTests.kt | 7 +++---- .../pubnub/internal/logging/ConfigurationLogger.kt | 2 +- .../com/pubnub/internal/v2/PNConfigurationImpl.kt | 4 ---- 6 files changed, 8 insertions(+), 33 deletions(-) diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt index a499fc918..784b0bf0d 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt @@ -252,14 +252,10 @@ interface PNConfiguration : com.pubnub.api.v2.PNConfiguration { val certificatePinner: CertificatePinner? /** - * Sets a custom [HttpLoggingInterceptor] for logging network traffic. + * Sets a custom [HttpLoggingInterceptor]. * * @see [HttpLoggingInterceptor] */ - @Deprecated( - message = "This setting is deprecated. Use customLoggers instead", - level = DeprecationLevel.WARNING - ) val httpLoggingInterceptor: HttpLoggingInterceptor? /** @@ -500,14 +496,10 @@ interface PNConfiguration : com.pubnub.api.v2.PNConfiguration { fun certificatePinner(certificatePinner: CertificatePinner?): Builder /** - * Sets a custom [HttpLoggingInterceptor] for logging network traffic. + * Sets a custom [HttpLoggingInterceptor]. * * @see [HttpLoggingInterceptor] */ - @Deprecated( - message = "This setting is deprecated. Use customLoggers instead", - level = DeprecationLevel.WARNING - ) fun httpLoggingInterceptor(httpLoggingInterceptor: HttpLoggingInterceptor?): Builder /** diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt index b92b477ed..8c1b62e62 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt @@ -317,10 +317,6 @@ class PNConfigurationImpl( override var certificatePinner: CertificatePinner? = defaultConfiguration.certificatePinner - @Deprecated( - message = "This setting is deprecated. Use customLoggers instead.", - level = DeprecationLevel.WARNING - ) override fun httpLoggingInterceptor(httpLoggingInterceptor: HttpLoggingInterceptor?): Builder { this.httpLoggingInterceptor = httpLoggingInterceptor return this diff --git a/pubnub-kotlin/pubnub-kotlin-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt b/pubnub-kotlin/pubnub-kotlin-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt index 1afd8174f..32d19ee9e 100644 --- a/pubnub-kotlin/pubnub-kotlin-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt +++ b/pubnub-kotlin/pubnub-kotlin-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt @@ -238,14 +238,10 @@ actual interface PNConfiguration { val certificatePinner: CertificatePinner? /** - * Sets a custom [HttpLoggingInterceptor] for logging network traffic. + * Sets a custom [HttpLoggingInterceptor]. * * @see [HttpLoggingInterceptor] */ - @Deprecated( - message = "This setting is deprecated. Use customLoggers instead.", - level = DeprecationLevel.WARNING - ) val httpLoggingInterceptor: HttpLoggingInterceptor? /** @@ -564,14 +560,10 @@ actual interface PNConfiguration { var certificatePinner: CertificatePinner? /** - * Sets a custom [HttpLoggingInterceptor] for logging network traffic. + * Sets a custom [HttpLoggingInterceptor]. * * @see [HttpLoggingInterceptor] */ - @Deprecated( - message = "This setting is deprecated. Use customLoggers instead", - level = DeprecationLevel.WARNING - ) var httpLoggingInterceptor: HttpLoggingInterceptor? /** diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt index 44d24a557..355bc113f 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt @@ -1254,7 +1254,7 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { } @Test - fun `shouldDeduplicateChannelSubscriptionsWhenSubscribingToSameChannelMultipleTimes`() { + fun shouldDeduplicateChannelSubscriptionsWhenSubscribingToSameChannelMultipleTimes() { // given val numberOfSubscribe = 4 // punbub.subscribe does subscribe to already subscribed channel so only two subscribe calls should occur. Handshake and actual subscribe. @@ -1306,7 +1306,7 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { } @Test - fun `heartbeatShouldDeduplicateChannelNameInUrlWhenSubscribingToSameChannelMultipleTimes`() { + fun heartbeatShouldDeduplicateChannelNameInUrlWhenSubscribingToSameChannelMultipleTimes() { // given val numberOfSubscribe = 4 val countDownLatch = CountDownLatch(2) // we want to verify second heartbeat URL @@ -1347,14 +1347,13 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { val channelList = channelsParam.split(",").filter { it.isNotEmpty() } assertEquals(1, channelList.count { it == testChannel }) - } finally { pubnub.forceDestroy() } } @Test - fun `shouldDeduplicateChannelSubscriptionsWhenSubscribingToListOfTheSameChannels`() { + fun shouldDeduplicateChannelSubscriptionsWhenSubscribingToListOfTheSameChannels() { // given val countDownLatch = CountDownLatch(2) // Only two subscribe calls should occur. Handshake and actual subscribe. var interceptedUrl: HttpUrl? = null diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/logging/ConfigurationLogger.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/logging/ConfigurationLogger.kt index ca594b7d9..68b869aaa 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/logging/ConfigurationLogger.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/logging/ConfigurationLogger.kt @@ -75,7 +75,7 @@ object ConfigurationLogger { "proxySelector" to (configuration.proxySelector?.toString() ?: NOT_SET), "proxyAuthenticator" to (configuration.proxyAuthenticator?.toString() ?: NOT_SET), "maximumConnections" to (configuration.maximumConnections?.toString() ?: NOT_SET), - "httpLoggingInterceptor" to (configuration.httpLoggingInterceptor?.let { "(@Deprecated) enabled (${it.level})" } ?: NOT_SET), + "httpLoggingInterceptor" to (configuration.httpLoggingInterceptor?.toString() ?: NOT_SET), // SSL/TLS settings "sslSocketFactory" to (configuration.sslSocketFactory?.toString() ?: NOT_SET), "x509ExtendedTrustManager" to (configuration.x509ExtendedTrustManager?.toString() ?: NOT_SET), diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt index a6e976d69..965766d5a 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt @@ -189,10 +189,6 @@ class PNConfigurationImpl( override var certificatePinner: CertificatePinner? = defaultConfiguration.certificatePinner - @Deprecated( - message = "This setting is deprecated. Use customLoggers instead.", - level = DeprecationLevel.WARNING - ) override var httpLoggingInterceptor: HttpLoggingInterceptor? = defaultConfiguration.httpLoggingInterceptor override var sslSocketFactory: SSLSocketFactory? = defaultConfiguration.sslSocketFactory From bf102b7df725e538a3157c37696244f553c5d0dd Mon Sep 17 00:00:00 2001 From: "marcin.cebo" Date: Wed, 22 Oct 2025 13:43:51 +0200 Subject: [PATCH 3/5] Revert "Remove deprecation warning for httpLoggingInterceptor config property." This reverts commit 9d2348dc2dcd530b771e49d56d26a2f497ef044d. --- .../java/com/pubnub/api/java/v2/PNConfiguration.kt | 12 ++++++++++-- .../pubnub/internal/java/v2/PNConfigurationImpl.kt | 4 ++++ .../kotlin/com/pubnub/api/v2/PNConfiguration.kt | 12 ++++++++++-- .../api/integration/SubscribeIntegrationTests.kt | 7 ++++--- .../pubnub/internal/logging/ConfigurationLogger.kt | 2 +- .../com/pubnub/internal/v2/PNConfigurationImpl.kt | 4 ++++ 6 files changed, 33 insertions(+), 8 deletions(-) diff --git a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt index 784b0bf0d..a499fc918 100644 --- a/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt +++ b/pubnub-gson/pubnub-gson-api/src/main/java/com/pubnub/api/java/v2/PNConfiguration.kt @@ -252,10 +252,14 @@ interface PNConfiguration : com.pubnub.api.v2.PNConfiguration { val certificatePinner: CertificatePinner? /** - * Sets a custom [HttpLoggingInterceptor]. + * Sets a custom [HttpLoggingInterceptor] for logging network traffic. * * @see [HttpLoggingInterceptor] */ + @Deprecated( + message = "This setting is deprecated. Use customLoggers instead", + level = DeprecationLevel.WARNING + ) val httpLoggingInterceptor: HttpLoggingInterceptor? /** @@ -496,10 +500,14 @@ interface PNConfiguration : com.pubnub.api.v2.PNConfiguration { fun certificatePinner(certificatePinner: CertificatePinner?): Builder /** - * Sets a custom [HttpLoggingInterceptor]. + * Sets a custom [HttpLoggingInterceptor] for logging network traffic. * * @see [HttpLoggingInterceptor] */ + @Deprecated( + message = "This setting is deprecated. Use customLoggers instead", + level = DeprecationLevel.WARNING + ) fun httpLoggingInterceptor(httpLoggingInterceptor: HttpLoggingInterceptor?): Builder /** diff --git a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt index 8c1b62e62..b92b477ed 100644 --- a/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt +++ b/pubnub-gson/pubnub-gson-impl/src/main/java/com/pubnub/internal/java/v2/PNConfigurationImpl.kt @@ -317,6 +317,10 @@ class PNConfigurationImpl( override var certificatePinner: CertificatePinner? = defaultConfiguration.certificatePinner + @Deprecated( + message = "This setting is deprecated. Use customLoggers instead.", + level = DeprecationLevel.WARNING + ) override fun httpLoggingInterceptor(httpLoggingInterceptor: HttpLoggingInterceptor?): Builder { this.httpLoggingInterceptor = httpLoggingInterceptor return this diff --git a/pubnub-kotlin/pubnub-kotlin-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt b/pubnub-kotlin/pubnub-kotlin-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt index 32d19ee9e..1afd8174f 100644 --- a/pubnub-kotlin/pubnub-kotlin-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt +++ b/pubnub-kotlin/pubnub-kotlin-core-api/src/jvmMain/kotlin/com/pubnub/api/v2/PNConfiguration.kt @@ -238,10 +238,14 @@ actual interface PNConfiguration { val certificatePinner: CertificatePinner? /** - * Sets a custom [HttpLoggingInterceptor]. + * Sets a custom [HttpLoggingInterceptor] for logging network traffic. * * @see [HttpLoggingInterceptor] */ + @Deprecated( + message = "This setting is deprecated. Use customLoggers instead.", + level = DeprecationLevel.WARNING + ) val httpLoggingInterceptor: HttpLoggingInterceptor? /** @@ -560,10 +564,14 @@ actual interface PNConfiguration { var certificatePinner: CertificatePinner? /** - * Sets a custom [HttpLoggingInterceptor]. + * Sets a custom [HttpLoggingInterceptor] for logging network traffic. * * @see [HttpLoggingInterceptor] */ + @Deprecated( + message = "This setting is deprecated. Use customLoggers instead", + level = DeprecationLevel.WARNING + ) var httpLoggingInterceptor: HttpLoggingInterceptor? /** diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt index 355bc113f..44d24a557 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt @@ -1254,7 +1254,7 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { } @Test - fun shouldDeduplicateChannelSubscriptionsWhenSubscribingToSameChannelMultipleTimes() { + fun `shouldDeduplicateChannelSubscriptionsWhenSubscribingToSameChannelMultipleTimes`() { // given val numberOfSubscribe = 4 // punbub.subscribe does subscribe to already subscribed channel so only two subscribe calls should occur. Handshake and actual subscribe. @@ -1306,7 +1306,7 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { } @Test - fun heartbeatShouldDeduplicateChannelNameInUrlWhenSubscribingToSameChannelMultipleTimes() { + fun `heartbeatShouldDeduplicateChannelNameInUrlWhenSubscribingToSameChannelMultipleTimes`() { // given val numberOfSubscribe = 4 val countDownLatch = CountDownLatch(2) // we want to verify second heartbeat URL @@ -1347,13 +1347,14 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { val channelList = channelsParam.split(",").filter { it.isNotEmpty() } assertEquals(1, channelList.count { it == testChannel }) + } finally { pubnub.forceDestroy() } } @Test - fun shouldDeduplicateChannelSubscriptionsWhenSubscribingToListOfTheSameChannels() { + fun `shouldDeduplicateChannelSubscriptionsWhenSubscribingToListOfTheSameChannels`() { // given val countDownLatch = CountDownLatch(2) // Only two subscribe calls should occur. Handshake and actual subscribe. var interceptedUrl: HttpUrl? = null diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/logging/ConfigurationLogger.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/logging/ConfigurationLogger.kt index 68b869aaa..ca594b7d9 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/logging/ConfigurationLogger.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/logging/ConfigurationLogger.kt @@ -75,7 +75,7 @@ object ConfigurationLogger { "proxySelector" to (configuration.proxySelector?.toString() ?: NOT_SET), "proxyAuthenticator" to (configuration.proxyAuthenticator?.toString() ?: NOT_SET), "maximumConnections" to (configuration.maximumConnections?.toString() ?: NOT_SET), - "httpLoggingInterceptor" to (configuration.httpLoggingInterceptor?.toString() ?: NOT_SET), + "httpLoggingInterceptor" to (configuration.httpLoggingInterceptor?.let { "(@Deprecated) enabled (${it.level})" } ?: NOT_SET), // SSL/TLS settings "sslSocketFactory" to (configuration.sslSocketFactory?.toString() ?: NOT_SET), "x509ExtendedTrustManager" to (configuration.x509ExtendedTrustManager?.toString() ?: NOT_SET), diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt index 965766d5a..a6e976d69 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/main/kotlin/com/pubnub/internal/v2/PNConfigurationImpl.kt @@ -189,6 +189,10 @@ class PNConfigurationImpl( override var certificatePinner: CertificatePinner? = defaultConfiguration.certificatePinner + @Deprecated( + message = "This setting is deprecated. Use customLoggers instead.", + level = DeprecationLevel.WARNING + ) override var httpLoggingInterceptor: HttpLoggingInterceptor? = defaultConfiguration.httpLoggingInterceptor override var sslSocketFactory: SSLSocketFactory? = defaultConfiguration.sslSocketFactory From 57a0b03a0810df2b7ff3178e0236eabb1a3bb3bf Mon Sep 17 00:00:00 2001 From: "marcin.cebo" Date: Wed, 22 Oct 2025 13:48:12 +0200 Subject: [PATCH 4/5] Modified test to use customLogger instead of httpLoggingInterceptor --- .../integration/SubscribeIntegrationTests.kt | 91 +++++++++++-------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt index 44d24a557..855af9bf7 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt @@ -5,6 +5,10 @@ import com.google.gson.JsonObject import com.pubnub.api.PubNub import com.pubnub.api.callbacks.SubscribeCallback import com.pubnub.api.enums.PNStatusCategory +import com.pubnub.api.logging.CustomLogger +import com.pubnub.api.logging.LogMessage +import com.pubnub.api.logging.LogMessageContent +import com.pubnub.api.logging.LogMessageType import com.pubnub.api.models.consumer.PNStatus import com.pubnub.api.models.consumer.channel_group.PNChannelGroupsAddChannelResult import com.pubnub.api.models.consumer.pubsub.PNMessageResult @@ -1254,7 +1258,7 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { } @Test - fun `shouldDeduplicateChannelSubscriptionsWhenSubscribingToSameChannelMultipleTimes`() { + fun shouldDeduplicateChannelSubscriptionsWhenSubscribingToSameChannelMultipleTimes() { // given val numberOfSubscribe = 4 // punbub.subscribe does subscribe to already subscribed channel so only two subscribe calls should occur. Handshake and actual subscribe. @@ -1262,22 +1266,26 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { var interceptedUrl: HttpUrl? = null val testChannel = randomChannel() - clientConfig = { - httpLoggingInterceptor = - HttpLoggingInterceptor { message -> - // Intercept subscribe GET request - if (message.startsWith("--> GET https://") && message.contains("/v2/subscribe/")) { - val url = message.substringAfter("--> GET ").substringBefore(" HTTP") - interceptedUrl = url.toHttpUrlOrNull() + val customLogger = object : CustomLogger { + override fun debug(logMessage: LogMessage) { + if(logMessage.type == LogMessageType.NETWORK_REQUEST){ + val networkRequestDetails = logMessage.message as LogMessageContent.NetworkRequest + if (networkRequestDetails.path.contains("/v2/subscribe/")) { + interceptedUrl = (networkRequestDetails.origin + networkRequestDetails.path).toHttpUrlOrNull() countDownLatch.countDown() } - }.apply { level = HttpLoggingInterceptor.Level.BASIC } + } + } + } + + clientConfig = { + customLoggers = listOf(customLogger) } try { repeat(numberOfSubscribe) { iteration -> pubnub.subscribe(channels = listOf(testChannel)) - Thread.sleep(2000) + Thread.sleep(150) println("Subscribe call ${iteration + 1}/$numberOfSubscribe completed") } @@ -1293,9 +1301,10 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { // then: verify the actual HTTP request only includes the channel once assertNotNull("Expected to intercept subscribe URL", interceptedUrl) - val channelsParam = - interceptedUrl!!.queryParameter("channel") ?: interceptedUrl!!.encodedPath.substringAfter("/subscribe/") - .substringAfter("/").substringBefore("/") + val channelsParam = interceptedUrl!!.encodedPath + .substringAfter("/subscribe/") + .substringAfter("/") + .substringBefore("/") val channelList = channelsParam.split(",").filter { it.isNotEmpty() } @@ -1306,24 +1315,28 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { } @Test - fun `heartbeatShouldDeduplicateChannelNameInUrlWhenSubscribingToSameChannelMultipleTimes`() { + fun heartbeatShouldDeduplicateChannelNameInUrlWhenSubscribingToSameChannelMultipleTimes() { // given val numberOfSubscribe = 4 val countDownLatch = CountDownLatch(2) // we want to verify second heartbeat URL var interceptedUrl: HttpUrl? = null val testChannel = randomChannel() - clientConfig = { - heartbeatInterval = 5 - httpLoggingInterceptor = - HttpLoggingInterceptor { message -> - // Intercept subscribe GET request - if (message.startsWith("--> GET https://") && message.contains("/v2/presence/") && message.contains("/heartbeat")) { - val url = message.substringAfter("--> GET ").substringBefore(" HTTP") - interceptedUrl = url.toHttpUrlOrNull() + val customLogger = object : CustomLogger { + override fun debug(logMessage: LogMessage) { + if(logMessage.type == LogMessageType.NETWORK_REQUEST){ + val networkRequestDetails = logMessage.message as LogMessageContent.NetworkRequest + if (networkRequestDetails.path.contains("/v2/presence/") && networkRequestDetails.path.contains("/heartbeat")) { + interceptedUrl = (networkRequestDetails.origin + networkRequestDetails.path).toHttpUrlOrNull() countDownLatch.countDown() } - }.apply { level = HttpLoggingInterceptor.Level.BASIC } + } + } + } + + clientConfig = { + customLoggers = listOf(customLogger) + heartbeatInterval = 5 } try { @@ -1354,27 +1367,30 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { } @Test - fun `shouldDeduplicateChannelSubscriptionsWhenSubscribingToListOfTheSameChannels`() { + fun shouldDeduplicateChannelSubscriptionsWhenSubscribingToListOfTheSameChannels() { // given val countDownLatch = CountDownLatch(2) // Only two subscribe calls should occur. Handshake and actual subscribe. var interceptedUrl: HttpUrl? = null val testChannel = randomChannel() + val customLogger = object : CustomLogger { + override fun debug(logMessage: LogMessage) { + if(logMessage.type == LogMessageType.NETWORK_REQUEST){ + val networkRequestDetails = logMessage.message as LogMessageContent.NetworkRequest + if (networkRequestDetails.path.contains("/v2/subscribe/")) { + interceptedUrl = (networkRequestDetails.origin + networkRequestDetails.path).toHttpUrlOrNull() + countDownLatch.countDown() + } + } + } + } + clientConfig = { - httpLoggingInterceptor = - HttpLoggingInterceptor { message -> - // Intercept subscribe GET request - if (message.startsWith("--> GET https://") && message.contains("/v2/subscribe/")) { - val url = message.substringAfter("--> GET ").substringBefore(" HTTP") - interceptedUrl = url.toHttpUrlOrNull() - countDownLatch.countDown() - } - }.apply { level = HttpLoggingInterceptor.Level.BASIC } + customLoggers = listOf(customLogger) } try { pubnub.subscribe(channels = listOf(testChannel, testChannel, testChannel)) - Thread.sleep(2000) // Wait for the subscribe request to be made assertTrue(countDownLatch.await(12000, TimeUnit.MILLISECONDS)) @@ -1388,9 +1404,10 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { // then: verify the actual HTTP request only includes the channel once assertNotNull("Expected to intercept subscribe URL", interceptedUrl) - val channelsParam = - interceptedUrl!!.queryParameter("channel") ?: interceptedUrl!!.encodedPath.substringAfter("/subscribe/") - .substringAfter("/").substringBefore("/") + val channelsParam = interceptedUrl!!.encodedPath + .substringAfter("/subscribe/") + .substringAfter("/") + .substringBefore("/") val channelList = channelsParam.split(",").filter { it.isNotEmpty() } From 174733cfa154e6f02ac77e4cea0312669cc2294d Mon Sep 17 00:00:00 2001 From: "marcin.cebo" Date: Wed, 22 Oct 2025 13:52:30 +0200 Subject: [PATCH 5/5] Lint --- .../integration/SubscribeIntegrationTests.kt | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt index 855af9bf7..bc30cb94b 100644 --- a/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt +++ b/pubnub-kotlin/pubnub-kotlin-impl/src/integrationTest/kotlin/com/pubnub/api/integration/SubscribeIntegrationTests.kt @@ -1268,7 +1268,7 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { val customLogger = object : CustomLogger { override fun debug(logMessage: LogMessage) { - if(logMessage.type == LogMessageType.NETWORK_REQUEST){ + if (logMessage.type == LogMessageType.NETWORK_REQUEST) { val networkRequestDetails = logMessage.message as LogMessageContent.NetworkRequest if (networkRequestDetails.path.contains("/v2/subscribe/")) { interceptedUrl = (networkRequestDetails.origin + networkRequestDetails.path).toHttpUrlOrNull() @@ -1324,7 +1324,7 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { val customLogger = object : CustomLogger { override fun debug(logMessage: LogMessage) { - if(logMessage.type == LogMessageType.NETWORK_REQUEST){ + if (logMessage.type == LogMessageType.NETWORK_REQUEST) { val networkRequestDetails = logMessage.message as LogMessageContent.NetworkRequest if (networkRequestDetails.path.contains("/v2/presence/") && networkRequestDetails.path.contains("/heartbeat")) { interceptedUrl = (networkRequestDetails.origin + networkRequestDetails.path).toHttpUrlOrNull() @@ -1360,7 +1360,6 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { val channelList = channelsParam.split(",").filter { it.isNotEmpty() } assertEquals(1, channelList.count { it == testChannel }) - } finally { pubnub.forceDestroy() } @@ -1373,17 +1372,17 @@ class SubscribeIntegrationTests : BaseIntegrationTest() { var interceptedUrl: HttpUrl? = null val testChannel = randomChannel() - val customLogger = object : CustomLogger { - override fun debug(logMessage: LogMessage) { - if(logMessage.type == LogMessageType.NETWORK_REQUEST){ - val networkRequestDetails = logMessage.message as LogMessageContent.NetworkRequest - if (networkRequestDetails.path.contains("/v2/subscribe/")) { - interceptedUrl = (networkRequestDetails.origin + networkRequestDetails.path).toHttpUrlOrNull() - countDownLatch.countDown() - } - } - } - } + val customLogger = object : CustomLogger { + override fun debug(logMessage: LogMessage) { + if (logMessage.type == LogMessageType.NETWORK_REQUEST) { + val networkRequestDetails = logMessage.message as LogMessageContent.NetworkRequest + if (networkRequestDetails.path.contains("/v2/subscribe/")) { + interceptedUrl = (networkRequestDetails.origin + networkRequestDetails.path).toHttpUrlOrNull() + countDownLatch.countDown() + } + } + } + } clientConfig = { customLoggers = listOf(customLogger)