Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions koap-core/api/koap-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,19 @@ public final class com/juul/koap/Message$Option$Edhoc : com/juul/koap/Message$Op
public fun toString ()Ljava/lang/String;
}

public final class com/juul/koap/Message$Option$ExperimentalUse : com/juul/koap/Message$Option {
public fun <init> (I[B)V
public final fun component1 ()I
public final fun component2 ()[B
public final fun copy (I[B)Lcom/juul/koap/Message$Option$ExperimentalUse;
public static synthetic fun copy$default (Lcom/juul/koap/Message$Option$ExperimentalUse;I[BILjava/lang/Object;)Lcom/juul/koap/Message$Option$ExperimentalUse;
public fun equals (Ljava/lang/Object;)Z
public final fun getNumber ()I
public final fun getValue ()[B
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract class com/juul/koap/Message$Option$Format : com/juul/koap/Message$Option {
public abstract fun getNumber ()I
}
Expand Down Expand Up @@ -554,6 +567,18 @@ public final class com/juul/koap/Message$Option$RequestTag : com/juul/koap/Messa
public fun toString ()Ljava/lang/String;
}

public final class com/juul/koap/Message$Option$Reserved : com/juul/koap/Message$Option {
public final fun component1 ()I
public final fun component2 ()[B
public final fun copy (I[B)Lcom/juul/koap/Message$Option$Reserved;
public static synthetic fun copy$default (Lcom/juul/koap/Message$Option$Reserved;I[BILjava/lang/Object;)Lcom/juul/koap/Message$Option$Reserved;
public fun equals (Ljava/lang/Object;)Z
public final fun getNumber ()I
public final fun getValue ()[B
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/juul/koap/Message$Option$Size1 : com/juul/koap/Message$Option {
public fun <init> (J)V
public final fun component1 ()J
Expand All @@ -576,6 +601,18 @@ public final class com/juul/koap/Message$Option$Size2 : com/juul/koap/Message$Op
public fun toString ()Ljava/lang/String;
}

public final class com/juul/koap/Message$Option$Unassigned : com/juul/koap/Message$Option {
public final fun component1 ()I
public final fun component2 ()[B
public final fun copy (I[B)Lcom/juul/koap/Message$Option$Unassigned;
public static synthetic fun copy$default (Lcom/juul/koap/Message$Option$Unassigned;I[BILjava/lang/Object;)Lcom/juul/koap/Message$Option$Unassigned;
public fun equals (Ljava/lang/Object;)Z
public final fun getNumber ()I
public final fun getValue ()[B
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class com/juul/koap/Message$Option$UriHost : com/juul/koap/Message$Option {
public fun <init> (Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/String;
Expand Down
44 changes: 44 additions & 0 deletions koap-core/api/koap-core.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,22 @@ sealed class com.juul.koap/Message { // com.juul.koap/Message|null[0]
final fun toString(): kotlin/String // com.juul.koap/Message.Option.Echo.toString|toString(){}[0]
}

final class ExperimentalUse : com.juul.koap/Message.Option { // com.juul.koap/Message.Option.ExperimentalUse|null[0]
constructor <init>(kotlin/Int, kotlin/ByteArray) // com.juul.koap/Message.Option.ExperimentalUse.<init>|<init>(kotlin.Int;kotlin.ByteArray){}[0]

final val number // com.juul.koap/Message.Option.ExperimentalUse.number|{}number[0]
final fun <get-number>(): kotlin/Int // com.juul.koap/Message.Option.ExperimentalUse.number.<get-number>|<get-number>(){}[0]
final val value // com.juul.koap/Message.Option.ExperimentalUse.value|{}value[0]
final fun <get-value>(): kotlin/ByteArray // com.juul.koap/Message.Option.ExperimentalUse.value.<get-value>|<get-value>(){}[0]

final fun component1(): kotlin/Int // com.juul.koap/Message.Option.ExperimentalUse.component1|component1(){}[0]
final fun component2(): kotlin/ByteArray // com.juul.koap/Message.Option.ExperimentalUse.component2|component2(){}[0]
final fun copy(kotlin/Int = ..., kotlin/ByteArray = ...): com.juul.koap/Message.Option.ExperimentalUse // com.juul.koap/Message.Option.ExperimentalUse.copy|copy(kotlin.Int;kotlin.ByteArray){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.juul.koap/Message.Option.ExperimentalUse.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.juul.koap/Message.Option.ExperimentalUse.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.juul.koap/Message.Option.ExperimentalUse.toString|toString(){}[0]
}

final class HopLimit : com.juul.koap/Message.Option { // com.juul.koap/Message.Option.HopLimit|null[0]
constructor <init>(kotlin/Long) // com.juul.koap/Message.Option.HopLimit.<init>|<init>(kotlin.Long){}[0]

Expand Down Expand Up @@ -573,6 +589,20 @@ sealed class com.juul.koap/Message { // com.juul.koap/Message|null[0]
final fun toString(): kotlin/String // com.juul.koap/Message.Option.RequestTag.toString|toString(){}[0]
}

final class Reserved : com.juul.koap/Message.Option { // com.juul.koap/Message.Option.Reserved|null[0]
final val number // com.juul.koap/Message.Option.Reserved.number|{}number[0]
final fun <get-number>(): kotlin/Int // com.juul.koap/Message.Option.Reserved.number.<get-number>|<get-number>(){}[0]
final val value // com.juul.koap/Message.Option.Reserved.value|{}value[0]
final fun <get-value>(): kotlin/ByteArray // com.juul.koap/Message.Option.Reserved.value.<get-value>|<get-value>(){}[0]

final fun component1(): kotlin/Int // com.juul.koap/Message.Option.Reserved.component1|component1(){}[0]
final fun component2(): kotlin/ByteArray // com.juul.koap/Message.Option.Reserved.component2|component2(){}[0]
final fun copy(kotlin/Int = ..., kotlin/ByteArray = ...): com.juul.koap/Message.Option.Reserved // com.juul.koap/Message.Option.Reserved.copy|copy(kotlin.Int;kotlin.ByteArray){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.juul.koap/Message.Option.Reserved.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.juul.koap/Message.Option.Reserved.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.juul.koap/Message.Option.Reserved.toString|toString(){}[0]
}

final class Size1 : com.juul.koap/Message.Option { // com.juul.koap/Message.Option.Size1|null[0]
constructor <init>(kotlin/Long) // com.juul.koap/Message.Option.Size1.<init>|<init>(kotlin.Long){}[0]

Expand All @@ -599,6 +629,20 @@ sealed class com.juul.koap/Message { // com.juul.koap/Message|null[0]
final fun toString(): kotlin/String // com.juul.koap/Message.Option.Size2.toString|toString(){}[0]
}

final class Unassigned : com.juul.koap/Message.Option { // com.juul.koap/Message.Option.Unassigned|null[0]
final val number // com.juul.koap/Message.Option.Unassigned.number|{}number[0]
final fun <get-number>(): kotlin/Int // com.juul.koap/Message.Option.Unassigned.number.<get-number>|<get-number>(){}[0]
final val value // com.juul.koap/Message.Option.Unassigned.value|{}value[0]
final fun <get-value>(): kotlin/ByteArray // com.juul.koap/Message.Option.Unassigned.value.<get-value>|<get-value>(){}[0]

final fun component1(): kotlin/Int // com.juul.koap/Message.Option.Unassigned.component1|component1(){}[0]
final fun component2(): kotlin/ByteArray // com.juul.koap/Message.Option.Unassigned.component2|component2(){}[0]
final fun copy(kotlin/Int = ..., kotlin/ByteArray = ...): com.juul.koap/Message.Option.Unassigned // com.juul.koap/Message.Option.Unassigned.copy|copy(kotlin.Int;kotlin.ByteArray){}[0]
final fun equals(kotlin/Any?): kotlin/Boolean // com.juul.koap/Message.Option.Unassigned.equals|equals(kotlin.Any?){}[0]
final fun hashCode(): kotlin/Int // com.juul.koap/Message.Option.Unassigned.hashCode|hashCode(){}[0]
final fun toString(): kotlin/String // com.juul.koap/Message.Option.Unassigned.toString|toString(){}[0]
}

final class UriHost : com.juul.koap/Message.Option { // com.juul.koap/Message.Option.UriHost|null[0]
constructor <init>(kotlin/String) // com.juul.koap/Message.Option.UriHost.<init>|<init>(kotlin.String){}[0]

Expand Down
7 changes: 6 additions & 1 deletion koap-core/src/commonMain/kotlin/Decoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.juul.koap.Message.Option.ContentFormat
import com.juul.koap.Message.Option.ETag
import com.juul.koap.Message.Option.Echo
import com.juul.koap.Message.Option.Edhoc
import com.juul.koap.Message.Option.ExperimentalUse
import com.juul.koap.Message.Option.Format
import com.juul.koap.Message.Option.HopLimit
import com.juul.koap.Message.Option.IfMatch
Expand All @@ -48,8 +49,10 @@ import com.juul.koap.Message.Option.ProxyUri
import com.juul.koap.Message.Option.QBlock1
import com.juul.koap.Message.Option.QBlock2
import com.juul.koap.Message.Option.RequestTag
import com.juul.koap.Message.Option.Reserved
import com.juul.koap.Message.Option.Size1
import com.juul.koap.Message.Option.Size2
import com.juul.koap.Message.Option.Unassigned
import com.juul.koap.Message.Option.UriHost
import com.juul.koap.Message.Option.UriPath
import com.juul.koap.Message.Option.UriPort
Expand Down Expand Up @@ -428,7 +431,9 @@ internal fun ByteArrayReader.readOption(preceding: Format?): Option? {
252 -> Echo(readByteArray(length))
258 -> NoResponse(readNumberOfLength(length))
292 -> RequestTag(readByteArray(length))
else -> error("Unsupported option number $number")
in RESERVED_OPTION_NUMBERS -> Reserved(number, readByteArray(length))
in EXPERIMENTAL_USE_OPTION_RANGE -> ExperimentalUse(number, readByteArray(length))
else -> Unassigned(number, readByteArray(length))
}
}

Expand Down
63 changes: 63 additions & 0 deletions koap-core/src/commonMain/kotlin/Message.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ private val HOP_LIMIT_RANGE = 1..255
private val ECHO_SIZE_RANGE = 1..40
private val REQUEST_TAG_SIZE_RANGE = 0..8
private val NO_RESPONSE_RANGE = 0..127
internal val RESERVED_OPTION_NUMBERS = setOf(0, 128, 132, 136, 140)
internal val EXPERIMENTAL_USE_OPTION_RANGE = 65000..65535

sealed class Message {

Expand Down Expand Up @@ -149,6 +151,67 @@ sealed class Message {
) : Format()
}

data class Unassigned internal constructor(
val number: Int,
val value: ByteArray,
) : Option() {

override fun equals(other: Any?): Boolean =
this === other ||
(other is Unassigned && number == other.number && value.contentEquals(other.value))

override fun hashCode(): Int {
var result = number
result = 31 * result + value.contentHashCode()
return result
}

override fun toString(): String = "Unassigned(number=$number, value=${value.toHexString()})"
}

/** RFC 7252 5.10.7. Location-Path and Location-Query reserved option numbers, and zero */
data class Reserved internal constructor(
val number: Int,
val value: ByteArray,
) : Option() {

override fun equals(other: Any?): Boolean =
this === other ||
(other is Reserved && number == other.number && value.contentEquals(other.value))

override fun hashCode(): Int {
var result = number
result = 31 * result + value.contentHashCode()
return result
}

override fun toString(): String = "Reserved(number=$number, value=${value.toHexString()})"
}

/** RFC 7252 12.2. CoAP Option Numbers Registry, Table 8, Experimental use */
data class ExperimentalUse(
val number: Int,
val value: ByteArray,
) : Option() {
init {
require(number in EXPERIMENTAL_USE_OPTION_RANGE) {
"Option number $number is outside experimental use range of $EXPERIMENTAL_USE_OPTION_RANGE"
}
}

override fun equals(other: Any?): Boolean =
this === other ||
(other is ExperimentalUse && number == other.number && value.contentEquals(other.value))

override fun hashCode(): Int {
var result = number
result = 31 * result + value.contentHashCode()
return result
}

override fun toString(): String = "ExperimentalUse(number=$number, value=${value.toHexString()})"
}

/** RFC 7252 5.10.1. Uri-Host, Uri-Port, Uri-Path, and Uri-Query */
data class UriHost(
val uri: String,
Expand Down
6 changes: 6 additions & 0 deletions koap-core/src/commonMain/kotlin/ToFormat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.juul.koap.Message.Option.ContentFormat
import com.juul.koap.Message.Option.ETag
import com.juul.koap.Message.Option.Echo
import com.juul.koap.Message.Option.Edhoc
import com.juul.koap.Message.Option.ExperimentalUse
import com.juul.koap.Message.Option.Format
import com.juul.koap.Message.Option.Format.empty
import com.juul.koap.Message.Option.Format.opaque
Expand All @@ -27,8 +28,10 @@ import com.juul.koap.Message.Option.ProxyUri
import com.juul.koap.Message.Option.QBlock1
import com.juul.koap.Message.Option.QBlock2
import com.juul.koap.Message.Option.RequestTag
import com.juul.koap.Message.Option.Reserved
import com.juul.koap.Message.Option.Size1
import com.juul.koap.Message.Option.Size2
import com.juul.koap.Message.Option.Unassigned
import com.juul.koap.Message.Option.UriHost
import com.juul.koap.Message.Option.UriPath
import com.juul.koap.Message.Option.UriPort
Expand Down Expand Up @@ -65,4 +68,7 @@ internal fun Option.toFormat(): Format =
is Echo -> opaque(252, option.value)
is NoResponse -> uint(258, option.value)
is RequestTag -> opaque(292, option.tag)
is Reserved -> opaque(option.number, option.value)
is ExperimentalUse -> opaque(option.number, option.value)
is Unassigned -> opaque(option.number, option.value)
}
36 changes: 36 additions & 0 deletions koap-core/src/commonTest/kotlin/DecoderTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.juul.koap.Message.Option.Block1
import com.juul.koap.Message.Option.Block2
import com.juul.koap.Message.Option.Echo
import com.juul.koap.Message.Option.Edhoc
import com.juul.koap.Message.Option.ExperimentalUse
import com.juul.koap.Message.Option.HopLimit
import com.juul.koap.Message.Option.NoResponse
import com.juul.koap.Message.Option.NoResponse.NotInterestedIn.Response4xx
Expand All @@ -20,8 +21,10 @@ import com.juul.koap.Message.Option.Oscore
import com.juul.koap.Message.Option.QBlock1
import com.juul.koap.Message.Option.QBlock2
import com.juul.koap.Message.Option.RequestTag
import com.juul.koap.Message.Option.Reserved
import com.juul.koap.Message.Option.Size1
import com.juul.koap.Message.Option.Size2
import com.juul.koap.Message.Option.Unassigned
import com.juul.koap.Message.Option.UriHost
import com.juul.koap.Message.Option.UriPath
import com.juul.koap.Message.Option.UriPort
Expand Down Expand Up @@ -459,6 +462,39 @@ class DecoderTest {
)
}

@Test
fun decodeUnassignedOption() {
testReadOption(
encoded = """
E3 11 27 # Option Delta: 0x1234, Option Length: 3
01 02 03 # Option Value: 0x01, 0x02, 0x03
""",
expected = Unassigned(0x1234, byteArrayOf(0x01, 0x02, 0x03)),
)
}

@Test
fun decodeReservedOption() {
testReadOption(
encoded = """
D3 7F # Option Delta: 140, Option Length: 3
61 62 63 # Option Value: 0x61, 0x62, 0x63
""",
expected = Reserved(140, byteArrayOf(0x61, 0x62, 0x63)),
)
}

@Test
fun decodeExperimentalUseOption() {
testReadOption(
encoded = """
E3 FC DE # Option Delta: 65003, Option Length: 3
41 42 43 # Option Value: 0x41, 0x42, 0x43
""",
expected = ExperimentalUse(65003, byteArrayOf(0x41, 0x42, 0x43)),
)
}

@Test
fun decodingTcpMessageDoesNotReadBeyondLengthSpecifiedInHeader() {
val message = Message.Tcp(
Expand Down
43 changes: 43 additions & 0 deletions koap-core/src/commonTest/kotlin/EncoderTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.juul.koap.Message.Option.Block1
import com.juul.koap.Message.Option.Block2
import com.juul.koap.Message.Option.Echo
import com.juul.koap.Message.Option.Edhoc
import com.juul.koap.Message.Option.ExperimentalUse
import com.juul.koap.Message.Option.HopLimit
import com.juul.koap.Message.Option.NoResponse
import com.juul.koap.Message.Option.NoResponse.NotInterestedIn.Response2xx
Expand All @@ -20,8 +21,10 @@ import com.juul.koap.Message.Option.Oscore
import com.juul.koap.Message.Option.QBlock1
import com.juul.koap.Message.Option.QBlock2
import com.juul.koap.Message.Option.RequestTag
import com.juul.koap.Message.Option.Reserved
import com.juul.koap.Message.Option.Size1
import com.juul.koap.Message.Option.Size2
import com.juul.koap.Message.Option.Unassigned
import com.juul.koap.Message.Option.UriPath
import com.juul.koap.Message.Option.UriPort
import com.juul.koap.Message.Udp.Type.Acknowledgement
Expand Down Expand Up @@ -523,6 +526,46 @@ class EncoderTest {
""",
)
}

@Test
fun writeUnassignedOption() {
testWriteOption(
option = Unassigned(0x4321, byteArrayOf(0x04, 0x03, 0x02, 0x01)),
expected = """
E4 42 14 # Option Delta: 0x4321, Option Length: 4
04 03 02 01 # Option Value: 0x04, 0x03, 0x02, 0x01
""",
)
}

@Test
fun writeReservedOption() {
testWriteOption(
option = Reserved(136, byteArrayOf(0x34, 0x33, 0x32, 0x31)),
expected = """
D4 7B # Option Delta: 136, Option Length: 4
34 33 32 31 # Option Value: 0x34, 0x33, 0x32, 0x31
""",
)
}

@Test
fun writeExperimentalUseOption() {
testWriteOption(
option = ExperimentalUse(65007, byteArrayOf(0x24, 0x23, 0x22, 0x21)),
expected = """
E4 FC E2 # Option Delta: 65007, Option Length: 4
24 23 22 21 # Option Value: 0x24, 0x23, 0x22, 0x21
""",
)
}

@Test
fun experimentalUseOptionWithNumberOutsideExperimentalRangeThrowsIllegalArgumentException() {
assertFailsWith<IllegalArgumentException> {
ExperimentalUse(64999, byteArrayOf())
}
}
}

private fun testWriteToken(
Expand Down
Loading
Loading