1use crate::error::{Error, Result};
20use crate::{Did, Document, Message};
21use async_trait::async_trait;
22
23#[async_trait]
24pub(crate) trait OutboxWire: Send + std::fmt::Debug {
25 async fn send_payload(&mut self, payload: &[u8]) -> Result<()>;
26 fn close_box(self: Box<Self>);
27}
28
29#[derive(Debug)]
33pub struct Outbox {
34 inner: Option<Box<dyn OutboxWire>>,
35 recipient_document: Document,
36 local: bool,
37 did: String,
38 protocol: String,
39}
40
41impl Outbox {
42 pub(crate) fn from_transport<T>(
44 transport: T,
45 recipient_document: Document,
46 local: bool,
47 did: String,
48 protocol: String,
49 ) -> Self
50 where
51 T: OutboxWire + 'static,
52 {
53 Self {
54 inner: Some(Box::new(transport)),
55 recipient_document,
56 local,
57 did,
58 protocol,
59 }
60 }
61
62 pub async fn send(&mut self, message: &Message) -> Result<()> {
70 message.headers().validate()?;
71 let unencrypted = message.message_type == crate::service::MESSAGE_TYPE_BROADCAST
72 || (self.local && same_base_did(&message.from, &message.to)?);
73 let payload = if unencrypted {
74 message.encode()?
75 } else {
76 message.enclose_for(&self.recipient_document)?.encode()?
77 };
78 match self.inner.as_mut() {
79 Some(transport) => transport.send_payload(&payload).await,
80 None => Err(Error::ConnectionClosed("outbox is closed".to_string())),
81 }
82 }
83
84 #[must_use]
86 pub fn did(&self) -> &str {
87 &self.did
88 }
89
90 #[must_use]
92 pub fn protocol(&self) -> &str {
93 &self.protocol
94 }
95
96 pub fn close(mut self) {
98 if let Some(transport) = self.inner.take() {
99 transport.close_box();
100 }
101 }
102}
103
104fn same_base_did(from: &str, to: &str) -> Result<bool> {
105 let (from_base, _) = Did::parse(from)?;
106 let (to_base, _) = Did::parse(to)?;
107 Ok(from_base == to_base)
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::{generate_identity_from_secret, Envelope, SigningKey};
114 use std::sync::{Arc, Mutex};
115
116 #[derive(Debug, Clone)]
117 struct CaptureWire(Arc<Mutex<Vec<u8>>>);
118
119 #[async_trait]
120 impl OutboxWire for CaptureWire {
121 async fn send_payload(&mut self, payload: &[u8]) -> Result<()> {
122 self.0
123 .lock()
124 .expect("capture lock")
125 .extend_from_slice(payload);
126 Ok(())
127 }
128
129 fn close_box(self: Box<Self>) {}
130 }
131
132 fn signing_key(identity: &crate::GeneratedIdentity) -> SigningKey {
133 let did = Did::new_url(&identity.subject_url.ipns, Some("sign")).expect("signing DID");
134 let bytes = hex::decode(&identity.signing_private_key_hex).expect("private key hex");
135 SigningKey::from_private_key_bytes(did, bytes.try_into().expect("private key length"))
136 .expect("signing key")
137 }
138
139 fn outbox(recipient_document: &Document, local: bool) -> (Outbox, Arc<Mutex<Vec<u8>>>) {
140 let captured = Arc::new(Mutex::new(Vec::new()));
141 let outbox = Outbox::from_transport(
142 CaptureWire(captured.clone()),
143 recipient_document.clone(),
144 local,
145 recipient_document.id.clone(),
146 crate::service::INBOX_PROTOCOL_ID.to_string(),
147 );
148 (outbox, captured)
149 }
150
151 fn inbox_url(identity: &crate::GeneratedIdentity) -> String {
152 format!("{}#inbox", identity.document.id)
153 }
154
155 #[tokio::test]
156 async fn remote_message_is_transmitted_as_envelope() {
157 let sender = generate_identity_from_secret([1; 32]).expect("sender identity");
158 let recipient = generate_identity_from_secret([2; 32]).expect("recipient identity");
159 let message = Message::new(
160 sender.document.id.clone(),
161 inbox_url(&recipient),
162 crate::service::MESSAGE_TYPE_MESSAGE,
163 "text/plain",
164 b"secret",
165 &signing_key(&sender),
166 )
167 .expect("message");
168 let (mut outbox, captured) = outbox(&recipient.document, false);
169
170 outbox.send(&message).await.expect("send message");
171
172 let payload = captured.lock().expect("capture lock");
173 Envelope::decode(&payload).expect("encrypted envelope");
174 assert!(
175 Message::decode(&payload).is_err(),
176 "raw message reached wire"
177 );
178 }
179
180 #[tokio::test]
181 async fn broadcast_is_transmitted_as_raw_message() {
182 let sender = generate_identity_from_secret([1; 32]).expect("sender identity");
183 let recipient = generate_identity_from_secret([2; 32]).expect("recipient identity");
184 let message = Message::new(
185 sender.document.id.clone(),
186 String::new(),
187 crate::service::MESSAGE_TYPE_BROADCAST,
188 "text/plain",
189 b"public",
190 &signing_key(&sender),
191 )
192 .expect("broadcast");
193 let (mut outbox, captured) = outbox(&recipient.document, false);
194
195 outbox.send(&message).await.expect("send broadcast");
196
197 let payload = captured.lock().expect("capture lock");
198 assert_eq!(Message::decode(&payload).expect("raw message"), message);
199 }
200
201 #[tokio::test]
202 async fn local_same_base_did_is_transmitted_as_raw_message() {
203 let identity = generate_identity_from_secret([1; 32]).expect("identity");
204 let from = format!("{}#sender", identity.document.id);
205 let to = format!("{}#recipient", identity.document.id);
206 let message = Message::new(
207 from,
208 to,
209 crate::service::MESSAGE_TYPE_MESSAGE,
210 "text/plain",
211 b"local",
212 &signing_key(&identity),
213 )
214 .expect("message");
215 let (mut outbox, captured) = outbox(&identity.document, true);
216
217 outbox.send(&message).await.expect("send local message");
218
219 let payload = captured.lock().expect("capture lock");
220 assert_eq!(Message::decode(&payload).expect("raw message"), message);
221 }
222
223 #[tokio::test]
224 async fn local_wire_does_not_exempt_different_base_dids() {
225 let sender = generate_identity_from_secret([1; 32]).expect("sender identity");
226 let recipient = generate_identity_from_secret([2; 32]).expect("recipient identity");
227 let message = Message::new(
228 sender.document.id.clone(),
229 inbox_url(&recipient),
230 crate::service::MESSAGE_TYPE_MESSAGE,
231 "text/plain",
232 b"secret",
233 &signing_key(&sender),
234 )
235 .expect("message");
236 let (mut outbox, captured) = outbox(&recipient.document, true);
237
238 outbox.send(&message).await.expect("send message");
239
240 let payload = captured.lock().expect("capture lock");
241 Envelope::decode(&payload).expect("encrypted envelope");
242 }
243}