Browse Source

Logging, ping handling

tags/v0.1.0
Chris Smith 5 years ago
parent
commit
89ad92f562

+ 3
- 0
src/main/kotlin/com/dmdirc/ktirc/events/Events.kt View File

@@ -1,4 +1,7 @@
1
+@file:Suppress("ArrayInDataClass")
2
+
1 3
 package com.dmdirc.ktirc.events
2 4
 
3 5
 sealed class IrcEvent
4 6
 object ServerConnected : IrcEvent()
7
+data class PingReceived(val nonce: ByteArray): IrcEvent()

+ 6
- 7
src/main/kotlin/com/dmdirc/ktirc/io/LineBufferedSocket.kt View File

@@ -1,5 +1,6 @@
1 1
 package com.dmdirc.ktirc.io
2 2
 
3
+import com.dmdirc.ktirc.util.logger
3 4
 import io.ktor.network.selector.ActorSelectorManager
4 5
 import io.ktor.network.sockets.Socket
5 6
 import io.ktor.network.sockets.aSocket
@@ -15,9 +16,6 @@ import java.net.InetSocketAddress
15 16
 
16 17
 interface LineBufferedSocket {
17 18
 
18
-    // TODO: This is a bit pants.
19
-    var debugReceiver: ((String) -> Unit)?
20
-
21 19
     suspend fun connect()
22 20
     fun disconnect()
23 21
 
@@ -39,26 +37,27 @@ class KtorLineBufferedSocket(private val host: String, private val port: Int): L
39 37
         const val LINE_FEED = '\n'.toByte()
40 38
     }
41 39
 
42
-    // TODO: This is a bit pants.
43
-    override var debugReceiver: ((String) -> Unit)? = null
40
+    private val log by logger()
44 41
 
45 42
     private lateinit var socket: Socket
46 43
     private lateinit var readChannel: ByteReadChannel
47 44
     private lateinit var writeChannel: ByteWriteChannel
48 45
 
49 46
     override suspend fun connect() {
47
+        log.info { "Connecting..." }
50 48
         socket = aSocket(ActorSelectorManager(ioCoroutineDispatcher)).tcp().connect(InetSocketAddress(host, port))
51 49
         readChannel = socket.openReadChannel()
52 50
         writeChannel = socket.openWriteChannel()
53 51
     }
54 52
 
55 53
     override fun disconnect() {
54
+        log.info { "Disconnecting..." }
56 55
         socket.close()
57 56
     }
58 57
 
