Skip to main content

Fragmentizer

Struct Fragmentizer 

Source
pub struct Fragmentizer { /* private fields */ }
Expand description

Splits IMAP bytes into line and literal fragments.

The Fragmentizer prevents excessive memory allocation through a configurable maximum message size. Correct fragmentation is ensured even for messages exceeding the allowed message size.

If the message size is exceeded, Fragmentizer::decode_message will fail and Fragmentizer::message_bytes will emit truncated message bytes. However, fragmentation will seamlessly continue with the following message.

Implementations§

Source§

impl Fragmentizer

Source

pub fn new(max_message_size: u32) -> Self

Creates a Fragmentizer with a maximum message size.

The maximum message size is bounded by max_message_size to prevent excessive memory allocation.

Examples found in repository?
examples/fragmentizer.rs (line 6)
5fn main() {
6    let mut fragmentizer = Fragmentizer::new(1024);
7
8    loop {
9        match fragmentizer.progress() {
10            Some(fragment_info) => {
11                println!(
12                    "[!] Fragment: {fragment_info:#?} // b\"{}\"",
13                    escape_byte_string(fragmentizer.fragment_bytes(fragment_info))
14                );
15
16                if fragmentizer.is_message_complete() {
17                    println!(
18                        "[!] Complete message: {}",
19                        escape_byte_string(fragmentizer.message_bytes())
20                    );
21                }
22            }
23            None => {
24                println!("[!] Reading stdin (ctrl+d to flush)...");
25                let mut buffer = [0; 64];
26                let count = stdin().read(&mut buffer).unwrap();
27                if count == 0 {
28                    println!("[!] Connection closed");
29                    break;
30                }
31                let chunk = &buffer[..count];
32
33                println!(
34                    "[!] Enqueueing {} byte(s) (b\"{}\")",
35                    count,
36                    escape_byte_string(chunk)
37                );
38                fragmentizer.enqueue_bytes(chunk);
39            }
40        }
41    }
42}
More examples
Hide additional examples
examples/client.rs (line 30)
27fn main() {
28    println!("{WELCOME}");
29
30    let mut fragmentizer = Fragmentizer::new(10 * 1024);
31    let mut state = State::Greeting;
32
33    loop {
34        // Progress next fragment.
35        let Some(_fragment_info) = fragmentizer.progress() else {
36            // Read more bytes ...
37            let bytes = read_more(Role::Server, fragmentizer.message_bytes().is_empty());
38
39            // ... and pass the bytes to the Fragmentizer ...
40            fragmentizer.enqueue_bytes(&bytes);
41
42            // ... and try again.
43            continue;
44        };
45
46        // Check whether the Fragmentizer detected a complete message.
47        if !fragmentizer.is_message_complete() {
48            // Read next fragment.
49            continue;
50        }
51
52        // The Fragmentizer detected a complete message.
53        match state {
54            State::Greeting => {
55                match fragmentizer.decode_message(&GreetingCodec::default()) {
56                    Ok(greeting) => {
57                        // Do something with the greeting ...
58                        println!("{greeting:#?}");
59
60                        // ... and proceed with reponses.
61                        state = State::Response;
62                    }
63                    Err(err) => {
64                        println!("Error parsing greeting: {err:?}");
65                    }
66                };
67            }
68            State::Response => {
69                match fragmentizer.decode_message(&ResponseCodec::default()) {
70                    Ok(response) => {
71                        // Do something with the response.
72                        println!("{response:#?}");
73                    }
74                    Err(err) => {
75                        println!("Error parsing response: {err:?}");
76                    }
77                };
78            }
79        };
80    }
81}
examples/server.rs (line 40)
37fn main() {
38    println!("{WELCOME}");
39
40    let mut fragmentizer = Fragmentizer::new(10 * 1024);
41    let mut state = State::Command;
42
43    // Send a greeting.
44    println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46    loop {
47        // Progress next fragment.
48        let Some(fragment_info) = fragmentizer.progress() else {
49            // Read more bytes ...
50            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52            // ... and pass the bytes to the Fragmentizer ...
53            fragmentizer.enqueue_bytes(&bytes);
54
55            // ... and try again.
56            continue;
57        };
58
59        // The Fragmentizer detected a line that announces a sync literal.
60        if let FragmentInfo::Line {
61            announcement:
62                Some(LiteralAnnouncement {
63                    mode: LiteralMode::Sync,
64                    length,
65                }),
66            ..
67        } = fragment_info
68        {
69            // Check the length of the literal.
70            if length <= 1024 {
71                // Accept the literal ...
72                println!("S: {COLOR_SERVER}+ {RESET}");
73
74                // ... and continue with the remaining message.
75                continue;
76            } else if let Some(tag) = fragmentizer.decode_tag() {
77                // Reject the literal ...
78                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80                // ... and skip the current message ...
81                fragmentizer.skip_message();
82
83                // ... and continue with the next message.
84                continue;
85            } else {
86                // The partially received message is malformed. It's unclear what will follow.
87                // To be on the safe side, prevent the message from being decoded ...
88                fragmentizer.poison_message();
89
90                // ... but continue parsing the message.
91                continue;
92            }
93        }
94
95        // Check whether the Fragmentizer detected a complete message.
96        if !fragmentizer.is_message_complete() {
97            // Read next fragment.
98            continue;
99        }
100
101        // The Fragmentizer detected a complete message.
102        match state {
103            State::Command => {
104                match fragmentizer.decode_message(&CommandCodec::default()) {
105                    Ok(Command {
106                        tag,
107                        body: CommandBody::Authenticate { .. },
108                    }) => {
109                        // Request another SASL round ...
110                        println!("S: {COLOR_SERVER}+ {RESET}");
111
112                        // ... and proceed with authenticate data.
113                        state = State::Authenticate(tag.into_static());
114                    }
115                    Ok(Command {
116                        body: CommandBody::Idle,
117                        ..
118                    }) => {
119                        // Accept the idle ...
120                        println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122                        // ... and proceed with idle done.
123                        state = State::Idle;
124                    }
125                    Ok(command) => {
126                        // Do something with the command.
127                        println!("{command:#?}");
128                    }
129                    Err(err) => {
130                        println!("Error parsing command: {err:?}");
131                    }
132                };
133            }
134            State::Authenticate(ref tag) => {
135                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136                    Ok(_authenticate_data) => {
137                        // Accept the authentication after one SASL round.
138                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140                        // ... and proceed with commands.
141                        state = State::Command;
142                    }
143                    Err(err) => {
144                        println!("Error parsing authenticate data: {err:?}");
145                    }
146                };
147            }
148            State::Idle => {
149                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150                    Ok(_idle_done) => {
151                        // End idle and proceed with commands.
152                        state = State::Command;
153                    }
154                    Err(err) => {
155                        println!("Error parsing idle done: {err:?}");
156                    }
157                };
158            }
159        }
160    }
161}
Source

