summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--changelog.d/4840.feature1
-rw-r--r--synapse/federation/transport/client.py24
-rw-r--r--synapse/http/matrixfederationclient.py45
3 files changed, 59 insertions, 11 deletions
diff --git a/changelog.d/4840.feature b/changelog.d/4840.feature
new file mode 100644
index 0000000000..9d1fd59053
--- /dev/null
+++ b/changelog.d/4840.feature
@@ -0,0 +1 @@
+Remove trailing slashes from certain outbound federation requests. Retry if receiving a 404. Context: #3622.
\ No newline at end of file
diff --git a/synapse/federation/transport/client.py b/synapse/federation/transport/client.py
index 4e8919d657..68808e9203 100644
--- a/synapse/federation/transport/client.py
+++ b/synapse/federation/transport/client.py
@@ -51,8 +51,8 @@ class TransportLayerClient(object):
         logger.debug("get_room_state dest=%s, room=%s",
                      destination, room_id)
 
-        path = _create_v1_path("/state/%s/", room_id)
-        return self.client.get_json(
+        path = _create_v1_path("/state/%s", room_id)
+        return self.client.get_json_with_trailing_slashes_on_404(
             destination, path=path, args={"event_id": event_id},
         )
 
@@ -73,8 +73,8 @@ class TransportLayerClient(object):
         logger.debug("get_room_state_ids dest=%s, room=%s",
                      destination, room_id)
 
-        path = _create_v1_path("/state_ids/%s/", room_id)
-        return self.client.get_json(
+        path = _create_v1_path("/state_ids/%s", room_id)
+        return self.client.get_json_with_trailing_slashes_on_404(
             destination, path=path, args={"event_id": event_id},
         )
 
@@ -95,8 +95,10 @@ class TransportLayerClient(object):
         logger.debug("get_pdu dest=%s, event_id=%s",
                      destination, event_id)
 
-        path = _create_v1_path("/event/%s/", event_id)
-        return self.client.get_json(destination, path=path, timeout=timeout)
+        path = _create_v1_path("/event/%s", event_id)
+        return self.client.get_json_with_trailing_slashes_on_404(
+            destination, path=path, timeout=timeout,
+        )
 
     @log_function
     def backfill(self, destination, room_id, event_tuples, limit):
@@ -121,14 +123,14 @@ class TransportLayerClient(object):
             # TODO: raise?
             return
 
-        path = _create_v1_path("/backfill/%s/", room_id)
+        path = _create_v1_path("/backfill/%s", room_id)
 
         args = {
             "v": event_tuples,
             "limit": [str(limit)],
         }
 
-        return self.client.get_json(
+        return self.client.get_json_with_trailing_slashes_on_404(
             destination,
             path=path,
             args=args,
@@ -169,7 +171,7 @@ class TransportLayerClient(object):
 
         path = _create_v1_path("/send/%s", transaction.transaction_id)
 
-        response = yield self.client.put_json(
+        response = yield self.client.put_json_with_trailing_slashes_on_404(
             transaction.destination,
             path=path,
             data=json_data,
@@ -959,7 +961,7 @@ def _create_v1_path(path, *args):
 
     Example:
 
-        _create_v1_path("/event/%s/", event_id)
+        _create_v1_path("/event/%s", event_id)
 
     Args:
         path (str): String template for the path
@@ -980,7 +982,7 @@ def _create_v2_path(path, *args):
 
     Example:
 
-        _create_v2_path("/event/%s/", event_id)
+        _create_v2_path("/event/%s", event_id)
 
     Args:
         path (str): String template for the path
diff --git a/synapse/http/matrixfederationclient.py b/synapse/http/matrixfederationclient.py
index 1682c9af13..8776639d6a 100644
--- a/synapse/http/matrixfederationclient.py
+++ b/synapse/http/matrixfederationclient.py
@@ -644,6 +644,51 @@ class MatrixFederationHttpClient(object):
         defer.returnValue(body)
 
     @defer.inlineCallbacks
+    def get_json_with_trailing_slashes_on_404(self, args={}):
+        """Runs client.get_json under the hood, but if receiving a 404, tries
+        the request again with a trailing slash. This is a result of removing
+        trailing slashes from some federation endpoints and in an effort to
+        remain backwards compatible with older versions of Synapse, we try
+        again if a server requires a trailing slash.
+
+        Args:
+            args (dict): A dictionary of arguments matching those provided by put_json.
+        Returns:
+            Deferred[dict|list]: Succeeds when we get a 2xx HTTP response. The
+            result will be the decoded JSON body.
+        """
+        response = yield self.get_json(**args)
+
+        # Retry with a trailing slash if we received a 404
+        if response.code == 404:
+            args["path"] += "/"
+            response = yield self.get_json(**args)
+
+        defer.returnValue(response)
+
+    @defer.inlineCallbacks
+    def put_json_with_trailing_slashes_on_404(self, args={}):
+        """Runs client.put_json under the hood, but if receiving a 404, tries
+        the request again with a trailing slash.
+
+        See get_json_with_trailing_slashes_on_404 for more details.
+
+        Args:
+            args (dict): A dictionary of arguments matching those provided by put_json.
+        Returns:
+            Deferred[dict|list]: Succeeds when we get a 2xx HTTP response. The
+            result will be the decoded JSON body.
+        """
+        response = yield self.put_json(**args)
+
+        # Retry with a trailing slash if we received a 404
+        if response.code == 404:
+            args["path"] += "/"
+            response = yield self.put_json(**args)
+
+        defer.returnValue(response)
+
+    @defer.inlineCallbacks
     def delete_json(self, destination, path, long_retries=False,
                     timeout=None, ignore_backoff=False, args={}):
         """Send a DELETE request to the remote expecting some json response