11use anyhow:: Result ;
22use axum:: {
33 body:: Body ,
4- http:: { HeaderMap , HeaderValue , Method , Request , Response , StatusCode } ,
4+ http:: { uri , HeaderMap , HeaderValue , Method , Request , Response , StatusCode } ,
55} ;
66use reqwest:: Client ;
77use std:: sync:: Arc ;
88use tracing:: { debug, error, info} ;
99
1010use crate :: { models:: ProxyContext , AppState } ;
1111
12+ fn infer_origin ( headers : & HeaderMap ) -> Result < String > {
13+ let mut scheme = "http" . to_string ( ) ;
14+ let mut host = "" . to_string ( ) ;
15+
16+ if let Some ( uri_str) = headers. get ( "x-forwarded-uri" ) . and_then ( |v| v. to_str ( ) . ok ( ) ) {
17+ if let Ok ( u) = uri:: Uri :: try_from ( uri_str) {
18+ if let Some ( s) = u. scheme_str ( ) {
19+ scheme = s. to_string ( ) ;
20+ }
21+ if let Some ( h) = u. host ( ) {
22+ host = h. to_string ( ) ;
23+ }
24+ }
25+ }
26+
27+ if let Some ( origin) = headers
28+ . get ( "x-forwarded-origin" )
29+ . and_then ( |v| v. to_str ( ) . ok ( ) )
30+ {
31+ return Ok ( origin. to_string ( ) ) ;
32+ }
33+
34+ if let Some ( h) = headers
35+ . get ( "x-forwarded-host" )
36+ . and_then ( |v| v. to_str ( ) . ok ( ) )
37+ {
38+ host = h. to_string ( ) ;
39+ }
40+
41+ if let Some ( s) = headers
42+ . get ( "x-forwarded-proto" )
43+ . and_then ( |v| v. to_str ( ) . ok ( ) )
44+ {
45+ scheme = s. to_string ( ) ;
46+ }
47+
48+ if let Some ( h) = headers. get ( "host" ) . and_then ( |v| v. to_str ( ) . ok ( ) ) {
49+ host = h. to_string ( ) ;
50+ }
51+
52+ if !host. is_empty ( ) {
53+ return Ok ( format ! ( "{}://{}" , scheme, host) ) ;
54+ }
55+
56+ Err ( anyhow:: anyhow!( "Cannot extract host from request headers" ) )
57+ }
58+
1259pub async fn proxy_request (
1360 state : Arc < AppState > ,
1461 request : Request < Body > ,
@@ -28,7 +75,6 @@ pub async fn proxy_request(
2875
2976 // Extract user context from request extensions (clone it before consuming request)
3077 let proxy_context = request. extensions ( ) . get :: < ProxyContext > ( ) . cloned ( ) ;
31-
3278 info ! (
3379 method = %method,
3480 path = %path,
@@ -121,13 +167,44 @@ pub async fn proxy_request(
121167 continue ;
122168 }
123169
170+ // Replace header location with the original request origin
171+ if name_str. eq_ignore_ascii_case ( "location" ) {
172+ let loc = value. to_str ( ) . map_err ( |e| {
173+ error ! ( "Invalid location header value: {}" , e) ;
174+ StatusCode :: BAD_GATEWAY
175+ } ) ?;
176+ let origin = infer_origin ( & headers) . map_err ( |e| {
177+ error ! ( "Failed to infer origin from request: {}" , e) ;
178+ StatusCode :: BAD_REQUEST
179+ } ) ?;
180+ let new_location = match uri:: Uri :: try_from ( loc) {
181+ Ok ( u) => {
182+ let path_and_query = u. path_and_query ( ) . map_or ( "" , |pq| pq. as_str ( ) ) ;
183+ format ! ( "{}{}" , origin, path_and_query)
184+ }
185+ Err ( _) => {
186+ // If location is a relative path, just prepend the origin
187+ format ! ( "{}{}" , origin, loc)
188+ }
189+ } ;
190+ if let Ok ( new_value) = HeaderValue :: from_str ( & new_location) {
191+ response_headers. insert ( name. clone ( ) , new_value) ;
192+ }
193+ continue ;
194+ }
195+
124196 if let Ok ( header_value) = HeaderValue :: from_bytes ( value_bytes) {
125197 if let Ok ( header_name) = name_str. parse :: < axum:: http:: HeaderName > ( ) {
126198 response_headers. insert ( header_name, header_value) ;
127199 }
128200 }
129201 }
130202
203+ debug ! (
204+ headers = ?response_headers,
205+ "Response headers processed"
206+ ) ;
207+
131208 let body_bytes = match response. bytes ( ) . await {
132209 Ok ( bytes) => bytes,
133210 Err ( e) => {
0 commit comments