#![no_std]
mod message;
use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::{FromPrimitive, ToPrimitive};
use embedded_nal::nb::Result;
const COAP_VERSION: u8 = 1;
const MAX_SIZE: usize = 1152;
#[allow(clippy::upper_case_acronyms)] #[derive(FromPrimitive, ToPrimitive, PartialEq)]
enum Type {
CON = 0,
NON = 1,
ACK = 2,
RST = 3,
}
pub fn poll<ST>(
stack: &mut ST,
socket: &mut ST::UdpSocket,
handler: &mut impl coap_handler::Handler,
) -> Result<(), ST::Error>
where
ST: embedded_nal::UdpFullStack + ?Sized,
{
fn all_responses_are_unexpected(
_: u16,
_: &[u8],
_: &coap_message_utils::inmemory::Message<'_>,
) -> bool {
false
}
poll_with_response_handler(stack, socket, handler, all_responses_are_unexpected)
}
pub fn poll_with_response_handler<ST>(
stack: &mut ST,
socket: &mut ST::UdpSocket,
handler: &mut impl coap_handler::Handler,
response_handler: impl for<'a> FnOnce(
u16,
&'a [u8],
&'a coap_message_utils::inmemory::Message<'a>,
) -> bool,
) -> Result<(), ST::Error>
where
ST: embedded_nal::UdpFullStack + ?Sized,
{
let (extracted, addr, t_in, msgid, token) = {
let mut buf: [u8; MAX_SIZE] = [0; MAX_SIZE];
let (len, addr) = stack.receive(socket, &mut buf)?;
let buf = &mut buf[..len];
if len < 4 {
return Ok(());
}
let ver = buf[0] >> 6;
if ver != COAP_VERSION {
return Ok(());
}
let t_in = Type::from_u8((buf[0] >> 4) & 0x03)
.expect("Success guaranteed by numberof variants and input size");
let tkl = buf[0] & 0x0f;
let tkl: usize = tkl.into();
let code = buf[1];
let msgid = u16::from_be_bytes([buf[2], buf[3]]);
if len < 4 + tkl {
return Ok(());
}
let token = match heapless::Vec::<_, heapless::consts::U8>::from_slice(&buf[4..4 + tkl]) {
Ok(t) => t,
_ => return Ok(()),
};
let tail = &buf[4 + tkl..];
let mut immediate_response = match t_in {
Type::CON => Some(Type::RST),
_ => None,
};
let msg = coap_message_utils::inmemory::Message::new(code, tail);
if matches!(
coap_numbers::code::classify(code),
coap_numbers::code::Range::Response(_) | coap_numbers::code::Range::Empty
) {
let was_expected = response_handler(msgid, &token, &msg);
if was_expected && t_in == Type::CON {
immediate_response = Some(Type::ACK)
}
}
if !matches!(
coap_numbers::code::classify(code),
coap_numbers::code::Range::Request
) {
if let Some(t_response) = immediate_response {
let empty_tkl = 0;
buf[0] = (COAP_VERSION << 6) | (t_response.to_u8().unwrap() << 4) | empty_tkl;
stack.send_to(socket, addr, &buf[..4])?;
}
return Ok(());
}
if t_in == Type::ACK || t_in == Type::RST {
return Ok(());
}
(handler.extract_request_data(&msg), addr, t_in, msgid, token)
};
{
let mut buf: [u8; MAX_SIZE] = [0; MAX_SIZE];
let t_out = if t_in == Type::CON {
Type::ACK
} else {
Type::NON
};
buf[0] = (COAP_VERSION << 6) | ((t_out.to_u8().unwrap()) << 4) | (token.len() as u8);
buf[2..4].copy_from_slice(&msgid.to_be_bytes());
buf[4..4 + token.len()].copy_from_slice(&token);
let (header, tokentail) = buf.split_at_mut(4);
let code = &mut header[1];
let tail = &mut tokentail[token.len()..];
let mut message = message::Message::new(code, tail);
handler.build_response(&mut message, extracted);
let written = 4 + token.len() + message.finish();
stack.send_to(socket, addr, &buf[..written])?;
};
Ok(())
}