1use relay_config::Config;
2
3use crate::envelope::{Envelope, Item, ItemType};
4use crate::integrations::Integration;
5use crate::managed::Managed;
6use crate::services::outcome::DiscardItemType;
7use crate::statsd::RelayCounters;
8
9pub fn check_envelope_size_limits(
17 config: &Config,
18 envelope: &Envelope,
19) -> Result<(), DiscardItemType> {
20 let mut empty = Limit {
24 item_size: 0,
25 total_count: 0,
26 total_size: 0,
27 };
28 let mut event = Limit {
29 item_size: config.max_event_size(),
30 total_count: 1,
31 total_size: config.max_event_size(),
32 };
33 let mut attachment = Limit {
34 item_size: config.max_attachment_size(),
35 total_count: config.max_attachment_count(),
36 total_size: config.max_attachments_size(),
37 };
38 let mut replay_recording = Limit {
39 item_size: config.max_replay_compressed_size(),
40 total_count: 1,
41 total_size: config.max_replay_compressed_size(),
42 };
43 let mut replay_video = Limit {
44 item_size: config.max_replay_compressed_size(),
45 total_count: 1,
46 total_size: config.max_replay_compressed_size(),
47 };
48 let mut session = Limit {
49 item_size: config.max_sessions_size(),
50 total_count: config.max_session_count(),
51 total_size: config.max_sessions_size(),
52 };
53 let mut client_report = Limit {
54 item_size: config.max_client_reports_size(),
55 total_count: config.max_client_reports_count(),
56 total_size: config.max_client_reports_size(),
57 };
58 let mut profile = Limit {
59 item_size: config.max_profile_size(),
60 total_count: 1,
61 total_size: config.max_profile_size(),
62 };
63 let mut check_in = Limit {
64 item_size: config.max_check_in_size(),
65 total_count: 1,
66 total_size: config.max_check_in_size(),
67 };
68 let mut statsd = Limit {
69 item_size: config.max_statsd_size(),
70 total_count: 1,
71 total_size: config.max_statsd_size(),
72 };
73 let mut metric_bucket = Limit {
74 item_size: config.max_metric_buckets_size(),
75 total_count: 1,
76 total_size: config.max_metric_buckets_size(),
77 };
78 let mut log = Limit {
79 item_size: config.max_log_size(),
80 total_count: 1,
81 total_size: config.max_container_size(),
82 };
83 let mut log_integration = Limit {
84 item_size: config.max_container_size(),
85 total_count: 1,
86 total_size: config.max_container_size(),
87 };
88 let mut trace_metric = Limit {
89 item_size: config.max_trace_metric_size(),
90 total_count: 1,
91 total_size: config.max_container_size(),
92 };
93 let mut span = Limit {
94 item_size: config.max_span_size(),
95 total_count: 1,
96 total_size: config.max_container_size(),
97 };
98 let mut span_integration = Limit {
99 item_size: config.max_container_size(),
100 total_count: 1,
101 total_size: config.max_container_size(),
102 };
103 let mut span_legacy = Limit {
104 item_size: config.max_span_size(),
105 total_count: config.max_standalone_span_count(),
106 total_size: config.max_container_size(),
107 };
108
109 for item in envelope.items() {
110 let limit = match item.ty() {
111 ItemType::Event
112 | ItemType::Transaction
113 | ItemType::Security
114 | ItemType::ReplayEvent
115 | ItemType::RawSecurity
116 | ItemType::UserReportV2
117 | ItemType::FormData => &mut event,
118 ItemType::Attachment | ItemType::UnrealReport | ItemType::UserReport => &mut attachment,
119 ItemType::ReplayRecording => &mut replay_recording,
120 ItemType::ReplayVideo => &mut replay_video,
121 ItemType::Session | ItemType::Sessions => &mut session,
122 ItemType::ClientReport => &mut client_report,
123 ItemType::Profile | ItemType::ProfileChunk => &mut profile,
124 ItemType::CheckIn => &mut check_in,
125 ItemType::Statsd => &mut statsd,
126 ItemType::MetricBuckets => &mut metric_bucket,
127 ItemType::Log => &mut log,
128 ItemType::TraceMetric => &mut trace_metric,
129 ItemType::Span => match item.is_container() {
130 true => &mut span,
131 false => &mut span_legacy,
132 },
133 ItemType::Integration => match item.integration() {
134 Some(Integration::Logs(_)) => &mut log_integration,
135 Some(Integration::Spans(_)) => &mut span_integration,
136 None => {
137 debug_assert!(false);
139 &mut empty
140 }
141 },
142 ItemType::Unknown(_) => continue,
144 };
145
146 if let Err(err) = limit.apply(item) {
147 relay_statsd::metric!(
148 counter(RelayCounters::EnvelopeSizeLimited) += 1,
149 item = item.ty().name(),
150 limit = match err {
151 Rejected::ItemSize => "item_size",
152 Rejected::TotalCount => "total_count",
153 Rejected::TotalSize => "total_size",
154 }
155 );
156
157 let discard_item: DiscardItemType = item
158 .attachment_type()
159 .map(|t| t.into())
160 .unwrap_or_else(|| item.ty().into());
161 return Err(discard_item);
162 }
163 }
164
165 Ok(())
166}
167
168enum Rejected {
169 ItemSize,
170 TotalCount,
171 TotalSize,
172}
173
174struct Limit {
175 item_size: usize,
177 total_count: usize,
179 total_size: usize,
181}
182
183impl Limit {
184 fn apply(&mut self, item: &Item) -> Result<(), Rejected> {
185 let size = match item.is_container() {
186 true => item.len() / item.item_count().unwrap_or(1).max(1) as usize,
190 false => item.len(),
191 };
192 if size > self.item_size {
193 return Err(Rejected::ItemSize);
194 }
195
196 self.total_count = self
197 .total_count
198 .checked_sub(1)
199 .ok_or(Rejected::TotalCount)?;
200
201 self.total_size = self
202 .total_size
203 .checked_sub(item.len())
204 .ok_or(Rejected::TotalSize)?;
205
206 Ok(())
207 }
208}
209
210pub fn remove_unknown_items(config: &Config, envelope: &mut Managed<Box<Envelope>>) {
215 if config.accept_unknown_items() {
216 return;
217 }
218
219 envelope.modify(|envelope, records| {
220 envelope.retain_items(|item| {
221 if item.is_unknown() {
222 relay_log::debug!("dropping unknown item");
223 records.reject_err(None, &*item);
225 false
226 } else {
227 true
228 }
229 });
230 });
231}