rainmaker_components/protocomm/
mod.rs

1//! Protocol Communications
2//!
3//! Protocomm is a component that can be used for building services with transport independent endpoints for interaction with other applications.
4//! It's architecture is roughly based on Protocomm component in ESP-IDf. You can read more about it [here].
5//!
6//! By default it provides 2 transports(types of servers):
7//!     - Https: Uses an HTTP server.
8//!     - Gatt:  Serves a GATT application over BLE.
9//!
10//! You can read more information about the transports at [ProtocommHttpd] and [ProtocommGatt]
11//!
12//! [here]: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/provisioning/protocomm.html
13
14mod security;
15mod transports;
16
17use std::sync::Arc;
18use transports::{TransportGatt, TransportHttpd};
19use uuid::Uuid;
20
21pub use self::security::ProtocommSecurity;
22use self::security::SecurityTrait;
23use crate::http;
24
25/// Protcomm with HTTP transport
26///
27/// Uses an HTTP server for providing interaction with endpoints.
28///
29/// All the registered endpoints are available as POST endpoints as "/ep_name".   
30/// The caller is responsible for setting up appropriate mechanism for interacting with this HTTP server.    
31/// For e.g., by creating a WiFi Access Point, connecting to a known WiFi network, mDNS etc.
32///
33/// <b> HTTP server is started as soon as it is initialized so the endpoints are available as soon as they are registered.    
34/// The service stops when the object is dropped </b>
35#[allow(private_interfaces)]
36pub type ProtocommHttpd = Protocomm<TransportHttpd>;
37
38/// Protcomm with GATT transport
39///
40/// Uses an GATT service for providing interaction with endpoints.   
41///
42/// All the registered endpoints are available as GATT characteristics with the UUID specified while registering them.   
43/// The caller is responsible for suitable BLE advertisement for this service.
44///
45/// <b> The registered endpoints are made function after calling `start()`.   
46/// This should be done after all the required endpoints are registered.   
47/// There is no corresponding `stop()` method. The service stops when the object is dropped </b>
48#[allow(private_interfaces)]
49pub type ProtocommGatt = Protocomm<TransportGatt>;
50
51#[derive(Default)]
52pub struct ProtocommGattConfig {
53    pub service_uuid: Uuid,
54}
55
56pub type ProtocommHttpdConfig = http::HttpConfiguration;
57
58#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
59pub(crate) enum EndpointType {
60    Version,
61    Security,
62    #[default]
63    Other,
64}
65
66pub type ProtocommCallbackType = Box<dyn Fn(&str, &[u8]) -> Vec<u8> + Send + Sync + 'static>;
67
68pub struct Protocomm<T> {
69    transport: T,
70    sec: Arc<ProtocommSecurity>,
71}
72
73impl Protocomm<TransportGatt> {
74    pub fn new(gatt_config: ProtocommGattConfig, security: ProtocommSecurity) -> Self {
75        let transport_ble = transports::TransportGatt::new(gatt_config.service_uuid);
76
77        Self {
78            transport: transport_ble,
79            sec: Arc::new(security),
80        }
81    }
82
83    pub fn set_version_info(&mut self, uuid: Uuid, ep_name: &str, version_info: String) {
84        self.transport.add_endpoint(
85            uuid,
86            ep_name,
87            Box::new(move |_ep, _data| version_info.as_bytes().to_vec()),
88            EndpointType::Version,
89            self.sec.clone(),
90        );
91    }
92
93    pub fn set_security_endpoint(&mut self, uuid: Uuid, ep_name: &str) {
94        self.transport.add_endpoint(
95            uuid,
96            ep_name,
97            Box::new(|_, _| Vec::default()),
98            EndpointType::Security,
99            self.sec.clone(),
100        );
101    }
102
103    pub fn register_endpoint(
104        &mut self,
105        uuid: Uuid,
106        ep_name: &str,
107        callback: ProtocommCallbackType,
108    ) {
109        log::debug!("Registering endpoint: {}", ep_name);
110        self.transport.add_endpoint(
111            uuid,
112            ep_name,
113            callback,
114            EndpointType::Other,
115            self.sec.clone(),
116        );
117        log::debug!("Registered endpoint: {}", ep_name);
118    }
119
120    pub fn start(&mut self) {
121        self.transport.start();
122    }
123}
124
125impl Protocomm<TransportHttpd> {
126    pub fn new(config: ProtocommHttpdConfig, security: ProtocommSecurity) -> Self {
127        let transport = TransportHttpd::new(config);
128        Self {
129            transport,
130            sec: Arc::new(security),
131        }
132    }
133
134    pub fn set_version_info(&mut self, ep_name: &str, version_info: String) {
135        self.register_endpoint_internal(
136            ep_name,
137            Box::new(move |_ep, _data| version_info.as_bytes().to_vec()),
138            EndpointType::Version,
139        );
140    }
141
142    pub fn set_security_endpoint(&mut self, ep_name: &str) {
143        self.register_endpoint_internal(ep_name, Box::new(|_, _| vec![]), EndpointType::Security);
144    }
145
146    pub fn register_endpoint(&mut self, ep_name: &str, callback: ProtocommCallbackType) {
147        self.register_endpoint_internal(ep_name, callback, EndpointType::Other);
148    }
149
150    fn register_endpoint_internal(
151        &mut self,
152        ep_name: &str,
153        cb: ProtocommCallbackType,
154        ep_type: EndpointType,
155    ) {
156        let sec = self.sec.clone();
157        self.transport.add_endpoint(ep_name, cb, ep_type, sec);
158    }
159}
160
161pub(crate) fn protocomm_req_handler(
162    ep: &str,
163    data: &[u8],
164    cb: &ProtocommCallbackType,
165    ep_type: &EndpointType,
166    sec: &Arc<ProtocommSecurity>,
167) -> Vec<u8> {
168    match ep_type {
169        EndpointType::Version => cb(ep, data),
170        EndpointType::Security => sec.security_handler(ep, data.to_vec()),
171        EndpointType::Other => {
172            // for decrypting
173            let mut data = data.to_vec();
174
175            sec.decrypt(&mut data);
176            let mut res = cb(ep, &data);
177            sec.encrypt(&mut res);
178
179            res
180        }
181    }
182}