Add JS5 remote protocol implementation

Signed-off-by: Graham <gpe@openrs2.org>
Graham 4 years ago
parent 0e706bc578
commit 5036eb3da8
  1. 28
      protocol/build.gradle.kts
  2. 15
      protocol/src/main/kotlin/org/openrs2/protocol/js5/Js5Request.kt
  3. 35
      protocol/src/main/kotlin/org/openrs2/protocol/js5/Js5RequestDecoder.kt
  4. 44
      protocol/src/main/kotlin/org/openrs2/protocol/js5/Js5RequestEncoder.kt
  5. 43
      protocol/src/main/kotlin/org/openrs2/protocol/js5/Js5Response.kt
  6. 97
      protocol/src/main/kotlin/org/openrs2/protocol/js5/Js5ResponseDecoder.kt
  7. 45
      protocol/src/main/kotlin/org/openrs2/protocol/js5/Js5ResponseEncoder.kt
  8. 13
      protocol/src/main/kotlin/org/openrs2/protocol/js5/XorDecoder.kt
  9. 13
      protocol/src/main/kotlin/org/openrs2/protocol/js5/XorEncoder.kt
  10. 35
      protocol/src/main/kotlin/org/openrs2/protocol/js5/XorExtensions.kt
  11. 45
      protocol/src/test/kotlin/org/openrs2/protocol/js5/Js5RequestDecoderTest.kt
  12. 32
      protocol/src/test/kotlin/org/openrs2/protocol/js5/Js5RequestEncoderTest.kt
  13. 138
      protocol/src/test/kotlin/org/openrs2/protocol/js5/Js5ResponseDecoderTest.kt
  14. 60
      protocol/src/test/kotlin/org/openrs2/protocol/js5/Js5ResponseEncoderTest.kt
  15. 40
      protocol/src/test/kotlin/org/openrs2/protocol/js5/XorDecoderTest.kt
  16. 40
      protocol/src/test/kotlin/org/openrs2/protocol/js5/XorEncoderTest.kt
  17. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1019-prefetch.dat
  18. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1019-urgent.dat
  19. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1019.dat
  20. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1020-prefetch.dat
  21. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1020-urgent.dat
  22. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1020.dat
  23. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1530-prefetch.dat
  24. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1530-urgent.dat
  25. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1530.dat
  26. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1531-prefetch.dat
  27. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1531-urgent.dat
  28. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/1531.dat
  29. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/508-prefetch.dat
  30. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/508-urgent.dat
  31. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/508.dat
  32. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/509-prefetch.dat
  33. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/509-urgent.dat
  34. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/509.dat
  35. BIN
      protocol/src/test/resources/org/openrs2/protocol/js5/invalid-block-trailer.dat
  36. 1
      settings.gradle.kts

@ -0,0 +1,28 @@
plugins {
`maven-publish`
kotlin("jvm")
}
dependencies {
api("io.netty:netty-codec:${Versions.netty}")
implementation(project(":buffer"))
testImplementation(project(":buffer"))
}
publishing {
publications.create<MavenPublication>("maven") {
from(components["java"])
pom {
packaging = "jar"
name.set("OpenRS2 Protocol")
description.set(
"""
An implementation of the RuneScape protocol.
""".trimIndent()
)
}
}
}

@ -0,0 +1,15 @@
package org.openrs2.protocol.js5
public sealed class Js5Request {
public data class Group(
public val prefetch: Boolean,
public val archive: Int,
public val group: Int
) : Js5Request()
public object LoggedIn : Js5Request()
public object LoggedOut : Js5Request()
public data class Rekey(public val key: Int) : Js5Request()
public object Connected : Js5Request()
public object Disconnect : Js5Request()
}

