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}