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

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

Go to download

Library providing common functionality for interacting with the Pylons ecosystem

The newest version!
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