Giter Club home page Giter Club logo

koap's Introduction

badge badge codecov Apache 2.0

KoAP

Kotlin CoAP encoder/decoder that provides basic support for:

Usage

Encoding

ByteArrays of encoded CoAP messages can be created from Message objects (either Message.Udp or Message.Tcp depending on the desired encoding).

To encode a Message as a CoAP UDP ByteArray:

import com.juul.koap.Message.Udp
import com.juul.koap.encode

val message = Message.Udp(/* ... */)
val encoded = message.encode()

Similarly, to encode a Message as a CoAP TCP ByteArray:

import com.juul.koap.Message.Tcp
import com.juul.koap.encode

val message = Message.Tcp(/* ... */)
val encoded = message.encode()

Javascript Encoding Example

Encode a Message as a CoAP TCP ByteArray:

import kotlin from 'kotlin'
import koapModule from '@juullabs/koap'

const koap = koapModule.com.juul.koap

let method = koap.Message.Code.Method.GET
let token = new kotlin.Long(66)
let emptyPayload = new Uint8Array()
let optionsArray = [
    new koap.Message.Option.UriPath('info'),
    new koap.Message.Option.UriQuery('batt')
]
let optionsList = new kotlin.kotlin.collections.ArrayList(optionsArray)

let message = new koap.Message.Tcp(
    method,
    token,
    optionsList,
    emptyPayload
)
let encoded = koap.encode(message)

Decoding

Encoded CoAP messages (in the form of ByteArrays) can be decoded to Message objects (either Message.Udp or Message.Tcp, depending on the encoding).

To decode a ByteArray containing a message encoded as CoAP UDP:

import com.juul.koap.Message.Udp
import com.juul.koap.decode

val encoded: ByteArray // Encoded message that adheres to RFC 7252.
val message = encoded.decode<Udp>()

Similarly, to decode a ByteArray encoded as a CoAP TCP message:

import com.juul.koap.Message.Tcp
import com.juul.koap.decode

val encoded: ByteArray // Encoded message that adheres to RFC 8323.
val message = encoded.decode<Tcp>()

Header

If it's desirable to examine the header prior to decoding the entire encoded CoAP message, then ByteArray.decodeUdpHeader or ByteArray.decodeTcpHeader extension functions are available. The remaining encoded CoAP message can then be decoded by passing the header to the ByteArray.decode extension function, for example:

val encoded: ByteArray // Encoded message that adheres to RFC 7252.
val header = encoded.decodeUdpHeader()

// Examine header and determine that message should be decoded:
val message = encoded.decode(header)

Javascript Decoding Example

Decode a ByteArray as a CoAP TCP Message:

import kotlin from 'kotlin'
import koapModule from '@juullabs/koap'

const koap = koapModule.com.juul.koap

let encoded = new Uint8Array(/* ...message data... */)
let message = koap.decodeTcp(encoded)

Examples

Encoding

val message = Message.Udp(
    type = Confirmable,
    code = GET,
    id = 0xFEED,
    token = 0xCAFE,
    options = listOf(
        UriPath("example")
    ),
    payload = byteArrayOf()
)

Encoding the above Message will produce the following encoded data:

42 01 FE ED CA FE B7 65 78 61 6D 70 6C 65

Encoded messages adhere to the format described in Figure 7: Message Format of RFC 7252:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Ver| T |  TKL  |      Code     |          Message ID           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Token (if any, TKL bytes) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Options (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|1 1 1 1 1 1 1 1|    Payload (if any) ...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Field(s) Hex Description
Ver, T, TKL 42 4 = Version: 1, Type: 0 (Confirmable)
2 = Token Length: 2
Code 01 Code: 0.01 (GET)
Message ID FE ED
Token CA FE
Option delta/length B7 B = Delta option: 11 (Uri-Path)
7 = Delta length: 7
Option value 65 78 61 6D 70 6C 65 "example"

Decoding

Assuming an encoded message consists of the following CoAP UDP encoded data:

42 01 FE ED CA FE B5 2F 74 65 73 74 FF 48 65 6C 6C 6F 20 55 44 50 21
Field(s) Hex Description
Ver, T, TKL 42 4 = Version: 1, Type: 0 (Confirmable)
2 = Token Length: 2
Code 01 Code: 0.01 (GET)
Message ID FE ED
Token CA FE
Option delta/length B5 B = Delta option: 11 (Uri-Path)
5 = Delta length: 5
Option value 2F 74 65 73 74 "/test"
Payload marker FF Signifies the end of Options and beginning of payload.
Payload 48 65 6C 6C 6F 20 55 44 50 21 "Hello UDP!"

The above encoded UDP message can be decoded to a Message.Udp via ByteArray.decode extension function:

val message = encoded.decode<Udp>()

The resulting Message will be equivalent to:

Message.Udp(
    type = Confirmable,
    code = GET,
    id = 0xFEED,
    token = 0xCAFE,
    options = listOf(
        UriPath("/test")
    ),
    payload = "Hello UDP!".toByteArray()
)

Setup

Gradle

Maven Central

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.juul.koap:koap-core:$version")
}

License

Copyright 2020 JUUL Labs, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

koap's People

Contributors

cedrickcooke avatar davidtaylor-juul avatar juul-mobile-bot avatar kvelasco avatar phoenix7351 avatar twyatt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

bppleman

koap's Issues

Example using with BLE

Hello! Just discovered this implementation, very cool. I've had the idea to use CoAP over BLE for some time and was wondering if you had any examples of that, perhaps with kable?

java.lang.IllegalStateException: Unsupported option number 21