@ -0,0 +1,35 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
import io.netty.handler.codec.DecoderException
public class Js5RequestDecoder : ByteToMessageDecoder() {
override fun decode(ctx: ChannelHandlerContext, input: ByteBuf, out: MutableList<Any>) {
if (input.readableBytes() < 4) {
return
}
val opcode = input.readUnsignedByte().toInt()
if (opcode == 0 || opcode == 1) {
val archive = input.readUnsignedByte().toInt()
val group = input.readUnsignedShort()
out += Js5Request.Group(opcode == 0, archive, group)
} else if (opcode == 4) {
val key = input.readUnsignedByte().toInt()
input.skipBytes(2)
out += Js5Request.Rekey(key)
} else {
input.skipBytes(3)
out += when (opcode) {
2 -> Js5Request.LoggedIn
3 -> Js5Request.LoggedOut
6 -> Js5Request.Connected
7 -> Js5Request.Disconnect
else -> throw DecoderException("Unknown JS5 opcode: $opcode")
}
}
}
}

@ -0,0 +1,44 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandler
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.MessageToByteEncoder
@ChannelHandler.Sharable
public class Js5RequestEncoder : MessageToByteEncoder<Js5Request>(Js5Request::class.java) {
override fun encode(ctx: ChannelHandlerContext, msg: Js5Request, out: ByteBuf) {
when (msg) {
is Js5Request.Group -> {
out.writeByte(if (msg.prefetch) 0 else 1)
out.writeByte(msg.archive)
out.writeShort(msg.group)
}
is Js5Request.Rekey -> {
out.writeByte(4)
out.writeByte(msg.key)
out.writeZero(2)
}
is Js5Request.LoggedIn -> encodeSimple(out, 2)
is Js5Request.LoggedOut -> encodeSimple(out, 3)
is Js5Request.Connected -> {
out.writeByte(6)
out.writeMedium(3)
}
is Js5Request.Disconnect -> encodeSimple(out, 7)
}
}
private fun encodeSimple(out: ByteBuf, opcode: Int) {
out.writeByte(opcode)
out.writeZero(3)
}
override fun allocateBuffer(ctx: ChannelHandlerContext, msg: Js5Request, preferDirect: Boolean): ByteBuf {
return if (preferDirect) {
ctx.alloc().ioBuffer(4, 4)
} else {
ctx.alloc().heapBuffer(4, 4)
}
}
}

@ -0,0 +1,43 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.util.ReferenceCounted
public data class Js5Response(
public val prefetch: Boolean,
public val archive: Int,
public val group: Int,
public val data: ByteBuf
) : ReferenceCounted {
override fun refCnt(): Int {
return data.refCnt()
}
override fun retain(): Js5Response {
data.retain()
return this
}
override fun retain(increment: Int): Js5Response {
data.retain(increment)
return this
}
override fun touch(): Js5Response {
data.touch()
return this
}
override fun touch(hint: Any?): Js5Response {
data.touch(hint)
return this
}
override fun release(): Boolean {
return data.release()
}
override fun release(decrement: Int): Boolean {
return data.release(decrement)
}
}

@ -0,0 +1,97 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.ByteToMessageDecoder
import io.netty.handler.codec.DecoderException
import kotlin.math.min
public class Js5ResponseDecoder : ByteToMessageDecoder() {
private enum class State {
READ_HEADER,
READ_DATA
}
private var state = State.READ_HEADER
private var prefetch: Boolean = false
private var archive: Int = 0
private var group: Int = 0
private var type: Int = 0
private var data: ByteBuf = Unpooled.EMPTY_BUFFER
override fun decode(ctx: ChannelHandlerContext, input: ByteBuf, out: MutableList<Any>) {
if (state == State.READ_HEADER) {
if (input.readableBytes() < 8) {
return
}
archive = input.readUnsignedByte().toInt()
group = input.readUnsignedShort()
type = input.readUnsignedByte().toInt()
if (type and 0x80 != 0) {
prefetch = true
type = type and 0x80.inv()
} else {
prefetch = false
}
val len = input.readInt()
if (len < 0) {
throw DecoderException("Length is negative: $len")
}
val totalLen = if (type == 0) {
len + 5
} else {
len + 9
}
if (totalLen < 0) {
throw DecoderException("Total length exceeds maximum ByteBuf size")
}
// TODO(gpe): release data here?
data = ctx.alloc().buffer(totalLen, totalLen)
data.writeByte(type)
data.writeInt(len)
state = State.READ_DATA
}
if (state == State.READ_DATA) {
while (data.isWritable) {
val blockLen = min(511 - ((data.readableBytes() + 3) % 511), data.writableBytes())
val last = data.writableBytes() <= blockLen
val blockLenIncludingTrailer = if (last) {
blockLen
} else {
blockLen + 1
}
if (input.readableBytes() < blockLenIncludingTrailer) {
return
}
data.writeBytes(input, blockLen)
if (!last && input.readUnsignedByte().toInt() != 0xFF) {
throw DecoderException("Invalid block trailer")
}
}
out += Js5Response(prefetch, archive, group, data)
data = Unpooled.EMPTY_BUFFER
state = State.READ_HEADER
}
}
override fun handlerRemoved0(ctx: ChannelHandlerContext?) {
data.release()
data = Unpooled.EMPTY_BUFFER
}
}

