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