Skip to main content

relay_server/utils/
sizes.rs

1use relay_config::Config;
2
3use crate::envelope::{Envelope, ItemType};
4use crate::integrations::Integration;
5use crate::managed::Managed;
6use crate::services::outcome::{DiscardAttachmentType, DiscardItemType};
7
8/// Checks for size limits of items in this envelope.
9///
10/// Returns `Ok`, if the envelope adheres to the configured size limits. Otherwise, returns
11/// an `Err` containing the offending item type, in which case the envelope should be discarded
12/// and a `413 Payload Too Large` response should be given.
13///
14/// The following limits are checked:
15///
16///  - `max_attachment_size`
17///  - `max_attachments_size`
18///  - `max_check_in_size`
19///  - `max_event_size`
20///  - `max_log_size`
21///  - `max_metric_buckets_size`
22///  - `max_profile_size`
23///  - `max_replay_compressed_size`
24///  - `max_session_count`
25///  - `max_span_size`
26///  - `max_statsd_size`
27///  - `max_trace_metric_size`
28///  - `max_container_size`
29pub fn check_envelope_size_limits(
30    config: &Config,
31    envelope: &Envelope,
32) -> Result<(), DiscardItemType> {
33    const NO_LIMIT: usize = usize::MAX;
34
35    let mut event_size = 0;
36    let mut attachments_size = 0;
37    let mut session_count = 0;
38    let mut client_reports_size = 0;
39
40    for item in envelope.items() {
41        if item.is_container() && item.len() > config.max_container_size() {
42            return Err(item.ty().into());
43        }
44
45        let max_size = match item.ty() {
46            ItemType::Event
47            | ItemType::Transaction
48            | ItemType::Security
49            | ItemType::ReplayEvent
50            | ItemType::RawSecurity
51            | ItemType::UserReportV2
52            | ItemType::FormData => {
53                event_size += item.len();
54                NO_LIMIT
55            }
56            ItemType::Attachment | ItemType::UnrealReport | ItemType::UserReport => {
57                attachments_size += item.len();
58                config.max_attachment_size()
59            }
60            ItemType::ReplayRecording => config.max_replay_compressed_size(),
61            ItemType::ReplayVideo => config.max_replay_compressed_size(),
62            ItemType::Session | ItemType::Sessions => {
63                session_count += 1;
64                NO_LIMIT
65            }
66            ItemType::ClientReport => {
67                client_reports_size += item.len();
68                NO_LIMIT
69            }
70            ItemType::Profile => config.max_profile_size(),
71            ItemType::CheckIn => config.max_check_in_size(),
72            ItemType::Statsd => config.max_statsd_size(),
73            ItemType::MetricBuckets => config.max_metric_buckets_size(),
74            ItemType::Log => config.max_log_size(),
75            ItemType::TraceMetric => config.max_trace_metric_size(),
76            ItemType::Span => config.max_span_size(),
77            ItemType::Integration => match item.integration() {
78                Some(Integration::Logs(_)) => config.max_logs_integration_size(),
79                Some(Integration::Spans(_)) => config.max_spans_integration_size(),
80                None => NO_LIMIT,
81            },
82            ItemType::ProfileChunk => config.max_profile_size(),
83            ItemType::Unknown(_) => NO_LIMIT,
84        };
85
86        // For item containers, we want to check that the contained items obey
87        // the size limits *on average*.
88        // For standalone items, this is just the item size itself.
89        let avg_item_size = item.len() / (item.item_count().unwrap_or(1).max(1) as usize);
90        if avg_item_size > max_size {
91            return Err(item
92                .attachment_type()
93                .map(|t| t.into())
94                .unwrap_or_else(|| item.ty().into()));
95        }
96    }
97
98    if event_size > config.max_event_size() {
99        return Err(DiscardItemType::Event);
100    }
101    if attachments_size > config.max_attachments_size() {
102        return Err(DiscardItemType::Attachment(
103            DiscardAttachmentType::Attachment,
104        ));
105    }
106    if session_count > config.max_session_count() {
107        return Err(DiscardItemType::Session);
108    }
109    if client_reports_size > config.max_client_reports_size() {
110        return Err(DiscardItemType::ClientReport);
111    }
112
113    Ok(())
114}
115
116/// Checks for valid envelope items.
117///
118/// If Relay is configured to drop unknown items, this function removes them from the Envelope. All
119/// known items will be retained.
120pub fn remove_unknown_items(config: &Config, envelope: &mut Managed<Box<Envelope>>) {
121    if config.accept_unknown_items() {
122        return;
123    }
124
125    envelope.modify(|envelope, records| {
126        envelope.retain_items(|item| {
127            if item.is_unknown() {
128                relay_log::debug!("dropping unknown item");
129                // Reject items silently, without outcomes.
130                records.reject_err(None, &*item);
131                false
132            } else {
133                true
134            }
135        });
136    });
137}