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