use std::net::SocketAddr;
use axum::body::Bytes;
use axum::http::{HeaderMap, HeaderValue, StatusCode, header};
use axum::response::{IntoResponse, Response};
use tracing::{error, instrument};
use super::ip::resolve_true_client_ip_address;
#[instrument(skip_all)]
pub async fn relay_script(client: &reqwest::Client, upstream: &str) -> Response {
let response = match client.get(upstream).send().await {
Ok(response) => response,
Err(error) => {
error!("failed to fetch `{upstream}`: {error}");
return StatusCode::BAD_GATEWAY.into_response();
}
};
let status: StatusCode =
StatusCode::from_u16(response.status().as_u16()).unwrap_or(StatusCode::BAD_GATEWAY);
let mut headers: HeaderMap = HeaderMap::new();
for name in [header::CONTENT_TYPE, header::CACHE_CONTROL, header::ETAG] {
if let Some(value) = response.headers().get(&name)
&& let Ok(value) = HeaderValue::from_bytes(value.as_bytes())
{
headers.insert(name, value);
}
}
match response.bytes().await {
Ok(body) => (status, headers, body).into_response(),
Err(error) => {
error!("failed to read `{upstream}`: {error}");
StatusCode::BAD_GATEWAY.into_response()
}
}
}
#[instrument(skip_all)]
pub async fn relay_event(
client: &reqwest::Client,
upstream: &str,
headers: &HeaderMap,
peer: SocketAddr,
body: Bytes,
) -> Response {
let mut request: reqwest::RequestBuilder = client.post(upstream).body(body).header(
"X-Forwarded-For",
resolve_true_client_ip_address(peer, headers),
);
if let Some(user_agent) = headers.get(header::USER_AGENT) {
request = request.header(header::USER_AGENT, user_agent.clone());
}
if let Some(content_type) = headers.get(header::CONTENT_TYPE) {
request = request.header(header::CONTENT_TYPE, content_type.clone());
}
match request.send().await {
Ok(response) => StatusCode::from_u16(response.status().as_u16())
.unwrap_or(StatusCode::ACCEPTED)
.into_response(),
Err(error) => {
error!("failed to forward an analytics event: {error}");
StatusCode::ACCEPTED.into_response()
}
}
}
#[instrument(skip_all)]
pub async fn relay_envelope(client: &reqwest::Client, upstream: &str, body: Bytes) -> Response {
match client
.post(upstream)
.header(header::CONTENT_TYPE, "application/x-sentry-envelope")
.body(body)
.send()
.await
{
Ok(response) => StatusCode::from_u16(response.status().as_u16())
.unwrap_or(StatusCode::OK)
.into_response(),
Err(error) => {
error!("failed to relay a Sentry envelope: {error}");
StatusCode::OK.into_response()
}
}
}