this repo has no description
1use crate::api::proxy_client::proxy_client;
2use crate::state::AppState;
3use axum::{
4 body::Bytes,
5 extract::{Path, RawQuery, State},
6 http::{HeaderMap, Method, StatusCode},
7 response::{IntoResponse, Response},
8};
9use tracing::{error, info, warn};
10
11pub async fn proxy_handler(
12 State(state): State<AppState>,
13 Path(method): Path<String>,
14 method_verb: Method,
15 headers: HeaderMap,
16 RawQuery(query): RawQuery,
17 body: Bytes,
18) -> Response {
19 let proxy_header = headers
20 .get("atproto-proxy")
21 .and_then(|h| h.to_str().ok())
22 .map(|s| s.to_string());
23 let (appview_url, service_aud) = match &proxy_header {
24 Some(did_str) => {
25 let did_without_fragment = did_str.split('#').next().unwrap_or(did_str).to_string();
26 match state.appview_registry.resolve_appview_did(&did_without_fragment).await {
27 Some(resolved) => (resolved.url, Some(resolved.did)),
28 None => {
29 error!(did = %did_str, "Could not resolve service DID");
30 return (StatusCode::BAD_GATEWAY, "Could not resolve service DID")
31 .into_response();
32 }
33 }
34 }
35 None => {
36 match state.appview_registry.get_appview_for_method(&method).await {
37 Some(resolved) => (resolved.url, Some(resolved.did)),
38 None => {
39 return (StatusCode::BAD_GATEWAY, "No upstream AppView configured for this method")
40 .into_response();
41 }
42 }
43 }
44 };
45 let target_url = match &query {
46 Some(q) => format!("{}/xrpc/{}?{}", appview_url, method, q),
47 None => format!("{}/xrpc/{}", appview_url, method),
48 };
49 info!("Proxying {} request to {}", method_verb, target_url);
50 let client = proxy_client();
51 let mut request_builder = client.request(method_verb, &target_url);
52 let mut auth_header_val = headers.get("Authorization").cloned();
53 if let Some(aud) = &service_aud {
54 if let Some(token) = crate::auth::extract_bearer_token_from_header(
55 headers.get("Authorization").and_then(|h| h.to_str().ok()),
56 ) {
57 match crate::auth::validate_bearer_token(&state.db, &token).await {
58 Ok(auth_user) => {
59 if let Some(key_bytes) = auth_user.key_bytes {
60 match crate::auth::create_service_token(&auth_user.did, aud, &method, &key_bytes) {
61 Ok(new_token) => {
62 if let Ok(val) = axum::http::HeaderValue::from_str(&format!("Bearer {}", new_token)) {
63 auth_header_val = Some(val);
64 }
65 }
66 Err(e) => {
67 warn!("Failed to create service token: {:?}", e);
68 }
69 }
70 }
71 }
72 Err(e) => {
73 warn!("Token validation failed: {:?}", e);
74 }
75 }
76 }
77 }
78 if let Some(val) = auth_header_val {
79 request_builder = request_builder.header("Authorization", val);
80 }
81 for header_name in crate::api::proxy_client::HEADERS_TO_FORWARD {
82 if let Some(val) = headers.get(*header_name) {
83 request_builder = request_builder.header(*header_name, val);
84 }
85 }
86 if !body.is_empty() {
87 request_builder = request_builder.body(body);
88 }
89 match request_builder.send().await {
90 Ok(resp) => {
91 let status = resp.status();
92 let headers = resp.headers().clone();
93 let body = match resp.bytes().await {
94 Ok(b) => b,
95 Err(e) => {
96 error!("Error reading proxy response body: {:?}", e);
97 return (StatusCode::BAD_GATEWAY, "Error reading upstream response")
98 .into_response();
99 }
100 };
101 let mut response_builder = Response::builder().status(status);
102 for header_name in crate::api::proxy_client::RESPONSE_HEADERS_TO_FORWARD {
103 if let Some(val) = headers.get(*header_name) {
104 response_builder = response_builder.header(*header_name, val);
105 }
106 }
107 match response_builder.body(axum::body::Body::from(body)) {
108 Ok(r) => r,
109 Err(e) => {
110 error!("Error building proxy response: {:?}", e);
111 (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error").into_response()
112 }
113 }
114 }
115 Err(e) => {
116 error!("Error sending proxy request: {:?}", e);
117 if e.is_timeout() {
118 (StatusCode::GATEWAY_TIMEOUT, "Upstream Timeout").into_response()
119 } else {
120 (StatusCode::BAD_GATEWAY, "Upstream Error").into_response()
121 }
122 }
123 }
124}