Skip to main content

sentinel_driver/protocol/
codec.rs

1use bytes::BytesMut;
2
3use crate::error::{Error, Result};
4use crate::protocol::backend::BackendMessage;
5
6/// Minimum header size: type (1) + length (4) = 5 bytes.
7const HEADER_LEN: usize = 5;
8
9/// Attempts to decode a single backend message from `buf`.
10///
11/// Returns `Ok(Some(msg))` if a complete message was decoded (consuming it from `buf`),
12/// `Ok(None)` if more data is needed, or `Err` on protocol violation.
13pub fn decode_message(buf: &mut BytesMut) -> Result<Option<BackendMessage>> {
14    if buf.len() < HEADER_LEN {
15        return Ok(None);
16    }
17
18    let msg_type = buf[0];
19    let body_len = i32::from_be_bytes([buf[1], buf[2], buf[3], buf[4]]);
20
21    if body_len < 4 {
22        return Err(Error::protocol(format!(
23            "invalid message length: {body_len}"
24        )));
25    }
26
27    let total_len = 1 + body_len as usize; // type byte + declared length
28
29    if buf.len() < total_len {
30        // Need more data. Reserve space to avoid repeated allocations.
31        buf.reserve(total_len - buf.len());
32        return Ok(None);
33    }
34
35    // Consume the entire message frame from the buffer.
36    let frame = buf.split_to(total_len).freeze();
37
38    // Body is everything after type(1) + length(4).
39    let body = frame.slice(HEADER_LEN..);
40
41    super::backend::decode(msg_type, body).map(Some)
42}
43
44/// The initial response to an SSLRequest: 'S' (supports) or 'N' (doesn't).
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum SslResponse {
47    Accepted,
48    Rejected,
49}
50
51/// Decode the single-byte SSL response.
52pub fn decode_ssl_response(buf: &mut BytesMut) -> Option<SslResponse> {
53    if buf.is_empty() {
54        return None;
55    }
56    let byte = buf.split_to(1)[0];
57    match byte {
58        b'S' => Some(SslResponse::Accepted),
59        b'N' => Some(SslResponse::Rejected),
60        _ => None,
61    }
62}