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}