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 reqwest::Client; 9use std::collections::HashMap; 10use tracing::{error, info}; 11 12pub async fn proxy_handler( 13 State(state): State<AppState>, 14 Path(method): Path<String>, 15 method_verb: Method, 16 headers: HeaderMap, 17 Query(params): Query<HashMap<String, String>>, 18 body: Bytes, 19) -> Response { 20 let proxy_header = headers 21 .get("atproto-proxy") 22 .and_then(|h| h.to_str().ok()) 23 .map(|s| s.to_string()); 24 25 let appview_url = match &proxy_header { 26 Some(url) => url.clone(), 27 None => match std::env::var("APPVIEW_URL") { 28 Ok(url) => url, 29 Err(_) => { 30 return (StatusCode::BAD_GATEWAY, "No upstream AppView configured").into_response(); 31 } 32 }, 33 }; 34 35 let target_url = format!("{}/xrpc/{}", appview_url, method); 36 37 info!("Proxying {} request to {}", method_verb, target_url); 38 39 let client = Client::new(); 40 41 let mut request_builder = client.request(method_verb, &target_url).query(&params); 42 43 let mut auth_header_val = headers.get("Authorization").map(|h| h.clone()); 44 45 if let Some(aud) = &proxy_header { 46 if let Some(token) = crate::auth::extract_bearer_token_from_header( 47 headers.get("Authorization").and_then(|h| h.to_str().ok()) 48 ) { 49 if let Ok(did) = crate::auth::get_did_from_token(&token) { 50 let key_row = sqlx::query!("SELECT k.key_bytes, k.encryption_version FROM user_keys k JOIN users u ON k.user_id = u.id WHERE u.did = $1", did) 51 .fetch_optional(&state.db) 52 .await; 53 54 if let Ok(Some(row)) = key_row { 55 if let Ok(decrypted_key) = crate::config::decrypt_key(&row.key_bytes, row.encryption_version) { 56 if let Ok(new_token) = 57 crate::auth::create_service_token(&did, aud, &method, &decrypted_key) 58 { 59 if let Ok(val) = 60 axum::http::HeaderValue::from_str(&format!("Bearer {}", new_token)) 61 { 62 auth_header_val = Some(val); 63 } 64 } 65 } 66 } 67 } 68 } 69 } 70 71 if let Some(val) = auth_header_val { 72 request_builder = request_builder.header("Authorization", val); 73 } 74 75 for (key, value) in headers.iter() { 76 if key != "host" && key != "content-length" && key != "authorization" { 77 request_builder = request_builder.header(key, value); 78 } 79 } 80 81 request_builder = request_builder.body(body); 82 83 match request_builder.send().await { 84 Ok(resp) => { 85 let status = resp.status(); 86 let headers = resp.headers().clone(); 87 let body = match resp.bytes().await { 88 Ok(b) => b, 89 Err(e) => { 90 error!("Error reading proxy response body: {:?}", e); 91 return (StatusCode::BAD_GATEWAY, "Error reading upstream response") 92 .into_response(); 93 } 94 }; 95 96 let mut response_builder = Response::builder().status(status); 97 98 for (key, value) in headers.iter() { 99 response_builder = response_builder.header(key, value); 100 } 101 102 match response_builder.body(axum::body::Body::from(body)) { 103 Ok(r) => r, 104 Err(e) => { 105 error!("Error building proxy response: {:?}", e); 106 (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error").into_response() 107 } 108 } 109 } 110 Err(e) => { 111 error!("Error sending proxy request: {:?}", e); 112 if e.is_timeout() { 113 (StatusCode::GATEWAY_TIMEOUT, "Upstream Timeout").into_response() 114 } else { 115 (StatusCode::BAD_GATEWAY, "Upstream Error").into_response() 116 } 117 } 118 } 119}