@@ -451,6 +451,28 @@ async def get_event_auth(
451451
452452 return signed_auth
453453
454+ def _is_unknown_endpoint (
455+ self , e : HttpResponseException , synapse_error : Optional [SynapseError ] = None
456+ ) -> bool :
457+ """
458+ Returns true if the response was due to an endpoint being unimplemented.
459+
460+ Args:
461+ e: The error response received from the remote server.
462+ synapse_error: The above error converted to a SynapseError. This is
463+ automatically generated if not provided.
464+
465+ """
466+ if synapse_error is None :
467+ synapse_error = e .to_synapse_error ()
468+ # There is no good way to detect an "unknown" endpoint.
469+ #
470+ # Dendrite returns a 404 (with no body); synapse returns a 400
471+ # with M_UNRECOGNISED.
472+ return e .code == 404 or (
473+ e .code == 400 and synapse_error .errcode == Codes .UNRECOGNIZED
474+ )
475+
454476 async def _try_destination_list (
455477 self ,
456478 description : str ,
@@ -468,9 +490,9 @@ async def _try_destination_list(
468490 callback: Function to run for each server. Passed a single
469491 argument: the server_name to try.
470492
471- If the callback raises a CodeMessageException with a 300/400 code,
472- attempts to perform the operation stop immediately and the exception is
473- reraised.
493+ If the callback raises a CodeMessageException with a 300/400 code or
494+ an UnsupportedRoomVersionError, attempts to perform the operation
495+ stop immediately and the exception is reraised.
474496
475497 Otherwise, if the callback raises an Exception the error is logged and the
476498 next server tried. Normally the stacktrace is logged but this is
@@ -492,8 +514,7 @@ async def _try_destination_list(
492514 continue
493515
494516 try :
495- res = await callback (destination )
496- return res
517+ return await callback (destination )
497518 except InvalidResponseError as e :
498519 logger .warning ("Failed to %s via %s: %s" , description , destination , e )
499520 except UnsupportedRoomVersionError :
@@ -502,17 +523,15 @@ async def _try_destination_list(
502523 synapse_error = e .to_synapse_error ()
503524 failover = False
504525
526+ # Failover on an internal server error, or if the destination
527+ # doesn't implemented the endpoint for some reason.
505528 if 500 <= e .code < 600 :
506529 failover = True
507530
508- elif failover_on_unknown_endpoint :
509- # there is no good way to detect an "unknown" endpoint. Dendrite
510- # returns a 404 (with no body); synapse returns a 400
511- # with M_UNRECOGNISED.
512- if e .code == 404 or (
513- e .code == 400 and synapse_error .errcode == Codes .UNRECOGNIZED
514- ):
515- failover = True
531+ elif failover_on_unknown_endpoint and self ._is_unknown_endpoint (
532+ e , synapse_error
533+ ):
534+ failover = True
516535
517536 if not failover :
518537 raise synapse_error from e
@@ -570,9 +589,8 @@ async def make_membership_event(
570589 UnsupportedRoomVersionError: if remote responds with
571590 a room version we don't understand.
572591
573- SynapseError: if the chosen remote server returns a 300/400 code.
574-
575- RuntimeError: if no servers were reachable.
592+ SynapseError: if the chosen remote server returns a 300/400 code, or
593+ no servers successfully handle the request.
576594 """
577595 valid_memberships = {Membership .JOIN , Membership .LEAVE }
578596 if membership not in valid_memberships :
@@ -642,9 +660,8 @@ async def send_join(
642660 ``auth_chain``.
643661
644662 Raises:
645- SynapseError: if the chosen remote server returns a 300/400 code.
646-
647- RuntimeError: if no servers were reachable.
663+ SynapseError: if the chosen remote server returns a 300/400 code, or
664+ no servers successfully handle the request.
648665 """
649666
650667 async def send_request (destination ) -> Dict [str , Any ]:
@@ -673,7 +690,7 @@ async def send_request(destination) -> Dict[str, Any]:
673690 if create_event is None :
674691 # If the state doesn't have a create event then the room is
675692 # invalid, and it would fail auth checks anyway.
676- raise SynapseError ( 400 , "No create event in state" )
693+ raise InvalidResponseError ( "No create event in state" )
677694
678695 # the room version should be sane.
679696 create_room_version = create_event .content .get (
@@ -746,16 +763,11 @@ async def _do_send_join(self, destination: str, pdu: EventBase) -> JsonDict:
746763 content = pdu .get_pdu_json (time_now ),
747764 )
748765 except HttpResponseException as e :
749- if e .code in [400 , 404 ]:
750- err = e .to_synapse_error ()
751-
752- # If we receive an error response that isn't a generic error, or an
753- # unrecognised endpoint error, we assume that the remote understands
754- # the v2 invite API and this is a legitimate error.
755- if err .errcode not in [Codes .UNKNOWN , Codes .UNRECOGNIZED ]:
756- raise err
757- else :
758- raise e .to_synapse_error ()
766+ # If an error is received that is due to an unrecognised endpoint,
767+ # fallback to the v1 endpoint. Otherwise consider it a legitmate error
768+ # and raise.
769+ if not self ._is_unknown_endpoint (e ):
770+ raise
759771
760772 logger .debug ("Couldn't send_join with the v2 API, falling back to the v1 API" )
761773
@@ -802,6 +814,11 @@ async def _do_send_invite(
802814
803815 Returns:
804816 The event as a dict as returned by the remote server
817+
818+ Raises:
819+ SynapseError: if the remote server returns an error or if the server
820+ only supports the v1 endpoint and a room version other than "1"
821+ or "2" is requested.
805822 """
806823 time_now = self ._clock .time_msec ()
807824
@@ -817,28 +834,19 @@ async def _do_send_invite(
817834 },
818835 )
819836 except HttpResponseException as e :
820- if e .code in [400 , 404 ]:
821- err = e .to_synapse_error ()
822-
823- # If we receive an error response that isn't a generic error, we
824- # assume that the remote understands the v2 invite API and this
825- # is a legitimate error.
826- if err .errcode != Codes .UNKNOWN :
827- raise err
828-
829- # Otherwise, we assume that the remote server doesn't understand
830- # the v2 invite API. That's ok provided the room uses old-style event
831- # IDs.
837+ # If an error is received that is due to an unrecognised endpoint,
838+ # fallback to the v1 endpoint if the room uses old-style event IDs.
839+ # Otherwise consider it a legitmate error and raise.
840+ err = e .to_synapse_error ()
841+ if self ._is_unknown_endpoint (e , err ):
832842 if room_version .event_format != EventFormatVersions .V1 :
833843 raise SynapseError (
834844 400 ,
835845 "User's homeserver does not support this room version" ,
836846 Codes .UNSUPPORTED_ROOM_VERSION ,
837847 )
838- elif e .code in (403 , 429 ):
839- raise e .to_synapse_error ()
840848 else :
841- raise
849+ raise err
842850
843851 # Didn't work, try v1 API.
844852 # Note the v1 API returns a tuple of `(200, content)`
@@ -865,9 +873,8 @@ async def send_leave(self, destinations: Iterable[str], pdu: EventBase) -> None:
865873 pdu: event to be sent
866874
867875 Raises:
868- SynapseError if the chosen remote server returns a 300/400 code.
869-
870- RuntimeError if no servers were reachable.
876+ SynapseError: if the chosen remote server returns a 300/400 code, or
877+ no servers successfully handle the request.
871878 """
872879
873880 async def send_request (destination : str ) -> None :
@@ -889,16 +896,11 @@ async def _do_send_leave(self, destination: str, pdu: EventBase) -> JsonDict:
889896 content = pdu .get_pdu_json (time_now ),
890897 )
891898 except HttpResponseException as e :
892- if e .code in [400 , 404 ]:
893- err = e .to_synapse_error ()
894-
895- # If we receive an error response that isn't a generic error, or an
896- # unrecognised endpoint error, we assume that the remote understands
897- # the v2 invite API and this is a legitimate error.
898- if err .errcode not in [Codes .UNKNOWN , Codes .UNRECOGNIZED ]:
899- raise err
900- else :
901- raise e .to_synapse_error ()
899+ # If an error is received that is due to an unrecognised endpoint,
900+ # fallback to the v1 endpoint. Otherwise consider it a legitmate error
901+ # and raise.
902+ if not self ._is_unknown_endpoint (e ):
903+ raise
902904
903905 logger .debug ("Couldn't send_leave with the v2 API, falling back to the v1 API" )
904906
0 commit comments