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

tech.pylons.ipc.HttpIpcWire.kt Maven / Gradle / Ivy

package tech.pylons.ipc

import tech.pylons.lib.logging.LogEvent
import tech.pylons.lib.logging.LogTag
import tech.pylons.lib.logging.Logger
import org.apache.commons.codec.binary.Hex
import java.net.ServerSocket
import java.nio.ByteBuffer
import java.nio.charset.Charset

private const val HANDSHAKE_MAGIC = "__PYLONS_WALLET_SERVER"
private const val HANDSHAKE_REPLY_MAGIC = "__PYLONS_WALLET_CLIENT"

object HttpIpcWire {
    private val ascii = Charset.forName("US-ASCII")

    private fun readNext(): ByteArray? {
        Logger.implementation.log(LogEvent.AWAIT_TCP, "{\"info\":\"reading bytes from client\"}", LogTag.info)
        ServerSocket(50001).use { serverSocket ->
            serverSocket.accept().use { socket ->
                Logger.implementation.log(LogEvent.ACCEPTED_CLIENT, "{\"client\":\"${socket.localAddress}\"}", LogTag.info)
                socket.getInputStream().use {
                    while (it.available() == 0) Thread.sleep(100)
                    val lenBuffer = ByteArray(4)
                    var l = it.read(lenBuffer, 0, lenBuffer.size)
                    if (l != 4) {
                        throw Exception("Got $l bytes down when getting data length; expected a 32-bit integer (4 octets)")
                    }
                    val len = ByteBuffer.wrap(lenBuffer).int
                    val dataBuffer = ByteArray(len)
                    l = it.read(dataBuffer, 0, len)
                    if (l != len) {
                        throw Exception("Got $l bytes down; expected $len")
                    }
                    Logger.implementation.log(
                        LogEvent.GOT_BYTES,
                        """{"msg":"${ascii.decode(ByteBuffer.wrap(dataBuffer)).subSequence(0, len)}"}""", LogTag.info)
                    if (len == 0) return null
                    val m = ByteArray(len)
                    for (i in 0 until len) m[i] = dataBuffer[i]

                    return m
                }
            }
        }
    }

    fun writeString(s: String) =
        writeBytes(ascii.encode(s).array())

    private fun writeBytes(bytes: ByteArray) {
        Logger.implementation.log(
            LogEvent.AWAIT_TCP,
            "{\"info\":\"Start write... (${bytes.size}) bytes\"}", LogTag.info)
        ServerSocket(50001).use { serverSocket ->
            serverSocket.accept().use { socket ->
                Logger.implementation.log(
                    LogEvent.ACCEPTED_CLIENT,
                    "{\"client\":\"${socket.localAddress}\"}", LogTag.info)
                socket.getOutputStream().use {
                    val lenBuffer = ByteArray(4)
                    ByteBuffer.wrap(lenBuffer).putInt(bytes.size)
                    it.write(lenBuffer + bytes)
                    it.flush()
                    Logger.implementation.log(
                        LogEvent.SENT_BYTES,
                        "{\"data\":\"${bytes}\"}", LogTag.info)
                }
            }
        }
    }

    private fun getStringRaw(): String {
        var m: ByteArray? = null
        while (m == null) m = readNext()
        return ascii.decode(ByteBuffer.wrap(m)).toString()
    }

    private fun checkForHandshake(): String {
        // HACK: We use reflection to get current process handle here to ensure we compile on java 8.
        // It'll crash if it actually runs on j8, but that's fine; this code should never actually
        // run on Android devices. Reflection is obv. considerably slower, but that doesn't
        // matter b/c we only grab the PID once when we attach to a process.
        val pid = (Class.forName("java.lang.ProcessHandle").getMethod("current").invoke(null) as ProcessHandle).pid()
        val byteBuffer = ByteBuffer.allocate(Long.SIZE_BYTES + 4).putInt(IPCLayer.implementation!!.walletId).putLong(pid)
        writeBytes(ascii.encode(HANDSHAKE_MAGIC).array() + byteBuffer.array())
        println("getting reply now")
        return getStringRaw()
    }

    fun doHandshake () {
        println("start handshake")
        val handshakeReply = checkForHandshake()
        println("foo")
        if (!handshakeReply.startsWith(HANDSHAKE_REPLY_MAGIC)) {
            Logger.implementation.log(
                LogEvent.IPC_HANDSHAKE_FAIL,
                """{"handshake_reply":"$handshakeReply"}""",
                LogTag.walletError)
        }
        println("bar")
        IPCLayer.implementation!!.clientId = handshakeReply.removePrefix(HANDSHAKE_REPLY_MAGIC).toInt()
        println("handshake OK")
        writeString("OKfillerfillerfillerfillerfillerfiller")
    }

    fun readMessage(): String? {
        val bytes = readNext() ?: return null
        val json = ascii.decode(ByteBuffer.wrap(bytes)).toString()
        Logger.implementation.log(
            LogEvent.PARSED_MESSAGE,
            """{"bytes":"${Hex.encodeHexString(bytes)}", "msg":{$json}""", LogTag.info)
        return json
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy