From 2e64d442820d7fa54d8cdb0f2fe90dab0317f2d3 Mon Sep 17 00:00:00 2001 From: chrysn Date: Fri, 19 Jan 2024 20:48:17 +0100 Subject: [PATCH 1/7] coap: Implement traits independently of feature flag Those dependencies used to be limited to nightly; they have been stable for some time now. --- Cargo.toml | 14 +++++--------- src/lib.rs | 2 -- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d1ec0417..7b6657d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,14 +35,12 @@ rand_core_06 = { package = "rand_core", version = "^0.6" } # For nimble UUID parsing and some debug implementations hex = { version = "^0.4.3", default-features = false } -coap-numbers = { version = "^0.2.0", optional = true } +coap-numbers = "^0.2.0" embedded-graphics = "0.6" -# Originally disabled by default because they were not published yet; now -# disabled by default we're moving toward opt-in modules anyway. -coap-message = { version = "^0.2.3", optional = true } -coap-handler = { version = "^0.1.4", optional = true } +coap-message = "^0.2.3" +coap-handler = "^0.1.4" embedded-nal = { version = "0.6.0", optional = true } embedded-nal-tcpextensions = { version = "0.1", optional = true } pin-utils = "0.1" @@ -69,10 +67,8 @@ panic_handler_format = [] # only affects that single thread. panic_handler_crash = [] -# If these are present, traits for the respective optional dependencies will be -# implemented. -with_coap_message = ["coap-message" ] -with_coap_handler = ["coap-handler", "coap-numbers", "with_coap_message"] +with_coap_message = [] +with_coap_handler = [] with_embedded_nal = ["embedded-nal", "embedded-nal-tcpextensions"] # See msg::v2 documentation. Enabling this exposes components not under semver diff --git a/src/lib.rs b/src/lib.rs index f922a16b..ddc81bd0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,9 +143,7 @@ pub mod rwlock; #[cfg(feature = "set_panic_handler")] mod panic; -#[cfg(feature = "with_coap_handler")] pub mod coap_handler; -#[cfg(feature = "with_coap_message")] pub mod coap_message; #[cfg(riot_module_sock)] From 177e866063bed9cd9dff884f08ee0587316cb920 Mon Sep 17 00:00:00 2001 From: chrysn Date: Fri, 19 Jan 2024 20:51:17 +0100 Subject: [PATCH 2/7] coap: Move into directory to enable refactoring --- src/{coap_handler.rs => coap_handler/mod.rs} | 0 src/{coap_message.rs => coap_message/mod.rs} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/{coap_handler.rs => coap_handler/mod.rs} (100%) rename src/{coap_message.rs => coap_message/mod.rs} (100%) diff --git a/src/coap_handler.rs b/src/coap_handler/mod.rs similarity index 100% rename from src/coap_handler.rs rename to src/coap_handler/mod.rs diff --git a/src/coap_message.rs b/src/coap_message/mod.rs similarity index 100% rename from src/coap_message.rs rename to src/coap_message/mod.rs From 24bd97da2b84bab121d03e1f0a06df6012a91b52 Mon Sep 17 00:00:00 2001 From: chrysn Date: Fri, 19 Jan 2024 21:02:38 +0100 Subject: [PATCH 3/7] coap: Split impl from helper types --- src/coap_message/impl_0_2.rs | 88 +++++++++++++++++++++++++++++++++ src/coap_message/mod.rs | 94 ++---------------------------------- 2 files changed, 93 insertions(+), 89 deletions(-) create mode 100644 src/coap_message/impl_0_2.rs diff --git a/src/coap_message/impl_0_2.rs b/src/coap_message/impl_0_2.rs new file mode 100644 index 00000000..37fb5025 --- /dev/null +++ b/src/coap_message/impl_0_2.rs @@ -0,0 +1,88 @@ +use coap_message::{ + MessageOption, + MinimalWritableMessage, + MutableWritableMessage, + ReadableMessage, + WithSortedOptions, +}; + +impl<'a> MessageOption for super::MessageOption<'a> { + fn number(&self) -> u16 { + self.number + } + + fn value(&self) -> &[u8] { + self.value + } +} + +impl WithSortedOptions for super::PacketBuffer { + // valid because gcoap just reads options from the message where they are stored in sequence +} + +impl ReadableMessage for super::PacketBuffer { + type Code = u8; + type OptionsIter<'a> = super::OptionsIterator<'a>; + type MessageOption<'a> = super::MessageOption<'a>; + + fn code(&self) -> Self::Code { + self.get_code_raw() + } + + fn payload(&self) -> &[u8] { + self.payload() + } + + fn options(&self) -> Self::OptionsIter<'_> { + super::OptionsIterator(self.opt_iter()) + } +} + +impl<'a> MinimalWritableMessage for super::ResponseMessage<'a> { + type Code = u8; + type OptionNumber = u16; + + fn set_code(&mut self, code: Self::Code) { + self.message.set_code_raw(code); + } + + fn add_option(&mut self, number: Self::OptionNumber, value: &[u8]) { + if self.payload_written.is_some() { + panic!("Options can not be added after payload was added"); + } + self.message + .opt_add_opaque(number.into(), value) + .expect("Options exceed allocated buffer"); + } + + fn set_payload(&mut self, data: &[u8]) { + self.payload_mut_with_len(data.len()).copy_from_slice(data); + self.truncate(data.len()); + } +} + +impl<'a> MutableWritableMessage for super::ResponseMessage<'a> { + fn available_space(&self) -> usize { + self.message.payload().len() + } + + fn payload_mut(&mut self) -> &mut [u8] { + self.payload_written = Some(0); + let payload = self.message.payload_mut(); + payload[0] = 0xff; + &mut payload[1..] + } + + fn truncate(&mut self, len: usize) { + self.payload_written = Some(len); + } + + fn mutate_options(&mut self, mut callback: F) + where + F: FnMut(Self::OptionNumber, &mut [u8]), + { + for (opt_num, slice) in self.message.opt_iter_mut() { + callback(opt_num.into(), slice); + } + } +} diff --git a/src/coap_message/mod.rs b/src/coap_message/mod.rs index 6a97b0ea..dadfdcc0 100644 --- a/src/coap_message/mod.rs +++ b/src/coap_message/mod.rs @@ -1,28 +1,15 @@ -/// This module implements coap_message::ReadableMessage for, and a wrapper that provides -/// coap_message::WritableMessage around RIOT's coap_pkt_t. +//! This module implements [coap_message::ReadableMessage] for, and a wrapper that provides +//! [coap_message::WritableMessage] around RIOT's coap_pkt_t. + +mod impl_0_2; + use crate::gcoap::{PacketBuffer, PacketBufferOptIter}; -use coap_message::{ - MinimalWritableMessage, - MutableWritableMessage, - ReadableMessage, - WithSortedOptions, -}; pub struct MessageOption<'a> { number: u16, value: &'a [u8], } -impl<'a> coap_message::MessageOption for MessageOption<'a> { - fn number(&self) -> u16 { - self.number - } - - fn value(&self) -> &[u8] { - self.value - } -} - pub struct OptionsIterator<'a>(PacketBufferOptIter<'a>); impl<'a> Iterator for OptionsIterator<'a> { type Item = MessageOption<'a>; @@ -36,28 +23,6 @@ impl<'a> Iterator for OptionsIterator<'a> { } } -impl WithSortedOptions for PacketBuffer { - // valid because gcoap just reads options from the message where they are stored in sequence -} - -impl ReadableMessage for PacketBuffer { - type Code = u8; - type OptionsIter<'a> = OptionsIterator<'a>; - type MessageOption<'a> = MessageOption<'a>; - - fn code(&self) -> Self::Code { - self.get_code_raw() - } - - fn payload(&self) -> &[u8] { - self.payload() - } - - fn options(&self) -> Self::OptionsIter<'_> { - OptionsIterator(self.opt_iter()) - } -} - pub struct ResponseMessage<'a> { message: &'a mut PacketBuffer, payload_written: Option, @@ -81,52 +46,3 @@ impl<'a> ResponseMessage<'a> { }) as isize } } - -impl<'a> MinimalWritableMessage for ResponseMessage<'a> { - type Code = u8; - type OptionNumber = u16; - - fn set_code(&mut self, code: Self::Code) { - self.message.set_code_raw(code); - } - - fn add_option(&mut self, number: Self::OptionNumber, value: &[u8]) { - if self.payload_written.is_some() { - panic!("Options can not be added after payload was added"); - } - self.message - .opt_add_opaque(number.into(), value) - .expect("Options exceed allocated buffer"); - } - - fn set_payload(&mut self, data: &[u8]) { - self.payload_mut_with_len(data.len()).copy_from_slice(data); - self.truncate(data.len()); - } -} - -impl<'a> MutableWritableMessage for ResponseMessage<'a> { - fn available_space(&self) -> usize { - self.message.payload().len() - } - - fn payload_mut(&mut self) -> &mut [u8] { - self.payload_written = Some(0); - let payload = self.message.payload_mut(); - payload[0] = 0xff; - &mut payload[1..] - } - - fn truncate(&mut self, len: usize) { - self.payload_written = Some(len); - } - - fn mutate_options(&mut self, mut callback: F) - where - F: FnMut(Self::OptionNumber, &mut [u8]), - { - for (opt_num, slice) in self.message.opt_iter_mut() { - callback(opt_num.into(), slice); - } - } -} From a2bcdcedf00dfa8122995bc3fcdc6a8aa1f929d9 Mon Sep 17 00:00:00 2001 From: chrysn Date: Fri, 19 Jan 2024 21:06:58 +0100 Subject: [PATCH 4/7] coap: Split into version independent and versioned parts --- Cargo.toml | 2 +- src/coap_handler/mod.rs | 116 ++--------------------------------- src/coap_handler/v0_1.rs | 114 ++++++++++++++++++++++++++++++++++ src/coap_message/impl_0_2.rs | 2 +- 4 files changed, 121 insertions(+), 113 deletions(-) create mode 100644 src/coap_handler/v0_1.rs diff --git a/Cargo.toml b/Cargo.toml index 7b6657d2..618d858c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,7 +39,7 @@ coap-numbers = "^0.2.0" embedded-graphics = "0.6" -coap-message = "^0.2.3" +coap-message-0-2 = { package = "coap-message", version = "^0.2.3" } coap-handler = "^0.1.4" embedded-nal = { version = "0.6.0", optional = true } embedded-nal-tcpextensions = { version = "0.1", optional = true } diff --git a/src/coap_handler/mod.rs b/src/coap_handler/mod.rs index 3123d5ae..3bdfe837 100644 --- a/src/coap_handler/mod.rs +++ b/src/coap_handler/mod.rs @@ -1,113 +1,7 @@ -/// This module provides a wrapper around a coap_handler::Handler that can be registered at a RIOT -/// GcoapHandler. -use core::convert::TryInto; +//! This module provides a wrappers around a coap_handler::Handler in different versions, all of +//! which can be registered at a RIOT GcoapHandler. -use coap_message::{MutableWritableMessage, ReadableMessage}; +pub mod v0_1; -use crate::coap_message::ResponseMessage; -use crate::gcoap::PacketBuffer; - -/// Adapter to get a [crate::gcoap::Handler] from a more generic [coap_handler::Handler], typically -/// to register it through a [crate::gcoap::SingleHandlerListener]. -pub struct GcoapHandler(pub H) -where - H: coap_handler::Handler; - -impl crate::gcoap::Handler for GcoapHandler -where - H: coap_handler::Handler, -{ - fn handle(&mut self, pkt: &mut PacketBuffer) -> isize { - let request_data = self.0.extract_request_data(pkt); - let mut lengthwrapped = ResponseMessage::new(pkt); - self.0.build_response(&mut lengthwrapped, request_data); - lengthwrapped.finish() - } -} - -impl crate::gcoap::WithLinkEncoder for GcoapHandler -where - H: coap_handler::Handler + coap_handler::Reporting, -{ - fn encode(&self, writer: &mut crate::gcoap::LinkEncoder) { - use coap_handler::Record; - for record in self.0.report() { - writer.write_comma_maybe(); - writer.write(b"<"); - for pathelement in record.path() { - writer.write(b"/"); - writer.write(pathelement.as_ref().as_bytes()); - } - writer.write(b">"); - if let Some(rel) = record.rel() { - // Not trying to be smart about whether or not we need the quotes - writer.write(b";rel=\""); - writer.write(rel.as_bytes()); - writer.write(b"\""); - } - for attr in record.attributes() { - use coap_handler::Attribute::*; - match attr { - Observable => writer.write(b";obs"), - Interface(i) => { - writer.write(b";if=\""); - writer.write(i.as_bytes()); - writer.write(b"\""); - } - ResourceType(r) => { - writer.write(b";rt=\""); - writer.write(r.as_bytes()); - writer.write(b"\""); - } - // FIXME: deduplicate with what's somewhere in coap-handler-implementations; - // implement remaining items - _ => (), - } - } - } - } -} - -/// Blanket implementation for mutex wrapped resources -/// -/// This is useful in combination with the defauilt implementation for Option as well. -impl<'b, H> coap_handler::Handler for &'b crate::mutex::Mutex -where - H: coap_handler::Handler, -{ - type RequestData = Option; - - fn extract_request_data<'a>(&mut self, request: &'a impl ReadableMessage) -> Self::RequestData { - self.try_lock().map(|mut h| h.extract_request_data(request)) - } - - fn estimate_length(&mut self, request: &Self::RequestData) -> usize { - if let Some(r) = request { - if let Some(mut s) = self.try_lock() { - return s.estimate_length(r); - } - } - - 1 - } - - fn build_response( - &mut self, - response: &mut impl MutableWritableMessage, - request: Self::RequestData, - ) { - if let Some(r) = request { - if let Some(mut s) = self.try_lock() { - return s.build_response(response, r); - } - } - - response.set_code( - coap_numbers::code::SERVICE_UNAVAILABLE - .try_into() - .map_err(|_| "Message type can't even exprss Service Unavailable") - .unwrap(), - ); - response.set_payload(b""); - } -} +#[deprecated(note = "Use through the v0_1 module.")] +pub use v0_1::*; diff --git a/src/coap_handler/v0_1.rs b/src/coap_handler/v0_1.rs new file mode 100644 index 00000000..db517aa0 --- /dev/null +++ b/src/coap_handler/v0_1.rs @@ -0,0 +1,114 @@ +//! This module provides a wrapper around a coap_handler::Handler that can be registered at a RIOT +//! GcoapHandler. + +use core::convert::TryInto; + +use coap_message_0_2::{MutableWritableMessage, ReadableMessage}; + +use crate::coap_message::ResponseMessage; +use crate::gcoap::PacketBuffer; + +/// Adapter to get a [crate::gcoap::Handler] from a more generic [coap_handler::Handler], typically +/// to register it through a [crate::gcoap::SingleHandlerListener]. +pub struct GcoapHandler(pub H) +where + H: coap_handler::Handler; + +impl crate::gcoap::Handler for GcoapHandler +where + H: coap_handler::Handler, +{ + fn handle(&mut self, pkt: &mut PacketBuffer) -> isize { + let request_data = self.0.extract_request_data(pkt); + let mut lengthwrapped = ResponseMessage::new(pkt); + self.0.build_response(&mut lengthwrapped, request_data); + lengthwrapped.finish() + } +} + +impl crate::gcoap::WithLinkEncoder for GcoapHandler +where + H: coap_handler::Handler + coap_handler::Reporting, +{ + fn encode(&self, writer: &mut crate::gcoap::LinkEncoder) { + use coap_handler::Record; + for record in self.0.report() { + writer.write_comma_maybe(); + writer.write(b"<"); + for pathelement in record.path() { + writer.write(b"/"); + writer.write(pathelement.as_ref().as_bytes()); + } + writer.write(b">"); + if let Some(rel) = record.rel() { + // Not trying to be smart about whether or not we need the quotes + writer.write(b";rel=\""); + writer.write(rel.as_bytes()); + writer.write(b"\""); + } + for attr in record.attributes() { + use coap_handler::Attribute::*; + match attr { + Observable => writer.write(b";obs"), + Interface(i) => { + writer.write(b";if=\""); + writer.write(i.as_bytes()); + writer.write(b"\""); + } + ResourceType(r) => { + writer.write(b";rt=\""); + writer.write(r.as_bytes()); + writer.write(b"\""); + } + // FIXME: deduplicate with what's somewhere in coap-handler-implementations; + // implement remaining items + _ => (), + } + } + } + } +} + +/// Blanket implementation for mutex wrapped resources +/// +/// This is useful in combination with the defauilt implementation for Option as well. +impl<'b, H> coap_handler::Handler for &'b crate::mutex::Mutex +where + H: coap_handler::Handler, +{ + type RequestData = Option; + + fn extract_request_data<'a>(&mut self, request: &'a impl ReadableMessage) -> Self::RequestData { + self.try_lock().map(|mut h| h.extract_request_data(request)) + } + + fn estimate_length(&mut self, request: &Self::RequestData) -> usize { + if let Some(r) = request { + if let Some(mut s) = self.try_lock() { + return s.estimate_length(r); + } + } + + 1 + } + + fn build_response( + &mut self, + response: &mut impl MutableWritableMessage, + request: Self::RequestData, + ) { + if let Some(r) = request { + if let Some(mut s) = self.try_lock() { + return s.build_response(response, r); + } + } + + response.set_code( + coap_numbers::code::SERVICE_UNAVAILABLE + .try_into() + .map_err(|_| "Message type can't even exprss Service Unavailable") + .unwrap(), + ); + response.set_payload(b""); + } +} diff --git a/src/coap_message/impl_0_2.rs b/src/coap_message/impl_0_2.rs index 37fb5025..af0fc110 100644 --- a/src/coap_message/impl_0_2.rs +++ b/src/coap_message/impl_0_2.rs @@ -1,4 +1,4 @@ -use coap_message::{ +use coap_message_0_2::{ MessageOption, MinimalWritableMessage, MutableWritableMessage, From 4aa1b273443a2bf92e33bfbca8d5d3d2ff4ab804 Mon Sep 17 00:00:00 2001 From: chrysn Date: Sat, 20 Jan 2024 00:18:04 +0100 Subject: [PATCH 5/7] coap: Implement coap-message 0.3 and -handler 0.2 --- Cargo.toml | 4 +- src/coap_handler/mod.rs | 1 + src/coap_handler/v0_1.rs | 15 ++-- src/coap_handler/v0_2.rs | 146 +++++++++++++++++++++++++++++++++++ src/coap_message/impl_0_3.rs | 132 +++++++++++++++++++++++++++++++ src/coap_message/mod.rs | 7 ++ 6 files changed, 297 insertions(+), 8 deletions(-) create mode 100644 src/coap_handler/v0_2.rs create mode 100644 src/coap_message/impl_0_3.rs diff --git a/Cargo.toml b/Cargo.toml index 618d858c..8c2c5bc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,9 @@ coap-numbers = "^0.2.0" embedded-graphics = "0.6" coap-message-0-2 = { package = "coap-message", version = "^0.2.3" } -coap-handler = "^0.1.4" +coap-message-0-3 = { package = "coap-message", version = "^0.3.0" } +coap-handler-0-1 = { package = "coap-handler", version = "^0.1.4" } +coap-handler-0-2 = { package = "coap-handler", version = "^0.2.0" } embedded-nal = { version = "0.6.0", optional = true } embedded-nal-tcpextensions = { version = "0.1", optional = true } pin-utils = "0.1" diff --git a/src/coap_handler/mod.rs b/src/coap_handler/mod.rs index 3bdfe837..65d33a7f 100644 --- a/src/coap_handler/mod.rs +++ b/src/coap_handler/mod.rs @@ -2,6 +2,7 @@ //! which can be registered at a RIOT GcoapHandler. pub mod v0_1; +pub mod v0_2; #[deprecated(note = "Use through the v0_1 module.")] pub use v0_1::*; diff --git a/src/coap_handler/v0_1.rs b/src/coap_handler/v0_1.rs index db517aa0..e64bfbbe 100644 --- a/src/coap_handler/v0_1.rs +++ b/src/coap_handler/v0_1.rs @@ -3,6 +3,8 @@ use core::convert::TryInto; +use coap_handler_0_1::{Attribute, Handler, Record, Reporting}; + use coap_message_0_2::{MutableWritableMessage, ReadableMessage}; use crate::coap_message::ResponseMessage; @@ -12,11 +14,11 @@ use crate::gcoap::PacketBuffer; /// to register it through a [crate::gcoap::SingleHandlerListener]. pub struct GcoapHandler(pub H) where - H: coap_handler::Handler; + H: Handler; impl crate::gcoap::Handler for GcoapHandler where - H: coap_handler::Handler, + H: Handler, { fn handle(&mut self, pkt: &mut PacketBuffer) -> isize { let request_data = self.0.extract_request_data(pkt); @@ -28,10 +30,9 @@ where impl crate::gcoap::WithLinkEncoder for GcoapHandler where - H: coap_handler::Handler + coap_handler::Reporting, + H: Handler + Reporting, { fn encode(&self, writer: &mut crate::gcoap::LinkEncoder) { - use coap_handler::Record; for record in self.0.report() { writer.write_comma_maybe(); writer.write(b"<"); @@ -47,7 +48,7 @@ where writer.write(b"\""); } for attr in record.attributes() { - use coap_handler::Attribute::*; + use Attribute::*; match attr { Observable => writer.write(b";obs"), Interface(i) => { @@ -72,9 +73,9 @@ where /// Blanket implementation for mutex wrapped resources /// /// This is useful in combination with the defauilt implementation for Option as well. -impl<'b, H> coap_handler::Handler for &'b crate::mutex::Mutex +impl<'b, H> Handler for &'b crate::mutex::Mutex where - H: coap_handler::Handler, + H: Handler, { type RequestData = Option; diff --git a/src/coap_handler/v0_2.rs b/src/coap_handler/v0_2.rs new file mode 100644 index 00000000..d1fcb59c --- /dev/null +++ b/src/coap_handler/v0_2.rs @@ -0,0 +1,146 @@ +//! This module provides a wrapper around a coap_handler::Handler that can be registered at a RIOT +//! GcoapHandler. + +use core::convert::TryInto; + +use coap_handler_0_2::{Attribute, Handler, Record, Reporting}; + +use coap_message_0_3::{ + error::RenderableOnMinimal, + MinimalWritableMessage, + MutableWritableMessage, + ReadableMessage, +}; + +use crate::coap_message::ResponseMessage; +use crate::gcoap::PacketBuffer; + +/// Adapter to get a [crate::gcoap::Handler] from a more generic [coap_handler::Handler], typically +/// to register it through a [crate::gcoap::SingleHandlerListener]. +pub struct GcoapHandler(pub H) +where + H: Handler; + +impl crate::gcoap::Handler for GcoapHandler +where + H: Handler, +{ + fn handle(&mut self, pkt: &mut PacketBuffer) -> isize { + let request_data = self.0.extract_request_data(pkt); + let mut lengthwrapped = ResponseMessage::new(pkt); + match request_data { + Ok(r) => { + if let Err(e) = self.0.build_response(&mut lengthwrapped, r) { + lengthwrapped.rewind(); + if let Err(_e2) = e.render(&mut lengthwrapped) { + // Render double fault + lengthwrapped.rewind(); + lengthwrapped.set_code(coap_numbers::code::INTERNAL_SERVER_ERROR); + } + } + } + Err(e) => { + if let Err(_e2) = e.render(&mut lengthwrapped) { + // Render double fault + lengthwrapped.rewind(); + lengthwrapped.set_code(coap_numbers::code::INTERNAL_SERVER_ERROR); + } + } + } + lengthwrapped.finish() + } +} + +impl crate::gcoap::WithLinkEncoder for GcoapHandler +where + H: Handler + Reporting, +{ + fn encode(&self, writer: &mut crate::gcoap::LinkEncoder) { + for record in self.0.report() { + writer.write_comma_maybe(); + writer.write(b"<"); + for pathelement in record.path() { + writer.write(b"/"); + writer.write(pathelement.as_ref().as_bytes()); + } + writer.write(b">"); + if let Some(rel) = record.rel() { + // Not trying to be smart about whether or not we need the quotes + writer.write(b";rel=\""); + writer.write(rel.as_bytes()); + writer.write(b"\""); + } + for attr in record.attributes() { + use Attribute::*; + match attr { + Observable => writer.write(b";obs"), + Interface(i) => { + writer.write(b";if=\""); + writer.write(i.as_bytes()); + writer.write(b"\""); + } + ResourceType(r) => { + writer.write(b";rt=\""); + writer.write(r.as_bytes()); + writer.write(b"\""); + } + // FIXME: deduplicate with what's somewhere in coap-handler-implementations; + // implement remaining items + _ => (), + } + } + } + } +} + +/// Blanket implementation for mutex wrapped resources +/// +/// This is useful in combination with the defauilt implementation for Option as well. +impl<'b, H> Handler for &'b crate::mutex::Mutex +where + H: Handler, +{ + type RequestData = Option; + + type ExtractRequestError = H::ExtractRequestError; + type BuildResponseError = H::BuildResponseError; + + fn extract_request_data( + &mut self, + request: &M, + ) -> Result { + self.try_lock() + .map(|mut h| h.extract_request_data(request)) + .transpose() + } + + fn estimate_length(&mut self, request: &Self::RequestData) -> usize { + if let Some(r) = request { + if let Some(mut s) = self.try_lock() { + return s.estimate_length(r); + } + } + + 1 + } + + fn build_response( + &mut self, + response: &mut M, + request: Self::RequestData, + ) -> Result<(), Self::BuildResponseError> { + if let Some(r) = request { + if let Some(mut s) = self.try_lock() { + return s.build_response(response, r); + } + } + + response.set_code( + coap_numbers::code::SERVICE_UNAVAILABLE + .try_into() + .map_err(|_| "Message type can't even exprss Service Unavailable") + .unwrap(), + ); + Ok(()) + } +} diff --git a/src/coap_message/impl_0_3.rs b/src/coap_message/impl_0_3.rs new file mode 100644 index 00000000..7b0c1730 --- /dev/null +++ b/src/coap_message/impl_0_3.rs @@ -0,0 +1,132 @@ +use coap_message_0_3::{ + error::RenderableOnMinimal, + Code, + MessageOption, + MinimalWritableMessage, + MutableWritableMessage, + ReadableMessage, + WithSortedOptions, +}; + +use crate::error::NumericError; + +/// Thin wrapper around NumericError that can render and satisfies all conversion requirements +#[derive(Debug)] +pub struct Error(NumericError); + +impl From for Error { + fn from(e: NumericError) -> Error { + Error(e) + } +} + +impl From for Error { + fn from(e: core::convert::Infallible) -> Error { + match e {} + } +} + +impl RenderableOnMinimal for Error { + type Error = IE; + fn render(self, msg: &mut M) -> Result<(), M::UnionError> { + msg.set_code(Code::new(coap_numbers::code::INTERNAL_SERVER_ERROR)?); + Ok(()) + } +} + +impl<'a> MessageOption for super::MessageOption<'a> { + fn number(&self) -> u16 { + self.number + } + + fn value(&self) -> &[u8] { + self.value + } +} + +impl WithSortedOptions for super::PacketBuffer { + // valid because gcoap just reads options from the message where they are stored in sequence +} + +impl ReadableMessage for super::PacketBuffer { + type Code = u8; + type OptionsIter<'a> = super::OptionsIterator<'a>; + type MessageOption<'a> = super::MessageOption<'a>; + + fn code(&self) -> Self::Code { + self.get_code_raw() + } + + fn payload(&self) -> &[u8] { + self.payload() + } + + fn options(&self) -> Self::OptionsIter<'_> { + super::OptionsIterator(self.opt_iter()) + } +} + +impl<'a> MinimalWritableMessage for super::ResponseMessage<'a> { + type Code = u8; + type OptionNumber = u16; + + type AddOptionError = Error; + type SetPayloadError = Error; + type UnionError = Error; + + fn set_code(&mut self, code: Self::Code) { + self.message.set_code_raw(code); + } + + fn add_option(&mut self, number: Self::OptionNumber, value: &[u8]) -> Result<(), Error> { + if self.payload_written.is_some() { + return Err(NumericError::from_constant(riot_sys::EINVAL as _).into()); + } + self.message.opt_add_opaque(number.into(), value)?; + Ok(()) + } + + fn set_payload(&mut self, data: &[u8]) -> Result<(), Error> { + self.payload_mut_with_len(data.len())?.copy_from_slice(data); + Ok(()) + } +} + +impl<'a> MutableWritableMessage for super::ResponseMessage<'a> { + fn available_space(&self) -> usize { + self.message.payload().len() + } + + fn payload_mut_with_len(&mut self, len: usize) -> Result<&mut [u8], Error> { + self.payload_written = Some(len); + let payload = self.message.payload_mut(); + if let Some((pm, pl)) = payload.get_mut(..len + 1).and_then(<[u8]>::split_first_mut) { + *pm = 0xff; + Ok(pl) + } else { + Err(NumericError::from_constant(riot_sys::EINVAL as _).into()) + } + } + + fn truncate(&mut self, len: usize) -> Result<(), Error> { + if self.payload_written.is_none() { + // payload() will not even return anything sensible yet + return Err(NumericError::from_constant(riot_sys::EINVAL as _).into()); + } + let pl_len = self.message.payload().len() - 1; + if len > pl_len { + return Err(NumericError::from_constant(riot_sys::EINVAL as _).into()); + } + self.payload_written = Some(len); + Ok(()) + } + + fn mutate_options(&mut self, mut callback: F) + where + F: FnMut(Self::OptionNumber, &mut [u8]), + { + for (opt_num, slice) in self.message.opt_iter_mut() { + callback(opt_num.into(), slice); + } + } +} diff --git a/src/coap_message/mod.rs b/src/coap_message/mod.rs index dadfdcc0..5140e54a 100644 --- a/src/coap_message/mod.rs +++ b/src/coap_message/mod.rs @@ -2,6 +2,7 @@ //! [coap_message::WritableMessage] around RIOT's coap_pkt_t. mod impl_0_2; +mod impl_0_3; use crate::gcoap::{PacketBuffer, PacketBufferOptIter}; @@ -24,6 +25,8 @@ impl<'a> Iterator for OptionsIterator<'a> { } pub struct ResponseMessage<'a> { + /// Note that this is a slightly weird version of PacketBuffer, where opt_finish is never + /// called, and .payload() perpetually reports the payload marker as part of the payload. message: &'a mut PacketBuffer, payload_written: Option, } @@ -39,6 +42,10 @@ impl<'a> ResponseMessage<'a> { } } + pub(crate) fn rewind(&mut self) { + self.message.resp_init(5 << 5).unwrap(); + } + pub fn finish(&self) -> isize { self.message.get_length(match self.payload_written { None => 0, From 9a56c59364241233e02825ca8683e5a9a236d4ae Mon Sep 17 00:00:00 2001 From: chrysn Date: Sat, 20 Jan 2024 13:03:30 +0100 Subject: [PATCH 6/7] coap: Gate by presence of relevant modules --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index ddc81bd0..d93ae117 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -143,7 +143,9 @@ pub mod rwlock; #[cfg(feature = "set_panic_handler")] mod panic; +#[cfg(riot_module_gcoap)] pub mod coap_handler; +#[cfg(riot_module_gcoap)] pub mod coap_message; #[cfg(riot_module_sock)] From 26a6fe281a93ddc53128286193171819b8020a8b Mon Sep 17 00:00:00 2001 From: chrysn Date: Sat, 20 Jan 2024 13:05:14 +0100 Subject: [PATCH 7/7] doc: Enable doc_auto_cfg --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d93ae117..97e5826c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ )] // Primarily for documentation, see feature docs #![cfg_attr(feature = "actual_never_type", feature(never_type))] -#![cfg_attr(feature = "nightly_docs", feature(fundamental))] +#![cfg_attr(feature = "nightly_docs", feature(fundamental, doc_auto_cfg))] /// riot-sys is re-exported here as it is necessary in some places when using it to get values (eg. /// in [error::NumericError::from_constant]). It is also used in macros such as [static_command!].