[][src]Trait async_coap::LocalEndpoint

pub trait LocalEndpoint: Sized {
    type SocketAddr: SocketAddrExt + ToSocketAddrs<SocketAddr = Self::SocketAddr, Error = Self::SocketError>;
    type SocketError: Debug;
    type DefaultTransParams: TransParams;
    type InboundContext: InboundContext<SocketAddr = Self::SocketAddr>;
    type RespondableInboundContext: RespondableInboundContext<SocketAddr = Self::SocketAddr>;
    type LookupStream: Stream<Item = Self::SocketAddr> + Unpin;
    type RemoteEndpoint: RemoteEndpoint<SocketAddr = Self::SocketAddr, InboundContext = Self::InboundContext>;
    fn scheme(&self) -> &str;
fn default_port(&self) -> u16;
fn lookup(
        &self,
        hostname: &str,
        port: u16
    ) -> Result<Self::LookupStream, Error>;
fn remote_endpoint<S, H, P>(
        &self,
        addr: S,
        host: Option<H>,
        path: P
    ) -> Self::RemoteEndpoint
    where
        S: ToSocketAddrs<SocketAddr = Self::SocketAddr, Error = Self::SocketError>,
        H: Into<String>,
        P: Into<RelRefBuf>
;
fn remote_endpoint_from_uri(
        &self,
        uri: &Uri
    ) -> Result<Self::RemoteEndpoint, Error>;
#[must_use = "nothing will be sent unless the returned future is polled"] fn send<'a, S, R, SD>(
        &'a self,
        remote_addr: S,
        send_desc: SD
    ) -> BoxFuture<'a, Result<R, Error>>
    where
        S: ToSocketAddrs<SocketAddr = Self::SocketAddr, Error = Self::SocketError> + 'a,
        SD: SendDesc<Self::InboundContext, R> + 'a,
        R: Send + 'a
;
#[must_use = "nothing will be received unless the returned future is polled"] fn receive<'a, F>(&'a self, handler: F) -> BoxFuture<'a, Result<(), Error>>
    where
        F: FnMut(&Self::RespondableInboundContext) -> Result<(), Error> + 'a + Send + Unpin
; }

Trait representing a local (as opposed to remote) CoAP endpoint. Allows for sending and receiving CoAP requests.

Implementations

LocalEndpoint is a trait, which allows for multiple back-end implementations. async-coap comes with two: NullLocalEndpoint and DatagramLocalEndpoint.

NullLocalEndpoint does what you might expect: nothing. Attempts to send requests always results in Error::ResponseTimeout and LocalEndpoint::receive will block indefinitely. Creating an instance of it is quite straightforward:

use std::sync::Arc;
use async_coap::null::NullLocalEndpoint;

let local_endpoint = Arc::new(NullLocalEndpoint);

If you want to do something more useful, then DatagramLocalEndpoint is likely what you are looking for. It takes an instance of AsyncDatagramSocket at construction:

use std::sync::Arc;
use async_coap::prelude::*;
use async_coap::datagram::{DatagramLocalEndpoint,AllowStdUdpSocket};

// `AllowStdUdpSocket`, which is a (inefficient) wrapper around the
// standard rust `UdpSocket`. It is convenient for testing and for examples
// but should not be used in production code.
let socket = AllowStdUdpSocket::bind("[::]:0").expect("UDP bind failed");

// Create a new local endpoint from the socket instance we just created,
// wrapping it in a `Arc<>` to ensure it can live long enough.
let local_endpoint = Arc::new(DatagramLocalEndpoint::new(socket));

Client Usage

Before you can start sending requests and receiving responses, you will need to make sure that the LocalEndpoint::receive method gets called repeatedly. The easiest way to do that is to add the std::future::Future returned by LocalEndpointExt::receive_loop_arc to an execution pool:

use futures::{prelude::*,executor::ThreadPool,task::Spawn,task::SpawnExt};

let mut pool = ThreadPool::new().expect("Unable to create thread pool");

// We use a receiver handler of `null_receiver!()` because this instance
// will be used purely as a client, not a server.
pool.spawn(local_endpoint
    .clone()
    .receive_loop_arc(null_receiver!())
    .map(|_|unreachable!())
);

Once the Arc<LocalEndpint> has been added to an execution pool, the run_until method on the pool can be used to block execution of the futures emitted by LocalEndpoint:


let result = local_pool.run_until(
    local_endpoint.send(
        "coap.me:5683",
        CoapRequest::get()       // This is a CoAP GET request
            .emit_any_response() // Return the first response we get
    )
);

println!("result: {:?}", result);

Or, more naturally, the returned futures can be used directly in async blocks:

