Skip to content

Commit 456ef23

Browse files
authored
feat: Allow Envelopes to contain raw binary data (#549)
1 parent bc06e5f commit 456ef23

File tree

1 file changed

+73
-19
lines changed

1 file changed

+73
-19
lines changed

Diff for: sentry-types/src/protocol/envelope.rs

+73-19
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,31 @@ impl<'s> Iterator for EnvelopeItemIter<'s> {
163163
}
164164
}
165165

166+
/// The items contained in an [`Envelope`].
167+
///
168+
/// This may be a vector of [`EnvelopeItem`]s (the standard case)
169+
/// or a binary blob.
170+
#[derive(Debug, Clone, PartialEq)]
171+
enum Items {
172+
EnvelopeItems(Vec<EnvelopeItem>),
173+
Raw(Vec<u8>),
174+
}
175+
176+
impl Default for Items {
177+
fn default() -> Self {
178+
Self::EnvelopeItems(Default::default())
179+
}
180+
}
181+
182+
impl Items {
183+
fn is_empty(&self) -> bool {
184+
match self {
185+
Items::EnvelopeItems(items) => items.is_empty(),
186+
Items::Raw(bytes) => bytes.is_empty(),
187+
}
188+
}
189+
}
190+
166191
/// A Sentry Envelope.
167192
///
168193
/// An Envelope is the data format that Sentry uses for Ingestion. It can contain
@@ -174,7 +199,7 @@ impl<'s> Iterator for EnvelopeItemIter<'s> {
174199
#[derive(Clone, Default, Debug, PartialEq)]
175200
pub struct Envelope {
176201
event_id: Option<Uuid>,
177-
items: Vec<EnvelopeItem>,
202+
items: Items,
178203
}
179204

180205
impl Envelope {
@@ -188,6 +213,11 @@ impl Envelope {
188213
where
189214
I: Into<EnvelopeItem>,
190215
{
216+
let Items::EnvelopeItems(ref mut items) = self.items else {
217+
eprintln!("WARNING: This envelope contains raw items. Adding an item is not supported.");
218+
return;
219+
};
220+
191221
let item = item.into();
192222
if self.event_id.is_none() {
193223
if let EnvelopeItem::Event(ref event) = item {
@@ -196,14 +226,17 @@ impl Envelope {
196226
self.event_id = Some(transaction.event_id);
197227
}
198228
}
199-
self.items.push(item);
229+
items.push(item);
200230
}
201231

202232
/// Create an [`Iterator`] over all the [`EnvelopeItem`]s.
203233
pub fn items(&self) -> EnvelopeItemIter {
204-
EnvelopeItemIter {
205-
inner: self.items.iter(),
206-
}
234+
let inner = match &self.items {
235+
Items::EnvelopeItems(items) => items.iter(),
236+
Items::Raw(_) => [].iter(),
237+
};
238+
239+
EnvelopeItemIter { inner }
207240
}
208241

209242
/// Returns the Envelopes Uuid, if any.
@@ -215,13 +248,14 @@ impl Envelope {
215248
///
216249
/// [`Event`]: struct.Event.html
217250
pub fn event(&self) -> Option<&Event<'static>> {
218-
self.items
219-
.iter()
220-
.filter_map(|item| match item {
221-
EnvelopeItem::Event(event) => Some(event),
222-
_ => None,
223-
})
224-
.next()
251+
let Items::EnvelopeItems(ref items) = self.items else {
252+
return None;
253+
};
254+
255+
items.iter().find_map(|item| match item {
256+
EnvelopeItem::Event(event) => Some(event),
257+
_ => None,
258+
})
225259
}
226260

227261
/// Filters the Envelope's [`EnvelopeItem`]s based on a predicate,
@@ -236,8 +270,12 @@ impl Envelope {
236270
where
237271
P: FnMut(&EnvelopeItem) -> bool,
238272
{
273+
let Items::EnvelopeItems(items) = self.items else {
274+
return None;
275+
};
276+
239277
let mut filtered = Envelope::new();
240-
for item in self.items {
278+
for item in items {
241279
if predicate(&item) {
242280
filtered.add_item(item);
243281
}
@@ -246,9 +284,9 @@ impl Envelope {
246284
// filter again, removing attachments which do not make any sense without
247285
// an event/transaction
248286
if filtered.uuid().is_none() {
249-
filtered
250-
.items
251-
.retain(|item| !matches!(item, EnvelopeItem::Attachment(..)))
287+
if let Items::EnvelopeItems(ref mut items) = filtered.items {
288+
items.retain(|item| !matches!(item, EnvelopeItem::Attachment(..)))
289+
}
252290
}
253291

254292
if filtered.items.is_empty() {
@@ -265,17 +303,21 @@ impl Envelope {
265303
where
266304
W: Write,
267305
{
268-
let mut item_buf = Vec::new();
269-
270306
// write the headers:
271307
let event_id = self.uuid();
272308
match event_id {
273309
Some(uuid) => writeln!(writer, r#"{{"event_id":"{uuid}"}}"#)?,
274310
_ => writeln!(writer, "{{}}")?,
275311
}
276312

313+
let items = match &self.items {
314+
Items::Raw(bytes) => return writer.write_all(bytes).map(|_| ()),
315+
Items::EnvelopeItems(items) => items,
316+
};
317+
318+
let mut item_buf = Vec::new();
277319
// write each item:
278-
for item in &self.items {
320+
for item in items {
279321
// we write them to a temporary buffer first, since we need their length
280322
match item {
281323
EnvelopeItem::Event(event) => serde_json::to_writer(&mut item_buf, event)?,
@@ -340,6 +382,18 @@ impl Envelope {
340382
Envelope::from_slice(&bytes)
341383
}
342384

385+
/// Creates a new Envelope from path without attempting to parse anything.
386+
///
387+
/// The resulting Envelope will have no `event_id` and the file contents will
388+
/// be contained verbatim in the `items` field.
389+
pub fn from_path_raw<P: AsRef<Path>>(path: P) -> Result<Self, EnvelopeError> {
390+
let bytes = std::fs::read(path).map_err(|_| EnvelopeError::UnexpectedEof)?;
391+
Ok(Self {
392+
event_id: None,
393+
items: Items::Raw(bytes),
394+
})
395+
}
396+
343397
fn parse_header(slice: &[u8]) -> Result<(EnvelopeHeader, usize), EnvelopeError> {
344398
let mut stream = serde_json::Deserializer::from_slice(slice).into_iter();
345399

0 commit comments

Comments
 (0)