-
Notifications
You must be signed in to change notification settings - Fork 103
feat(processor): Playstation endpoint #4555
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
tobias-wilfert
merged 24 commits into
master
from
tobis-wilfert/feat/add-playstation-endpoint
Mar 20, 2025
Merged
Changes from 2 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
e528b95
Initial logic
tobias-wilfert 898fdb7
Add feature flag logic
tobias-wilfert abdc937
Removed the decompression from the endpoint, renamed feature
tobias-wilfert 1d5fa97
Updated the type inference logic.
tobias-wilfert 282d070
Moved the expand and filter logic behind flag.
tobias-wilfert fa6440b
Moved guard into expand
tobias-wilfert eec54f8
Add placeholder metric
tobias-wilfert 65b62a4
Added initial integration tests
tobias-wilfert 05a9e88
Add test for playstation attachment
tobias-wilfert 0ad318a
Remove outdated todos
tobias-wilfert 4ac3bf0
Update relay-server/src/endpoints/playstation.rs
tobias-wilfert 580d4e9
Update tests/integration/test_playstation.py
tobias-wilfert 846502b
Address feedback
tobias-wilfert f718df9
Update tests
tobias-wilfert b2e731a
Remove outdated comment
tobias-wilfert f128036
Appease clippy
tobias-wilfert 14d9fa5
Appease clippy
tobias-wilfert e75551d
Add changelog entry
tobias-wilfert b433f31
Update CHANGELOG.md
tobias-wilfert 59641cd
Merge branch 'master' into tobis-wilfert/feat/add-playstation-endpoint
tobias-wilfert 1eb8354
Update CHANGELOG.md
tobias-wilfert 17b3a5d
Moved changelog to correct section
tobias-wilfert a1f798d
Update CHANGELOG.md
tobias-wilfert 8ece4ac
Update CHANGELOG.md
tobias-wilfert File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
use axum::extract::{DefaultBodyLimit, Request}; | ||
use axum::response::IntoResponse; | ||
use axum::routing::{post, MethodRouter}; | ||
use axum::RequestExt; | ||
use bytes::Bytes; | ||
use lz4_flex::frame::FrameDecoder as lz4Decoder; | ||
use multer::Multipart; | ||
use relay_config::Config; | ||
use relay_dynamic_config::Feature; | ||
use relay_event_schema::protocol::EventId; | ||
use std::io::Cursor; | ||
use std::io::Read; | ||
|
||
use crate::endpoints::common::{self, BadStoreRequest, TextResponse}; | ||
use crate::envelope::ContentType::OctetStream; | ||
use crate::envelope::{AttachmentType, Envelope}; | ||
use crate::extractors::{RawContentType, Remote, RequestMeta}; | ||
use crate::service::ServiceState; | ||
use crate::utils; | ||
|
||
/// The extension of a prosperodump in the multipart form-data upload. | ||
const PROSPERODUMP_EXTENSION: &str = "prosperodmp"; | ||
tobias-wilfert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
/// The extension of a screenshot in the multipart form-data upload. | ||
const SCREENSHOT_EXTENSION: &str = "jpg"; | ||
|
||
/// The extension of a video in the multipart form-data upload. | ||
const VIDEO_EXTENSION: &str = "webm"; | ||
|
||
/// The extension of a memorydump in the multipart form-data upload. | ||
const MEMORYDUMP_EXTENSION: &str = "prosperomemdmp"; | ||
|
||
/// Prosperodump attachments should have these magic bytes | ||
const PROSPERODUMP_MAGIC_HEADER: &[u8] = b"\x7FELF"; | ||
|
||
/// Magic bytes for lz4 compressed prosperodump containers. | ||
const LZ4_MAGIC_HEADER: &[u8] = b"\x04\x22\x4d\x18"; | ||
|
||
fn validate_prosperodump(data: &[u8]) -> Result<(), BadStoreRequest> { | ||
if !data.starts_with(PROSPERODUMP_MAGIC_HEADER) { | ||
relay_log::trace!("invalid prosperodump file"); | ||
return Err(BadStoreRequest::InvalidProsperodump); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
// TODO: Decide if we want to move this into a utils since it is duplicate. | ||
/// Convenience wrapper to let a decoder decode its full input into a buffer | ||
fn run_decoder(decoder: &mut Box<dyn Read>) -> std::io::Result<Vec<u8>> { | ||
let mut buffer = Vec::new(); | ||
decoder.read_to_end(&mut buffer)?; | ||
Ok(buffer) | ||
} | ||
tobias-wilfert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
||
// TODO: Decide if this extra function is worth it if we only have one encoder | ||
/// Creates a decoder based on the magic bytes the prosperodump payload | ||
fn decoder_from(prosperodump_data: Bytes) -> Option<Box<dyn Read>> { | ||
if prosperodump_data.starts_with(LZ4_MAGIC_HEADER) { | ||
return Some(Box::new(lz4Decoder::new(Cursor::new(prosperodump_data)))); | ||
tobias-wilfert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
} | ||
None | ||
} | ||
|
||
/// Tries to decode a prosperodump using any of the supported compression formats | ||
/// or returns the provided minidump payload untouched if no format where detected | ||
fn decode_prosperodump(prosperodump_data: Bytes) -> Result<Bytes, BadStoreRequest> { | ||
match decoder_from(prosperodump_data.clone()) { | ||
Some(mut decoder) => { | ||
match run_decoder(&mut decoder) { | ||
Ok(decoded) => Ok(Bytes::from(decoded)), | ||
Err(err) => { | ||
// we detected a compression container but failed to decode it | ||
relay_log::trace!("invalid compression container"); | ||
Err(BadStoreRequest::InvalidCompressionContainer(err)) | ||
} | ||
} | ||
} | ||
None => { | ||
// this means we haven't detected any compression container | ||
// TODO: Decide if we want to fail here | ||
Ok(prosperodump_data) | ||
} | ||
} | ||
} | ||
|
||
fn infer_attachment_type(field_name: Option<&str>) -> AttachmentType { | ||
match field_name.unwrap_or("") { | ||
PROSPERODUMP_EXTENSION => AttachmentType::Prosperodump, | ||
// TODO: Think about if we want these to be a special attachment type. | ||
SCREENSHOT_EXTENSION | VIDEO_EXTENSION | MEMORYDUMP_EXTENSION | _ => { | ||
AttachmentType::Attachment | ||
} | ||
} | ||
} | ||
|
||
async fn extract_multipart( | ||
multipart: Multipart<'static>, | ||
meta: RequestMeta, | ||
) -> Result<Box<Envelope>, BadStoreRequest> { | ||
let mut items = utils::multipart_items_by_extension(multipart, infer_attachment_type).await?; | ||
|
||
let prosperodump_item = items | ||
.iter_mut() | ||
.find(|item| item.attachment_type() == Some(&AttachmentType::Prosperodump)) | ||
.ok_or(BadStoreRequest::MissingProsperodump)?; | ||
|
||
// TODO: Think about if we want a ContentType::Prosperodump ? | ||
prosperodump_item.set_payload( | ||
OctetStream, | ||
decode_prosperodump(prosperodump_item.payload())?, | ||
); | ||
|
||
validate_prosperodump(&prosperodump_item.payload())?; | ||
|
||
let event_id = common::event_id_from_items(&items)?.unwrap_or_else(EventId::new); | ||
let mut envelope = Envelope::from_request(Some(event_id), meta); | ||
|
||
for item in items { | ||
envelope.add_item(item); | ||
} | ||
|
||
Ok(envelope) | ||
} | ||
|
||
async fn handle( | ||
state: ServiceState, | ||
meta: RequestMeta, | ||
_content_type: RawContentType, | ||
request: Request, | ||
) -> axum::response::Result<impl IntoResponse> { | ||
// The crash dumps are transmitted as `...` in a multipart form-data/ request. | ||
let Remote(multipart) = request.extract_with_state(&state).await?; | ||
let mut envelope = extract_multipart(multipart, meta).await?; | ||
envelope.require_feature(Feature::PlaystationEndpoint); | ||
|
||
let id = envelope.event_id(); | ||
|
||
// Never respond with a 429 since clients often retry these | ||
match common::handle_envelope(&state, envelope).await { | ||
Ok(_) | Err(BadStoreRequest::RateLimited(_)) => (), | ||
Err(error) => return Err(error.into()), | ||
}; | ||
|
||
// Return here needs to be a 200 with arbitrary text to make the sender happy. | ||
// TODO: Think about if there is something else to return here | ||
Ok(TextResponse(id)) | ||
} | ||
|
||
pub fn route(config: &Config) -> MethodRouter<ServiceState> { | ||
// TODO: Check if this even has an effect since we will always have a multipart message. | ||
post(handle).route_layer(DefaultBodyLimit::max(config.max_attachment_size())) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
//! Playstation related code. | ||
//! | ||
//! These functions are included only in the processing mode. | ||
|
||
use crate::envelope::{AttachmentType, ItemType}; | ||
use crate::services::processor::{ErrorGroup, EventFullyNormalized, ProcessingError}; | ||
use crate::utils::TypedEnvelope; | ||
use relay_config::Config; | ||
use relay_event_schema::protocol::Event; | ||
use relay_protocol::Annotated; | ||
|
||
pub fn expand( | ||
managed_envelope: &mut TypedEnvelope<ErrorGroup>, | ||
_config: &Config, | ||
) -> Result<(), ProcessingError> { | ||
let envelope = &mut managed_envelope.envelope_mut(); | ||
|
||
if let Some(item) = envelope.take_item_by(|item| item.ty() == &ItemType::Attachment) { | ||
if let Some(&AttachmentType::Prosperodump) = item.attachment_type() { | ||
// TODO: Do some work here | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
// FIXME: Decide on weather we also want to keep the double function here to do the extraction work on the custom tags. | ||
pub fn process( | ||
_managed_envelope: &mut TypedEnvelope<ErrorGroup>, | ||
_event: &mut Annotated<Event>, | ||
) -> Result<Option<EventFullyNormalized>, ProcessingError> { | ||
Ok(None) | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.