1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

use super::*;
use crate::UriBuf;

/// An object that represents a remote CoAP endpoint with a default, overridable path.
///
/// # Example
///
/// ```
/// # #![feature(async_await)]
/// #
/// # use std::sync::Arc;
/// # use futures::{prelude::*,executor::LocalPool,task::LocalSpawnExt};
/// # use async_coap::prelude::*;
/// # use async_coap::datagram::{DatagramLocalEndpoint,AllowStdUdpSocket};
/// #
/// # // Create our asynchronous socket. In this case, it is just an
/// # // (inefficient) wrapper around the standard rust `UdpSocket`,
/// # // but that is quite adequate in this case.
/// # let socket = AllowStdUdpSocket::bind("[::]:0").expect("UDP bind failed");
/// #
/// # // Create a new local endpoint from the socket we just created,
/// # // wrapping it in a `Arc<>` to ensure it can live long enough.
/// # let local_endpoint = Arc::new(DatagramLocalEndpoint::new(socket));
/// #
/// # // Create a local execution pool for running our local endpoint.
/// # let mut pool = LocalPool::new();
/// #
/// # // Add our local endpoint to the pool, so that it
/// # // can receive packets.
/// # pool.spawner().spawn_local(local_endpoint
/// #     .clone()
/// #     .receive_loop_arc(null_receiver!())
/// #     .map(|err| panic!("Receive loop terminated: {}", err))
/// # );
/// #
/// # let future = async move {
/// // Create a remote endpoint instance to represent the
/// // device we wish to interact with.
/// let remote_endpoint = local_endpoint
///     .remote_endpoint_from_uri(uri!("coap://coap.me"))
///     .unwrap(); // Will only fail if the URI scheme or authority is unrecognizable
///
/// // Create a future that sends a request to a specific path
/// // on the remote endpoint, collecting any blocks in the response
/// // and returning `Ok(OwnedImmutableMessage)` upon success.
/// let future = remote_endpoint.send_to(
///     rel_ref!("large"),
///     CoapRequest::get()       // This is a CoAP GET request
///         .accept(ContentFormat::TEXT_PLAIN_UTF8) // We only want plaintext
///         .block2(Some(Default::default()))       // Enable block2 processing
///         .emit_successful_collected_response()                 // Collect all blocks into a single message
/// );
///
/// // Wait for the final result and print it.
/// println!("result: {:?}", future.await.unwrap());
/// # };
/// #
/// # pool.run_until(future);
/// ```
///
pub trait RemoteEndpoint {
    /// 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 SocketAddr: SocketAddrExt;

    /// Type used by closure that is passed into `send()`, representing the context for the
    /// response.
    type InboundContext: InboundContext<SocketAddr = Self::SocketAddr>;

    /// Returns a [`UriBuf`] describing the underlying destination of this remote endpoint.
    fn uri(&self) -> UriBuf;

    /// Returns a string slice containing the scheme for this `RemoteEndpoint`.
    fn scheme(&self) -> &'static str;

    /// Prevents this remote endpoint from including a `Uri-Host` option.
    fn remove_host_option(&mut self);

    /// Creates a clone of this `RemoteEndpoint` with a different relative path.
    fn clone_using_rel_ref(&self, uri: &RelRef) -> Self;

    /// Uses `send_desc` to send a request to the endpoint and path described by this
    /// `RemoteEndpoint` instance.
    fn send<'a, R, SD>(&'a self, send_desc: SD) -> BoxFuture<'_, Result<R, Error>>
    where
        SD: SendDesc<Self::InboundContext, R> + 'a,
        R: Send + 'a;

    /// Uses `send_desc` to send a request to the given relative path on the endpoint described
    /// by this `RemoteEndpoint` instance.
    fn send_to<'a, R, SD, UF>(&'a self, path: UF, send_desc: SD) -> BoxFuture<'_, Result<R, Error>>
    where
        SD: SendDesc<Self::InboundContext, R> + 'a,
        R: Send + 'a,
        UF: AsRef<RelRef>;
}

/// Extension trait which implements additional helper methods.
pub trait RemoteEndpointExt: RemoteEndpoint {
    /// Sends an application-level ping to to one or more addresses specified by `dest`.
    /// The first response received causes the future to emit `Ok(())`.
    fn ping(&self) -> BoxFuture<'_, Result<(), Error>> {
        self.send(Ping::new())
    }

    /// Analogous to [`LocalEndpointExt::send_as_stream`], except using this `RemoteEndpoint` for
    /// the destination SocketAddr and path.
    fn send_as_stream<'a, R, SD>(&'a self, send_desc: SD) -> SendAsStream<'a, R>
    where
        SD: SendDesc<Self::InboundContext, R> + 'a,
        R: Send + 'a,
    {
        let (sender, receiver) = futures::channel::mpsc::channel::<Result<R, Error>>(10);

        SendAsStream {
            receiver,
            send_future: self.send(SendAsStreamDesc::new(send_desc, sender)),
        }
    }

    /// Analogous to [`LocalEndpointExt::send_as_stream`], except using this `RemoteEndpoint` for
    /// the destination SocketAddr and using a path relative to this `RemoteEndpoint`.
    fn send_to_as_stream<'a, R, SD, UF>(&'a self, path: UF, send_desc: SD) -> SendAsStream<'a, R>
    where
        SD: SendDesc<Self::InboundContext, R> + 'a,
        R: Send + 'a,
        UF: AsRef<RelRef>,
    {
        let (sender, receiver) = futures::channel::mpsc::channel::<Result<R, Error>>(10);

        SendAsStream {
            receiver,
            send_future: self.send_to(path, SendAsStreamDesc::new(send_desc, sender)),
        }
    }
}

/// Blanket implementation of `RemoteEndpointExt` for all `RemoteEndpoint` instances.
impl<T: RemoteEndpoint> RemoteEndpointExt for T {}