@ -0,0 +1,45 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandler
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.EncoderException
import io.netty.handler.codec.MessageToByteEncoder
import kotlin.math.min
@ChannelHandler.Sharable
public class Js5ResponseEncoder : MessageToByteEncoder<Js5Response>(Js5Response::class.java) {
override fun encode(ctx: ChannelHandlerContext, msg: Js5Response, out: ByteBuf) {
out.writeByte(msg.archive)
out.writeShort(msg.group)
if (!msg.data.isReadable) {
// TOOD(gpe): check if the entire container is well-formed?
throw EncoderException("Missing compression byte")
}
var compression = msg.data.readUnsignedByte().toInt()
if (msg.prefetch) {
compression = compression xor 0x80
}
out.writeByte(compression)
out.writeBytes(msg.data, min(msg.data.readableBytes(), 507))
while (msg.data.isReadable) {
out.writeByte(0xFF)
out.writeBytes(msg.data, min(msg.data.readableBytes(), 511))
}
}
override fun allocateBuffer(ctx: ChannelHandlerContext, msg: Js5Response, preferDirect: Boolean): ByteBuf {
val dataLen = msg.data.readableBytes()
val len = 3 + dataLen + (3 + dataLen) / 511
return if (preferDirect) {
ctx.alloc().ioBuffer(len, len)
} else {
ctx.alloc().heapBuffer(len, len)
}
}
}

@ -0,0 +1,13 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.MessageToMessageDecoder
public class XorDecoder : MessageToMessageDecoder<ByteBuf>(ByteBuf::class.java) {
public var key: Int = 0
override fun decode(ctx: ChannelHandlerContext, msg: ByteBuf, out: MutableList<Any>) {
out += msg.xor(key)
}
}

@ -0,0 +1,13 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.channel.ChannelHandlerContext
import io.netty.handler.codec.MessageToMessageEncoder
public class XorEncoder : MessageToMessageEncoder<ByteBuf>(ByteBuf::class.java) {
public var key: Int = 0
override fun encode(ctx: ChannelHandlerContext, msg: ByteBuf, out: MutableList<Any>) {
out += msg.xor(key)
}
}

@ -0,0 +1,35 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
internal fun ByteBuf.xor(key: Int): ByteBuf {
if (key == 0) {
return retain()
}
val buf = if (refCnt() == 1) {
retain()
} else {
copy()
}
if (buf.hasArray()) {
val array = buf.array()
val off = buf.arrayOffset() + buf.readerIndex()
val len = buf.readableBytes()
for (i in off until off + len) {
array[i] = (array[i].toInt() xor key).toByte()
}
} else {
val off = buf.readerIndex()
val len = buf.readableBytes()
for (i in off until off + len) {
buf.setByte(i, buf.getByte(i).toInt() xor key)
}
}
return buf
}

