Browse Source

Prefer IPv6, default to TLS

tags/v1.0.0
Chris Smith 5 years ago
parent
commit
f1ecbf256f

+ 24
- 20
CHANGELOG View File

@@ -5,6 +5,10 @@ vNEXT (in development)
5 5
    client certificate.
6 6
  * Added NicknameChangeRequired event for the case when a nickname is
7 7
    not allowed during connection and *MUST* be changed
8
+ * BREAKING: Added 'PreferIPv6' behaviour config, defaulting to true.
9
+   With this configuration KtIrc will try to use IPv6 if the server
10
+   publishes AAAA records.
11
+ * BREAKING: Default port is now 6697, TLS is enabled by default
8 12
 
9 13
 v0.11.0
10 14
 
@@ -88,7 +92,7 @@ v0.8.0
88 92
 v0.7.0
89 93
 
90 94
  * Fixed experimental API warnings when using IrcClient
91
- * IrcClients are now constructed using a DSL
95
+ * BREAKING: IrcClients are now constructed using a DSL
92 96
    * Users of the library no longer need to care about the implementing class
93 97
    * Facilitates adding more options in the future without breaking existing implementations
94 98
  * SASL improvements
@@ -144,31 +148,31 @@ v0.4.0
144 148
 
145 149
 v0.3.1
146 150
 
147
-  * Added more documentation to public methods/classes
148
-  * Fixed exception when sending multiple lines at once (e.g. when connecting!)
151
+ * Added more documentation to public methods/classes
152
+ * Fixed exception when sending multiple lines at once (e.g. when connecting!)
149 153
 
150 154
 v0.3.0
151 155
 
152
-  * Simplified how messages are constructed.
153
-    Instead of: client.send(joinMessage("#channel"))
154
-    Now use: client.sendJoin("#channel")
155
-  * Added reply utility to easily send replies to message events
156
-  * Server state improvements:
157
-    * Added status field to ServerState
158
-    * ServerConnected event is emitted as soon as the socket is connected
159
-    * ServerReady event is emitted after logging in, negotiating, etc
160
-  * Added extra debugging to show what type of events are being dispatched
161
-  * Added ChannelQuit event, raised for each channel a user is in when they quit
162
-  * (Internal) Event handlers can now return more events to emit
156
+ * Simplified how messages are constructed.
157
+   Instead of: client.send(joinMessage("#channel"))
158
+   Now use: client.sendJoin("#channel")
159
+ * Added reply utility to easily send replies to message events
160
+ * Server state improvements:
161
+   * Added status field to ServerState
162
+   * ServerConnected event is emitted as soon as the socket is connected
163
+   * ServerReady event is emitted after logging in, negotiating, etc
164
+ * Added extra debugging to show what type of events are being dispatched
165
+ * Added ChannelQuit event, raised for each channel a user is in when they quit
166
+ * (Internal) Event handlers can now return more events to emit
163 167
 
164 168
 v0.2.1
165 169
 
166
-  * Added documentation and reduced visibility of some internal methods/classes
167
-  * (Internal) Enabled Travis, Codacy and Coveralls
170
+ * Added documentation and reduced visibility of some internal methods/classes
171
+ * (Internal) Enabled Travis, Codacy and Coveralls
168 172
 
169 173
 v0.2.0
170 174
 
171
-  * Added support for connecting over TLS
172
-  * Simplified how event handlers are registered
173
-  * Improved use of coroutines so users don't have to worry about them
174
-  * (Internal) Upgraded to Gradle 5.1.1
175
+ * Added support for connecting over TLS
176
+ * BREAKING: Simplified how event handlers are registered
177
+ * BREAKING: Improved use of coroutines so users don't have to worry about them
178
+ * (Internal) Upgraded to Gradle 5.1.1

+ 6
- 2
docs/index.adoc View File

@@ -237,8 +237,8 @@ The server block allows you to specify the details of the IRC server you
237 237
 wish to connect to:
238 238
 
239 239
  * `host` - the hostname or IP address of the server *(required)*