async move {
    let future = local_endpoint.send(
        "coap.me:5683",
        CoapRequest::get()       // This is a CoAP GET request
            .emit_any_response() // Return the first response we get
    );

    // Wait for the final result and print it.
    println!("result: {:?}", future.await);
}

Server Usage

In order to serve resources for other devices to interact with, you will need to replace the null_receiver! we were using earlier with something more substantial. The method takes a closure as an argument, and the closure itself has a single argument: a borrowed RespondableInboundContext.

For example, to have our server return a response for a request instead of just returning an error, we could use the following function as our receive handler:

use async_coap::prelude::*;
use async_coap::{RespondableInboundContext, Error};

fn receive_handler<T: RespondableInboundContext>(context: &T) -> Result<(),Error> {
    context.respond(|msg_out|{
        msg_out.set_msg_code(MsgCode::SuccessContent);
        msg_out.insert_option(option::CONTENT_FORMAT, ContentFormat::TEXT_PLAIN_UTF8)?;
        msg_out.append_payload_string("Successfully fetched!")?;
        Ok(())
    })?;
    Ok(())
}

However, that's actually not super useful: it returns a successful result for every possible request: including bogus ones. Let's say that we wanted to expose a resource that lives at "/test" on our server, returning a 4.04 Not Found for every other request. That might look something like this:

use async_coap::prelude::*;
use async_coap::{RespondableInboundContext, Error, LinkFormatWrite, LINK_ATTR_TITLE};
use core::fmt::Write; // For `write!()`
use core::borrow::Borrow;
use option::CONTENT_FORMAT;

fn receive_handler<T: RespondableInboundContext>(context: &T) -> Result<(),Error> {
    let msg = context.message();
    let uri = msg.options().extract_uri()?;
    let decoded_path = uri.raw_path().unescape_uri().skip_slashes().to_cow();

    match (msg.msg_code(), decoded_path.borrow()) {
        // Handle GET /test
        (MsgCode::MethodGet, "test") => context.respond(|msg_out| {
            msg_out.set_msg_code(MsgCode::SuccessContent);
            msg_out.insert_option(CONTENT_FORMAT, ContentFormat::TEXT_PLAIN_UTF8);
            write!(msg_out,"Successfully fetched {:?}!", uri.as_str())?;
            Ok(())
        }),

        // Handle GET /.well-known/core, for service discovery.
        (MsgCode::MethodGet, ".well-known/core") => context.respond(|msg_out| {
            msg_out.set_msg_code(MsgCode::SuccessContent);
            msg_out.insert_option(CONTENT_FORMAT, ContentFormat::APPLICATION_LINK_FORMAT);
            LinkFormatWrite::new(msg_out)
                .link(uri_ref!("/test"))
                    .attr(LINK_ATTR_TITLE, "Test Resource")
                    .finish()?;
            Ok(())
        }),

        // Handle unsupported methods
        (_, "test") | (_, ".well-known/core") => context.respond(|msg_out| {
           msg_out.set_msg_code(MsgCode::ClientErrorMethodNotAllowed);
            write!(msg_out,"Method \"{:?}\" Not Allowed", msg.msg_code())?;
            Ok(())
        }),

        // Everything else is a 4.04
        (_, _) => context.respond(|msg_out| {
            msg_out.set_msg_code(MsgCode::ClientErrorNotFound);
            write!(msg_out,"{:?} Not Found", uri.as_str())?;
            Ok(())
        }),
    }
}

Associated Types

type SocketAddr: SocketAddrExt + ToSocketAddrs<SocketAddr = Self::SocketAddr, Error = Self::SocketError>

The SocketAddr type to use with this local endpoint. This is usually simply std::net::SocketAddr, but may be different in some cases (like for CoAP-SMS endpoints).

type SocketError: Debug

The error type associated with errors generated by socket and address-lookup operations. Typically, this is std::io::Error, but it may be different if Self::SocketAddr isn't std::net::SocketAddr.

type DefaultTransParams: TransParams

The trait representing the default transmission parameters to use.

type InboundContext: InboundContext<SocketAddr = Self::SocketAddr>

Type used by closure that is passed into send(), representing the context for the response.

type RespondableInboundContext: RespondableInboundContext<SocketAddr = Self::SocketAddr>

Type used by closure that is passed into receive(), representing the context for inbound requests.

type LookupStream: Stream<Item = Self::SocketAddr> + Unpin

The concrete return type of the lookup() method.

type RemoteEndpoint: RemoteEndpoint<SocketAddr = Self::SocketAddr, InboundContext = Self::InboundContext>

The concrete type for a RemoteEndpoint associated with this local endpoint.

Loading content...

Required methods

fn scheme(&self) -> &str

Returns a string representing the scheme of the underlying transport. For example, this could return "coap", "coaps+sms", etc.