@ -0,0 +1,45 @@
package org.openrs2.protocol.js5
import io.netty.buffer.Unpooled
import io.netty.channel.embedded.EmbeddedChannel
import io.netty.handler.codec.DecoderException
import org.junit.jupiter.api.assertThrows
import kotlin.test.Test
import kotlin.test.assertEquals
object Js5RequestDecoderTest {
@Test
fun testDecode() {
testDecode(byteArrayOf(0, 2, 0, 3), Js5Request.Group(true, 2, 3))
testDecode(byteArrayOf(1, 2, 0, 3), Js5Request.Group(false, 2, 3))
testDecode(byteArrayOf(4, 0x55, 0, 0), Js5Request.Rekey(0x55))
testDecode(byteArrayOf(2, 0, 0, 0), Js5Request.LoggedIn)
testDecode(byteArrayOf(3, 0, 0, 0), Js5Request.LoggedOut)
testDecode(byteArrayOf(6, 0, 0, 3), Js5Request.Connected)
testDecode(byteArrayOf(7, 0, 0, 0), Js5Request.Disconnect)
}
@Test
fun testFragmented() {
val channel = EmbeddedChannel(Js5RequestDecoder())
channel.writeInbound(Unpooled.wrappedBuffer(byteArrayOf(0, 2)))
channel.writeInbound(Unpooled.wrappedBuffer(byteArrayOf(0, 3)))
assertEquals(Js5Request.Group(true, 2, 3), channel.readInbound())
}
@Test
fun testUnknownOpcode() {
val channel = EmbeddedChannel(Js5RequestDecoder())
assertThrows<DecoderException> {
channel.writeInbound(Unpooled.wrappedBuffer(byteArrayOf(8, 0, 0, 0)))
}
}
private fun testDecode(bytes: ByteArray, expected: Js5Request) {
val channel = EmbeddedChannel(Js5RequestDecoder())
channel.writeInbound(Unpooled.wrappedBuffer(bytes))
assertEquals(expected, channel.readInbound())
}
}

@ -0,0 +1,32 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import io.netty.channel.embedded.EmbeddedChannel
import org.openrs2.buffer.use
import kotlin.test.Test
import kotlin.test.assertEquals
object Js5RequestEncoderTest {
@Test
fun testEncode() {
testEncode(Js5Request.Group(true, 2, 3), byteArrayOf(0, 2, 0, 3))
testEncode(Js5Request.Group(false, 2, 3), byteArrayOf(1, 2, 0, 3))
testEncode(Js5Request.Rekey(0x55), byteArrayOf(4, 0x55, 0, 0))
testEncode(Js5Request.LoggedIn, byteArrayOf(2, 0, 0, 0))
testEncode(Js5Request.LoggedOut, byteArrayOf(3, 0, 0, 0))
testEncode(Js5Request.Connected, byteArrayOf(6, 0, 0, 3))
testEncode(Js5Request.Disconnect, byteArrayOf(7, 0, 0, 0))
}
private fun testEncode(request: Js5Request, expected: ByteArray) {
val channel = EmbeddedChannel(Js5RequestEncoder())
channel.writeOutbound(request)
channel.readOutbound<ByteBuf>().use { actual ->
Unpooled.wrappedBuffer(expected).use { expected ->
assertEquals(expected, actual)
}
}
}
}

