#![no_std]
#![cfg_attr(
feature = "nontrivial_option_processing",
feature(type_alias_impl_trait)
)]
use coap_message::{MessageOption, MutableWritableMessage, ReadableMessage};
use core::marker::PhantomData;
pub mod helpers;
pub mod option_processing;
pub mod wkc;
mod wkc_implementation;
mod forking;
mod forking_helpers;
pub use forking::{
new_dispatcher, ForkingHandler, ForkingRequestData, ForkingTreeHandler, HandlerBuilder,
ReportingHandlerBuilder,
};
use crate::helpers::{block2_write_with_cf, codeconvert, Block2RequestData};
use coap_handler::Handler;
use coap_numbers::{code, option};
type Code = u8;
pub struct NeverFound {}
impl Handler for NeverFound {
type RequestData = ();
fn extract_request_data(&mut self, _request: &impl ReadableMessage) -> Self::RequestData {
()
}
fn estimate_length(&mut self, _request: &Self::RequestData) -> usize {
0
}
fn build_response(
&mut self,
response: &mut impl MutableWritableMessage,
_request: Self::RequestData,
) {
response.set_code(codeconvert(code::NOT_FOUND));
response.set_payload(b"");
}
}
pub struct SimpleRenderableData(Result<Block2RequestData, Code>);
pub trait SimpleRenderable {
fn render<W>(&mut self, writer: &mut W)
where
W: core::fmt::Write;
fn render_bytes(&mut self, writer: &mut helpers::WindowedInfinityWithETag) {
self.render(writer)
}
fn content_format(&self) -> Option<u16> {
None
}
}
#[derive(Debug, Copy, Clone)]
pub struct SimpleRendered<T: SimpleRenderable>(pub T);
impl<'a> SimpleRendered<TypedStaticRenderable<'a>> {
pub fn new_typed_slice(data: &'a [u8], content_format: Option<u16>) -> Self {
SimpleRendered(TypedStaticRenderable {
data,
content_format,
})
}
pub fn new_typed_str(data: &'a str, content_format: Option<u16>) -> Self {
let data = data.as_bytes();
Self::new_typed_slice(data, content_format)
}
}
impl<T> Handler for SimpleRendered<T>
where
T: SimpleRenderable,
{
type RequestData = SimpleRenderableData;
fn extract_request_data(&mut self, request: &impl ReadableMessage) -> Self::RequestData {
let expected_accept = self.0.content_format();
let mut block2 = Ok(None);
for o in request.options() {
match o.number() {
coap_numbers::option::ACCEPT => {
if expected_accept.is_some() && o.value_uint() != expected_accept {
return SimpleRenderableData(Err(coap_numbers::code::NOT_ACCEPTABLE));
}
}
coap_numbers::option::BLOCK2 => {
block2 = match block2 {
Err(e) => Err(e),
Ok(Some(_)) => Err(coap_numbers::code::BAD_REQUEST),
Ok(None) => Block2RequestData::from_option(&o)
.map(Some)
.map_err(|_| code::BAD_REQUEST),
}
}
o if option::get_criticality(o) == option::Criticality::Critical => {
return SimpleRenderableData(Err(code::BAD_OPTION));
}
_ => (),
}
}
let reqdata = match request.code().into() {
code::GET => block2.map(|o| o.unwrap_or_default()),
_ => Err(code::METHOD_NOT_ALLOWED),
};
SimpleRenderableData(reqdata)
}
fn estimate_length(&mut self, _request: &Self::RequestData) -> usize {
1280 - 40 - 4 }
fn build_response(
&mut self,
response: &mut impl MutableWritableMessage,
request: Self::RequestData,
) {
let block2data = match request.0 {
Ok(x) => x,
Err(c) => {
response.set_code(codeconvert(c));
response.set_payload(b"");
return;
}
};
let cf = self.0.content_format();
response.set_code(codeconvert(code::CONTENT));
block2_write_with_cf(block2data, response, |w| self.0.render_bytes(w), cf);
}
}
impl<T> wkc::Reporting for SimpleRendered<T>
where
T: SimpleRenderable,
{
type Record<'a> = wkc::EmptyRecord
where
Self: 'a,
;
type Reporter<'a> = core::iter::Once<wkc::EmptyRecord>
where
Self: 'a,
;
fn report(&self) -> Self::Reporter<'_> {
core::iter::once(wkc::EmptyRecord {})
}
}
impl<'a> SimpleRenderable for &'a str {
fn render<W>(&mut self, writer: &mut W)
where
W: core::fmt::Write,
{
writer
.write_str(self)
.expect("The backend of SimpleRenderable supports infallible writing");
}
fn content_format(&self) -> Option<u16> {
Some(0)
}
}
pub struct TypedStaticRenderable<'a> {
data: &'a [u8],
content_format: Option<u16>,
}
impl<'a> SimpleRenderable for TypedStaticRenderable<'a> {
fn render<W>(&mut self, _: &mut W)
where
W: core::fmt::Write,
{
unimplemented!("render_bytes is implemented instead")
}
fn render_bytes(&mut self, writer: &mut helpers::WindowedInfinityWithETag) {
writer.write(self.data)
}
fn content_format(&self) -> Option<u16> {
self.content_format
}
}
use serde::Serialize;
pub trait SimpleCBORHandler {
type Get;
type Post;
type Put;
fn get(&mut self) -> Result<Self::Get, Code> {
Err(code::METHOD_NOT_ALLOWED)
}
fn post(&mut self, _representation: &Self::Post) -> Code {
code::METHOD_NOT_ALLOWED
}
fn put(&mut self, _representation: &Self::Put) -> Code {
code::METHOD_NOT_ALLOWED
}
}
mod sealed {
pub trait SimpleSerialization {
const CF: Option<u16>;
}
pub struct SerdeCBORSerialization;
pub struct MiniCBORSerialization;
}
use sealed::*;
impl SimpleSerialization for SerdeCBORSerialization {
const CF: Option<u16> = Some(60);
}
impl SimpleSerialization for MiniCBORSerialization {
const CF: Option<u16> = Some(60);
}
pub struct SimpleWrapper<H, S: SimpleSerialization = SerdeCBORSerialization>
where
H: SimpleCBORHandler,
{
handler: H,
_phantom: PhantomData<S>,
}
#[deprecated(note = "Renamed to SimpleWrapper")]
pub type SimpleCBORWrapper<H> = SimpleWrapper<H, SerdeCBORSerialization>;
impl<H, S> SimpleWrapper<H, S>
where
H: SimpleCBORHandler,
S: SimpleSerialization,
{
fn check_get_options(request: &impl ReadableMessage) -> Result<Block2RequestData, Code> {
let mut block2 = Ok(None);
for o in request.options() {
match o.number() {
option::ACCEPT => {
if let Some(cf) = S::CF {
if o.value_uint() != Some(cf) {
return Err(code::UNSUPPORTED_CONTENT_FORMAT);
}
}
}
option::BLOCK2 => {
block2 = match block2 {
Err(e) => Err(e),
Ok(Some(_)) => Err(coap_numbers::code::BAD_REQUEST),
Ok(None) => Block2RequestData::from_option(&o)
.map(Some)
.map_err(|_| code::BAD_REQUEST),
}
}
o if option::get_criticality(o) == option::Criticality::Critical => {
return Err(code::BAD_OPTION);
}
_ => {}
}
}
Ok(block2?.unwrap_or_default())
}
fn check_postput_options(request: &impl ReadableMessage) -> Result<(), Code> {
for o in request.options() {
match o.number() {
option::CONTENT_FORMAT => {
if let Some(cf) = S::CF {
if o.value_uint() != Some(cf) {
return Err(code::NOT_ACCEPTABLE);
}
} else {
return Err(code::NOT_ACCEPTABLE);
}
}
o if option::get_criticality(o) == option::Criticality::Critical => {
return Err(code::BAD_OPTION);
}
_ => {}
}
}
Ok(())
}
}
impl<H> SimpleWrapper<H, SerdeCBORSerialization>
where
H: SimpleCBORHandler,
H::Get: for<'de> serde::Serialize,
H::Post: for<'de> serde::Deserialize<'de>,
H::Put: for<'de> serde::Deserialize<'de>,
{
pub fn new(handler: H) -> Self {
SimpleWrapper {
handler,
_phantom: PhantomData,
}
}
}
#[cfg(feature = "minicbor")]
impl<H> SimpleWrapper<H, MiniCBORSerialization>
where
H: SimpleCBORHandler,
H::Get: for<'de> minicbor::Encode,
H::Post: for<'de> minicbor::Decode<'de>,
H::Put: for<'de> minicbor::Decode<'de>,
{
pub fn new_minicbor(handler: H) -> Self {
SimpleWrapper {
handler,
_phantom: PhantomData,
}
}
}
pub enum SimpleRequestData {
Get(Block2RequestData), Done(Code), }
#[deprecated(note = "Renamed to SimpleRequestData")]
pub type SimpleCBORRequestData = SimpleRequestData;
use self::SimpleRequestData::{Done, Get};
impl<H> Handler for SimpleWrapper<H, SerdeCBORSerialization>
where
H: SimpleCBORHandler,
H::Get: for<'de> serde::Serialize,
H::Post: for<'de> serde::Deserialize<'de>,
H::Put: for<'de> serde::Deserialize<'de>,
{
type RequestData = SimpleRequestData;
fn extract_request_data(&mut self, request: &impl ReadableMessage) -> Self::RequestData {
match request.code().into() {
code::GET => match Self::check_get_options(request) {
Err(e) => Done(e),
Ok(block) => Get(block),
},
code::POST => {
if let Err(e) = Self::check_postput_options(request) {
return Done(e);
}
let parsed: Result<H::Post, _> =
serde_cbor::de::from_slice_with_scratch(request.payload(), &mut []);
match parsed {
Ok(p) => Done(self.handler.post(&p)),
Err(_) => Done(code::BAD_REQUEST),
}
}
code::PUT => {
if let Err(e) = Self::check_postput_options(request) {
return Done(e);
}
let parsed: Result<H::Put, _> =
serde_cbor::de::from_slice_with_scratch(request.payload(), &mut []);
match parsed {
Ok(p) => Done(self.handler.put(&p)),
Err(_) => Done(code::BAD_REQUEST),
}
}
_ => Done(code::METHOD_NOT_ALLOWED),
}
}
fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
match request {
Done(_) => 4,
Get(block) => (block.size() + 25).into(), }
}
fn build_response(
&mut self,
response: &mut impl MutableWritableMessage,
request: Self::RequestData,
) {
match request {
Done(r) => {
response.set_code(codeconvert(r));
response.set_payload(b"");
}
Get(block2) => {
let repr = self.handler.get();
match repr {
Err(e) => {
response.set_code(codeconvert(e));
response.set_payload(b"");
}
Ok(repr) => {
response.set_code(codeconvert(code::CONTENT));
match block2_write_with_cf(
block2,
response,
|win| repr.serialize(&mut serde_cbor::ser::Serializer::new(win)),
SerdeCBORSerialization::CF,
) {
Ok(()) => (),
Err(_) => {
response.set_code(codeconvert(code::INTERNAL_SERVER_ERROR));
}
}
}
}
}
}
}
}
#[cfg(feature = "minicbor")]
impl<H> Handler for SimpleWrapper<H, MiniCBORSerialization>
where
H: SimpleCBORHandler,
H::Get: for<'de> minicbor::Encode,
H::Post: for<'de> minicbor::Decode<'de>,
H::Put: for<'de> minicbor::Decode<'de>,
{
type RequestData = SimpleRequestData;
fn extract_request_data(&mut self, request: &impl ReadableMessage) -> Self::RequestData {
match request.code().into() {
code::GET => match Self::check_get_options(request) {
Err(e) => Done(e),
Ok(block) => Get(block),
},
code::POST => {
if let Err(e) = Self::check_postput_options(request) {
return Done(e);
}
let parsed: Result<H::Post, _> = minicbor::decode(request.payload());
match parsed {
Ok(p) => Done(self.handler.post(&p)),
Err(_) => Done(code::BAD_REQUEST),
}
}
code::PUT => {
if let Err(e) = Self::check_postput_options(request) {
return Done(e);
}
let parsed: Result<H::Put, _> = minicbor::decode(request.payload());
match parsed {
Ok(p) => Done(self.handler.put(&p)),
Err(_) => Done(code::BAD_REQUEST),
}
}
_ => Done(code::METHOD_NOT_ALLOWED),
}
}
fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
match request {
Done(_) => 4,
Get(block) => (block.size() + 25).into(), }
}
fn build_response(
&mut self,
response: &mut impl MutableWritableMessage,
request: Self::RequestData,
) {
match request {
Done(r) => {
response.set_code(codeconvert(r));
response.set_payload(b"");
}
Get(block2) => {
let repr = self.handler.get();
match repr {
Err(e) => {
response.set_code(codeconvert(e));
response.set_payload(b"");
}
Ok(repr) => {
response.set_code(codeconvert(code::CONTENT));
match block2_write_with_cf(
block2,
response,
|win| minicbor::encode(&repr, win),
MiniCBORSerialization::CF,
) {
Ok(()) => (),
Err(_) => {
response.set_code(codeconvert(code::INTERNAL_SERVER_ERROR));
}
}
}
}
}
}
}
}