com.pubnub.api.workers.SubscribeMessageProcessor.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pubnub-kotlin Show documentation
Show all versions of pubnub-kotlin Show documentation
PubNub is a cross-platform client-to-client (1:1 and 1:many) push service in the cloud, capable of
broadcasting real-time messages to millions of web and mobile clients simultaneously, in less than a quarter
second!
package com.pubnub.api.workers
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import com.pubnub.api.PNConfiguration
import com.pubnub.api.PNConfiguration.Companion.isValid
import com.pubnub.api.PubNub
import com.pubnub.api.PubNubUtil
import com.pubnub.api.crypto.decryptString
import com.pubnub.api.managers.DuplicationManager
import com.pubnub.api.models.consumer.files.PNDownloadableFile
import com.pubnub.api.models.consumer.message_actions.PNMessageAction
import com.pubnub.api.models.consumer.pubsub.BasePubSubResult
import com.pubnub.api.models.consumer.pubsub.PNEvent
import com.pubnub.api.models.consumer.pubsub.PNMessageResult
import com.pubnub.api.models.consumer.pubsub.PNPresenceEventResult
import com.pubnub.api.models.consumer.pubsub.PNSignalResult
import com.pubnub.api.models.consumer.pubsub.files.PNFileEventResult
import com.pubnub.api.models.consumer.pubsub.message_actions.PNMessageActionResult
import com.pubnub.api.models.consumer.pubsub.objects.ObjectPayload
import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventMessage
import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult
import com.pubnub.api.models.server.PresenceEnvelope
import com.pubnub.api.models.server.SubscribeMessage
import com.pubnub.api.models.server.files.FileUploadNotification
import com.pubnub.api.services.FilesService
import org.slf4j.LoggerFactory
internal class SubscribeMessageProcessor(
private val pubnub: PubNub,
private val duplicationManager: DuplicationManager
) {
private val log = LoggerFactory.getLogger("SubscribeMessageProcessor")
companion object {
internal const val TYPE_MESSAGE = 0
internal const val TYPE_SIGNAL = 1
internal const val TYPE_OBJECT = 2
internal const val TYPE_MESSAGE_ACTION = 3
internal const val TYPE_FILES = 4
}
fun processIncomingPayload(message: SubscribeMessage): PNEvent? {
if (message.channel == null) {
return null
}
val channel = message.channel
var subscriptionMatch = message.subscriptionMatch
val publishMetaData = message.publishMetaData
if (channel == subscriptionMatch) {
subscriptionMatch = null
}
if (pubnub.configuration.dedupOnSubscribe) {
if (duplicationManager.isDuplicate(message)) {
return null
} else {
duplicationManager.addEntry(message)
}
}
if (message.channel.endsWith("-pnpres")) {
val presencePayload = pubnub.mapper.convertValue(message.payload, PresenceEnvelope::class.java)
val strippedPresenceChannel = PubNubUtil.replaceLast(channel, "-pnpres", "")
val strippedPresenceSubscription = subscriptionMatch?.let {
PubNubUtil.replaceLast(it, "-pnpres", "")
}
val isHereNowRefresh = message.payload?.asJsonObject?.get("here_now_refresh")
return PNPresenceEventResult(
event = presencePayload.action,
uuid = presencePayload.uuid,
timestamp = presencePayload.timestamp,
occupancy = presencePayload.occupancy,
state = presencePayload.data,
channel = strippedPresenceChannel,
subscription = strippedPresenceSubscription,
timetoken = publishMetaData?.publishTimetoken,
join = getDelta(message.payload?.asJsonObject?.get("join")),
leave = getDelta(message.payload?.asJsonObject?.get("leave")),
timeout = getDelta(message.payload?.asJsonObject?.get("timeout")),
hereNowRefresh = isHereNowRefresh != null && isHereNowRefresh.asBoolean
)
} else {
val extractedMessage = processMessage(message)
if (extractedMessage == null) {
log.debug("unable to parse payload on #processIncomingMessages")
}
val result = BasePubSubResult(
channel = channel,
subscription = subscriptionMatch,
timetoken = publishMetaData?.publishTimetoken,
userMetadata = message.userMetadata,
publisher = message.issuingClientId
)
return when (message.type) {
null -> {
PNMessageResult(result, extractedMessage!!)
}
TYPE_MESSAGE -> {
PNMessageResult(result, extractedMessage!!)
}
TYPE_SIGNAL -> {
PNSignalResult(result, extractedMessage!!)
}
TYPE_OBJECT -> {
PNObjectEventResult(
result,
pubnub.mapper.convertValue(
extractedMessage, PNObjectEventMessage::class.java
)
)
}
TYPE_MESSAGE_ACTION -> {
val objectPayload = pubnub.mapper.convertValue(extractedMessage, ObjectPayload::class.java)
val data = objectPayload.data.asJsonObject
if (!data.has("uuid")) {
data.addProperty("uuid", result.publisher)
}
PNMessageActionResult(
result = result,
event = objectPayload.event,
data = pubnub.mapper.convertValue(data, PNMessageAction::class.java)
)
}
TYPE_FILES -> {
val fileUploadNotification = pubnub.mapper.convertValue(
extractedMessage, FileUploadNotification::class.java
)
PNFileEventResult(
channel = message.channel,
message = fileUploadNotification.message,
file = PNDownloadableFile(
id = fileUploadNotification.file.id,
name = fileUploadNotification.file.name,
url = buildFileUrl(
message.channel, fileUploadNotification.file.id, fileUploadNotification.file.name
)
),
publisher = message.issuingClientId,
timetoken = result.timetoken,
jsonMessage = fileUploadNotification.message?.let { pubnub.mapper.toJsonTree(it) }
?: JsonNull.INSTANCE
)
}
else -> null
}
}
}
private val formatFriendlyGetFileUrl = "%s" + FilesService.GET_FILE_URL.replace("\\{.*?\\}".toRegex(), "%s")
private fun buildFileUrl(channel: String, fileId: String, fileName: String): String {
val basePath: String = java.lang.String.format(
formatFriendlyGetFileUrl, pubnub.baseUrl(), pubnub.configuration.subscribeKey, channel, fileId, fileName
)
val queryParams = ArrayList()
val authKey = if (pubnub.configuration.authKey.isValid()) pubnub.configuration.authKey else null
if (PubNubUtil.shouldSignRequest(pubnub.configuration)) {
val timestamp: Int = pubnub.timestamp()
val signature: String = generateSignature(pubnub.configuration, basePath, authKey, timestamp)
queryParams.add(PubNubUtil.TIMESTAMP_QUERY_PARAM_NAME + "=" + timestamp)
queryParams.add(PubNubUtil.SIGNATURE_QUERY_PARAM_NAME + "=" + signature)
}
authKey?.run { queryParams.add(PubNubUtil.AUTH_QUERY_PARAM_NAME + "=" + authKey) }
return if (queryParams.isEmpty()) {
basePath
} else {
"$basePath?${queryParams.joinToString(separator = "&")}"
}
}
private fun generateSignature(
configuration: PNConfiguration,
url: String,
authKey: String?,
timestamp: Int,
): String {
val queryParams = mutableMapOf()
if (authKey != null) {
queryParams["auth"] = authKey
}
return PubNubUtil.generateSignature(
configuration, url, queryParams, "get", null, timestamp
)
}
private fun processMessage(subscribeMessage: SubscribeMessage): JsonElement? {
val input = subscribeMessage.payload
val cryptoModule = pubnub.cryptoModule ?: return input
// if we do not have a crypto module, there is no way to process the node; let's return.
// if the message couldn't possibly be encrypted in the first place, there is no way to process the node;
// let's return.
if (!subscribeMessage.supportsEncryption()) {
return input
}
val inputText = if (pubnub.mapper.isJsonObject(input!!) && pubnub.mapper.hasField(input, "pn_other")) {
pubnub.mapper.elementToString(input, "pn_other")
} else {
pubnub.mapper.elementToString(input)
}
val outputText = cryptoModule.decryptString(inputText!!)
var outputObject = pubnub.mapper.fromJson(outputText, JsonElement::class.java)
if (pubnub.mapper.isJsonObject(input) && pubnub.mapper.hasField(input, "pn_other")) {
val objectNode = pubnub.mapper.getAsObject(input)
pubnub.mapper.putOnObject(objectNode, "pn_other", outputObject)
outputObject = objectNode
}
return outputObject
}
private fun getDelta(delta: JsonElement?): List {
val list = mutableListOf()
delta?.let {
it.asJsonArray.forEach { item: JsonElement? ->
item?.let {
list.add(it.asString)
}
}
}
return list
}
}