1use super::codec::{self, Message};
6use crate::utils::clock::Instant;
7use anyhow::Result;
8use std::collections::VecDeque;
9use std::time::Duration;
10
11const RESEND_STORE: usize = 2048;
13pub const SESSION_TAGS: &[u32] = &[8, 9, 10, 34, 35, 43, 49, 52, 56, 97, 122];
15
16struct Sent {
17 seq: u32,
18 msg_type: String,
19 sending_time: String,
20 body: Vec<(u32, String)>,
21}
22
23pub struct Session {
24 pub begin: String,
25 pub sender: String,
26 pub target: String,
27 pub heartbeat: Duration,
28 next_out: u32,
29 next_in: u32,
30 sent: VecDeque<Sent>,
31 resend_until: Option<u32>,
33 last_in: Instant,
34 last_out: Instant,
35 test_request: Option<(String, Instant)>,
36 test_counter: u32,
37 pub logout_sent: bool,
38}
39
40pub struct Step {
42 pub send: Vec<Vec<u8>>,
44 pub app: Option<Message>,
46 pub close: Option<String>,
48}
49
50impl Step {
51 fn send(bytes: Vec<Vec<u8>>) -> Self {
52 Self {
53 send: bytes,
54 app: None,
55 close: None,
56 }
57 }
58}
59
60pub fn body(m: &Message) -> Vec<(u32, String)> {
62 m.fields
63 .iter()
64 .filter(|(t, _)| !SESSION_TAGS.contains(t))
65 .cloned()
66 .collect()
67}
68
69impl Session {
70 pub fn new(begin: &str, sender: &str, target: &str, heartbeat: Duration) -> Self {
71 let now = Instant::now();
72 Self {
73 begin: begin.to_owned(),
74 sender: sender.to_owned(),
75 target: target.to_owned(),
76 heartbeat,
77 next_out: 1,
78 next_in: 1,
79 sent: VecDeque::new(),
80 resend_until: None,
81 last_in: now,
82 last_out: now,
83 test_request: None,
84 test_counter: 0,
85 logout_sent: false,
86 }
87 }
88
89 pub fn reset(&mut self) {
90 self.next_out = 1;
91 self.next_in = 1;
92 self.sent.clear();
93 self.resend_until = None;
94 }
95
96 pub fn next_in(&self) -> u32 {
97 self.next_in
98 }
99
100 pub fn next_out(&self) -> u32 {
101 self.next_out
102 }
103
104 pub fn accept_logon(&mut self, seq: u32) {
106 self.next_in = seq + 1;
107 self.last_in = Instant::now();
108 }
109
110 fn header(&self, msg_type: &str, seq: u32, poss_dup: Option<&str>) -> Vec<(u32, String)> {
111 let mut h = vec![
112 (35, msg_type.to_owned()),
113 (49, self.sender.clone()),
114 (56, self.target.clone()),
115 (34, seq.to_string()),
116 ];
117 if poss_dup.is_some() {
118 h.push((43, "Y".into()));
119 }
120 h.push((52, codec::timestamp()));
121 if let Some(orig) = poss_dup {
122 h.push((122, orig.to_owned()));
123 }
124 h
125 }
126
127 pub fn encode(&mut self, msg_type: &str, body: &[(u32, String)]) -> Result<Vec<u8>> {
129 let seq = self.next_out;
130 let mut fields = self.header(msg_type, seq, None);
131 let sending_time = fields
132 .iter()
133 .find(|(t, _)| *t == 52)
134 .map(|(_, v)| v.clone())
135 .unwrap_or_default();
136 fields.extend_from_slice(body);
137 let bytes = codec::encode(&self.begin, &fields)?;
138 self.next_out += 1;
139 self.last_out = Instant::now();
140 if !super::dict::MESSAGES
141 .iter()
142 .any(|(t, _, admin)| *t == msg_type && *admin)
143 {
144 self.sent.push_back(Sent {
145 seq,
146 msg_type: msg_type.to_owned(),
147 sending_time,
148 body: body.to_vec(),
149 });
150 while self.sent.len() > RESEND_STORE {
151 self.sent.pop_front();
152 }
153 }
154 Ok(bytes)
155 }
156
157 pub fn logout(&mut self, text: Option<&str>) -> Vec<u8> {
158 self.logout_sent = true;
159 let body: Vec<(u32, String)> = text.map(|t| vec![(58, t.to_owned())]).unwrap_or_default();
160 self.encode("5", &body).unwrap_or_default()
161 }
162
163 fn reject(&mut self, ref_seq: u32, reason: u32, ref_tag: Option<u32>, text: &str) -> Vec<u8> {
164 let mut body = vec![(45, ref_seq.to_string())];
165 if let Some(t) = ref_tag {
166 body.push((371, t.to_string()));
167 }
168 body.push((373, reason.to_string()));
169 body.push((58, text.to_owned()));
170 self.encode("3", &body).unwrap_or_default()
171 }
172
173 fn resend(&mut self, begin: u32, end: u32) -> Vec<Vec<u8>> {
175 let last = self.next_out - 1;
176 let end = if end == 0 || end > last { last } else { end };
177 let mut out = Vec::new();
178 let mut gap_from: Option<u32> = None;
179 let mut seq = begin.max(1);
180 while seq <= end {
181 match self.sent.iter().find(|s| s.seq == seq) {
182 Some(s) => {
183 if let Some(g) = gap_from.take() {
184 out.push(self.gap_fill(g, seq));
185 }
186 let mut fields = self.header(&s.msg_type, s.seq, Some(&s.sending_time));
187 fields.extend_from_slice(&s.body);
188 if let Ok(bytes) = codec::encode(&self.begin, &fields) {
189 out.push(bytes);
190 }
191 }
192 None => {
193 gap_from.get_or_insert(seq);
194 }
195 }
196 seq += 1;
197 }
198 if let Some(g) = gap_from {
199 out.push(self.gap_fill(g, end + 1));
200 }
201 self.last_out = Instant::now();
202 out
203 }
204
205 fn gap_fill(&self, seq: u32, new_seq: u32) -> Vec<u8> {
206 let mut fields = self.header("4", seq, Some(&codec::timestamp()));
207 fields.push((123, "Y".into()));
208 fields.push((36, new_seq.to_string()));
209 codec::encode(&self.begin, &fields).unwrap_or_default()
210 }
211
212 pub fn on_message(&mut self, m: Message) -> Step {
214 self.last_in = Instant::now();
215 let msg_type = m.msg_type().to_owned();
216 let Some(seq) = m.seq() else {
217 return Step {
218 send: vec![self.logout(Some("MsgSeqNum missing"))],
219 app: None,
220 close: Some("MsgSeqNum missing".into()),
221 };
222 };
223 if m.get(49) != Some(self.target.as_str()) || m.get(56) != Some(self.sender.as_str()) {
224 let r = self.reject(
225 seq,
226 9,
227 Some(if m.get(49) != Some(self.target.as_str()) {
228 49
229 } else {
230 56
231 }),
232 "CompID problem",
233 );
234 return Step {
235 send: vec![r, self.logout(Some("CompID problem"))],
236 app: None,
237 close: Some("CompID problem".into()),
238 };
239 }
240 let poss_dup = m.get(43) == Some("Y");
241 if msg_type == "4" && m.get(123) != Some("Y") {
243 return match m.get(36).and_then(|v| v.parse::<u32>().ok()) {
244 Some(n) if n >= self.next_in => {
245 self.next_in = n;
246 self.resend_until = None;
247 Step::send(vec![])
248 }
249 _ => {
250 let r = self.reject(seq, 5, Some(36), "NewSeqNo is lower than expected");
251 Step::send(vec![r])
252 }
253 };
254 }
255 if seq < self.next_in {
256 if poss_dup {
257 return Step::send(vec![]);
258 }
259 let text = format!(
260 "MsgSeqNum too low, expecting {} but received {seq}",
261 self.next_in
262 );
263 return Step {
264 send: vec![self.logout(Some(&text))],
265 app: None,
266 close: Some(text),
267 };
268 }
269 if seq > self.next_in {
270 let mut send = Vec::new();
271 match msg_type.as_str() {
272 "2" => send.extend(self.on_resend_request(&m)),
273 "5" => {
274 let reply = if self.logout_sent {
275 vec![]
276 } else {
277 vec![self.logout(None)]
278 };
279 return Step {
280 send: reply,
281 app: None,
282 close: Some("peer logged out".into()),
283 };
284 }
285 _ => {}
286 }
287 if self.resend_until.is_none() {
288 let from = self.next_in.to_string();
289 if let Ok(b) = self.encode("2", &[(7, from), (16, "0".into())]) {
290 send.push(b);
291 }
292 }
293 self.resend_until = Some(self.resend_until.unwrap_or(0).max(seq));
294 return Step::send(send);
295 }
296 self.next_in += 1;
298 if self.resend_until.is_some_and(|u| self.next_in > u) {
299 self.resend_until = None;
300 }
301 match msg_type.as_str() {
302 "0" => {
303 if let (Some(id), Some((want, _))) = (m.get(112), &self.test_request) {
304 if id == want {
305 self.test_request = None;
306 }
307 }
308 Step::send(vec![])
309 }
310 "1" => {
311 let id = m.get(112).unwrap_or_default().to_owned();
312 let body: Vec<(u32, String)> = if id.is_empty() {
313 vec![]
314 } else {
315 vec![(112, id)]
316 };
317 Step::send(self.encode("0", &body).into_iter().collect())
318 }
319 "2" => Step::send(self.on_resend_request(&m)),
320 "3" => Step::send(vec![]),
321 "4" => match m.get(36).and_then(|v| v.parse::<u32>().ok()) {
322 Some(n) if n > seq => {
323 self.next_in = n;
324 if self.resend_until.is_some_and(|u| self.next_in > u) {
325 self.resend_until = None;
326 }
327 Step::send(vec![])
328 }
329 _ => {
330 let r = self.reject(seq, 5, Some(36), "GapFill NewSeqNo must exceed MsgSeqNum");
331 Step::send(vec![r])
332 }
333 },
334 "5" => {
335 let reply = if self.logout_sent {
336 vec![]
337 } else {
338 vec![self.logout(None)]
339 };
340 Step {
341 send: reply,
342 app: None,
343 close: Some(
344 m.get(58)
345 .map(|t| format!("peer logged out: {t}"))
346 .unwrap_or_else(|| "peer logged out".into()),
347 ),
348 }
349 }
350 "A" => {
351 let r = self.reject(seq, 11, Some(35), "already logged on");
352 Step::send(vec![r])
353 }
354 _ => {
355 if m.get(52).is_none() {
356 let r = self.reject(seq, 1, Some(52), "Required tag missing");
357 return Step::send(vec![r]);
358 }
359 Step {
360 send: vec![],
361 app: Some(m),
362 close: None,
363 }
364 }
365 }
366 }
367
368 fn on_resend_request(&mut self, m: &Message) -> Vec<Vec<u8>> {
369 let begin = m.get(7).and_then(|v| v.parse().ok()).unwrap_or(1);
370 let end = m.get(16).and_then(|v| v.parse().ok()).unwrap_or(0);
371 self.resend(begin, end)
372 }
373
374 pub fn tick(&mut self) -> (Vec<Vec<u8>>, Option<String>) {
377 let now = Instant::now();
378 let mut out = Vec::new();
379 if let Some((_, sent)) = &self.test_request {
380 if now.duration_since(*sent) >= self.heartbeat {
381 let text = "no answer to TestRequest";
382 return (vec![self.logout(Some(text))], Some(text.into()));
383 }
384 } else if now.duration_since(self.last_in) >= self.heartbeat + self.heartbeat / 5 {
385 self.test_counter += 1;
386 let id = format!("TEST{}", self.test_counter);
387 if let Ok(b) = self.encode("1", &[(112, id.clone())]) {
388 out.push(b);
389 }
390 self.test_request = Some((id, now));
391 }
392 if now.duration_since(self.last_out) >= self.heartbeat {
393 if let Ok(b) = self.encode("0", &[]) {
394 out.push(b);
395 }
396 }
397 (out, None)
398 }
399}