#![no_std]
#![feature(generic_associated_types)]
#![feature(iter_order_by)]
use core::convert::TryInto;
use coap_message::{MessageOption, MutableWritableMessage, ReadableMessage};
pub mod helpers;
use crate::helpers::{
block2_write_with_cf, codeconvert, Block2RequestData, MaskingUriUpToPath,
};
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 fn new_dispatcher() -> NeverFound {
NeverFound {}
}
pub trait HandlerBuilder<'a, OldRD>
where
Self: Handler + Sized,
{
fn at<H>(self, path: &'a [&'a str], handler: H) -> ForkingHandler<'a, H, Self>
where
H: Handler + Sized;
}
pub struct ForkingHandler<'a, H1, H2> {
h1: H1,
h2: H2,
h1_condition: &'a [&'a str],
}
pub enum ForkingRequestData<RD1, RD2> {
First(RD1),
Second(RD2),
}
impl<'a, RD1, H1, RD2, H2> Handler for ForkingHandler<'a, H1, H2>
where
H1: Handler<RequestData = RD1>,
H2: Handler<RequestData = RD2>,
{
type RequestData = ForkingRequestData<RD1, RD2>;
fn extract_request_data(&mut self, request: &impl ReadableMessage) -> Self::RequestData {
let expected_path = self.h1_condition.iter().map(|s| s.as_bytes());
let actual_path = request.options().filter(|o| o.number() == option::URI_PATH);
if expected_path.cmp_by(actual_path, |e, a| e.cmp(a.value())) == core::cmp::Ordering::Equal
{
let masked = MaskingUriUpToPath(request);
ForkingRequestData::First(self.h1.extract_request_data(&masked))
} else {
ForkingRequestData::Second(self.h2.extract_request_data(request))
}
}
fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
match request {
ForkingRequestData::First(r) => self.h1.estimate_length(r),
ForkingRequestData::Second(r) => self.h2.estimate_length(r),
}
}
fn build_response(
&mut self,
response: &mut impl MutableWritableMessage,
request: Self::RequestData,
) {
match request {
ForkingRequestData::First(r) => self.h1.build_response(response, r),
ForkingRequestData::Second(r) => self.h2.build_response(response, r),
}
}
}
impl<'a, OldRD, OldH> HandlerBuilder<'a, OldRD> for OldH
where
Self: Handler<RequestData = OldRD> + Sized,
{
fn at<H>(self, path: &'a [&'a str], handler: H) -> ForkingHandler<'a, H, Self> {
ForkingHandler {
h1: handler,
h2: self,
h1_condition: path,
}
}
}
pub struct TypedStaticResponse {
pub payload: &'static [u8],
pub contentformat: &'static [u8],
}
impl From<&'static str> for TypedStaticResponse {
fn from(s: &'static str) -> Self {
TypedStaticResponse {
payload: s.as_bytes(),
contentformat: &[],
}
}
}
impl Handler for TypedStaticResponse {
type RequestData = Code;
fn extract_request_data(&mut self, request: &impl ReadableMessage) -> Self::RequestData {
match request.code().into() {
code::GET => (),
_ => return code::METHOD_NOT_ALLOWED,
};
for o in request.options() {
match o.number() {
option::ACCEPT => {
if o.value() != self.contentformat {
return code::UNSUPPORTED_CONTENT_FORMAT;
}
}
o if option::get_criticality(o) == option::Criticality::Critical => {
return code::BAD_OPTION;
}
_ => {}
}
}
code::CONTENT
}
fn estimate_length(&mut self, request: &Self::RequestData) -> usize {
match *request {
code::CONTENT => {
2 + self.payload.len() + self.contentformat.len()
}
_ => 0,
}
}
fn build_response(
&mut self,
response: &mut impl MutableWritableMessage,
request: Self::RequestData,
) {
response.set_code(codeconvert(request));
let mut p = &b""[..];
if request == code::CONTENT {
if let Ok(cfopt) = option::CONTENT_FORMAT.try_into() {
response.add_option(cfopt, self.contentformat);
}
p = self.payload;
}
response.set_payload(p);
}
}
pub struct SimpleRenderableData(Result<Block2RequestData, Code>);
pub trait SimpleRenderable {
fn render<W>(&mut self, writer: &mut W)
where
W: core::fmt::Write;
fn content_format(&self) -> Option<u16> {
None
}
}
#[derive(Debug, Copy, Clone)]
pub struct SimpleRendered<T: SimpleRenderable>(pub T);
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(|o| Some(o))
.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_else(Default::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(w), cf);
}
}
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");
}
}
use serde::Serialize;
use serde_cbor;
pub trait SimpleCBORHandler {
type Get: for<'de> serde::Serialize;
type Post: for<'de> serde::Deserialize<'de>;
type Put: for<'de> serde::Deserialize<'de>;
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
}
}
pub struct SimpleCBORWrapper<H: SimpleCBORHandler> {
handler: H,
}
impl<H: SimpleCBORHandler> SimpleCBORWrapper<H> {
pub fn new(handler: H) -> Self {
SimpleCBORWrapper { handler }
}
fn check_get_options(request: &impl ReadableMessage) -> Result<(), Code> {
for o in request.options() {
match o.number().into() {
option::ACCEPT => {
if o.value_uint() != Some(60u8) {
return Err(code::UNSUPPORTED_CONTENT_FORMAT);
}
}
o if option::get_criticality(o) == option::Criticality::Critical => {
return Err(code::BAD_OPTION);
}
_ => {}
}
}
Ok(())
}
fn check_postput_options(request: &impl ReadableMessage) -> Result<(), Code> {
for o in request.options() {
match o.number().into() {
option::CONTENT_FORMAT => {
if o.value_uint() != Some(60u8) {
return Err(code::NOT_ACCEPTABLE);
}
}
o if option::get_criticality(o) == option::Criticality::Critical => {
return Err(code::BAD_OPTION);
}
_ => {}
}
}
Ok(())
}
}
pub enum SimpleCBORRequestData {
Get,
Done(Code),
}
use self::SimpleCBORRequestData::{Done, Get};
impl<H> Handler for SimpleCBORWrapper<H>
where
H: SimpleCBORHandler,
H::Get: serde::Serialize,
H::Post: for<'de> serde::Deserialize<'de>,
H::Put: for<'de> serde::Deserialize<'de>,
{
type RequestData = SimpleCBORRequestData;
fn extract_request_data(&mut self, request: &impl ReadableMessage) -> Self::RequestData {
match request.code().into() {
code::GET => {
if let Err(e) = Self::check_get_options(request) {
return Done(e);
}
Get
}
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 => 1280 - 40 - 4,
}
}
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 => {
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));
if let Ok(cfopt) = option::CONTENT_FORMAT.try_into() {
response.add_option_uint(cfopt, 60u8);
}
let (written, available) = {
let len = response.available_space() - 1;
let len = len.min(1024);
let payload = &mut response.payload_mut_with_len(len);
let mut win = windowed_infinity::WindowedInfinity::new(payload, 0);
match repr.serialize(&mut serde_cbor::ser::Serializer::new(&mut win)) {
Ok(()) => (win.get_cursor().try_into().ok(), payload.len()),
Err(_) => (None, 0),
}
};
match written {
Some(w) if w <= available => {
response.truncate(w);
}
_ => {
response.set_code(codeconvert(code::INTERNAL_SERVER_ERROR));
response.truncate(0);
}
}
}
}
}
}
}
}