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.

LineBufferedSocketImplTest.kt 7.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. package com.dmdirc.ktirc.io
  2. import kotlinx.coroutines.*
  3. import org.junit.jupiter.api.Assertions.assertEquals
  4. import org.junit.jupiter.api.Assertions.assertNotNull
  5. import org.junit.jupiter.api.Test
  6. import org.junit.jupiter.api.parallel.Execution
  7. import org.junit.jupiter.api.parallel.ExecutionMode
  8. import java.net.ServerSocket
  9. import java.security.KeyStore
  10. import java.security.cert.X509Certificate
  11. import javax.net.ssl.KeyManagerFactory
  12. import javax.net.ssl.SSLContext
  13. import javax.net.ssl.X509TrustManager
  14. @ExperimentalCoroutinesApi
  15. @Execution(ExecutionMode.SAME_THREAD)
  16. internal class LineBufferedSocketImplTest {
  17. @Test
  18. fun `KtorLineBufferedSocket can connect to a server`() = runBlocking {
  19. ServerSocket(12321).use { serverSocket ->
  20. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
  21. val clientSocketAsync = GlobalScope.async { serverSocket.accept() }
  22. socket.connect()
  23. assertNotNull(clientSocketAsync.await())
  24. }
  25. }
  26. @Test
  27. fun `KtorLineBufferedSocket can send a byte array to a server`() = runBlocking {
  28. ServerSocket(12321).use { serverSocket ->
  29. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
  30. val clientBytesAsync = GlobalScope.async {
  31. ByteArray(13).apply {
  32. serverSocket.accept().getInputStream().read(this)
  33. }
  34. }
  35. socket.connect()
  36. socket.sendChannel.send("Hello World".toByteArray())
  37. val bytes = clientBytesAsync.await()
  38. assertNotNull(bytes)
  39. assertEquals("Hello World\r\n", String(bytes))
  40. }
  41. }
  42. @Test
  43. fun `KtorLineBufferedSocket can send a string to a server over TLS`() = runBlocking {
  44. tlsServerSocket(12321).use { serverSocket ->
  45. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321, true)
  46. socket.tlsTrustManager = getTrustingManager()
  47. val clientBytesAsync = GlobalScope.async {
  48. ByteArray(13).apply {
  49. serverSocket.accept().getInputStream().read(this)
  50. }
  51. }
  52. socket.connect()
  53. socket.sendChannel.send("Hello World".toByteArray())
  54. val bytes = clientBytesAsync.await()
  55. assertNotNull(bytes)
  56. assertEquals("Hello World\r\n", String(bytes))
  57. }
  58. }
  59. @Test
  60. fun `KtorLineBufferedSocket can receive a line of CRLF delimited text`() = runBlocking {
  61. ServerSocket(12321).use { serverSocket ->
  62. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
  63. GlobalScope.launch {
  64. serverSocket.accept().getOutputStream().write("Hi there\r\n".toByteArray())
  65. }
  66. socket.connect()
  67. assertEquals("Hi there", String(socket.receiveChannel.receive()))
  68. }
  69. }
  70. @Test
  71. fun `KtorLineBufferedSocket can receive a line of LF delimited text`() = runBlocking {
  72. ServerSocket(12321).use { serverSocket ->
  73. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
  74. GlobalScope.launch {
  75. serverSocket.accept().getOutputStream().write("Hi there\n".toByteArray())
  76. }
  77. socket.connect()
  78. assertEquals("Hi there", String(socket.receiveChannel.receive()))
  79. }
  80. }
  81. @Test
  82. fun `KtorLineBufferedSocket can receive multiple lines of text in one packet`() = runBlocking {
  83. ServerSocket(12321).use { serverSocket ->
  84. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
  85. GlobalScope.launch {
  86. serverSocket.accept().getOutputStream().write("Hi there\nThis is a test\r".toByteArray())
  87. }
  88. socket.connect()
  89. val lineProducer = socket.receiveChannel
  90. assertEquals("Hi there", String(lineProducer.receive()))
  91. assertEquals("This is a test", String(lineProducer.receive()))
  92. }
  93. }
  94. @Test
  95. fun `KtorLineBufferedSocket can receive multiple long lines of text`() = runBlocking {
  96. ServerSocket(12321).use { serverSocket ->
  97. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
  98. val line1 = "abcdefghijklmnopqrstuvwxyz".repeat(500)
  99. val line2 = "1234567890987654321[];'#,.".repeat(500)
  100. val line3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".repeat(500)
  101. GlobalScope.launch {
  102. serverSocket.accept().getOutputStream().write("$line1\r\n$line2\r$line3\n".toByteArray())
  103. }
  104. socket.connect()
  105. val lineProducer = socket.receiveChannel
  106. assertEquals(line1, String(lineProducer.receive()))
  107. assertEquals(line2, String(lineProducer.receive()))
  108. assertEquals(line3, String(lineProducer.receive()))
  109. }
  110. }
  111. @Test
  112. fun `KtorLineBufferedSocket can receive one line of text over multiple packets`() = runBlocking {
  113. ServerSocket(12321).use { serverSocket ->
  114. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
  115. GlobalScope.launch {
  116. with(serverSocket.accept().getOutputStream()) {
  117. write("Hi".toByteArray())
  118. flush()
  119. write(" t".toByteArray())
  120. flush()
  121. write("here\r\n".toByteArray())
  122. flush()
  123. }
  124. }
  125. socket.connect()
  126. val lineProducer = socket.receiveChannel
  127. assertEquals("Hi there", String(lineProducer.receive()))
  128. }
  129. }
  130. @Test
  131. fun `KtorLineBufferedSocket returns from readLines when socket is closed`() = runBlocking {
  132. ServerSocket(12321).use { serverSocket ->
  133. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
  134. GlobalScope.launch {
  135. with(serverSocket.accept()) {
  136. getOutputStream().write("Hi there\r\n".toByteArray())
  137. close()
  138. }
  139. }
  140. socket.connect()
  141. val lineProducer = socket.receiveChannel
  142. assertEquals("Hi there", String(lineProducer.receive()))
  143. }
  144. }
  145. @Test
  146. fun `KtorLineBufferedSocket disconnects from server`() = runBlocking {
  147. ServerSocket(12321).use { serverSocket ->
  148. val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
  149. val clientSocketAsync = GlobalScope.async { serverSocket.accept() }
  150. socket.connect()
  151. socket.disconnect()
  152. assertEquals(-1, clientSocketAsync.await().getInputStream().read()) { "Server socket should EOF after KtorLineBufferedSocket disconnects" }
  153. }
  154. }
  155. private fun tlsServerSocket(port: Int): ServerSocket {
  156. val keyStore = KeyStore.getInstance("PKCS12")
  157. keyStore.load(LineBufferedSocketImplTest::class.java.getResourceAsStream("localhost.p12"), CharArray(0))
  158. val keyManagerFactory = KeyManagerFactory.getInstance("PKIX")
  159. keyManagerFactory.init(keyStore, CharArray(0))
  160. val sslContext = SSLContext.getInstance("TLSv1.2")
  161. sslContext.init(keyManagerFactory.keyManagers, null, null)
  162. return sslContext.serverSocketFactory.createServerSocket(port)
  163. }
  164. private fun getTrustingManager() = object : X509TrustManager {
  165. override fun getAcceptedIssuers(): Array<X509Certificate> = emptyArray()
  166. override fun checkClientTrusted(certs: Array<X509Certificate>, authType: String) {}
  167. override fun checkServerTrusted(certs: Array<X509Certificate>, authType: String) {}
  168. }
  169. }