ferron/util/
fcgi_encoder.rs

1use tokio_util::bytes::{BufMut, BytesMut};
2use tokio_util::codec::Encoder;
3
4use crate::ferron_util::fcgi_record::construct_fastcgi_record;
5
6pub struct FcgiEncoder;
7
8impl FcgiEncoder {
9  pub fn new() -> Self {
10    Self
11  }
12}
13
14impl Encoder<&[u8]> for FcgiEncoder {
15  type Error = std::io::Error;
16
17  fn encode(&mut self, item: &[u8], dst: &mut BytesMut) -> Result<(), Self::Error> {
18    let mut offset = 0;
19    let mut first_written = false;
20    while offset < item.len() || (item.is_empty() && !first_written) {
21      let chunk_size = std::cmp::min(65535, item.len() - offset);
22      let chunk = &item[offset..offset + chunk_size];
23
24      // Record type 5 means STDIN
25      let record = construct_fastcgi_record(5, 1, chunk);
26      dst.put(record.as_slice());
27
28      first_written = true;
29      offset += chunk_size;
30    }
31
32    Ok(())
33  }
34}
35
36#[cfg(test)]
37mod tests {
38  use super::*;
39  use tokio_util::codec::Encoder;
40
41  #[test]
42  fn test_fcgi_encoder() {
43    let mut encoder = FcgiEncoder::new();
44    let mut dst = BytesMut::new();
45    let item = b"Test data";
46
47    encoder.encode(item, &mut dst).unwrap();
48
49    // Expected encoded record structure
50    let expected_record = vec![
51      1, // FCGI_VERSION_1
52      5, // Record type
53      0, 1, // Request ID (big-endian)
54      0, 9, // Content length (big-endian)
55      7, // Padding length
56      0, // Reserved
57      84, 101, 115, 116, 32, // Content: "Test "
58      100, 97, 116, 97, // Content: "data"
59      0, 0, 0, 0, 0, 0, 0, // Padding
60    ];
61
62    assert_eq!(dst.to_vec(), expected_record);
63  }
64}