59 58
     override suspend fun sendLine(line: ByteArray, offset: Int, length: Int) {
60 59
         with (writeChannel) {
61
-            debugReceiver?.let { it(">>> ${String(line, offset, length)}") }
60
+            log.fine { ">>> ${String(line, offset, length)}" }
62 61
             writeAvailable(line, offset, length)
63 62
             writeByte(CARRIAGE_RETURN)
64 63
             writeByte(LINE_FEED)
@@ -78,7 +77,7 @@ class KtorLineBufferedSocket(private val host: String, private val port: Int): L
78 77
                 if (lineBuffer[i] == CARRIAGE_RETURN || lineBuffer[i] == LINE_FEED) {
79 78
                     if (start < i) {
80 79
                         val line = lineBuffer.sliceArray(start until i)
81
-                        debugReceiver?.let { it("<<< ${String(line)}") }
80
+                        log.fine { "<<< ${String(line)}" }
82 81
                         send(line)
83 82
                     }
84 83
                     start = i + 1

+ 7
- 1
src/main/kotlin/com/dmdirc/ktirc/io/MessageHandler.kt View File

@@ -2,16 +2,22 @@ package com.dmdirc.ktirc.io
2 2
 
3 3
 import com.dmdirc.ktirc.events.IrcEvent
4 4
 import com.dmdirc.ktirc.messages.MessageProcessor
5
+import com.dmdirc.ktirc.util.logger
5 6
 import kotlinx.coroutines.channels.ReceiveChannel
6 7
 import kotlinx.coroutines.channels.consumeEach
7 8
 
8 9
 class MessageHandler(private val processors: Collection<MessageProcessor>, private val eventHandler: (IrcEvent) -> Unit) {
9 10
 
11
+    private val log by logger()
12
+
10 13
     suspend fun processMessages(messages: ReceiveChannel<IrcMessage>) {
11 14
         messages.consumeEach { it.process().forEach(eventHandler) }
12 15
     }
13 16
 
14 17
     private fun IrcMessage.process() = this.getProcessor()?.process(this) ?: emptyList()
15
-    private fun IrcMessage.getProcessor() = processors.firstOrNull { it.commands.contains(this.command) }
18
+    private fun IrcMessage.getProcessor() = processors.firstOrNull { it.commands.contains(command) } ?: run {
19
+        log.warning { "No processor found for $command" }
20
+        null
21
+    }
16 22
 
17 23
 }

+ 18
- 2
src/main/kotlin/com/dmdirc/ktirc/messages/ISupportProcessor.kt View File

@@ -5,10 +5,13 @@ import com.dmdirc.ktirc.io.CaseMapping
5 5
 import com.dmdirc.ktirc.io.IrcMessage
6 6
 import com.dmdirc.ktirc.state.ServerState
7 7
 import com.dmdirc.ktirc.state.serverFeatures
8
+import com.dmdirc.ktirc.util.logger
8 9
 import kotlin.reflect.KClass
9 10
 
10 11
 class ISupportProcessor(val serverState: ServerState) : MessageProcessor {
11 12
 
13
+    private val log by logger()
14
+
12 15
     override val commands = arrayOf("005")
13 16
 
14 17
     override fun process(message: IrcMessage): List<IrcEvent> {
@@ -27,20 +30,33 @@ class ISupportProcessor(val serverState: ServerState) : MessageProcessor {
27 30
         }
28 31
     }
29 32
 
30
-    private fun resetFeature(name: ByteArray) = name.asFeature()?.let { serverState.resetFeature(it) }
33
+    private fun resetFeature(name: ByteArray) = name.asFeature()?.let {
34
+        serverState.resetFeature(it)
35
+        log.finer { "Reset feature ${it::class}" }
36
+    }
31 37
 
32 38
     @Suppress("UNCHECKED_CAST")
33 39
     private fun enableFeature(name: ByteArray, value: ByteArray) {
34 40
         name.asFeature()?.let { feature ->
35 41
             serverState.setFeature(feature, value.cast(feature.type))
42
+            log.finer { "Set feature ${feature::class} to ${String(value)}" }
36 43
         }
37 44
     }
38 45
 
39 46
     private fun enableFeatureWithDefault(name: ByteArray) {
40
-        TODO("not implemented")
47
+        name.asFeature()?.let { feature ->
48
+            when (feature.type) {
49
+                Boolean::class -> serverState.setFeature(feature, true)
50
+                else -> TODO("not implemented")
51
+            }
52
+        }
41 53
     }
42 54
 
43 55
     private fun ByteArray.asFeature() = serverFeatures[String(this)]
56
+            ?: run {
57
+                log.warning { "Unknown feature in 005: ${String(this)}" }
58
+                null
59
+            }
44 60
 
45 61
     private fun ByteArray.cast(to: KClass<out Any>): Any = when (to) {
46 62
         Int::class -> String(this).toInt()

+ 12
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/PingProcessor.kt View File

@@ -0,0 +1,12 @@
1
+package com.dmdirc.ktirc.messages
2
+
3
+import com.dmdirc.ktirc.events.PingReceived
4
+import com.dmdirc.ktirc.io.IrcMessage
5
+
6
+class PingProcessor : MessageProcessor {
7
+
8
+    override val commands = arrayOf("PING")
9
+
10
+    override fun process(message: IrcMessage) = listOf(PingReceived(message.params[0]))
11
+
12
+}

+ 4
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/WelcomeProcessor.kt View File

@@ -1,6 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3 3
 import com.dmdirc.ktirc.events.IrcEvent
4
+import com.dmdirc.ktirc.events.ServerConnected
4 5
 import com.dmdirc.ktirc.io.IrcMessage
5 6
 import com.dmdirc.ktirc.state.ServerState
6 7
 
@@ -10,7 +11,9 @@ class WelcomeProcessor(private val serverState: ServerState) : MessageProcessor
10 11
 
11 12
     override fun process(message: IrcMessage): List<IrcEvent> {
12 13
         serverState.localNickname = String(message.params[0])
13
-        return emptyList()
14
+
15
+        // TODO: Maybe this should be later, like after the first line received after 001-005
16
+        return listOf(ServerConnected)
14 17
     }
15 18
 
16 19
 }

+ 1
- 0
src/main/kotlin/com/dmdirc/ktirc/state/ServerState.kt View File

@@ -37,6 +37,7 @@ sealed class ServerFeature<T : Any>(val name: String, val type: KClass<T>, val d
37 37
     object MaximumChannels : ServerFeature<Int>("CHANLIMIT", Int::class)
38 38
     object ChannelModes : ServerFeature<String>("CHANMODES", String::class)
39 39
     object MaximumChannelNameLength : ServerFeature<Int>("CHANNELLEN", Int::class, 200)
40
+    object WhoxSupport : ServerFeature<Boolean>("WHOX", Boolean::class, false)
40 41
 }
41 42
 
42 43
 val serverFeatures: Map<String, ServerFeature<*>> by lazy {

+ 12
- 0
src/main/kotlin/com/dmdirc/ktirc/util/Logging.kt View File

@@ -0,0 +1,12 @@
1
+package com.dmdirc.ktirc.util
2
+
3
+import java.util.logging.Logger
4
+import kotlin.reflect.KClass
5
+
6
+private fun <T: Any> logger(forClass: KClass<T>): Logger {
7
+    return Logger.getLogger(forClass.qualifiedName)
8
+}
9
+
10
+fun <R : Any> R.logger(): Lazy<Logger> {
11
+    return lazy { logger(this::class) }
12
+}

+ 0
- 37
src/test/kotlin/com/dmdirc/ktirc/io/KtorLineBufferedSocketTest.kt View File

@@ -175,41 +175,4 @@ internal class KtorLineBufferedSocketTest {
175 175
         }
176 176
     }
177 177
 
178
-    @Test
179
-    fun `KtorLineBufferedSocket reports sent lines to debug receiver`() = runBlocking {
180
-        ServerSocket(12321).use { serverSocket ->
181
-            val socket = KtorLineBufferedSocket("localhost", 12321)
182
-            GlobalScope.launch {
183
-                serverSocket.accept()
184
-            }
185
-
186
-            var received = ""
187
-            socket.debugReceiver = { str -> received = str }
188
-            socket.connect()
189
-            socket.sendLine("Test 123")
190
-
191
-            assertEquals(">>> Test 123", received)
192
-        }
193
-    }
194
-
195
-    @Test
196
-    fun `KtorLineBufferedSocket reports received lines to debug receiver`() = runBlocking {
197
-        ServerSocket(12321).use { serverSocket ->
198
-            val socket = KtorLineBufferedSocket("localhost", 12321)
199
-            GlobalScope.launch {
200
-                with(serverSocket.accept()) {
201
-                    getOutputStream().write("Hi there\r\n".toByteArray())
202
-                    close()
203
-                }
204
-            }
205
-
206
-            var received = ""
207
-            socket.debugReceiver = { str -> received = str }
208
-            socket.connect()
209
-            socket.readLines(this).receive()
210
-
211
-            assertEquals("<<< Hi there", received)
212
-        }
213
-    }
214
-
215 178
 }

+ 9
- 1
src/test/kotlin/com/dmdirc/ktirc/messages/ISupportProcessorTest.kt View File

@@ -6,7 +6,7 @@ import com.dmdirc.ktirc.state.ServerFeature
6 6
 import com.dmdirc.ktirc.state.ServerState
7 7
 import com.nhaarman.mockitokotlin2.mock
8 8
 import com.nhaarman.mockitokotlin2.verify
9
-import org.junit.jupiter.api.Assertions.*
9
+import org.junit.jupiter.api.Assertions.assertTrue
10 10
 import org.junit.jupiter.api.Test
11 11
 
12 12
 internal class ISupportProcessorTest {
@@ -52,4 +52,12 @@ internal class ISupportProcessorTest {
52 52
         verify(state).setFeature(ServerFeature.ServerCaseMapping, CaseMapping.RfcStrict)
53 53
     }
54 54
 
55
+    @Test
56
+    fun `ISupportProcessor handles boolean features with no arguments`() {
57
+        processor.process(IrcMessage(null, "server.com".toByteArray(), "005",
58
+                listOf("nickname", "WHOX", "are supported blah blah").map { it.toByteArray() }))
59
+
60
+        verify(state).setFeature(ServerFeature.WhoxSupport, true)
61
+    }
62
+
55 63
 }

+ 12
- 1
src/test/kotlin/com/dmdirc/ktirc/messages/WelcomeProcessorTest.kt View File

@@ -1,10 +1,13 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3
+import com.dmdirc.ktirc.events.IrcEvent
4
+import com.dmdirc.ktirc.events.ServerConnected
3 5
 import com.dmdirc.ktirc.io.IrcMessage
4 6
 import com.dmdirc.ktirc.state.ServerState
5 7
 import com.nhaarman.mockitokotlin2.mock
6 8
 import com.nhaarman.mockitokotlin2.verify
7
-import org.junit.jupiter.api.Assertions.*
9
+import org.junit.jupiter.api.Assertions.assertEquals
10
+import org.junit.jupiter.api.Assertions.assertTrue
8 11
 import org.junit.jupiter.api.Test
9 12
 
10 13
 internal class WelcomeProcessorTest {
@@ -25,4 +28,12 @@ internal class WelcomeProcessorTest {
25 28
         verify(state).localNickname = "acidBurn"
26 29
     }
27 30
 
31
+    @Test
32
+    fun `WelcomeProcessor returns server connected event`() {
33
+        val events = processor.process(IrcMessage(null, ":thegibson.com".toByteArray(), "001", listOf(
34
+                "acidBurn".toByteArray(),
35
+                "Welcome to the Internet Relay Network, acidBurn!burn@hacktheplanet.com".toByteArray())))
36
+        assertEquals(listOf<IrcEvent>(ServerConnected), events)
37
+    }
38
+
28 39
 }

+ 15
- 0
src/test/kotlin/com/dmdirc/ktirc/util/LoggingTest.kt View File

@@ -0,0 +1,15 @@
1
+package com.dmdirc.ktirc.util
2
+
3
+import org.junit.jupiter.api.Assertions.assertEquals
4
+import org.junit.jupiter.api.Test
5
+
6
+internal class LoggingTest {
7
+
8
+    private val log by logger()
9
+
10
+    @Test
11
+    fun `logger gives logger with correct class name`() {
12
+        assertEquals("com.dmdirc.ktirc.util.LoggingTest", log.name)
13
+    }
14
+
15
+}

Loading…
Cancel
Save