@ -0,0 +1,138 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import io.netty.channel.embedded.EmbeddedChannel
import io.netty.handler.codec.DecoderException
import org.junit.jupiter.api.assertThrows
import org.openrs2.buffer.use
import kotlin.test.Test
import kotlin.test.assertEquals
object Js5ResponseDecoderTest {
@Test
fun testDecode() {
testDecode("508.dat", "508-prefetch.dat", true)
testDecode("508.dat", "508-urgent.dat", false)
testDecode("509.dat", "509-prefetch.dat", true)
testDecode("509.dat", "509-urgent.dat", false)
testDecode("1019.dat", "1019-prefetch.dat", true)
testDecode("1019.dat", "1019-urgent.dat", false)
testDecode("1020.dat", "1020-prefetch.dat", true)
testDecode("1020.dat", "1020-urgent.dat", false)
testDecode("1530.dat", "1530-prefetch.dat", true)
testDecode("1530.dat", "1530-urgent.dat", false)
testDecode("1531.dat", "1531-prefetch.dat", true)
testDecode("1531.dat", "1531-urgent.dat", false)
}
@Test
fun testDecodeFragmented() {
val channel = EmbeddedChannel(Js5ResponseDecoder())
channel.writeInbound(Unpooled.wrappedBuffer(byteArrayOf(2, 0, 3, 0, 0, 0, 0)))
channel.writeInbound(
Unpooled.wrappedBuffer(
byteArrayOf(
7,
'O'.toByte(),
'p'.toByte(),
'e'.toByte(),
'n'.toByte()
)
)
)
channel.writeInbound(Unpooled.wrappedBuffer(byteArrayOf('R'.toByte(), 'S'.toByte(), '2'.toByte())))
Unpooled.buffer().use { buf ->
buf.writeByte(0)
buf.writeInt(7)
buf.writeCharSequence("OpenRS2", Charsets.UTF_8)
val expected = Js5Response(false, 2, 3, buf)
channel.readInbound<Js5Response>().use { actual ->
assertEquals(expected, actual)
}
}
}
@Test
fun testDecodeNegativeLength() {
val channel = EmbeddedChannel(Js5ResponseDecoder())
assertThrows<DecoderException> {
channel.writeInbound(
Unpooled.wrappedBuffer(
byteArrayOf(
2, 0, 3, 0, 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte(), 0xFF.toByte()
)
)
)
}
}
@Test
fun testDecodeOverflowUncompressed() {
val channel = EmbeddedChannel(Js5ResponseDecoder())
assertThrows<DecoderException> {
channel.writeInbound(
Unpooled.wrappedBuffer(
byteArrayOf(
2, 0, 3, 0, 0x7F, 0xFF.toByte(), 0xFF.toByte(), 0xFB.toByte()
)
)
)
}
}
@Test
fun testDecodeOverflowCompressed() {
val channel = EmbeddedChannel(Js5ResponseDecoder())
assertThrows<DecoderException> {
channel.writeInbound(
Unpooled.wrappedBuffer(
byteArrayOf(
2, 0, 3, 1, 0x7F, 0xFF.toByte(), 0xFF.toByte(), 0xF7.toByte()
)
)
)
}
}
@Test
fun testDecodeInvalidBlockTrailer() {
val channel = EmbeddedChannel(Js5ResponseDecoder())
assertThrows<DecoderException> {
channel.writeInbound(read("invalid-block-trailer.dat"))
}
}
private fun testDecode(container: String, encoded: String, prefetch: Boolean) {
val channel = EmbeddedChannel(Js5ResponseDecoder())
channel.writeInbound(read(encoded))
read(container).use { data ->
val expected = Js5Response(prefetch, 2, 3, data)
channel.readInbound<Js5Response>().use { actual ->
assertEquals(expected, actual)
}
}
}
private fun read(name: String): ByteBuf {
Js5ResponseDecoderTest::class.java.getResourceAsStream(name).use { input ->
return Unpooled.wrappedBuffer(input.readAllBytes())
}
}
}