fn default_port(&self) -> u16

Returns the default port to use when the port is unspecified. This value is typically defined by the scheme. Returns zero if port numbers are ignored by the underlying technology.

fn lookup(&self, hostname: &str, port: u16) -> Result<Self::LookupStream, Error>

Method for asynchronously looking up the Self::SocketAddr instances for the given hostname and port.

fn remote_endpoint<S, H, P>(
    &self,
    addr: S,
    host: Option<H>,
    path: P
) -> Self::RemoteEndpoint where
    S: ToSocketAddrs<SocketAddr = Self::SocketAddr, Error = Self::SocketError>,
    H: Into<String>,
    P: Into<RelRefBuf>, 

Constructs a new RemoteEndpoint instance for the given address, host, and path.

fn remote_endpoint_from_uri(
    &self,
    uri: &Uri
) -> Result<Self::RemoteEndpoint, Error>

Constructs a new RemoteEndpoint instance for the given Uri.

#[must_use = "nothing will be sent unless the returned future is polled"] fn send<'a, S, R, SD>(
    &'a self,
    remote_addr: S,
    send_desc: SD
) -> BoxFuture<'a, Result<R, Error>> where
    S: ToSocketAddrs<SocketAddr = Self::SocketAddr, Error = Self::SocketError> + 'a,
    SD: SendDesc<Self::InboundContext, R> + 'a,
    R: Send + 'a, 

Sends a message to remote_addr based on the criteria provided by send_desc.

send_desc, which implements SendDesc, is the real heavy lifter here. It defines the message content, retransmit timing, resending logic---even the return type of this method if defined by send_desc. This flexibility allows this method to uniformly perform complex interactions like block transfers and resource observing.

A variant of this method, LocalEndpointExt::send_as_stream, is used to handle cases where multiple responses are expected, such as when sending multicast requests or doing resource observing.

Performance Tips

If you are going to be calling this method frequently for a destination that you are referencing by a hostname, it will significantly improve performance on some platforms if you only pass SocketAddr types to remote_addr and not rely on having ToSocketAddrs do hostname lookups inside of send.

The easiest way to do this is to use either the remote_endpoint or remote_endpoint_from_uri methods to create a RemoteEndpoint instance and call the send method on that instead. From that instance you can call send multiple times: any hostname that needs to be resolved is calculated and cached when the RemoteEndpoint is first created.

Transaction Tracking

All state tracking the transmission of the message is stored in the returned future. To cancel retransmits, drop the returned future.

The returned future is lazily evaluated, so nothing will be transmitted unless the returned future is polled: you cannot simply fire and forget. Because of this lazy evaluation, the futures returned by this method do not need to be used immediately and may be stored for later use if that happens to be useful.

#[must_use = "nothing will be received unless the returned future is polled"] fn receive<'a, F>(&'a self, handler: F) -> BoxFuture<'a, Result<(), Error>> where
    F: FnMut(&Self::RespondableInboundContext) -> Result<(), Error> + 'a + Send + Unpin

Receives a single request and runs the given handler on it once.

Each call handles (at most) one single inbound request. To handle multiple requests, call this function from a loop. The LocalEndpointExt trait comes with some helpers to make implementing such a loop easier: receive_as_stream, receive_loop, and receive_loop_arc.

Properly calling this method in the background is absolutely critical to the correct operation of this trait: send will not work without it.

Local endpoints which implement Sync can have this method called from multiple threads, allowing multiple requests to be handled concurrently.

Handler

If you are going to be serving resources using this LocalEndpoint, you will need specify a handler to handle inbound requests. See the section Server Usage above for an example.

If instead you are only using this LocalEndpoint as a client, then you may pass null_receiver!() as the handler, as shown in Client Usage.

Loading content...

Implementors

impl LocalEndpoint for NullLocalEndpoint[src]

type SocketAddr = SocketAddr

type SocketError = Error

type DefaultTransParams = StandardCoapConstants

type RemoteEndpoint = NullRemoteEndpoint

type LookupStream = Iter<IntoIter<Self::SocketAddr>>

type InboundContext = NullInboundContext

type RespondableInboundContext = NullRespondableInboundContext

impl<US: AsyncDatagramSocket> LocalEndpoint for DatagramLocalEndpoint<US>[src]

type SocketAddr = US::SocketAddr

type SocketError = US::Error

type DefaultTransParams = StandardCoapConstants

type LookupStream = Iter<IntoIter<Self::SocketAddr>>

type RespondableInboundContext = DatagramRespondableInboundContext<Self::SocketAddr>

type InboundContext = DatagramInboundContext<Self::SocketAddr>

type RemoteEndpoint = DatagramRemoteEndpoint<US>

Loading content...