All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.com.wisetrack.sdk.WiseTrackLinkResolution.kt Maven / Gradle / Ivy

The newest version!
package com.wisetrack.sdk

import android.net.Uri
import java.net.HttpURLConnection
import java.net.MalformedURLException
import java.net.URL
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

/**
@author hamed (@hamed-hsb)
 * @since 19th October 2021
 */

class WiseTrackLinkResolution private constructor() {
    interface WiseTrackLinkResolutionCallback {
        fun resolvedLinkCallback(resolvedLink: Uri?)
    }

    companion object {
        // https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
        @Volatile
        private var executor: ExecutorService? = null

        private const val maxRecursions = 10
        private val expectedUrlHostSuffixArray = arrayOf(
            "wisetrack.com",
            "go.link"
        )

        fun resolveLink(
            url: String?,
            resolveUrlSuffixArray: Array?,
            wiseTrackLinkResolutionCallback: WiseTrackLinkResolutionCallback?
        ) {
            if (wiseTrackLinkResolutionCallback == null) {
                return
            }
            if (url == null) {
                wiseTrackLinkResolutionCallback.resolvedLinkCallback(null)
                return
            }
            var originalURL: URL? = null
            try {
                originalURL = URL(url)
            } catch (ignored: MalformedURLException) {
            }
            if (originalURL == null) {
                wiseTrackLinkResolutionCallback.resolvedLinkCallback(null)
                return
            }
            if (!urlMatchesSuffix(
                    originalURL.host,
                    resolveUrlSuffixArray
                )
            ) {
                wiseTrackLinkResolutionCallback.resolvedLinkCallback(
                    convertToUri(originalURL)
                )
                return
            }
            if (executor == null) {
                synchronized(expectedUrlHostSuffixArray) {
                    if (executor == null) {
                        executor = Executors.newSingleThreadExecutor()
                    }
                }
            }
            val finalOriginalURL: URL = originalURL
            executor!!.execute(Runnable {
                requestAndResolve(
                    finalOriginalURL,
                    0,
                    wiseTrackLinkResolutionCallback
                )
            })
        }

        private fun resolveLink(
            responseUrl: URL?,
            previousUrl: URL,
            recursionNumber: Int,
            wiseTrackLinkResolutionCallback: WiseTrackLinkResolutionCallback
        ) {
            // return (possible null) previous url when the current one does not exist
            if (responseUrl == null) {
                wiseTrackLinkResolutionCallback.resolvedLinkCallback(
                    convertToUri(previousUrl)
                )
                return
            }

            // return found url with expected host
            if (isTerminalUrl(responseUrl.host)) {
                wiseTrackLinkResolutionCallback.resolvedLinkCallback(
                    convertToUri(responseUrl)
                )
                return
            }

            // return previous (non-null) url when it reached the max number of recursive tries
            if (recursionNumber > maxRecursions) {
                wiseTrackLinkResolutionCallback.resolvedLinkCallback(
                    convertToUri(responseUrl)
                )
                return
            }
            requestAndResolve(
                responseUrl,
                recursionNumber,
                wiseTrackLinkResolutionCallback
            )
        }

        private fun requestAndResolve(
            urlToRequest: URL,
            recursionNumber: Int,
            wiseTrackLinkResolutionCallback: WiseTrackLinkResolutionCallback
        ) {
            val httpsUrl: URL? = convertToHttps(urlToRequest)
            var resolvedURL: URL? = null
            var ucon: HttpURLConnection? = null
            try {
                ucon = httpsUrl!!.openConnection() as HttpURLConnection
                ucon.instanceFollowRedirects = false
                ucon.connect()
                val headerLocationField = ucon.getHeaderField("Location")
                if (headerLocationField != null) {
                    resolvedURL = URL(headerLocationField)
                }
            } catch (ignored: Throwable) {
            } finally {
                ucon?.disconnect()
                resolveLink(
                    resolvedURL,
                    httpsUrl!!,
                    recursionNumber + 1,
                    wiseTrackLinkResolutionCallback
                )
            }
        }

        private fun isTerminalUrl(urlHost: String): Boolean {
            return urlMatchesSuffix(
                urlHost,
                WiseTrackLinkResolution.expectedUrlHostSuffixArray
            )
        }

        private fun urlMatchesSuffix(urlHost: String?, suffixArray: Array?): Boolean {
            if (urlHost == null) {
                return false
            }
            if (suffixArray == null) {
                return false
            }
            for (expectedUrlHostSuffix in suffixArray) {
                if (urlHost.endsWith(expectedUrlHostSuffix)) {
                    return true
                }
            }
            return false
        }

        private fun convertToHttps(urlToConvert: URL?): URL? {
            if (urlToConvert == null) {
                return urlToConvert
            }
            val stringUrlToConvert = urlToConvert.toExternalForm() ?: return urlToConvert
            if (!stringUrlToConvert.startsWith("http:")) {
                return urlToConvert
            }
            var convertedUrl = urlToConvert
            try {
                convertedUrl = URL("https:" + stringUrlToConvert.substring(5))
            } catch (ignored: MalformedURLException) {
            }
            return convertedUrl
        }

        private fun convertToUri(url: URL?): Uri? {
            return if (url == null) {
                null
            } else Uri.parse(url.toString())
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy