Skip to main content

agp_service/
session.rs

1// Copyright AGNTCY Contributors (https://github.com/agntcy)
2// SPDX-License-Identifier: Apache-2.0
3
4use async_trait::async_trait;
5use parking_lot::RwLock;
6use tonic::Status;
7
8use crate::errors::SessionError;
9use crate::fire_and_forget::FireAndForgetConfiguration;
10use crate::request_response::RequestResponseConfiguration;
11use crate::streaming::StreamingConfiguration;
12use agp_datapath::messages::encoder::Agent;
13use agp_datapath::pubsub::proto::pubsub::v1::{Message, SessionHeaderType};
14
15/// Session ID
16pub type Id = u32;
17
18/// Reserved session id
19pub const SESSION_RANGE: std::ops::Range<u32> = 0..(u32::MAX - 1000);
20pub const SESSION_UNSPECIFIED: u32 = u32::MAX;
21
22/// Message wrapper
23#[derive(Clone, PartialEq, Debug)]
24pub struct SessionMessage {
25    /// The message to be sent
26    pub message: Message,
27    /// The optional session info
28    pub info: Info,
29}
30
31impl SessionMessage {
32    /// Create a new session message
33    pub fn new(message: Message, info: Info) -> Self {
34        SessionMessage { message, info }
35    }
36}
37
38impl From<(Message, Info)> for SessionMessage {
39    fn from(tuple: (Message, Info)) -> Self {
40        SessionMessage {
41            message: tuple.0,
42            info: tuple.1,
43        }
44    }
45}
46
47impl From<Message> for SessionMessage {
48    fn from(message: Message) -> Self {
49        let info = Info::from(&message);
50        SessionMessage { message, info }
51    }
52}
53
54impl From<SessionMessage> for Message {
55    fn from(session_message: SessionMessage) -> Self {
56        session_message.message
57    }
58}
59
60/// Channel used in the path service -> app
61pub type AppChannelSender = tokio::sync::mpsc::Sender<Result<SessionMessage, SessionError>>;
62/// Channel used in the path app -> service
63pub type AppChannelReceiver = tokio::sync::mpsc::Receiver<Result<SessionMessage, SessionError>>;
64/// Channel used in the path service -> gw
65pub type GwChannelSender = tokio::sync::mpsc::Sender<Result<Message, Status>>;
66/// Channel used in the path gw -> service
67pub type GwChannelReceiver = tokio::sync::mpsc::Receiver<Result<Message, Status>>;
68
69/// Session Info
70#[derive(Clone, PartialEq, Debug)]
71pub struct Info {
72    /// The id of the session
73    pub id: Id,
74    /// The message nonce used to identify the message
75    pub message_id: Option<u32>,
76    /// The Message Type
77    pub session_header_type: SessionHeaderType,
78    /// The identifier of the agent that sent the message
79    pub message_source: Option<Agent>,
80    /// The input connection id
81    pub input_connection: Option<u64>,
82}
83
84impl Info {
85    /// Create a new session info
86    pub fn new(id: Id) -> Self {
87        Info {
88            id,
89            message_id: None,
90            session_header_type: SessionHeaderType::Unspecified,
91            message_source: None,
92            input_connection: None,
93        }
94    }
95
96    pub fn set_message_id(&mut self, message_id: u32) {
97        self.message_id = Some(message_id);
98    }
99
100    pub fn set_session_header_type(&mut self, session_header_type: SessionHeaderType) {
101        self.session_header_type = session_header_type;
102    }
103
104    pub fn set_message_source(&mut self, message_source: Agent) {
105        self.message_source = Some(message_source);
106    }
107
108    pub fn set_input_connection(&mut self, input_connection: u64) {
109        self.input_connection = Some(input_connection);
110    }
111
112    pub fn get_message_id(&self) -> Option<u32> {
113        self.message_id
114    }
115
116    pub fn get_session_header_type(&self) -> SessionHeaderType {
117        self.session_header_type
118    }
119
120    pub fn get_message_source(&self) -> Option<Agent> {
121        self.message_source.clone()
122    }
123
124    pub fn get_input_connection(&self) -> Option<u64> {
125        self.input_connection
126    }
127}
128
129impl From<&Message> for Info {
130    fn from(message: &Message) -> Self {
131        let session_header = message.get_session_header();
132        let agp_header = message.get_agp_header();
133
134        let id = session_header.session_id;
135        let message_id = session_header.message_id;
136        let message_source = message.get_source();
137        let input_connection = agp_header.incoming_conn;
138        let session_header_type = session_header.header_type;
139
140        Info {
141            id,
142            message_id: Some(message_id),
143            session_header_type: SessionHeaderType::try_from(session_header_type)
144                .unwrap_or(SessionHeaderType::Unspecified),
145            message_source: Some(message_source),
146            input_connection,
147        }
148    }
149}
150
151/// The state of a session
152#[derive(Clone, PartialEq, Debug)]
153pub enum State {
154    Active,
155    Inactive,
156}
157
158/// The type of a session
159#[derive(Clone, PartialEq, Debug)]
160pub enum SessionDirection {
161    #[allow(dead_code)]
162    Sender,
163    #[allow(dead_code)]
164    Receiver,
165    Bidirectional,
166}
167
168#[derive(Clone, PartialEq, Debug)]
169pub(crate) enum MessageDirection {
170    North,
171    South,
172}
173
174/// The session type
175#[derive(Clone, PartialEq, Debug)]
176pub enum SessionType {
177    FireAndForget,
178    RequestResponse,
179    Streaming,
180}
181
182impl std::fmt::Display for SessionType {
183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184        match self {
185            SessionType::FireAndForget => write!(f, "FireAndForget"),
186            SessionType::RequestResponse => write!(f, "RequestResponse"),
187            SessionType::Streaming => write!(f, "Streaming"),
188        }
189    }
190}
191
192#[derive(Clone, PartialEq, Debug)]
193pub enum SessionConfig {
194    FireAndForget(FireAndForgetConfiguration),
195    RequestResponse(RequestResponseConfiguration),
196    Streaming(StreamingConfiguration),
197}
198
199pub trait SessionConfigTrait {
200    fn replace(&mut self, session_config: &SessionConfig) -> Result<(), SessionError>;
201}
202
203impl std::fmt::Display for SessionConfig {
204    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205        match self {
206            SessionConfig::FireAndForget(ff) => write!(f, "{}", ff),
207            SessionConfig::RequestResponse(rr) => write!(f, "{}", rr),
208            SessionConfig::Streaming(s) => write!(f, "{}", s),
209        }
210    }
211}
212
213pub(crate) trait CommonSession {
214    // Session ID
215    #[allow(dead_code)]
216    fn id(&self) -> Id;
217
218    // get the session state
219    #[allow(dead_code)]
220    fn state(&self) -> &State;
221
222    fn source(&self) -> &Agent;
223
224    // get the session config
225    fn session_config(&self) -> SessionConfig;
226
227    // set the session config
228    fn set_session_config(&self, session_config: &SessionConfig) -> Result<(), SessionError>;
229}
230
231#[async_trait]
232pub(crate) trait Session: CommonSession {
233    // publish a message as part of the session
234    async fn on_message(
235        &self,
236        message: SessionMessage,
237        direction: MessageDirection,
238    ) -> Result<(), SessionError>;
239}
240
241/// Common session data
242pub(crate) struct Common {
243    /// Session ID - unique identifier for the session
244    #[allow(dead_code)]
245    id: Id,
246
247    /// Session state
248    #[allow(dead_code)]
249    state: State,
250
251    /// Session type
252    session_config: RwLock<SessionConfig>,
253
254    /// Session direction
255    #[allow(dead_code)]
256    session_direction: SessionDirection,
257
258    /// Source agent
259    source: Agent,
260
261    /// Sender for messages to gw
262    tx_gw: GwChannelSender,
263
264    /// Sender for messages to app
265    tx_app: AppChannelSender,
266}
267
268impl CommonSession for Common {
269    fn id(&self) -> Id {
270        self.id
271    }
272
273    fn state(&self) -> &State {
274        &self.state
275    }
276
277    fn source(&self) -> &Agent {
278        &self.source
279    }
280
281    fn session_config(&self) -> SessionConfig {
282        self.session_config.read().clone()
283    }
284
285    fn set_session_config(&self, session_config: &SessionConfig) -> Result<(), SessionError> {
286        let mut conf = self.session_config.write();
287
288        match *conf {
289            SessionConfig::FireAndForget(ref mut config) => {
290                config.replace(session_config)?;
291            }
292            SessionConfig::RequestResponse(ref mut config) => {
293                config.replace(session_config)?;
294            }
295            SessionConfig::Streaming(ref mut config) => {
296                config.replace(session_config)?;
297            }
298        }
299        Ok(())
300    }
301}
302
303impl Common {
304    pub(crate) fn new(
305        id: Id,
306        session_direction: SessionDirection,
307        session_type: SessionConfig,
308        source: Agent,
309        tx_gw: GwChannelSender,
310        tx_app: AppChannelSender,
311    ) -> Common {
312        Common {
313            id,
314            state: State::Active,
315            session_direction,
316            session_config: RwLock::new(session_type),
317            source,
318            tx_gw,
319            tx_app,
320        }
321    }
322
323    #[allow(dead_code)]
324    pub(crate) fn tx_gw(&self) -> GwChannelSender {
325        self.tx_gw.clone()
326    }
327
328    pub(crate) fn tx_gw_ref(&self) -> &GwChannelSender {
329        &self.tx_gw
330    }
331
332    #[allow(dead_code)]
333    pub(crate) fn tx_app(&self) -> AppChannelSender {
334        self.tx_app.clone()
335    }
336
337    pub(crate) fn tx_app_ref(&self) -> &AppChannelSender {
338        &self.tx_app
339    }
340}
341
342// Define a macro to delegate trait implementation
343macro_rules! delegate_common_behavior {
344    ($parent:ident, $($tokens:ident),+) => {
345        impl CommonSession for $parent {
346            fn id(&self) -> Id {
347                // concat the token stream
348                self.$($tokens).+.id()
349            }
350
351            fn state(&self) -> &State {
352                self.$($tokens).+.state()
353            }
354
355            fn session_config(&self) -> SessionConfig {
356                self.$($tokens).+.session_config()
357            }
358
359            fn set_session_config(&self, session_config: &SessionConfig) -> Result<(), SessionError> {
360                self.$($tokens).+.set_session_config(session_config)
361            }
362
363            fn source(&self) -> &Agent {
364                self.$($tokens).+.source()
365            }
366        }
367    };
368}