Implement handle_payload

This commit is contained in:
Jazz Turner-Baggs 2026-02-04 17:33:06 -08:00
parent 97a1cf150a
commit 2fc2f15a04
No known key found for this signature in database
7 changed files with 93 additions and 32 deletions

View File

@ -148,7 +148,7 @@ pub fn handle_payload(
mut content_out: c_slice::Mut<'_, u8>, mut content_out: c_slice::Mut<'_, u8>,
) -> i32 { ) -> i32 {
match ctx.0.handle_payload(&payload) { match ctx.0.handle_payload(&payload) {
Some(content) => { Ok(Some(content)) => {
let convo_id_bytes = content.conversation_id.as_bytes(); let convo_id_bytes = content.conversation_id.as_bytes();
if conversation_id_out.len() < convo_id_bytes.len() { if conversation_id_out.len() < convo_id_bytes.len() {
@ -165,7 +165,7 @@ pub fn handle_payload(
content.data.len() as i32 content.data.len() as i32
} }
None => 0, _ => 0,
} }
} }

View File

@ -5,6 +5,7 @@ use crate::{
errors::ChatError, errors::ChatError,
identity::Identity, identity::Identity,
inbox::Inbox, inbox::Inbox,
proto::{EncryptedPayload, EnvelopeV1, Message},
types::{AddressedEnvelope, ContentData}, types::{AddressedEnvelope, ContentData},
}; };
@ -54,7 +55,7 @@ impl Context {
.map(|p| p.to_envelope(convo.id().to_string())) .map(|p| p.to_envelope(convo.id().to_string()))
.collect(); .collect();
let convo_handle = self.add_convo(convo); let convo_handle = self.add_convo(Box::new(convo));
(convo_handle, payload_bytes) (convo_handle, payload_bytes)
} }
@ -76,12 +77,30 @@ impl Context {
.collect()) .collect())
} }
pub fn handle_payload(&mut self, _payload: &[u8]) -> Option<ContentData> { // Decode bytes and send to protocol for processing.
// !TODO Replace Mock pub fn handle_payload(&mut self, payload: &[u8]) -> Result<Option<ContentData>, ChatError> {
Some(ContentData { let env = match EnvelopeV1::decode(payload) {
conversation_id: "convo_id".into(), Ok(v) => v,
data: vec![1, 2, 3, 4, 5, 6], Err(e) => return Err(e.into()),
}) };
// TODO: Impl Conversation hinting
let convo_id = env.conversation_hint;
let enc = EncryptedPayload::decode(payload)?;
// Call handle_payload on the appropriate protocol.
if convo_id == self.inbox.id() {
let (convo, content) = self.inbox.handle_frame(enc)?;
self.add_convo(convo);
Ok(content)
} else {
let Some(convo) = self.store.get_mut(&convo_id) else {
return Err(ChatError::NoConvo(0)); // TODO: Remove ConvoHandle type
};
convo.handle_frame(enc)
}
} }
pub fn create_intro_bundle(&mut self) -> Result<Vec<u8>, ChatError> { pub fn create_intro_bundle(&mut self) -> Result<Vec<u8>, ChatError> {
@ -89,7 +108,7 @@ impl Context {
Ok(Introduction::from(pkb).into()) Ok(Introduction::from(pkb).into())
} }
fn add_convo(&mut self, convo: impl Convo + Id + 'static) -> ConvoHandle { fn add_convo(&mut self, convo: Box<dyn Convo>) -> ConvoHandle {
let handle = self.next_convo_handle; let handle = self.next_convo_handle;
self.next_convo_handle += 1; self.next_convo_handle += 1;
let convo_id = self.store.insert_convo(convo); let convo_id = self.store.insert_convo(convo);
@ -123,7 +142,7 @@ mod tests {
let mut store: ConversationStore = ConversationStore::new(); let mut store: ConversationStore = ConversationStore::new();
let new_convo = GroupTestConvo::new(); let new_convo = GroupTestConvo::new();
let convo_id = store.insert_convo(new_convo); let convo_id = store.insert_convo(Box::new(new_convo));
let convo = store.get_mut(&convo_id).ok_or_else(|| 0); let convo = store.get_mut(&convo_id).ok_or_else(|| 0);
convo.unwrap(); convo.unwrap();

View File

@ -3,7 +3,7 @@ use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
pub use crate::errors::ChatError; pub use crate::errors::ChatError;
use crate::types::AddressedEncryptedPayload; use crate::types::{AddressedEncryptedPayload, ContentData};
pub type ConversationId<'a> = &'a str; pub type ConversationId<'a> = &'a str;
pub type ConversationIdOwned = Arc<str>; pub type ConversationIdOwned = Arc<str>;
@ -16,6 +16,16 @@ pub trait Convo: Id + Debug {
fn send_message(&mut self, content: &[u8]) fn send_message(&mut self, content: &[u8])
-> Result<Vec<AddressedEncryptedPayload>, ChatError>; -> Result<Vec<AddressedEncryptedPayload>, ChatError>;
/// Decrypts and processes an incoming encrypted frame.
///
/// Returns `Ok(Some(ContentData))` if the frame contains user content,
/// `Ok(None)` for protocol frames (e.g., placeholders), or an error if
/// decryption or frame parsing fails.
fn handle_frame(
&mut self,
enc_payload: EncryptedPayload,
) -> Result<Option<ContentData>, ChatError>;
fn remote_id(&self) -> String; fn remote_id(&self) -> String;
} }
@ -30,10 +40,9 @@ impl ConversationStore {
} }
} }
pub fn insert_convo(&mut self, conversation: impl Convo + Id + 'static) -> ConversationIdOwned { pub fn insert_convo(&mut self, conversation: Box<dyn Convo>) -> ConversationIdOwned {
let key: ConversationIdOwned = Arc::from(conversation.id()); let key: ConversationIdOwned = Arc::from(conversation.id());
self.conversations self.conversations.insert(key.clone(), conversation);
.insert(key.clone(), Box::new(conversation));
key key
} }
@ -53,5 +62,6 @@ impl ConversationStore {
mod group_test; mod group_test;
mod privatev1; mod privatev1;
use chat_proto::logoschat::encryption::EncryptedPayload;
pub use group_test::GroupTestConvo; pub use group_test::GroupTestConvo;
pub use privatev1::PrivateV1Convo; pub use privatev1::PrivateV1Convo;

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
conversation::{ChatError, ConversationId, Convo, Id}, conversation::{ChatError, ConversationId, Convo, Id},
types::AddressedEncryptedPayload, proto::EncryptedPayload,
types::{AddressedEncryptedPayload, ContentData},
}; };
#[derive(Debug)] #[derive(Debug)]
@ -27,6 +28,13 @@ impl Convo for GroupTestConvo {
Ok(vec![]) Ok(vec![])
} }
fn handle_frame(
&mut self,
_encoded_payload: EncryptedPayload,
) -> Result<Option<ContentData>, ChatError> {
Ok(None)
}
fn remote_id(&self) -> String { fn remote_id(&self) -> String {
self.id().to_string() self.id().to_string()
} }

