소스 검색

Logging, ping handling

tags/v0.1.0
Chris Smith 5 년 전
부모
커밋
89ad92f562

+ 3
- 0
src/main/kotlin/com/dmdirc/ktirc/events/Events.kt 파일 보기

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

+ 6
- 7
src/main/kotlin/com/dmdirc/ktirc/io/LineBufferedSocket.kt 파일 보기

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

+ 7
- 1
src/main/kotlin/com/dmdirc/ktirc/io/MessageHandler.kt 파일 보기

2
 
2
 
3
 import com.dmdirc.ktirc.events.IrcEvent
3
 import com.dmdirc.ktirc.events.IrcEvent
4
 import com.dmdirc.ktirc.messages.MessageProcessor
4
 import com.dmdirc.ktirc.messages.MessageProcessor
5
+import com.dmdirc.ktirc.util.logger
5
 import kotlinx.coroutines.channels.ReceiveChannel
6
 import kotlinx.coroutines.channels.ReceiveChannel
6
 import kotlinx.coroutines.channels.consumeEach
7
 import kotlinx.coroutines.channels.consumeEach
7
 
8
 
8
 class MessageHandler(private val processors: Collection<MessageProcessor>, private val eventHandler: (IrcEvent) -> Unit) {
9
 class MessageHandler(private val processors: Collection<MessageProcessor>, private val eventHandler: (IrcEvent) -> Unit) {
9
 
10
 
11
+    private val log by logger()
12
+
10
     suspend fun processMessages(messages: ReceiveChannel<IrcMessage>) {
13
     suspend fun processMessages(messages: ReceiveChannel<IrcMessage>) {
11
         messages.consumeEach { it.process().forEach(eventHandler) }
14
         messages.consumeEach { it.process().forEach(eventHandler) }
12
     }
15
     }
13
 
16
 
14
     private fun IrcMessage.process() = this.getProcessor()?.process(this) ?: emptyList()
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 파일 보기

5
 import com.dmdirc.ktirc.io.IrcMessage
5
 import com.dmdirc.ktirc.io.IrcMessage
6
 import com.dmdirc.ktirc.state.ServerState
6
 import com.dmdirc.ktirc.state.ServerState
7
 import com.dmdirc.ktirc.state.serverFeatures
7
 import com.dmdirc.ktirc.state.serverFeatures
8
+import com.dmdirc.ktirc.util.logger
8
 import kotlin.reflect.KClass
9
 import kotlin.reflect.KClass
9
 
10
 
10
 class ISupportProcessor(val serverState: ServerState) : MessageProcessor {
11
 class ISupportProcessor(val serverState: ServerState) : MessageProcessor {
11
 
12
 
13
+    private val log by logger()
14
+
12
     override val commands = arrayOf("005")
15
     override val commands = arrayOf("005")
13
 
16
 
14
     override fun process(message: IrcMessage): List<IrcEvent> {
17
     override fun process(message: IrcMessage): List<IrcEvent> {
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
     @Suppress("UNCHECKED_CAST")
38
     @Suppress("UNCHECKED_CAST")
33
     private fun enableFeature(name: ByteArray, value: ByteArray) {
39
     private fun enableFeature(name: ByteArray, value: ByteArray) {
34
         name.asFeature()?.let { feature ->
40
         name.asFeature()?.let { feature ->
35
             serverState.setFeature(feature, value.cast(feature.type))
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
     private fun enableFeatureWithDefault(name: ByteArray) {
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
     private fun ByteArray.asFeature() = serverFeatures[String(this)]
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
     private fun ByteArray.cast(to: KClass<out Any>): Any = when (to) {
61
     private fun ByteArray.cast(to: KClass<out Any>): Any = when (to) {
46
         Int::class -> String(this).toInt()
62
         Int::class -> String(this).toInt()

+ 12
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/PingProcessor.kt 파일 보기

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 파일 보기

1
 package com.dmdirc.ktirc.messages
1
 package com.dmdirc.ktirc.messages
2
 
2
 
3
 import com.dmdirc.ktirc.events.IrcEvent
3
 import com.dmdirc.ktirc.events.IrcEvent
4
+import com.dmdirc.ktirc.events.ServerConnected
4
 import com.dmdirc.ktirc.io.IrcMessage
5
 import com.dmdirc.ktirc.io.IrcMessage
5
 import com.dmdirc.ktirc.state.ServerState
6
 import com.dmdirc.ktirc.state.ServerState
6
 
7
 
10
 
11
 
11
     override fun process(message: IrcMessage): List<IrcEvent> {
12
     override fun process(message: IrcMessage): List<IrcEvent> {
12
         serverState.localNickname = String(message.params[0])
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 파일 보기

37
     object MaximumChannels : ServerFeature<Int>("CHANLIMIT", Int::class)
37
     object MaximumChannels : ServerFeature<Int>("CHANLIMIT", Int::class)
38
     object ChannelModes : ServerFeature<String>("CHANMODES", String::class)
38
     object ChannelModes : ServerFeature<String>("CHANMODES", String::class)
39
     object MaximumChannelNameLength : ServerFeature<Int>("CHANNELLEN", Int::class, 200)
39
     object MaximumChannelNameLength : ServerFeature<Int>("CHANNELLEN", Int::class, 200)
40
+    object WhoxSupport : ServerFeature<Boolean>("WHOX", Boolean::class, false)
40
 }
41
 }
41
 
42
 
42
 val serverFeatures: Map<String, ServerFeature<*>> by lazy {
43
 val serverFeatures: Map<String, ServerFeature<*>> by lazy {

+ 12
- 0
src/main/kotlin/com/dmdirc/ktirc/util/Logging.kt 파일 보기

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 파일 보기

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 파일 보기

6
 import com.dmdirc.ktirc.state.ServerState
6
 import com.dmdirc.ktirc.state.ServerState
7
 import com.nhaarman.mockitokotlin2.mock
7
 import com.nhaarman.mockitokotlin2.mock
8
 import com.nhaarman.mockitokotlin2.verify
8
 import com.nhaarman.mockitokotlin2.verify
9
-import org.junit.jupiter.api.Assertions.*
9
+import org.junit.jupiter.api.Assertions.assertTrue
10
 import org.junit.jupiter.api.Test
10
 import org.junit.jupiter.api.Test
11
 
11
 
12
 internal class ISupportProcessorTest {
12
 internal class ISupportProcessorTest {
52
         verify(state).setFeature(ServerFeature.ServerCaseMapping, CaseMapping.RfcStrict)
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 파일 보기

1
 package com.dmdirc.ktirc.messages
1
 package com.dmdirc.ktirc.messages
2
 
2
 
3
+import com.dmdirc.ktirc.events.IrcEvent
4
+import com.dmdirc.ktirc.events.ServerConnected
3
 import com.dmdirc.ktirc.io.IrcMessage
5
 import com.dmdirc.ktirc.io.IrcMessage
4
 import com.dmdirc.ktirc.state.ServerState
6
 import com.dmdirc.ktirc.state.ServerState
5
 import com.nhaarman.mockitokotlin2.mock
7
 import com.nhaarman.mockitokotlin2.mock
6
 import com.nhaarman.mockitokotlin2.verify
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
 import org.junit.jupiter.api.Test
11
 import org.junit.jupiter.api.Test
9
 
12
 
10
 internal class WelcomeProcessorTest {
13
 internal class WelcomeProcessorTest {
25
         verify(state).localNickname = "acidBurn"
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 파일 보기

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…
취소
저장