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

nativeMain.fr.acinq.lightning.io.KtorNoTlsTcpSocket.kt Maven / Gradle / Ivy

package fr.acinq.lightning.io

import co.touchlab.kermit.Logger
import fr.acinq.lightning.logging.LoggerFactory
import fr.acinq.lightning.logging.error
import io.ktor.network.selector.*
import io.ktor.network.sockets.*
import io.ktor.network.tls.*
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.ClosedReceiveChannelException
import kotlinx.coroutines.channels.ClosedSendChannelException
import kotlin.time.Duration.Companion.seconds

/**
 * Uses ktor native socket implementation, which does not support TLS.
 */
class KtorNoTlsTcpSocket(private val socket: Socket) : TcpSocket {

    private val connection = socket.connection()

    override suspend fun send(bytes: ByteArray?, offset: Int, length: Int, flush: Boolean) =
        withContext(Dispatchers.IO) {
            ensureActive()
            try {
                if (bytes != null) connection.output.writeFully(bytes, offset, length)
                if (flush) connection.output.flush()
            } catch (ex: ClosedSendChannelException) {
                throw TcpSocket.IOException.ConnectionClosed(ex)
            } catch (ex: CancellationException) {
                throw ex
            } catch (ex: Throwable) {
                throw TcpSocket.IOException.Unknown(ex.message, ex)
            }
        }

    private inline fun  tryReceive(receive: () -> R): R {
        try {
            return receive()
        } catch (ex: ClosedReceiveChannelException) {
            throw TcpSocket.IOException.ConnectionClosed(ex)
        } catch (ex: CancellationException) {
            throw ex
        } catch (ex: Throwable) {
            throw TcpSocket.IOException.Unknown(ex.message, ex)
        }
    }

    private suspend fun  receive(read: suspend () -> R): R =
        withContext(Dispatchers.IO) {
            ensureActive()
            tryReceive { read() }
        }

    override suspend fun receiveFully(buffer: ByteArray, offset: Int, length: Int) {
        receive { connection.input.readFully(buffer, offset, length) }
    }

    override suspend fun receiveAvailable(buffer: ByteArray, offset: Int, length: Int): Int {
        return receive { connection.input.readAvailable(buffer, offset, length) }
            .takeUnless { it == -1 } ?: throw TcpSocket.IOException.ConnectionClosed()
    }

    override suspend fun startTls(tls: TcpSocket.TLS): TcpSocket = TODO("TLS not supported")

    override fun close() {
        // NB: this safely calls close(), wrapping it into a try/catch.
        socket.dispose()
    }

}

internal object KtorSocketBuilder : TcpSocket.Builder {
    override suspend fun connect(host: String, port: Int, tls: TcpSocket.TLS, loggerFactory: LoggerFactory): TcpSocket {
        return withContext(Dispatchers.IO) {
            try {
                val socket = aSocket(SelectorManager(Dispatchers.IO)).tcp().connect(host, port,
                    configure = {
                        keepAlive = true
                        socketTimeout = 15.seconds.inWholeMilliseconds
                        noDelay = true
                    }).let { socket ->
                    when (tls) {
                        is TcpSocket.TLS.DISABLED -> socket
                        else -> TODO("TLS not supported")
                    }
                }
                KtorNoTlsTcpSocket(socket)
            } catch (e: Exception) {
                throw e
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy