1use alloc::collections::VecDeque;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::mem;
5use core::time::Duration;
6
7use crate::Event;
8
9const BOM: [u8; 3] = [0xEF, 0xBB, 0xBF];
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13enum Bom {
14 Undecided,
15 Done,
16}
17
18#[derive(Debug, Clone, Default)]
21struct Core {
22 data: String,
23 event_type: String,
24 id_buf: String,
25 last_event_id: String,
26 retry: Option<Duration>,
27 pending_retry: Option<Duration>,
28 retry_updates: u64,
29 events: VecDeque<Event>,
30}
31
32impl Core {
33 fn process_line(&mut self, line: &[u8]) {
37 if line.is_empty() {
38 self.dispatch();
39 return;
40 }
41 if line[0] == b':' {
42 return;
43 }
44 let (name, value) = match memchr::memchr(b':', line) {
45 Some(i) => {
46 let value = &line[i + 1..];
47 (&line[..i], value.strip_prefix(b" ").unwrap_or(value))
48 }
49 None => (line, &line[line.len()..]),
50 };
51 match name {
52 b"event" => {
53 self.event_type.clear();
54 push_lossy(&mut self.event_type, value);
55 }
56 b"data" => {
57 push_lossy(&mut self.data, value);
58 self.data.push('\n');
59 }
60 b"id" => {
61 if !value.contains(&0) {
62 self.id_buf.clear();
63 push_lossy(&mut self.id_buf, value);
64 }
65 }
66 b"retry" => {
67 if let Some(ms) = parse_retry(value) {
68 let retry = Duration::from_millis(ms);
69 self.retry = Some(retry);
70 self.pending_retry = Some(retry);
71 self.retry_updates += 1;
72 }
73 }
74 _ => {}
75 }
76 }
77
78 fn dispatch(&mut self) {
81 if self.last_event_id != self.id_buf {
82 self.last_event_id.clone_from(&self.id_buf);
83 }
84 if self.data.is_empty() {
85 self.event_type.clear();
86 return;
87 }
88 if self.data.ends_with('\n') {
89 self.data.pop();
90 }
91 let event = if self.event_type.is_empty() {
92 String::from("message")
93 } else {
94 mem::take(&mut self.event_type)
95 };
96 self.events.push_back(Event {
97 event,
98 data: mem::take(&mut self.data),
99 id: self.last_event_id.clone(),
100 retry: self.pending_retry.take(),
101 });
102 }
103
104 fn discard_block(&mut self) {
107 self.data.clear();
108 self.event_type.clear();
109 }
110
111 fn block_len(&self) -> usize {
112 self.data.len() + self.event_type.len()
113 }
114}
115
116fn push_lossy(dst: &mut String, bytes: &[u8]) {
117 match core::str::from_utf8(bytes) {
118 Ok(s) => dst.push_str(s),
119 Err(_) => dst.push_str(&String::from_utf8_lossy(bytes)),
120 }
121}
122
123fn parse_retry(value: &[u8]) -> Option<u64> {
126 if value.is_empty() {
127 return None;
128 }
129 let mut ms: u64 = 0;
130 for &b in value {
131 if !b.is_ascii_digit() {
132 return None;
133 }
134 ms = ms.checked_mul(10)?.checked_add(u64::from(b - b'0'))?;
135 }
136 Some(ms)
137}
138
139#[doc(hidden)]
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
142pub struct Stats {
143 pub bytes_scanned: u64,
146 pub retry_updates: u64,
148 pub buffered_bytes: usize,
151}
152
153#[derive(Debug, Clone)]
165pub struct SseParser {
166 buf: Vec<u8>,
170 consumed: usize,
171 scan_pos: usize,
172 held_cr: bool,
175 bom: Bom,
176 finished: bool,
177 core: Core,
178 max_buffer: Option<usize>,
179 discarding: bool,
181 in_dropped_line: bool,
184 overflows: u64,
185 bytes_scanned: u64,
186}
187
188impl Default for SseParser {
189 fn default() -> Self {
190 Self::new()
191 }
192}
193
194impl SseParser {
195 pub fn new() -> Self {
197 SseParser {
198 buf: Vec::new(),
199 consumed: 0,
200 scan_pos: 0,
201 held_cr: false,
202 bom: Bom::Undecided,
203 finished: false,
204 core: Core::default(),
205 max_buffer: None,
206 discarding: false,
207 in_dropped_line: false,
208 overflows: 0,
209 bytes_scanned: 0,
210 }
211 }
212
213 pub fn with_last_event_id(id: impl Into<String>) -> Self {
217 let mut parser = Self::new();
218 parser.set_last_event_id(id);
219 parser
220 }
221
222 pub fn max_buffer(mut self, limit: usize) -> Self {
240 self.max_buffer = Some(limit);
241 self
242 }
243
244 pub fn overflows(&self) -> u64 {
247 self.overflows
248 }
249
250 pub(crate) fn has_pending_events(&self) -> bool {
251 !self.core.events.is_empty()
252 }
253
254 pub(crate) fn set_last_event_id(&mut self, id: impl Into<String>) {
255 let id = id.into();
256 self.core.id_buf.clone_from(&id);
257 self.core.last_event_id = id;
258 }
259
260 pub fn feed(&mut self, bytes: &[u8]) {
269 debug_assert!(!self.finished, "SseParser::feed called after finish()");
270 if self.finished || bytes.is_empty() {
271 return;
272 }
273 self.buf.extend_from_slice(bytes);
274 if self.bom == Bom::Undecided && !self.resolve_bom() {
275 return;
276 }
277 self.scan();
278 self.enforce_limit();
279 self.compact();
280 }
281
282 pub fn next_event(&mut self) -> Option<Event> {
284 self.core.events.pop_front()
285 }
286
287 pub fn finish(&mut self) {
293 if self.finished {
294 return;
295 }
296 self.finished = true;
297 self.held_cr = false;
298 self.buf = Vec::new();
299 self.consumed = 0;
300 self.scan_pos = 0;
301 self.discarding = false;
302 self.in_dropped_line = false;
303 self.core.discard_block();
304 }
305
306 pub fn last_event_id(&self) -> &str {
311 &self.core.last_event_id
312 }
313
314 pub fn retry(&self) -> Option<Duration> {
319 self.core.retry
320 }
321
322 #[doc(hidden)]
324 pub fn stats(&self) -> Stats {
325 Stats {
326 bytes_scanned: self.bytes_scanned,
327 retry_updates: self.core.retry_updates,
328 buffered_bytes: self.buf.len() + self.core.block_len(),
329 }
330 }
331
332 fn resolve_bom(&mut self) -> bool {
339 let n = self.buf.len().min(BOM.len());
340 if self.buf[..n] != BOM[..n] {
341 self.bom = Bom::Done;
342 return true;
343 }
344 if n < BOM.len() {
345 return false;
346 }
347 self.bom = Bom::Done;
348 self.consumed = BOM.len();
349 self.scan_pos = BOM.len();
350 true
351 }
352
353 fn scan(&mut self) {
358 if self.held_cr {
359 if self.scan_pos >= self.buf.len() {
360 return;
361 }
362 self.held_cr = false;
363 if self.buf[self.scan_pos] == b'\n' {
364 self.bytes_scanned += 1;
365 self.scan_pos += 1;
366 self.consumed = self.scan_pos;
367 }
368 }
369 while self.scan_pos < self.buf.len() {
370 let unscanned = &self.buf[self.scan_pos..];
371 let i = match memchr::memchr2(b'\r', b'\n', unscanned) {
372 Some(i) => i,
373 None => {
374 self.bytes_scanned += unscanned.len() as u64;
375 self.scan_pos = self.buf.len();
376 break;
377 }
378 };
379 let term = self.scan_pos + i;
380 let is_cr = self.buf[term] == b'\r';
381 self.bytes_scanned += i as u64 + 1;
382 self.handle_line(term);
383 self.consumed = term + 1;
384 self.scan_pos = term + 1;
385 if is_cr {
386 if self.scan_pos < self.buf.len() {
387 if self.buf[self.scan_pos] == b'\n' {
388 self.bytes_scanned += 1;
389 self.scan_pos += 1;
390 self.consumed = self.scan_pos;
391 }
392 } else {
393 self.held_cr = true;
394 }
395 }
396 }
397 }
398
399 fn handle_line(&mut self, end: usize) {
401 let line = &self.buf[self.consumed..end];
402 self.bytes_scanned += line.len() as u64;
403 if self.discarding {
404 if self.in_dropped_line {
405 self.in_dropped_line = false;
406 } else if line.is_empty() {
407 self.discarding = false;
408 self.core.process_line(line);
409 }
410 return;
411 }
412 if !line.is_empty() && self.exceeds_cap(line.len()) {
413 self.overflow();
414 return;
415 }
416 self.core.process_line(line);
417 if self.exceeds_cap(0) {
421 self.overflow();
422 }
423 }
424
425 fn exceeds_cap(&self, line_bytes: usize) -> bool {
428 match self.max_buffer {
429 Some(limit) => line_bytes.saturating_add(self.core.block_len()) > limit,
430 None => false,
431 }
432 }
433
434 fn overflow(&mut self) {
435 self.overflows += 1;
436 self.discarding = true;
437 self.core.discard_block();
438 }
439
440 fn enforce_limit(&mut self) {
444 if self.max_buffer.is_none() {
445 return;
446 }
447 let pending = self.buf.len() - self.consumed;
448 if pending == 0 {
449 return;
450 }
451 if !self.discarding && self.exceeds_cap(pending) {
452 self.overflow();
453 }
454 if self.discarding {
455 self.in_dropped_line = true;
456 self.consumed = self.buf.len();
457 }
458 }
459
460 fn compact(&mut self) {
464 if self.consumed == 0 {
465 return;
466 }
467 if self.consumed == self.buf.len() {
468 self.buf.clear();
469 } else if self.consumed >= self.buf.len() / 2 {
470 self.buf.drain(..self.consumed);
471 } else {
472 return;
473 }
474 self.scan_pos -= self.consumed;
475 self.consumed = 0;
476 }
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482
483 #[test]
484 fn retry_parsing() {
485 assert_eq!(parse_retry(b""), None);
486 assert_eq!(parse_retry(b"0"), Some(0));
487 assert_eq!(parse_retry(b"3000"), Some(3000));
488 assert_eq!(parse_retry(b"30x0"), None);
489 assert_eq!(parse_retry(b"12 "), None);
490 assert_eq!(parse_retry(b"-1"), None);
491 assert_eq!(parse_retry(b"18446744073709551615"), Some(u64::MAX));
492 assert_eq!(parse_retry(b"18446744073709551616"), None);
493 assert_eq!(parse_retry(b"99999999999999999999999"), None);
494 }
495
496 #[test]
497 fn compaction_keeps_offsets_consistent() {
498 let mut parser = SseParser::new();
499 parser.feed(b"data: a\n\ndata: bbbbbbbbbbbbbbbbbbbb");
500 assert_eq!(parser.next_event().unwrap().data, "a");
501 parser.feed(b"\n\n");
502 assert_eq!(parser.next_event().unwrap().data, "bbbbbbbbbbbbbbbbbbbb");
503 assert_eq!(parser.buf.len(), 0);
504 assert_eq!(parser.consumed, 0);
505 assert_eq!(parser.scan_pos, 0);
506 }
507}