240
- * `port` - the port to connect on _(default: 6667)_
241
- * `useTls` - whether to use a secure connection or not _(default: false)_
240
+ * `port` - the port to connect on _(default: 6697)_
241
+ * `useTls` - whether to use a secure connection or not _(default: true)_
242 242
  * `password` - the password to provide to the server _(default: null)_
243 243
 
244 244
 An alternative more compact syntax is available for configuring server details:
@@ -296,6 +296,10 @@ advanced IRC features even if the server doesn't support them:
296 296
    in a `MessageReceived` event being returned. Servers that support the
297 297
    IRCv3 `echo-message` capability will do this automatically; enabling the
298 298
    behaviour will make all servers act the same way _(default: false)_
299
+ * `preferIPv6` - if enabled, KtIrc will prefer to connect over IPv6 if the
300
+   server publishes AAAA DNS records. If disabled, KtIrc will prefer IPv4.
301
+   If the server is available exclusively on IPv4 or IPv6 then this option
302
+   has no effect. _(default: true)_
299 303
 
300 304
 The behaviour block is optional in its entirety.
301 305
 

+ 5
- 3
src/main/kotlin/com/dmdirc/ktirc/Dsl.kt View File

@@ -34,6 +34,7 @@ internal data class IrcClientConfig(
34 34
  * behaviour {
35 35
  *     requestModesOnJoin = true
36 36
  *     alwaysEchoMessages = true
37
+ *     preferIPv6 = false
37 38
  * }
38 39
  *
39 40
  * sasl {
@@ -141,10 +142,10 @@ class IrcClientConfigBuilder {
141 142
 class ServerConfig {
142 143
     /** The hostname (or IP address) of the server to connect to. */
143 144
     var host: String = ""
144
-    /** The port to connect on. Defaults to 6667. */
145
-    var port: Int = 6667
145
+    /** The port to connect on. Defaults to 6697. */
146
+    var port: Int = 6697
146 147
     /** Whether or not to use TLS (an encrypted connection). */
147
-    var useTls: Boolean = false
148
+    var useTls: Boolean = true
148 149
     /** The password required to connect to the server, if any. */
149 150
     var password: String? = null
150 151
 }
@@ -212,4 +213,5 @@ class SaslConfig {
212 213
 class BehaviourConfig : ClientBehaviour {
213 214
     override var requestModesOnJoin = false
214 215
     override var alwaysEchoMessages = false
216
+    override var preferIPv6 = true
215 217
 }

+ 7
- 0
src/main/kotlin/com/dmdirc/ktirc/IrcClient.kt View File

@@ -198,6 +198,13 @@ interface ClientBehaviour {
198 198
      */
199 199
     val alwaysEchoMessages: Boolean
200 200
 
201
+    /**
202
+     * If enabled, KtIRC will try to connect to IRC servers over IPv6 if they publish the appropriate DNS entries.
203
+     *
204
+     * Otherwise, KtIrc will prefer IPv4.
205
+     */
206
+    val preferIPv6: Boolean
207
+
201 208
 }
202 209
 
203 210
 /**

+ 32
- 4
src/main/kotlin/com/dmdirc/ktirc/IrcClientImpl.kt View File

@@ -10,12 +10,15 @@ import com.dmdirc.ktirc.io.MessageParser
10 10
 import com.dmdirc.ktirc.messages.*
11 11
 import com.dmdirc.ktirc.messages.processors.messageProcessors
12 12
 import com.dmdirc.ktirc.model.*
13
+import com.dmdirc.ktirc.util.RemoveIn
13 14
 import com.dmdirc.ktirc.util.currentTimeProvider
14 15
 import com.dmdirc.ktirc.util.generateLabel
15 16
 import com.dmdirc.ktirc.util.logger
16 17
 import kotlinx.coroutines.*
17 18
 import kotlinx.coroutines.channels.Channel
18 19
 import kotlinx.coroutines.channels.map
20
+import java.net.Inet6Address
21
+import java.net.InetAddress
19 22
 import java.time.Duration
20 23
 import java.util.concurrent.atomic.AtomicBoolean
21 24
 import java.util.logging.Level
@@ -23,8 +26,6 @@ import java.util.logging.Level
23 26
 /**
24 27
  * Concrete implementation of an [IrcClient].
25 28
  */
26
-// TODO: How should alternative nicknames work?
27
-// TODO: Should IRC Client take a pool of servers and rotate through, or make the caller do that?
28 29
 internal class IrcClientImpl(private val config: IrcClientConfig) : ExperimentalIrcClient, CoroutineScope {
29 30
 
30 31
     private val log by logger()
@@ -33,7 +34,10 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : Experimental
33 34
     override val coroutineContext = GlobalScope.newCoroutineContext(Dispatchers.IO)
34 35
 
35 36
     @ExperimentalCoroutinesApi
36
-    internal var socketFactory: (CoroutineScope, String, Int, Boolean) -> LineBufferedSocket = ::LineBufferedSocketImpl
37
+    internal var socketFactory: (CoroutineScope, String, String, Int, Boolean) -> LineBufferedSocket = ::LineBufferedSocketImpl
38
+    internal var resolver: (String) -> Collection<ResolveResult> = { host ->
39
+        InetAddress.getAllByName(host).map { ResolveResult(it.hostAddress, it is Inet6Address) }
40
+    }
37 41
 
38 42
     internal var asyncTimeout = Duration.ofSeconds(20)
39 43
 
@@ -51,6 +55,8 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : Experimental
51 55
 
52 56
     private val connecting = AtomicBoolean(false)
53 57
 
58
+    @Deprecated("Use structured send instead", ReplaceWith("send(command, arguments)"))
59
+    @RemoveIn("2.0.0")
54 60
     override fun send(message: String) {
55 61
         socket?.sendChannel?.offer(message.toByteArray()) ?: log.warning { "No send channel for message: $message" }
56 62
     }
@@ -81,8 +87,18 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : Experimental
81 87
     override fun connect() {
82 88
         check(!connecting.getAndSet(true))
83 89
 
90
+        val ip: String
91
+        try {
92
+            ip = resolve(config.server.host)
93
+        } catch (ex: Exception) {
94
+            log.log(Level.SEVERE, ex) { "Error resolving ${config.server.host}" }
95
+            emitEvent(ServerConnectionError(EventMetadata(currentTimeProvider()), ConnectionError.UnresolvableAddress, ex.localizedMessage))
96
+            reset()
97
+            return
98
+        }
99
+
84 100
         @Suppress("EXPERIMENTAL_API_USAGE")
85
-        with(socketFactory(this, config.server.host, config.server.port, config.server.useTls)) {
101
+        with(socketFactory(this, config.server.host, ip, config.server.port, config.server.useTls)) {
86 102
             socket = this
87 103
 
88 104
             emitEvent(ServerConnecting(EventMetadata(currentTimeProvider())))
@@ -129,6 +145,16 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : Experimental
129 145
         }
130 146
     }
131 147
 
148
+    private fun resolve(host: String): String {
149
+        val hosts = resolver(host)
150
+        val preferredHosts = hosts.filter { it.isV6 == behaviour.preferIPv6 }
151
+        return if (preferredHosts.isNotEmpty()) {
152
+            preferredHosts.random().ip
153
+        } else {
154
+            hosts.random().ip
155
+        }
156
+    }
157
+
132 158
     internal fun reset() {
133 159
         serverState.reset()
134 160
         channelState.clear()
@@ -138,3 +164,5 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : Experimental
138 164
     }
139 165
 
140 166
 }
167
+
168
+internal data class ResolveResult(val ip: String, val isV6: Boolean)

+ 2
- 3
src/main/kotlin/com/dmdirc/ktirc/io/LineBufferedSocket.kt View File

@@ -31,7 +31,7 @@ internal interface LineBufferedSocket {
31 31
  */
32 32
 // TODO: Expose advanced TLS options
33 33
 @ExperimentalCoroutinesApi
34
-internal class LineBufferedSocketImpl(coroutineScope: CoroutineScope, private val host: String, private val port: Int, private val tls: Boolean = false) : CoroutineScope, LineBufferedSocket {
34
+internal class LineBufferedSocketImpl(coroutineScope: CoroutineScope, private val host: String, private val ip: String, private val port: Int, private val tls: Boolean = false) : CoroutineScope, LineBufferedSocket {
35 35
 
36 36
     companion object {
37 37
         const val CARRIAGE_RETURN = '\r'.toByte()
@@ -59,8 +59,7 @@ internal class LineBufferedSocketImpl(coroutineScope: CoroutineScope, private va
59 59
                     socket = TlsSocket(this@LineBufferedSocketImpl, socket, this, host)
60 60
                 }
61 61
             }
62
-            socket.connect(InetSocketAddress(host, port))
63
-            println("connected!")
62
+            socket.connect(InetSocketAddress(ip, port))
64 63
             writeChannel = socket.write
65 64
         }
66 65
         launch { writeLines() }

+ 132
- 5
src/test/kotlin/com/dmdirc/ktirc/IrcClientImplTest.kt View File

@@ -17,6 +17,7 @@ import org.junit.jupiter.api.Assertions.*
17 17
 import org.junit.jupiter.api.BeforeEach
18 18
 import org.junit.jupiter.api.Test
19 19
 import org.junit.jupiter.api.assertThrows
20
+import java.net.UnknownHostException
20 21
 import java.nio.channels.UnresolvedAddressException
21 22
 import java.security.cert.CertificateException
22 23
 import java.util.concurrent.atomic.AtomicReference
@@ -26,6 +27,8 @@ internal class IrcClientImplTest {
26 27
 
27 28
     companion object {
28 29
         private const val HOST = "thegibson.com"
30
+        private const val HOST2 = "irc.thegibson.com"
31
+        private const val IP = "127.0.13.37"
29 32
         private const val PORT = 12345
30 33
         private const val NICK = "AcidBurn"
31 34
         private const val REAL_NAME = "Kate Libby"
@@ -41,8 +44,13 @@ internal class IrcClientImplTest {
41 44
         every { sendChannel } returns sendLineChannel
42 45
     }
43 46
 
44
-    private val mockSocketFactory = mockk<(CoroutineScope, String, Int, Boolean) -> LineBufferedSocket> {
45
-        every { this@mockk.invoke(any(), eq(HOST), eq(PORT), any()) } returns mockSocket
47
+    private val mockSocketFactory = mockk<(CoroutineScope, String, String, Int, Boolean) -> LineBufferedSocket> {
48
+        every { this@mockk.invoke(any(), eq(HOST), eq(IP), eq(PORT), any()) } returns mockSocket
49
+        every { this@mockk.invoke(any(), eq(HOST2), any(), eq(PORT), any()) } returns mockSocket
50
+    }
51
+
52
+    private val mockResolver = mockk<(String) -> Collection<ResolveResult>> {
53
+        every { this@mockk.invoke(HOST) } returns listOf(ResolveResult(IP, false))
46 54
     }
47 55
 
48 56
     private val mockEventHandler = mockk<(IrcEvent) -> Unit> {
@@ -58,6 +66,7 @@ internal class IrcClientImplTest {
58 66
     private val serverConfig = ServerConfig().apply {
59 67
         host = HOST
60 68
         port = PORT
69
+        useTls = false
61 70
     }
62 71
 
63 72
     private val normalConfig = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig(), null)
@@ -71,9 +80,10 @@ internal class IrcClientImplTest {
71 80
     fun `uses socket factory to create a new socket on connect`() {
72 81
         val client = IrcClientImpl(normalConfig)
73 82
         client.socketFactory = mockSocketFactory
83
+        client.resolver = mockResolver
74 84
         client.connect()
75 85
 
76
-        verify(timeout = 500) { mockSocketFactory(client, HOST, PORT, false) }
86
+        verify(timeout = 500) { mockSocketFactory(client, HOST, IP, PORT, false) }
77 87
     }
78 88
 
79 89
     @Test
@@ -84,15 +94,111 @@ internal class IrcClientImplTest {
84 94
             useTls = true
85 95
         }, profileConfig, BehaviourConfig(), null))
86 96
         client.socketFactory = mockSocketFactory
97
+        client.resolver = mockResolver
98
+        client.connect()
99
+
100
+        verify(timeout = 500) { mockSocketFactory(client, HOST, IP, PORT, true) }
101
+    }
102
+
103
+    @Test
104
+    fun `prefers ipv6 addresses if behaviour is enabled`() {
105
+        val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
106
+            host = HOST2
107
+            port = PORT
108
+        }, profileConfig, BehaviourConfig().apply { preferIPv6 = true }, null))
109
+
110
+        every { mockResolver(HOST2) } returns listOf(
111
+                ResolveResult(IP, false),
112
+                ResolveResult("::13:37", true),
113
+                ResolveResult("0.0.0.0", false)
114
+        )
115
+
116
+        client.socketFactory = mockSocketFactory
117
+        client.resolver = mockResolver
118
+        client.connect()
119
+
120
+        verify(timeout = 500) { mockSocketFactory(client, HOST2, "::13:37", PORT, true) }
121
+    }
122
+
123
+    @Test
124
+    fun `falls back to ipv4 if no ipv6 addresses are available`() {
125
+        val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
126
+            host = HOST2
127
+            port = PORT
128
+        }, profileConfig, BehaviourConfig().apply { preferIPv6 = true }, null))
129
+
130
+        every { mockResolver(HOST2) } returns listOf(
131
+                ResolveResult("0.0.0.0", false)
132
+        )
133
+
134
+        client.socketFactory = mockSocketFactory
135
+        client.resolver = mockResolver
87 136
         client.connect()
88 137
 
89
-        verify(timeout = 500) { mockSocketFactory(client, HOST, PORT, true) }
138
+        verify(timeout = 500) { mockSocketFactory(client, HOST2, "0.0.0.0", PORT, true) }
139
+    }
140
+
141
+    @Test
142
+    fun `prefers ipv4 addresses if ipv6 behaviour is disabled`() {
143
+        val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
144
+            host = HOST2
145
+            port = PORT
146
+        }, profileConfig, BehaviourConfig().apply { preferIPv6 = false }, null))
147
+
148
+        every { mockResolver(HOST2) } returns listOf(
149
+                ResolveResult("::13:37", true),
150
+                ResolveResult("::313:37", true),
151
+                ResolveResult("0.0.0.0", false)
152
+        )
153
+
154
+        client.socketFactory = mockSocketFactory
155
+        client.resolver = mockResolver
156
+        client.connect()
157
+
158
+        verify(timeout = 500) { mockSocketFactory(client, HOST2, "0.0.0.0", PORT, true) }
159
+    }
160
+
161
+    @Test
162
+    fun `falls back to ipv6 if no ipv4 addresses available`() {
163
+        val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
164
+            host = HOST2
165
+            port = PORT
166
+        }, profileConfig, BehaviourConfig().apply { preferIPv6 = false }, null))
167
+
168
+        every { mockResolver(HOST2) } returns listOf(
169
+                ResolveResult("::13:37", true)
170
+        )
171
+
172
+        client.socketFactory = mockSocketFactory
173
+        client.resolver = mockResolver
174
+        client.connect()
175
+
176
+        verify(timeout = 500) { mockSocketFactory(client, HOST2, "::13:37", PORT, true) }
177
+    }
178
+
179
+    @Test
180
+    fun `raises error if dns fails`() {
181
+        val client = IrcClientImpl(IrcClientConfig(ServerConfig().apply {
182
+            host = HOST2
183
+        }, profileConfig, BehaviourConfig().apply { preferIPv6 = true }, null))
184
+
185
+        every { mockResolver(HOST2) } throws UnknownHostException("oops")
186
+
187
+        client.socketFactory = mockSocketFactory
188
+        client.resolver = mockResolver
189
+        client.onEvent(mockEventHandler)
190
+        client.connect()
191
+
192
+        verify(timeout = 500) {
193
+            mockEventHandler(match { it is ServerConnectionError && it.error == ConnectionError.UnresolvableAddress })
194
+        }
90 195
     }
