11use crate :: token:: { self , Claims } ;
2- use crate :: twin:: { RelayDomains , TwinDB } ;
2+ use crate :: twin:: TwinDB ;
33use crate :: types:: { Envelope , EnvelopeExt , Pong } ;
44use anyhow:: { Context , Result } ;
55use futures:: stream:: SplitSink ;
@@ -208,6 +208,16 @@ async fn federation<D: TwinDB, R: RateLimiter>(
208208 Envelope :: parse_from_bytes ( & body) . map_err ( |err| HttpError :: BadRequest ( err. to_string ( ) ) ) ?;
209209
210210 let dst: SessionID = ( & envelope. destination ) . into ( ) ;
211+
212+ // fast-fail: if requested and destination is not local, return error immediately
213+ if has_fast_fail ( & envelope) && !data. switch . is_local ( & dst) . await {
214+ // destination session doesn’t exist on this relay
215+ return Response :: builder ( )
216+ . status ( http:: StatusCode :: NOT_FOUND )
217+ . body ( Body :: from ( "destination offline" ) )
218+ . map_err ( HttpError :: Http ) ;
219+ }
220+
211221 data. switch . send ( & dst, & body) . await ?;
212222
213223 Response :: builder ( )
@@ -216,25 +226,13 @@ async fn federation<D: TwinDB, R: RateLimiter>(
216226 . map_err ( HttpError :: Http )
217227}
218228
219- async fn update_cache_relays ( envelope : & Envelope , twin_db : & impl TwinDB ) -> Result < ( ) > {
220- if envelope. relays . is_empty ( ) {
221- return Ok ( ( ) ) ;
222- }
223- let mut twin = twin_db
224- . get_twin ( envelope. source . twin . into ( ) )
225- . await ?
226- . ok_or_else ( || anyhow:: Error :: msg ( "unknown twin source" ) ) ?;
227- let envelope_relays = RelayDomains :: new ( & envelope. relays ) ;
228- match twin. relay {
229- Some ( twin_relays) => {
230- if twin_relays == envelope_relays {
231- return Ok ( ( ) ) ;
232- }
233- twin. relay = Some ( envelope_relays) ;
234- }
235- None => twin. relay = Some ( envelope_relays) ,
236- }
237- twin_db. set_twin ( twin) . await
229+ #[ inline]
230+ fn has_fast_fail ( envelope : & Envelope ) -> bool {
231+ envelope
232+ . tags
233+ . as_deref ( )
234+ . map ( |t| t. contains ( "fast-fail" ) )
235+ . unwrap_or ( false )
238236}
239237
240238type Writer = SplitSink < WebSocketStream < Upgraded > , Message > ;
@@ -378,9 +376,10 @@ impl<M: Metrics, D: TwinDB> Session<M, D> {
378376 }
379377
380378 let is_local = self . switch . is_local ( & dst) . await ;
381- // it's safe to update the local cache since we already authenticated
382- // the twin hence we trust their information.
383- update_cache_relays ( envelope, & self . twins ) . await ?;
379+ // fast-fail: if requested and destination is not local, return error immediately
380+ if has_fast_fail ( envelope) && !is_local {
381+ anyhow:: bail!( "destination offline" ) ;
382+ }
384383
385384 // check if the dst twin id is already connected locally
386385 // if so, we don't have to check federation and directly
0 commit comments