Add a method for Synapse modules to carry out HTTP federation requests
Provides a fairly basic interface for Synapse modules to complete HTTP federation requests. Custom error types were used in order to prevent stabilising any of the internal MatrixFederationHttpClient code.
This commit is contained in:
@@ -37,7 +37,12 @@ from twisted.internet import defer
|
||||
from twisted.web.resource import Resource
|
||||
|
||||
from synapse.api import errors
|
||||
from synapse.api.errors import SynapseError
|
||||
from synapse.api.errors import (
|
||||
FederationDeniedError,
|
||||
HttpResponseException,
|
||||
RequestSendFailed,
|
||||
SynapseError,
|
||||
)
|
||||
from synapse.events import EventBase
|
||||
from synapse.events.presence_router import (
|
||||
GET_INTERESTED_USERS_CALLBACK,
|
||||
@@ -129,6 +134,14 @@ from synapse.util import Clock
|
||||
from synapse.util.async_helpers import maybe_awaitable
|
||||
from synapse.util.caches.descriptors import CachedFunction, cached as _cached
|
||||
from synapse.util.frozenutils import freeze
|
||||
from synapse.util.retryutils import NotRetryingDestination
|
||||
|
||||
from .errors import (
|
||||
FederationHttpDeniedException,
|
||||
FederationHttpNotRetryingDestinationException,
|
||||
FederationHttpRequestSendFailedException,
|
||||
FederationHttpResponseException,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from synapse.app.generic_worker import GenericWorkerSlavedStore
|
||||
@@ -1612,6 +1625,123 @@ class ModuleApi:
|
||||
deactivation=deactivation,
|
||||
)
|
||||
|
||||
async def _try_federation_http_request(
|
||||
self,
|
||||
method: str,
|
||||
remote_server_name: str,
|
||||
path: str,
|
||||
query_parameters: Optional[Dict[str, Any]],
|
||||
body: Optional[JsonDict] = None,
|
||||
timeout: Optional[int] = None,
|
||||
) -> Union[JsonDict, List]:
|
||||
"""
|
||||
Send a federation request to a remote homeserver and return the response.
|
||||
|
||||
This method assumes the `method` argument is fully capitalised.
|
||||
|
||||
A helper method for self.send_federation_http_request, see that method for
|
||||
more details.
|
||||
"""
|
||||
assert method in ["GET", "PUT", "POST", "DELETE"]
|
||||
|
||||
fed_client = self._hs.get_federation_http_client()
|
||||
|
||||
if method == "GET":
|
||||
return await fed_client.get_json(
|
||||
destination=remote_server_name,
|
||||
path=path,
|
||||
args=query_parameters,
|
||||
timeout=timeout,
|
||||
)
|
||||
elif method == "PUT":
|
||||
return await fed_client.put_json(
|
||||
destination=remote_server_name,
|
||||
path=path,
|
||||
args=query_parameters,
|
||||
data=body,
|
||||
timeout=timeout,
|
||||
)
|
||||
elif method == "POST":
|
||||
return await fed_client.post_json(
|
||||
destination=remote_server_name,
|
||||
path=path,
|
||||
args=query_parameters,
|
||||
data=body,
|
||||
timeout=timeout,
|
||||
)
|
||||
elif method == "DELETE":
|
||||
return await fed_client.delete_json(
|
||||
destination=remote_server_name,
|
||||
path=path,
|
||||
args=query_parameters,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
return {}
|
||||
|
||||
async def send_federation_http_request(
|
||||
self,
|
||||
method: str,
|
||||
remote_server_name: str,
|
||||
path: str,
|
||||
query_parameters: Optional[Dict[str, Any]],
|
||||
body: Optional[JsonDict] = None,
|
||||
timeout: Optional[int] = None,
|
||||
) -> Union[JsonDict, List]:
|
||||
"""
|
||||
Send an HTTP federation request to a remote homeserver.
|
||||
|
||||
Added in Synapse v1.79.0.
|
||||
|
||||
If the request is successful, the parsed response body will be returned. If
|
||||
unsuccessful, an exception will be raised. Callers are expected to handle the
|
||||
possible exception cases. See exception class docstrings for a more detailed
|
||||
explanation of each.
|
||||
|
||||
Args:
|
||||
method: The HTTP method to use. Must be one of: "GET", "PUT", "POST",
|
||||
"DELETE".
|
||||
remote_server_name: The remote server to send the request to. This method
|
||||
will resolve delegated homeserver URLs automatically (well-known etc).
|
||||
path: The HTTP path for the request.
|
||||
query_parameters: Any query parameters for the request.
|
||||
body: The body of the request.
|
||||
timeout: The timeout in seconds to wait before giving up on a request.
|
||||
|
||||
Returns:
|
||||
The response to the request as a Python object.
|
||||
|
||||
Raises:
|
||||
FederationHttpResponseException: If we get an HTTP response code >= 300
|
||||
(except 429).
|
||||
FederationHttpNotRetryingDestinationException: If the homeserver believes the
|
||||
remote homeserver is down and is not yet ready to attempt to contact it.
|
||||
FederationHttpDeniedException: If this destination is not on the local
|
||||
homeserver's configured federation whitelist.
|
||||
FederationHttpRequestSendFailedException: If there were problems connecting
|
||||
to the remote, due to e.g. DNS failures, connection timeouts etc.
|
||||
"""
|
||||
try:
|
||||
return await self._try_federation_http_request(
|
||||
method.upper(), remote_server_name, path, query_parameters, body, timeout
|
||||
)
|
||||
except HttpResponseException as e:
|
||||
raise FederationHttpResponseException(
|
||||
remote_server_name,
|
||||
status_code=e.code,
|
||||
msg=e.msg,
|
||||
response_body=e.response,
|
||||
)
|
||||
except NotRetryingDestination:
|
||||
raise FederationHttpNotRetryingDestinationException(remote_server_name)
|
||||
except FederationDeniedError:
|
||||
raise FederationHttpDeniedException(remote_server_name)
|
||||
except RequestSendFailed as e:
|
||||
raise FederationHttpRequestSendFailedException(
|
||||
remote_server_name,
|
||||
can_retry=e.can_retry,
|
||||
)
|
||||
|
||||
|
||||
class PublicRoomListManager:
|
||||
"""Contains methods for adding to, removing from and querying whether a room
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
"""Exception types which are exposed as part of the stable module API"""
|
||||
import attr
|
||||
|
||||
from synapse.api.errors import (
|
||||
Codes,
|
||||
@@ -24,6 +25,57 @@ from synapse.config._base import ConfigError
|
||||
from synapse.handlers.push_rules import InvalidRuleException
|
||||
from synapse.storage.push_rule import RuleNotFoundException
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class FederationHttpResponseException(Exception):
|
||||
"""
|
||||
Raised when an HTTP request over federation returns a status code > 300 (and not 429).
|
||||
"""
|
||||
|
||||
remote_server_name: str
|
||||
# The HTTP status code of the response.
|
||||
status_code: int
|
||||
# A human-readable explanation for the error.
|
||||
msg: str
|
||||
# The non-parsed HTTP response body.
|
||||
response_body: bytes
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class FederationHttpNotRetryingDestinationException(Exception):
|
||||
"""
|
||||
Raised when the local homeserver refuses to send traffic to a remote homeserver that
|
||||
it believes is experiencing an outage.
|
||||
"""
|
||||
|
||||
remote_server_name: str
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class FederationHttpDeniedException(Exception):
|
||||
"""
|
||||
Raised when the local homeserver refuses to send federation traffic to a remote
|
||||
homeserver. This is due to the remote homeserver not being on the configured
|
||||
federation whitelist.
|
||||
"""
|
||||
|
||||
remote_server_name: str
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class FederationHttpRequestSendFailedException(Exception):
|
||||
"""
|
||||
Raised when there are problems connecting to the remote homeserver due to e.g.
|
||||
DNS failures, connection timeouts, etc.
|
||||
"""
|
||||
|
||||
remote_server_name: str
|
||||
# Whether the request can be retried with a chance of success. This will be True
|
||||
# if the failure occurred due to e.g. timeouts, a disruption in the connection etc.
|
||||
# Will be false in the case of e.g. a malformed response from the remote homeserver.
|
||||
can_retry: bool
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Codes",
|
||||
"InvalidClientCredentialsError",
|
||||
@@ -32,4 +84,8 @@ __all__ = [
|
||||
"ConfigError",
|
||||
"InvalidRuleException",
|
||||
"RuleNotFoundException",
|
||||
"FederationHttpResponseException",
|
||||
"FederationHttpNotRetryingDestinationException",
|
||||
"FederationHttpDeniedException",
|
||||
"FederationHttpRequestSendFailedException",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user