webserver_base/analytics/
proxy.rs1use std::net::SocketAddr;
17
18use axum::body::Bytes;
19use axum::http::{HeaderMap, HeaderValue, StatusCode, header};
20use axum::response::{IntoResponse, Response};
21use tracing::{error, instrument};
22
23use super::ip::resolve_true_client_ip_address;
24
25#[instrument(skip_all)]
30pub async fn relay_script(client: &reqwest::Client, upstream: &str) -> Response {
31 let response = match client.get(upstream).send().await {
32 Ok(response) => response,
33 Err(error) => {
34 error!("failed to fetch `{upstream}`: {error}");
35 return StatusCode::BAD_GATEWAY.into_response();
36 }
37 };
38
39 let status: StatusCode =
40 StatusCode::from_u16(response.status().as_u16()).unwrap_or(StatusCode::BAD_GATEWAY);
41 let mut headers: HeaderMap = HeaderMap::new();
42 for name in [header::CONTENT_TYPE, header::CACHE_CONTROL, header::ETAG] {
45 if let Some(value) = response.headers().get(&name)
46 && let Ok(value) = HeaderValue::from_bytes(value.as_bytes())
47 {
48 headers.insert(name, value);
49 }
50 }
51
52 match response.bytes().await {
53 Ok(body) => (status, headers, body).into_response(),
54 Err(error) => {
55 error!("failed to read `{upstream}`: {error}");
56 StatusCode::BAD_GATEWAY.into_response()
57 }
58 }
59}
60
61#[instrument(skip_all)]
67pub async fn relay_event(
68 client: &reqwest::Client,
69 upstream: &str,
70 headers: &HeaderMap,
71 peer: SocketAddr,
72 body: Bytes,
73) -> Response {
74 let mut request = client.post(upstream).body(body).header(
75 "X-Forwarded-For",
76 resolve_true_client_ip_address(peer, headers),
77 );
78 if let Some(user_agent) = headers.get(header::USER_AGENT) {
79 request = request.header(header::USER_AGENT, user_agent.clone());
80 }
81 if let Some(content_type) = headers.get(header::CONTENT_TYPE) {
82 request = request.header(header::CONTENT_TYPE, content_type.clone());
83 }
84
85 match request.send().await {
86 Ok(response) => StatusCode::from_u16(response.status().as_u16())
87 .unwrap_or(StatusCode::ACCEPTED)
88 .into_response(),
89 Err(error) => {
90 error!("failed to forward an analytics event: {error}");
91 StatusCode::ACCEPTED.into_response()
93 }
94 }
95}
96
97#[instrument(skip_all)]
104pub async fn relay_envelope(client: &reqwest::Client, upstream: &str, body: Bytes) -> Response {
105 match client
106 .post(upstream)
107 .header(header::CONTENT_TYPE, "application/x-sentry-envelope")
108 .body(body)
109 .send()
110 .await
111 {
112 Ok(response) => StatusCode::from_u16(response.status().as_u16())
113 .unwrap_or(StatusCode::OK)
114 .into_response(),
115 Err(error) => {
116 error!("failed to relay a Sentry envelope: {error}");
117 StatusCode::OK.into_response()
118 }
119 }
120}