gproxy_transform/transform/common/
sse.rs1#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct SseFrame {
7 pub event: Option<String>,
8 pub data: String,
9}
10
11impl SseFrame {
12 pub fn data(data: impl Into<String>) -> Self {
13 Self {
14 event: None,
15 data: data.into(),
16 }
17 }
18
19 pub fn event(event: impl Into<String>, data: impl Into<String>) -> Self {
20 Self {
21 event: Some(event.into()),
22 data: data.into(),
23 }
24 }
25
26 pub fn encode(&self) -> String {
27 let mut encoded = String::new();
28 if let Some(event) = &self.event {
29 encoded.push_str("event: ");
30 encoded.push_str(event);
31 encoded.push('\n');
32 }
33 for line in self.data.lines() {
34 encoded.push_str("data: ");
35 encoded.push_str(line);
36 encoded.push('\n');
37 }
38 encoded.push('\n');
39 encoded
40 }
41}
42
43#[derive(Debug, Default)]
48pub struct SseDecoder {
49 buf: String,
50 utf8: super::utf8::Utf8StreamDecoder,
51 limits: SseLimits,
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub struct SseLimits {
57 pub max_frame_bytes: usize,
59 pub max_buffer_bytes: usize,
61}
62
63impl Default for SseLimits {
64 fn default() -> Self {
65 Self {
66 max_frame_bytes: 1024 * 1024,
67 max_buffer_bytes: 8 * 1024 * 1024,
68 }
69 }
70}
71
72impl SseDecoder {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_limits(limits: SseLimits) -> Self {
80 Self {
81 limits,
82 ..Self::default()
83 }
84 }
85
86 pub fn push(
96 &mut self,
97 chunk: &[u8],
98 ) -> Result<Vec<SseFrame>, crate::transform::TransformError> {
99 self.push_inner(chunk)
100 }
101
102 fn push_inner(
103 &mut self,
104 chunk: &[u8],
105 ) -> Result<Vec<SseFrame>, crate::transform::TransformError> {
106 let pending = self.buf.len().saturating_add(chunk.len());
107 self.check_limit("buffer", self.limits.max_buffer_bytes, pending)?;
108 self.utf8.decode_into(chunk, &mut self.buf);
109 self.check_limit("buffer", self.limits.max_buffer_bytes, self.buf.len())?;
110 if self.buf.contains('\r') {
111 self.buf = self.buf.replace("\r\n", "\n");
112 }
113 let mut frames = Vec::new();
114 let mut cursor = 0;
115 while let Some(pos) = self.buf[cursor..].find("\n\n") {
116 let end = cursor + pos + 2;
117 self.check_limit("frame", self.limits.max_frame_bytes, end - cursor)?;
118 if let Some(frame) = parse_frame(&self.buf[cursor..end]) {
119 frames.push(frame);
120 }
121 cursor = end;
122 }
123 if cursor > 0 {
124 self.buf.drain(..cursor);
125 }
126 self.check_limit("frame", self.limits.max_frame_bytes, self.buf.len())?;
127 Ok(frames)
128 }
129
130 pub fn finish(&mut self) -> Result<Option<SseFrame>, crate::transform::TransformError> {
133 self.finish_inner()
134 }
135
136 fn finish_inner(&mut self) -> Result<Option<SseFrame>, crate::transform::TransformError> {
137 self.utf8.flush(&mut self.buf);
138 self.check_limit("frame", self.limits.max_frame_bytes, self.buf.len())?;
139 let raw = std::mem::take(&mut self.buf);
140 Ok(parse_frame(&raw))
141 }
142
143 fn check_limit(
144 &self,
145 limit: &'static str,
146 max_bytes: usize,
147 actual_bytes: usize,
148 ) -> Result<(), crate::transform::TransformError> {
149 if actual_bytes <= max_bytes {
150 Ok(())
151 } else {
152 Err(crate::transform::TransformError::StreamLimitExceeded {
153 limit,
154 max_bytes,
155 actual_bytes,
156 })
157 }
158 }
159}
160
161fn parse_frame(raw: &str) -> Option<SseFrame> {
162 let mut event = None;
163 let mut data_lines: Vec<&str> = Vec::new();
164 for line in raw.lines() {
165 if let Some(rest) = line.strip_prefix("event:") {
166 event = Some(rest.trim_start().to_owned());
167 } else if let Some(rest) = line.strip_prefix("data:") {
168 data_lines.push(rest.strip_prefix(' ').unwrap_or(rest));
169 }
170 }
171 if data_lines.is_empty() {
172 return None;
173 }
174 Some(SseFrame {
175 event,
176 data: data_lines.join("\n"),
177 })
178}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn frame_split_across_chunks() {
186 let mut d = SseDecoder::new();
187 assert!(d.push(b"event: ping\nda").unwrap().is_empty());
188 let frames = d.push(b"ta: {\"a\":1}\n\n: comment\ndata: x").unwrap();
189 assert_eq!(frames.len(), 1);
190 assert_eq!(frames[0].event.as_deref(), Some("ping"));
191 assert_eq!(frames[0].data, "{\"a\":1}");
192 assert_eq!(d.finish().unwrap().unwrap().data, "x");
194 }
195
196 #[test]
197 fn crlf_and_multiline_data() {
198 let mut d = SseDecoder::new();
199 let frames = d.push(b"data: l1\r\ndata: l2\r\n\r\n").unwrap();
200 assert_eq!(frames.len(), 1);
201 assert_eq!(frames[0].data, "l1\nl2");
202 }
203
204 #[test]
205 fn multibyte_char_split_across_chunks() {
206 let mut d = SseDecoder::new();
208 let bytes = "data: 汉字\n\n".as_bytes();
209 assert!(d.push(&bytes[..7]).unwrap().is_empty()); let frames = d.push(&bytes[7..]).unwrap();
211 assert_eq!(frames.len(), 1);
212 assert_eq!(frames[0].data, "汉字");
213 }
214
215 #[test]
216 fn rejects_oversized_frame_and_buffer() {
217 let limits = SseLimits {
218 max_frame_bytes: 16,
219 max_buffer_bytes: 32,
220 };
221 let mut d = SseDecoder::with_limits(limits);
222 assert!(d.push(b"data: 12345678901\n\n").is_err());
223
224 let mut d = SseDecoder::with_limits(limits);
225 assert!(d.push(&[b'x'; 33]).is_err());
226 }
227}