pub fn without_max_message_size() -> Self

Creates a Fragmentizer without a maximum message size.

This is dangerous because it allows an attacker to allocate an excessive amount of memory by sending a huge message.
Source

pub fn progress(&mut self) -> Option<FragmentInfo>

Continue parsing the current message until the next fragment is detected.

Returns None if more bytes need to be enqueued via Fragmentizer::enqueue_bytes. If Fragmentizer::is_message_complete returns true after this function was called, then the message was fully parsed. The following call of this function will then start the next message.

Examples found in repository?
examples/fragmentizer.rs (line 9)
5fn main() {
6    let mut fragmentizer = Fragmentizer::new(1024);
7
8    loop {
9        match fragmentizer.progress() {
10            Some(fragment_info) => {
11                println!(
12                    "[!] Fragment: {fragment_info:#?} // b\"{}\"",
13                    escape_byte_string(fragmentizer.fragment_bytes(fragment_info))
14                );
15
16                if fragmentizer.is_message_complete() {
17                    println!(
18                        "[!] Complete message: {}",
19                        escape_byte_string(fragmentizer.message_bytes())
20                    );
21                }
22            }
23            None => {
24                println!("[!] Reading stdin (ctrl+d to flush)...");
25                let mut buffer = [0; 64];
26                let count = stdin().read(&mut buffer).unwrap();
27                if count == 0 {
28                    println!("[!] Connection closed");
29                    break;
30                }
31                let chunk = &buffer[..count];
32
33                println!(
34                    "[!] Enqueueing {} byte(s) (b\"{}\")",
35                    count,
36                    escape_byte_string(chunk)
37                );
38                fragmentizer.enqueue_bytes(chunk);
39            }
40        }
41    }
42}
More examples
Hide additional examples
examples/client.rs (line 35)
27fn main() {
28    println!("{WELCOME}");
29
30    let mut fragmentizer = Fragmentizer::new(10 * 1024);
31    let mut state = State::Greeting;
32
33    loop {
34        // Progress next fragment.
35        let Some(_fragment_info) = fragmentizer.progress() else {
36            // Read more bytes ...
37            let bytes = read_more(Role::Server, fragmentizer.message_bytes().is_empty());
38
39            // ... and pass the bytes to the Fragmentizer ...
40            fragmentizer.enqueue_bytes(&bytes);
41
42            // ... and try again.
43            continue;
44        };
45
46        // Check whether the Fragmentizer detected a complete message.
47        if !fragmentizer.is_message_complete() {
48            // Read next fragment.
49            continue;
50        }
51
52        // The Fragmentizer detected a complete message.
53        match state {
54            State::Greeting => {
55                match fragmentizer.decode_message(&GreetingCodec::default()) {
56                    Ok(greeting) => {
57                        // Do something with the greeting ...
58                        println!("{greeting:#?}");
59
60                        // ... and proceed with reponses.
61                        state = State::Response;
62                    }
63                    Err(err) => {
64                        println!("Error parsing greeting: {err:?}");
65                    }
66                };
67            }
68            State::Response => {
69                match fragmentizer.decode_message(&ResponseCodec::default()) {
70                    Ok(response) => {
71                        // Do something with the response.
72                        println!("{response:#?}");
73                    }
74                    Err(err) => {
75                        println!("Error parsing response: {err:?}");
76                    }
77                };
78            }
79        };
80    }
81}
examples/server.rs (line 48)
37fn main() {
38    println!("{WELCOME}");
39
40    let mut fragmentizer = Fragmentizer::new(10 * 1024);
41    let mut state = State::Command;
42
43    // Send a greeting.
44    println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46    loop {
47        // Progress next fragment.
48        let Some(fragment_info) = fragmentizer.progress() else {
49            // Read more bytes ...
50            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52            // ... and pass the bytes to the Fragmentizer ...
53            fragmentizer.enqueue_bytes(&bytes);
54
55            // ... and try again.
56            continue;
57        };
58
59        // The Fragmentizer detected a line that announces a sync literal.
60        if let FragmentInfo::Line {
61            announcement:
62                Some(LiteralAnnouncement {
63                    mode: LiteralMode::Sync,
64                    length,
65                }),
66            ..
67        } = fragment_info
68        {
69            // Check the length of the literal.
70            if length <= 1024 {
71                // Accept the literal ...
72                println!("S: {COLOR_SERVER}+ {RESET}");
73
74                // ... and continue with the remaining message.
75                continue;
76            } else if let Some(tag) = fragmentizer.decode_tag() {
77                // Reject the literal ...
78                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80                // ... and skip the current message ...
81                fragmentizer.skip_message();
82
83                // ... and continue with the next message.
84                continue;
85            } else {
86                // The partially received message is malformed. It's unclear what will follow.
87                // To be on the safe side, prevent the message from being decoded ...
88                fragmentizer.poison_message();
89
90                // ... but continue parsing the message.
91                continue;
92            }
93        }
94
95        // Check whether the Fragmentizer detected a complete message.
96        if !fragmentizer.is_message_complete() {
97            // Read next fragment.
98            continue;
99        }
100
101        // The Fragmentizer detected a complete message.
102        match state {
103            State::Command => {
104                match fragmentizer.decode_message(&CommandCodec::default()) {
105                    Ok(Command {
106                        tag,
107                        body: CommandBody::Authenticate { .. },
108                    }) => {
109                        // Request another SASL round ...
110                        println!("S: {COLOR_SERVER}+ {RESET}");
111
112                        // ... and proceed with authenticate data.
113                        state = State::Authenticate(tag.into_static());
114                    }
115                    Ok(Command {
116                        body: CommandBody::Idle,
117                        ..
118                    }) => {
119                        // Accept the idle ...
120                        println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122                        // ... and proceed with idle done.
123                        state = State::Idle;
124                    }
125                    Ok(command) => {
126                        // Do something with the command.
127                        println!("{command:#?}");
128                    }
129                    Err(err) => {
130                        println!("Error parsing command: {err:?}");
131                    }
132                };
133            }
134            State::Authenticate(ref tag) => {
135                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136                    Ok(_authenticate_data) => {
137                        // Accept the authentication after one SASL round.
138                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140                        // ... and proceed with commands.
141                        state = State::Command;
142                    }
143                    Err(err) => {
144                        println!("Error parsing authenticate data: {err:?}");
145                    }
146                };
147            }
148            State::Idle => {
149                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150                    Ok(_idle_done) => {
151                        // End idle and proceed with commands.
152                        state = State::Command;
153                    }
154                    Err(err) => {
155                        println!("Error parsing idle done: {err:?}");
156                    }
157                };
158            }
159        }
160    }
161}
Source

