You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Sockets.kt 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package com.dmdirc.ktirc.io
  2. import kotlinx.coroutines.CancellableContinuation
  3. import kotlinx.coroutines.CoroutineScope
  4. import kotlinx.coroutines.io.ByteChannel
  5. import kotlinx.coroutines.io.ByteWriteChannel
  6. import kotlinx.coroutines.io.close
  7. import kotlinx.coroutines.launch
  8. import kotlinx.coroutines.suspendCancellableCoroutine
  9. import kotlinx.io.pool.DefaultPool
  10. import java.net.SocketAddress
  11. import java.nio.ByteBuffer
  12. import java.nio.channels.*
  13. import kotlin.coroutines.resume
  14. import kotlin.coroutines.resumeWithException
  15. internal const val BUFFER_SIZE = 32768
  16. internal const val POOL_SIZE = 128
  17. internal val defaultPool = ByteBufferPool()
  18. internal class ByteBufferPool : DefaultPool<ByteBuffer>(POOL_SIZE) {
  19. override fun produceInstance(): ByteBuffer = ByteBuffer.allocate(BUFFER_SIZE)
  20. override fun clearInstance(instance: ByteBuffer): ByteBuffer = instance.apply { clear() }
  21. inline fun <T> borrow(block: (ByteBuffer) -> T): T {
  22. val buffer = borrow()
  23. try {
  24. return block(buffer)
  25. } finally {
  26. recycle(buffer)
  27. }
  28. }
  29. }
  30. internal interface Socket {
  31. fun bind(socketAddress: SocketAddress)
  32. suspend fun connect(socketAddress: SocketAddress)
  33. suspend fun read(buffer: ByteBuffer): Int
  34. fun close()
  35. val write: ByteWriteChannel
  36. val isOpen: Boolean
  37. }
  38. internal class PlainTextSocket(private val scope: CoroutineScope) : Socket {
  39. private val client = AsynchronousSocketChannel.open()
  40. private var writeChannel = ByteChannel(autoFlush = true)
  41. override val write: ByteWriteChannel
  42. get() = writeChannel
  43. override val isOpen: Boolean
  44. get() = client.isOpen
  45. override fun bind(socketAddress: SocketAddress) {
  46. client.bind(socketAddress)
  47. }
  48. override suspend fun connect(socketAddress: SocketAddress) {
  49. writeChannel = ByteChannel(autoFlush = true)
  50. suspendCancellableCoroutine<Unit> { continuation ->
  51. client.closeOnCancel(continuation)
  52. client.connect(socketAddress, continuation, AsyncVoidIOHandler)
  53. }
  54. scope.launch { writeLoop() }
  55. }
  56. override fun close() {
  57. writeChannel.close()
  58. client.close()
  59. }
  60. override suspend fun read(buffer: ByteBuffer) = try {
  61. val bytes = suspendCancellableCoroutine<Int> { continuation ->
  62. client.closeOnCancel(continuation)
  63. client.read(buffer, continuation, asyncIOHandler())
  64. }
  65. if (bytes == -1) {
  66. close()
  67. }
  68. bytes
  69. } catch (_: ClosedChannelException) {
  70. // Ignore
  71. 0
  72. }
  73. private suspend fun writeLoop() {
  74. while (client.isOpen) {
  75. defaultPool.borrow { buffer ->
  76. writeChannel.readAvailable(buffer)
  77. buffer.flip()
  78. try {
  79. suspendCancellableCoroutine<Int> { continuation ->
  80. client.closeOnCancel(continuation)
  81. client.write(buffer, continuation, asyncIOHandler())
  82. }
  83. } catch (_: ClosedChannelException) {
  84. // Ignore
  85. }
  86. }
  87. }
  88. }
  89. }
  90. private fun Channel.closeOnCancel(cont: CancellableContinuation<*>) {
  91. cont.invokeOnCancellation {
  92. try {
  93. close()
  94. } catch (ex: Throwable) {
  95. // Specification says that it is Ok to call it any time, but reality is different,
  96. // so we have just to ignore exception
  97. }
  98. }
  99. }
  100. @Suppress("UNCHECKED_CAST")
  101. private fun <T> asyncIOHandler(): CompletionHandler<T, CancellableContinuation<T>> =
  102. AsyncIOHandlerAny as CompletionHandler<T, CancellableContinuation<T>>
  103. private object AsyncIOHandlerAny : CompletionHandler<Any, CancellableContinuation<Any>> {
  104. override fun completed(result: Any, cont: CancellableContinuation<Any>) {
  105. cont.resume(result)
  106. }
  107. override fun failed(ex: Throwable, cont: CancellableContinuation<Any>) {
  108. // just return if already cancelled and got an expected exception for that case
  109. if (ex is AsynchronousCloseException && cont.isCancelled) return
  110. cont.resumeWithException(ex)
  111. }
  112. }
  113. private object AsyncVoidIOHandler : CompletionHandler<Void?, CancellableContinuation<Unit>> {
  114. override fun completed(result: Void?, cont: CancellableContinuation<Unit>) {
  115. cont.resume(Unit)
  116. }
  117. override fun failed(ex: Throwable, cont: CancellableContinuation<Unit>) {
  118. // just return if already cancelled and got an expected exception for that case
  119. if (ex is AsynchronousCloseException && cont.isCancelled) return
  120. cont.resumeWithException(ex)
  121. }
  122. }