relay_server/endpoints/
forward.rs1use std::future::Future;
7use std::sync::LazyLock;
8
9use axum::body::Body;
10use axum::extract::Request;
11use axum::handler::Handler;
12use axum::http::{HeaderMap, HeaderValue, StatusCode, Uri};
13use axum::response::{IntoResponse, Response};
14use relay_common::glob2::GlobMatcher;
15use relay_config::ConfigSnapshot;
16use tower_http::limit::RequestBodyLimitLayer;
17
18use crate::extractors::ForwardedFor;
19use crate::service::ServiceState;
20use crate::services::upstream::Method;
21use crate::utils::ForwardRequest;
22
23const API_PATH: &str = "/api/";
25
26async fn handle(
28 state: ServiceState,
29 forwarded_for: ForwardedFor,
30 method: Method,
31 uri: Uri,
32 headers: HeaderMap<HeaderValue>,
33 data: Body,
34) -> impl IntoResponse {
35 let config = state.config();
36
37 if !config.http_forward() {
38 return StatusCode::NOT_FOUND.into_response();
39 }
40
41 if uri.path() == API_PATH || !uri.path().starts_with(API_PATH) {
44 return StatusCode::NOT_FOUND.into_response();
45 }
46
47 ForwardRequest::builder(method, uri.to_string())
48 .with_name("forward")
49 .with_headers(headers)
50 .with_forwarded_for(forwarded_for)
51 .with_body(data)
52 .with_config(&config)
53 .send_to(state.upstream_relay())
54 .await
55 .into_response()
56}
57
58#[derive(Clone, Copy, Debug)]
60enum SpecialRoute {
61 FileUpload,
62 ChunkUpload,
63}
64
65static SPECIAL_ROUTES: LazyLock<GlobMatcher<SpecialRoute>> = LazyLock::new(|| {
67 let mut m = GlobMatcher::new();
68 m.add(
70 "/api/0/projects/*/*/releases/*/files/",
71 SpecialRoute::FileUpload,
72 );
73 m.add(
74 "/api/0/projects/*/*/releases/*/dsyms/",
75 SpecialRoute::FileUpload,
76 );
77 m.add(
79 "/api/0/organizations/*/chunk-upload/",
80 SpecialRoute::ChunkUpload,
81 );
82 m
83});
84
85fn get_limit_for_path(path: &str, config: &ConfigSnapshot) -> usize {
87 match SPECIAL_ROUTES.test(path) {
88 Some(SpecialRoute::FileUpload) => config.max_api_file_upload_size(),
89 Some(SpecialRoute::ChunkUpload) => config.max_api_chunk_upload_size(),
90 None => config.max_api_payload_size(),
91 }
92}
93
94pub fn forward(state: ServiceState, req: Request) -> impl Future<Output = Response> {
107 let limit = get_limit_for_path(req.uri().path(), &state.config());
108 handle
109 .layer(RequestBodyLimitLayer::new(limit))
111 .call(req, state)
112}