pub fn enqueue_bytes(&mut self, bytes: &[u8])

Enqueues more byte that can be parsed by Fragmentizer::progress.

Note that the message size limit is not enforced on the enqueued bytes. You can control the size of the enqueued bytes by only calling this function if more bytes are necessary. More bytes are necessary if Fragmentizer::progress returns None.

Examples found in repository?
examples/fragmentizer.rs (line 38)
5fn main() {
6    let mut fragmentizer = Fragmentizer::new(1024);
7
8    loop {
9        match fragmentizer.progress() {
10            Some(fragment_info) => {
11                println!(
12                    "[!] Fragment: {fragment_info:#?} // b\"{}\"",
13                    escape_byte_string(fragmentizer.fragment_bytes(fragment_info))
14                );
15
16                if fragmentizer.is_message_complete() {
17                    println!(
18                        "[!] Complete message: {}",
19                        escape_byte_string(fragmentizer.message_bytes())
20                    );
21                }
22            }
23            None => {
24                println!("[!] Reading stdin (ctrl+d to flush)...");
25                let mut buffer = [0; 64];
26                let count = stdin().read(&mut buffer).unwrap();
27                if count == 0 {
28                    println!("[!] Connection closed");
29                    break;
30                }
31                let chunk = &buffer[..count];
32
33                println!(
34                    "[!] Enqueueing {} byte(s) (b\"{}\")",
35                    count,
36                    escape_byte_string(chunk)
37                );
38                fragmentizer.enqueue_bytes(chunk);
39            }
40        }
41    }
42}
More examples
Hide additional examples
examples/client.rs (line 40)
27fn main() {
28    println!("{WELCOME}");
29
30    let mut fragmentizer = Fragmentizer::new(10 * 1024);
31    let mut state = State::Greeting;
32
33    loop {
34        // Progress next fragment.
35        let Some(_fragment_info) = fragmentizer.progress() else {
36            // Read more bytes ...
37            let bytes = read_more(Role::Server, fragmentizer.message_bytes().is_empty());
38
39            // ... and pass the bytes to the Fragmentizer ...
40            fragmentizer.enqueue_bytes(&bytes);
41
42            // ... and try again.
43            continue;
44        };
45
46        // Check whether the Fragmentizer detected a complete message.
47        if !fragmentizer.is_message_complete() {
48            // Read next fragment.
49            continue;
50        }
51
52        // The Fragmentizer detected a complete message.
53        match state {
54            State::Greeting => {
55                match fragmentizer.decode_message(&GreetingCodec::default()) {
56                    Ok(greeting) => {
57                        // Do something with the greeting ...
58                        println!("{greeting:#?}");
59
60                        // ... and proceed with reponses.
61                        state = State::Response;
62                    }
63                    Err(err) => {
64                        println!("Error parsing greeting: {err:?}");
65                    }
66                };
67            }
68            State::Response => {
69                match fragmentizer.decode_message(&ResponseCodec::default()) {
70                    Ok(response) => {
71                        // Do something with the response.
72                        println!("{response:#?}");
73                    }
74                    Err(err) => {
75                        println!("Error parsing response: {err:?}");
76                    }
77                };
78            }
79        };
80    }
81}
examples/server.rs (line 53)
37fn main() {
38    println!("{WELCOME}");
39
40    let mut fragmentizer = Fragmentizer::new(10 * 1024);
41    let mut state = State::Command;
42
43    // Send a greeting.
44    println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46    loop {
47        // Progress next fragment.
48        let Some(fragment_info) = fragmentizer.progress() else {
49            // Read more bytes ...
50            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52            // ... and pass the bytes to the Fragmentizer ...
53            fragmentizer.enqueue_bytes(&bytes);
54
55            // ... and try again.
56            continue;
57        };
58
59        // The Fragmentizer detected a line that announces a sync literal.
60        if let FragmentInfo::Line {
61            announcement:
62                Some(LiteralAnnouncement {
63                    mode: LiteralMode::Sync,
64                    length,
65                }),
66            ..
67        } = fragment_info
68        {
69            // Check the length of the literal.
70            if length <= 1024 {
71                // Accept the literal ...
72                println!("S: {COLOR_SERVER}+ {RESET}");
73
74                // ... and continue with the remaining message.
75                continue;
76            } else if let Some(tag) = fragmentizer.decode_tag() {
77                // Reject the literal ...
78                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80                // ... and skip the current message ...
81                fragmentizer.skip_message();
82
83                // ... and continue with the next message.
84                continue;
85            } else {
86                // The partially received message is malformed. It's unclear what will follow.
87                // To be on the safe side, prevent the message from being decoded ...
88                fragmentizer.poison_message();
89
90                // ... but continue parsing the message.
91                continue;
92            }
93        }
94
95        // Check whether the Fragmentizer detected a complete message.
96        if !fragmentizer.is_message_complete() {
97            // Read next fragment.
98            continue;
99        }
100
101        // The Fragmentizer detected a complete message.
102        match state {
103            State::Command => {
104                match fragmentizer.decode_message(&CommandCodec::default()) {
105                    Ok(Command {
106                        tag,
107                        body: CommandBody::Authenticate { .. },
108                    }) => {
109                        // Request another SASL round ...
110                        println!("S: {COLOR_SERVER}+ {RESET}");
111
112                        // ... and proceed with authenticate data.
113                        state = State::Authenticate(tag.into_static());
114                    }
115                    Ok(Command {
116                        body: CommandBody::Idle,
117                        ..
118                    }) => {
119                        // Accept the idle ...
120                        println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122                        // ... and proceed with idle done.
123                        state = State::Idle;
124                    }
125                    Ok(command) => {
126                        // Do something with the command.
127                        println!("{command:#?}");
128                    }
129                    Err(err) => {
130                        println!("Error parsing command: {err:?}");
131                    }
132                };
133            }
134            State::Authenticate(ref tag) => {
135                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136                    Ok(_authenticate_data) => {
137                        // Accept the authentication after one SASL round.
138                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140                        // ... and proceed with commands.
141                        state = State::Command;
142                    }
143                    Err(err) => {
144                        println!("Error parsing authenticate data: {err:?}");
145                    }
146                };
147            }
148            State::Idle => {
149                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150                    Ok(_idle_done) => {
151                        // End idle and proceed with commands.
152                        state = State::Command;
153                    }
154                    Err(err) => {
155                        println!("Error parsing idle done: {err:?}");
156                    }
157                };
158            }
159        }
160    }
161}
Source

