this repo has no description
1use crate::state::AppState;
2use axum::{
3 body::Bytes,
4 extract::{Path, Query, State},
5 http::{HeaderMap, Method, StatusCode},
6 response::{IntoResponse, Response},
7};
8use reqwest::Client;
9use std::collections::HashMap;
10use tracing::{error, info};
11
12pub async fn proxy_handler(
13 State(state): State<AppState>,
14 Path(method): Path<String>,
15 method_verb: Method,
16 headers: HeaderMap,
17 Query(params): Query<HashMap<String, String>>,
18 body: Bytes,
19) -> Response {
20 let proxy_header = headers
21 .get("atproto-proxy")
22 .and_then(|h| h.to_str().ok())
23 .map(|s| s.to_string());
24
25 let appview_url = match &proxy_header {
26 Some(url) => url.clone(),
27 None => match std::env::var("APPVIEW_URL") {
28 Ok(url) => url,
29 Err(_) => {
30 return (StatusCode::BAD_GATEWAY, "No upstream AppView configured").into_response();
31 }
32 },
33 };
34
35 let target_url = format!("{}/xrpc/{}", appview_url, method);
36
37 info!("Proxying {} request to {}", method_verb, target_url);
38
39 let client = Client::new();
40
41 let mut request_builder = client.request(method_verb, &target_url).query(¶ms);
42
43 let mut auth_header_val = headers.get("Authorization").map(|h| h.clone());
44
45 if let Some(aud) = &proxy_header {
46 if let Some(token) = crate::auth::extract_bearer_token_from_header(
47 headers.get("Authorization").and_then(|h| h.to_str().ok())
48 ) {
49 if let Ok(auth_user) = crate::auth::validate_bearer_token(&state.db, &token).await {
50 if let Some(key_bytes) = auth_user.key_bytes {
51 if let Ok(new_token) =
52 crate::auth::create_service_token(&auth_user.did, aud, &method, &key_bytes)
53 {
54 if let Ok(val) =
55 axum::http::HeaderValue::from_str(&format!("Bearer {}", new_token))
56 {
57 auth_header_val = Some(val);
58 }
59 }
60 }
61 }
62 }
63 }
64
65 if let Some(val) = auth_header_val {
66 request_builder = request_builder.header("Authorization", val);
67 }
68
69 for (key, value) in headers.iter() {
70 if key != "host" && key != "content-length" && key != "authorization" {
71 request_builder = request_builder.header(key, value);
72 }
73 }
74
75 request_builder = request_builder.body(body);
76
77 match request_builder.send().await {
78 Ok(resp) => {
79 let status = resp.status();
80 let headers = resp.headers().clone();
81 let body = match resp.bytes().await {
82 Ok(b) => b,
83 Err(e) => {
84 error!("Error reading proxy response body: {:?}", e);
85 return (StatusCode::BAD_GATEWAY, "Error reading upstream response")
86 .into_response();
87 }
88 };
89
90 let mut response_builder = Response::builder().status(status);
91
92 for (key, value) in headers.iter() {
93 response_builder = response_builder.header(key, value);
94 }
95
96 match response_builder.body(axum::body::Body::from(body)) {
97 Ok(r) => r,
98 Err(e) => {
99 error!("Error building proxy response: {:?}", e);
100 (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error").into_response()
101 }
102 }
103 }
104 Err(e) => {
105 error!("Error sending proxy request: {:?}", e);
106 if e.is_timeout() {
107 (StatusCode::GATEWAY_TIMEOUT, "Upstream Timeout").into_response()
108 } else {
109 (StatusCode::BAD_GATEWAY, "Upstream Error").into_response()
110 }
111 }
112 }
113}