View File

@ -12,7 +12,7 @@ use crate::{
conversation::{ChatError, ConversationId, Convo, Id}, conversation::{ChatError, ConversationId, Convo, Id},
errors::EncryptionError, errors::EncryptionError,
proto, proto,
types::AddressedEncryptedPayload, types::{AddressedEncryptedPayload, ContentData},
utils::timestamp_millis, utils::timestamp_millis,
}; };
@ -89,6 +89,15 @@ impl PrivateV1Convo {
.map_err(|e| EncryptionError::Decryption(e.to_string()))?; .map_err(|e| EncryptionError::Decryption(e.to_string()))?;
Ok(PrivateV1Frame::decode(content_bytes.as_slice()).unwrap()) Ok(PrivateV1Frame::decode(content_bytes.as_slice()).unwrap())
} }
// Handler for application content
fn handle_content(&self, data: Vec<u8>) -> Option<ContentData> {
Some(ContentData {
conversation_id: self.id().into(),
data,
is_new_convo: false,
})
}
} }
impl Id for PrivateV1Convo { impl Id for PrivateV1Convo {
@ -118,6 +127,28 @@ impl Convo for PrivateV1Convo {
}]) }])
} }
fn handle_frame(
&mut self,
encoded_payload: EncryptedPayload,
) -> Result<Option<ContentData>, ChatError> {
// Extract expected frame
let frame = self
.decrypt(encoded_payload)
.map_err(|_| ChatError::Protocol("decryption".into()))?;
let Some(frame_type) = frame.frame_type else {
return Err(ChatError::ProtocolExpectation("None", "Some".into()));
};
// Handle FrameTypes
let output = match frame_type {
FrameType::Content(bytes) => self.handle_content(bytes.into()),
FrameType::Placeholder(_) => None,
};
Ok(output)
}
fn remote_id(&self) -> String { fn remote_id(&self) -> String {
//TODO: Implement as per spec //TODO: Implement as per spec
self.id().into() self.id().into()

View File

@ -1,3 +1,4 @@
use chat_proto::logoschat::encryption::EncryptedPayload;
use hex; use hex;
use prost::Message; use prost::Message;
use prost::bytes::Bytes; use prost::bytes::Bytes;
@ -119,15 +120,11 @@ impl Inbox {
Ok((convo, payloads)) Ok((convo, payloads))
} }
fn handle_frame( pub fn handle_frame(
&mut self, &mut self,
message: &[u8], enc_payload: EncryptedPayload,
) -> Result<(Box<dyn Convo>, Vec<ContentData>), ChatError> { ) -> Result<(Box<dyn Convo>, Vec<ContentData>), ChatError> {
if message.len() == 0 { let handshake = Self::extract_payload(enc_payload)?;
return Err(ChatError::Protocol("Example error".into()));
}
let handshake = Self::extract_payload(proto::EncryptedPayload::decode(message)?)?;
let header = handshake let header = handshake
.header .header
@ -240,19 +237,14 @@ mod tests {
let mut raya_inbox = Inbox::new(raya_ident.into()); let mut raya_inbox = Inbox::new(raya_ident.into());
let bundle = raya_inbox.create_bundle(); let bundle = raya_inbox.create_bundle();
let (_, payloads) = saro_inbox let (_, mut payloads) = saro_inbox
.invite_to_private_convo(&bundle.into(), "hello".as_bytes()) .invite_to_private_convo(&bundle.into(), "hello".as_bytes())
.unwrap(); .unwrap();
let payload = payloads let payload = payloads.remove(0);
.get(0)
.expect("RemoteInbox::invite_to_private_convo did not generate any payloads");
let mut buf = Vec::new();
payload.data.encode(&mut buf).unwrap();
// Test handle_frame with valid payload // Test handle_frame with valid payload
let result = raya_inbox.handle_frame(&buf); let result = raya_inbox.handle_frame(payload.data);
assert!( assert!(
result.is_ok(), result.is_ok(),

View File

@ -14,6 +14,7 @@ pub struct AddressedEnvelope {
pub struct ContentData { pub struct ContentData {
pub conversation_id: String, pub conversation_id: String,
pub data: Vec<u8>, pub data: Vec<u8>,
pub is_new_convo: bool,
} }
// Internal type Definitions // Internal type Definitions