pub fn fragment_bytes(&self, fragment_info: FragmentInfo) -> &[u8]

Returns the bytes for a fragment of the current message.

Examples found in repository?
examples/fragmentizer.rs (line 13)
5fn main() {
6    let mut fragmentizer = Fragmentizer::new(1024);
7
8    loop {
9        match fragmentizer.progress() {
10            Some(fragment_info) => {
11                println!(
12                    "[!] Fragment: {fragment_info:#?} // b\"{}\"",
13                    escape_byte_string(fragmentizer.fragment_bytes(fragment_info))
14                );
15
16                if fragmentizer.is_message_complete() {
17                    println!(
18                        "[!] Complete message: {}",
19                        escape_byte_string(fragmentizer.message_bytes())
20                    );
21                }
22            }
23            None => {
24                println!("[!] Reading stdin (ctrl+d to flush)...");
25                let mut buffer = [0; 64];
26                let count = stdin().read(&mut buffer).unwrap();
27                if count == 0 {
28                    println!("[!] Connection closed");
29                    break;
30                }
31                let chunk = &buffer[..count];
32
33                println!(
34                    "[!] Enqueueing {} byte(s) (b\"{}\")",
35                    count,
36                    escape_byte_string(chunk)
37                );
38                fragmentizer.enqueue_bytes(chunk);
39            }
40        }
41    }
42}
Source

pub fn is_message_complete(&self) -> bool

Returns whether the current message was fully parsed.

If it returns true then it makes sense to call Fragmentizer::decode_message to decode the message. Alternatively, you can access all bytes of the message via Fragmentizer::message_bytes.

Examples found in repository?
examples/fragmentizer.rs (line 16)
5fn main() {
6    let mut fragmentizer = Fragmentizer::new(1024);
7
8    loop {
9        match fragmentizer.progress() {
10            Some(fragment_info) => {
11                println!(
12                    "[!] Fragment: {fragment_info:#?} // b\"{}\"",
13                    escape_byte_string(fragmentizer.fragment_bytes(fragment_info))
14                );
15
16                if fragmentizer.is_message_complete() {
17                    println!(
18                        "[!] Complete message: {}",
19                        escape_byte_string(fragmentizer.message_bytes())
20                    );
21                }
22            }
23            None => {
24                println!("[!] Reading stdin (ctrl+d to flush)...");
25                let mut buffer = [0; 64];
26                let count = stdin().read(&mut buffer).unwrap();
27                if count == 0 {
28                    println!("[!] Connection closed");
29                    break;
30                }
31                let chunk = &buffer[..count];
32
33                println!(
34                    "[!] Enqueueing {} byte(s) (b\"{}\")",
35                    count,
36                    escape_byte_string(chunk)
37                );
38                fragmentizer.enqueue_bytes(chunk);
39            }
40        }
41    }
42}
More examples
Hide additional examples
examples/client.rs (line 47)
27fn main() {
28    println!("{WELCOME}");
29
30    let mut fragmentizer = Fragmentizer::new(10 * 1024);
31    let mut state = State::Greeting;
32
33    loop {
34        // Progress next fragment.
35        let Some(_fragment_info) = fragmentizer.progress() else {
36            // Read more bytes ...
37            let bytes = read_more(Role::Server, fragmentizer.message_bytes().is_empty());
38
39            // ... and pass the bytes to the Fragmentizer ...
40            fragmentizer.enqueue_bytes(&bytes);
41
42            // ... and try again.
43            continue;
44        };
45
46        // Check whether the Fragmentizer detected a complete message.
47        if !fragmentizer.is_message_complete() {
48            // Read next fragment.
49            continue;
50        }
51
52        // The Fragmentizer detected a complete message.
53        match state {
54            State::Greeting => {
55                match fragmentizer.decode_message(&GreetingCodec::default()) {
56                    Ok(greeting) => {
57                        // Do something with the greeting ...
58                        println!("{greeting:#?}");
59
60                        // ... and proceed with reponses.
61                        state = State::Response;
62                    }
63                    Err(err) => {
64                        println!("Error parsing greeting: {err:?}");
65                    }
66                };
67            }
68            State::Response => {
69                match fragmentizer.decode_message(&ResponseCodec::default()) {
70                    Ok(response) => {
71                        // Do something with the response.
72                        println!("{response:#?}");
73                    }
74                    Err(err) => {
75                        println!("Error parsing response: {err:?}");
76                    }
77                };
78            }
79        };
80    }
81}
examples/server.rs (line 96)
37fn main() {
38    println!("{WELCOME}");
39
40    let mut fragmentizer = Fragmentizer::new(10 * 1024);
41    let mut state = State::Command;
42
43    // Send a greeting.
44    println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46    loop {
47        // Progress next fragment.
48        let Some(fragment_info) = fragmentizer.progress() else {
49            // Read more bytes ...
50            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52            // ... and pass the bytes to the Fragmentizer ...
53            fragmentizer.enqueue_bytes(&bytes);
54
55            // ... and try again.
56            continue;
57        };
58
59        // The Fragmentizer detected a line that announces a sync literal.
60        if let FragmentInfo::Line {
61            announcement:
62                Some(LiteralAnnouncement {
63                    mode: LiteralMode::Sync,
64                    length,
65                }),
66            ..
67        } = fragment_info
68        {
69            // Check the length of the literal.
70            if length <= 1024 {
71                // Accept the literal ...
72                println!("S: {COLOR_SERVER}+ {RESET}");
73
74                // ... and continue with the remaining message.
75                continue;
76            } else if let Some(tag) = fragmentizer.decode_tag() {
77                // Reject the literal ...
78                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80                // ... and skip the current message ...
81                fragmentizer.skip_message();
82
83                // ... and continue with the next message.
84                continue;
85            } else {
86                // The partially received message is malformed. It's unclear what will follow.
87                // To be on the safe side, prevent the message from being decoded ...
88                fragmentizer.poison_message();
89
90                // ... but continue parsing the message.
91                continue;
92            }
93        }
94
95        // Check whether the Fragmentizer detected a complete message.
96        if !fragmentizer.is_message_complete() {
97            // Read next fragment.
98            continue;
99        }
100
101        // The Fragmentizer detected a complete message.
102        match state {
103            State::Command => {
104                match fragmentizer.decode_message(&CommandCodec::default()) {
105                    Ok(Command {
106                        tag,
107                        body: CommandBody::Authenticate { .. },
108                    }) => {
109                        // Request another SASL round ...
110                        println!("S: {COLOR_SERVER}+ {RESET}");
111
112                        // ... and proceed with authenticate data.
113                        state = State::Authenticate(tag.into_static());
114                    }
115                    Ok(Command {
116                        body: CommandBody::Idle,
117                        ..
118                    }) => {
119                        // Accept the idle ...
120                        println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122                        // ... and proceed with idle done.
123                        state = State::Idle;
124                    }
125                    Ok(command) => {
126                        // Do something with the command.
127                        println!("{command:#?}");
128                    }
129                    Err(err) => {
130                        println!("Error parsing command: {err:?}");
131                    }
132                };
133            }
134            State::Authenticate(ref tag) => {
135                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136                    Ok(_authenticate_data) => {
137                        // Accept the authentication after one SASL round.
138                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140                        // ... and proceed with commands.
141                        state = State::Command;
142                    }
143                    Err(err) => {
144                        println!("Error parsing authenticate data: {err:?}");
145                    }
146                };
147            }
148            State::Idle => {
149                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150                    Ok(_idle_done) => {
151                        // End idle and proceed with commands.
152                        state = State::Command;
153                    }
154                    Err(err) => {
155                        println!("Error parsing idle done: {err:?}");
156                    }
157                };
158            }
159        }
160    }
161}
Source

