Browse Source

DSL for ping timeout settings

wip-ping-timeouts
Chris Smith 5 years ago
parent
commit
85d453c2a9

+ 53
- 6
src/main/kotlin/com/dmdirc/ktirc/Dsl.kt View File

@@ -1,17 +1,13 @@
1 1
 package com.dmdirc.ktirc
2 2
 
3
+import java.time.Duration
4
+
3 5
 /**
4 6
  * Dsl marker for [IrcClient] dsl.
5 7
  */
6 8
 @DslMarker
7 9
 annotation class IrcClientDsl
8 10
 
9
-internal data class IrcClientConfig(
10
-        val server: ServerConfig,
11
-        val profile: ProfileConfig,
12
-        val behaviour: ClientBehaviour,
13
-        val sasl: SaslConfig?)
14
-
15 11
 /**
16 12
  * Dsl for configuring an IRC Client.
17 13
  *
@@ -206,12 +202,63 @@ class SaslConfig {
206 202
     }
207 203
 }
208 204
 
205
+@IrcClientDsl
206
+class PingConfig {
207
+
208
+    /**
209
+     * The period of time we will wait between sending pings to the server.
210
+     *
211
+     * If [incomingLinesResetTimer] is enabled, then a ping will be sent this period of time after the last line
212
+     * is received from the server. Otherwise, it will be sent this period of time after the last PONG response.
213
+     */
214
+    var sendPeriod: Duration? = null
215
+
216
+    /**
217
+     * The period of time to wait for a reply.
218
+     *
219
+     * If the server does not respond to a PING in this period, we consider it stoned and disconnect.
220
+     */
221
+    var responseGracePeriod: Duration? = null
222
+
223
+    /**
224
+     * Whether to treat incoming lines from the server as an indication that it is still active.
225
+     *
226
+     * This reduces the amount of pings that KtIrc will send, but can result in KtIrc staying connected even if the
227
+     * server is severely lagged or ignores all lines sent to it (for example).
228
+     */
229
+    var incomingLinesResetTimer: Boolean = false
230
+
231
+}
232
+
209 233
 /**
210 234
  * Dsl for configuring the behaviour of an [IrcClient].
211 235
  */
212 236
 @IrcClientDsl
213 237
 class BehaviourConfig : ClientBehaviour {
238
+
214 239
     override var requestModesOnJoin = false
215 240
     override var alwaysEchoMessages = false
216 241
     override var preferIPv6 = true
242
+
243
+    override var pingTimeouts: PingTimeouts? = null
244
+        internal set(value) {
245
+            check(field == null) { "ping timeouts may only be specified once" }
246
+            field = value
247
+        }
248
+
249
+    /**
250
+     * Configures how frequently KtIrc will send pings, and how it will deal with non-responsive servers.
251
+     *
252
+     * If not specified, KtIrc will not send pings.
253
+     *
254
+     * See [PingConfig].
255
+     */
256
+    @IrcClientDsl
257
+    fun sendPings(block: PingConfig.() -> Unit) {
258
+        val config = PingConfig().apply(block)
259
+        requireNotNull(config.sendPeriod) { "send period must be specified" }
260
+        requireNotNull(config.responseGracePeriod) { "response grace period must be specified" }
261
+        pingTimeouts = IrcPingTimeouts(config.sendPeriod!!, config.responseGracePeriod!!, config.incomingLinesResetTimer)
262
+    }
263
+
217 264
 }

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

@@ -7,6 +7,7 @@ import com.dmdirc.ktirc.messages.tagMap
7 7
 import com.dmdirc.ktirc.model.*
8 8
 import com.dmdirc.ktirc.util.RemoveIn
9 9
 import kotlinx.coroutines.Deferred
10
+import java.time.Duration
10 11
 
11 12
 /**
12 13
  * Primary interface for interacting with KtIrc.
@@ -186,6 +187,36 @@ internal interface ExperimentalIrcClient : IrcClient {
186 187
 
187 188
 }
188 189
 
190
+/**
191
+ * Defines the configuration for sending pings and dealing with ping timeouts.
192
+ */
193
+interface PingTimeouts {
194
+
195
+    /**
196
+     * The period of time we will wait between sending pings to the server.
197
+     *
198
+     * If [incomingLinesResetTimer] is enabled, then a ping will be sent this period of time after the last line
199
+     * is received from the server. Otherwise, it will be sent this period of time after the last PONG response.
200
+     */
201
+    val sendPeriod: Duration
202
+
203
+    /**
204
+     * The period of time to wait for a reply.
205
+     *
206
+     * If the server does not respond to a PING in this period, we consider it stoned and disconnect.
207
+     */
208
+    val responseGracePeriod: Duration
209
+
210
+    /**
211
+     * Whether to treat incoming lines from the server as an indication that it is still active.
212
+     *
213
+     * This reduces the amount of pings that KtIrc will send, but can result in KtIrc staying connected even if the
214
+     * server is severely lagged or ignores all lines sent to it (for example).
215
+     */
216
+    val incomingLinesResetTimer: Boolean
217
+
218
+}
219
+
189 220
 /**
190 221
  * Defines the behaviour of an [IrcClient].
191 222
  */