@ -0,0 +1,60 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import io.netty.channel.embedded.EmbeddedChannel
import io.netty.handler.codec.EncoderException
import org.junit.jupiter.api.assertThrows
import org.openrs2.buffer.use
import kotlin.test.Test
import kotlin.test.assertEquals
object Js5ResponseEncoderTest {
@Test
fun testEncode() {
testEncode("508.dat", "508-prefetch.dat", true)
testEncode("508.dat", "508-urgent.dat", false)
testEncode("509.dat", "509-prefetch.dat", true)
testEncode("509.dat", "509-urgent.dat", false)
testEncode("1019.dat", "1019-prefetch.dat", true)
testEncode("1019.dat", "1019-urgent.dat", false)
testEncode("1020.dat", "1020-prefetch.dat", true)
testEncode("1020.dat", "1020-urgent.dat", false)
testEncode("1530.dat", "1530-prefetch.dat", true)
testEncode("1530.dat", "1530-urgent.dat", false)
testEncode("1531.dat", "1531-prefetch.dat", true)
testEncode("1531.dat", "1531-urgent.dat", false)
}
@Test
fun testEncodeEmpty() {
val channel = EmbeddedChannel(Js5ResponseEncoder())
assertThrows<EncoderException> {
channel.writeOutbound(Js5Response(true, 2, 3, Unpooled.EMPTY_BUFFER))
}
}
private fun testEncode(container: String, encoded: String, prefetch: Boolean) {
val channel = EmbeddedChannel(Js5ResponseEncoder())
read(container).use { buf ->
channel.writeOutbound(Js5Response(prefetch, 2, 3, buf.retain()))
}
read(encoded).use { expected ->
assertEquals(expected, channel.readOutbound())
}
}
private fun read(name: String): ByteBuf {
Js5ResponseEncoderTest::class.java.getResourceAsStream(name).use { input ->
return Unpooled.wrappedBuffer(input.readAllBytes())
}
}
}

@ -0,0 +1,40 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.buffer.PooledByteBufAllocator
import io.netty.buffer.Unpooled
import io.netty.channel.embedded.EmbeddedChannel
import org.openrs2.buffer.use
import kotlin.test.Test
import kotlin.test.assertEquals
object XorDecoderTest {
@Test
fun testDecode() {
testDecode(0, "OpenRS2", false)
testDecode(0, "OpenRS2", true)
testDecode(32, "oPENrs\u0012", false)
testDecode(32, "oPENrs\u0012", true)
}
private fun testDecode(key: Int, expected: String, direct: Boolean) {
val decoder = XorDecoder()
decoder.key = key
val channel = EmbeddedChannel(decoder)
if (direct) {
PooledByteBufAllocator.DEFAULT.ioBuffer().use { buf ->
buf.writeBytes("OpenRS2".toByteArray())
channel.writeInbound(buf.retain())
}
} else {
channel.writeInbound(Unpooled.wrappedBuffer("OpenRS2".toByteArray()))
}
channel.readInbound<ByteBuf>().use { actual ->
Unpooled.wrappedBuffer(expected.toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}

@ -0,0 +1,40 @@
package org.openrs2.protocol.js5
import io.netty.buffer.ByteBuf
import io.netty.buffer.PooledByteBufAllocator
import io.netty.buffer.Unpooled
import io.netty.channel.embedded.EmbeddedChannel
import org.openrs2.buffer.use
import kotlin.test.Test
import kotlin.test.assertEquals
object XorEncoderTest {
@Test
fun testEncode() {
testEncode(0, "OpenRS2", false)
testEncode(0, "OpenRS2", true)
testEncode(32, "oPENrs\u0012", false)
testEncode(32, "oPENrs\u0012", true)
}
private fun testEncode(key: Int, expected: String, direct: Boolean) {
val encoder = XorEncoder()
encoder.key = key
val channel = EmbeddedChannel(encoder)
if (direct) {
PooledByteBufAllocator.DEFAULT.ioBuffer().use { buf ->
buf.writeBytes("OpenRS2".toByteArray())
channel.writeOutbound(buf.retain())
}
} else {
channel.writeOutbound(Unpooled.wrappedBuffer("OpenRS2".toByteArray()))
}
channel.readOutbound<ByteBuf>().use { actual ->
Unpooled.wrappedBuffer(expected.toByteArray()).use { expected ->
assertEquals(expected, actual)
}
}
}
}

@ -28,6 +28,7 @@ include(
"nonfree:signlink",
"nonfree:unpack",
"nonfree:unpackclass",
"protocol",
"util",
"yaml"
)

Loading…
Cancel
Save