pub fn message_bytes(&self) -> &[u8]

Returns the bytes of the current message.

Note that the bytes might be incomplete:

Examples found in repository?
examples/fragmentizer.rs (line 19)
5fn main() {
6    let mut fragmentizer = Fragmentizer::new(1024);
7
8    loop {
9        match fragmentizer.progress() {
10            Some(fragment_info) => {
11                println!(
12                    "[!] Fragment: {fragment_info:#?} // b\"{}\"",
13                    escape_byte_string(fragmentizer.fragment_bytes(fragment_info))
14                );
15
16                if fragmentizer.is_message_complete() {
17                    println!(
18                        "[!] Complete message: {}",
19                        escape_byte_string(fragmentizer.message_bytes())
20                    );
21                }
22            }
23            None => {
24                println!("[!] Reading stdin (ctrl+d to flush)...");
25                let mut buffer = [0; 64];
26                let count = stdin().read(&mut buffer).unwrap();
27                if count == 0 {
28                    println!("[!] Connection closed");
29                    break;
30                }
31                let chunk = &buffer[..count];
32
33                println!(
34                    "[!] Enqueueing {} byte(s) (b\"{}\")",
35                    count,
36                    escape_byte_string(chunk)
37                );
38                fragmentizer.enqueue_bytes(chunk);
39            }
40        }
41    }
42}
More examples
Hide additional examples
examples/client.rs (line 37)
27fn main() {
28    println!("{WELCOME}");
29
30    let mut fragmentizer = Fragmentizer::new(10 * 1024);
31    let mut state = State::Greeting;
32
33    loop {
34        // Progress next fragment.
35        let Some(_fragment_info) = fragmentizer.progress() else {
36            // Read more bytes ...
37            let bytes = read_more(Role::Server, fragmentizer.message_bytes().is_empty());
38
39            // ... and pass the bytes to the Fragmentizer ...
40            fragmentizer.enqueue_bytes(&bytes);
41
42            // ... and try again.
43            continue;
44        };
45
46        // Check whether the Fragmentizer detected a complete message.
47        if !fragmentizer.is_message_complete() {
48            // Read next fragment.
49            continue;
50        }
51
52        // The Fragmentizer detected a complete message.
53        match state {
54            State::Greeting => {
55                match fragmentizer.decode_message(&GreetingCodec::default()) {
56                    Ok(greeting) => {
57                        // Do something with the greeting ...
58                        println!("{greeting:#?}");
59
60                        // ... and proceed with reponses.
61                        state = State::Response;
62                    }
63                    Err(err) => {
64                        println!("Error parsing greeting: {err:?}");
65                    }
66                };
67            }
68            State::Response => {
69                match fragmentizer.decode_message(&ResponseCodec::default()) {
70                    Ok(response) => {
71                        // Do something with the response.
72                        println!("{response:#?}");
73                    }
74                    Err(err) => {
75                        println!("Error parsing response: {err:?}");
76                    }
77                };
78            }
79        };
80    }
81}
examples/server.rs (line 50)
37fn main() {
38    println!("{WELCOME}");
39
40    let mut fragmentizer = Fragmentizer::new(10 * 1024);
41    let mut state = State::Command;
42
43    // Send a greeting.
44    println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46    loop {
47        // Progress next fragment.
48        let Some(fragment_info) = fragmentizer.progress() else {
49            // Read more bytes ...
50            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52            // ... and pass the bytes to the Fragmentizer ...
53            fragmentizer.enqueue_bytes(&bytes);
54
55            // ... and try again.
56            continue;
57        };
58
59        // The Fragmentizer detected a line that announces a sync literal.
60        if let FragmentInfo::Line {
61            announcement:
62                Some(LiteralAnnouncement {
63                    mode: LiteralMode::Sync,
64                    length,
65                }),
66            ..
67        } = fragment_info
68        {
69            // Check the length of the literal.
70            if length <= 1024 {
71                // Accept the literal ...
72                println!("S: {COLOR_SERVER}+ {RESET}");
73
74                // ... and continue with the remaining message.
75                continue;
76            } else if let Some(tag) = fragmentizer.decode_tag() {
77                // Reject the literal ...
78                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80                // ... and skip the current message ...
81                fragmentizer.skip_message();
82
83                // ... and continue with the next message.
84                continue;
85            } else {
86                // The partially received message is malformed. It's unclear what will follow.
87                // To be on the safe side, prevent the message from being decoded ...
88                fragmentizer.poison_message();
89
90                // ... but continue parsing the message.
91                continue;
92            }
93        }
94
95        // Check whether the Fragmentizer detected a complete message.
96        if !fragmentizer.is_message_complete() {
97            // Read next fragment.
98            continue;
99        }
100
101        // The Fragmentizer detected a complete message.
102        match state {
103            State::Command => {
104                match fragmentizer.decode_message(&CommandCodec::default()) {
105                    Ok(Command {
106                        tag,
107                        body: CommandBody::Authenticate { .. },
108                    }) => {
109                        // Request another SASL round ...
110                        println!("S: {COLOR_SERVER}+ {RESET}");
111
112                        // ... and proceed with authenticate data.
113                        state = State::Authenticate(tag.into_static());
114                    }
115                    Ok(Command {
116                        body: CommandBody::Idle,
117                        ..
118                    }) => {
119                        // Accept the idle ...
120                        println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122                        // ... and proceed with idle done.
123                        state = State::Idle;
124                    }
125                    Ok(command) => {
126                        // Do something with the command.
127                        println!("{command:#?}");
128                    }
129                    Err(err) => {
130                        println!("Error parsing command: {err:?}");
131                    }
132                };
133            }
134            State::Authenticate(ref tag) => {
135                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136                    Ok(_authenticate_data) => {
137                        // Accept the authentication after one SASL round.
138                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140                        // ... and proceed with commands.
141                        state = State::Command;
142                    }
143                    Err(err) => {
144                        println!("Error parsing authenticate data: {err:?}");
145                    }
146                };
147            }
148            State::Idle => {
149                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150                    Ok(_idle_done) => {
151                        // End idle and proceed with commands.
152                        state = State::Command;
153                    }
154                    Err(err) => {
155                        println!("Error parsing idle done: {err:?}");
156                    }
157                };
158            }
159        }
160    }
161}
Source

