1use std::collections::{HashMap, VecDeque};
4use std::time::{Duration, Instant};
5
6use super::{
7 DEADLINE_GRACE, DisconnectReason, HandshakeError, Negotiated, PEER_TIMEOUT, PING_INTERVAL,
8 Phase, RESYNC_BACKOFF, RESYNC_BACKOFF_MAX, select_version,
9};
10use crate::capability::Capabilities;
11use crate::error::{Error, code};
12use crate::event::{Event, Stream};
13use crate::frame::{DEFAULT_MAX_FRAME, FrameDecoder, MIN_MAX_FRAME, Oversized, write_frame};
14use crate::liveness::{Alive, Liveness};
15use crate::message::{
16 Call, DecodeError, Hello, Message, Method, Ping, Pong, Reply, Request, Response, ResyncParams,
17 Welcome, kind,
18};
19use crate::sequence::{SeqCheck, StreamTracker};
20
21#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct HostConfig {
24 pub versions: Vec<u16>,
26 pub max_frame: u32,
28 pub token: Option<String>,
30 pub ping_interval: Duration,
31 pub peer_timeout: Duration,
32 pub deadline_grace: Duration,
33}
34
35impl Default for HostConfig {
36 fn default() -> HostConfig {
37 HostConfig {
38 versions: crate::SUPPORTED_VERSIONS.to_vec(),
39 max_frame: DEFAULT_MAX_FRAME,
40 token: None,
41 ping_interval: PING_INTERVAL,
42 peer_timeout: PEER_TIMEOUT,
43 deadline_grace: DEADLINE_GRACE,
44 }
45 }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum HostEvent {
51 Ready(Negotiated),
53 Response {
56 id: u32,
57 method: Method,
58 result: Result<Reply, Error>,
59 },
60 Event(Event),
62 OutOfSync {
65 stream: Stream,
66 expected: u64,
67 received: u64,
68 },
69 Resynced { stream: Stream, seq: u64 },
72 ResyncFailed { stream: Stream, error: Error },
78 FrameTooLarge(Oversized),
80 Closed(DisconnectReason),
82}
83
84#[derive(Debug)]
85enum State {
86 AwaitingHello,
87 Ready(Negotiated),
88 Closed(DisconnectReason),
89}
90
91#[derive(Debug)]
93struct Pending {
94 method: Method,
95 deadline_ms: Option<u32>,
96 expires: Option<Instant>,
99 resync: Option<ResyncOf>,
102}
103
104#[derive(Clone, Copy, Debug)]
107struct ResyncOf {
108 stream: Stream,
109 automatic: bool,
110}
111
112#[derive(Clone, Copy, Debug)]
114struct Retry {
115 failures: u32,
117 at: Option<Instant>,
119}
120
121#[derive(Debug)]
123pub struct HostSession {
124 config: HostConfig,
125 state: State,
126 decoder: FrameDecoder,
127 outbox: Vec<u8>,
128 events: VecDeque<HostEvent>,
129 pending: HashMap<u32, Pending>,
130 next_id: u32,
131 liveness: Liveness,
132 next_nonce: u64,
133 streams: StreamTracker,
134 retries: HashMap<Stream, Retry>,
136 in_flight: HashMap<Stream, u32>,
139}
140
141impl HostSession {
142 pub fn new(config: HostConfig, now: Instant) -> HostSession {
145 HostSession {
146 decoder: FrameDecoder::new(config.max_frame.max(MIN_MAX_FRAME)),
147 liveness: Liveness::new(config.ping_interval, config.peer_timeout, now),
148 config,
149 state: State::AwaitingHello,
150 outbox: Vec::new(),
151 events: VecDeque::new(),
152 pending: HashMap::new(),
153 next_id: 1,
154 next_nonce: 0,
155 streams: StreamTracker::new(),
156 retries: HashMap::new(),
157 in_flight: HashMap::new(),
158 }
159 }
160
161 pub fn receive(&mut self, bytes: &[u8], now: Instant) {
163 if self.is_closed() {
164 return;
165 }
166 self.decoder.push(bytes);
167 let mut alive = !self.decoder.is_discarding();
170 while !self.is_closed() {
171 let frame = match self.decoder.next_frame() {
172 None => break,
173 Some(Ok(payload)) => Ok(Message::decode(payload)),
174 Some(Err(oversized)) => Err(oversized),
175 };
176 alive = true;
177 match frame {
178 Ok(Ok(msg)) => self.handle(msg, now),
179 Ok(Err(DecodeError::UnknownKind(_))) => {}
181 Ok(Err(e)) => self.close_with(DisconnectReason::Protocol(e.to_string())),
182 Err(oversized) => self.handle_oversized(oversized),
183 }
184 }
185 if alive {
186 self.liveness.heard(now);
187 }
188 self.schedule_retries(now);
189 }
190
191 pub fn receive_eof(&mut self, now: Instant) {
193 let _ = now;
194 if !self.is_closed() {
195 self.close_with(DisconnectReason::Eof);
196 }
197 }
198
199 pub fn handle_timeout(&mut self, now: Instant) {
201 if self.is_closed() {
202 return;
203 }
204 let mut expired: Vec<u32> = self
205 .pending
206 .iter()
207 .filter(|(_, p)| p.expires.is_some_and(|at| at <= now))
208 .map(|(id, _)| *id)
209 .collect();
210 expired.sort_unstable();
211 for id in expired {
212 let pending = self.pending.remove(&id).expect("listed above");
213 let error =
214 Error::deadline_exceeded(pending.method, id, pending.deadline_ms.unwrap_or(0));
215 self.resolve(id, pending, Err(error));
216 }
217 self.schedule_retries(now);
218 self.send_due_retries(now);
219 match self.liveness.check(now, self.phase() == Phase::Ready) {
220 Alive::Yes => {}
221 Alive::Ping => {
222 let nonce = self.next_nonce;
223 self.next_nonce = self.next_nonce.wrapping_add(1);
224 self.send_control(&Message::Ping(Ping { nonce }));
225 }
226 Alive::Dead(silence) => self.close_with(DisconnectReason::Silent(silence)),
227 }
228 }
229
230 pub fn poll_timeout(&self) -> Option<Instant> {
232 if self.is_closed() {
233 return None;
234 }
235 let liveness = self.liveness.next_check(self.phase() == Phase::Ready);
236 let deadline = self.pending.values().filter_map(|p| p.expires).min();
237 let retry = self.retries.values().filter_map(|r| r.at).min();
238 Some(
239 [deadline, retry]
240 .into_iter()
241 .flatten()
242 .fold(liveness, Instant::min),
243 )
244 }
245
246 pub fn send_request(
251 &mut self,
252 call: Call,
253 deadline: Option<Duration>,
254 now: Instant,
255 ) -> Result<u32, Error> {
256 self.send(call, deadline, now, None)
257 }
258
259 pub fn resync(&mut self, stream: Stream, now: Instant) -> Result<u32, Error> {
262 self.send(
263 Call::Resync(ResyncParams { stream }),
264 None,
265 now,
266 Some(ResyncOf {
267 stream,
268 automatic: false,
269 }),
270 )
271 }
272
273 fn resync_automatically(&mut self, stream: Stream, failures: u32, now: Instant) {
277 let sent = self.send(
278 Call::Resync(ResyncParams { stream }),
279 None,
280 now,
281 Some(ResyncOf {
282 stream,
283 automatic: true,
284 }),
285 );
286 match sent {
287 Ok(_) => {
288 self.in_flight.insert(stream, failures);
289 }
290 Err(error) => self
291 .events
292 .push_back(HostEvent::ResyncFailed { stream, error }),
293 }
294 }
295
296 fn schedule_retries(&mut self, now: Instant) {
299 for retry in self.retries.values_mut() {
300 if retry.at.is_none() {
301 let doublings = retry.failures.saturating_sub(1).min(16);
302 let wait = RESYNC_BACKOFF
303 .saturating_mul(1 << doublings)
304 .min(RESYNC_BACKOFF_MAX);
305 retry.at = Some(now + wait);
306 }
307 }
308 }
309
310 fn send_due_retries(&mut self, now: Instant) {
312 if self.phase() != Phase::Ready {
313 return;
314 }
315 let mut due: Vec<(Stream, u32)> = self
316 .retries
317 .iter()
318 .filter(|(_, retry)| retry.at.is_some_and(|at| at <= now))
319 .map(|(stream, retry)| (*stream, retry.failures))
320 .collect();
321 due.sort_unstable_by_key(|(stream, _)| stream.0);
322 for (stream, failures) in due {
323 self.retries.remove(&stream);
324 self.resync_automatically(stream, failures, now);
325 }
326 }
327
328 pub fn streams(&self) -> &StreamTracker {
330 &self.streams
331 }
332
333 fn send(
334 &mut self,
335 call: Call,
336 deadline: Option<Duration>,
337 now: Instant,
338 resync: Option<ResyncOf>,
339 ) -> Result<u32, Error> {
340 let (capabilities, limit) = match &self.state {
341 State::Ready(n) => (n.capabilities, n.max_frame),
342 State::AwaitingHello => {
343 return Err(Error::new(
344 code::NOT_READY,
345 "the agent has not finished its handshake",
346 ));
347 }
348 State::Closed(reason) => return Err(Error::disconnected(reason)),
349 };
350 let method = call.method();
351 let needed = method.required_capability();
352 if !capabilities.contains(needed) {
353 let missing = Capabilities(needed.0 & !capabilities.0);
354 return Err(Error::capability_missing(method, missing));
355 }
356 let id = self.allocate_id();
357 let deadline_ms = deadline.map(|d| u32::try_from(d.as_millis()).unwrap_or(u32::MAX));
358 let request = Message::Request(Request {
359 id,
360 deadline_ms,
361 call,
362 });
363 write_frame(&request, limit, &mut self.outbox)
364 .map_err(|e| Error::frame_too_large(method, id, e.size as u64, e.limit))?;
365 let expires = deadline.map(|d| now + d + self.config.deadline_grace);
366 self.pending.insert(
367 id,
368 Pending {
369 method,
370 deadline_ms,
371 expires,
372 resync,
373 },
374 );
375 Ok(id)
376 }
377
378 pub fn poll_transmit(&mut self) -> Option<Vec<u8>> {
380 if self.outbox.is_empty() {
381 None
382 } else {
383 Some(std::mem::take(&mut self.outbox))
384 }
385 }
386
387 pub fn poll_event(&mut self) -> Option<HostEvent> {
388 self.events.pop_front()
389 }
390
391 pub fn negotiated(&self) -> Option<&Negotiated> {
393 match &self.state {
394 State::Ready(n) => Some(n),
395 _ => None,
396 }
397 }
398
399 pub fn is_closed(&self) -> bool {
400 matches!(self.state, State::Closed(_))
401 }
402
403 pub fn close_reason(&self) -> Option<&DisconnectReason> {
405 match &self.state {
406 State::Closed(reason) => Some(reason),
407 _ => None,
408 }
409 }
410
411 pub fn close(&mut self) {
413 if !self.is_closed() {
414 self.close_with(DisconnectReason::Closed);
415 }
416 }
417
418 fn phase(&self) -> Phase {
419 match self.state {
420 State::AwaitingHello => Phase::Handshake,
421 State::Ready(_) => Phase::Ready,
422 State::Closed(_) => Phase::Closed,
423 }
424 }
425
426 fn handle(&mut self, msg: Message, now: Instant) {
427 match (self.phase(), msg) {
428 (Phase::Handshake, Message::Hello(hello)) => self.accept_hello(hello),
429 (Phase::Ready, Message::Response(response)) => self.accept_response(response),
430 (Phase::Ready, Message::Event(event)) => self.accept_event(event, now),
431 (Phase::Ready, Message::Ping(ping)) => {
432 self.send_control(&Message::Pong(Pong { nonce: ping.nonce }));
433 }
434 (Phase::Ready, Message::Pong(_)) | (Phase::Closed, _) => {}
435 (phase, other) => {
436 let expected = if phase == Phase::Handshake {
437 "a hello"
438 } else {
439 "a response, an event, a ping or a pong"
440 };
441 self.close_with(DisconnectReason::Protocol(format!(
442 "expected {expected} from the agent, received a {}",
443 kind::name(other.kind())
444 )));
445 }
446 }
447 }
448
449 fn accept_hello(&mut self, hello: Hello) {
450 match self.negotiate(&hello) {
451 Ok(negotiated) => {
452 self.send_control(&Message::Welcome(Welcome {
453 versions: self.config.versions.clone(),
454 version: Some(negotiated.version),
455 max_frame: negotiated.max_frame,
456 error: None,
457 }));
458 self.decoder.set_limit(negotiated.max_frame);
459 self.state = State::Ready(negotiated.clone());
460 self.events.push_back(HostEvent::Ready(negotiated));
461 }
462 Err(failure) => {
463 self.send_control(&Message::Welcome(Welcome {
465 versions: self.config.versions.clone(),
466 version: None,
467 max_frame: self.config.max_frame.min(hello.max_frame),
468 error: Some(failure.to_error()),
469 }));
470 self.close_with(DisconnectReason::Handshake(failure));
471 }
472 }
473 }
474
475 fn negotiate(&self, hello: &Hello) -> Result<Negotiated, HandshakeError> {
476 if let Some(token) = &self.config.token
477 && hello.token.as_ref() != Some(token)
478 {
479 return Err(HandshakeError::TokenMismatch);
480 }
481 let version = select_version(&self.config.versions, &hello.versions).ok_or_else(|| {
482 HandshakeError::NoCommonVersion {
483 host: self.config.versions.clone(),
484 agent: hello.versions.clone(),
485 }
486 })?;
487 let max_frame = self.config.max_frame.min(hello.max_frame);
488 if max_frame < MIN_MAX_FRAME {
489 return Err(HandshakeError::FrameLimitTooSmall { limit: max_frame });
490 }
491 Ok(Negotiated {
492 version,
493 max_frame,
494 capabilities: hello.capabilities,
495 agent: hello.agent.clone(),
496 host_versions: self.config.versions.clone(),
497 agent_versions: hello.versions.clone(),
498 })
499 }
500
501 fn accept_response(&mut self, response: Response) {
502 let Some(pending) = self.pending.remove(&response.id) else {
504 return;
505 };
506 let result = if response.method == pending.method {
507 response.result
508 } else {
509 Err(Error::new(
510 code::PROTOCOL_ERROR,
511 format!(
512 "the agent answered {} request {} as {}",
513 pending.method, response.id, response.method
514 ),
515 ))
516 };
517 self.resolve(response.id, pending, result);
518 }
519
520 fn resolve(&mut self, id: u32, pending: Pending, result: Result<Reply, Error>) {
525 let Some(ResyncOf { stream, automatic }) = pending.resync else {
526 self.events.push_back(HostEvent::Response {
527 id,
528 method: pending.method,
529 result,
530 });
531 return;
532 };
533 let failures = if automatic {
534 self.in_flight.remove(&stream).unwrap_or(0)
535 } else {
536 0
537 };
538 match result {
539 Ok(Reply::Resync(ack)) if ack.stream == stream => {
540 self.streams.resynced(stream, ack.seq);
541 self.events.push_back(HostEvent::Resynced {
542 stream,
543 seq: ack.seq,
544 });
545 }
546 Ok(other) => self.events.push_back(HostEvent::ResyncFailed {
547 stream,
548 error: Error::new(
549 code::PROTOCOL_ERROR,
550 format!("the resync of the {stream} stream was answered with {other:?}"),
551 ),
552 }),
553 Err(error) if automatic && error.retryable && !self.is_closed() => {
554 self.retries.insert(
555 stream,
556 Retry {
557 failures: failures + 1,
558 at: None,
559 },
560 );
561 }
562 Err(error) => self
563 .events
564 .push_back(HostEvent::ResyncFailed { stream, error }),
565 }
566 }
567
568 fn accept_event(&mut self, event: Event, now: Instant) {
569 let stream = event.stream();
570 match self.streams.check(stream, event.seq) {
571 SeqCheck::Deliver { .. } => self.events.push_back(HostEvent::Event(event)),
572 SeqCheck::Gap { expected, received } => {
573 self.events.push_back(HostEvent::OutOfSync {
574 stream,
575 expected,
576 received,
577 });
578 self.retries.remove(&stream);
579 self.resync_automatically(stream, 0, now);
580 }
581 SeqCheck::Discard => {}
582 }
583 }
584
585 fn handle_oversized(&mut self, oversized: Oversized) {
586 if oversized.kind == Some(kind::RESPONSE)
587 && let Some(id) = oversized.id
588 && let Some(pending) = self.pending.remove(&id)
589 {
590 let error = Error::frame_too_large(
591 pending.method,
592 id,
593 u64::from(oversized.size),
594 oversized.limit,
595 );
596 self.resolve(id, pending, Err(error));
597 return;
598 }
599 self.events.push_back(HostEvent::FrameTooLarge(oversized));
600 }
601
602 fn send_control(&mut self, msg: &Message) {
605 write_frame(msg, u32::MAX, &mut self.outbox).expect("a control message fits in a frame");
606 }
607
608 fn allocate_id(&mut self) -> u32 {
609 loop {
610 let id = self.next_id;
611 self.next_id = self.next_id.wrapping_add(1);
612 if id != 0 && !self.pending.contains_key(&id) {
613 return id;
614 }
615 }
616 }
617
618 fn close_with(&mut self, reason: DisconnectReason) {
619 self.retries.clear();
620 let mut ids: Vec<u32> = self.pending.keys().copied().collect();
621 ids.sort_unstable();
622 for id in ids {
623 let pending = self.pending.remove(&id).expect("listed above");
624 self.resolve(id, pending, Err(Error::disconnected(&reason)));
625 }
626 self.state = State::Closed(reason.clone());
627 self.events.push_back(HostEvent::Closed(reason));
628 }
629}