Caused by java.lang.IllegalStateException: Unsupported option number 21
       at com.juul.koap.DecoderKt.readOption(DecoderKt.java:412)
       at com.juul.koap.DecoderKt.readOptions(DecoderKt.java:329)
       at com.juul.koap.DecoderKt.decodeContent(DecoderKt.java:194)
       at com.juul.koap.DecoderKt.decode(DecoderKt.java:178)
       at com.juul.koap.DecoderKt.decode$default(DecoderKt.java:175)
       <redacted>
       at kotlinx.coroutines.flow.FlowKt__TransformKt$onEach$$inlined$unsafeTransform$1$2.emit(FlowKt__TransformKt.java:137)
       at com.juul.kable.Observers$acquire$$inlined$map$1$2.emit(Observers.java:137)
       at com.juul.kable.Observers$acquire$$inlined$filter$1$2.emit(Observers.java:137)
       at kotlinx.coroutines.flow.SubscribedFlowCollector.emit(SubscribedFlowCollector.java:2)
       at kotlinx.coroutines.flow.SharedFlowImpl.collect(SharedFlowImpl.java:351)
       at kotlinx.coroutines.flow.SharedFlowImpl$collect$1.invokeSuspend(SharedFlowImpl.java:12)
       at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(BaseContinuationImpl.java:33)
       at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.java:106)
       at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.java:571)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.java:750)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.java:678)
       at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.java:665)

Optimize online CoAP decoder

#42 integrated a CoAP decoder webapp that is deployed to GitHub Pages.

It utlilizes browserDevelopmentWebpack Gradle task for building the static pages for the webapp. Unfortunately the development builds are extremely bloated. Attempts to use the production tasks for generating web distributions failed (unable to call Kotlin from JavaScript).

Testing with the js(IR) compiler worked (for calling Kotlin from JavaScript) but did not work with the current version of Okio (which is not compiled with IR compiler).

In the future we should either:

  1. Check if js(LEGACY) has fixes that properly allow calling Kotlin from JavaScript
  2. Switch to js(IR) when Okio supports it (preferred)

More details can be found at https://stackoverflow.com/a/65680291/196486.

Provide pre-defined Content-Format constants

Per RFC 7252 12.3.:

The Content-Format Option indicates the representation format of the message payload. The representation format is given as a numeric Content-Format identifier that is defined in the "CoAP Content-Formats" registry (Section 12.3).

KoAP should provide constants for the media types listed in Table 9: CoAP Content-Formats (found in section 12.3):

   +--------------------------+----------+----+------------------------+
   | Media type               | Encoding | ID | Reference              |
   +--------------------------+----------+----+------------------------+
   | text/plain;              | -        |  0 | [RFC2046] [RFC3676]    |
   | charset=utf-8            |          |    | [RFC5147]              |
   | application/link-format  | -        | 40 | [RFC6690]              |
   | application/xml          | -        | 41 | [RFC3023]              |
   | application/octet-stream | -        | 42 | [RFC2045] [RFC2046]    |
   | application/exi          | -        | 47 | [REC-exi-20140211]     |
   | application/json         | -        | 50 | [RFC7159]              |
   +--------------------------+----------+----+------------------------+

Parse unknown options as opaque instead of failing the whole message

Currently it is not possible to see anything about a message that includes an unsupported option, except that option number.

For example, the UDP message 40020011c13cffd1e91a8401020304: "Unsupported option number 258"

(Option 258 is the No-Response option RFC-7967, listed in IANA CoAP Option Numbers)

Removing the unsupported option, 40020011c13cff8401020304, you get to see the full message details:

Message:
{
"type": "Confirmable",
"code": "POST",
"id": 17,
"token": 0,
"options": [
"Content-Format: application/cbor"
]
}

Payload (CBOR):
[1, 2, 3, 4]

It would be nice if unsupported options could be listed as opaque option number, value pairs in the options list, perhaps with a warning at the end.

How to receive payload data?

Hello, I am connecting my kotlin app to an ESP8266 Coap server. I have defined my message like so:

private val message = Message.Udp(
    type = Udp.Type.Confirmable,
    code = Message.Code.Method.GET,
    id = 0x0,
    token = 0x0,
    options = listOf(Message.Option.UriPath("192.168.4.1"), Message.Option.UriPort(5683), Message.Option.LocationPath("/SpO2")),
    payload = byteArrayOf()
)

Then I have a scheduled timer task sending the encoded message periodically and am trying to decode:

val encoded = message.encode()
val decoded = encoded.decode<Udp>()
Log.d("CoAP:  ", decoded.payload.toString())

However I keep receiving garbage data. "[B@91f6bff, [B@b82ddcc, [B@e14c815, [B@90dc72a, [B@208c91b" etc...

Is there a way to verify I have the right URI path?

Online CoAP decoder

Similar to cbor.me, an online encoder/decoder tool should be created. Tool should be published to GitHub Pages on commits merged to main.

Public API Not Directly Usable From Javascript

Unable to invoke the Message.encode method from js, method is defined here:
https://github.com/JuulLabs/koap/blob/main/koap/src/commonMain/kotlin/Encoder.kt#L56

Example:

import koapModule from '@juullabs/koap'
import kotlin from 'kotlin'
const koap = koapModule.com.juul.koap
let token = new kotlin.Long(66)
let emptyPayload = new Uint8Array()
let kMsg = new koap.Message.Tcp(
    koap.Message.Code.Method.GET,
    token,
    new kotlin.kotlin.collections.ArrayList(
        [
            new koap.Message.Option.UriPath("dev"),
            new koap.Message.Option.UriQuery("abd")
        ]
    ),
    emptyPayload
}
let outMsg = koap.encode(kMsg)

Results in the exception: "Unhandled Rejection (TypeError): koap.encode is not a function"

IMHO there needs to be some javascript unit tests added to exercise the API to verify it performs as expected when published as a library.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.