http_sse/
event.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::io::{self, Write};
6use thiserror::Error;
7
8/// An Event from an http sse stream
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct Event {
11    event_type: String,
12    data: String,
13}
14
15impl Event {
16    pub fn from_type_and_data(
17        event_type: impl Into<String>,
18        data: impl Into<String>,
19    ) -> Result<Self, EventError> {
20        let event_type = event_type.into();
21        if event_type.contains('\r') {
22            return Err(EventError::TypeHasCarriageReturn);
23        }
24        if event_type.contains('\n') {
25            return Err(EventError::TypeHasNewline);
26        }
27        let data = data.into();
28        if data.is_empty() {
29            return Err(EventError::DataIsEmpty);
30        }
31        if data.contains('\r') {
32            return Err(EventError::DataHasCarriageReturn);
33        }
34        Ok(Self { event_type, data })
35    }
36
37    /// Serialize the `Event` to bytes suitable for writing to an http sse stream.
38    pub fn to_writer(&self, mut writer: impl Write) -> io::Result<()> {
39        if !self.event_type.is_empty() {
40            write!(&mut writer, "event: {}\n", self.event_type)?;
41        }
42        for line in self.data.split('\n') {
43            write!(&mut writer, "data: {}\n", line)?;
44        }
45        writer.write_all(b"\n")?;
46        Ok(())
47    }
48
49    /// Serialize the `Event` to bytes suitable for writing to an http sse stream.
50    pub fn to_vec(&self) -> Vec<u8> {
51        let mut ret = vec![];
52        // to_writer only errors if the Writer errors, and write for Vec does not error
53        self.to_writer(&mut ret).unwrap();
54        ret
55    }
56
57    pub fn event_type(&self) -> &str {
58        &self.event_type
59    }
60
61    pub fn data(&self) -> &str {
62        &self.data
63    }
64}
65
66#[derive(Debug, Error, PartialEq, Eq)]
67pub enum EventError {
68    #[error("event type cannot contain carriage returns")]
69    TypeHasCarriageReturn,
70
71    #[error("event type cannot contain newlines")]
72    TypeHasNewline,
73
74    #[error("event data cannot be empty")]
75    DataIsEmpty,
76
77    #[error("event data cannot contain carriage returns")]
78    DataHasCarriageReturn,
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    fn assert_to_writer(event: &Event, expected: &str) {
86        let mut bytes = vec![];
87        event.to_writer(&mut bytes).unwrap();
88        assert_eq!(std::str::from_utf8(&bytes).unwrap(), expected, "event: {:?}", event);
89    }
90
91    #[test]
92    fn error_if_type_has_carriage_return() {
93        assert_eq!(Event::from_type_and_data("\r", "data"), Err(EventError::TypeHasCarriageReturn));
94    }
95
96    #[test]
97    fn error_if_type_has_newline() {
98        assert_eq!(Event::from_type_and_data("\n", "data"), Err(EventError::TypeHasNewline));
99    }
100
101    #[test]
102    fn error_if_data_is_empty() {
103        assert_eq!(Event::from_type_and_data("", ""), Err(EventError::DataIsEmpty));
104    }
105
106    #[test]
107    fn error_if_data_has_carriage_return() {
108        assert_eq!(Event::from_type_and_data("", "\r"), Err(EventError::DataHasCarriageReturn));
109    }
110
111    #[test]
112    fn to_writer_type_and_data() {
113        let event = Event::from_type_and_data("type", "data").unwrap();
114        assert_to_writer(&event, "event: type\ndata: data\n\n");
115    }
116
117    #[test]
118    fn to_writer_no_type() {
119        let event = Event::from_type_and_data("", "data").unwrap();
120        assert_to_writer(&event, "data: data\n\n");
121    }
122
123    #[test]
124    fn to_writer_data_trailing_newline() {
125        let event = Event::from_type_and_data("", "data\n").unwrap();
126        assert_to_writer(&event, "data: data\ndata: \n\n");
127    }
128
129    #[test]
130    fn to_writer_two_line_data() {
131        let event = Event::from_type_and_data("", "data1\ndata2").unwrap();
132        assert_to_writer(&event, "data: data1\ndata: data2\n\n");
133    }
134
135    #[test]
136    fn to_writer_two_line_data_trailing_newline() {
137        let event = Event::from_type_and_data("", "data1\ndata2\n").unwrap();
138        assert_to_writer(&event, "data: data1\ndata: data2\ndata: \n\n");
139    }
140
141    #[test]
142    fn to_writer_consecutive_newlines() {
143        let event = Event::from_type_and_data("", "\n\n").unwrap();
144        assert_to_writer(&event, "data: \ndata: \ndata: \n\n");
145    }
146}