pub fn is_max_message_size_exceeded(&self) -> bool

Returns whether the size limit is exceeded for the current message.

Source

pub fn is_message_poisoned(&self) -> bool

Returns whether the current message was explicitly poisoned to prevent decoding.

Source

pub fn skip_message(&mut self)

Skips the current message and starts the next message immediately.

Warning: Using this method might be dangerous. If client and server don’t agree at which point a message is skipped, then the client or server might treat untrusted bytes (e.g. literal bytes) as IMAP messages. Currently the only valid use-case is a server that rejects synchronizing literals from the client. Otherwise consider using Fragmentizer::poison_message.

Examples found in repository?
examples/server.rs (line 81)
37fn main() {
38    println!("{WELCOME}");
39
40    let mut fragmentizer = Fragmentizer::new(10 * 1024);
41    let mut state = State::Command;
42
43    // Send a greeting.
44    println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46    loop {
47        // Progress next fragment.
48        let Some(fragment_info) = fragmentizer.progress() else {
49            // Read more bytes ...
50            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52            // ... and pass the bytes to the Fragmentizer ...
53            fragmentizer.enqueue_bytes(&bytes);
54
55            // ... and try again.
56            continue;
57        };
58
59        // The Fragmentizer detected a line that announces a sync literal.
60        if let FragmentInfo::Line {
61            announcement:
62                Some(LiteralAnnouncement {
63                    mode: LiteralMode::Sync,
64                    length,
65                }),
66            ..
67        } = fragment_info
68        {
69            // Check the length of the literal.
70            if length <= 1024 {
71                // Accept the literal ...
72                println!("S: {COLOR_SERVER}+ {RESET}");
73
74                // ... and continue with the remaining message.
75                continue;
76            } else if let Some(tag) = fragmentizer.decode_tag() {
77                // Reject the literal ...
78                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80                // ... and skip the current message ...
81                fragmentizer.skip_message();
82
83                // ... and continue with the next message.
84                continue;
85            } else {
86                // The partially received message is malformed. It's unclear what will follow.
87                // To be on the safe side, prevent the message from being decoded ...
88                fragmentizer.poison_message();
89
90                // ... but continue parsing the message.
91                continue;
92            }
93        }
94
95        // Check whether the Fragmentizer detected a complete message.
96        if !fragmentizer.is_message_complete() {
97            // Read next fragment.
98            continue;
99        }
100
101        // The Fragmentizer detected a complete message.
102        match state {
103            State::Command => {
104                match fragmentizer.decode_message(&CommandCodec::default()) {
105                    Ok(Command {
106                        tag,
107                        body: CommandBody::Authenticate { .. },
108                    }) => {
109                        // Request another SASL round ...
110                        println!("S: {COLOR_SERVER}+ {RESET}");
111
112                        // ... and proceed with authenticate data.
113                        state = State::Authenticate(tag.into_static());
114                    }
115                    Ok(Command {
116                        body: CommandBody::Idle,
117                        ..
118                    }) => {
119                        // Accept the idle ...
120                        println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122                        // ... and proceed with idle done.
123                        state = State::Idle;
124                    }
125                    Ok(command) => {
126                        // Do something with the command.
127                        println!("{command:#?}");
128                    }
129                    Err(err) => {
130                        println!("Error parsing command: {err:?}");
131                    }
132                };
133            }
134            State::Authenticate(ref tag) => {
135                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136                    Ok(_authenticate_data) => {
137                        // Accept the authentication after one SASL round.
138                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140                        // ... and proceed with commands.
141                        state = State::Command;
142                    }
143                    Err(err) => {
144                        println!("Error parsing authenticate data: {err:?}");
145                    }
146                };
147            }
148            State::Idle => {
149                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150                    Ok(_idle_done) => {
151                        // End idle and proceed with commands.
152                        state = State::Command;
153                    }
154                    Err(err) => {
155                        println!("Error parsing idle done: {err:?}");
156                    }
157                };
158            }
159        }
160    }
161}
Source

pub fn poison_message(&mut self)

Poisons the current message to prevent its decoding.

