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