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(auth_val) = &auth_header_val {
47 if let Ok(token) = auth_val.to_str() {
48 let token = token.replace("Bearer ", "");
49 if let Ok(did) = crate::auth::get_did_from_token(&token) {
50 let key_row = sqlx::query!("SELECT k.key_bytes FROM user_keys k JOIN users u ON k.user_id = u.id WHERE u.did = $1", did)
51 .fetch_optional(&state.db)
52 .await;
53
54 if let Ok(Some(row)) = key_row {
55 if let Ok(new_token) =
56 crate::auth::create_service_token(&did, aud, &method, &row.key_bytes)
57 {
58 if let Ok(val) =
59 axum::http::HeaderValue::from_str(&format!("Bearer {}", new_token))
60 {
61 auth_header_val = Some(val);
62 }
63 }
64 }
65 }
66 }
67 }
68 }
69
70 if let Some(val) = auth_header_val {
71 request_builder = request_builder.header("Authorization", val);
72 }
73
74 for (key, value) in headers.iter() {
75 if key != "host" && key != "content-length" && key != "authorization" {
76 request_builder = request_builder.header(key, value);
77 }
78 }
79
80 request_builder = request_builder.body(body);
81
82 match request_builder.send().await {
83 Ok(resp) => {
84 let status = resp.status();
85 let headers = resp.headers().clone();
86 let body = match resp.bytes().await {
87 Ok(b) => b,
88 Err(e) => {
89 error!("Error reading proxy response body: {:?}", e);
90 return (StatusCode::BAD_GATEWAY, "Error reading upstream response")
91 .into_response();
92 }
93 };
94
95 let mut response_builder = Response::builder().status(status);
96
97 for (key, value) in headers.iter() {
98 response_builder = response_builder.header(key, value);
99 }
100
101 match response_builder.body(axum::body::Body::from(body)) {
102 Ok(r) => r,
103 Err(e) => {
104 error!("Error building proxy response: {:?}", e);
105 (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error").into_response()
106 }
107 }
108 }
109 Err(e) => {
110 error!("Error sending proxy request: {:?}", e);
111 if e.is_timeout() {
112 (StatusCode::GATEWAY_TIMEOUT, "Upstream Timeout").into_response()
113 } else {
114 (StatusCode::BAD_GATEWAY, "Upstream Error").into_response()
115 }
116 }
117 }
118}