When this function is called the fragments of the current message are parsed normally, but Fragmentizer::decode_message is guaranteed to fail and return DecodeMessageError::MessagePoisoned. This allows to skip malformed messages (e.g. a message with an unexpected line ending) safely without the risk of treating untrusted bytes (e.g. literal bytes) as IMAP messages

Examples found in repository?
examples/server.rs (line 88)
37fn main() {
38    println!("{WELCOME}");
39
40    let mut fragmentizer = Fragmentizer::new(10 * 1024);
41    let mut state = State::Command;
42
43    // Send a greeting.
44    println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46    loop {
47        // Progress next fragment.
48        let Some(fragment_info) = fragmentizer.progress() else {
49            // Read more bytes ...
50            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52            // ... and pass the bytes to the Fragmentizer ...
53            fragmentizer.enqueue_bytes(&bytes);
54
55            // ... and try again.
56            continue;
57        };
58
59        // The Fragmentizer detected a line that announces a sync literal.
60        if let FragmentInfo::Line {
61            announcement:
62                Some(LiteralAnnouncement {
63                    mode: LiteralMode::Sync,
64                    length,
65                }),
66            ..
67        } = fragment_info
68        {
69            // Check the length of the literal.
70            if length <= 1024 {
71                // Accept the literal ...
72                println!("S: {COLOR_SERVER}+ {RESET}");
73
74                // ... and continue with the remaining message.
75                continue;
76            } else if let Some(tag) = fragmentizer.decode_tag() {
77                // Reject the literal ...
78                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80                // ... and skip the current message ...
81                fragmentizer.skip_message();
82
83                // ... and continue with the next message.
84                continue;
85            } else {
86                // The partially received message is malformed. It's unclear what will follow.
87                // To be on the safe side, prevent the message from being decoded ...
88                fragmentizer.poison_message();
89
90                // ... but continue parsing the message.
91                continue;
92            }
93        }
94
95        // Check whether the Fragmentizer detected a complete message.
96        if !fragmentizer.is_message_complete() {
97            // Read next fragment.
98            continue;
99        }
100
101        // The Fragmentizer detected a complete message.
102        match state {
103            State::Command => {
104                match fragmentizer.decode_message(&CommandCodec::default()) {
105                    Ok(Command {
106                        tag,
107                        body: CommandBody::Authenticate { .. },
108                    }) => {
109                        // Request another SASL round ...
110                        println!("S: {COLOR_SERVER}+ {RESET}");
111
112                        // ... and proceed with authenticate data.
113                        state = State::Authenticate(tag.into_static());
114                    }
115                    Ok(Command {
116                        body: CommandBody::Idle,
117                        ..
118                    }) => {
119                        // Accept the idle ...
120                        println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122                        // ... and proceed with idle done.
123                        state = State::Idle;
124                    }
125                    Ok(command) => {
126                        // Do something with the command.
127                        println!("{command:#?}");
128                    }
129                    Err(err) => {
130                        println!("Error parsing command: {err:?}");
131                    }
132                };
133            }
134            State::Authenticate(ref tag) => {
135                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136                    Ok(_authenticate_data) => {
137                        // Accept the authentication after one SASL round.
138                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140                        // ... and proceed with commands.
141                        state = State::Command;
142                    }
143                    Err(err) => {
144                        println!("Error parsing authenticate data: {err:?}");
145                    }
146                };
147            }
148            State::Idle => {
149                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150                    Ok(_idle_done) => {
151                        // End idle and proceed with commands.
152                        state = State::Command;
153                    }
154                    Err(err) => {
155                        println!("Error parsing idle done: {err:?}");
156                    }
157                };
158            }
159        }
160    }
161}
Source

pub fn decode_tag(&self) -> Option<Tag<'_>>

Tries to decode the Tag for the current message.

Note that decoding the Tag is on best effort basis. Some message types don’t have a Tag and without context you can’t know whether this function will succeed. However, this function is useful if the message is incomplete or malformed and you want to decode the Tag in order to send a response.

Examples found in repository?
examples/server.rs (line 76)
37fn main() {
38    println!("{WELCOME}");
39
40    let mut fragmentizer = Fragmentizer::new(10 * 1024);
41    let mut state = State::Command;
42
43    // Send a greeting.
44    println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46    loop {
47        // Progress next fragment.
48        let Some(fragment_info) = fragmentizer.progress() else {
49            // Read more bytes ...
50            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52            // ... and pass the bytes to the Fragmentizer ...
53            fragmentizer.enqueue_bytes(&bytes);
54
55            // ... and try again.
56            continue;
57        };
58
59        // The Fragmentizer detected a line that announces a sync literal.
60        if let FragmentInfo::Line {
61            announcement:
62                Some(LiteralAnnouncement {
63                    mode: LiteralMode::Sync,
64                    length,
65                }),
66            ..
67        } = fragment_info
68        {
69            // Check the length of the literal.
70            if length <= 1024 {
71                // Accept the literal ...
72                println!("S: {COLOR_SERVER}+ {RESET}");
73
74                // ... and continue with the remaining message.
75                continue;
76            } else if let Some(tag) = fragmentizer.decode_tag() {
77                // Reject the literal ...
78                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80                // ... and skip the current message ...
81                fragmentizer.skip_message();
82
83                // ... and continue with the next message.
84                continue;
85            } else {
86                // The partially received message is malformed. It's unclear what will follow.
87                // To be on the safe side, prevent the message from being decoded ...
88                fragmentizer.poison_message();
89
90                // ... but continue parsing the message.
91                continue;
92            }
93        }
94
95        // Check whether the Fragmentizer detected a complete message.
96        if !fragmentizer.is_message_complete() {
97            // Read next fragment.
98            continue;
99        }
100
101        // The Fragmentizer detected a complete message.
102        match state {
103            State::Command => {
104                match fragmentizer.decode_message(&CommandCodec::default()) {
105                    Ok(Command {
106                        tag,
107                        body: CommandBody::Authenticate { .. },
108                    }) => {
109                        // Request another SASL round ...
110                        println!("S: {COLOR_SERVER}+ {RESET}");
111
112                        // ... and proceed with authenticate data.
113                        state = State::Authenticate(tag.into_static());
114                    }
115                    Ok(Command {
116                        body: CommandBody::Idle,
117                        ..
118                    }) => {
119                        // Accept the idle ...
120                        println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122                        // ... and proceed with idle done.
123                        state = State::Idle;
124                    }
125                    Ok(command) => {
126                        // Do something with the command.
127                        println!("{command:#?}");
128                    }
129                    Err(err) => {
130                        println!("Error parsing command: {err:?}");
131                    }
132                };
133            }
134            State::Authenticate(ref tag) => {
135                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136                    Ok(_authenticate_data) => {
137                        // Accept the authentication after one SASL round.
138                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140                        // ... and proceed with commands.
141                        state = State::Command;
142                    }
143                    Err(err) => {
144                        println!("Error parsing authenticate data: {err:?}");
145                    }
146                };
147            }
148            State::Idle => {
149                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150                    Ok(_idle_done) => {
151                        // End idle and proceed with commands.
152                        state = State::Command;
153                    }
154                    Err(err) => {
155                        println!("Error parsing idle done: {err:?}");
156                    }
157                };
158            }
159        }
160    }
161}
Source

