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}