relay_server/endpoints/
unreal.rs1use axum::extract::{DefaultBodyLimit, FromRequest, Query};
2use axum::response::IntoResponse;
3use axum::routing::{MethodRouter, post};
4use bytes::Bytes;
5use relay_config::Config;
6use relay_event_schema::protocol::EventId;
7use serde::Deserialize;
8
9use crate::constants::UNREAL_USER_HEADER;
10use crate::endpoints::common::{self, BadStoreRequest, TextResponse};
11use crate::envelope::{ContentType, Envelope, Item, ItemType};
12use crate::extractors::RequestMeta;
13use crate::service::ServiceState;
14
15#[derive(Debug, Deserialize)]
16struct UnrealQuery {
17 #[serde(rename = "UserID")]
18 user_id: Option<String>,
19}
20
21#[derive(Debug, FromRequest)]
22#[from_request(state(ServiceState))]
23struct UnrealParams {
24 meta: RequestMeta,
25 #[from_request(via(Query))]
26 query: UnrealQuery,
27 data: Bytes,
28}
29
30impl UnrealParams {
31 fn extract_envelope(self) -> Result<Box<Envelope>, BadStoreRequest> {
32 let Self { meta, query, data } = self;
33
34 if data.is_empty() {
35 return Err(BadStoreRequest::EmptyBody);
36 }
37
38 let mut envelope = Envelope::from_request(Some(EventId::new()), meta);
39
40 let mut item = Item::new(ItemType::UnrealReport);
41 item.set_payload(ContentType::OctetStream, data);
42 envelope.add_item(item);
43
44 if let Some(user_id) = query.user_id {
45 envelope.set_header(UNREAL_USER_HEADER, user_id);
46 }
47
48 Ok(envelope)
49 }
50}
51
52async fn handle(
53 state: ServiceState,
54 params: UnrealParams,
55) -> Result<impl IntoResponse, BadStoreRequest> {
56 let envelope = params.extract_envelope()?;
57 let id = envelope.event_id();
58
59 match common::handle_envelope(&state, envelope).await {
61 Ok(_) | Err(BadStoreRequest::RateLimited(_)) => (),
62 Err(error) => return Err(error),
63 };
64
65 Ok(TextResponse(id))
68}
69
70pub fn route(config: &Config) -> MethodRouter<ServiceState> {
71 post(handle).route_layer(DefaultBodyLimit::max(config.max_attachments_size()))
72}