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 crate::api::proxy_client::proxy_client; 9use std::collections::HashMap; 10use tracing::error; 11 12fn resolve_service_did(did_with_fragment: &str) -> Option<(String, String)> { 13 if did_with_fragment.starts_with("did:web:") { 14 let without_prefix = &did_with_fragment[8..]; 15 let host = without_prefix.split('#').next()?; 16 let url = format!("https://{}", host); 17 let did_without_fragment = format!("did:web:{}", host); 18 Some((url, did_without_fragment)) 19 } else if did_with_fragment.starts_with("did:plc:") { 20 None 21 } else { 22 None 23 } 24} 25 26pub async fn proxy_handler( 27 State(state): State<AppState>, 28 Path(method): Path<String>, 29 method_verb: Method, 30 headers: HeaderMap, 31 Query(params): Query<HashMap<String, String>>, 32 body: Bytes, 33) -> Response { 34 let proxy_header = headers 35 .get("atproto-proxy") 36 .and_then(|h| h.to_str().ok()) 37 .map(|s| s.to_string()); 38 let (appview_url, service_aud) = match &proxy_header { 39 Some(did_str) => { 40 let (url, did_without_fragment) = match resolve_service_did(did_str) { 41 Some(resolved) => resolved, 42 None => { 43 error!(did = %did_str, "Could not resolve service DID"); 44 return (StatusCode::BAD_GATEWAY, "Could not resolve service DID").into_response(); 45 } 46 }; 47 (url, Some(did_without_fragment)) 48 } 49 None => { 50 let url = match std::env::var("APPVIEW_URL") { 51 Ok(url) => url, 52 Err(_) => { 53 return (StatusCode::BAD_GATEWAY, "No upstream AppView configured").into_response(); 54 } 55 }; 56 let aud = std::env::var("APPVIEW_DID").ok(); 57 (url, aud) 58 } 59 }; 60 let target_url = format!("{}/xrpc/{}", appview_url, method); 61 let client = proxy_client(); 62 let mut request_builder = client.request(method_verb, &target_url).query(&params); 63 let mut auth_header_val = headers.get("Authorization").map(|h| h.clone()); 64 if let Some(aud) = &service_aud { 65 if let Some(token) = crate::auth::extract_bearer_token_from_header( 66 headers.get("Authorization").and_then(|h| h.to_str().ok()) 67 ) { 68 if let Ok(auth_user) = crate::auth::validate_bearer_token(&state.db, &token).await { 69 if let Some(key_bytes) = auth_user.key_bytes { 70 if let Ok(new_token) = 71 crate::auth::create_service_token(&auth_user.did, aud, &method, &key_bytes) 72 { 73 if let Ok(val) = 74 axum::http::HeaderValue::from_str(&format!("Bearer {}", new_token)) 75 { 76 auth_header_val = Some(val); 77 } 78 } 79 } 80 } 81 } 82 } 83 if let Some(val) = auth_header_val { 84 request_builder = request_builder.header("Authorization", val); 85 } 86 for (key, value) in headers.iter() { 87 if key != "host" && key != "content-length" && key != "authorization" { 88 request_builder = request_builder.header(key, value); 89 } 90 } 91 request_builder = request_builder.body(body); 92 match request_builder.send().await { 93 Ok(resp) => { 94 let status = resp.status(); 95 let headers = resp.headers().clone(); 96 let body = match resp.bytes().await { 97 Ok(b) => b, 98 Err(e) => { 99 error!("Error reading proxy response body: {:?}", e); 100 return (StatusCode::BAD_GATEWAY, "Error reading upstream response") 101 .into_response(); 102 } 103 }; 104 let mut response_builder = Response::builder().status(status); 105 for (key, value) in headers.iter() { 106 response_builder = response_builder.header(key, value); 107 } 108 match response_builder.body(axum::body::Body::from(body)) { 109 Ok(r) => r, 110 Err(e) => { 111 error!("Error building proxy response: {:?}", e); 112 (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error").into_response() 113 } 114 } 115 } 116 Err(e) => { 117 error!("Error sending proxy request: {:?}", e); 118 if e.is_timeout() { 119 (StatusCode::GATEWAY_TIMEOUT, "Upstream Timeout").into_response() 120 } else { 121 (StatusCode::BAD_GATEWAY, "Upstream Error").into_response() 122 } 123 } 124 } 125}