91 196
 
92 197
     @Test
93 198
     fun `throws if socket already exists`() {
94 199
         val client = IrcClientImpl(normalConfig)
95 200
         client.socketFactory = mockSocketFactory
201
+        client.resolver = mockResolver
96 202
         client.connect()
97 203
 
98 204
         assertThrows<IllegalStateException> {
@@ -112,6 +218,7 @@ internal class IrcClientImplTest {
112 218
 
113 219
         val client = IrcClientImpl(normalConfig)
114 220
         client.socketFactory = mockSocketFactory
221
+        client.resolver = mockResolver
115 222
         client.onEvent(mockEventHandler)
116 223
         client.connect()
117 224
 
@@ -128,6 +235,7 @@ internal class IrcClientImplTest {
128 235
     fun `sends basic connection strings`() = runBlocking {
129 236
         val client = IrcClientImpl(normalConfig)
130 237
         client.socketFactory = mockSocketFactory
238
+        client.resolver = mockResolver
131 239
         client.connect()
132 240
 
133 241
         assertEquals("CAP LS 302", String(sendLineChannel.receive()))
@@ -143,6 +251,7 @@ internal class IrcClientImplTest {
143 251
             password = PASSWORD
144 252
         }, profileConfig, BehaviourConfig(), null))
145 253
         client.socketFactory = mockSocketFactory
254
+        client.resolver = mockResolver
146 255
         client.connect()
147 256
 
148 257
         assertEquals("CAP LS 302", String(sendLineChannel.receive()))
@@ -153,6 +262,7 @@ internal class IrcClientImplTest {
153 262
     fun `sends events to provided event handler`() {
154 263
         val client = IrcClientImpl(normalConfig)
155 264
         client.socketFactory = mockSocketFactory
265
+        client.resolver = mockResolver
156 266
         client.onEvent(mockEventHandler)
157 267
 
158 268
         GlobalScope.launch {
@@ -204,6 +314,7 @@ internal class IrcClientImplTest {
204 314
     fun `sends text to socket`() = runBlocking {
205 315
         val client = IrcClientImpl(normalConfig)
206 316
         client.socketFactory = mockSocketFactory
317
+        client.resolver = mockResolver
207 318
         client.connect()
208 319
 
209 320
         client.send("testing 123")
@@ -215,6 +326,7 @@ internal class IrcClientImplTest {
215 326
     fun `sends structured text to socket`() = runBlocking {
216 327
         val client = IrcClientImpl(normalConfig)
217 328
         client.socketFactory = mockSocketFactory
329
+        client.resolver = mockResolver
218 330
         client.connect()
219 331
 
220 332
         client.send("testing", "123", "456")
@@ -227,6 +339,7 @@ internal class IrcClientImplTest {
227 339
         val config = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig().apply { alwaysEchoMessages = true }, null)
228 340
         val client = IrcClientImpl(config)
229 341
         client.socketFactory = mockSocketFactory
342
+        client.resolver = mockResolver
230 343
 
231 344
         val slot = slot<MessageReceived>()
232 345
         val mockkEventHandler = mockk<(IrcEvent) -> Unit>(relaxed = true)
@@ -250,6 +363,7 @@ internal class IrcClientImplTest {
250 363
         val config = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig().apply { alwaysEchoMessages = true }, null)
251 364
         val client = IrcClientImpl(config)
252 365
         client.socketFactory = mockSocketFactory
366
+        client.resolver = mockResolver
253 367
         client.serverState.capabilities.enabledCapabilities[Capability.EchoMessages] = ""
254 368
         client.connect()
255 369
 
@@ -266,6 +380,7 @@ internal class IrcClientImplTest {
266 380
         val config = IrcClientConfig(serverConfig, profileConfig, BehaviourConfig().apply { alwaysEchoMessages = false }, null)
267 381
         val client = IrcClientImpl(config)
268 382
         client.socketFactory = mockSocketFactory
383
+        client.resolver = mockResolver
269 384
         client.connect()
270 385
 
271 386
         client.onEvent(mockEventHandler)
@@ -273,12 +388,14 @@ internal class IrcClientImplTest {
273 388
 
274 389
         verify(inverse = true) {
275 390
             mockEventHandler(ofType<MessageReceived>())
276
-        }    }
391
+        }
392
+    }
277 393
 
278 394
     @Test
279 395
     fun `sends structured text to socket with tags`() = runBlocking {
280 396
         val client = IrcClientImpl(normalConfig)
281 397
         client.socketFactory = mockSocketFactory
398
+        client.resolver = mockResolver
282 399
         client.connect()
283 400
 
284 401
         client.send(tagMap(MessageTag.AccountName to "acidB"), "testing", "123", "456")
@@ -290,6 +407,8 @@ internal class IrcClientImplTest {
290 407
     fun `asynchronously sends text to socket without label if cap is missing`() = runBlocking {
291 408
         val client = IrcClientImpl(normalConfig)
292 409
         client.socketFactory = mockSocketFactory
410
+        client.resolver = mockResolver
411
+
293 412
         client.connect()
294 413
 
295 414
         client.sendAsync(tagMap(), "testing", arrayOf("123")) { false }
@@ -302,6 +421,8 @@ internal class IrcClientImplTest {
302 421
         generateLabel = { "abc123" }
303 422
         val client = IrcClientImpl(normalConfig)
304 423
         client.socketFactory = mockSocketFactory
424
+        client.resolver = mockResolver
425
+
305 426
         client.serverState.capabilities.enabledCapabilities[Capability.LabeledResponse] = ""
306 427
         client.connect()
307 428
 
@@ -315,6 +436,8 @@ internal class IrcClientImplTest {
315 436
         generateLabel = { "abc123" }
316 437
         val client = IrcClientImpl(normalConfig)
317 438
         client.socketFactory = mockSocketFactory
439
+        client.resolver = mockResolver
440
+
318 441
         client.serverState.capabilities.enabledCapabilities[Capability.LabeledResponse] = ""
319 442
         client.connect()
320 443
 
@@ -327,6 +450,7 @@ internal class IrcClientImplTest {
327 450
     fun `disconnects the socket`() = runBlocking {
328 451
         val client = IrcClientImpl(normalConfig)
329 452
         client.socketFactory = mockSocketFactory
453
+        client.resolver = mockResolver
330 454
         client.connect()
331 455
 
332 456
         client.disconnect()
@@ -341,6 +465,7 @@ internal class IrcClientImplTest {
341 465
     fun `sends messages in order`() = runBlocking {
342 466
         val client = IrcClientImpl(normalConfig)
343 467
         client.socketFactory = mockSocketFactory
468
+        client.resolver = mockResolver
344 469
         client.connect()
345 470
 
346 471
         (0..100).forEach { client.send("TEST", "$it") }
@@ -399,6 +524,7 @@ internal class IrcClientImplTest {
399 524
         every { mockSocket.connect() } throws UnresolvedAddressException()
400 525
         with(IrcClientImpl(normalConfig)) {
401 526
             socketFactory = mockSocketFactory
527
+            resolver = mockResolver
402 528
             withTimeout(500) {
403 529
                 launch {
404 530
                     delay(50)
@@ -415,6 +541,7 @@ internal class IrcClientImplTest {
415 541
         every { mockSocket.connect() } throws CertificateException("Boooo")
416 542
         with(IrcClientImpl(normalConfig)) {
417 543
             socketFactory = mockSocketFactory
544
+            resolver = mockResolver
418 545
             withTimeout(500) {
419 546
                 launch {
420 547
                     delay(50)

+ 10
- 10
src/test/kotlin/com/dmdirc/ktirc/io/LineBufferedSocketImplTest.kt View File

@@ -20,7 +20,7 @@ internal class LineBufferedSocketImplTest {
20 20
     @Test
21 21
     fun `KtorLineBufferedSocket can connect to a server`() = runBlocking {
22 22
         ServerSocket(12321).use { serverSocket ->
23
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
23
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321)
24 24
             val clientSocketAsync = GlobalScope.async { serverSocket.accept() }
25 25
 
26 26
             socket.connect()
@@ -32,7 +32,7 @@ internal class LineBufferedSocketImplTest {
32 32
     @Test
33 33
     fun `KtorLineBufferedSocket can send a byte array to a server`() = runBlocking {
34 34
         ServerSocket(12321).use { serverSocket ->
35
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
35
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321)
36 36
             val clientBytesAsync = GlobalScope.async {
37 37
                 ByteArray(13).apply {
38 38
                     serverSocket.accept().getInputStream().read(this)
@@ -51,7 +51,7 @@ internal class LineBufferedSocketImplTest {
51 51
     @Test
52 52
     fun `KtorLineBufferedSocket can send a string to a server over TLS`() = runBlocking {
53 53
         tlsServerSocket(12321).use { serverSocket ->
54
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321, true)
54
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321, true)
55 55
             socket.tlsTrustManager = getTrustingManager()
56 56
             val clientBytesAsync = GlobalScope.async {
57 57
                 ByteArray(13).apply {
@@ -71,7 +71,7 @@ internal class LineBufferedSocketImplTest {
71 71
     @Test
72 72
     fun `KtorLineBufferedSocket can receive a line of CRLF delimited text`() = runBlocking {
73 73
         ServerSocket(12321).use { serverSocket ->
74
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
74
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321)
75 75
             GlobalScope.launch {
76 76
                 serverSocket.accept().getOutputStream().write("Hi there\r\n".toByteArray())
77 77
             }
@@ -84,7 +84,7 @@ internal class LineBufferedSocketImplTest {
84 84
     @Test
85 85
     fun `KtorLineBufferedSocket can receive a line of LF delimited text`() = runBlocking {
86 86
         ServerSocket(12321).use { serverSocket ->
87
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
87
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321)
88 88
             GlobalScope.launch {
89 89
                 serverSocket.accept().getOutputStream().write("Hi there\n".toByteArray())
90 90
             }
@@ -97,7 +97,7 @@ internal class LineBufferedSocketImplTest {
97 97
     @Test
98 98
     fun `KtorLineBufferedSocket can receive multiple lines of text in one packet`() = runBlocking {
99 99
         ServerSocket(12321).use { serverSocket ->
100
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
100
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321)
101 101
             GlobalScope.launch {
102 102
                 serverSocket.accept().getOutputStream().write("Hi there\nThis is a test\r".toByteArray())
103 103
             }
@@ -112,7 +112,7 @@ internal class LineBufferedSocketImplTest {
112 112
     @Test
113 113
     fun `KtorLineBufferedSocket can receive multiple long lines of text`() = runBlocking {
114 114
         ServerSocket(12321).use { serverSocket ->
115
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
115
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321)
116 116
             val line1 = "abcdefghijklmnopqrstuvwxyz".repeat(500)
117 117
             val line2 = "1234567890987654321[];'#,.".repeat(500)
118 118
             val line3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".repeat(500)
@@ -131,7 +131,7 @@ internal class LineBufferedSocketImplTest {
131 131
     @Test
132 132
     fun `KtorLineBufferedSocket can receive one line of text over multiple packets`() = runBlocking {
133 133
         ServerSocket(12321).use { serverSocket ->
134
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
134
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321)
135 135
             GlobalScope.launch {
136 136
                 with(serverSocket.accept().getOutputStream()) {
137 137
                     write("Hi".toByteArray())
@@ -152,7 +152,7 @@ internal class LineBufferedSocketImplTest {
152 152
     @Test
153 153
     fun `KtorLineBufferedSocket returns from readLines when socket is closed`() = runBlocking {
154 154
         ServerSocket(12321).use { serverSocket ->
155
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
155
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321)
156 156
             GlobalScope.launch {
157 157
                 with(serverSocket.accept()) {
158 158
                     getOutputStream().write("Hi there\r\n".toByteArray())
@@ -169,7 +169,7 @@ internal class LineBufferedSocketImplTest {
169 169
     @Test
170 170
     fun `KtorLineBufferedSocket disconnects from server`() = runBlocking {
171 171
         ServerSocket(12321).use { serverSocket ->
172
-            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", 12321)
172
+            val socket = LineBufferedSocketImpl(GlobalScope, "localhost", "localhost", 12321)
173 173
             val clientSocketAsync = GlobalScope.async { serverSocket.accept() }
174 174
 
175 175
             socket.connect()

Loading…
Cancel
Save