relay_server/utils/
sizes.rs

1use relay_config::Config;
2
3use crate::envelope::{AttachmentType, Envelope, ItemType};
4use crate::integrations::Integration;
5use crate::managed::{ItemAction, ManagedEnvelope};
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_container_size`
28///  - `max_span_count`
29///  - `max_log_count`
30///  - `max_trace_metric_size`
31pub fn check_envelope_size_limits(
32    config: &Config,
33    envelope: &Envelope,
34) -> Result<(), DiscardItemType> {
35    const NO_LIMIT: usize = usize::MAX;
36
37    let mut event_size = 0;
38    let mut attachments_size = 0;
39    let mut session_count = 0;
40    let mut span_count = 0;
41    let mut log_count = 0;
42    let mut client_reports_size = 0;
43    let mut trace_metric_count = 0;
44
45    for item in envelope.items() {
46        if item.is_container() && item.len() > config.max_container_size() {
47            return Err(item.ty().into());
48        }
49
50        let max_size = match item.ty() {
51            ItemType::Event
52            | ItemType::Transaction
53            | ItemType::Security
54            | ItemType::ReplayEvent
55            | ItemType::RawSecurity
56            | ItemType::Nel
57            | ItemType::UserReportV2
58            | ItemType::FormData => {
59                event_size += item.len();
60                NO_LIMIT
61            }
62            ItemType::Attachment | ItemType::UnrealReport | ItemType::UserReport => {
63                attachments_size += item.len();
64                config.max_attachment_size()
65            }
66            ItemType::ReplayRecording => config.max_replay_compressed_size(),
67            ItemType::ReplayVideo => config.max_replay_compressed_size(),
68            ItemType::Session | ItemType::Sessions => {
69                session_count += 1;
70                NO_LIMIT
71            }
72            ItemType::ClientReport => {
73                client_reports_size += item.len();
74                NO_LIMIT
75            }
76            ItemType::Profile => config.max_profile_size(),
77            ItemType::CheckIn => config.max_check_in_size(),
78            ItemType::Statsd => config.max_statsd_size(),
79            ItemType::MetricBuckets => config.max_metric_buckets_size(),
80            ItemType::Log => {
81                log_count += item.item_count().unwrap_or(1) as usize;
82                config.max_log_size()
83            }
84            ItemType::TraceMetric => {
85                trace_metric_count += item.item_count().unwrap_or(1) as usize;
86                config.max_trace_metric_size()
87            }
88            ItemType::Span => {
89                span_count += item.item_count().unwrap_or(1) as usize;
90                config.max_span_size()
91            }
92            ItemType::Integration => match item.integration() {
93                Some(Integration::Logs(_)) => {
94                    log_count += item.item_count().unwrap_or(1) as usize;
95                    config.max_log_size()
96                }
97                Some(Integration::Spans(_)) => {
98                    span_count += item.item_count().unwrap_or(1) as usize;
99                    config.max_event_size()
100                }
101                None => NO_LIMIT,
102            },
103            ItemType::ProfileChunk => config.max_profile_size(),
104            ItemType::Unknown(_) => NO_LIMIT,
105        };
106
107        // For item containers, we want to check that the contained items obey
108        // the size limits *on average*.
109        // For standalone items, this is just the item size itself.
110        let avg_item_size = item.len() / (item.item_count().unwrap_or(1).max(1) as usize);
111        if avg_item_size > max_size {
112            return Err(item
113                .attachment_type()
114                .map(|t| t.into())
115                .unwrap_or_else(|| item.ty().into()));
116        }
117    }
118
119    if event_size > config.max_event_size() {
120        return Err(DiscardItemType::Event);
121    }
122    if attachments_size > config.max_attachments_size() {
123        return Err(DiscardItemType::Attachment(
124            DiscardAttachmentType::Attachment,
125        ));
126    }
127    if session_count > config.max_session_count() {
128        return Err(DiscardItemType::Session);
129    }
130    if span_count > config.max_span_count() {
131        return Err(DiscardItemType::Span);
132    }
133    if log_count > config.max_log_count() {
134        return Err(DiscardItemType::Log);
135    }
136    if trace_metric_count > config.max_trace_metric_count() {
137        return Err(DiscardItemType::TraceMetric);
138    }
139    if client_reports_size > config.max_client_reports_size() {
140        return Err(DiscardItemType::ClientReport);
141    }
142
143    Ok(())
144}
145
146/// Checks for valid envelope items.
147///
148/// If Relay is configured to drop unknown items, this function removes them from the Envelope. All
149/// known items will be retained.
150pub fn remove_unknown_items(config: &Config, envelope: &mut ManagedEnvelope) {
151    if !config.accept_unknown_items() {
152        envelope.retain_items(|item| match item.ty() {
153            ItemType::Unknown(ty) => {
154                relay_log::debug!("dropping unknown item of type '{ty}'");
155                ItemAction::DropSilently
156            }
157            _ => match item.attachment_type() {
158                Some(AttachmentType::Unknown(ty)) => {
159                    relay_log::debug!("dropping unknown attachment of type '{ty}'");
160                    ItemAction::DropSilently
161                }
162                _ => ItemAction::Keep,
163            },
164        });
165    }
166}