@@ -7,12 +7,12 @@ use edgezero_core::error::EdgeError;
77use edgezero_core:: http:: { header, HeaderMap , HeaderValue , Method , Uri } ;
88use edgezero_core:: proxy:: { ProxyClient , ProxyRequest , ProxyResponse } ;
99use fastly:: {
10- http:: body:: StreamingBody , Backend , Request as FastlyRequest , Response as FastlyResponse ,
10+ error:: anyhow, http:: body:: StreamingBody , Backend , Request as FastlyRequest ,
11+ Response as FastlyResponse ,
1112} ;
1213use futures_util:: stream:: { BoxStream , StreamExt } ;
13- use std:: collections:: hash_map:: DefaultHasher ;
14- use std:: hash:: { Hash , Hasher } ;
1514use std:: io:: { self , Write } ;
15+ use std:: time:: Duration ;
1616
1717const BACKEND_PREFIX : & str = "edgezero-dynamic-" ;
1818
@@ -22,10 +22,10 @@ pub struct FastlyProxyClient;
2222impl ProxyClient for FastlyProxyClient {
2323 async fn send ( & self , request : ProxyRequest ) -> Result < ProxyResponse , EdgeError > {
2424 let ( method, uri, headers, body, _ext) = request. into_parts ( ) ;
25- let backend = ensure_backend ( & uri) ?;
25+ let backend_name = ensure_backend ( & uri) ?;
2626 let fastly_request = build_fastly_request ( method, & uri, headers) ?;
2727 let ( mut streaming_body, pending_request) = fastly_request
28- . send_async_streaming ( backend . name ( ) )
28+ . send_async_streaming ( & backend_name )
2929 . map_err ( EdgeError :: internal) ?;
3030 forward_request_body ( body, & mut streaming_body) . await ?;
3131 streaming_body. finish ( ) . map_err ( EdgeError :: internal) ?;
@@ -88,45 +88,66 @@ async fn forward_request_body(
8888 Ok ( ( ) )
8989}
9090
91- fn ensure_backend ( uri : & Uri ) -> Result < Backend , EdgeError > {
91+ fn ensure_backend ( uri : & Uri ) -> Result < String , EdgeError > {
9292 let host = uri
9393 . host ( )
9494 . ok_or_else ( || EdgeError :: bad_request ( "proxy target must include host" ) ) ?;
95- let port = uri. port_u16 ( ) ;
96- let target = match port {
97- Some ( port) => format ! ( "{}:{}" , host, port) ,
98- None => host. to_string ( ) ,
99- } ;
10095
101- let name = backend_name ( & target, uri. scheme_str ( ) ) ;
96+ let scheme = uri. scheme_str ( ) . unwrap_or ( "https" ) ;
97+ let is_https = scheme. eq_ignore_ascii_case ( "https" ) ;
10298
103- let host_with_port = match port {
104- Some ( p) => format ! ( "{}:{}" , host, p) ,
105- None => host. to_string ( ) ,
99+ let target_port = match ( uri. port_u16 ( ) , is_https) {
100+ ( Some ( p) , _) => p,
101+ ( None , true ) => 443 ,
102+ ( None , false ) => 80 ,
106103 } ;
107104
108- let builder = Backend :: builder ( & name, & host_with_port) . override_host ( host) ;
105+ let host_with_port = format ! ( "{}:{}" , host, target_port) ;
106+
107+ // Human-readable name: backend_{scheme}_{host}_{port} with dots/colons sanitised
108+ let name_base = format ! ( "{}_{}_{}" , scheme, host, target_port) ;
109+ let backend_name = format ! ( "{}{}" , BACKEND_PREFIX , name_base. replace( [ '.' , ':' ] , "_" ) ) ;
110+
111+ let mut builder = Backend :: builder ( & backend_name, & host_with_port)
112+ . override_host ( host)
113+ . connect_timeout ( Duration :: from_secs ( 1 ) )
114+ . first_byte_timeout ( Duration :: from_secs ( 15 ) )
115+ . between_bytes_timeout ( Duration :: from_secs ( 10 ) ) ;
116+
117+ if is_https {
118+ builder = builder
119+ . enable_ssl ( )
120+ . sni_hostname ( host)
121+ . check_certificate ( host) ;
122+ log:: debug!( "enable ssl for backend: {}" , backend_name) ;
123+ }
109124
110125 match builder. finish ( ) {
111- Ok ( backend) => Ok ( backend) ,
112- Err ( _) => {
113- let mut builder = Backend :: builder ( & name, & target) ;
114- if uri. scheme_str ( ) == Some ( "https" ) {
115- builder = builder. enable_ssl ( ) ;
126+ Ok ( _) => {
127+ log:: debug!(
128+ "created dynamic backend: {} -> {}" ,
129+ backend_name,
130+ host_with_port
131+ ) ;
132+ Ok ( backend_name)
133+ }
134+ Err ( e) => {
135+ let msg = e. to_string ( ) ;
136+ if msg. contains ( "NameInUse" ) || msg. contains ( "already in use" ) {
137+ log:: debug!( "reusing existing dynamic backend: {}" , backend_name) ;
138+ Ok ( backend_name)
139+ } else {
140+ Err ( EdgeError :: internal ( anyhow ! (
141+ "dynamic backend creation failed ({} -> {}): {}" ,
142+ backend_name,
143+ host_with_port,
144+ msg
145+ ) ) )
116146 }
117- builder. finish ( ) . map_err ( EdgeError :: internal)
118147 }
119148 }
120149}
121150
122- fn backend_name ( target : & str , scheme : Option < & str > ) -> String {
123- let mut hasher = DefaultHasher :: new ( ) ;
124- target. hash ( & mut hasher) ;
125- scheme. hash ( & mut hasher) ;
126- let hash = hasher. finish ( ) ;
127- format ! ( "{}{:016x}" , BACKEND_PREFIX , hash)
128- }
129-
130151fn convert_response ( fastly_response : & mut FastlyResponse ) -> Result < ProxyResponse , EdgeError > {
131152 let status = fastly_response. get_status ( ) ;
132153 let mut proxy_response = ProxyResponse :: new ( status, Body :: empty ( ) ) ;
0 commit comments