pub fn decode_message<'a, C: Decoder>( &'a self, codec: &C, ) -> Result<C::Message<'a>, DecodeMessageError<'a, C>>

Tries to decode the current message with the given decoder.

You usually want to call this method once Fragmentizer::is_message_complete returns true. Which decoder should be used depends on the state of the IMAP conversation. The caller is responsible for tracking this state and choosing the decoder.

Examples found in repository?
examples/client.rs (line 55)
27fn main() {
28    println!("{WELCOME}");
29
30    let mut fragmentizer = Fragmentizer::new(10 * 1024);
31    let mut state = State::Greeting;
32
33    loop {
34        // Progress next fragment.
35        let Some(_fragment_info) = fragmentizer.progress() else {
36            // Read more bytes ...
37            let bytes = read_more(Role::Server, fragmentizer.message_bytes().is_empty());
38
39            // ... and pass the bytes to the Fragmentizer ...
40            fragmentizer.enqueue_bytes(&bytes);
41
42            // ... and try again.
43            continue;
44        };
45
46        // Check whether the Fragmentizer detected a complete message.
47        if !fragmentizer.is_message_complete() {
48            // Read next fragment.
49            continue;
50        }
51
52        // The Fragmentizer detected a complete message.
53        match state {
54            State::Greeting => {
55                match fragmentizer.decode_message(&GreetingCodec::default()) {
56                    Ok(greeting) => {
57                        // Do something with the greeting ...
58                        println!("{greeting:#?}");
59
60                        // ... and proceed with reponses.
61                        state = State::Response;
62                    }
63                    Err(err) => {
64                        println!("Error parsing greeting: {err:?}");
65                    }
66                };
67            }
68            State::Response => {
69                match fragmentizer.decode_message(&ResponseCodec::default()) {
70                    Ok(response) => {
71                        // Do something with the response.
72                        println!("{response:#?}");
73                    }
74                    Err(err) => {
75                        println!("Error parsing response: {err:?}");
76                    }
77                };
78            }
79        };
80    }
81}
More examples
Hide additional examples
examples/server.rs (line 104)
37fn main() {
38    println!("{WELCOME}");
39
40    let mut fragmentizer = Fragmentizer::new(10 * 1024);
41    let mut state = State::Command;
42
43    // Send a greeting.
44    println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46    loop {
47        // Progress next fragment.
48        let Some(fragment_info) = fragmentizer.progress() else {
49            // Read more bytes ...
50            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52            // ... and pass the bytes to the Fragmentizer ...
53            fragmentizer.enqueue_bytes(&bytes);
54
55            // ... and try again.
56            continue;
57        };
58
59        // The Fragmentizer detected a line that announces a sync literal.
60        if let FragmentInfo::Line {
61            announcement:
62                Some(LiteralAnnouncement {
63                    mode: LiteralMode::Sync,
64                    length,
65                }),
66            ..
67        } = fragment_info
68        {
69            // Check the length of the literal.
70            if length <= 1024 {
71                // Accept the literal ...
72                println!("S: {COLOR_SERVER}+ {RESET}");
73
74                // ... and continue with the remaining message.
75                continue;
76            } else if let Some(tag) = fragmentizer.decode_tag() {
77                // Reject the literal ...
78                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80                // ... and skip the current message ...
81                fragmentizer.skip_message();
82
83                // ... and continue with the next message.
84                continue;
85            } else {
86                // The partially received message is malformed. It's unclear what will follow.
87                // To be on the safe side, prevent the message from being decoded ...
88                fragmentizer.poison_message();
89
90                // ... but continue parsing the message.
91                continue;
92            }
93        }
94
95        // Check whether the Fragmentizer detected a complete message.
96        if !fragmentizer.is_message_complete() {
97            // Read next fragment.
98            continue;
99        }
100
101        // The Fragmentizer detected a complete message.
102        match state {
103            State::Command => {
104                match fragmentizer.decode_message(&CommandCodec::default()) {
105                    Ok(Command {
106                        tag,
107                        body: CommandBody::Authenticate { .. },
108                    }) => {
109                        // Request another SASL round ...
110                        println!("S: {COLOR_SERVER}+ {RESET}");
111
112                        // ... and proceed with authenticate data.
113                        state = State::Authenticate(tag.into_static());
114                    }
115                    Ok(Command {
116                        body: CommandBody::Idle,
117                        ..
118                    }) => {
119                        // Accept the idle ...
120                        println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122                        // ... and proceed with idle done.
123                        state = State::Idle;
124                    }
125                    Ok(command) => {
126                        // Do something with the command.
127                        println!("{command:#?}");
128                    }
129                    Err(err) => {
130                        println!("Error parsing command: {err:?}");
131                    }
132                };
133            }
134            State::Authenticate(ref tag) => {
135                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136                    Ok(_authenticate_data) => {
137                        // Accept the authentication after one SASL round.
138                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140                        // ... and proceed with commands.
141                        state = State::Command;
142                    }
143                    Err(err) => {
144                        println!("Error parsing authenticate data: {err:?}");
145                    }
146                };
147            }
148            State::Idle => {
149                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150                    Ok(_idle_done) => {
151                        // End idle and proceed with commands.
152                        state = State::Command;
153                    }
154                    Err(err) => {
155                        println!("Error parsing idle done: {err:?}");
156                    }
157                };
158            }
159        }
160    }
161}

Trait Implementations§

Source§

impl Clone for Fragmentizer

Source§

fn clone(&self) -> Fragmentizer

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Fragmentizer

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.