TextureSync/client/src/main/kotlin/org/hso/texturesyncclient/controller/net/Package.kt

161 lines
4.2 KiB
Kotlin

package org.hso.texturesyncclient.controller.net
import com.google.gson.JsonElement
import com.google.gson.JsonParseException
import com.google.gson.JsonParser
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.IOException
internal abstract class Package {
@Throws(IOException::class)
abstract fun write(out: DataOutputStream)
companion object {
const val TYPE_ERROR: Byte = 0
protected const val TYPE_ERROR_MAX_PAYLOAD: Int = 1024
const val TYPE_JSON: Byte = 1
protected const val TYPE_JSON_MAX_PAYLOAD: Int = 16 * 1024 * 1024
const val TYPE_BINARY: Byte = 2
protected const val TYPE_BINARY_MAX_PAYLOAD: Int = 512 * 1024 * 1024
@Throws(PacketException::class, IOException::class)
fun read(input: DataInputStream): Package {
// Type Byte
val type = input.readByte()
// 3 Reserved Bytes
input.readByte()
input.readByte()
input.readByte()
// 4 Len Bytes
val length = input.readInt()
when (type) {
TYPE_ERROR -> if (length > TYPE_ERROR_MAX_PAYLOAD) {
throw PacketTooLongException()
}
TYPE_JSON -> if (length > TYPE_JSON_MAX_PAYLOAD) {
throw PacketTooLongException()
}
TYPE_BINARY -> if (length > TYPE_BINARY_MAX_PAYLOAD) {
throw PacketTooLongException()
}
else -> throw PacketInvalidType()
}
val payload = ByteArray(length)
input.readFully(payload)
when (type) {
TYPE_ERROR -> {
val msg = String(payload)
val results = msg.split(' ', ignoreCase = false, limit = 2)
return if (results.size == 2) {
val code = results[0].toIntOrNull()
if (code == null) {
ErrorPackage(0, msg)
} else {
ErrorPackage(code, results[1])
}
} else {
ErrorPackage(0, msg)
}
}
TYPE_JSON -> {
try {
val obj = JsonParser().parse(String(payload))
return JsonPackage(obj)
} catch (e: JsonParseException) {
throw PacketInvalidData()
}
}
TYPE_BINARY -> {
return BinaryPackage(payload)
}
else -> {
// Unreachable
throw PacketInvalidType()
}
}
}
}
}
internal data class JsonPackage(val content: JsonElement) : Package() {
override fun write(out: DataOutputStream) {
val payload = content.toString().toByteArray()
// Tag Byte
out.writeByte(TYPE_JSON.toInt())
// 3 Reserved Bytes
out.writeByte(0x42)
out.writeByte(0x42)
out.writeByte(0x42)
// Length of Payload
out.writeInt(payload.size)
// Payload
out.write(payload)
out.flush()
}
}
@Suppress("ArrayInDataClass")
internal data class BinaryPackage(val content: ByteArray) : Package() {
override fun write(out: DataOutputStream) {
// Tag Byte
out.writeByte(TYPE_BINARY.toInt())
// 3 Reserved Bytes
out.writeByte(0x42)
out.writeByte(0x42)
out.writeByte(0x42)
// Length of Payload
out.writeInt(content.size)
// Payload
out.write(content)
out.flush()
}
}
internal data class ErrorPackage(val code: Int, val message: String) : Package() {
override fun write(out: DataOutputStream) {
val payload = "$code $message".toByteArray()
// Tag Byte
out.writeByte(TYPE_ERROR.toInt())
// 3 Reserved Bytes
out.writeByte(0x42)
out.writeByte(0x42)
out.writeByte(0x42)
// Length of Payload
out.writeInt(payload.size)
// Payload
out.write(payload)
out.flush()
}
}