Code Monkey home page Code Monkey logo

coap-lite's Introduction

coap-lite

Latest version Documentation License

A lightweight low-level CoAP message manipulation crate.

Its goal is to be compliant with the CoAP standards and to provide a building block for libraries (e.g. coap) and applications.

coap-lite supports #![no_std] and embedded environments.

It was originally based on the improved low-level message handling code from the coap crate as well as rust-async-coap, made to work in bare metal environments.

Supported RFCs

Usage

This crate provides several types that can be used to build, modify and encode/decode CoAP messages to/from their byte representation.

Note for no_std users: it does require allocation, so you might have to set a global allocator depending on your target.

Client

The following example uses std::net::UdpSocket to send the UDP packet but you can use anything, e.g. smoltcp for embedded.

use coap_lite::{
    CoapRequest, RequestType as Method
};
use std::net::{SocketAddr, UdpSocket};

fn main() {
    let mut request: CoapRequest<SocketAddr> = CoapRequest::new();

    request.set_method(Method::Get);
    request.set_path("/test");

    let socket = UdpSocket::bind("127.0.0.1:0").unwrap();

    let packet = request.message.to_bytes().unwrap();
    socket.send_to(&packet[..], "127.0.0.1:5683").expect("Could not send the data");
}

Server

use coap_lite::{CoapRequest, Packet};
use std::net::{UdpSocket};

fn main() {
    let socket = UdpSocket::bind("127.0.0.1:5683").unwrap();
    let mut buf = [0; 100];
    let (size, src) = socket.recv_from(&mut buf).expect("Didn't receive data");

    println!("Payload {:x?}", &buf[..size]);

    let packet = Packet::from_bytes(&buf[..size]).unwrap();
    let request = CoapRequest::from_packet(packet, src);

    let method = request.get_method().clone();
    let path = request.get_path();

    println!("Received CoAP request '{:?} {}' from {}", method, path, src);

    let mut response = request.response.unwrap();
    response.message.payload = b"OK".to_vec();

    let packet = response.message.to_bytes().unwrap();
    socket.send_to(&packet[..], &src).expect("Could not send the data");
}

Low-level binary conversion

use coap_lite::{
    CoapOption, MessageClass, MessageType,
    Packet, RequestType, ResponseType,
};

let mut request = Packet::new();
request.header.message_id = 23839;
request.header.code = MessageClass::Request(RequestType::Get);
request.set_token(vec![0, 0, 57, 116]);
request.add_option(CoapOption::UriHost, b"localhost".to_vec());
request.add_option(CoapOption::UriPath, b"tv1".to_vec());
assert_eq!(
    [
        0x44, 0x01, 0x5D, 0x1F, 0x00, 0x00, 0x39, 0x74, 0x39, 0x6C, 0x6F,
        0x63, 0x61, 0x6C, 0x68, 0x6F, 0x73, 0x74, 0x83, 0x74, 0x76, 0x31,
    ],
    request.to_bytes().unwrap()[..]
);

let response = Packet::from_bytes(&[
    0x64, 0x45, 0x5D, 0x1F, 0x00, 0x00, 0x39, 0x74, 0xFF, 0x48, 0x65,
    0x6C, 0x6C, 0x6F, 0x20, 0x57, 0x6F, 0x72, 0x6C, 0x64, 0x21,
])
.unwrap();
assert_eq!(23839, response.header.message_id);
assert_eq!(
    MessageClass::Response(ResponseType::Content),
    response.header.code
);
assert_eq!(MessageType::Acknowledgement, response.header.get_type());
assert_eq!([0, 0, 57, 116], response.get_token()[..]);
assert_eq!(b"Hello World!", &response.payload[..]);

License

Licensed under either of

at your option.

This is a modification of the coap and rust-async-coap crates, their licenses are in LICENSE-3RD-PARTY.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

coap-lite's People

Contributors

amvasil-v avatar chrysn avatar covertness avatar jasta avatar jcbernack avatar jiayihu avatar jkrhb avatar martindisch avatar matteobettini avatar runfalk avatar

Stargazers

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

Watchers

 avatar  avatar  avatar  avatar  avatar

coap-lite's Issues

Add support for non confirmable notifications

According to the CoAP Observe Option RFC 7641, there is no restriction whether a notification (get observe's response) should be MessageType::confirmable or MessageType::NonConfirmable. It says (in page 6) that "The server may send a notification in a confirmable CoAP message to request an acknowledgement from the client." and not MUST (as the RFC's standard for limitations) send.

