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_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::Nel
52            | ItemType::UserReportV2
53            | ItemType::FormData => {
54                event_size += item.len();
55                NO_LIMIT
56            }
57            ItemType::Attachment | ItemType::UnrealReport | ItemType::UserReport => {
58                attachments_size += item.len();
59                config.max_attachment_size()
60            }
61            ItemType::ReplayRecording => config.max_replay_compressed_size(),
62            ItemType::ReplayVideo => config.max_replay_compressed_size(),
63            ItemType::Session | ItemType::Sessions => {
64                session_count += 1;
65                NO_LIMIT
66            }
67            ItemType::ClientReport => {
68                client_reports_size += item.len();
69                NO_LIMIT
70            }
71            ItemType::Profile => config.max_profile_size(),
72            ItemType::CheckIn => config.max_check_in_size(),
73            ItemType::Statsd => config.max_statsd_size(),
74            ItemType::MetricBuckets => config.max_metric_buckets_size(),
75            ItemType::Log => config.max_log_size(),
76            ItemType::TraceMetric => config.max_trace_metric_size(),
77            ItemType::Span => config.max_span_size(),
78            ItemType::Integration => match item.integration() {
79                Some(Integration::Logs(_)) => config.max_logs_integration_size(),
80                Some(Integration::Spans(_)) => config.max_spans_integration_size(),
81                None => NO_LIMIT,
82            },
83            ItemType::ProfileChunk => config.max_profile_size(),
84            ItemType::Unknown(_) => NO_LIMIT,
85        };
86
87        // For item containers, we want to check that the contained items obey
88        // the size limits *on average*.
89        // For standalone items, this is just the item size itself.
90        let avg_item_size = item.len() / (item.item_count().unwrap_or(1).max(1) as usize);
91        if avg_item_size > max_size {
92            return Err(item
93                .attachment_type()
94                .map(|t| t.into())
95                .unwrap_or_else(|| item.ty().into()));
96        }
97    }
98
99    if event_size > config.max_event_size() {
100        return Err(DiscardItemType::Event);
101    }
102    if attachments_size > config.max_attachments_size() {
103        return Err(DiscardItemType::Attachment(
104            DiscardAttachmentType::Attachment,
105        ));
106    }
107    if session_count > config.max_session_count() {
108        return Err(DiscardItemType::Session);
109    }
110    if client_reports_size > config.max_client_reports_size() {
111        return Err(DiscardItemType::ClientReport);
112    }
113
114    Ok(())
115}
116
117/// Checks for valid envelope items.
118///
119/// If Relay is configured to drop unknown items, this function removes them from the Envelope. All
120/// known items will be retained.
121pub fn remove_unknown_items(config: &Config, envelope: &mut ManagedEnvelope) {
122    if !config.accept_unknown_items() {
123        envelope.retain_items(|item| match item.ty() {
124            ItemType::Unknown(ty) => {
125                relay_log::debug!("dropping unknown item of type '{ty}'");
126                ItemAction::DropSilently
127            }
128            _ => match item.attachment_type() {
129                Some(AttachmentType::Unknown(ty)) => {
130                    relay_log::debug!("dropping unknown attachment of type '{ty}'");
131                    ItemAction::DropSilently
132                }
133                _ => ItemAction::Keep,
134            },
135        });
136    }
137}