coap_server/transport.rs
1use async_trait::async_trait;
2use coap_lite::error::MessageError;
3use coap_lite::Packet;
4use futures::{Sink, Stream};
5use std::fmt::Debug;
6use std::io;
7use std::pin::Pin;
8
9/// Generalization of the underlying CoAP transport, intended primarily to make it easy to support a
10/// wide range of protocols (TCP, DTLS, websockets, BLE, etc) but also to eventually support
11/// alternative runtime frameworks such as Embassy for embedded devices.
12#[async_trait]
13pub trait Transport {
14 type Endpoint: Debug + Send + Clone;
15
16 /// Perform the binding, that is, begin accepting new data from this transport even if
17 /// there isn't yet a handler serving the data source yet. Note that this is not quite
18 /// the same as TcpSocket::bind which requires a loop to accept the incoming connections. In
19 /// our case, we expect a continuous async stream of (Packet, Endpoint) pairs which distinguish
20 /// each individual socket. The transport impl is expected to spawn new tasks for each
21 /// accepted source as necessary.
22 async fn bind(self) -> Result<BoxedFramedBinding<Self::Endpoint>, TransportError>;
23}
24
25pub type BoxedFramedBinding<Endpoint> = Pin<Box<dyn FramedBinding<Endpoint>>>;
26
27/// Trait generalizing a common feature of async libraries like tokio where a socket is exposed
28/// as both a stream and a sink. It should be possible even for libraries that have them split
29/// to unify in the bridging layer with this crate.
30///
31/// Note that this binding is intended to generalize cases like TCP and UDP together which is why
32/// the abstraction doesn't have an additional layer for accepting an incoming connection. For
33/// UDP there is no such concept and framed items can simply arrive at any time from any source.
34pub trait FramedBinding<Endpoint>:
35 Send
36 + Stream<Item = Result<FramedItem<Endpoint>, FramedReadError<Endpoint>>>
37 + Sink<FramedItem<Endpoint>, Error = FramedWriteError>
38{
39 /// Access the link's MTU which can be used to determine things like the ideal block
40 /// transfer size to recommend. If it cannot be determined by the link, a suitable
41 /// default one will be selected based on the CoAP specification.
42 fn mtu(&self) -> Option<u32>;
43}
44
45/// Parsed CoAP packet coming from a remote peer, as designated by [`Endpoint`]. Note that
46/// the endpoint is delivered with each packet so that packet-oriented protocols can
47/// avoid the leaky abstraction of a "connection" to a given Endpoint.
48pub type FramedItem<Endpoint> = (Packet, Endpoint);
49
50/// Error when receiving from a remote peer. Note that here [`Endpoint`] is optional as there may
51/// be a generic read error unrelated to any remote peer, for example if the underlying bound
52/// socket is closed.
53pub type FramedReadError<Endpoint> = (TransportError, Option<Endpoint>);
54
55/// Error when sending to a remote peer. Note that [`Endpoint`] is omitted in this case as the
56/// endpoint is provided to the send APIs themselves so we can easily tell which peer generated
57/// the error.
58pub type FramedWriteError = TransportError;
59
60/// Generalized errors indicating a range of transport-related issues such as being unable to bind,
61/// disconnections from remote peers, malformed input, etc. Most of these errors are non-fatal
62/// and the server can happily continue serving other customers.
63#[derive(thiserror::Error, Debug)]
64pub enum TransportError {
65 #[error("generic I/O error")]
66 IoError(#[from] Option<io::Error>),
67
68 #[error("packet was malformed")]
69 MalformedPacket(#[from] MessageError),
70
71 #[error("unspecified: {0}")]
72 Unspecified(String),
73}