1use std::collections::HashMap;
5use std::sync::Arc;
6
7use tokio::sync::oneshot;
8use tokio::task::JoinHandle;
9
10use crate::application::Application;
11use crate::datadictionary::DataDictionary;
12use crate::error::{Error, Result};
13use crate::log::LogFactory;
14use crate::session::{Command, Session, SessionHandle};
15use crate::session_id::SessionId;
16use crate::settings::{ConnectionType, Settings, TlsSettings};
17use crate::store::MessageStoreFactory;
18use crate::transport::{self, SessionKey, Tls};
19
20pub struct Engine {
21 handles: HashMap<SessionKey, SessionHandle>,
22 io_tasks: Vec<JoinHandle<()>>,
23}
24
25fn key_of(id: &SessionId) -> SessionKey {
26 (id.begin_string.clone(), id.sender_comp_id.clone(), id.target_comp_id.clone())
27}
28
29impl Engine {
30 pub async fn start(
33 settings: &Settings,
34 app: Arc<dyn Application>,
35 store_factory: Arc<dyn MessageStoreFactory>,
36 log_factory: Arc<dyn LogFactory>,
37 ) -> Result<Engine> {
38 let configs = settings.session_configs()?;
39 if configs.is_empty() {
40 return Err(Error::Config("no [SESSION] sections configured".into()));
41 }
42
43 let mut handles = HashMap::new();
44 let mut io_tasks = Vec::new();
45 let mut acceptors_by_port: HashMap<
46 u16,
47 (HashMap<SessionKey, SessionHandle>, TlsSettings),
48 > = HashMap::new();
49 let mut dictionaries: HashMap<String, Arc<DataDictionary>> = HashMap::new();
50
51 for cfg in configs {
52 let (dictionary, admin_dictionary) = if !cfg.use_data_dictionary {
56 (None, None)
57 } else {
58 match (&cfg.transport_data_dictionary, &cfg.app_data_dictionary) {
59 (Some(transport), Some(app)) => {
60 let merged_key = format!("{transport}+{app}");
61 let (merged, transport_dd) = match (
62 dictionaries.get(&merged_key),
63 dictionaries.get(transport),
64 ) {
65 (Some(m), Some(t)) => (m.clone(), t.clone()),
66 _ => {
67 let t = Arc::new(DataDictionary::load(transport).await?);
68 let a = DataDictionary::load(app).await?;
69 let m = Arc::new((*t).clone().merged_with_app(&a));
70 dictionaries.insert(merged_key, m.clone());
71 dictionaries.insert(transport.clone(), t.clone());
72 (m, t)
73 }
74 };
75 (Some(merged), Some(transport_dd))
76 }
77 _ => match &cfg.data_dictionary {
78 Some(path) => {
79 let dd = match dictionaries.get(path) {
80 Some(dd) => dd.clone(),
81 None => {
82 let dd = Arc::new(DataDictionary::load(path).await?);
83 dictionaries.insert(path.clone(), dd.clone());
84 dd
85 }
86 };
87 (Some(dd.clone()), Some(dd))
88 }
89 None => (None, None),
90 },
91 }
92 };
93 let key = key_of(&cfg.session_id);
94 if handles.contains_key(&key) {
95 return Err(Error::Config(format!(
96 "duplicate session {}",
97 cfg.session_id
98 )));
99 }
100 let store = store_factory.create(&cfg.session_id)?;
101 let log = log_factory.create(&cfg.session_id)?;
102 let connection_type = cfg.connection_type;
103 let (host, port, reconnect) =
104 (cfg.socket_connect_host.clone(), cfg.socket_connect_port, cfg.reconnect_interval);
105 let accept_port = cfg.socket_accept_port;
106 let tls_settings = cfg.tls.clone();
107
108 let handle =
109 Session::spawn(cfg, store, log, app.clone(), dictionary, admin_dictionary);
110 handles.insert(key.clone(), handle.clone());
111
112 match connection_type {
113 ConnectionType::Initiator => {
114 let tls = client_tls(&tls_settings, &host)?;
115 io_tasks.push(tokio::spawn(transport::run_initiator(
116 host, port, reconnect, handle, tls,
117 )));
118 }
119 ConnectionType::Acceptor => {
120 let entry = acceptors_by_port
123 .entry(accept_port)
124 .or_insert_with(|| (HashMap::new(), tls_settings.clone()));
125 entry.0.insert(key, handle);
126 }
127 }
128 }
129
130 for (port, (registry, tls_settings)) in acceptors_by_port {
131 let tls = server_tls(&tls_settings)?;
132 let listener = tokio::net::TcpListener::bind(("0.0.0.0", port)).await?;
133 io_tasks
134 .push(tokio::spawn(transport::run_acceptor(listener, Arc::new(registry), tls)));
135 }
136
137 Ok(Engine { handles, io_tasks })
138 }
139
140 pub fn session(
142 &self,
143 begin_string: &str,
144 sender_comp_id: &str,
145 target_comp_id: &str,
146 ) -> Option<SessionHandle> {
147 self.handles
148 .get(&(begin_string.to_owned(), sender_comp_id.to_owned(), target_comp_id.to_owned()))
149 .cloned()
150 }
151
152 pub fn sessions(&self) -> impl Iterator<Item = &SessionHandle> {
153 self.handles.values()
154 }
155
156 pub async fn stop(self) {
159 for task in &self.io_tasks {
160 task.abort();
161 }
162 for handle in self.handles.values() {
163 let (tx, rx) = oneshot::channel();
164 if handle.cmd_tx.send(Command::Stop(tx)).await.is_ok() {
165 let _ = rx.await;
166 }
167 }
168 }
169}
170
171fn client_tls(tls: &TlsSettings, host: &str) -> Result<Tls> {
173 if !tls.enabled {
174 return Ok(Tls::None);
175 }
176 #[cfg(feature = "tls")]
177 return Ok(Tls::Client(crate::tls::build_connector(tls, host)?));
178 #[cfg(not(feature = "tls"))]
179 {
180 let _ = host;
181 Err(Error::Config("SocketUseSSL=Y requires the 'tls' feature".into()))
182 }
183}
184
185fn server_tls(tls: &TlsSettings) -> Result<Tls> {
187 if !tls.enabled {
188 return Ok(Tls::None);
189 }
190 #[cfg(feature = "tls")]
191 return Ok(Tls::Server(crate::tls::build_acceptor(tls)?));
192 #[cfg(not(feature = "tls"))]
193 Err(Error::Config("SocketUseSSL=Y requires the 'tls' feature".into()))
194}