coap_zero/endpoint/
connect.rs

1// Copyright Open Logistics Foundation
2//
3// Licensed under the Open Logistics Foundation License 1.3.
4// For details on the licensing terms, see the LICENSE file.
5// SPDX-License-Identifier: OLFL-1.3
6
7//! Connection Layer of the CoAP [Endpoint](super::CoapEndpoint)
8//!
9//! Handles all connect and close functionality
10
11use core::str::FromStr;
12
13use embedded_hal::blocking::rng;
14use embedded_nal::{AddrType, Dns, IpAddr, SocketAddr, UdpClientStack};
15use embedded_timers::clock::Clock;
16
17use super::{error::Error, CoapEndpoint, ConnectionLink, Uri};
18
19impl<
20        'a,
21        UDP,
22        RNG,
23        CLOCK,
24        const MAX_OPTION_COUNT: usize,
25        const MAX_OPTION_SIZE: usize,
26        const INCOMING_BUFFER_SIZE: usize,
27        const OUTGOING_BUFFER_SIZE: usize,
28        const RECEIVE_BUFFER_SIZE: usize,
29    >
30    CoapEndpoint<
31        'a,
32        UDP,
33        RNG,
34        CLOCK,
35        MAX_OPTION_COUNT,
36        MAX_OPTION_SIZE,
37        INCOMING_BUFFER_SIZE,
38        OUTGOING_BUFFER_SIZE,
39        RECEIVE_BUFFER_SIZE,
40    >
41where
42    UDP: UdpClientStack,
43    RNG: rng::Read,
44    CLOCK: Clock,
45{
46    /// Stores an already initialized socket inside the CoAP Endpoint.
47    ///
48    /// This socket has to already be connected to a remote endpoint.
49    pub fn connect_with_socket(
50        &mut self,
51        socket: UDP::UdpSocket,
52        addr: SocketAddr,
53    ) -> Result<(), Error<<UDP as UdpClientStack>::Error>> {
54        if self.connection_link.is_none() {
55            self.connection_link = Some(ConnectionLink { socket, addr });
56            Ok(())
57        } else {
58            Err(Error::AlreadyConnected)
59        }
60    }
61
62    /// Connects the CoAP Endpoint to a socket address and stores the socket.
63    pub fn connect_to_addr<'client>(
64        &mut self,
65        client: &'client mut UDP,
66        addr: SocketAddr,
67    ) -> Result<&'client mut UDP, Error<<UDP as UdpClientStack>::Error>> {
68        if self.connection_link.is_some() {
69            Err(Error::AlreadyConnected)
70        } else {
71            let mut socket = client.socket().map_err(Error::Network)?;
72            client.connect(&mut socket, addr).map_err(Error::Network)?;
73
74            self.connection_link = Some(ConnectionLink { socket, addr });
75
76            Ok(client)
77        }
78    }
79
80    /// Closes the connection specified by the CoAP Endpoints socket.
81    pub fn close<'client>(
82        &mut self,
83        client: &'client mut UDP,
84    ) -> Result<&'client mut UDP, Error<<UDP as UdpClientStack>::Error>> {
85        if self.connection_link.is_none() {
86            Err(Error::NotConnected)
87        } else {
88            client
89                .close(self.connection_link.take().unwrap().socket)
90                .map_err(Error::Network)?;
91            self.connection_link = None;
92
93            Ok(client)
94        }
95    }
96}
97
98impl<
99        'a,
100        UDP,
101        RNG,
102        CLOCK,
103        const MAX_OPTION_COUNT: usize,
104        const MAX_OPTION_SIZE: usize,
105        const INCOMING_BUFFER_SIZE: usize,
106        const OUTGOING_BUFFER_SIZE: usize,
107        const RECEIVE_BUFFER_SIZE: usize,
108    >
109    CoapEndpoint<
110        'a,
111        UDP,
112        RNG,
113        CLOCK,
114        MAX_OPTION_COUNT,
115        MAX_OPTION_SIZE,
116        INCOMING_BUFFER_SIZE,
117        OUTGOING_BUFFER_SIZE,
118        RECEIVE_BUFFER_SIZE,
119    >
120where
121    UDP: UdpClientStack + Dns,
122    RNG: rng::Read,
123    CLOCK: Clock,
124{
125    /// Connect the Endpoint to the specified URL string.
126    ///
127    /// The URL has to have the
128    /// format `coap://<host>:<port>`, although the port is optional.
129    /// If you specify a hostname, a DNS lookup will be made.
130    /// If you specify an IP Address, no lookup has to be done.
131    pub fn connect_to_url(
132        &mut self,
133        client: &mut UDP,
134        url: &str,
135    ) -> Result<(), Error<<UDP as UdpClientStack>::Error>> {
136        if self.connection_link.is_some() {
137            return Err(Error::AlreadyConnected);
138        }
139
140        let uri = Uri::new(url).map_err(|_| Error::Uri)?;
141
142        let components = uri.authority_components().ok_or(Error::Uri)?;
143
144        let host = components.host();
145        let port = if let Some(port_str) = components.port() {
146            port_str.parse::<u16>().map_err(|_| Error::Uri)?
147        } else {
148            super::DEFAULT_COAP_PORT
149        };
150
151        let ip = if let Ok(ip) = IpAddr::from_str(host) {
152            ip
153        } else {
154            nb::block!(client.get_host_by_name(host, AddrType::Either)).unwrap()
155        };
156
157        let socket_addr = SocketAddr::new(ip, port);
158
159        self.connect_to_addr(client, socket_addr)?;
160
161        Ok(())
162    }
163}