@@ -210,6 +241,14 @@ interface ClientBehaviour {
210 241
      */
211 242
     val preferIPv6: Boolean
212 243
 
244
+    /**
245
+     * The settings to use for sending pings to the server, and timing out connections that don't reply to those
246
+     * pings.
247
+     *
248
+     * If unset (i.e., `null`), no pings will be sent, and KtIrc will never time out servers.
249
+     */
250
+    val pingTimeouts: PingTimeouts?
251
+
213 252
 }
214 253
 
215 254
 /**

+ 11
- 0
src/main/kotlin/com/dmdirc/ktirc/IrcClientImpl.kt View File

@@ -23,6 +23,17 @@ import java.net.InetAddress
23 23
 import java.time.Duration
24 24
 import java.util.logging.Level
25 25
 
26
+internal data class IrcClientConfig(
27
+        val server: ServerConfig,
28
+        val profile: ProfileConfig,
29
+        val behaviour: ClientBehaviour,
30
+        val sasl: SaslConfig?)
31
+
32
+internal data class IrcPingTimeouts(
33
+        override val sendPeriod: Duration,
34
+        override val responseGracePeriod: Duration,
35
+        override val incomingLinesResetTimer: Boolean) : PingTimeouts
36
+
26 37
 /**
27 38
  * Concrete implementation of an [IrcClient].
28 39
  */

+ 87
- 0
src/test/kotlin/com/dmdirc/ktirc/DslTest.kt View File

@@ -3,6 +3,7 @@ package com.dmdirc.ktirc
3 3
 import org.junit.jupiter.api.Assertions.*
4 4
 import org.junit.jupiter.api.Test
5 5
 import org.junit.jupiter.api.assertThrows
6
+import java.time.Duration
6 7
 
7 8
 internal class IrcClientConfigBuilderTest {
8 9
 
@@ -212,6 +213,92 @@ internal class IrcClientConfigBuilderTest {
212 213
         assertFalse(config.behaviour.requestModesOnJoin)
213 214
     }
214 215
 
216
+    @Test
217
+    fun `defaults to null ping timeouts if sendPings omitted`() {
218
+        val config = IrcClientConfigBuilder().apply {
219
+            profile { nickname = "acidBurn" }
220
+            server { host = "thegibson.com" }
221
+            behaviour {
222
+                requestModesOnJoin = true
223
+            }
224
+        }.build()
225
+
226
+        assertNull(config.behaviour.pingTimeouts)
227
+    }
228
+
229
+    @Test
230
+    fun `throws if sendPings specified without send period`() {
231
+        assertThrows<IllegalArgumentException> {
232
+            IrcClientConfigBuilder().apply {
233
+                profile { nickname = "acidBurn" }
234
+                server { host = "thegibson.com" }
235
+                behaviour {
236
+                    requestModesOnJoin = true
237
+                    sendPings {
238
+                        responseGracePeriod = Duration.ofSeconds(10)
239
+                    }
240
+                }
241
+            }.build()
242
+        }
243
+    }
244
+
245
+    @Test
246
+    fun `throws if sendPings specified without grace period`() {
247
+        assertThrows<IllegalArgumentException> {
248
+            IrcClientConfigBuilder().apply {
249
+                profile { nickname = "acidBurn" }
250
+                server { host = "thegibson.com" }
251
+                behaviour {
252
+                    requestModesOnJoin = true
253
+                    sendPings {
254
+                        sendPeriod = Duration.ofSeconds(10)
255
+                    }
256
+                }
257
+            }.build()
258
+        }
259
+    }
260
+
261
+    @Test
262
+    fun `throws if sendPings specified twice`() {
263
+        assertThrows<IllegalStateException> {
264
+            IrcClientConfigBuilder().apply {
265
+                profile { nickname = "acidBurn" }
266
+                server { host = "thegibson.com" }
267
+                behaviour {
268
+                    requestModesOnJoin = true
269
+                    sendPings {
270
+                        sendPeriod = Duration.ofSeconds(10)
271
+                        responseGracePeriod = Duration.ofSeconds(10)
272
+                    }
273
+                    sendPings {
274
+                        sendPeriod = Duration.ofSeconds(10)
275
+                        responseGracePeriod = Duration.ofSeconds(10)
276
+                    }
277
+                }
278
+            }.build()
279
+        }
280
+    }
281
+
282
+    @Test
283
+    fun `configures ping timeouts`() {
284
+        val config = IrcClientConfigBuilder().apply {
285
+            profile { nickname = "acidBurn" }
286
+            server { host = "thegibson.com" }
287
+            behaviour {
288
+                requestModesOnJoin = true
289
+                sendPings {
290
+                    sendPeriod = Duration.ofSeconds(50)
291
+                    responseGracePeriod = Duration.ofSeconds(100)
292
+                    incomingLinesResetTimer = true
293
+                }
294
+            }
295
+        }.build()
296
+
297
+        assertEquals(50, config.behaviour.pingTimeouts?.sendPeriod?.seconds)
298
+        assertEquals(100, config.behaviour.pingTimeouts?.responseGracePeriod?.seconds)
299
+        assertEquals(true, config.behaviour.pingTimeouts?.incomingLinesResetTimer)
300
+    }
301
+
215 302
     @Test
216 303
     fun `applies sasl settings`() {
217 304
         val config = IrcClientConfigBuilder().apply {

Loading…
Cancel
Save