However, in the observe.rs implementation, the existence of 'unacknowledged_messages' member in 'struct Observer' as 'u8' (and 'unacknowledged_limit' of Subject) practically selects a side and restricts the library user to only receive support for confirmable notifications.
If we add the implementation of Subject::resource_changed, specifically the "retain" of every observer that reach the ack limit, we will find that the library don't support the non-confirmable option.

A use case for the non-confirmable side is obvious - say we need to minimize the transferred data (hence - also the number of coap protocol's packets) between client and server that communicates over a reliable connection (which eliminates the need of Acknowledgments).
It is directly implied that with the current library support, for each notification, we will be "fined" with an additional (and unnecessary) ack message.

The solution is pretty trivial:
We can change the Observer::unacknowledged_messages member to be Option (instead of u8). That will allow the user to choose between confirmable or non-confirmable notification for every resource's observer it registers.
Another minor change that should be done is in the 'create_notification' function that currently set the message type to be 'MessageType::Confirmable'.

Thanks in advance for your support.

No_std support lost due to lru_time_cache dependency?

Hi, I was looking into using your library in a no_std environment, but I am unable to compile due to lru_time_cache dependency which seems to need std. Even with setting default-features to false, the project will not build.

coap-lite = { version = "0.11.1", default-features = false }
   Compiling lru_time_cache v0.11.11
error[E0463]: can't find crate for `std`

get_path tolerates '/' in path

When the CoapRequest::get_path method encounters a slash in a path option, it plainly reports it in the output. Thus, both requests containing the correct sequence of two options Uri-Path: ".well-known", Uri-Path: "core" and requests containing the incorrect1 sequence of one option Uri-Path: ".well-known/core" are reported as ".well-known/core" in get_path.

This leads to server applications silently tolerating erroneous clients, which has already led to such clients not being detected (which then later fail when encountering servers that do URI processing according to RFC7252 Section 6.5)

This behavior is not necessarily wrong, depending on the intention of get_path: It is correct if get_path claims to do a concatenation of the Uri-Path options with slashes, but incorrect if get_path claims to produce the path component of the requested URI. If the latter were to be produced, any slash found in a Uri-Path option would need to be expressed as %2f, any '%' would become '%25', and a few other characters would need conversion too. (Including non-ASCII characters, unless the intention is to produce an IRI path, which would also be valid). Another option would be to make the method fallible and return an error on paths that would need percent encoding; this would also allow a more visible error behavior for components that are not UTF-8, which are currently dropped silently.

Footnotes

  1. It's not incorrect as CoAP request, but it accesses a different resource, which while the server is free to define it, it is very surprising coap://host/.well-known%2fcore. โ†ฉ

Copying request payload to the reply

``While testing coap-rs, which is based on coap-lite, I noticed that coap server by default replies to the POST and PUT requests with an echo of the request, which is confusing. Moreover, it copies request payload to the reply messages to a block1 request, which is clearly a bug.

This happens because in src/response.rs in the function pub fn new(request: &Packet):
packet.payload = request.payload.clone();
request payload is always copied to the reply.

Is there a particular reason to do it, or can this line be deleted? I think in all use cases user is actually responsible for populating response.message.payload.

Message size discovery

When assembling a message, the application may need to decide how much data it packs into a response -- be it because it is doing non-atomic blockwise, be it because it does pagination.

How can the application tell how much space there is available in a message? If that is dependent on the underlying transport: Can the application provide the transport's size (eg. path MTU), and coap-lite would tell it how much space there is? (The calculation is rather simple -- the delta is 4 + token length, but things do get a bit more complicated when extended token lengths are supported, and to some "4" is already too large of a magic number).

Context: Some applications rely on the coap-message method available_length() to decide how much of a message to populate; coap-lite somewhat correctly reports usize::MAX here -- after all, there's no hard limit and there might be jumbogram or IP fragmentation support. Then of course when they do pick that maximum, the allocation of the Vec fails... and I'm looking for ways to provide more sensible data before declaring this to be an error on the side of the application.

Add Support for heapless

Using alloc on embedded devices with highly constrained memory often is difficult because estimation of the required memory is hard. It would be nice if this library would allow using the structures implemented in heapless instead of collections based on alloc.

Of course, that would require additional const generics to pass the allocated memory to the heapless parameters, but I think this is doable. Are there any plans for that to be implemented yet or should I look into it?

ObserveOption doesn't support sequence numbers

The current implementation is actually wrong because the RFC 7641 states that it's also used to send the message sequence, so 0/1 are not the only allowed values. There was already an error from the the previous impl because if the value is 0 (Register), it should actually be omitted but this has been fixed in #2

When included in a response, the Observe Option identifies the message as a notification. This implies that a matching entry exists in the list of observers and that the server will notify the client of changes to the resource state. The option value is a sequence number for reordering detection (see Sections 3.4 and 4.4).

coap-lite/src/packet.rs

Lines 173 to 198 in 2082b6a

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ObserveOption {
Register,
Deregister,
}
impl TryFrom<usize> for ObserveOption {
type Error = InvalidObserve;
fn try_from(number: usize) -> Result<ObserveOption, InvalidObserve> {
match number {
0 => Ok(ObserveOption::Register),
1 => Ok(ObserveOption::Deregister),
_ => Err(InvalidObserve),
}
}
}
impl From<ObserveOption> for usize {
fn from(observe: ObserveOption) -> usize {
match observe {
ObserveOption::Register => 0,
ObserveOption::Deregister => 1,
}
}
}

It's not a big issue since one can workaround by directly reading and writing the option without using the enum. Packet::set_observe takes a generic Vec<u8>. It's also not a big issue for embedded devices which usually send the data, it's the receiver which must correctly check the sequence number for ordering.

I don't have the time to fix it now so I'll just leave the issue open for information

Message codes for server errors are wrong

The message codes for server error are not the right ones

MessageClass::Response(ResponseType::InternalServerError) => 0x90,
MessageClass::Response(ResponseType::NotImplemented) => 0x91,
MessageClass::Response(ResponseType::BadGateway) => 0x92,
MessageClass::Response(ResponseType::ServiceUnavailable) => 0x93,
MessageClass::Response(ResponseType::GatewayTimeout) => 0x94,
MessageClass::Response(ResponseType::ProxyingNotSupported) => 0x95,

while they should be:

MessageClass::Response(ResponseType::InternalServerError) => 0xA0,
MessageClass::Response(ResponseType::NotImplemented) => 0xA1,
MessageClass::Response(ResponseType::BadGateway) => 0xA2,
MessageClass::Response(ResponseType::ServiceUnavailable) => 0xA3,
MessageClass::Response(ResponseType::GatewayTimeout) => 0xA4,
MessageClass::Response(ResponseType::ProxyingNotSupported) => 0xA5,

Derive PartialEq for structs

During my development I encountered the need to compare several pub structs:

  • CoapRequest
  • CoapResponse
  • Packet
  • Header

It will be nice if you can derive PartialEq for them in order to simplify the usage of your library (and save the need to write these comparisons manually).

Thanks in advance

`get_token` returns `&Vec<u8>`

Something i noticed while looking at the documentation, I think the general ergonomic usage of returning byte slices is returning &[u8] over &Vec<u8>

More examples is needed.

The current example client code in the README doesn't receive the response and doesn't process the Block-Wise Transfers.

Improve README

Now that coap-rs uses this crate under the hood, I think we could improve the description in the README. I think we should emphasise more on describing it as a low-level CoAP implementation, but no that much low-level that it's useful only for embedded/no-std as one might think.

Also we can use some better example than showing how to format to and parse from binary, e.g. using higher-level CoapRequest and CoapRequest. Also pointing to coap-rs as real-world example of usage.

message size limit

coap-lite currently implements two different message size limits. 1280 bytes or 64000 bytes with the "udp" feature.

1280 Bytes is the minimum IP MTU which must be supported by an IPv6 link. Within these 1280 bytes you also have to accommodate space for the IP and UDP headers, which leaves less than 1280 bytes space for the CoAP message. RFC 7252 recommends to use 1152 bytes for the maximum CoAP message size if nothing about the lower layers is known, which is 128 bytes fewer than coap-lite currently implements.

coap-lite limits the message size to near the minimum supported IP MTU. As a user I would like to use a different message size limit if the MTU is known beforehand or if an application performed a path MTU discovery at runtime.

For the "udp" feature case, the theoretical maximum size of UDP over IPv4 is 65535 bytes, subtracting IPv4 and UDP header sizes from that results in 65507 bytes, which is 1507 bytes more than coap-lite implements in this case. While such large IP packets are possible, they will most surely be fragmented, which is not recommended for CoAP. (For IPv6 with the jumbo payload option, theoretical packet sizes of up to 4 GiB are possible.)

I suggest to make the message size check optional and/or make it configurable with a custom limit, or remove it at all.

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.